diff --git a/CHANGELOG.md b/CHANGELOG.md
index 568333c0..6dad485a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -50,6 +50,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- 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)
- Show Snow amounts in new weather module [#1545](https://github.com/MichMich/MagicMirror/issues/1545)
+- Added weather.gov as a new weather provider for US locations
## [2.6.0] - 2019-01-01
diff --git a/modules/default/weather/README.md b/modules/default/weather/README.md
index d15a208f..1b406958 100644
--- a/modules/default/weather/README.md
+++ b/modules/default/weather/README.md
@@ -35,21 +35,21 @@ The following properties can be configured:
| Option | Description
| ---------------------------- | -----------
-| `weatherProvider` | Which weather provider should be used.
**Possible values:** `openweathermap` and `darksky`
**Default value:** `openweathermap`
-| `type` | Which type of weather data should be displayed.
**Possible values:** `current` and `forecast`
**Default value:** `current`
-| `units` | What units to use. Specified by config.js
**Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` =Fahrenheit
**Default value:** `config.units`
+| `weatherProvider` | Which weather provider should be used.
**Possible values:** `openweathermap` , `darksky` , or `weathergov`
**Default value:** `openweathermap`
+| `type` | Which type of weather data should be displayed.
**Possible values:** `current` or `forecast`
**Default value:** `current`
+| `units` | What units to use. Specified by config.js
**Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` = Fahrenheit
**Default value:** `config.units`
| `roundTemp` | Round temperature value to nearest integer.
**Possible values:** `true` (round to integer) or `false` (display exact value with decimal point)
**Default value:** `false`
-| `degreeLabel` | Show the degree label for your chosen units (Metric = C, Imperial = F, Kelvins = K).
**Possible values:** `true` or `false`
**Default value:** `false`
+| `degreeLabel` | Show the degree label for your chosen units (Metric = C, Imperial = F, Kelvin = K).
**Possible values:** `true` or `false`
**Default value:** `false`
| `updateInterval` | How often does the content needs to be fetched? (Milliseconds)
**Possible values:** `1000` - `86400000`
**Default value:** `600000` (10 minutes)
-| `animationSpeed` | Speed of the update animation. (Milliseconds)
**Possible values:**`0` - `5000`
**Default value:** `1000` (1 second)
+| `animationSpeed` | Speed of the update animation. (Milliseconds)
**Possible values:** `0` - `5000`
**Default value:** `1000` (1 second)
| `timeFormat` | Use 12 or 24 hour format.
**Possible values:** `12` or `24`
**Default value:** uses value of _config.timeFormat_
| `showPeriod` | Show the period (am/pm) with 12 hour format
**Possible values:** `true` or `false`
**Default value:** `true`
| `showPeriodUpper` | Show the period (AM/PM) with 12 hour format as uppercase
**Possible values:** `true` or `false`
**Default value:** `false`
| `lang` | The language of the days.
**Possible values:** `en`, `nl`, `ru`, etc ...
**Default value:** uses value of _config.language_
| `decimalSymbol` | The decimal symbol to use.
**Possible values:** `.`, `,` or any other symbol.
**Default value:** `.`
-| `initialLoadDelay` | 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)
**Possible values:** `1000` - `5000`
**Default value:** `0`
-| `appendLocationNameToHeader` | If set to `true`, the returned location name will be appended to the header of the module, if the header is enabled. This is mainly intresting when using calender based weather.
**Default value:** `true`
-| `calendarClass` | The class for the calender module to base the event based weather information on.
**Default value:** `'calendar'`
+| `initialLoadDelay` | 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)
**Possible values:** `1000` - `5000`
**Default value:** `0`
+| `appendLocationNameToHeader` | If set to `true`, the returned location name will be appended to the header of the module, if the header is enabled. This is mainly interesting when using calender based weather.
**Default value:** `true`
+| `calendarClass` | The class for the calender module to base the event based weather information on.
**Default value:** `'calendar'`
#### Current weather options
@@ -62,14 +62,14 @@ The following properties can be configured:
| `showHumidity` | Show the current humidity
**Possible values:** `true` or `false`
**Default value:** `false`
| `showIndoorTemperature` | If you have another module that emits the `INDOOR_TEMPERATURE` notification, the indoor temperature will be displayed
**Default value:** `false`
| `showIndoorHumidity` | If you have another module that emits the `INDOOR_HUMIDITY` notification, the indoor humidity will be displayed
**Default value:** `false`
-| `showFeelsLike` | Shows the Feels like temperature weather.
**Possible values:**`true` or `false`
**Default value:** `true`
+| `showFeelsLike` | Shows the Feels like temperature weather.
**Possible values:** `true` or `false`
**Default value:** `true`
#### Weather forecast options
| Option | Description
| ---------------------------- | -----------
-| `tableClass` | The class for the forecast table.
**Default value:** `'small'`
-| `colored` | If set to `true`, the min and max temperature are color coded.
**Default value:** `false`
+| `tableClass` | The class for the forecast table.
**Default value:** `'small'`
+| `colored` | If set to `true`, the min and max temperature are color coded.
**Default value:** `false`
| `showPrecipitationAmount` | Show the amount of rain/snow in the forecast
**Possible values:** `true` or `false`
**Default value:** `false`
| `fade` | Fade the future events to black. (Gradient)
**Possible values:** `true` or `false`
**Default value:** `true`
| `fadePoint` | Where to start fade?
**Possible values:** `0` (top of the list) - `1` (bottom of list)
**Default value:** `0.25`
@@ -79,22 +79,31 @@ The following properties can be configured:
| Option | Description
| ---------------------------- | -----------
-| `apiVersion` | The OpenWeatherMap API version to use.
**Default value:** `2.5`
-| `apiBase` | The OpenWeatherMap base URL.
**Default value:** `'http://api.openweathermap.org/data/'`
-| `weatherEndpoint` | The OpenWeatherMap API endPoint.
**Possible values:** `/weather`, `/forecast` (free users) or `/forecast/daily` (paying users or old apiKey only)
**Default value:** `'/weather'`
+| `apiVersion` | The OpenWeatherMap API version to use.
**Default value:** `2.5`
+| `apiBase` | The OpenWeatherMap base URL.
**Default value:** `'http://api.openweathermap.org/data/'`
+| `weatherEndpoint` | The OpenWeatherMap API endPoint.
**Possible values:** `/weather`, `/forecast` (free users) or `/forecast/daily` (paying users or old apiKey only)
**Default value:** `'/weather'`
| `locationID` | Location ID from [OpenWeatherMap](https://openweathermap.org/find) **This will override anything you put in location.**
Leave blank if you want to use location.
**Example:** `1234567`
**Default value:** `false`
**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.
**Example:** `'Amsterdam,Netherlands'`
**Default value:** `false`
**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.
This value is **REQUIRED**
+| `apiKey` | The [OpenWeatherMap](https://home.openweathermap.org) API key, which can be obtained by creating an OpenWeatherMap account.
This value is **REQUIRED**
### Darksky options
| 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.
**Possible value:** `'https://cors-anywhere.herokuapp.com/https://api.darksky.net'`
This value is **REQUIRED**
-| `weatherEndpoint` | The DarkSky API endPoint.
**Possible values:** `/forecast`
This value is **REQUIRED**
-| `apiKey` | The [DarkSky](https://darksky.net/dev/register) API key, which can be obtained by creating an DarkSky account.
This value is **REQUIRED**
-| `lat` | The geo coordinate latitude.
This value is **REQUIRED**
-| `lon` | The geo coordinate longitude.
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.
**Possible value:** `'https://cors-anywhere.herokuapp.com/https://api.darksky.net'`
This value is **REQUIRED**
+| `weatherEndpoint` | The DarkSky API endPoint.
**Possible values:** `/forecast`
This value is **REQUIRED**
+| `apiKey` | The [DarkSky](https://darksky.net/dev/register) API key, which can be obtained by creating an DarkSky account.
This value is **REQUIRED**
+| `lat` | The geo coordinate latitude.
This value is **REQUIRED**
+| `lon` | The geo coordinate longitude.
This value is **REQUIRED**
+
+### Weather.gov options
+
+| Option | Description
+| ---------------------------- | -----------
+| `apiBase` | The weather.gov base URL.
**Possible value:** `'https://api.weather.gov/points/'`
This value is **REQUIRED**
+| `weatherEndpoint` | The weather.gov API endPoint.
**Possible values:** `/forecast` for forecast and `/forecast/hourly` for current.
This value is **REQUIRED**
+| `lat` | The geo coordinate latitude.
This value is **REQUIRED**
+| `lon` | The geo coordinate longitude.
This value is **REQUIRED**
## API Provider Development
diff --git a/modules/default/weather/providers/README.md b/modules/default/weather/providers/README.md
index 85d9c3c5..7acb7e34 100644
--- a/modules/default/weather/providers/README.md
+++ b/modules/default/weather/providers/README.md
@@ -85,7 +85,7 @@ Notify the delegate that new weather is available.
#### `fetchData(url, method, data)`
-A convinience function to make requests. It returns a promise.
+A convenience function to make requests. It returns a promise.
### WeatherObject
diff --git a/modules/default/weather/providers/openweathermap.js b/modules/default/weather/providers/openweathermap.js
index e388e278..f7fe0edc 100644
--- a/modules/default/weather/providers/openweathermap.js
+++ b/modules/default/weather/providers/openweathermap.js
@@ -103,13 +103,13 @@ WeatherProvider.register("openweathermap", {
// initial variable declaration
const days = [];
// variables for temperature range and rain
- var minTemp = [];
- var maxTemp = [];
- var rain = 0;
- var snow = 0;
+ let minTemp = [];
+ let maxTemp = [];
+ let rain = 0;
+ let snow = 0;
// variable for date
let date = "";
- var weather = new WeatherObject(this.config.units);
+ let weather = new WeatherObject(this.config.units);
for (const forecast of forecasts) {
diff --git a/modules/default/weather/providers/weathergov.js b/modules/default/weather/providers/weathergov.js
new file mode 100644
index 00000000..1110c68e
--- /dev/null
+++ b/modules/default/weather/providers/weathergov.js
@@ -0,0 +1,256 @@
+/* global WeatherProvider, WeatherObject */
+
+/* Magic Mirror
+ * Module: Weather
+ * Provider: weather.gov
+ *
+ * By Vince Peri
+ * MIT Licensed.
+ *
+ * This class is a provider for weather.gov.
+ * Note that this is only for US locations (lat and lon) and does not require an API key
+ * Since it is free, there are some items missing - like sunrise, sunset, humidity, etc.
+ */
+
+WeatherProvider.register("weathergov", {
+
+ // Set the name of the provider.
+ // This isn't strictly necessary, since it will fallback to the provider identifier
+ // But for debugging (and future alerts) it would be nice to have the real name.
+ providerName: "Weather.gov",
+
+ // Overwrite the fetchCurrentWeather method.
+ fetchCurrentWeather() {
+ this.fetchData(this.getUrl())
+ .then(data => {
+ if (!data || !data.properties || !data.properties.periods || !data.properties.periods.length) {
+ // Did not receive usable new data.
+ // Maybe this needs a better check?
+ return;
+ }
+
+ const currentWeather = this.generateWeatherObjectFromCurrentWeather(data.properties.periods[0]);
+ this.setCurrentWeather(currentWeather);
+ })
+ .catch(function(request) {
+ Log.error("Could not load data ... ", request);
+ })
+ },
+
+ // Overwrite the fetchCurrentWeather method.
+ fetchWeatherForecast() {
+ this.fetchData(this.getUrl())
+ .then(data => {
+ if (!data || !data.properties || !data.properties.periods || !data.properties.periods.length) {
+ // Did not receive usable new data.
+ // Maybe this needs a better check?
+ return;
+ }
+
+ const forecast = this.generateWeatherObjectsFromForecast(data.properties.periods);
+ this.setWeatherForecast(forecast);
+ })
+ .catch(function(request) {
+ Log.error("Could not load data ... ", request);
+ })
+ },
+
+ /** Weather.gov Specific Methods - These are not part of the default provider methods */
+ /*
+ * Gets the complete url for the request
+ */
+ getUrl() {
+ return this.config.apiBase + this.config.lat + "," + this.config.lon + this.config.weatherEndpoint;
+ },
+
+ /*
+ * Generate a WeatherObject based on currentWeatherInformation
+ */
+ generateWeatherObjectFromCurrentWeather(currentWeatherData) {
+ const currentWeather = new WeatherObject(this.config.units);
+
+ currentWeather.temperature = currentWeatherData.temperature;
+ currentWeather.windSpeed = currentWeatherData.windSpeed.split(" ", 1);
+ currentWeather.windDirection = this.convertDirectiontoDegrees(currentWeatherData.windDirection);
+ currentWeather.weatherType = this.convertWeatherType(currentWeatherData.shortForecast, currentWeatherData.isDaytime);
+
+ return currentWeather;
+ },
+
+ /*
+ * Generate WeatherObjects based on forecast information
+ */
+ generateWeatherObjectsFromForecast(forecasts) {
+ return this.fetchForecastDaily(forecasts);
+ },
+
+ /*
+ * fetch forecast information for daily forecast.
+ */
+ fetchForecastDaily(forecasts) {
+ // initial variable declaration
+ const days = [];
+ // variables for temperature range and rain
+ let minTemp = [];
+ let maxTemp = [];
+ // variable for date
+ let date = "";
+ let weather = new WeatherObject(this.config.units);
+ weather.precipitation = 0;
+
+ for (const forecast of forecasts) {
+
+ if (date !== moment(forecast.startTime).format("YYYY-MM-DD")) {
+
+ // calculate minimum/maximum temperature, specify rain amount
+ weather.minTemperature = Math.min.apply(null, minTemp);
+ weather.maxTemperature = Math.max.apply(null, maxTemp);
+
+ // push weather information to days array
+ days.push(weather);
+ // create new weather-object
+ weather = new WeatherObject(this.config.units);
+
+ minTemp = [];
+ maxTemp = [];
+ weather.precipitation = 0;
+
+ // set new date
+ date = moment(forecast.startTime).format("YYYY-MM-DD");
+
+ // specify date
+ weather.date = moment(forecast.startTime);
+
+ // If the first value of today is later than 17:00, we have an icon at least!
+ weather.weatherType = this.convertWeatherType(forecast.shortForecast, forecast.isDaytime);
+ }
+
+ if (moment(forecast.startTime).format("H") >= 8 && moment(forecast.startTime).format("H") <= 17) {
+ weather.weatherType = this.convertWeatherType(forecast.shortForecast, forecast.isDaytime);
+ }
+
+ // the same day as before
+ // add values from forecast to corresponding variables
+ minTemp.push(forecast.temperature);
+ maxTemp.push(forecast.temperature);
+ }
+
+ // last day
+ // calculate minimum/maximum temperature, specify rain amount
+ weather.minTemperature = Math.min.apply(null, minTemp);
+ weather.maxTemperature = Math.max.apply(null, maxTemp);
+
+ // push weather information to days array
+ days.push(weather);
+ return days.slice(1);
+ },
+
+ /*
+ * Convert the icons to a more usable name.
+ */
+ convertWeatherType(weatherType, isDaytime) {
+ //https://w1.weather.gov/xml/current_obs/weather.php
+ // There are way too many types to create, so lets just look for certain strings
+
+ if (weatherType.includes("Cloudy") || weatherType.includes("Partly")) {
+ if (isDaytime) {
+ return "day-cloudy";
+ }
+
+ return "night-cloudy";
+ } else if (weatherType.includes("Overcast")) {
+ if (isDaytime) {
+ return "cloudy";
+ }
+
+ return "night-cloudy";
+ } else if (weatherType.includes("Freezing") || weatherType.includes("Ice")) {
+ return "rain-mix";
+ } else if (weatherType.includes("Snow")) {
+ if (isDaytime) {
+ return "snow";
+ }
+
+ return "night-snow";
+ } else if (weatherType.includes("Thunderstorm")) {
+ if (isDaytime) {
+ return "thunderstorm";
+ }
+
+ return "night-thunderstorm";
+ } else if (weatherType.includes("Showers")) {
+ if (isDaytime) {
+ return "showers";
+ }
+
+ return "night-showers";
+ } else if (weatherType.includes("Rain") || weatherType.includes("Drizzle")) {
+ if (isDaytime) {
+ return "rain";
+ }
+
+ return "night-rain";
+ } else if (weatherType.includes("Breezy") || weatherType.includes("Windy")) {
+ if (isDaytime) {
+ return "cloudy-windy";
+ }
+
+ return "night-alt-cloudy-windy";
+ } else if (weatherType.includes("Fair") || weatherType.includes("Clear") || weatherType.includes("Few") || weatherType.includes("Sunny")) {
+ if (isDaytime) {
+ return "day-sunny";
+ }
+
+ return "night-clear";
+ } else if (weatherType.includes("Dust") || weatherType.includes("Sand")) {
+ return "dust";
+ } else if (weatherType.includes("Fog")) {
+ return "fog";
+ } else if (weatherType.includes("Smoke")) {
+ return "smoke";
+ } else if (weatherType.includes("Haze")) {
+ return "day-haze";
+ }
+
+ return null;
+ },
+
+ /*
+ Convert the direction into Degrees
+ */
+ convertDirectiontoDegrees(direction) {
+ if (direction === "NNE"){
+ return 33.75;
+ } else if (direction === "NE") {
+ return 56.25;
+ } else if (direction === "ENE") {
+ return 78.75;
+ } else if (direction === "E") {
+ return 101.25;
+ } else if (direction === "ESE") {
+ return 123.75;
+ } else if (direction === "SE") {
+ return 146.25;
+ } else if (direction === "SSE") {
+ return 168.75;
+ } else if (direction === "S") {
+ return 191.25;
+ } else if (direction === "SSW") {
+ return 213.75;
+ } else if (direction === "SW") {
+ return 236.25;
+ } else if (direction === "WSW") {
+ return 258.75;
+ } else if (direction === "W") {
+ return 281.25;
+ } else if (direction === "WNW") {
+ return 303.75;
+ } else if (direction === "NW") {
+ return 326.25;
+ } else if (direction === "NNW") {
+ return 348.75;
+ } else {
+ return 0;
+ }
+ }
+});