Merge pull request #1514 from fwitte/features/weather_forecast_and_forecast_daily_support

Add forecast and forecast/daily support to new weather module
This commit is contained in:
Michael Teeuw 2019-02-14 17:41:10 +01:00 committed by GitHub
commit 4bb32c6d09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 31 deletions

View File

@ -39,6 +39,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Aligned indoor values in current weather vertical [#1499](https://github.com/MichMich/MagicMirror/issues/1499). - Aligned indoor values in current weather vertical [#1499](https://github.com/MichMich/MagicMirror/issues/1499).
- Added humidity support to nunjuck unit filter. - Added humidity support to nunjuck unit filter.
- Do not display degree symbol for temperature in Kelvin [#1503](https://github.com/MichMich/MagicMirror/issues/1503). - Do not display degree symbol for temperature in Kelvin [#1503](https://github.com/MichMich/MagicMirror/issues/1503).
- Weather forecast now works with openweathermap for both, `/forecast` and `/forecast/daily`, in new weather module. If you use the `/forecast`-weatherEndpoint, the hourly data are converted to daily data, see issues [#1504](https://github.com/MichMich/MagicMirror/issues/1504), [#1513](https://github.com/MichMich/MagicMirror/issues/1513).
- Added fade, fadePoint and maxNumberOfDays properties to the forecast mode [#1516](https://github.com/MichMich/MagicMirror/issues/1516) - Added fade, fadePoint and maxNumberOfDays properties to the forecast mode [#1516](https://github.com/MichMich/MagicMirror/issues/1516)
- Fixed Loading string and decimalSymbol string replace [#1538](https://github.com/MichMich/MagicMirror/issues/1538) - Fixed Loading string and decimalSymbol string replace [#1538](https://github.com/MichMich/MagicMirror/issues/1538)

View File

@ -81,17 +81,17 @@ The following properties can be configured:
| ---------------------------- | ----------- | ---------------------------- | -----------
| `apiVersion` | The OpenWeatherMap API version to use. <br><br> **Default value:** `2.5` | `apiVersion` | The OpenWeatherMap API version to use. <br><br> **Default value:** `2.5`
| `apiBase` | The OpenWeatherMap base URL. <br><br> **Default value:** `'http://api.openweathermap.org/data/'` | `apiBase` | The OpenWeatherMap base URL. <br><br> **Default value:** `'http://api.openweathermap.org/data/'`
| `weatherEndpoint` | The OpenWeatherMap API endPoint. <br><br> **Possible values:** `/weather`, `/forecast` or `/forecast/daily` (paying users only) <br> **Default value:** `'/weather'` | `weatherEndpoint` | The OpenWeatherMap API endPoint. <br><br> **Possible values:** `/weather`, `/forecast` (free users) or `/forecast/daily` (paying users or old apiKey only) <br> **Default value:** `'/weather'`
| `locationID` | Location ID from [OpenWeatherMap](https://openweathermap.org/find) **This will override anything you put in location.** <br> Leave blank if you want to use location. <br> **Example:** `1234567` <br> **Default value:** `false` <br><br> **Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used. | `locationID` | Location ID from [OpenWeatherMap](https://openweathermap.org/find) **This will override anything you put in location.** <br> Leave blank if you want to use location. <br> **Example:** `1234567` <br> **Default value:** `false` <br><br> **Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used.
| `location` | The location used for weather information. <br><br> **Example:** `'Amsterdam,Netherlands'` <br> **Default value:** `false` <br><br> **Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used. | `location` | The location used for weather information. <br><br> **Example:** `'Amsterdam,Netherlands'` <br> **Default value:** `false` <br><br> **Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used.
| `apiKey` | The [OpenWeatherMap](https://home.openweathermap.org) API key, which can be obtained by creating an OpenWeatherMap account. <br><br> This value is **REQUIRED** | `apiKey` | The [OpenWeatherMap](https://home.openweathermap.org) API key, which can be obtained by creating an OpenWeatherMap account. <br><br> This value is **REQUIRED**
### Darksky options ### Darksky options
| Option | Description | Option | Description
| ---------------------------- | ----------- | ---------------------------- | -----------
| `apiBase` | The DarkSky base URL. The darksky api has disabled [cors](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), therefore a proxy is required. <br><br> **Possible value:** `'https://cors-anywhere.herokuapp.com/https://api.darksky.net'` <br> This value is **REQUIRED** | `apiBase` | The DarkSky base URL. The darksky api has disabled [cors](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), therefore a proxy is required. <br><br> **Possible value:** `'https://cors-anywhere.herokuapp.com/https://api.darksky.net'` <br> This value is **REQUIRED**
| `weatherEndpoint` | The DarkSky API endPoint. <br><br> **Possible values:** `/forecast` <br> This value is **REQUIRED** | `weatherEndpoint` | The DarkSky API endPoint. <br><br> **Possible values:** `/forecast` <br> This value is **REQUIRED**
| `apiKey` | The [DarkSky](https://darksky.net/dev/register) API key, which can be obtained by creating an DarkSky account. <br><br> This value is **REQUIRED** | `apiKey` | The [DarkSky](https://darksky.net/dev/register) API key, which can be obtained by creating an DarkSky account. <br><br> This value is **REQUIRED**
| `lat` | The geo coordinate latitude. <br><br> This value is **REQUIRED** | `lat` | The geo coordinate latitude. <br><br> This value is **REQUIRED**
| `lon` | The geo coordinate longitude. <br><br> This value is **REQUIRED** | `lon` | The geo coordinate longitude. <br><br> This value is **REQUIRED**

View File

@ -5,7 +5,7 @@
* *
* By Michael Teeuw http://michaelteeuw.nl * By Michael Teeuw http://michaelteeuw.nl
* MIT Licensed. * MIT Licensed.
* *
* This class is the blueprint for a weather provider. * This class is the blueprint for a weather provider.
*/ */
@ -56,8 +56,6 @@ WeatherProvider.register("openweathermap", {
}) })
}, },
/** OpenWeatherMap Specific Methods - These are not part of the default provider methods */ /** OpenWeatherMap Specific Methods - These are not part of the default provider methods */
/* /*
* Gets the complete url for the request * Gets the complete url for the request
@ -66,7 +64,7 @@ WeatherProvider.register("openweathermap", {
return this.config.apiBase + this.config.apiVersion + this.config.weatherEndpoint + this.getParams(); return this.config.apiBase + this.config.apiVersion + this.config.weatherEndpoint + this.getParams();
}, },
/* /*
* Generate a WeatherObject based on currentWeatherInformation * Generate a WeatherObject based on currentWeatherInformation
*/ */
generateWeatherObjectFromCurrentWeather(currentWeatherData) { generateWeatherObjectFromCurrentWeather(currentWeatherData) {
@ -87,6 +85,21 @@ WeatherProvider.register("openweathermap", {
* Generate WeatherObjects based on forecast information * Generate WeatherObjects based on forecast information
*/ */
generateWeatherObjectsFromForecast(forecasts) { generateWeatherObjectsFromForecast(forecasts) {
if (this.config.weatherEndpoint == "/forecast") {
return this.fetchForecastHourly(forecasts);
} else if (this.config.weatherEndpoint == "/forecast/daily") {
return this.fetchForecastDaily(forecasts);
}
// if weatherEndpoint does not match forecast or forecast/daily, what should be returned?
const days = [new WeatherObject(this.config.units)];
return days;
},
/*
* fetch forecast information for 3-hourly forecast (available for free subscription).
*/
fetchForecastHourly(forecasts) {
// initial variable declaration // initial variable declaration
const days = []; const days = [];
// variables for temperature range and rain // variables for temperature range and rain
@ -98,19 +111,8 @@ WeatherProvider.register("openweathermap", {
var weather = new WeatherObject(this.config.units); var weather = new WeatherObject(this.config.units);
for (const forecast of forecasts) { for (const forecast of forecasts) {
if (date === moment(forecast.dt, "X").format("YYYY-MM-DD")) { if (date !== moment(forecast.dt, "X").format("YYYY-MM-DD")) {
// the same day as before
// add values from forecast to corresponding variables
minTemp.push(forecast.main.temp_min);
maxTemp.push(forecast.main.temp_max);
if (this.config.units === "imperial" && !isNaN(forecast.rain["3h"])) {
rain += forecast.rain["3h"] / 25.4;
} else {
rain += forecast.rain["3h"];
}
} else {
// a new day
// calculate minimum/maximum temperature, specify rain amount // calculate minimum/maximum temperature, specify rain amount
weather.minTemperature = Math.min.apply(null, minTemp); weather.minTemperature = Math.min.apply(null, minTemp);
weather.maxTemperature = Math.max.apply(null, maxTemp); weather.maxTemperature = Math.max.apply(null, maxTemp);
@ -119,34 +121,87 @@ WeatherProvider.register("openweathermap", {
days.push(weather); days.push(weather);
// create new weather-object // create new weather-object
weather = new WeatherObject(this.config.units); weather = new WeatherObject(this.config.units);
minTemp = []; minTemp = [];
maxTemp = []; maxTemp = [];
rain *= 0; rain = 0;
// set new date // set new date
date = moment(forecast.dt, "X").format("YYYY-MM-DD"); date = moment(forecast.dt, "X").format("YYYY-MM-DD");
// specify date // specify date
weather.date = moment(forecast.dt, "X"); weather.date = moment(forecast.dt, "X");
// select weather type by first forecast value of a day, is this reasonable? // If the first value of today is later than 17:00, we have an icon at least!
weather.weatherType = this.convertWeatherType(forecast.weather[0].icon); weather.weatherType = this.convertWeatherType(forecast.weather[0].icon);
}
// add values from first forecast of this day to corresponding variables if (moment(forecast.dt, "X").format("H") >= 8 && moment(forecast.dt, "X").format("H") <= 17) {
minTemp.push(forecast.main.temp_min); weather.weatherType = this.convertWeatherType(forecast.weather[0].icon);
maxTemp.push(forecast.main.temp_max); }
// the same day as before
// add values from forecast to corresponding variables
minTemp.push(forecast.main.temp_min);
maxTemp.push(forecast.main.temp_max);
if (forecast.hasOwnProperty("rain")) {
if (this.config.units === "imperial" && !isNaN(forecast.rain["3h"])) { if (this.config.units === "imperial" && !isNaN(forecast.rain["3h"])) {
rain += forecast.rain["3h"] / 25.4; rain += forecast.rain["3h"] / 25.4;
} else { } else if (!isNaN(forecast.rain["3h"])){
rain += forecast.rain["3h"]; rain += forecast.rain["3h"];
} else {
rain += 0;
} }
} else {
rain += 0;
} }
} }
// last day
// calculate minimum/maximum temperature, specify rain amount
weather.minTemperature = Math.min.apply(null, minTemp);
weather.maxTemperature = Math.max.apply(null, maxTemp);
weather.rain = rain;
// push weather information to days array
days.push(weather);
return days.slice(1); return days.slice(1);
}, },
/*
* fetch forecast information for daily forecast (available for paid subscription or old apiKey).
*/
fetchForecastDaily(forecasts) {
// initial variable declaration
const days = [];
for (const forecast of forecasts) {
const weather = new WeatherObject(this.config.units);
weather.date = moment(forecast.dt, "X");
weather.minTemperature = forecast.temp.min;
weather.maxTemperature = forecast.temp.max;
weather.weatherType = this.convertWeatherType(forecast.weather[0].icon);
// forecast.rain not available if amount is zero
if (forecast.hasOwnProperty("rain")) {
if (this.config.units === "imperial" && !isNaN(forecast.rain)) {
weather.rain = forecast.rain / 25.4;
} else if (!isNaN(forecast.rain)){
weather.rain = forecast.rain;
} else {
weather.rain = 0;
}
} else {
weather.rain = 0;
}
days.push(weather);
}
return days;
},
/* /*
* Convert the OpenWeatherMap icons to a more usable name. * Convert the OpenWeatherMap icons to a more usable name.
*/ */