diff --git a/CHANGELOG.md b/CHANGELOG.md index 03f7360fd9..71e52458c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ _This release is scheduled to be released on 2023-10-01._ - Added UV Index support to OpenWeatherMap - Added 'hideDuplicates' flag to the calendar module +- Added `allowOverrideNotification` to weather module to enable sending current weather objects with the `CURRENT_WEATHER_OVERRIDE` notification to supplement/replace the current weather displayed ### Removed diff --git a/modules/default/weather/providers/overrideWrapper.js b/modules/default/weather/providers/overrideWrapper.js new file mode 100644 index 0000000000..e529efe4c7 --- /dev/null +++ b/modules/default/weather/providers/overrideWrapper.js @@ -0,0 +1,112 @@ +/* global Class, WeatherObject */ + +/* + * Wrapper class to enable overrides of currentOverrideWeatherObject. + * + * Sits between the weather.js module and the provider implementations to allow us to + * combine the incoming data from the CURRENT_WEATHER_OVERRIDE notification with the + * existing data received from the current api provider. If no notifications have + * been received then the api provider's data is used. + * + * The intent is to allow partial WeatherObjects from local sensors to augment or + * replace the WeatherObjects from the api providers. + * + * This class shares the signature of WeatherProvider, and passes any methods not + * concerning the current weather directly to the api provider implementation that + * is currently in use. + */ +const OverrideWrapper = Class.extend({ + baseProvider: null, + providerName: "localWrapper", + notificationWeatherObject: null, + currentOverrideWeatherObject: null, + + init(baseProvider) { + this.baseProvider = baseProvider; + + // Binding the scope of current weather functions so any fetchData calls with + // setCurrentWeather nested in them call this classes implementation instead + // of the provider's default + this.baseProvider.setCurrentWeather = this.setCurrentWeather.bind(this); + this.baseProvider.currentWeather = this.currentWeather.bind(this); + }, + + /* Unchanged Api Provider Methods */ + + setConfig(config) { + this.baseProvider.setConfig(config); + }, + start() { + this.baseProvider.start(); + }, + fetchCurrentWeather() { + this.baseProvider.fetchCurrentWeather(); + }, + fetchWeatherForecast() { + this.baseProvider.fetchWeatherForecast(); + }, + fetchWeatherHourly() { + this.baseProvider.fetchEatherHourly(); + }, + weatherForecast() { + this.baseProvider.weatherForecast(); + }, + weatherHourly() { + this.baseProvider.weatherHourly(); + }, + fetchedLocation() { + this.baseProvider.fetchedLocation(); + }, + setWeatherForecast(weatherForecastArray) { + this.baseProvider.setWeatherForecast(weatherForecastArray); + }, + setWeatherHourly(weatherHourlyArray) { + this.baseProvider.setWeatherHourly(weatherHourlyArray); + }, + setFetchedLocation(name) { + this.baseProvider.setFetchedLocation(name); + }, + updateAvailable() { + this.baseProvider.updateAvailable(); + }, + async fetchData(url, type = "json", requestHeaders = undefined, expectedResponseHeaders = undefined) { + this.baseProvider.fetchData(url, type, requestHeaders, expectedResponseHeaders); + }, + + /* Override Methods */ + + /** + * Override to return this scope's + * @returns {WeatherObject} The current weather object. May or may not contain overridden data. + */ + currentWeather() { + return this.currentOverrideWeatherObject; + }, + + /** + * Override to combine the overrideWeatherObejct provided in the + * notificationReceived method with the currentOverrideWeatherObject provided by the + * api provider fetchData implementation. + * @param {WeatherObject} currentWeatherObject - the api provider weather object + */ + setCurrentWeather(currentWeatherObject) { + this.currentOverrideWeatherObject = Object.assign(currentWeatherObject, this.notificationWeatherObject); + }, + + /** + * Updates the overrideWeatherObject, calls setCurrentWeather to combine it with + * the existing current weather object provided by the base provider, and signals + * that an update is ready. + * @param {WeatherObject} payload - the weather object received from the CURRENT_WEATHER_OVERRIDE + * notification. Represents information to augment the + * existing currentOverrideWeatherObject with. + */ + notificationReceived(payload) { + this.notificationWeatherObject = payload; + + // setCurrentWeather combines the newly received notification weather with + // the existing weather object we return for current weather + this.setCurrentWeather(this.currentOverrideWeatherObject); + this.updateAvailable(); + } +}); diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index 08f754c270..701e151622 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -23,6 +23,7 @@ Module.register("weather", { showHumidity: false, showIndoorHumidity: false, showIndoorTemperature: false, + allowOverrideNotification: false, showPeriod: true, showPeriodUpper: false, showPrecipitationAmount: false, @@ -61,7 +62,7 @@ Module.register("weather", { // Return the scripts that are necessary for the weather module. getScripts: function () { - return ["moment.js", this.file("../utils.js"), "weatherutils.js", "weatherprovider.js", "weatherobject.js", "suncalc.js", this.file(`providers/${this.config.weatherProvider.toLowerCase()}.js`)]; + return ["moment.js", this.file("../utils.js"), "weatherutils.js", "weatherobject.js", this.file("providers/overrideWrapper.js"), "weatherprovider.js", "suncalc.js", this.file(`providers/${this.config.weatherProvider.toLowerCase()}.js`)]; }, // Override getHeader method. @@ -119,6 +120,8 @@ Module.register("weather", { } else if (notification === "INDOOR_HUMIDITY") { this.indoorHumidity = this.roundValue(payload); this.updateDom(300); + } else if (notification === "CURRENT_WEATHER_OVERRIDE" && this.config.allowOverrideNotification) { + this.weatherProvider.notificationReceived(payload); } }, diff --git a/modules/default/weather/weatherprovider.js b/modules/default/weather/weatherprovider.js index 662f87befd..d3bdb78d69 100644 --- a/modules/default/weather/weatherprovider.js +++ b/modules/default/weather/weatherprovider.js @@ -1,4 +1,4 @@ -/* global Class, performWebRequest */ +/* global Class, performWebRequest, OverrideWrapper */ /* MagicMirror² * Module: Weather @@ -164,5 +164,9 @@ WeatherProvider.initialize = function (providerIdentifier, delegate) { provider.providerName = pi; } + if (config.allowOverrideNotification) { + return new OverrideWrapper(provider); + } + return provider; };