Merge pull request #2648 from rejas/weather_cleanup

Add common methods to weatherobject
This commit is contained in:
Michael Teeuw 2021-09-03 11:08:43 +02:00 committed by GitHub
commit 06389e35f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 110 additions and 103 deletions

View File

@ -23,7 +23,9 @@
}, },
"rules": { "rules": {
"prettier/prettier": "error", "prettier/prettier": "error",
"eqeqeq": "error",
"no-prototype-builtins": "off", "no-prototype-builtins": "off",
"no-unused-vars": "off" "no-unused-vars": "off",
"no-useless-return": "error"
} }
} }

View File

@ -20,9 +20,10 @@ _This release is scheduled to be released on 2021-10-01._
- Refactor test configs, use default test config for all tests. - Refactor test configs, use default test config for all tests.
- Updated github templates. - Updated github templates.
- Actually test all js and css files when lint script is run. - Actually test all js and css files when lint script is run.
- Update jsdocs and print warnings during testing too - Update jsdocs and print warnings during testing too.
- Update weathergov provider to try fetching not just current, but also foreacst, when API URLs available. - Update weathergov provider to try fetching not just current, but also foreacst, when API URLs available.
- Refactored clock layout - Refactored clock layout.
- Refactored methods from weatherproviders into weatherobject (isDaytime, updateSunTime).
### Fixed ### Fixed

View File

@ -164,9 +164,7 @@ WeatherProvider.register("envcanada", {
// CORS errors when accessing EC // CORS errors when accessing EC
// //
getUrl() { getUrl() {
const path = "https://thingproxy.freeboard.io/fetch/https://dd.weather.gc.ca/citypage_weather/xml/" + this.config.provCode + "/" + this.config.siteCode + "_e.xml"; return "https://thingproxy.freeboard.io/fetch/https://dd.weather.gc.ca/citypage_weather/xml/" + this.config.provCode + "/" + this.config.siteCode + "_e.xml";
return path;
}, },
// //
@ -513,8 +511,6 @@ WeatherProvider.register("envcanada", {
weather.maxTemperature = this.convertTemp(nextTemp); weather.maxTemperature = this.convertTemp(nextTemp);
} }
} }
return;
}, },
// //
@ -560,8 +556,6 @@ WeatherProvider.register("envcanada", {
weather.precipitation = foreGroup[today].querySelector("abbreviatedForecast pop").textContent; weather.precipitation = foreGroup[today].querySelector("abbreviatedForecast pop").textContent;
weather.precipitationUnits = foreGroup[today].querySelector("abbreviatedForecast pop").getAttribute("units"); weather.precipitationUnits = foreGroup[today].querySelector("abbreviatedForecast pop").getAttribute("units");
} }
return;
}, },
// //

View File

@ -1,4 +1,4 @@
/* global WeatherProvider, WeatherObject, SunCalc */ /* global WeatherProvider, WeatherObject */
/* Magic Mirror /* Magic Mirror
* Module: Weather * Module: Weather
@ -105,19 +105,20 @@ WeatherProvider.register("smhi", {
* @returns {WeatherObject} The converted weatherdata at the specified location * @returns {WeatherObject} The converted weatherdata at the specified location
*/ */
convertWeatherDataToObject(weatherData, coordinates) { convertWeatherDataToObject(weatherData, coordinates) {
let currentWeather = new WeatherObject("metric", "metric", "metric"); //Weather data is only for Sweden and nobody in Sweden would use imperial // Weather data is only for Sweden and nobody in Sweden would use imperial
let currentWeather = new WeatherObject("metric", "metric", "metric");
currentWeather.date = moment(weatherData.validTime); currentWeather.date = moment(weatherData.validTime);
let times = SunCalc.getTimes(currentWeather.date.toDate(), coordinates.lat, coordinates.lon); currentWeather.updateSunTime(coordinates.lat, coordinates.lon);
currentWeather.sunrise = moment(times.sunrise, "X");
currentWeather.sunset = moment(times.sunset, "X");
currentWeather.humidity = this.paramValue(weatherData, "r"); currentWeather.humidity = this.paramValue(weatherData, "r");
currentWeather.temperature = this.paramValue(weatherData, "t"); currentWeather.temperature = this.paramValue(weatherData, "t");
currentWeather.windSpeed = this.paramValue(weatherData, "ws"); currentWeather.windSpeed = this.paramValue(weatherData, "ws");
currentWeather.windDirection = this.paramValue(weatherData, "wd"); currentWeather.windDirection = this.paramValue(weatherData, "wd");
currentWeather.weatherType = this.convertWeatherType(this.paramValue(weatherData, "Wsymb2"), this.isDayTime(currentWeather)); currentWeather.weatherType = this.convertWeatherType(this.paramValue(weatherData, "Wsymb2"), currentWeather.isDayTime());
//Determine the precipitation amount and category and update the weatherObject with it, the valuetype to use can be configured or uses median as default. // Determine the precipitation amount and category and update the
// weatherObject with it, the valuetype to use can be configured or uses
// median as default.
let precipitationValue = this.paramValue(weatherData, this.config.precipitationValue); let precipitationValue = this.paramValue(weatherData, this.config.precipitationValue);
switch (this.paramValue(weatherData, "pcat")) { switch (this.paramValue(weatherData, "pcat")) {
// 0 = No precipitation // 0 = No precipitation
@ -171,7 +172,7 @@ WeatherProvider.register("smhi", {
} }
//Keep track of what icons has been used for each hour of daytime and use the middle one for the forecast //Keep track of what icons has been used for each hour of daytime and use the middle one for the forecast
if (this.isDayTime(weatherObject)) { if (weatherObject.isDayTime()) {
dayWeatherTypes.push(weatherObject.weatherType); dayWeatherTypes.push(weatherObject.weatherType);
} }
if (dayWeatherTypes.length > 0) { if (dayWeatherTypes.length > 0) {
@ -201,16 +202,6 @@ WeatherProvider.register("smhi", {
return { lat: data.geometry.coordinates[0][1], lon: data.geometry.coordinates[0][0] }; return { lat: data.geometry.coordinates[0][1], lon: data.geometry.coordinates[0][0] };
}, },
/**
* Checks if the weatherObject is at dayTime.
*
* @param {WeatherObject} weatherObject The weatherObject to look at
* @returns {boolean} true if it is at dayTime
*/
isDayTime(weatherObject) {
return weatherObject.date.isBetween(weatherObject.sunrise, weatherObject.sunset, undefined, "[]");
},
/** /**
* The distance between the data points is increasing in the data the more distant the prediction is. * The distance between the data points is increasing in the data the more distant the prediction is.
* Find these gaps and fill them with the previous hours data to make the data returned a complete set. * Find these gaps and fill them with the previous hours data to make the data returned a complete set.

View File

@ -1,4 +1,4 @@
/* global WeatherProvider, WeatherObject, SunCalc */ /* global WeatherProvider, WeatherObject */
/* Magic Mirror /* Magic Mirror
* Module: Weather * Module: Weather
@ -116,9 +116,7 @@ WeatherProvider.register("ukmetoffice", {
} }
// determine the sunrise/sunset times - not supplied in UK Met Office data // determine the sunrise/sunset times - not supplied in UK Met Office data
let times = this.calcAstroData(location); currentWeather.updateSunTime(location.lat, location.lon);
currentWeather.sunrise = times[0];
currentWeather.sunset = times[1];
return currentWeather; return currentWeather;
}, },
@ -154,20 +152,6 @@ WeatherProvider.register("ukmetoffice", {
return days; return days;
}, },
/*
* calculate the astronomical data
*/
calcAstroData(location) {
const sunTimes = [];
// determine the sunrise/sunset times
let times = SunCalc.getTimes(new Date(), location.lat, location.lon);
sunTimes.push(moment(times.sunrise, "X"));
sunTimes.push(moment(times.sunset, "X"));
return sunTimes;
},
/* /*
* Convert the Met Office icons to a more usable name. * Convert the Met Office icons to a more usable name.
*/ */
@ -248,16 +232,16 @@ WeatherProvider.register("ukmetoffice", {
return windCardinals.hasOwnProperty(windDirection) ? windCardinals[windDirection] : null; return windCardinals.hasOwnProperty(windDirection) ? windCardinals[windDirection] : null;
}, },
/* /**
* Generates an url with api parameters based on the config. * Generates an url with api parameters based on the config.
* *
* return String - URL params. * @param {string} forecastType daily or 3hourly forecast
* @returns {string} url
*/ */
getParams(forecastType) { getParams(forecastType) {
let params = "?"; let params = "?";
params += "res=" + forecastType; params += "res=" + forecastType;
params += "&key=" + this.config.apiKey; params += "&key=" + this.config.apiKey;
return params; return params;
} }
}); });

View File

@ -1,4 +1,4 @@
/* global WeatherProvider, WeatherObject, SunCalc */ /* global WeatherProvider, WeatherObject */
/* Magic Mirror /* Magic Mirror
* Module: Weather * Module: Weather
@ -71,13 +71,11 @@ WeatherProvider.register("ukmetofficedatahub", {
// For DataHub requests, the API key/secret are sent in the headers rather than as query strings. // For DataHub requests, the API key/secret are sent in the headers rather than as query strings.
// Headers defined according to Data Hub API (https://metoffice.apiconnect.ibmcloud.com/metoffice/production/api) // Headers defined according to Data Hub API (https://metoffice.apiconnect.ibmcloud.com/metoffice/production/api)
getHeaders() { getHeaders() {
let headers = { return {
accept: "application/json", accept: "application/json",
"x-ibm-client-id": this.config.apiKey, "x-ibm-client-id": this.config.apiKey,
"x-ibm-client-secret": this.config.apiSecret "x-ibm-client-secret": this.config.apiSecret
}; };
return headers;
}, },
// Fetch data using supplied URL and request headers // Fetch data using supplied URL and request headers
@ -150,11 +148,9 @@ WeatherProvider.register("ukmetofficedatahub", {
} }
// Determine the sunrise/sunset times - (still) not supplied in UK Met Office data // Determine the sunrise/sunset times - (still) not supplied in UK Met Office data
// Passes {longitude, latitude, height} to calcAstroData // Passes {longitude, latitude} to SunCalc, could pass height to, but
// Could just pass lat/long from this.config, but returned data from MO also contains elevation // SunCalc.getTimes doesnt take that into account
let times = this.calcAstroData(currentWeatherData.features[0].geometry.coordinates); currentWeather.updateSunTime(this.config.lat, this.config.lon);
currentWeather.sunrise = times[0];
currentWeather.sunset = times[1];
return currentWeather; return currentWeather;
}, },
@ -223,7 +219,6 @@ WeatherProvider.register("ukmetofficedatahub", {
// Pass on full details so they can be used in custom templates // Pass on full details so they can be used in custom templates
// Note the units of the supplied data when using this (see top of file) // Note the units of the supplied data when using this (see top of file)
forecastWeather.rawData = forecastDataDays[day]; forecastWeather.rawData = forecastDataDays[day];
dailyForecasts.push(forecastWeather); dailyForecasts.push(forecastWeather);
@ -238,18 +233,6 @@ WeatherProvider.register("ukmetofficedatahub", {
this.fetchedLocationName = name; this.fetchedLocationName = name;
}, },
// Calculate sunrise/sunset times
calcAstroData(location) {
const sunTimes = [];
// Careful to pass values to SunCalc in correct order (latitude, longitude, elevation)
let times = SunCalc.getTimes(new Date(), location[1], location[0], location[2]);
sunTimes.push(moment(times.sunrise, "X"));
sunTimes.push(moment(times.sunset, "X"));
return sunTimes;
},
// Convert temperatures to Fahrenheit (from degrees C), if required // Convert temperatures to Fahrenheit (from degrees C), if required
convertTemp(tempInC) { convertTemp(tempInC) {
return this.config.tempUnits === "imperial" ? (tempInC * 9) / 5 + 32 : tempInC; return this.config.tempUnits === "imperial" ? (tempInC * 9) / 5 + 32 : tempInC;

View File

@ -89,7 +89,6 @@ WeatherProvider.register("weatherbit", {
currentWeather.windSpeed = parseFloat(currentWeatherData.data[0].wind_spd); currentWeather.windSpeed = parseFloat(currentWeatherData.data[0].wind_spd);
currentWeather.windDirection = currentWeatherData.data[0].wind_dir; currentWeather.windDirection = currentWeatherData.data[0].wind_dir;
currentWeather.weatherType = this.convertWeatherType(currentWeatherData.data[0].weather.icon); currentWeather.weatherType = this.convertWeatherType(currentWeatherData.data[0].weather.icon);
Log.log("Wx Icon: " + currentWeatherData.data[0].weather.icon);
currentWeather.sunrise = moment(currentWeatherData.data[0].sunrise, "HH:mm").add(tzOffset, "m"); currentWeather.sunrise = moment(currentWeatherData.data[0].sunrise, "HH:mm").add(tzOffset, "m");
currentWeather.sunset = moment(currentWeatherData.data[0].sunset, "HH:mm").add(tzOffset, "m"); currentWeather.sunset = moment(currentWeatherData.data[0].sunset, "HH:mm").add(tzOffset, "m");

View File

@ -1,4 +1,4 @@
/* global WeatherProvider, WeatherObject, SunCalc */ /* global WeatherProvider, WeatherObject */
/* Magic Mirror /* Magic Mirror
* Module: Weather * Module: Weather
@ -130,7 +130,7 @@ WeatherProvider.register("weathergov", {
// excellent, let's fetch some actual wx data // excellent, let's fetch some actual wx data
this.configURLs = true; this.configURLs = true;
// handle 'forecast' config, fall back to 'current' // handle 'forecast' config, fall back to 'current'
if (config.type == "forecast") { if (config.type === "forecast") {
this.fetchWeatherForecast(); this.fetchWeatherForecast();
} else { } else {
this.fetchCurrentWeather(); this.fetchCurrentWeather();
@ -158,18 +158,11 @@ WeatherProvider.register("weathergov", {
currentWeather.precipitation = this.convertLength(currentWeatherData.precipitationLastHour.value); currentWeather.precipitation = this.convertLength(currentWeatherData.precipitationLastHour.value);
currentWeather.feelsLikeTemp = this.convertTemp(currentWeatherData.heatIndex.value); currentWeather.feelsLikeTemp = this.convertTemp(currentWeatherData.heatIndex.value);
let isDaytime = true;
if (currentWeatherData.icon.includes("day")) {
isDaytime = true;
} else {
isDaytime = false;
}
currentWeather.weatherType = this.convertWeatherType(currentWeatherData.textDescription, isDaytime);
// determine the sunrise/sunset times - not supplied in weather.gov data // determine the sunrise/sunset times - not supplied in weather.gov data
let times = this.calcAstroData(this.config.lat, this.config.lon); currentWeather.updateSunTime(this.config.lat, this.config.lon);
currentWeather.sunrise = times[0];
currentWeather.sunset = times[1]; // update weatherType
currentWeather.weatherType = this.convertWeatherType(currentWeatherData.textDescription, currentWeather.isDayTime());
return currentWeather; return currentWeather;
}, },
@ -272,20 +265,6 @@ WeatherProvider.register("weathergov", {
} }
}, },
/*
* Calculate the astronomical data
*/
calcAstroData(lat, lon) {
const sunTimes = [];
// determine the sunrise/sunset times
let times = SunCalc.getTimes(new Date(), lat, lon);
sunTimes.push(moment(times.sunrise, "X"));
sunTimes.push(moment(times.sunset, "X"));
return sunTimes;
},
/* /*
* Convert the icons to a more usable name. * Convert the icons to a more usable name.
*/ */

View File

@ -1,3 +1,5 @@
/* global SunCalc */
/* Magic Mirror /* Magic Mirror
* Module: Weather * Module: Weather
* *
@ -10,6 +12,14 @@
* As soon as we start implementing the forecast, mode properties will be added. * As soon as we start implementing the forecast, mode properties will be added.
*/ */
class WeatherObject { class WeatherObject {
/**
* Constructor for a WeatherObject
*
* @param {string} units what units to use, "imperial" or "metric"
* @param {string} tempUnits what tempunits to use
* @param {string} windUnits what windunits to use
* @param {boolean} useKmh use kmh if true, mps if false
*/
constructor(units, tempUnits, windUnits, useKmh) { constructor(units, tempUnits, windUnits, useKmh) {
this.units = units; this.units = units;
this.tempUnits = tempUnits; this.tempUnits = tempUnits;
@ -80,8 +90,7 @@ class WeatherObject {
} }
kmhWindSpeed() { kmhWindSpeed() {
const windInKmh = this.windUnits === "imperial" ? this.windSpeed * 1.609344 : (this.windSpeed * 60 * 60) / 1000; return this.windUnits === "imperial" ? this.windSpeed * 1.609344 : (this.windSpeed * 60 * 60) / 1000;
return windInKmh;
} }
nextSunAction() { nextSunAction() {
@ -113,4 +122,31 @@ class WeatherObject {
return this.tempUnits === "imperial" ? feelsLike : ((feelsLike - 32) * 5) / 9; return this.tempUnits === "imperial" ? feelsLike : ((feelsLike - 32) * 5) / 9;
} }
/**
* Checks if the weatherObject is at dayTime.
*
* @returns {boolean} true if it is at dayTime
*/
isDayTime() {
return this.date.isBetween(this.sunrise, this.sunset, undefined, "[]");
}
/**
* Update the sunrise / sunset time depending on the location
*
* @param {number} lat latitude
* @param {number} lon longitude
*/
updateSunTime(lat, lon) {
let now = !this.date ? new Date() : this.date.toDate();
let times = SunCalc.getTimes(now, lat, lon);
this.sunrise = moment(times.sunrise, "X");
this.sunset = moment(times.sunset, "X");
}
}
/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {
module.exports = WeatherObject;
} }

15
package-lock.json generated
View File

@ -44,7 +44,8 @@
"stylelint": "^13.13.1", "stylelint": "^13.13.1",
"stylelint-config-prettier": "^8.0.2", "stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^22.0.0", "stylelint-config-standard": "^22.0.0",
"stylelint-prettier": "^1.2.0" "stylelint-prettier": "^1.2.0",
"suncalc": "^1.8.0"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=12"
@ -9373,6 +9374,12 @@
"node": ">= 8.0" "node": ">= 8.0"
} }
}, },
"node_modules/suncalc": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.8.0.tgz",
"integrity": "sha1-HZiYEJVjB4dQ9JlKlZ5lTYdqy/U=",
"dev": true
},
"node_modules/supports-color": { "node_modules/supports-color": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@ -17712,6 +17719,12 @@
"debug": "^4.1.0" "debug": "^4.1.0"
} }
}, },
"suncalc": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.8.0.tgz",
"integrity": "sha1-HZiYEJVjB4dQ9JlKlZ5lTYdqy/U=",
"dev": true
},
"supports-color": { "supports-color": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",

View File

@ -62,7 +62,8 @@
"stylelint": "^13.13.1", "stylelint": "^13.13.1",
"stylelint-config-prettier": "^8.0.2", "stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^22.0.0", "stylelint-config-standard": "^22.0.0",
"stylelint-prettier": "^1.2.0" "stylelint-prettier": "^1.2.0",
"suncalc": "^1.8.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"electron": "^13.2.3" "electron": "^13.2.3"

View File

@ -0,0 +1,24 @@
const WeatherObject = require("../../../modules/default/weather/weatherobject.js");
global.moment = require("moment");
global.SunCalc = require("suncalc");
describe("WeatherObject", function () {
let weatherobject;
beforeAll(function () {
weatherobject = new WeatherObject("metric", "metric", "metric", true);
});
it("should return true for daytime at noon", function () {
weatherobject.date = moment(12, "HH");
weatherobject.updateSunTime(-6.774877582342688, 37.63345667023327);
expect(weatherobject.isDayTime()).toBe(true);
});
it("should return false for daytime at midnight", function () {
weatherobject.date = moment(0, "HH");
weatherobject.updateSunTime(-6.774877582342688, 37.63345667023327);
expect(weatherobject.isDayTime()).toBe(false);
});
});