diff --git a/CHANGELOG.md b/CHANGELOG.md index 3842a3a1..1566ea92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ _This release is scheduled to be released on 2021-04-01._ - Code cleanup for FEELS like and added {DEGREE} placeholder for FEELSLIKE for each language - Converted newsfeed module to use templates. - Update documentation and help screen about invalid config files. +- Moving weather provider specific code and configuration into each provider and making hourly part of the interface. ### Removed diff --git a/modules/default/weather/current.njk b/modules/default/weather/current.njk index e863eec4..ef09eb9c 100755 --- a/modules/default/weather/current.njk +++ b/modules/default/weather/current.njk @@ -1,7 +1,4 @@ -{% if current or weatherData %} - {% if weatherData %} - {% set current = weatherData.current %} - {% endif %} +{% if current %} {% if not config.onlyTemp %}
diff --git a/modules/default/weather/forecast.njk b/modules/default/weather/forecast.njk index a031d858..8fa04298 100644 --- a/modules/default/weather/forecast.njk +++ b/modules/default/weather/forecast.njk @@ -1,10 +1,5 @@ -{% if forecast or weatherData %} - {% if weatherData %} - {% set forecast = weatherData.days %} - {% set numSteps = forecast | calcNumEntries %} - {% else %} - {% set numSteps = forecast | calcNumSteps %} - {% endif %} +{% if forecast %} + {% set numSteps = forecast | calcNumSteps %} {% set currentStep = 0 %} {% set forecast = forecast.slice(0, numSteps) %} diff --git a/modules/default/weather/hourly.njk b/modules/default/weather/hourly.njk index 6fe17f6e..3950ece2 100644 --- a/modules/default/weather/hourly.njk +++ b/modules/default/weather/hourly.njk @@ -1,7 +1,4 @@ -{% if hourly or weatherData %} - {% if weatherData %} - {% set hourly = weatherData.hours %} - {% endif %} +{% if hourly %} {% set numSteps = hourly | calcNumEntries %} {% set currentStep = 0 %}
@@ -29,4 +26,4 @@ {% endif %} - + diff --git a/modules/default/weather/providers/README.md b/modules/default/weather/providers/README.md index 02cef8dd..f6e5d732 100755 --- a/modules/default/weather/providers/README.md +++ b/modules/default/weather/providers/README.md @@ -29,16 +29,23 @@ WeatherProvider.register("yourprovider", { #### `fetchCurrentWeather()` -This method is called when the weather module tries to fetch the current weather of your provider. The implementation of this method is required. +This method is called when the weather module tries to fetch the current weather of your provider. The implementation of this method is required for current weather support. The implementation can make use of the already implemented function `this.fetchData(url, method, data);`, which is returning a promise. After the response is processed, the current weather information (as a [WeatherObject](#weatherobject)) needs to be set with `this.setCurrentWeather(currentWeather);`. It will then automatically refresh the module DOM with the new data. #### `fetchWeatherForecast()` -This method is called when the weather module tries to fetch the weather of your provider. The implementation of this method is required. +This method is called when the weather module tries to fetch the weather of your provider. The implementation of this method is required for forecast support. The implementation can make use of the already implemented function `this.fetchData(url, method, data);`, which is returning a promise. -After the response is processed, the weather forecast information (as an array of [WeatherObject](#weatherobject)s) needs to be set with `this.setCurrentWeather(forecast);`. +After the response is processed, the weather forecast information (as an array of [WeatherObject](#weatherobject)s) needs to be set with `this.setWeatherForecast(forecast);`. +It will then automatically refresh the module DOM with the new data. + +#### `fetchWeatherHourly()` + +This method is called when the weather module tries to fetch the weather of your provider. The implementation of this method is required for hourly support. +The implementation can make use of the already implemented function `this.fetchData(url, method, data);`, which is returning a promise. +After the response is processed, the hourly weather forecast information (as an array of [WeatherObject](#weatherobject)s) needs to be set with `this.setWeatherHourly(forecast);`. It will then automatically refresh the module DOM with the new data. ### Weather Provider instance methods @@ -63,6 +70,10 @@ This returns a WeatherDay object for the current weather. This returns an array of WeatherDay objects for the weather forecast. +#### `weatherHourly()` + +This returns an array of WeatherDay objects for the hourly weather forecast. + #### `fetchedLocation()` This returns the name of the fetched location or an empty string. @@ -75,6 +86,10 @@ Set the currentWeather and notify the delegate that new information is available Set the weatherForecastArray and notify the delegate that new information is available. +#### `setWeatherHourly(weatherHourlyArray)` + +Set the weatherHourlyArray and notify the delegate that new information is available. + #### `setFetchedLocation(name)` Set the fetched location name. diff --git a/modules/default/weather/providers/darksky.js b/modules/default/weather/providers/darksky.js index b2bf4e78..ea073887 100755 --- a/modules/default/weather/providers/darksky.js +++ b/modules/default/weather/providers/darksky.js @@ -15,6 +15,15 @@ WeatherProvider.register("darksky", { // Not strictly required, but helps for debugging. providerName: "Dark Sky", + // Set the default config properties that is specific to this provider + defaults: { + apiBase: "https://cors-anywhere.herokuapp.com/https://api.darksky.net", + weatherEndpoint: "/forecast", + apiKey: "", + lat: 0, + lon: 0 + }, + units: { imperial: "us", metric: "si" diff --git a/modules/default/weather/providers/openweathermap.js b/modules/default/weather/providers/openweathermap.js index 2fde7139..66f31703 100755 --- a/modules/default/weather/providers/openweathermap.js +++ b/modules/default/weather/providers/openweathermap.js @@ -14,6 +14,18 @@ WeatherProvider.register("openweathermap", { // But for debugging (and future alerts) it would be nice to have the real name. providerName: "OpenWeatherMap", + // Set the default config properties that is specific to this provider + defaults: { + apiVersion: "2.5", + apiBase: "https://api.openweathermap.org/data/", + weatherEndpoint: "/weather", + locationID: false, + location: false, + lat: 0, + lon: 0, + apiKey: "" + }, + // Overwrite the fetchCurrentWeather method. fetchCurrentWeather() { this.fetchData(this.getUrl()) @@ -56,8 +68,8 @@ WeatherProvider.register("openweathermap", { .finally(() => this.updateAvailable()); }, - // Overwrite the fetchWeatherData method. - fetchWeatherData() { + // Overwrite the fetchWeatherHourly method. + fetchWeatherHourly() { this.fetchData(this.getUrl()) .then((data) => { if (!data) { @@ -69,7 +81,7 @@ WeatherProvider.register("openweathermap", { this.setFetchedLocation(`(${data.lat},${data.lon})`); const weatherData = this.generateWeatherObjectsFromOnecall(data); - this.setWeatherData(weatherData); + this.setWeatherHourly(weatherData.hours); }) .catch(function (request) { Log.error("Could not load data ... ", request); @@ -77,6 +89,18 @@ WeatherProvider.register("openweathermap", { .finally(() => this.updateAvailable()); }, + /** + * Overrides method for setting config to check if endpoint is correct for hourly + * + * @param config + */ + setConfig(config) { + this.config = config; + if (this.config.type === "hourly") { + this.config.weatherEndpoint = "/onecall"; + } + }, + /** OpenWeatherMap Specific Methods - These are not part of the default provider methods */ /* * Gets the complete url for the request diff --git a/modules/default/weather/providers/smhi.js b/modules/default/weather/providers/smhi.js index cdbaf013..a27b1300 100644 --- a/modules/default/weather/providers/smhi.js +++ b/modules/default/weather/providers/smhi.js @@ -14,6 +14,13 @@ WeatherProvider.register("smhi", { providerName: "SMHI", + // Set the default config properties that is specific to this provider + defaults: { + lat: 0, + lon: 0, + precipitationValue: "pmedian" + }, + /** * Implements method in interface for fetching current weather */ @@ -55,7 +62,7 @@ WeatherProvider.register("smhi", { this.config = config; if (!config.precipitationValue || ["pmin", "pmean", "pmedian", "pmax"].indexOf(config.precipitationValue) == -1) { console.log("invalid or not set: " + config.precipitationValue); - config.precipitationValue = "pmedian"; + config.precipitationValue = this.defaults.precipitationValue; } }, diff --git a/modules/default/weather/providers/ukmetoffice.js b/modules/default/weather/providers/ukmetoffice.js index b71ced88..11cee48f 100755 --- a/modules/default/weather/providers/ukmetoffice.js +++ b/modules/default/weather/providers/ukmetoffice.js @@ -14,6 +14,13 @@ WeatherProvider.register("ukmetoffice", { // But for debugging (and future alerts) it would be nice to have the real name. providerName: "UK Met Office", + // Set the default config properties that is specific to this provider + defaults: { + apiBase: "http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/json/", + locationID: false, + apiKey: "" + }, + units: { imperial: "us", metric: "si" diff --git a/modules/default/weather/providers/ukmetofficedatahub.js b/modules/default/weather/providers/ukmetofficedatahub.js index af2e09c6..03e5fa5a 100644 --- a/modules/default/weather/providers/ukmetofficedatahub.js +++ b/modules/default/weather/providers/ukmetofficedatahub.js @@ -44,6 +44,16 @@ WeatherProvider.register("ukmetofficedatahub", { // Set the name of the provider. providerName: "UK Met Office (DataHub)", + // Set the default config properties that is specific to this provider + defaults: { + apiBase: "https://api-metoffice.apiconnect.ibmcloud.com/metoffice/production/v0/forecasts/point/", + apiKey: "", + apiSecret: "", + lat: 0, + lon: 0, + windUnits: "mph" + }, + // Build URL with query strings according to DataHub API (https://metoffice.apiconnect.ibmcloud.com/metoffice/production/api) getUrl(forecastType) { let queryStrings = "?"; diff --git a/modules/default/weather/providers/weatherbit.js b/modules/default/weather/providers/weatherbit.js index 6bd3508e..afca0c83 100644 --- a/modules/default/weather/providers/weatherbit.js +++ b/modules/default/weather/providers/weatherbit.js @@ -14,6 +14,15 @@ WeatherProvider.register("weatherbit", { // Not strictly required, but helps for debugging. providerName: "Weatherbit", + // Set the default config properties that is specific to this provider + defaults: { + apiBase: "https://api.weatherbit.io/v2.0", + weatherEndpoint: "/current", + apiKey: "", + lat: 0, + lon: 0 + }, + units: { imperial: "I", metric: "M" diff --git a/modules/default/weather/providers/weathergov.js b/modules/default/weather/providers/weathergov.js index 86837763..ee418edc 100755 --- a/modules/default/weather/providers/weathergov.js +++ b/modules/default/weather/providers/weathergov.js @@ -19,6 +19,14 @@ WeatherProvider.register("weathergov", { // But for debugging (and future alerts) it would be nice to have the real name. providerName: "Weather.gov", + // Set the default config properties that is specific to this provider + defaults: { + apiBase: "https://api.weatherbit.io/v2.0", + weatherEndpoint: "/forecast", + lat: 0, + lon: 0 + }, + // Flag all needed URLs availability configURLs: false, diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index defe0ae5..50c73031 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -12,10 +12,6 @@ Module.register("weather", { weatherProvider: "openweathermap", roundTemp: false, type: "current", // current, forecast, daily (equivalent to forecast), hourly (only with OpenWeatherMap /onecall endpoint) - lat: 0, - lon: 0, - location: false, - locationID: false, units: config.units, useKmh: false, tempUnits: config.units, @@ -40,12 +36,6 @@ Module.register("weather", { fade: true, fadePoint: 0.25, // Start on 1/4th of the list. initialLoadDelay: 0, // 0 seconds delay - retryDelay: 2500, - apiKey: "", - apiSecret: "", - apiVersion: "2.5", - apiBase: "https://api.openweathermap.org/data/", // TODO: this should not be part of the weather.js file, but should be contained in the openweatherprovider - weatherEndpoint: "/weather", appendLocationNameToHeader: true, calendarClass: "calendar", tableClass: "small", @@ -130,8 +120,9 @@ Module.register("weather", { case "daily": case "forecast": return `forecast.njk`; + //Make the invalid values use the "Loading..." from forecast default: - return `${this.config.type.toLowerCase()}.njk`; + return `forecast.njk`; } }, @@ -141,7 +132,7 @@ Module.register("weather", { config: this.config, current: this.weatherProvider.currentWeather(), forecast: this.weatherProvider.weatherForecast(), - weatherData: this.weatherProvider.weatherData(), + hourly: this.weatherProvider.weatherHourly(), indoor: { humidity: this.indoorHumidity, temperature: this.indoorTemperature @@ -167,12 +158,19 @@ Module.register("weather", { } setTimeout(() => { - if (this.config.weatherEndpoint === "/onecall") { - this.weatherProvider.fetchWeatherData(); - } else if (this.config.type === "forecast") { - this.weatherProvider.fetchWeatherForecast(); - } else { - this.weatherProvider.fetchCurrentWeather(); + switch (this.config.type.toLowerCase()) { + case "current": + this.weatherProvider.fetchCurrentWeather(); + break; + case "hourly": + this.weatherProvider.fetchWeatherHourly(); + break; + case "daily": + case "forecast": + this.weatherProvider.fetchWeatherForecast(); + break; + default: + Log.error(`Invalid type ${this.config.type} configured (must be one of 'current', 'hourly', 'daily' or 'forecast')`); } }, nextLoad); }, diff --git a/modules/default/weather/weatherprovider.js b/modules/default/weather/weatherprovider.js index 8642e9a4..87667fd9 100644 --- a/modules/default/weather/weatherprovider.js +++ b/modules/default/weather/weatherprovider.js @@ -11,12 +11,13 @@ var WeatherProvider = Class.extend({ // Weather Provider Properties providerName: null, + defaults: {}, // The following properties have accessor methods. // Try to not access them directly. currentWeatherObject: null, weatherForecastArray: null, - weatherDataObject: null, + weatherHourlyArray: null, fetchedLocationName: null, // The following properties will be set automatically. @@ -57,10 +58,10 @@ var WeatherProvider = Class.extend({ Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchWeatherForecast method.`); }, - // This method should start the API request to fetch the weather forecast. + // This method should start the API request to fetch the weather hourly. // This method should definitely be overwritten in the provider. - fetchWeatherData: function () { - Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchWeatherData method.`); + fetchWeatherHourly: function () { + Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchWeatherHourly method.`); }, // This returns a WeatherDay object for the current weather. @@ -74,8 +75,8 @@ var WeatherProvider = Class.extend({ }, // This returns an object containing WeatherDay object(s) depending on the type of call. - weatherData: function () { - return this.weatherDataObject; + weatherHourly: function () { + return this.weatherHourlyArray; }, // This returns the name of the fetched location or an empty string. @@ -95,9 +96,9 @@ var WeatherProvider = Class.extend({ this.weatherForecastArray = weatherForecastArray; }, - // Set the weatherDataObject and notify the delegate that new information is available. - setWeatherData: function (weatherDataObject) { - this.weatherDataObject = weatherDataObject; + // Set the weatherHourlyArray and notify the delegate that new information is available. + setWeatherHourly: function (weatherHourlyArray) { + this.weatherHourlyArray = weatherHourlyArray; }, // Set the fetched location name. @@ -154,10 +155,11 @@ WeatherProvider.register = function (providerIdentifier, providerDetails) { WeatherProvider.initialize = function (providerIdentifier, delegate) { providerIdentifier = providerIdentifier.toLowerCase(); - var provider = new WeatherProvider.providers[providerIdentifier](); + const provider = new WeatherProvider.providers[providerIdentifier](); + const config = Object.assign({}, provider.defaults, delegate.config); provider.delegate = delegate; - provider.setConfig(delegate.config); + provider.setConfig(config); provider.providerIdentifier = providerIdentifier; if (!provider.providerName) {