Add module subfolder support.

This commit is contained in:
Michael Teeuw
2016-04-01 17:35:29 +02:00
parent 671338425a
commit e4c54cc655
27 changed files with 56 additions and 21 deletions

View File

@@ -0,0 +1,153 @@
# Module: Weather Forecast
The `weatherforecast` module is one of the default modules of the MagicMirror.
This module displays the weather forecast for the coming week, including an an icon to display the current conditions, the minimum temperature and the maximum temperature.
## Using the module
To use this module, add it to the modules array in the `config/config.js` file:
````javascript
modules: [
{
module: 'weatherforecast',
position: 'top_right', // This can be any of the regions.
// Best results in left or right regions.
config: {
// See 'Configuration options' for more information.
location: 'Amsterdam,Netherlands',
appid: 'abcde12345abcde12345abcde12345ab' //openweathermap.org API key.
}
}
]
````
## Configuration options
The following properties can be configured:
<table width="100%">
<!-- why, markdown... -->
<thead>
<tr>
<th>Option</th>
<th width="100%">Description</th>
</tr>
<thead>
<tbody>
<tr>
<td><code>location</code></td>
<td>The location used for weather information.<br>
<br><b>Example:</b> <code>Amsterdam,Netherlands</code>
<br><b>Default value:</b> <code>New York</code>
</td>
</tr>
<tr>
<td><code>appid</code></td>
<td>The <a href="https://home.openweathermap.org" target="_blank">OpenWeatherMap</a> API key, which can be obtained by creating an OpenWeatherMap account.<br>
<br> This value is <b>REQUIRED</b>
</td>
</tr>
<tr>
<td><code>units</code></td>
<td>What units to use?<br>
<br><b>Possible values:</b> <code>default</code> = Kelvin, <code>metric</code> = Celsius, <code>imperial</code> =Fahrenheit
<br><b>Default value:</b> <code>metric</code>
</td>
</tr>
<tr>
<td><code>updateInterval</code></td>
<td>How often does the content needs to be fetched? (Milliseconds)<br>
<br><b>Possible values:</b> <code>1000</code> - <code>86400000</code>
<br><b>Default value:</b> <code>300000</code> (10 minutes)
</td>
</tr>
<tr>
<td><code>animationSpeed</code></td>
<td>Speed of the update animation. (Milliseconds)<br>
<br><b>Possible values:</b><code>0</code> - <code>5000</code>
<br><b>Default value:</b> <code>2000</code> (2 seconds)
</td>
</tr>
<tr>
<td><code>lang</code></td>
<td>The language of the days.<br>
<br><b>Possible values:</b> <code>en</code>, <code>nl</code>, <code>ru</code>, etc ...
<br><b>Default value:</b> uses value of <i>config.language</i>
</td>
</tr>
<tr>
<td><code>fade</code></td>
<td>Fade the future events to black. (Gradient)<br>
<br><b>Possible values:</b> <code>true</code> or <code>false</code>
<br><b>Default value:</b> <code>true</code>
</td>
</tr>
<tr>
<td><code>fadePoint</code></td>
<td>Where to start fade?<br>
<br><b>Possible values:</b> <code>0</code> (top of the list) - <code>1</code> (bottom of list)
<br><b>Default value:</b> <code>0.25</code>
</td>
</tr>
<tr>
<td><code>initialLoadDelay</code></td>
<td>The initial delay before loading. If you have multiple modules that use the same API key, you might want to delay one of the requests. (Milliseconds)<br>
<br><b>Possible values:</b> <code>1000</code> - <code>5000</code>
<br><b>Default value:</b> <code>0</code>
</td>
</tr>
<tr>
<td><code>retryDelay</code></td>
<td>The delay before retrying after a request failure. (Milliseconds)<br>
<br><b>Possible values:</b> <code>1000</code> - <code>60000</code>
<br><b>Default value:</b> <code>2500</code>
</td>
</tr>
<tr>
<td><code>apiVersion</code></td>
<td>The OpenWeatherMap API version to use.<br>
<br><b>Default value:</b> <code>2.5</code>
</td>
</tr>
<tr>
<td><code>apiBase</code></td>
<td>The OpenWeatherMap base URL.<br>
<br><b>Default value:</b> <code>'http://api.openweathermap.org/data/'</code>
</td>
</tr>
<tr>
<td><code>weatherEndpoint</code></td>
<td>The OpenWeatherMap API endPoint.<br>
<br><b>Default value:</b> <code>'forecast/daily'</code>
</td>
</tr>
<tr>
<td><code>iconTable</code></td>
<td>The conversion table to convert the weather conditions to weather-icons.<br>
<br><b>Default value:</b> <code>iconTable: {
'01d':'wi-day-sunny',
'02d':'wi-day-cloudy',
'03d':'wi-cloudy',
'04d':'wi-cloudy-windy',
'09d':'wi-showers',
'10d':'wi-rain',
'11d':'wi-thunderstorm',
'13d':'wi-snow',
'50d':'wi-fog',
'01n':'wi-night-clear',
'02n':'wi-night-cloudy',
'03n':'wi-night-cloudy',
'04n':'wi-night-cloudy',
'09n':'wi-night-showers',
'10n':'wi-night-rain',
'11n':'wi-night-thunderstorm',
'13n':'wi-night-snow',
'50n':'wi-night-alt-cloudy-windy'
}</code>
</td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,14 @@
.weatherforecast .day {
padding-left: 0px;
padding-right: 25px;
}
.weatherforecast .weather-icon {
padding-right: 30px;
text-align: center;
}
.weatherforecast .min-temp {
padding-left: 20px;
padding-right: 0px;
}

View File

@@ -0,0 +1,280 @@
/* global Module */
/* Magic Mirror
* Module: WeatherForecast
*
* By Michael Teeuw http://michaelteeuw.nl
* MIT Licensed.
*/
Module.register('weatherforecast',{
// Default module config.
defaults: {
location: '',
appid: '',
units: 'metric',
updateInterval: 10 * 60 * 1000, // every 10 minutes
animationSpeed: 1000,
timeFormat: config.timeFormat,
lang: config.language,
fade: true,
fadePoint: 0.25, // Start on 1/4th of the list.
initialLoadDelay: 2500, // 2.5 seconds delay. This delay is used to keep the OpenWeather API happy.
retryDelay: 2500,
apiVersion: '2.5',
apiBase: 'http://api.openweathermap.org/data/',
forecastEndpoint: 'forecast/daily',
iconTable: {
'01d':'wi-day-sunny',
'02d':'wi-day-cloudy',
'03d':'wi-cloudy',
'04d':'wi-cloudy-windy',
'09d':'wi-showers',
'10d':'wi-rain',
'11d':'wi-thunderstorm',
'13d':'wi-snow',
'50d':'wi-fog',
'01n':'wi-night-clear',
'02n':'wi-night-cloudy',
'03n':'wi-night-cloudy',
'04n':'wi-night-cloudy',
'09n':'wi-night-showers',
'10n':'wi-night-rain',
'11n':'wi-night-thunderstorm',
'13n':'wi-night-snow',
'50n':'wi-night-alt-cloudy-windy'
},
},
// Define required scripts.
getScripts: function() {
return ['moment.js'];
},
// Define required scripts.
getStyles: function() {
return ['weather-icons.css', 'weatherforecast.css'];
},
// Define start sequence.
start: function() {
Log.info('Starting module: ' + this.name);
// Set locale.
moment.locale(config.language);
this.forecast = [];
this.loaded = false;
this.scheduleUpdate(this.config.initialLoadDelay);
this.updateTimer = null;
},
// Override dom generator.
getDom: function() {
var wrapper = document.createElement("div");
if (this.config.appid === '') {
wrapper.innerHTML = "Please set the correct openweather <i>appid</i> in the config for module: " + this.name + ".";
wrapper.className = "dimmed light small";
return wrapper;
}
if (this.config.location === '') {
wrapper.innerHTML = "Please set the openweather <i>location</i> in the config for module: " + this.name + ".";
wrapper.className = "dimmed light small";
return wrapper;
}
if (!this.loaded) {
wrapper.innerHTML = "Loading weather ...";
wrapper.className = "dimmed light small";
return wrapper;
}
var table = document.createElement("table");
table.className = "small";
for (var f in this.forecast) {
var forecast = this.forecast[f];
var row = document.createElement("tr");
table.appendChild(row);
var dayCell = document.createElement("td");
dayCell.className = 'day';
dayCell.innerHTML = forecast.day;
row.appendChild(dayCell);
var iconCell = document.createElement("td");
iconCell.className = "bright weather-icon";
row.appendChild(iconCell);
var icon = document.createElement("span");
icon.className = forecast.icon;
iconCell.appendChild(icon);
var maxTempCell = document.createElement("td");
maxTempCell.innerHTML = forecast.maxTemp;
maxTempCell.className = 'align-right bright max-temp';
row.appendChild(maxTempCell);
var minTempCell = document.createElement("td");
minTempCell.innerHTML = forecast.minTemp;
minTempCell.className = 'align-right min-temp';
row.appendChild(minTempCell);
if (this.config.fade && this.config.fadePoint < 1) {
if (this.config.fadePoint < 0) {
this.config.fadePoint = 0;
}
var startingPoint = this.forecast.length * this.config.fadePoint;
var steps = this.forecast.length - startingPoint;
if (f >= startingPoint) {
var currentStep = f - startingPoint;
row.style.opacity = 1 - (1 / steps * currentStep);
}
}
}
return table;
},
/* updateWeather(compliments)
* Requests new data from openweather.org.
* Calls processWeather on succesfull response.
*/
updateWeather: function() {
var url = this.config.apiBase + this.config.apiVersion + '/' + this.config.forecastEndpoint + this.getParams();
var self = this;
var retry = true;
var weatherRequest = new XMLHttpRequest();
weatherRequest.open("GET", url, true);
weatherRequest.onreadystatechange = function() {
if(this.readyState === 4) {
if(this.status === 200) {
self.processWeather(JSON.parse(this.response));
} else if (this.status === 401) {
self.config.appid = '';
self.updateDom(self.config.animationSpeed);
Log.error(self.name + ": Incorrect APPID.");
retry = false;
} else {
Log.error(self.name + ": Could not load weather.");
}
if (retry) {
self.scheduleUpdate((self.loaded) ? -1 : self.config.retryDelay);
}
}
};
weatherRequest.send();
},
/* getParams(compliments)
* Generates an url with api parameters based on the config.
*
* return String - URL params.
*/
getParams: function() {
var params = "?";
params += 'q=' + this.config.location;
params += '&units=' + this.config.units;
params += '&lang=' + this.config.lang;
params += '&APPID=' + this.config.appid;
return params;
},
/* processWeather(data)
* Uses the received data to set the various values.
*
* argument data object - Weather information received form openweather.org.
*/
processWeather: function(data) {
this.forecast = [];
for (var i = 0, count = data.list.length; i < count; i++) {
var forecast = data.list[i];
this.forecast.push({
day: moment(forecast.dt, 'X').format('ddd.'),
icon: this.config.iconTable[forecast.weather[0].icon],
maxTemp: this.roundValue(forecast.temp.max),
minTemp: this.roundValue(forecast.temp.min)
});
}
//Log.log(this.forecast);
this.loaded = true;
this.updateDom(this.config.animationSpeed);
},
/* scheduleUpdate()
* Schedule next update.
*
* argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used.
*/
scheduleUpdate: function(delay) {
var nextLoad = this.config.updateInterval;
if (typeof delay !== 'undefined' && delay >= 0) {
nextLoad = delay;
}
var self = this;
clearTimeout(this.updateTimer);
this.updateTimer = setTimeout(function() {
self.updateWeather();
}, nextLoad);
},
/* ms2Beaufort(ms)
* Converts m2 to beaufort (windspeed).
*
* argument ms number - Windspeed in m/s.
*
* return number - Windspeed in beaufort.
*/
ms2Beaufort: function(ms) {
var kmh = ms * 60 * 60 / 1000;
var speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
for (var beaufort in speeds) {
var speed = speeds[beaufort];
if (speed > kmh) {
return beaufort;
}
}
return 12;
},
/* function(temperature)
* Rounds a temperature to 1 decimal.
*
* argument temperature number - Temperature.
*
* return number - Rounded Temperature.
*/
roundValue: function (temperature) {
return parseFloat(temperature).toFixed(1);
}
});