From 9d2d170c2d4014f0ba5e11cfaf52410e1d35adb0 Mon Sep 17 00:00:00 2001 From: easyas314 <30437725+easyas314@users.noreply.github.com> Date: Sat, 13 Jun 2020 11:58:06 -0400 Subject: [PATCH] add additional URLs per API --- .../default/weather/providers/weathergov.js | 103 ++++++++++++------ 1 file changed, 69 insertions(+), 34 deletions(-) diff --git a/modules/default/weather/providers/weathergov.js b/modules/default/weather/providers/weathergov.js index e56c13a6..c41742fa 100755 --- a/modules/default/weather/providers/weathergov.js +++ b/modules/default/weather/providers/weathergov.js @@ -3,13 +3,14 @@ /* Magic Mirror * Module: Weather * Provider: weather.gov + * https://weather-gov.github.io/api/general-faqs * - * By Vince Peri + * Original 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. + * Since it is free, there are some items missing - like sunrise, sunset */ WeatherProvider.register("weathergov", { @@ -18,10 +19,10 @@ WeatherProvider.register("weathergov", { // But for debugging (and future alerts) it would be nice to have the real name. providerName: "Weather.gov", - // need setConfig URLs status + // Flag all needed URLs availability configURLs: false, - //multiple urls involved + //This API has multiple urls involved forecastURL: "tbd", forecastHourlyURL: "tbd", forecastGridDataURL: "tbd", @@ -31,9 +32,7 @@ WeatherProvider.register("weathergov", { // Called to set the config, this config is the same as the weather module's config. setConfig: function (config) { this.config = config; - Log.info(`Weather provider: ${this.providerName} config set.`, this.config); - // URLs needed from weather.gov - this.fetchWxGovURLs(this.config); + (this.config.apiBase = "https://api.weather.gov"), this.fetchWxGovURLs(this.config); }, // Called when the weather provider is about to start. @@ -41,26 +40,28 @@ WeatherProvider.register("weathergov", { Log.info(`Weather provider: ${this.providerName} started.`); }, + // This returns the name of the fetched location or an empty string. + fetchedLocation: function () { + return this.fetchedLocationName || ""; + }, + // Overwrite the fetchCurrentWeather method. fetchCurrentWeather() { if (!this.configURLs) { Log.info("fetch wx waiting on config URLs"); return; } - // this.fetchData(this.getUrl()) - this.fetchData(this.forecastURL) + this.fetchData(this.stationObsURL) .then((data) => { - if (!data || !data.properties || !data.properties.periods || !data.properties.periods.length) { + if (!data || !data.properties) { // Did not receive usable new data. - // Maybe this needs a better check? return; } - - const currentWeather = this.generateWeatherObjectFromCurrentWeather(data.properties.periods[0]); + const currentWeather = this.generateWeatherObjectFromCurrentWeather(data.properties); this.setCurrentWeather(currentWeather); }) .catch(function (request) { - Log.error("Could not load data ... ", request); + Log.error("Could not load station obs data ... ", request); }) .finally(() => this.updateAvailable()); }, @@ -71,20 +72,17 @@ WeatherProvider.register("weathergov", { Log.info("fetch wx waiting on config URLs"); return; } - //this.fetchData(this.getUrl()) - this.fetchData(this.forecastHourlyURL) + this.fetchData(this.forecastURL) .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); + Log.error("Could not load forecast hourly data ... ", request); }) .finally(() => this.updateAvailable()); }, @@ -95,21 +93,20 @@ WeatherProvider.register("weathergov", { * Get specific URLs */ fetchWxGovURLs(config) { - // wxCtl.initialLoadDelay here ? this.fetchData(`${config.apiBase}/points/${config.lat},${config.lon}`) .then((data) => { if (!data || !data.properties) { // points URL did not respond with usable data. return; } + this.fetchedLocationName = data.properties.relativeLocation.properties.city + ", " + data.properties.relativeLocation.properties.state; + Log.log("Forecast location is " + this.fetchedLocationName); this.forecastURL = data.properties.forecast; - Log.log(this.forecastURL); this.forecastHourlyURL = data.properties.forecastHourly; this.forecastGridDataURL = data.properties.forecastGridData; this.observationStationsURL = data.properties.observationStations; // with this URL, we chain another promise for the station obs URL return this.fetchData(data.properties.observationStations); - //const wxStations = await this.fetchData(wxPoints.properties.observationStationsURL); }) .then((obsData) => { if (!obsData || !obsData.features) { @@ -117,34 +114,44 @@ WeatherProvider.register("weathergov", { return; } this.stationObsURL = obsData.features[0].id + "/observations/latest"; - Log.log(this.stationObsURL); }) .catch((err) => { Log.error(err); }) .finally(() => { + // excellent, let's fetch some actual wx data this.configURLs = true; this.fetchCurrentWeather(); }); }, - /* - * 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 + * Weather.gov API uses specific units; API does not include choice of units + * ... object needs data in units based on config! */ generateWeatherObjectFromCurrentWeather(currentWeatherData) { const currentWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits); - currentWeather.temperature = currentWeatherData.temperature; - currentWeather.windSpeed = currentWeatherData.windSpeed.split(" ", 1); - currentWeather.windDirection = this.convertWindDirection(currentWeatherData.windDirection); - currentWeather.weatherType = this.convertWeatherType(currentWeatherData.shortForecast, currentWeatherData.isDaytime); + currentWeather.date = moment(currentWeatherData.timestamp); + currentWeather.temperature = this.convertTemp(currentWeatherData.temperature.value); + currentWeather.windSpeed = this.covertSpeed(currentWeatherData.windSpeed.value); + currentWeather.windDirection = currentWeatherData.windDirection.value; + currentWeather.minTemperature = this.convertTemp(currentWeatherData.minTemperatureLast24Hours.value); + currentWeather.maxTemperature = this.convertTemp(currentWeatherData.maxTemperatureLast24Hours.value); + currentWeather.humidity = Math.round(currentWeatherData.relativeHumidity.value); + currentWeather.rain = null; + currentWeather.snow = null; + currentWeather.precipitation = this.convertLength(currentWeatherData.precipitationLastHour.value); + currentWeather.feelsLikeTemp = this.convertTemp(currentWeatherData.heatIndex.value); + + let isDaytime = true; + if (currentWeatherData.icon.includes("day")) { + isDaytime = true; + } else { + isDaytime = false; + } + currentWeather.weatherType = this.convertWeatherType(currentWeatherData.textDescription, isDaytime); // determine the sunrise/sunset times - not supplied in weather.gov data let times = this.calcAstroData(this.config.lat, this.config.lon); @@ -220,6 +227,34 @@ WeatherProvider.register("weathergov", { return days.slice(1); }, + /* + * Unit conversions + */ + // conversion to fahrenheit + convertTemp(temp) { + if (this.config.tempUnits === "imperial") { + return (9 / 5) * temp + 32; + } else { + return temp; + } + }, + // conversion to mph + covertSpeed(metSec) { + if (this.config.windUnits === "imperial") { + return metSec * 2.23694; + } else { + return metSec; + } + }, + // conversion to inches + convertLength(meters) { + if (this.config.units === "imperial") { + return meters * 39.3701; + } else { + return meters; + } + }, + /* * Calculate the astronomical data */