From ef172592b8b1657a92ceea5a56b6b0629abf6046 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Thu, 21 Sep 2017 16:38:18 +0200 Subject: [PATCH 01/28] A first setup of the new Weather Module --- modules/default/defaultmodules.js | 3 +- .../weather/providers/openweathermap.js | 120 ++++++++++++++++ modules/default/weather/weather.js | 62 ++++++++ modules/default/weather/weatherday.js | 24 ++++ modules/default/weather/weatherprovider.js | 135 ++++++++++++++++++ 5 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 modules/default/weather/providers/openweathermap.js create mode 100644 modules/default/weather/weather.js create mode 100644 modules/default/weather/weatherday.js create mode 100644 modules/default/weather/weatherprovider.js diff --git a/modules/default/defaultmodules.js b/modules/default/defaultmodules.js index fccf3c52..656ba6b8 100644 --- a/modules/default/defaultmodules.js +++ b/modules/default/defaultmodules.js @@ -16,7 +16,8 @@ var defaultModules = [ "helloworld", "newsfeed", "weatherforecast", - "updatenotification" + "updatenotification", + "weather" ]; /*************** DO NOT EDIT THE LINE BELOW ***************/ diff --git a/modules/default/weather/providers/openweathermap.js b/modules/default/weather/providers/openweathermap.js new file mode 100644 index 00000000..4f3651a6 --- /dev/null +++ b/modules/default/weather/providers/openweathermap.js @@ -0,0 +1,120 @@ +/* global WeatherProvider, WeatherDay */ + +/* Magic Mirror + * Module: Weather + * + * By Michael Teeuw http://michaelteeuw.nl + * MIT Licensed. + * + * This class is the blueprint for a weather provider. + */ + +WeatherProvider.register("openweathermap", { + + // Set the name of the provider. + // This isn't strictly nessecery, since it will fallback to the provider identifier + // But for debugging (and future alerts) it would be nice to have the real name. + providerName: "OpenWeatherMap", + + // Overwrite the fetchCurrentWeather method. + fetchCurrentWeather: function() { + var apiVersion = "2.5" + var apiBase = "http://api.openweathermap.org/data/" + var weatherEndpoint = "weather" + + var url = apiBase + apiVersion + "/" + weatherEndpoint + this.getParams() + + this.fetchData(url) + .then(data => { + Log.log(data) + + if (!data || !data.main || typeof data.main.temp === "undefined") { + // Did not receive usable new data. + // Maybe this needs a better check? + return; + } + + var currentWeather = this.generateWeatherDayFromCurrentWeather(data) + this.setCurrentWeather(currentWeather) + }) + .catch(function(request) { + Log.error("Could not load data ... ", request) + }) + }, + + + /** OpenWeatherMap Specific Methods - These are not part of the default provider methods */ + + + /* + * Generate a WeatherDay based on currentWeatherInformation + */ + generateWeatherDayFromCurrentWeather: function(currentWeatherData) { + var currentWeather = new WeatherDay() + + currentWeather.humidity = parseFloat(currentWeatherData.main.humidity) + currentWeather.temperature = parseFloat(currentWeatherData.main.temp) + currentWeather.windSpeed = parseFloat(currentWeatherData.wind.speed) + currentWeather.windDirection = currentWeatherData.wind.deg + currentWeather.weatherType = this.convertWeatherType(currentWeatherData.weather[0].icon) + currentWeather.sunrise = new Date(currentWeatherData.sys.sunrise * 1000) + currentWeather.sunset = new Date(currentWeatherData.sys.sunset * 1000) + + return currentWeather + }, + + /* + * Convert the OpenWeatherMap icons to a more usable name. + */ + convertWeatherType: function(weatherType) { + var weatherTypes = { + "01d": "day-sunny", + "02d": "day-cloudy", + "03d": "cloudy", + "04d": "cloudy-windy", + "09d": "showers", + "10d": "rain", + "11d": "thunderstorm", + "13d": "snow", + "50d": "fog", + "01n": "night-clear", + "02n": "night-cloudy", + "03n": "night-cloudy", + "04n": "night-cloudy", + "09n": "night-showers", + "10n": "night-rain", + "11n": "night-thunderstorm", + "13n": "night-snow", + "50n": "night-alt-cloudy-windy" + } + + return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null + }, + + /* getParams(compliments) + * Generates an url with api parameters based on the config. + * + * return String - URL params. + */ + getParams: function() { + var params = "?"; + if(this.config.locationID) { + params += "id=" + this.config.locationID; + } else if(this.config.location) { + params += "q=" + this.config.location; + } else if (this.firstEvent && this.firstEvent.geo) { + params += "lat=" + this.firstEvent.geo.lat + "&lon=" + this.firstEvent.geo.lon + } else if (this.firstEvent && this.firstEvent.location) { + params += "q=" + this.firstEvent.location; + } else { + this.hide(this.config.animationSpeed, {lockString:this.identifier}); + return; + } + + params += "&units=" + this.config.units; + params += "&lang=" + this.config.lang; + params += "&APPID=" + this.config.apiKey; + + return params; + }, +}); \ No newline at end of file diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js new file mode 100644 index 00000000..2be3ff0b --- /dev/null +++ b/modules/default/weather/weather.js @@ -0,0 +1,62 @@ +/* global Module, WeatherProvider */ + +/* Magic Mirror + * Module: Weather + * + * By Michael Teeuw http://michaelteeuw.nl + * MIT Licensed. + */ + +Module.register("weather",{ + + // Default module config. + defaults: { + foo: "bar", + weatherProvider: "openweathermap" + }, + + // Module properties. + weatherProvider: null, + + // Return the scripts that are nessecery for the weather module. + getScripts: function () { + var scripts = [ + "weatherprovider.js", + "weatherday.js" + ]; + + // Add the provider file to the required scripts. + scripts.push(this.file("providers/" + this.config.weatherProvider.toLowerCase() + ".js")) + + return scripts + }, + + // Start the weather module. + start: function () { + // Initialize the weather provider. + this.weatherProvider = WeatherProvider.initialize(this.config.weatherProvider, this) + + // Let the weather provider know we are starting. + this.weatherProvider.start() + + // Fetch the current weather. This is something we need to schedule. + this.weatherProvider.fetchCurrentWeather() + }, + + // Generate the dom. This is now pretty simple for debugging. + getDom: function() { + var wrapper = document.createElement("div") + + wrapper.innerHTML += "Name: " + this.weatherProvider.providerName + "
" + wrapper.innerHTML += JSON.stringify(this.weatherProvider.currentWeather()) + + return wrapper + }, + + // What to do when the weather provider has new information available? + updateAvailable: function() { + Log.log("New weather information available.") + console.info(this.weatherProvider.currentWeather()) + this.updateDom(0); + } +}); diff --git a/modules/default/weather/weatherday.js b/modules/default/weather/weatherday.js new file mode 100644 index 00000000..c8f63057 --- /dev/null +++ b/modules/default/weather/weatherday.js @@ -0,0 +1,24 @@ +/* global Class */ + +/* Magic Mirror + * Module: Weather + * + * By Michael Teeuw http://michaelteeuw.nl + * MIT Licensed. + * + * This class is the blueprint for a day which includes weather information. + */ + +// Currently this is focused on the information which is nessecery for the current weather. +// As soon as we start implementing the forecast, mode properties will be added. + +class WeatherDay { + constructor() { + this.windSpeed = null + this.windDirection = null + this.sunrise = null + this.sunset = null + this.temperature = null + this.weatherType = null + } +}; \ No newline at end of file diff --git a/modules/default/weather/weatherprovider.js b/modules/default/weather/weatherprovider.js new file mode 100644 index 00000000..08489aeb --- /dev/null +++ b/modules/default/weather/weatherprovider.js @@ -0,0 +1,135 @@ +/* global Class */ + +/* Magic Mirror + * Module: Weather + * + * By Michael Teeuw http://michaelteeuw.nl + * MIT Licensed. + * + * This class is the blueprint for a weather provider. + */ + + +/** + * Base BluePrint for the WeatherProvider + */ +var WeatherProvider = Class.extend({ + // Weather Provider Properties + providerName: null, + + // The following properties have accestor methods. + // Try to not access them directly. + currentWeatherDay: null, + weatherForecastArray: null, + + // The following properties will be set automaticly. + // You do not need to overwrite these properties. + config: null, + delegate: null, + providerIdentifier: null, + + + // Weather Provider Methods + // All the following methods can be overwrited, although most are good as they are. + + // Called when a weather provider is initialized. + init: function(config) { + this.config = config; + Log.info("Weather provider: " + this.providerName + " initialized.") + }, + + // 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) + }, + + // Called when the weather provider is about to start. + start: function(config) { + Log.info("Weather provider: " + this.providerName + " started.") + }, + + // This method should start the API request to fetch the current weather. + // This method should definetly be overwritten in the provider. + fetchCurrentWeather: function() { + Log.warn("Weather provider: " + this.providerName + " does not subclass the fetchCurrentWeather method.") + }, + + // This method should start the API request to fetch the weather forecast. + // This method should definetly be overwritten in the provider. + fetchWeatherForeCast: function() { + Log.warn("Weather provider: " + this.providerName + " does not subclass the fetchWeatherForeCast method.") + }, + + // This returns a WeatherDay object for the current weather. + currentWeather: function() { + return this.currentWeatherDay + }, + + // This returns an array of WeatherDay objects for the weather forecast. + weatherForecast: function() { + return this.weatherForecastArray + }, + + // Set the currentWeather and notify the delegate that new information is availabe. + setCurrentWeather: function(currentWeatherDay) { + // We should check here if we are passing a WeatherDay + this.currentWeatherDay = currentWeatherDay + + this.updateAvailable() + }, + + // Notify the delegate that new weather is available. + updateAvailable: function() { + this.delegate.updateAvailable(this) + }, + + // A convinience function to make requests. It returns a promise. + fetchData: function(url, method = "GET", data = null) { + return new Promise(function(resolve, reject) { + var request = new XMLHttpRequest(); + request.open(method, url, true); + request.onreadystatechange = function() { + if (this.readyState === 4) { + if (this.status === 200) { + resolve(JSON.parse(this.response)); + } else { + reject(request) + } + } + }; + request.send(); + }) + } +}); + +/** + * Collection of registered weather providers. + */ +WeatherProvider.providers = [] + +/** + * Static method to register a new weather provider. + */ +WeatherProvider.register = function(providerIdentifier, providerDetails) { + WeatherProvider.providers[providerIdentifier.toLowerCase()] = WeatherProvider.extend(providerDetails) +} + +/** + * Static method to initialize a new weather provider. + */ +WeatherProvider.initialize = function(providerIdentifier, delegate) { + providerIdentifier = providerIdentifier.toLowerCase() + + var provider = new WeatherProvider.providers[providerIdentifier]() + + provider.delegate = delegate + provider.setConfig(delegate.config) + + provider.providerIdentifier = providerIdentifier; + if (!provider.providerName) { + provider.providerName = providerIdentifier + } + + return provider; +} \ No newline at end of file From 7f2e643e6249f8e91be1bfc7e8edfa66b4ed385f Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Thu, 21 Sep 2017 20:06:42 -0400 Subject: [PATCH 02/28] Add Dark Sky Module --- modules/default/weather/providers/darksky.js | 61 ++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 modules/default/weather/providers/darksky.js diff --git a/modules/default/weather/providers/darksky.js b/modules/default/weather/providers/darksky.js new file mode 100644 index 00000000..43a87fd1 --- /dev/null +++ b/modules/default/weather/providers/darksky.js @@ -0,0 +1,61 @@ +/* global WeatherProvider, WeatherDay */ + +/* Magic Mirror + * Module: Weather + * Provider: Dark Sky + * + * By Nicholas Hubbard https://github.com/nhubbard + * MIT Licensed + * + * This class is a provider for Dark Sky. + */ +WeatherProvider.register("darksky", { + // Set the name of the provider. + // Not strictly required, but helps for debugging. + providerName: "Dark Sky", + // Implement fetchCurrentWeather. + fetchCurrentWeather: function() { + // Create a URL from the config and base URL. + var url = `https://api.darksky.net/forecast/${this.config.apiKey}/${this.config.latLong}`; + // Run the request. + this.fetchData(url).then(data => { + Log.log(data); + if(!data || !data.main || typeof data.main.temp === "undefined") { + // No usable data? + return; + } + var currentWeather = this.generateWeatherDayFromCurrentWeather(data); + this.setCurrentWeather(currentWeather); + }).catch(function(request) { + Log.error("Could not load data!", request); + }); + }, + // Implement WeatherDay generator. + generateWeatherDayFromCurrentWeather: function(currentWeatherData) { + var currentWeather = new WeatherDay(); + currentWeather.humidity = parseFloat(currentWeatherData.currently.humidity); + currentWeather.temperature = parseFloat(currentWeatherData.currently.temperature); + currentWeather.windSpeed = parseFloat(currentWeatherData.currently.windSpeed); + currentWeather.windDirection = currentWeatherData.currently.windBearing; + currentWeather.weatherType = this.currentWeatherType(currentWeatherData.currently.icon); + currentWeather.sunrise = new Date(currentWeatherData.daily.data[0].sunriseTime); + currentWeather.sunset = new Date(currentWeatherData.daily.data[0].sunsetTime); + return currentWeather; + }, + // Map icons from Dark Sky to our icons. + convertWeatherType: function(weatherType) { + var weatherTypes = { + "clear-day": "day-sunny", + "clear-night": "night-clear", + "rain": "rain", + "snow": "snow", + "sleet": "snow", + "wind": "wind", + "fog": "fog", + "cloudy": "cloudy", + "partly-cloudy-day": "day-cloudy", + "partly-cloudy-night": "night-cloudy" + }; + return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null; + } +}); \ No newline at end of file From 2bf18d8bdab364636b2589a981c7e7fb395bc669 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Thu, 21 Sep 2017 20:12:25 -0400 Subject: [PATCH 03/28] Fix Indentation? --- js/class.js | 24 +++++++++---------- .../updatenotification/updatenotification.js | 4 +--- tests/configs/check_config.js | 2 +- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/js/class.js b/js/class.js index 3c44250e..d054a73e 100644 --- a/js/class.js +++ b/js/class.js @@ -31,22 +31,22 @@ // Check if we're overwriting an existing function prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn) { - return function() { - var tmp = this._super; + return function() { + var tmp = this._super; - // Add a new ._super() method that is the same method - // but on the super-class - this._super = _super[name]; + // Add a new ._super() method that is the same method + // but on the super-class + this._super = _super[name]; - // The method only need to be bound temporarily, so we - // remove it when we're done executing - var ret = fn.apply(this, arguments); - this._super = tmp; + // The method only need to be bound temporarily, so we + // remove it when we're done executing + var ret = fn.apply(this, arguments); + this._super = tmp; - return ret; - }; - })(name, prop[name]) : prop[name]; + return ret; + }; + })(name, prop[name]) : prop[name]; } // The dummy class constructor diff --git a/modules/default/updatenotification/updatenotification.js b/modules/default/updatenotification/updatenotification.js index f663f593..9772f06b 100644 --- a/modules/default/updatenotification/updatenotification.js +++ b/modules/default/updatenotification/updatenotification.js @@ -58,9 +58,7 @@ Module.register("updatenotification", { wrapper.appendChild(message); var subtext = document.createElement("div"); - subtext.innerHTML = this.translate("UPDATE_INFO") - .replace("COMMIT_COUNT", this.status.behind + " " + ((this.status.behind == 1)? "commit" : "commits")) - .replace("BRANCH_NAME", this.status.current); + subtext.innerHTML = this.translate("UPDATE_INFO").replace("COMMIT_COUNT", this.status.behind + " " + ((this.status.behind == 1)? "commit" : "commits")).replace("BRANCH_NAME", this.status.current); subtext.className = "xsmall dimmed"; wrapper.appendChild(subtext); } diff --git a/tests/configs/check_config.js b/tests/configs/check_config.js index fa294761..85cc6a48 100644 --- a/tests/configs/check_config.js +++ b/tests/configs/check_config.js @@ -48,7 +48,7 @@ try { // In case the there errors show messages and // return console.info(Utils.colors.info("Checking file... ", configFileName)); - // I'm not sure if all ever is utf-8 +// I'm not sure if all ever is utf-8 fs.readFile(configFileName, "utf-8", function(err, data) { if (err) {throw err;} v.JSHINT(data); // Parser by jshint From 5b1462a3e878fac01ba930cb1c934ac77456a3f2 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Fri, 22 Sep 2017 10:37:39 +0200 Subject: [PATCH 04/28] Add readme. --- modules/default/weather/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 modules/default/weather/README.md diff --git a/modules/default/weather/README.md b/modules/default/weather/README.md new file mode 100644 index 00000000..325732cc --- /dev/null +++ b/modules/default/weather/README.md @@ -0,0 +1,17 @@ +# Weather Module + +This module is aimed to be the replacement for the current `currentweather` and `weatherforcast` modules. The module will be configurable to be used as a current weather view, or to show the forecast. This way the module can be used twice to fullfil both purposes. + +The biggest cange is the use of weather providers. This way we are not bound to one API source. And users can choose which API they want to use as their source. Initially the current OpenWeatherMap will be added as the first source, but more will follow soon. + +The module is in a very early stage, and needs a lot of work. It's API isn't set in stone, so keep that in mind when you want to contribute. + +## TODO + +- [ ] Add Current Weather View +- [ ] Add Weather Forecast View +- [ ] Add Forecast API Call +- [ ] Add all original configuration options +- [ ] Add more providers +- [ ] Finish thi Todo list +- [ ] Write the documentation \ No newline at end of file From 713111254b3241b63b1178872dc670611a068c7c Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Fri, 22 Sep 2017 12:26:47 +0200 Subject: [PATCH 05/28] First implementation of the currentWeatherView --- .../weather/providers/openweathermap.js | 24 ++-- modules/default/weather/weather.css | 46 ++++++++ modules/default/weather/weather.js | 103 ++++++++++++++++-- modules/default/weather/weatherday.js | 24 ---- modules/default/weather/weatherobject.js | 60 ++++++++++ modules/default/weather/weatherprovider.js | 8 +- 6 files changed, 213 insertions(+), 52 deletions(-) create mode 100644 modules/default/weather/weather.css delete mode 100644 modules/default/weather/weatherday.js create mode 100644 modules/default/weather/weatherobject.js diff --git a/modules/default/weather/providers/openweathermap.js b/modules/default/weather/providers/openweathermap.js index 4f3651a6..edc27657 100644 --- a/modules/default/weather/providers/openweathermap.js +++ b/modules/default/weather/providers/openweathermap.js @@ -1,4 +1,4 @@ -/* global WeatherProvider, WeatherDay */ +/* global WeatherProvider, WeatherObject */ /* Magic Mirror * Module: Weather @@ -34,7 +34,7 @@ WeatherProvider.register("openweathermap", { return; } - var currentWeather = this.generateWeatherDayFromCurrentWeather(data) + var currentWeather = this.generateWeatherObjectFromCurrentWeather(data) this.setCurrentWeather(currentWeather) }) .catch(function(request) { @@ -47,18 +47,18 @@ WeatherProvider.register("openweathermap", { /* - * Generate a WeatherDay based on currentWeatherInformation + * Generate a WeatherObject based on currentWeatherInformation */ - generateWeatherDayFromCurrentWeather: function(currentWeatherData) { - var currentWeather = new WeatherDay() + generateWeatherObjectFromCurrentWeather: function(currentWeatherData) { + var currentWeather = new WeatherObject() - currentWeather.humidity = parseFloat(currentWeatherData.main.humidity) - currentWeather.temperature = parseFloat(currentWeatherData.main.temp) - currentWeather.windSpeed = parseFloat(currentWeatherData.wind.speed) - currentWeather.windDirection = currentWeatherData.wind.deg - currentWeather.weatherType = this.convertWeatherType(currentWeatherData.weather[0].icon) - currentWeather.sunrise = new Date(currentWeatherData.sys.sunrise * 1000) - currentWeather.sunset = new Date(currentWeatherData.sys.sunset * 1000) + currentWeather.humidity = currentWeatherData.main.humidity ? parseFloat(currentWeatherData.main.humidity) : null + currentWeather.temperature = currentWeatherData.main.temp ? parseFloat(currentWeatherData.main.temp) : null + currentWeather.windSpeed = currentWeatherData.wind.speed ? parseFloat(currentWeatherData.wind.speed) : null + currentWeather.windDirection = currentWeatherData.wind.deg ? currentWeatherData.wind.deg : null + currentWeather.weatherType = currentWeatherData.weather[0].icon ? this.convertWeatherType(currentWeatherData.weather[0].icon) : null + currentWeather.sunrise = currentWeatherData.sys.sunrise ? new Date(currentWeatherData.sys.sunrise * 1000) : null + currentWeather.sunset = currentWeatherData.sys.sunset ? new Date(currentWeatherData.sys.sunset * 1000) : null return currentWeather }, diff --git a/modules/default/weather/weather.css b/modules/default/weather/weather.css new file mode 100644 index 00000000..febc777c --- /dev/null +++ b/modules/default/weather/weather.css @@ -0,0 +1,46 @@ +.weather .weathericon, +.weather .fa-home { + font-size: 75%; + line-height: 65px; + display: inline-block; + -ms-transform: translate(0, -3px); /* IE 9 */ + -webkit-transform: translate(0, -3px); /* Safari */ + transform: translate(0, -3px); +} + +.weather .humidityIcon { + padding-right: 4px; +} + +.weather .humidity-padding { + padding-bottom: 6px; +} + + +.weather .day { + padding-left: 0; + padding-right: 25px; +} + +.weather .weather-icon { + padding-right: 30px; + text-align: center; +} + +.weather .min-temp { + padding-left: 20px; + padding-right: 0; +} + +.weather .rain { + padding-left: 20px; + padding-right: 0; +} + +.weather tr.colored .min-temp { + color: #BCDDFF; +} + +.weather tr.colored .max-temp { + color: #FF8E99; +} diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index 2be3ff0b..a5c410b2 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -11,18 +11,25 @@ Module.register("weather",{ // Default module config. defaults: { - foo: "bar", - weatherProvider: "openweathermap" + updateInterval: 10 * 60 * 1000, + weatherProvider: "openweathermap", + units: config.units, + roundTemp: false }, // Module properties. weatherProvider: null, + // Define required scripts. + getStyles: function() { + return ["weather-icons.css", "weather.css"]; + }, + // Return the scripts that are nessecery for the weather module. getScripts: function () { var scripts = [ "weatherprovider.js", - "weatherday.js" + "weatherobject.js" ]; // Add the provider file to the required scripts. @@ -39,24 +46,96 @@ Module.register("weather",{ // Let the weather provider know we are starting. this.weatherProvider.start() - // Fetch the current weather. This is something we need to schedule. - this.weatherProvider.fetchCurrentWeather() + // Schedule the first update. + this.scheduleUpdate(0) }, // Generate the dom. This is now pretty simple for debugging. getDom: function() { - var wrapper = document.createElement("div") - - wrapper.innerHTML += "Name: " + this.weatherProvider.providerName + "
" - wrapper.innerHTML += JSON.stringify(this.weatherProvider.currentWeather()) - - return wrapper + return this.currentWeatherView(this.weatherProvider.currentWeather()) }, // What to do when the weather provider has new information available? updateAvailable: function() { Log.log("New weather information available.") console.info(this.weatherProvider.currentWeather()) - this.updateDom(0); + this.updateDom(0) + this.scheduleUpdate() + }, + + scheduleUpdate: function(delay = null) { + var nextLoad = this.config.updateInterval; + if (delay !== null && delay >= 0) { + nextLoad = delay; + } + + setTimeout(() => { + // Currently we are fetching the currentWeather. + // In the future, it depends what we want to show. + // So there needs to be some logic here... + // if config.weatherType == 'current', do this... + // if config.weatherType === 'forecast, do that ... + this.weatherProvider.fetchCurrentWeather() + }, nextLoad); + }, + + /* Views */ + + // Generate the current weather view + currentWeatherView: function (currentWeather) { + var wrapper = document.createElement("div") + + if (currentWeather === null) { + return wrapper + } + + // Detail bar. + + var detailBar = document.createElement("div") + + this.addValueToWrapper(detailBar, null, "wi wi-strong-wind dimmed", "span", true) // Wind Icon + this.addValueToWrapper(detailBar, currentWeather.windSpeed ? Math.round(currentWeather.windSpeed) : null) // WindSpeed + this.addValueToWrapper(detailBar, currentWeather.windDirection ? this.translate(currentWeather.cardinalWindDirection()) + "  " : null, "", "sup") // WindDirection + + var now = new Date(); + var sunriseSunsetTime = (currentWeather.sunrise < now && currentWeather.sunset > now) ? currentWeather.sunset : currentWeather.sunrise + var sunriseSunsetIcon = (currentWeather.sunrise < now && currentWeather.sunset > now) ? "wi-sunset" : "wi-sunrise" + this.addValueToWrapper(detailBar, null, "wi dimmed " + sunriseSunsetIcon, "span", true) // Sunrise / Sunset Icon + this.addValueToWrapper(detailBar, moment(sunriseSunsetTime).format("HH:mm")) // Sunrise / Sunset Time + + detailBar.className = "normal medium" + wrapper.appendChild(detailBar) + + // Main info + + var mainInfo = document.createElement("div") + + this.addValueToWrapper(mainInfo, null, "weathericon wi wi-" + currentWeather.weatherType, "span", true) // Wind Icon + this.addValueToWrapper(mainInfo, parseFloat(currentWeather.temperature).toFixed(this.config.roundTemp ? 0 : 1) + "°", "bright" ) // WindSpeed + + mainInfo.className = "large light" + wrapper.appendChild(mainInfo) + + return wrapper + }, + + // A convenience function to add an element to a wrapper with a specific value and class. + addValueToWrapper: function(wrapper, value, classNames, element = "span", forceAdd = false, addSpacer = true) { + if (value === null && !forceAdd) { + return + } + + var valueWrapper = document.createElement(element) + valueWrapper.className = classNames + if (addSpacer) { + valueWrapper.innerHTML = " " + } + + if (value !== null) { + valueWrapper.innerHTML += value + } + + wrapper.appendChild(valueWrapper) } + }); diff --git a/modules/default/weather/weatherday.js b/modules/default/weather/weatherday.js deleted file mode 100644 index c8f63057..00000000 --- a/modules/default/weather/weatherday.js +++ /dev/null @@ -1,24 +0,0 @@ -/* global Class */ - -/* Magic Mirror - * Module: Weather - * - * By Michael Teeuw http://michaelteeuw.nl - * MIT Licensed. - * - * This class is the blueprint for a day which includes weather information. - */ - -// Currently this is focused on the information which is nessecery for the current weather. -// As soon as we start implementing the forecast, mode properties will be added. - -class WeatherDay { - constructor() { - this.windSpeed = null - this.windDirection = null - this.sunrise = null - this.sunset = null - this.temperature = null - this.weatherType = null - } -}; \ No newline at end of file diff --git a/modules/default/weather/weatherobject.js b/modules/default/weather/weatherobject.js new file mode 100644 index 00000000..2525eb6e --- /dev/null +++ b/modules/default/weather/weatherobject.js @@ -0,0 +1,60 @@ +/* global Class */ + +/* Magic Mirror + * Module: Weather + * + * By Michael Teeuw http://michaelteeuw.nl + * MIT Licensed. + * + * This class is the blueprint for a day which includes weather information. + */ + +// Currently this is focused on the information which is nessecery for the current weather. +// As soon as we start implementing the forecast, mode properties will be added. + +class WeatherObject { + constructor() { + this.windSpeed = null + this.windDirection = null + this.sunrise = null + this.sunset = null + this.temperature = null + this.weatherType = null + } + + cardinalWindDirection () { + if (this.windDirection>11.25 && this.windDirection<=33.75){ + return "NNE"; + } else if (this.windDirection > 33.75 && this.windDirection <= 56.25) { + return "NE"; + } else if (this.windDirection > 56.25 && this.windDirection <= 78.75) { + return "ENE"; + } else if (this.windDirection > 78.75 && this.windDirection <= 101.25) { + return "E"; + } else if (this.windDirection > 101.25 && this.windDirection <= 123.75) { + return "ESE"; + } else if (this.windDirection > 123.75 && this.windDirection <= 146.25) { + return "SE"; + } else if (this.windDirection > 146.25 && this.windDirection <= 168.75) { + return "SSE"; + } else if (this.windDirection > 168.75 && this.windDirection <= 191.25) { + return "S"; + } else if (this.windDirection > 191.25 && this.windDirection <= 213.75) { + return "SSW"; + } else if (this.windDirection > 213.75 && this.windDirection <= 236.25) { + return "SW"; + } else if (this.windDirection > 236.25 && this.windDirection <= 258.75) { + return "WSW"; + } else if (this.windDirection > 258.75 && this.windDirection <= 281.25) { + return "W"; + } else if (this.windDirection > 281.25 && this.windDirection <= 303.75) { + return "WNW"; + } else if (this.windDirection > 303.75 && this.windDirection <= 326.25) { + return "NW"; + } else if (this.windDirection > 326.25 && this.windDirection <= 348.75) { + return "NNW"; + } else { + return "N"; + } + } +}; \ No newline at end of file diff --git a/modules/default/weather/weatherprovider.js b/modules/default/weather/weatherprovider.js index 08489aeb..a7ca4cba 100644 --- a/modules/default/weather/weatherprovider.js +++ b/modules/default/weather/weatherprovider.js @@ -19,7 +19,7 @@ var WeatherProvider = Class.extend({ // The following properties have accestor methods. // Try to not access them directly. - currentWeatherDay: null, + currentWeatherObject: null, weatherForecastArray: null, // The following properties will be set automaticly. @@ -63,7 +63,7 @@ var WeatherProvider = Class.extend({ // This returns a WeatherDay object for the current weather. currentWeather: function() { - return this.currentWeatherDay + return this.currentWeatherObject }, // This returns an array of WeatherDay objects for the weather forecast. @@ -72,9 +72,9 @@ var WeatherProvider = Class.extend({ }, // Set the currentWeather and notify the delegate that new information is availabe. - setCurrentWeather: function(currentWeatherDay) { + setCurrentWeather: function(currentWeatherObject) { // We should check here if we are passing a WeatherDay - this.currentWeatherDay = currentWeatherDay + this.currentWeatherObject = currentWeatherObject this.updateAvailable() }, From ff9c6bac0a40e5241890067ea63417a45dcbd4e7 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Fri, 22 Sep 2017 13:26:44 +0200 Subject: [PATCH 06/28] Add a small forecast example. --- .../weather/providers/openweathermap.js | 24 ++++++ modules/default/weather/weather.js | 74 ++++++++++++++++--- modules/default/weather/weatherobject.js | 3 + modules/default/weather/weatherprovider.js | 14 +++- 4 files changed, 101 insertions(+), 14 deletions(-) diff --git a/modules/default/weather/providers/openweathermap.js b/modules/default/weather/providers/openweathermap.js index edc27657..c9e56d2c 100644 --- a/modules/default/weather/providers/openweathermap.js +++ b/modules/default/weather/providers/openweathermap.js @@ -42,6 +42,28 @@ WeatherProvider.register("openweathermap", { }) }, + // Overwrite the fetchCurrentWeather method. + fetchWeatherForecast: function() { + + // I haven't yet implemented the real api call, so let's just generate some random data. + + var forecast = [] + var today = moment() + + for (var i = 0; i < 5; i++ ) { + var weatherObject = new WeatherObject() + + weatherObject.date = moment(today).add(i, "days") + weatherObject.minTemperature = Math.random() * 10 + 10 + weatherObject.maxTemperature = Math.random() * 15 + 10 + + forecast.push(weatherObject) + } + + this.setWeatherForecast(forecast) + }, + + /** OpenWeatherMap Specific Methods - These are not part of the default provider methods */ @@ -52,6 +74,8 @@ WeatherProvider.register("openweathermap", { generateWeatherObjectFromCurrentWeather: function(currentWeatherData) { var currentWeather = new WeatherObject() + currentWeather.date = new Date + currentWeather.humidity = currentWeatherData.main.humidity ? parseFloat(currentWeatherData.main.humidity) : null currentWeather.temperature = currentWeatherData.main.temp ? parseFloat(currentWeatherData.main.temp) : null currentWeather.windSpeed = currentWeatherData.wind.speed ? parseFloat(currentWeatherData.wind.speed) : null diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index a5c410b2..7f33ffa8 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -14,7 +14,8 @@ Module.register("weather",{ updateInterval: 10 * 60 * 1000, weatherProvider: "openweathermap", units: config.units, - roundTemp: false + roundTemp: false, + displayType: "full" //current, forecast, full }, // Module properties. @@ -52,7 +53,17 @@ Module.register("weather",{ // Generate the dom. This is now pretty simple for debugging. getDom: function() { - return this.currentWeatherView(this.weatherProvider.currentWeather()) + switch (this.config.displayType) { + case "current": + return this.currentWeatherView() + break; + case "forecast": + return this.weatherForecastView() + break; + default: + return this.fullWeatherView() + break; + } }, // What to do when the weather provider has new information available? @@ -70,19 +81,27 @@ Module.register("weather",{ } setTimeout(() => { - // Currently we are fetching the currentWeather. - // In the future, it depends what we want to show. - // So there needs to be some logic here... - // if config.weatherType == 'current', do this... - // if config.weatherType === 'forecast, do that ... - this.weatherProvider.fetchCurrentWeather() + switch (this.config.displayType) { + case "current": + this.weatherProvider.fetchCurrentWeather() + break; + case "forecast": + this.weatherProvider.fetchWeatherForecast() + break; + default: + this.weatherProvider.fetchCurrentWeather() + this.weatherProvider.fetchWeatherForecast() + break; + } }, nextLoad); }, /* Views */ // Generate the current weather view - currentWeatherView: function (currentWeather) { + currentWeatherView: function () { + + var currentWeather = this.weatherProvider.currentWeather() var wrapper = document.createElement("div") if (currentWeather === null) { @@ -95,7 +114,7 @@ Module.register("weather",{ this.addValueToWrapper(detailBar, null, "wi wi-strong-wind dimmed", "span", true) // Wind Icon this.addValueToWrapper(detailBar, currentWeather.windSpeed ? Math.round(currentWeather.windSpeed) : null) // WindSpeed - this.addValueToWrapper(detailBar, currentWeather.windDirection ? this.translate(currentWeather.cardinalWindDirection()) + "  " : null, "", "sup") // WindDirection + this.addValueToWrapper(detailBar, currentWeather.windDirection ? this.translate(currentWeather.cardinalWindDirection()) + "  " : " ", "", "sup") // WindDirection var now = new Date(); var sunriseSunsetTime = (currentWeather.sunrise < now && currentWeather.sunset > now) ? currentWeather.sunset : currentWeather.sunrise @@ -111,7 +130,7 @@ Module.register("weather",{ var mainInfo = document.createElement("div") this.addValueToWrapper(mainInfo, null, "weathericon wi wi-" + currentWeather.weatherType, "span", true) // Wind Icon - this.addValueToWrapper(mainInfo, parseFloat(currentWeather.temperature).toFixed(this.config.roundTemp ? 0 : 1) + "°", "bright" ) // WindSpeed + this.addValueToWrapper(mainInfo, currentWeather.temperature.toFixed(this.config.roundTemp ? 0 : 1) + "°", "bright" ) // WindSpeed mainInfo.className = "large light" wrapper.appendChild(mainInfo) @@ -119,6 +138,39 @@ Module.register("weather",{ return wrapper }, + weatherForecastView: function() { + // This is just a dummy view to test it ... it needs A LOT of work :) + // Currently it outputs a div, it should be a table. + + var wrapper = document.createElement("div") + wrapper.className = "small" + + if (this.weatherProvider.weatherForecast() === null) { + return wrapper + } + + this.weatherProvider.weatherForecast().forEach((weatherObject) => { + var day = document.createElement("div") + + this.addValueToWrapper(day, moment(weatherObject.date).format("dd")) + this.addValueToWrapper(day, weatherObject.maxTemperature ? weatherObject.maxTemperature.toFixed(this.config.roundTemp ? 0 : 1) : null, "bright") + this.addValueToWrapper(day, weatherObject.minTemperature ? weatherObject.minTemperature.toFixed(this.config.roundTemp ? 0 : 1) : null, "dimmed") + + wrapper.appendChild(day) + }); + + return wrapper + }, + + fullWeatherView: function() { + var wrapper = document.createElement("div") + + wrapper.appendChild(this.currentWeatherView()) + wrapper.appendChild(this.weatherForecastView()) + + return wrapper + }, + // A convenience function to add an element to a wrapper with a specific value and class. addValueToWrapper: function(wrapper, value, classNames, element = "span", forceAdd = false, addSpacer = true) { if (value === null && !forceAdd) { diff --git a/modules/default/weather/weatherobject.js b/modules/default/weather/weatherobject.js index 2525eb6e..7448d118 100644 --- a/modules/default/weather/weatherobject.js +++ b/modules/default/weather/weatherobject.js @@ -14,11 +14,14 @@ class WeatherObject { constructor() { + this.date = null this.windSpeed = null this.windDirection = null this.sunrise = null this.sunset = null this.temperature = null + this.minTemperature = null, + this.maxTemperature = null, this.weatherType = null } diff --git a/modules/default/weather/weatherprovider.js b/modules/default/weather/weatherprovider.js index a7ca4cba..f31b22b7 100644 --- a/modules/default/weather/weatherprovider.js +++ b/modules/default/weather/weatherprovider.js @@ -57,8 +57,8 @@ var WeatherProvider = Class.extend({ // This method should start the API request to fetch the weather forecast. // This method should definetly be overwritten in the provider. - fetchWeatherForeCast: function() { - Log.warn("Weather provider: " + this.providerName + " does not subclass the fetchWeatherForeCast method.") + fetchWeatherForecast: function() { + Log.warn("Weather provider: " + this.providerName + " does not subclass the fetchWeatherForecast method.") }, // This returns a WeatherDay object for the current weather. @@ -71,7 +71,7 @@ var WeatherProvider = Class.extend({ return this.weatherForecastArray }, - // Set the currentWeather and notify the delegate that new information is availabe. + // Set the currentWeather and notify the delegate that new information is available. setCurrentWeather: function(currentWeatherObject) { // We should check here if we are passing a WeatherDay this.currentWeatherObject = currentWeatherObject @@ -79,6 +79,14 @@ var WeatherProvider = Class.extend({ this.updateAvailable() }, + // Set the weatherForecastArray and notify the delegate that new information is available. + setWeatherForecast: function(weatherForecastArray) { + // We should check here if we are passing a WeatherDay + this.weatherForecastArray = weatherForecastArray + + this.updateAvailable() + }, + // Notify the delegate that new weather is available. updateAvailable: function() { this.delegate.updateAvailable(this) From 837e275bfd238b10433731454280d414cce78218 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Fri, 29 Sep 2017 10:11:46 -0400 Subject: [PATCH 07/28] Update fork --- modules/default/weather/providers/darksky.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/default/weather/providers/darksky.js b/modules/default/weather/providers/darksky.js index 43a87fd1..3164d7c8 100644 --- a/modules/default/weather/providers/darksky.js +++ b/modules/default/weather/providers/darksky.js @@ -30,9 +30,23 @@ WeatherProvider.register("darksky", { Log.error("Could not load data!", request); }); }, + fetchWeatherForecast: function() { + // Also, fake data. + var forecast = []; + var today = moment(); + for(var i = 0; i < 5; i++) { + var weatherObject = new WeatherObject(); + weatherObject.date = moment(today).add(i, "days"); + weatherObject.minTemperature = Math.random() * 10 + 10; + weatherObject.maxTemperature = Math.random() * 15 + 10; + forecast.push(weatherObject); + } + this.setWeatherForecast(); + }, // Implement WeatherDay generator. generateWeatherDayFromCurrentWeather: function(currentWeatherData) { - var currentWeather = new WeatherDay(); + var currentWeather = new WeatherObject(); + currentWeather.date = new Date(); currentWeather.humidity = parseFloat(currentWeatherData.currently.humidity); currentWeather.temperature = parseFloat(currentWeatherData.currently.temperature); currentWeather.windSpeed = parseFloat(currentWeatherData.currently.windSpeed); From cd129fb05527b09f64ec8ffabc61314529906979 Mon Sep 17 00:00:00 2001 From: Nicholas Hubbard Date: Sat, 30 Sep 2017 19:44:54 -0400 Subject: [PATCH 08/28] Revert "Fix Indentation?" This reverts commit 2bf18d8bdab364636b2589a981c7e7fb395bc669. --- js/class.js | 24 +++++++++---------- .../updatenotification/updatenotification.js | 4 +++- tests/configs/check_config.js | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/js/class.js b/js/class.js index d054a73e..3c44250e 100644 --- a/js/class.js +++ b/js/class.js @@ -31,22 +31,22 @@ // Check if we're overwriting an existing function prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn) { - return function() { - var tmp = this._super; + return function() { + var tmp = this._super; - // Add a new ._super() method that is the same method - // but on the super-class - this._super = _super[name]; + // Add a new ._super() method that is the same method + // but on the super-class + this._super = _super[name]; - // The method only need to be bound temporarily, so we - // remove it when we're done executing - var ret = fn.apply(this, arguments); - this._super = tmp; + // The method only need to be bound temporarily, so we + // remove it when we're done executing + var ret = fn.apply(this, arguments); + this._super = tmp; - return ret; - }; - })(name, prop[name]) : prop[name]; + return ret; + }; + })(name, prop[name]) : prop[name]; } // The dummy class constructor diff --git a/modules/default/updatenotification/updatenotification.js b/modules/default/updatenotification/updatenotification.js index 9772f06b..f663f593 100644 --- a/modules/default/updatenotification/updatenotification.js +++ b/modules/default/updatenotification/updatenotification.js @@ -58,7 +58,9 @@ Module.register("updatenotification", { wrapper.appendChild(message); var subtext = document.createElement("div"); - subtext.innerHTML = this.translate("UPDATE_INFO").replace("COMMIT_COUNT", this.status.behind + " " + ((this.status.behind == 1)? "commit" : "commits")).replace("BRANCH_NAME", this.status.current); + subtext.innerHTML = this.translate("UPDATE_INFO") + .replace("COMMIT_COUNT", this.status.behind + " " + ((this.status.behind == 1)? "commit" : "commits")) + .replace("BRANCH_NAME", this.status.current); subtext.className = "xsmall dimmed"; wrapper.appendChild(subtext); } diff --git a/tests/configs/check_config.js b/tests/configs/check_config.js index 85cc6a48..fa294761 100644 --- a/tests/configs/check_config.js +++ b/tests/configs/check_config.js @@ -48,7 +48,7 @@ try { // In case the there errors show messages and // return console.info(Utils.colors.info("Checking file... ", configFileName)); -// I'm not sure if all ever is utf-8 + // I'm not sure if all ever is utf-8 fs.readFile(configFileName, "utf-8", function(err, data) { if (err) {throw err;} v.JSHINT(data); // Parser by jshint From 99e3a47dde1163eae3aa241bbc35d7ba061f787c Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Sun, 1 Oct 2017 13:50:15 +0200 Subject: [PATCH 09/28] Use templates to render weather. --- modules/default/weather/current.html | 16 ++++ modules/default/weather/forecast.html | 3 + modules/default/weather/weather.js | 126 +++----------------------- 3 files changed, 34 insertions(+), 111 deletions(-) create mode 100644 modules/default/weather/current.html create mode 100644 modules/default/weather/forecast.html diff --git a/modules/default/weather/current.html b/modules/default/weather/current.html new file mode 100644 index 00000000..949d2fbc --- /dev/null +++ b/modules/default/weather/current.html @@ -0,0 +1,16 @@ +
+ + + {{current.windSpeed}} + {{current.cardinalWindDirection()}} + + + 00:00 +
+
+ + {{current.temperature}}°
+ + + + \ No newline at end of file diff --git a/modules/default/weather/forecast.html b/modules/default/weather/forecast.html new file mode 100644 index 00000000..514a96df --- /dev/null +++ b/modules/default/weather/forecast.html @@ -0,0 +1,3 @@ +
Forecast template not yet implemented.
+ +
{{forecast | dump}}
\ No newline at end of file diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index 7f33ffa8..51eb8e75 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -15,7 +15,7 @@ Module.register("weather",{ weatherProvider: "openweathermap", units: config.units, roundTemp: false, - displayType: "full" //current, forecast, full + type: "current" //current, forecast }, // Module properties. @@ -51,18 +51,17 @@ Module.register("weather",{ this.scheduleUpdate(0) }, - // Generate the dom. This is now pretty simple for debugging. - getDom: function() { - switch (this.config.displayType) { - case "current": - return this.currentWeatherView() - break; - case "forecast": - return this.weatherForecastView() - break; - default: - return this.fullWeatherView() - break; + // Select the template depending on the display type. + getTemplate: function () { + return this.config.type.toLowerCase() + ".html" + }, + + // Add all the data to the template. + getTemplateData: function () { + return { + config: this.config, + current: this.weatherProvider.currentWeather(), + forecast: this.weatherProvider.weatherForecast() } }, @@ -81,113 +80,18 @@ Module.register("weather",{ } setTimeout(() => { - switch (this.config.displayType) { - case "current": - this.weatherProvider.fetchCurrentWeather() - break; + switch (this.config.type) { case "forecast": this.weatherProvider.fetchWeatherForecast() break; default: + case "current": this.weatherProvider.fetchCurrentWeather() - this.weatherProvider.fetchWeatherForecast() break; } }, nextLoad); }, - /* Views */ - - // Generate the current weather view - currentWeatherView: function () { - - var currentWeather = this.weatherProvider.currentWeather() - var wrapper = document.createElement("div") - - if (currentWeather === null) { - return wrapper - } - - // Detail bar. - - var detailBar = document.createElement("div") - - this.addValueToWrapper(detailBar, null, "wi wi-strong-wind dimmed", "span", true) // Wind Icon - this.addValueToWrapper(detailBar, currentWeather.windSpeed ? Math.round(currentWeather.windSpeed) : null) // WindSpeed - this.addValueToWrapper(detailBar, currentWeather.windDirection ? this.translate(currentWeather.cardinalWindDirection()) + "  " : " ", "", "sup") // WindDirection - - var now = new Date(); - var sunriseSunsetTime = (currentWeather.sunrise < now && currentWeather.sunset > now) ? currentWeather.sunset : currentWeather.sunrise - var sunriseSunsetIcon = (currentWeather.sunrise < now && currentWeather.sunset > now) ? "wi-sunset" : "wi-sunrise" - this.addValueToWrapper(detailBar, null, "wi dimmed " + sunriseSunsetIcon, "span", true) // Sunrise / Sunset Icon - this.addValueToWrapper(detailBar, moment(sunriseSunsetTime).format("HH:mm")) // Sunrise / Sunset Time - - detailBar.className = "normal medium" - wrapper.appendChild(detailBar) - - // Main info - - var mainInfo = document.createElement("div") - - this.addValueToWrapper(mainInfo, null, "weathericon wi wi-" + currentWeather.weatherType, "span", true) // Wind Icon - this.addValueToWrapper(mainInfo, currentWeather.temperature.toFixed(this.config.roundTemp ? 0 : 1) + "°", "bright" ) // WindSpeed - - mainInfo.className = "large light" - wrapper.appendChild(mainInfo) - - return wrapper - }, - - weatherForecastView: function() { - // This is just a dummy view to test it ... it needs A LOT of work :) - // Currently it outputs a div, it should be a table. - - var wrapper = document.createElement("div") - wrapper.className = "small" - - if (this.weatherProvider.weatherForecast() === null) { - return wrapper - } - - this.weatherProvider.weatherForecast().forEach((weatherObject) => { - var day = document.createElement("div") - - this.addValueToWrapper(day, moment(weatherObject.date).format("dd")) - this.addValueToWrapper(day, weatherObject.maxTemperature ? weatherObject.maxTemperature.toFixed(this.config.roundTemp ? 0 : 1) : null, "bright") - this.addValueToWrapper(day, weatherObject.minTemperature ? weatherObject.minTemperature.toFixed(this.config.roundTemp ? 0 : 1) : null, "dimmed") - - wrapper.appendChild(day) - }); - - return wrapper - }, - - fullWeatherView: function() { - var wrapper = document.createElement("div") - - wrapper.appendChild(this.currentWeatherView()) - wrapper.appendChild(this.weatherForecastView()) - - return wrapper - }, - - // A convenience function to add an element to a wrapper with a specific value and class. - addValueToWrapper: function(wrapper, value, classNames, element = "span", forceAdd = false, addSpacer = true) { - if (value === null && !forceAdd) { - return - } - - var valueWrapper = document.createElement(element) - valueWrapper.className = classNames - if (addSpacer) { - valueWrapper.innerHTML = " " - } - - if (value !== null) { - valueWrapper.innerHTML += value - } - - wrapper.appendChild(valueWrapper) - } + }); From ad240cf52fb16174783a08fca1f281146ec53a49 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Sun, 1 Oct 2017 16:19:14 +0200 Subject: [PATCH 10/28] Fix lint errors. --- modules/default/weather/weather.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index 51eb8e75..614c3243 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -8,7 +8,6 @@ */ Module.register("weather",{ - // Default module config. defaults: { updateInterval: 10 * 60 * 1000, @@ -90,8 +89,5 @@ Module.register("weather",{ break; } }, nextLoad); - }, - - - + } }); From 0776dfc80e62b9316bcc4facf1c55788c5fd9ead Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 18 Oct 2017 11:58:45 +0200 Subject: [PATCH 11/28] Minor changes. --- modules/default/weather/current.html | 28 ++++++++++---------- modules/default/weather/weather.js | 38 ++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/modules/default/weather/current.html b/modules/default/weather/current.html index 949d2fbc..416926b2 100644 --- a/modules/default/weather/current.html +++ b/modules/default/weather/current.html @@ -1,16 +1,18 @@ -
- - - {{current.windSpeed}} - {{current.cardinalWindDirection()}} - - - 00:00 -
-
- - {{current.temperature}}°
- +{% if current %} +
+ + + {{current.windSpeed}} + {{current.cardinalWindDirection()}} + + + 00:00 +
+
+ + {{current.temperature | round(1)}}°
+ +{% endif %} \ No newline at end of file diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index 614c3243..6a0eec54 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -14,7 +14,40 @@ Module.register("weather",{ weatherProvider: "openweathermap", units: config.units, roundTemp: false, - type: "current" //current, forecast + type: "current", //current, forecast + + // + + location: false, + locationID: false, + appid: "", + units: config.units, + updateInterval: 10 * 60 * 1000, // every 10 minutes + animationSpeed: 1000, + timeFormat: config.timeFormat, + showPeriod: true, + showPeriodUpper: false, + showWindDirection: true, + showWindDirectionAsArrow: false, + useBeaufort: true, + lang: config.language, + showHumidity: false, + degreeLabel: false, + showIndoorTemperature: false, + showIndoorHumidity: false, + + initialLoadDelay: 0, // 0 seconds delay + retryDelay: 2500, + + apiVersion: "2.5", + apiBase: "http://api.openweathermap.org/data/", + weatherEndpoint: "weather", + + appendLocationNameToHeader: true, + calendarClass: "calendar", + + onlyTemp: false, + roundTemp: false }, // Module properties. @@ -60,7 +93,8 @@ Module.register("weather",{ return { config: this.config, current: this.weatherProvider.currentWeather(), - forecast: this.weatherProvider.weatherForecast() + forecast: this.weatherProvider.weatherForecast(), + myBoolean: true } }, From 241ff5cb6e869035c35c21e9ae3b19c7d0bf94a3 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 18 Oct 2017 12:19:02 +0200 Subject: [PATCH 12/28] Set temperature rounding. --- modules/default/weather/current.html | 2 +- modules/default/weather/weather.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/default/weather/current.html b/modules/default/weather/current.html index 416926b2..1e618306 100644 --- a/modules/default/weather/current.html +++ b/modules/default/weather/current.html @@ -10,7 +10,7 @@
- {{current.temperature | round(1)}}°
+ {{current.temperature | round(0 if config.roundTemp else 1)}}° {% endif %} diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index 6a0eec54..f2993605 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -93,8 +93,7 @@ Module.register("weather",{ return { config: this.config, current: this.weatherProvider.currentWeather(), - forecast: this.weatherProvider.weatherForecast(), - myBoolean: true + forecast: this.weatherProvider.weatherForecast() } }, From ab732b5435c649da8e1724bfe61b00bc7f569da7 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 18 Oct 2017 13:38:56 +0200 Subject: [PATCH 13/28] Make all visiable values dynamic. --- modules/default/weather/current.html | 48 ++++++++++++++++++---- modules/default/weather/weather.js | 26 +++++++++++- modules/default/weather/weatherobject.js | 21 +++++++++- modules/default/weather/weatherprovider.js | 2 +- 4 files changed, 84 insertions(+), 13 deletions(-) diff --git a/modules/default/weather/current.html b/modules/default/weather/current.html index 1e618306..56f8a91b 100644 --- a/modules/default/weather/current.html +++ b/modules/default/weather/current.html @@ -1,13 +1,43 @@ +{# + TODO: + - Show Humidity + - Show Units + _ Show Indoor Temperature + _ Show Indoor Humidity +#} + {% if current %} -
- - - {{current.windSpeed}} - {{current.cardinalWindDirection()}} - - - 00:00 -
+ {% if not config.onlyTemp %} +
+ + + {% if config.useBeaufort %} + {{current.beaufortWindSpeed() | round}} + {% else %} + {{current.windSpeed | round}} + {% endif %} + + {% if config.showWindDirection %} + + {% if config.showWindDirectionAsArrow %} + + {% else %} + {{current.cardinalWindDirection()}} + {% endif %} +   + + {% endif %} + + + + {% if current.nextSunAction() == "sunset" %} + {{current.sunset | formatTime}} + {% else %} + {{current.sunrise | formatTime}} + {% endif %} + +
+ {% endif %}
{{current.temperature | round(0 if config.roundTemp else 1)}}°
diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index f2993605..75cb1060 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -55,7 +55,7 @@ Module.register("weather",{ // Define required scripts. getStyles: function() { - return ["weather-icons.css", "weather.css"]; + return ["font-awesome.css", "weather-icons.css", "weather.css"]; }, // Return the scripts that are nessecery for the weather module. @@ -79,6 +79,9 @@ Module.register("weather",{ // Let the weather provider know we are starting. this.weatherProvider.start() + // Add custom filters + this.addFilters() + // Schedule the first update. this.scheduleUpdate(0) }, @@ -122,5 +125,26 @@ Module.register("weather",{ break; } }, nextLoad); + }, + + addFilters() { + var self = this + this.nunjucksEnvironment().addFilter("formatTime", function(date) { + date = moment(date) + + if (self.config.timeFormat !== 24) { + if (self.config.showPeriod) { + if (self.config.showPeriodUpper) { + return date.format("h:mm A") + } else { + return date.format("h:mm a") + } + } else { + return date.format("h:mm") + } + } + + return date.format("HH:mm") + }); } }); diff --git a/modules/default/weather/weatherobject.js b/modules/default/weather/weatherobject.js index 7448d118..4ccc4bac 100644 --- a/modules/default/weather/weatherobject.js +++ b/modules/default/weather/weatherobject.js @@ -5,11 +5,11 @@ * * By Michael Teeuw http://michaelteeuw.nl * MIT Licensed. - * + * * This class is the blueprint for a day which includes weather information. */ -// Currently this is focused on the information which is nessecery for the current weather. +// Currently this is focused on the information which is necessary for the current weather. // As soon as we start implementing the forecast, mode properties will be added. class WeatherObject { @@ -60,4 +60,21 @@ class WeatherObject { return "N"; } } + + beaufortWindSpeed () { + var kmh = this.windSpeed * 60 * 60 / 1000; + var speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000]; + for (var beaufort in speeds) { + var speed = speeds[beaufort]; + if (speed > kmh) { + return beaufort; + } + } + return 12; + } + + nextSunAction () { + var now = new Date(); + return (this.sunrise < now && this.sunset > now) ? "sunset" : "sunrise"; + } }; \ No newline at end of file diff --git a/modules/default/weather/weatherprovider.js b/modules/default/weather/weatherprovider.js index f31b22b7..bb5598f4 100644 --- a/modules/default/weather/weatherprovider.js +++ b/modules/default/weather/weatherprovider.js @@ -5,7 +5,7 @@ * * By Michael Teeuw http://michaelteeuw.nl * MIT Licensed. - * + * * This class is the blueprint for a weather provider. */ From a79e1b6ca113403d315589cf369dca91772225f1 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 18 Oct 2017 13:52:11 +0200 Subject: [PATCH 14/28] Rename templates to .njk files to allow syntax highlighting. --- modules/default/weather/{current.html => current.njk} | 0 modules/default/weather/{forecast.html => forecast.njk} | 0 modules/default/weather/weather.js | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename modules/default/weather/{current.html => current.njk} (100%) rename modules/default/weather/{forecast.html => forecast.njk} (100%) diff --git a/modules/default/weather/current.html b/modules/default/weather/current.njk similarity index 100% rename from modules/default/weather/current.html rename to modules/default/weather/current.njk diff --git a/modules/default/weather/forecast.html b/modules/default/weather/forecast.njk similarity index 100% rename from modules/default/weather/forecast.html rename to modules/default/weather/forecast.njk diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index 75cb1060..8ef86a3a 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -88,7 +88,7 @@ Module.register("weather",{ // Select the template depending on the display type. getTemplate: function () { - return this.config.type.toLowerCase() + ".html" + return this.config.type.toLowerCase() + ".njk" }, // Add all the data to the template. From 22a50b72fdf41856bcc3e5d2bd0df04d070b2eb8 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Thu, 19 Oct 2017 16:43:12 +0200 Subject: [PATCH 15/28] Show unit. --- modules/default/weather/current.njk | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/default/weather/current.njk b/modules/default/weather/current.njk index 56f8a91b..b5e4a04f 100644 --- a/modules/default/weather/current.njk +++ b/modules/default/weather/current.njk @@ -1,7 +1,6 @@ {# TODO: - Show Humidity - - Show Units _ Show Indoor Temperature _ Show Indoor Humidity #} @@ -22,7 +21,7 @@ {% if config.showWindDirectionAsArrow %} {% else %} - {{current.cardinalWindDirection()}} + {{current.cardinalWindDirection() | translate}} {% endif %}   @@ -40,7 +39,13 @@ {% endif %}
- {{current.temperature | round(0 if config.roundTemp else 1)}}°
+ + {{current.temperature | round(0 if config.roundTemp else 1)}}°{% if config.degreeLabel %} + {% if config.units == "metric" %}C{% endif %} + {% if config.units == "imperial" %}F{% endif %} + {% if config.units == "default" %}K{% endif %} + {% endif %} + {% endif %} From 16c887814eb45f1a77df9a977d812ba89e6fc372 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Thu, 19 Oct 2017 16:51:51 +0200 Subject: [PATCH 16/28] Show humidity. --- modules/default/weather/current.njk | 3 +++ modules/default/weather/weatherobject.js | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/default/weather/current.njk b/modules/default/weather/current.njk index b5e4a04f..f578ba2b 100644 --- a/modules/default/weather/current.njk +++ b/modules/default/weather/current.njk @@ -27,6 +27,9 @@ {% endif %} + {% if config.showHumidity and current.humidity %} + {{ current.humidity }}  + {% endif %} {% if current.nextSunAction() == "sunset" %} diff --git a/modules/default/weather/weatherobject.js b/modules/default/weather/weatherobject.js index 4ccc4bac..59d2298a 100644 --- a/modules/default/weather/weatherobject.js +++ b/modules/default/weather/weatherobject.js @@ -20,9 +20,10 @@ class WeatherObject { this.sunrise = null this.sunset = null this.temperature = null - this.minTemperature = null, - this.maxTemperature = null, + this.minTemperature = null + this.maxTemperature = null this.weatherType = null + this.humidity = null } cardinalWindDirection () { From 07d35a85133d039486dc9ead9752a245345a3dc9 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Thu, 19 Oct 2017 16:52:57 +0200 Subject: [PATCH 17/28] Remove todo item. --- modules/default/weather/current.njk | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/default/weather/current.njk b/modules/default/weather/current.njk index f578ba2b..a830f4c4 100644 --- a/modules/default/weather/current.njk +++ b/modules/default/weather/current.njk @@ -1,8 +1,7 @@ {# TODO: - - Show Humidity - _ Show Indoor Temperature - _ Show Indoor Humidity + - Show Indoor Temperature + - Show Indoor Humidity #} {% if current %} From 91ddc00f7e04fc1a168d5f1c6e409b2d3a4dbe41 Mon Sep 17 00:00:00 2001 From: fewieden Date: Mon, 21 May 2018 10:56:46 +0200 Subject: [PATCH 18/28] fix moment, add unit filter --- modules/default/weather/current.njk | 8 +--- modules/default/weather/weather.js | 60 ++++++++++++++++++----------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/modules/default/weather/current.njk b/modules/default/weather/current.njk index a830f4c4..a50c38e7 100644 --- a/modules/default/weather/current.njk +++ b/modules/default/weather/current.njk @@ -42,14 +42,10 @@
- {{current.temperature | round(0 if config.roundTemp else 1)}}°{% if config.degreeLabel %} - {% if config.units == "metric" %}C{% endif %} - {% if config.units == "imperial" %}F{% endif %} - {% if config.units == "default" %}K{% endif %} - {% endif %} + {{current.temperature | round(0 if config.roundTemp else 1) | unit("temperature")}}
{% endif %} - \ No newline at end of file + diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index 8ef86a3a..469d5e12 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -16,8 +16,6 @@ Module.register("weather",{ roundTemp: false, type: "current", //current, forecast - // - location: false, locationID: false, appid: "", @@ -61,12 +59,13 @@ Module.register("weather",{ // Return the scripts that are nessecery for the weather module. getScripts: function () { var scripts = [ + "moment.js", "weatherprovider.js", "weatherobject.js" ]; // Add the provider file to the required scripts. - scripts.push(this.file("providers/" + this.config.weatherProvider.toLowerCase() + ".js")) + scripts.push(this.file("providers/" + this.config.weatherProvider.toLowerCase() + ".js")); return scripts }, @@ -74,16 +73,16 @@ Module.register("weather",{ // Start the weather module. start: function () { // Initialize the weather provider. - this.weatherProvider = WeatherProvider.initialize(this.config.weatherProvider, this) + this.weatherProvider = WeatherProvider.initialize(this.config.weatherProvider, this); // Let the weather provider know we are starting. - this.weatherProvider.start() + this.weatherProvider.start(); // Add custom filters - this.addFilters() + this.addFilters(); // Schedule the first update. - this.scheduleUpdate(0) + this.scheduleUpdate(0); }, // Select the template depending on the display type. @@ -102,10 +101,9 @@ Module.register("weather",{ // What to do when the weather provider has new information available? updateAvailable: function() { - Log.log("New weather information available.") - console.info(this.weatherProvider.currentWeather()) - this.updateDom(0) - this.scheduleUpdate() + Log.log("New weather information available."); + this.updateDom(0); + this.scheduleUpdate(5000); }, scheduleUpdate: function(delay = null) { @@ -117,34 +115,50 @@ Module.register("weather",{ setTimeout(() => { switch (this.config.type) { case "forecast": - this.weatherProvider.fetchWeatherForecast() + this.weatherProvider.fetchWeatherForecast(); break; default: case "current": - this.weatherProvider.fetchCurrentWeather() + this.weatherProvider.fetchCurrentWeather(); break; } }, nextLoad); }, addFilters() { - var self = this this.nunjucksEnvironment().addFilter("formatTime", function(date) { - date = moment(date) + date = moment(date); - if (self.config.timeFormat !== 24) { - if (self.config.showPeriod) { - if (self.config.showPeriodUpper) { - return date.format("h:mm A") + if (this.config.timeFormat !== 24) { + if (this.config.showPeriod) { + if (this.config.showPeriodUpper) { + return date.format("h:mm A"); } else { - return date.format("h:mm a") + return date.format("h:mm a"); } } else { - return date.format("h:mm") + return date.format("h:mm"); } } - return date.format("HH:mm") - }); + return date.format("HH:mm"); + }.bind(this)); + + this.nunjucksEnvironment().addFilter("unit", function (value, type) { + if (type === "temperature") { + value += "°"; + if (this.config.scale || this.config.degreeLabel) { + if (this.config.units === "metric") { + value += "C"; + } else if (this.config.units === "imperial") { + value += "F"; + } else { + value += "K"; + } + } + } + + return value; + }.bind(this)); } }); From 3341c9e3bf80867cded2dc81ff2eabb50fec7c40 Mon Sep 17 00:00:00 2001 From: fewieden Date: Mon, 21 May 2018 10:57:22 +0200 Subject: [PATCH 19/28] start with forecast template --- modules/default/weather/forecast.njk | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/modules/default/weather/forecast.njk b/modules/default/weather/forecast.njk index 514a96df..892b1b75 100644 --- a/modules/default/weather/forecast.njk +++ b/modules/default/weather/forecast.njk @@ -1,3 +1,21 @@ -
Forecast template not yet implemented.
+{% if forecast %} +
+ + {% for f in forecast %} + + + + + + + {% endfor %} +
{{f.day}} + {{f.maxTemperature | round(0 if config.roundTemp else 1) | unit("temperature")}} + + {{f.minTemperature | round(0 if config.roundTemp else 1) | unit("temperature")}} +
+
+{% endif %} -
{{forecast | dump}}
\ No newline at end of file + + From 66ceafd010a30999fc4160f1308960e9115009db Mon Sep 17 00:00:00 2001 From: fewieden Date: Sat, 16 Jun 2018 10:53:17 +0200 Subject: [PATCH 20/28] show indoor data, add loading message --- modules/default/weather/current.njk | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/modules/default/weather/current.njk b/modules/default/weather/current.njk index a50c38e7..b9b80365 100644 --- a/modules/default/weather/current.njk +++ b/modules/default/weather/current.njk @@ -1,9 +1,3 @@ -{# - TODO: - - Show Indoor Temperature - - Show Indoor Humidity -#} - {% if current %} {% if not config.onlyTemp %}
@@ -44,6 +38,22 @@ {{current.temperature | round(0 if config.roundTemp else 1) | unit("temperature")}} + {% if config.showIndoorTemperature and indoor.temperature %} + + + {{indoor.temperature | round(0 if config.roundTemp else 1) | unit("temperature")}} + + {% endif %} + {% if config.showIndoorHumidity and indoor.humidity %} + + + {{indoor.humidity | round(0 if config.roundTemp else 1)}}% + + {% endif %} +
+{% else %} +
+ {{"LOADING" | translate}}
{% endif %} From 0fe79b5288c1c6ad864211e6a0c512cbf923ebb9 Mon Sep 17 00:00:00 2001 From: fewieden Date: Mon, 2 Jul 2018 15:43:24 +0200 Subject: [PATCH 21/28] indoor data, new filter, small cleanup --- modules/default/weather/current.njk | 6 +-- modules/default/weather/forecast.njk | 6 ++- modules/default/weather/weather.js | 57 ++++++++++++++++++++++++---- 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/modules/default/weather/current.njk b/modules/default/weather/current.njk index b9b80365..a788bcc2 100644 --- a/modules/default/weather/current.njk +++ b/modules/default/weather/current.njk @@ -36,18 +36,18 @@
- {{current.temperature | round(0 if config.roundTemp else 1) | unit("temperature")}} + {{current.temperature | roundValue | unit("temperature")}} {% if config.showIndoorTemperature and indoor.temperature %} - {{indoor.temperature | round(0 if config.roundTemp else 1) | unit("temperature")}} + {{indoor.temperature | roundValue | unit("temperature")}} {% endif %} {% if config.showIndoorHumidity and indoor.humidity %} - {{indoor.humidity | round(0 if config.roundTemp else 1)}}% + {{indoor.humidity | roundValue}}% {% endif %}
diff --git a/modules/default/weather/forecast.njk b/modules/default/weather/forecast.njk index 892b1b75..66e7e427 100644 --- a/modules/default/weather/forecast.njk +++ b/modules/default/weather/forecast.njk @@ -6,15 +6,17 @@ {{f.day}} - {{f.maxTemperature | round(0 if config.roundTemp else 1) | unit("temperature")}} + {{f.maxTemperature | roundValue | unit("temperature")}} - {{f.minTemperature | round(0 if config.roundTemp else 1) | unit("temperature")}} + {{f.minTemperature | roundValue | unit("temperature")}} {% endfor %} +{% else %} + {{"LOADING" | translate}} {% endif %} diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index 469d5e12..598263a1 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -85,6 +85,38 @@ Module.register("weather",{ this.scheduleUpdate(0); }, + // Override notification handler. + notificationReceived: function(notification, payload, sender) { + if (notification === "DOM_OBJECTS_CREATED") { + if (this.config.appendLocationNameToHeader) { + this.hide(0, {lockString: this.identifier}); + } + } + if (notification === "CALENDAR_EVENTS") { + var senderClasses = sender.data.classes.toLowerCase().split(" "); + if (senderClasses.indexOf(this.config.calendarClass.toLowerCase()) !== -1) { + this.firstEvent = false; + + for (var e in payload) { + var event = payload[e]; + if (event.location || event.geo) { + this.firstEvent = event; + //Log.log("First upcoming event with location: ", event); + break; + } + } + } + } + if (notification === "INDOOR_TEMPERATURE") { + this.indoorTemperature = this.roundValue(payload); + this.updateDom(300); + } + if (notification === "INDOOR_HUMIDITY") { + this.indoorHumidity = this.roundValue(payload); + this.updateDom(300); + } + }, + // Select the template depending on the display type. getTemplate: function () { return this.config.type.toLowerCase() + ".njk" @@ -95,14 +127,18 @@ Module.register("weather",{ return { config: this.config, current: this.weatherProvider.currentWeather(), - forecast: this.weatherProvider.weatherForecast() + forecast: this.weatherProvider.weatherForecast(), + indoor: { + humidity: this.indoorHumidity, + temperature: this.indoorTemperature + } } }, // What to do when the weather provider has new information available? updateAvailable: function() { Log.log("New weather information available."); - this.updateDom(0); + this.updateDom(300); this.scheduleUpdate(5000); }, @@ -113,18 +149,19 @@ Module.register("weather",{ } setTimeout(() => { - switch (this.config.type) { - case "forecast": + if (this.config.type === "forecast") { this.weatherProvider.fetchWeatherForecast(); - break; - default: - case "current": + } else { this.weatherProvider.fetchCurrentWeather(); - break; } }, nextLoad); }, + roundValue: function(temperature) { + var decimals = this.config.roundTemp ? 0 : 1; + return parseFloat(temperature).toFixed(decimals); + } + addFilters() { this.nunjucksEnvironment().addFilter("formatTime", function(date) { date = moment(date); @@ -160,5 +197,9 @@ Module.register("weather",{ return value; }.bind(this)); + + this.nunjucksEnvironment().addFilter("roundValue", function(value) { + return this.roundValue(value); + }.bind(this)); } }); From 63836185d96a595969557e2f79b4da5d78aab5c6 Mon Sep 17 00:00:00 2001 From: fewieden Date: Thu, 27 Dec 2018 17:13:06 +0100 Subject: [PATCH 22/28] weatherprovider --- modules/default/weather/weatherprovider.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/default/weather/weatherprovider.js b/modules/default/weather/weatherprovider.js index bb5598f4..0be45663 100644 --- a/modules/default/weather/weatherprovider.js +++ b/modules/default/weather/weatherprovider.js @@ -21,6 +21,7 @@ var WeatherProvider = Class.extend({ // Try to not access them directly. currentWeatherObject: null, weatherForecastArray: null, + fetchedLocationName: null, // The following properties will be set automaticly. // You do not need to overwrite these properties. @@ -71,6 +72,11 @@ var WeatherProvider = Class.extend({ return this.weatherForecastArray }, + // This returns the name of the fetched location or an empty string + fetchedLocation: function() { + return this.fetchedLocationName || '' + }, + // Set the currentWeather and notify the delegate that new information is available. setCurrentWeather: function(currentWeatherObject) { // We should check here if we are passing a WeatherDay @@ -87,6 +93,11 @@ var WeatherProvider = Class.extend({ this.updateAvailable() }, + // Set the fetched location name + setFetchedLocation: function(name) { + this.fetchedLocationName = name + }, + // Notify the delegate that new weather is available. updateAvailable: function() { this.delegate.updateAvailable(this) From ebee80d10ea57d7b3000eb5ea52dd8c88c0f16f5 Mon Sep 17 00:00:00 2001 From: fewieden Date: Thu, 27 Dec 2018 17:13:49 +0100 Subject: [PATCH 23/28] small improvements --- modules/default/weather/weather.js | 37 ++++++++++++++++++------ modules/default/weather/weatherobject.js | 2 +- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index 598263a1..b3832dd0 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -39,13 +39,15 @@ Module.register("weather",{ apiVersion: "2.5", apiBase: "http://api.openweathermap.org/data/", - weatherEndpoint: "weather", + weatherEndpoint: "/weather", appendLocationNameToHeader: true, calendarClass: "calendar", + tableClass: 'small', onlyTemp: false, - roundTemp: false + roundTemp: false, + showRainAmount: true }, // Module properties. @@ -70,8 +72,18 @@ Module.register("weather",{ return scripts }, + // Override getHeader method. + getHeader: function() { + if (this.config.appendLocationNameToHeader && this.weatherProvider) { + return this.data.header + " " + this.weatherProvider.fetchedLocation(); + } + + return this.data.header; + }, + // Start the weather module. start: function () { + moment.locale(this.config.lang); // Initialize the weather provider. this.weatherProvider = WeatherProvider.initialize(this.config.weatherProvider, this); @@ -87,11 +99,6 @@ Module.register("weather",{ // Override notification handler. notificationReceived: function(notification, payload, sender) { - if (notification === "DOM_OBJECTS_CREATED") { - if (this.config.appendLocationNameToHeader) { - this.hide(0, {lockString: this.identifier}); - } - } if (notification === "CALENDAR_EVENTS") { var senderClasses = sender.data.classes.toLowerCase().split(" "); if (senderClasses.indexOf(this.config.calendarClass.toLowerCase()) !== -1) { @@ -139,7 +146,7 @@ Module.register("weather",{ updateAvailable: function() { Log.log("New weather information available."); this.updateDom(300); - this.scheduleUpdate(5000); + this.scheduleUpdate(60000); }, scheduleUpdate: function(delay = null) { @@ -160,7 +167,7 @@ Module.register("weather",{ roundValue: function(temperature) { var decimals = this.config.roundTemp ? 0 : 1; return parseFloat(temperature).toFixed(decimals); - } + }, addFilters() { this.nunjucksEnvironment().addFilter("formatTime", function(date) { @@ -201,5 +208,17 @@ Module.register("weather",{ this.nunjucksEnvironment().addFilter("roundValue", function(value) { return this.roundValue(value); }.bind(this)); + + this.nunjucksEnvironment().addFilter("formatRain", function(value) { + if (isNaN(value)) { + return ""; + } + + if(this.config.units === "imperial") { + return (parseFloat(value) / 25.4).toFixed(2) + " in"; + } else { + return parseFloat(value).toFixed(1) + " mm"; + } + }.bind(this)); } }); diff --git a/modules/default/weather/weatherobject.js b/modules/default/weather/weatherobject.js index 59d2298a..ff0de841 100644 --- a/modules/default/weather/weatherobject.js +++ b/modules/default/weather/weatherobject.js @@ -27,7 +27,7 @@ class WeatherObject { } cardinalWindDirection () { - if (this.windDirection>11.25 && this.windDirection<=33.75){ + if (this.windDirection > 11.25 && this.windDirection <= 33.75){ return "NNE"; } else if (this.windDirection > 33.75 && this.windDirection <= 56.25) { return "NE"; From 95adc0aec1b74167d79478be8627da7b75111e66 Mon Sep 17 00:00:00 2001 From: fewieden Date: Thu, 27 Dec 2018 17:14:03 +0100 Subject: [PATCH 24/28] forecast --- modules/default/weather/forecast.njk | 33 +++---- .../weather/providers/openweathermap.js | 87 +++++++++++-------- 2 files changed, 70 insertions(+), 50 deletions(-) diff --git a/modules/default/weather/forecast.njk b/modules/default/weather/forecast.njk index 66e7e427..edc11115 100644 --- a/modules/default/weather/forecast.njk +++ b/modules/default/weather/forecast.njk @@ -1,20 +1,23 @@ {% if forecast %} -
- - {% for f in forecast %} - - - - + {% endfor %} +
{{f.day}} - {{f.maxTemperature | roundValue | unit("temperature")}} + + {% for f in forecast %} + + + + + + {% if config.showRainAmount %} + - - - {% endfor %} -
{{f.date.format('ddd')}} + {{f.maxTemperature | roundValue | unit("temperature")}} + + {{f.minTemperature | roundValue | unit("temperature")}} + + {{f.rain | formatRain}} - {{f.minTemperature | roundValue | unit("temperature")}} -
- + {% endif %} +
{% else %} {{"LOADING" | translate}} {% endif %} diff --git a/modules/default/weather/providers/openweathermap.js b/modules/default/weather/providers/openweathermap.js index c9e56d2c..ff58547c 100644 --- a/modules/default/weather/providers/openweathermap.js +++ b/modules/default/weather/providers/openweathermap.js @@ -12,28 +12,22 @@ WeatherProvider.register("openweathermap", { // Set the name of the provider. - // This isn't strictly nessecery, since it will fallback to the provider identifier + // 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: "OpenWeatherMap", // Overwrite the fetchCurrentWeather method. fetchCurrentWeather: function() { - var apiVersion = "2.5" - var apiBase = "http://api.openweathermap.org/data/" - var weatherEndpoint = "weather" - - var url = apiBase + apiVersion + "/" + weatherEndpoint + this.getParams() - - this.fetchData(url) + this.fetchData(this.getUrl()) .then(data => { - Log.log(data) - if (!data || !data.main || typeof data.main.temp === "undefined") { // Did not receive usable new data. // Maybe this needs a better check? return; } + this.setFetchedLocation(data.name + ', ' + data.sys.country) + var currentWeather = this.generateWeatherObjectFromCurrentWeather(data) this.setCurrentWeather(currentWeather) }) @@ -44,29 +38,33 @@ WeatherProvider.register("openweathermap", { // Overwrite the fetchCurrentWeather method. fetchWeatherForecast: function() { + this.fetchData(this.getUrl()) + .then(data => { + if (!data || !data.list || !data.list.length) { + // Did not receive usable new data. + // Maybe this needs a better check? + return; + } - // I haven't yet implemented the real api call, so let's just generate some random data. + this.setFetchedLocation(data.city.name + ', ' + data.city.country) - var forecast = [] - var today = moment() - - for (var i = 0; i < 5; i++ ) { - var weatherObject = new WeatherObject() - - weatherObject.date = moment(today).add(i, "days") - weatherObject.minTemperature = Math.random() * 10 + 10 - weatherObject.maxTemperature = Math.random() * 15 + 10 - - forecast.push(weatherObject) - } - - this.setWeatherForecast(forecast) + var forecast = this.generateWeatherObjectsFromForecast(data.list) + this.setWeatherForecast(forecast) + }) + .catch(function(request) { + Log.error("Could not load data ... ", request) + }) }, /** OpenWeatherMap Specific Methods - These are not part of the default provider methods */ - + /* + * Gets the complete url for the request + */ + getUrl: function() { + return this.config.apiBase + this.config.apiVersion + this.config.weatherEndpoint + this.getParams() + }, /* * Generate a WeatherObject based on currentWeatherInformation @@ -74,19 +72,38 @@ WeatherProvider.register("openweathermap", { generateWeatherObjectFromCurrentWeather: function(currentWeatherData) { var currentWeather = new WeatherObject() - currentWeather.date = new Date - - currentWeather.humidity = currentWeatherData.main.humidity ? parseFloat(currentWeatherData.main.humidity) : null - currentWeather.temperature = currentWeatherData.main.temp ? parseFloat(currentWeatherData.main.temp) : null - currentWeather.windSpeed = currentWeatherData.wind.speed ? parseFloat(currentWeatherData.wind.speed) : null - currentWeather.windDirection = currentWeatherData.wind.deg ? currentWeatherData.wind.deg : null - currentWeather.weatherType = currentWeatherData.weather[0].icon ? this.convertWeatherType(currentWeatherData.weather[0].icon) : null - currentWeather.sunrise = currentWeatherData.sys.sunrise ? new Date(currentWeatherData.sys.sunrise * 1000) : null - currentWeather.sunset = currentWeatherData.sys.sunset ? new Date(currentWeatherData.sys.sunset * 1000) : null + currentWeather.humidity = currentWeatherData.main.humidity + currentWeather.temperature = currentWeatherData.main.temp + currentWeather.windSpeed = currentWeatherData.wind.speed + currentWeather.windDirection = currentWeatherData.wind.deg + currentWeather.weatherType = this.convertWeatherType(currentWeatherData.weather[0].icon) + currentWeather.sunrise = moment(currentWeatherData.sys.sunrise, "X") + currentWeather.sunset = moment(currentWeatherData.sys.sunset, "X") return currentWeather }, + /* + * Generate WeatherObjects based on forecast information + */ + generateWeatherObjectsFromForecast: function(forecasts) { + var days = [] + + for (var forecast of forecasts) { + var weather = new WeatherObject() + + weather.date = moment(forecast.dt, "X") + weather.minTemperature = forecast.temp.min + weather.maxTemperature = forecast.temp.max + weather.weatherType = this.convertWeatherType(forecast.weather[0].icon) + weather.rain = forecast.rain + + days.push(weather) + } + + return days + }, + /* * Convert the OpenWeatherMap icons to a more usable name. */ From 0ed2ba0183b64d93c99ede6c4f4f22f3bfb8e901 Mon Sep 17 00:00:00 2001 From: fewieden Date: Thu, 27 Dec 2018 17:56:34 +0100 Subject: [PATCH 25/28] darksky forecast and darksky current weather fixes --- modules/default/weather/providers/darksky.js | 87 +++++++++++++------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/modules/default/weather/providers/darksky.js b/modules/default/weather/providers/darksky.js index 3164d7c8..4ce0c895 100644 --- a/modules/default/weather/providers/darksky.js +++ b/modules/default/weather/providers/darksky.js @@ -13,49 +13,76 @@ WeatherProvider.register("darksky", { // Set the name of the provider. // Not strictly required, but helps for debugging. providerName: "Dark Sky", - // Implement fetchCurrentWeather. + fetchCurrentWeather: function() { - // Create a URL from the config and base URL. - var url = `https://api.darksky.net/forecast/${this.config.apiKey}/${this.config.latLong}`; - // Run the request. - this.fetchData(url).then(data => { - Log.log(data); - if(!data || !data.main || typeof data.main.temp === "undefined") { - // No usable data? - return; - } - var currentWeather = this.generateWeatherDayFromCurrentWeather(data); - this.setCurrentWeather(currentWeather); - }).catch(function(request) { - Log.error("Could not load data!", request); - }); + this.fetchData(this.getUrl()) + .then(data => { + Log.log(data); + if(!data || !data.currently || typeof data.currently.temperature === "undefined") { + // No usable data? + return; + } + var currentWeather = this.generateWeatherDayFromCurrentWeather(data); + this.setCurrentWeather(currentWeather); + }).catch(function(request) { + Log.error("Could not load data!", request); + }); }, + fetchWeatherForecast: function() { - // Also, fake data. - var forecast = []; - var today = moment(); - for(var i = 0; i < 5; i++) { - var weatherObject = new WeatherObject(); - weatherObject.date = moment(today).add(i, "days"); - weatherObject.minTemperature = Math.random() * 10 + 10; - weatherObject.maxTemperature = Math.random() * 15 + 10; - forecast.push(weatherObject); - } - this.setWeatherForecast(); + this.fetchData(this.getUrl()) + .then(data => { + Log.log(data); + if(!data || !data.daily || !data.daily.data.length) { + // No usable data? + return; + } + var forecast = this.generateWeatherObjectsFromForecast(data.daily.data); + this.setWeatherForecast(forecast); + }).catch(function(request) { + Log.error("Could not load data!", request); + }); }, + + // Create a URL from the config and base URL. + getUrl: function() { + return `https://cors-anywhere.herokuapp.com/https://api.darksky.net/forecast/${this.config.apiKey}/${this.config.lat},${this.config.lon}`; + }, + // Implement WeatherDay generator. generateWeatherDayFromCurrentWeather: function(currentWeatherData) { var currentWeather = new WeatherObject(); - currentWeather.date = new Date(); + + currentWeather.date = moment(); currentWeather.humidity = parseFloat(currentWeatherData.currently.humidity); currentWeather.temperature = parseFloat(currentWeatherData.currently.temperature); currentWeather.windSpeed = parseFloat(currentWeatherData.currently.windSpeed); currentWeather.windDirection = currentWeatherData.currently.windBearing; - currentWeather.weatherType = this.currentWeatherType(currentWeatherData.currently.icon); - currentWeather.sunrise = new Date(currentWeatherData.daily.data[0].sunriseTime); - currentWeather.sunset = new Date(currentWeatherData.daily.data[0].sunsetTime); + currentWeather.weatherType = this.convertWeatherType(currentWeatherData.currently.icon); + currentWeather.sunrise = moment(currentWeatherData.daily.data[0].sunriseTime, "X"); + currentWeather.sunset = moment(currentWeatherData.daily.data[0].sunsetTime, "X"); + return currentWeather; }, + + generateWeatherObjectsFromForecast: function(forecasts) { + var days = []; + + for (var forecast of forecasts) { + var weather = new WeatherObject(); + + weather.date = moment(forecast.time, "X"); + weather.minTemperature = forecast.temperatureMin; + weather.maxTemperature = forecast.temperatureMax; + weather.weatherType = this.convertWeatherType(forecast.icon); + weather.rain = forecast.precipAccumulation; + + days.push(weather) + } + + return days + }, + // Map icons from Dark Sky to our icons. convertWeatherType: function(weatherType) { var weatherTypes = { From 1920f8158e21830b1c597a03da4fd925f8af6bd5 Mon Sep 17 00:00:00 2001 From: fewieden Date: Thu, 27 Dec 2018 18:52:35 +0100 Subject: [PATCH 26/28] config options and documentation --- modules/default/weather/README.md | 95 +++++++++++++++++-- modules/default/weather/current.png | Bin 0 -> 8141 bytes modules/default/weather/forecast.png | Bin 0 -> 16385 bytes modules/default/weather/providers/darksky.js | 2 +- modules/default/weather/weather.js | 9 +- 5 files changed, 91 insertions(+), 15 deletions(-) create mode 100644 modules/default/weather/current.png create mode 100644 modules/default/weather/forecast.png diff --git a/modules/default/weather/README.md b/modules/default/weather/README.md index 325732cc..d8766824 100644 --- a/modules/default/weather/README.md +++ b/modules/default/weather/README.md @@ -2,16 +2,93 @@ This module is aimed to be the replacement for the current `currentweather` and `weatherforcast` modules. The module will be configurable to be used as a current weather view, or to show the forecast. This way the module can be used twice to fullfil both purposes. -The biggest cange is the use of weather providers. This way we are not bound to one API source. And users can choose which API they want to use as their source. Initially the current OpenWeatherMap will be added as the first source, but more will follow soon. +The biggest cange is the use of weather providers. This way we are not bound to one API source. And users can choose which API they want to use as their source. The module is in a very early stage, and needs a lot of work. It's API isn't set in stone, so keep that in mind when you want to contribute. -## TODO +## Example -- [ ] Add Current Weather View -- [ ] Add Weather Forecast View -- [ ] Add Forecast API Call -- [ ] Add all original configuration options -- [ ] Add more providers -- [ ] Finish thi Todo list -- [ ] Write the documentation \ No newline at end of file +![Current Weather Screenshot](current.png) ![Weather Forecast Screenshot](forecast.png) + +## Usage + +To use this module, add it to the modules array in the `config/config.js` file: + +````javascript +modules: [ + { + module: "weather", + position: "top_right", + config: { + // See 'Configuration options' for more information. + type: 'current' + } + } +] +```` + +## Configuration options + +The following properties can be configured: + +### General options + +| 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` +| `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` +| `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) +| `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` +| `retryDelay` | The delay before retrying after a request failure. (Milliseconds)

**Possible values:** `1000` - `60000`
**Default value:** `2500` +| `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'` + +#### Current weather options + +| Option | Description +| ---------------------------- | ----------- +| `onlyTemp` | Show only current Temperature and weather icon without windspeed, sunset, sunrise time and feels like.

**Possible values:** `true` or `false`
**Default value:** `false` +| `useBeaufort` | Pick between using the Beaufort scale for wind speed or using the default units.

**Possible values:** `true` or `false`
**Default value:** `true` +| `showWindDirection` | Show the wind direction next to the wind speed.

**Possible values:** `true` or `false`
**Default value:** `true` +| `showWindDirectionAsArrow` | Show the wind direction as an arrow instead of abbreviation

**Possible values:** `true` or `false`
**Default value:** `false` +| `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` + +#### 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` +| `showRainAmount` | Show the amount of rain in the forecast

**Possible values:** `true` or `false`
**Default value:** `true` + +### Openweathermap options + +| 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` or `/forecast/daily`
**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** + +### 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** diff --git a/modules/default/weather/current.png b/modules/default/weather/current.png new file mode 100644 index 0000000000000000000000000000000000000000..de6c5a39ea341592fcd25d19d6a382457bad9e35 GIT binary patch literal 8141 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 BxD@~Z literal 0 HcmV?d00001 diff --git a/modules/default/weather/forecast.png b/modules/default/weather/forecast.png new file mode 100644 index 0000000000000000000000000000000000000000..1afd31913ffba849a334aeedb0aad2c799a3de20 GIT binary patch literal 16385 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 ZC??9%ZOK#@g4?!_rz8gk6}~VF_+LS8+!_D? literal 0 HcmV?d00001 diff --git a/modules/default/weather/providers/darksky.js b/modules/default/weather/providers/darksky.js index 4ce0c895..e5358a0f 100644 --- a/modules/default/weather/providers/darksky.js +++ b/modules/default/weather/providers/darksky.js @@ -46,7 +46,7 @@ WeatherProvider.register("darksky", { // Create a URL from the config and base URL. getUrl: function() { - return `https://cors-anywhere.herokuapp.com/https://api.darksky.net/forecast/${this.config.apiKey}/${this.config.lat},${this.config.lon}`; + return `${this.config.apiBase}${this.config.weatherEndpoint}/${this.config.apiKey}/${this.config.lat},${this.config.lon}`; }, // Implement WeatherDay generator. diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index b3832dd0..d94f774e 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -12,7 +12,6 @@ Module.register("weather",{ defaults: { updateInterval: 10 * 60 * 1000, weatherProvider: "openweathermap", - units: config.units, roundTemp: false, type: "current", //current, forecast @@ -43,11 +42,11 @@ Module.register("weather",{ appendLocationNameToHeader: true, calendarClass: "calendar", - tableClass: 'small', + tableClass: "small", onlyTemp: false, - roundTemp: false, - showRainAmount: true + showRainAmount: true, + colored: false }, // Module properties. @@ -191,7 +190,7 @@ Module.register("weather",{ this.nunjucksEnvironment().addFilter("unit", function (value, type) { if (type === "temperature") { value += "°"; - if (this.config.scale || this.config.degreeLabel) { + if (this.config.degreeLabel) { if (this.config.units === "metric") { value += "C"; } else if (this.config.units === "imperial") { From 10bc3264909705fd47bb1c9cc8b408ea1d4ee8ef Mon Sep 17 00:00:00 2001 From: fewieden Date: Thu, 27 Dec 2018 19:37:02 +0100 Subject: [PATCH 27/28] cleanup --- css/main.css | 15 ++--- modules/default/weather/providers/darksky.js | 10 +-- .../weather/providers/openweathermap.js | 64 +++++++++---------- modules/default/weather/weather.css | 37 ++++++----- modules/default/weather/weather.js | 28 ++++---- modules/default/weather/weatherobject.js | 22 +++---- modules/default/weather/weatherprovider.js | 48 +++++++------- 7 files changed, 107 insertions(+), 117 deletions(-) diff --git a/css/main.css b/css/main.css index 49bfe611..ae590fdd 100644 --- a/css/main.css +++ b/css/main.css @@ -95,7 +95,7 @@ body { header { text-transform: uppercase; font-size: 15px; - font-family: "Roboto Condensed"; + font-family: "Roboto Condensed", sans-serif; font-weight: 400; border-bottom: 1px solid #666; line-height: 15px; @@ -151,6 +151,7 @@ sup { .region.right { right: 0; + text-align: right; } .region.top { @@ -161,6 +162,10 @@ sup { margin-bottom: 25px; } +.region.bottom .container { + margin-top: 25px; +} + .region.top .container:empty { margin-bottom: 0; } @@ -185,10 +190,6 @@ sup { bottom: 0; } -.region.bottom .container { - margin-top: 25px; -} - .region.bottom .container:empty { margin-top: 0; } @@ -231,10 +232,6 @@ sup { text-align: left; } -.region.right { - text-align: right; -} - .region table { width: 100%; border-spacing: 0; diff --git a/modules/default/weather/providers/darksky.js b/modules/default/weather/providers/darksky.js index e5358a0f..74758d6e 100644 --- a/modules/default/weather/providers/darksky.js +++ b/modules/default/weather/providers/darksky.js @@ -17,30 +17,30 @@ WeatherProvider.register("darksky", { fetchCurrentWeather: function() { this.fetchData(this.getUrl()) .then(data => { - Log.log(data); if(!data || !data.currently || typeof data.currently.temperature === "undefined") { // No usable data? return; } + var currentWeather = this.generateWeatherDayFromCurrentWeather(data); this.setCurrentWeather(currentWeather); }).catch(function(request) { - Log.error("Could not load data!", request); + Log.error("Could not load data ... ", request); }); }, fetchWeatherForecast: function() { this.fetchData(this.getUrl()) .then(data => { - Log.log(data); if(!data || !data.daily || !data.daily.data.length) { // No usable data? return; } + var forecast = this.generateWeatherObjectsFromForecast(data.daily.data); this.setWeatherForecast(forecast); }).catch(function(request) { - Log.error("Could not load data!", request); + Log.error("Could not load data ... ", request); }); }, @@ -99,4 +99,4 @@ WeatherProvider.register("darksky", { }; return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null; } -}); \ No newline at end of file +}); diff --git a/modules/default/weather/providers/openweathermap.js b/modules/default/weather/providers/openweathermap.js index ff58547c..8fd1e3be 100644 --- a/modules/default/weather/providers/openweathermap.js +++ b/modules/default/weather/providers/openweathermap.js @@ -26,13 +26,13 @@ WeatherProvider.register("openweathermap", { return; } - this.setFetchedLocation(data.name + ', ' + data.sys.country) + this.setFetchedLocation(`${data.name}, ${data.sys.country}`); - var currentWeather = this.generateWeatherObjectFromCurrentWeather(data) - this.setCurrentWeather(currentWeather) + var currentWeather = this.generateWeatherObjectFromCurrentWeather(data); + this.setCurrentWeather(currentWeather); }) .catch(function(request) { - Log.error("Could not load data ... ", request) + Log.error("Could not load data ... ", request); }) }, @@ -46,13 +46,13 @@ WeatherProvider.register("openweathermap", { return; } - this.setFetchedLocation(data.city.name + ', ' + data.city.country) + this.setFetchedLocation(`${data.city.name}, ${data.city.country}`); - var forecast = this.generateWeatherObjectsFromForecast(data.list) - this.setWeatherForecast(forecast) + var forecast = this.generateWeatherObjectsFromForecast(data.list); + this.setWeatherForecast(forecast); }) .catch(function(request) { - Log.error("Could not load data ... ", request) + Log.error("Could not load data ... ", request); }) }, @@ -63,45 +63,45 @@ WeatherProvider.register("openweathermap", { * Gets the complete url for the request */ getUrl: function() { - 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 */ generateWeatherObjectFromCurrentWeather: function(currentWeatherData) { - var currentWeather = new WeatherObject() + var currentWeather = new WeatherObject(); - currentWeather.humidity = currentWeatherData.main.humidity - currentWeather.temperature = currentWeatherData.main.temp - currentWeather.windSpeed = currentWeatherData.wind.speed - currentWeather.windDirection = currentWeatherData.wind.deg - currentWeather.weatherType = this.convertWeatherType(currentWeatherData.weather[0].icon) - currentWeather.sunrise = moment(currentWeatherData.sys.sunrise, "X") - currentWeather.sunset = moment(currentWeatherData.sys.sunset, "X") + currentWeather.humidity = currentWeatherData.main.humidity; + currentWeather.temperature = currentWeatherData.main.temp; + currentWeather.windSpeed = currentWeatherData.wind.speed; + currentWeather.windDirection = currentWeatherData.wind.deg; + currentWeather.weatherType = this.convertWeatherType(currentWeatherData.weather[0].icon); + currentWeather.sunrise = moment(currentWeatherData.sys.sunrise, "X"); + currentWeather.sunset = moment(currentWeatherData.sys.sunset, "X"); - return currentWeather + return currentWeather; }, /* * Generate WeatherObjects based on forecast information */ generateWeatherObjectsFromForecast: function(forecasts) { - var days = [] + var days = []; for (var forecast of forecasts) { - var weather = new WeatherObject() + var weather = new WeatherObject(); - weather.date = moment(forecast.dt, "X") - weather.minTemperature = forecast.temp.min - weather.maxTemperature = forecast.temp.max - weather.weatherType = this.convertWeatherType(forecast.weather[0].icon) - weather.rain = forecast.rain + weather.date = moment(forecast.dt, "X"); + weather.minTemperature = forecast.temp.min; + weather.maxTemperature = forecast.temp.max; + weather.weatherType = this.convertWeatherType(forecast.weather[0].icon); + weather.rain = forecast.rain; - days.push(weather) + days.push(weather); } - return days + return days; }, /* @@ -127,9 +127,9 @@ WeatherProvider.register("openweathermap", { "11n": "night-thunderstorm", "13n": "night-snow", "50n": "night-alt-cloudy-windy" - } + }; - return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null + return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null; }, /* getParams(compliments) @@ -144,7 +144,7 @@ WeatherProvider.register("openweathermap", { } else if(this.config.location) { params += "q=" + this.config.location; } else if (this.firstEvent && this.firstEvent.geo) { - params += "lat=" + this.firstEvent.geo.lat + "&lon=" + this.firstEvent.geo.lon + params += "lat=" + this.firstEvent.geo.lat + "&lon=" + this.firstEvent.geo.lon; } else if (this.firstEvent && this.firstEvent.location) { params += "q=" + this.firstEvent.location; } else { @@ -157,5 +157,5 @@ WeatherProvider.register("openweathermap", { params += "&APPID=" + this.config.apiKey; return params; - }, -}); \ No newline at end of file + } +}); diff --git a/modules/default/weather/weather.css b/modules/default/weather/weather.css index febc777c..dfa2b12a 100644 --- a/modules/default/weather/weather.css +++ b/modules/default/weather/weather.css @@ -1,46 +1,45 @@ .weather .weathericon, .weather .fa-home { - font-size: 75%; - line-height: 65px; - display: inline-block; - -ms-transform: translate(0, -3px); /* IE 9 */ - -webkit-transform: translate(0, -3px); /* Safari */ - transform: translate(0, -3px); + font-size: 75%; + line-height: 65px; + display: inline-block; + -ms-transform: translate(0, -3px); /* IE 9 */ + -webkit-transform: translate(0, -3px); /* Safari */ + transform: translate(0, -3px); } .weather .humidityIcon { - padding-right: 4px; + padding-right: 4px; } .weather .humidity-padding { - padding-bottom: 6px; + padding-bottom: 6px; } - .weather .day { - padding-left: 0; - padding-right: 25px; + padding-left: 0; + padding-right: 25px; } .weather .weather-icon { - padding-right: 30px; - text-align: center; + padding-right: 30px; + text-align: center; } .weather .min-temp { - padding-left: 20px; - padding-right: 0; + padding-left: 20px; + padding-right: 0; } .weather .rain { - padding-left: 20px; - padding-right: 0; + padding-left: 20px; + padding-right: 0; } .weather tr.colored .min-temp { - color: #BCDDFF; + color: #bcddff; } .weather tr.colored .max-temp { - color: #FF8E99; + color: #ff8e99; } diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index d94f774e..03e15bda 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -59,16 +59,12 @@ Module.register("weather",{ // Return the scripts that are nessecery for the weather module. getScripts: function () { - var scripts = [ + return [ "moment.js", "weatherprovider.js", - "weatherobject.js" + "weatherobject.js", + this.file("providers/" + this.config.weatherProvider.toLowerCase() + ".js") ]; - - // Add the provider file to the required scripts. - scripts.push(this.file("providers/" + this.config.weatherProvider.toLowerCase() + ".js")); - - return scripts }, // Override getHeader method. @@ -93,7 +89,7 @@ Module.register("weather",{ this.addFilters(); // Schedule the first update. - this.scheduleUpdate(0); + this.scheduleUpdate(this.config.initialLoadDelay); }, // Override notification handler. @@ -112,12 +108,10 @@ Module.register("weather",{ } } } - } - if (notification === "INDOOR_TEMPERATURE") { + } else if (notification === "INDOOR_TEMPERATURE") { this.indoorTemperature = this.roundValue(payload); this.updateDom(300); - } - if (notification === "INDOOR_HUMIDITY") { + } else if (notification === "INDOOR_HUMIDITY") { this.indoorHumidity = this.roundValue(payload); this.updateDom(300); } @@ -125,7 +119,7 @@ Module.register("weather",{ // Select the template depending on the display type. getTemplate: function () { - return this.config.type.toLowerCase() + ".njk" + return `${this.config.type.toLowerCase()}.njk`; }, // Add all the data to the template. @@ -144,8 +138,8 @@ Module.register("weather",{ // What to do when the weather provider has new information available? updateAvailable: function() { Log.log("New weather information available."); - this.updateDom(300); - this.scheduleUpdate(60000); + this.updateDom(0); + this.scheduleUpdate(); }, scheduleUpdate: function(delay = null) { @@ -214,9 +208,9 @@ Module.register("weather",{ } if(this.config.units === "imperial") { - return (parseFloat(value) / 25.4).toFixed(2) + " in"; + return `${(parseFloat(value) / 25.4).toFixed(2)} in`; } else { - return parseFloat(value).toFixed(1) + " mm"; + return `${parseFloat(value).toFixed(1)} mm`; } }.bind(this)); } diff --git a/modules/default/weather/weatherobject.js b/modules/default/weather/weatherobject.js index ff0de841..b6ee9b95 100644 --- a/modules/default/weather/weatherobject.js +++ b/modules/default/weather/weatherobject.js @@ -14,16 +14,16 @@ class WeatherObject { constructor() { - this.date = null - this.windSpeed = null - this.windDirection = null - this.sunrise = null - this.sunset = null - this.temperature = null - this.minTemperature = null - this.maxTemperature = null - this.weatherType = null - this.humidity = null + this.date = null; + this.windSpeed = null; + this.windDirection = null; + this.sunrise = null; + this.sunset = null; + this.temperature = null; + this.minTemperature = null; + this.maxTemperature = null; + this.weatherType = null; + this.humidity = null; } cardinalWindDirection () { @@ -78,4 +78,4 @@ class WeatherObject { var now = new Date(); return (this.sunrise < now && this.sunset > now) ? "sunset" : "sunrise"; } -}; \ No newline at end of file +} diff --git a/modules/default/weather/weatherprovider.js b/modules/default/weather/weatherprovider.js index 0be45663..bb139991 100644 --- a/modules/default/weather/weatherprovider.js +++ b/modules/default/weather/weatherprovider.js @@ -36,71 +36,71 @@ var WeatherProvider = Class.extend({ // Called when a weather provider is initialized. init: function(config) { this.config = config; - Log.info("Weather provider: " + this.providerName + " initialized.") + Log.info(`Weather provider: ${this.providerName} initialized.`); }, // 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) + this.config = config; + Log.info(`Weather provider: ${this.providerName} config set.`, this.config); }, // Called when the weather provider is about to start. start: function(config) { - Log.info("Weather provider: " + this.providerName + " started.") + Log.info(`Weather provider: ${this.providerName} started.`); }, // This method should start the API request to fetch the current weather. // This method should definetly be overwritten in the provider. fetchCurrentWeather: function() { - Log.warn("Weather provider: " + this.providerName + " does not subclass the fetchCurrentWeather method.") + Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchCurrentWeather method.`); }, // This method should start the API request to fetch the weather forecast. // This method should definetly be overwritten in the provider. fetchWeatherForecast: function() { - Log.warn("Weather provider: " + this.providerName + " does not subclass the fetchWeatherForecast method.") + Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchWeatherForecast method.`); }, // This returns a WeatherDay object for the current weather. currentWeather: function() { - return this.currentWeatherObject + return this.currentWeatherObject; }, // This returns an array of WeatherDay objects for the weather forecast. weatherForecast: function() { - return this.weatherForecastArray + return this.weatherForecastArray; }, // This returns the name of the fetched location or an empty string fetchedLocation: function() { - return this.fetchedLocationName || '' + return this.fetchedLocationName || ""; }, // Set the currentWeather and notify the delegate that new information is available. setCurrentWeather: function(currentWeatherObject) { // We should check here if we are passing a WeatherDay - this.currentWeatherObject = currentWeatherObject + this.currentWeatherObject = currentWeatherObject; - this.updateAvailable() + this.updateAvailable(); }, // Set the weatherForecastArray and notify the delegate that new information is available. setWeatherForecast: function(weatherForecastArray) { // We should check here if we are passing a WeatherDay - this.weatherForecastArray = weatherForecastArray + this.weatherForecastArray = weatherForecastArray; - this.updateAvailable() + this.updateAvailable(); }, // Set the fetched location name setFetchedLocation: function(name) { - this.fetchedLocationName = name + this.fetchedLocationName = name; }, // Notify the delegate that new weather is available. updateAvailable: function() { - this.delegate.updateAvailable(this) + this.delegate.updateAvailable(this); }, // A convinience function to make requests. It returns a promise. @@ -125,30 +125,30 @@ var WeatherProvider = Class.extend({ /** * Collection of registered weather providers. */ -WeatherProvider.providers = [] +WeatherProvider.providers = []; /** * Static method to register a new weather provider. */ WeatherProvider.register = function(providerIdentifier, providerDetails) { - WeatherProvider.providers[providerIdentifier.toLowerCase()] = WeatherProvider.extend(providerDetails) -} + WeatherProvider.providers[providerIdentifier.toLowerCase()] = WeatherProvider.extend(providerDetails); +}; /** * Static method to initialize a new weather provider. */ WeatherProvider.initialize = function(providerIdentifier, delegate) { - providerIdentifier = providerIdentifier.toLowerCase() + providerIdentifier = providerIdentifier.toLowerCase(); - var provider = new WeatherProvider.providers[providerIdentifier]() + var provider = new WeatherProvider.providers[providerIdentifier](); - provider.delegate = delegate - provider.setConfig(delegate.config) + provider.delegate = delegate; + provider.setConfig(delegate.config); provider.providerIdentifier = providerIdentifier; if (!provider.providerName) { - provider.providerName = providerIdentifier + provider.providerName = providerIdentifier; } return provider; -} \ No newline at end of file +}; From b853c00dd45eb8f897c6c1bc928d9f9a2103075b Mon Sep 17 00:00:00 2001 From: fewieden Date: Thu, 27 Dec 2018 23:12:28 +0100 Subject: [PATCH 28/28] Add changelog entry --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5ff3977..b65699f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [2.6.0] - Unreleased -*This release is scheduled to be released on 2018-10-01.* +*This release is scheduled to be released on 2019-01-01.* + +### Experimental +- New default [module weather](modules/default/weather). ### Added - Possibility to add classes to the cell of symbol, title and time of the events of calendar.