mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-06-27 03:39:55 +00:00
remove old weather modules
This commit is contained in:
parent
ad66c02735
commit
eb32ec89b4
@ -1,8 +0,0 @@
|
|||||||
# Module: Current Weather
|
|
||||||
|
|
||||||
> :warning: **This module is deprecated in favor of the [weather](https://docs.magicmirror.builders/modules/weather.html) module.**
|
|
||||||
|
|
||||||
The `currentweather` module is one of the default modules of the MagicMirror.
|
|
||||||
This module displays the current weather, including the windspeed, the sunset or sunrise time, the temperature and an icon to display the current conditions.
|
|
||||||
|
|
||||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/currentweather.html).
|
|
@ -1,15 +0,0 @@
|
|||||||
.currentweather .weathericon,
|
|
||||||
.currentweather .fa-home {
|
|
||||||
font-size: 75%;
|
|
||||||
line-height: 65px;
|
|
||||||
display: inline-block;
|
|
||||||
transform: translate(0, -3px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.currentweather .humidityIcon {
|
|
||||||
padding-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.currentweather .humidity-padding {
|
|
||||||
padding-bottom: 6px;
|
|
||||||
}
|
|
@ -1,600 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
/* Magic Mirror
|
|
||||||
* Module: CurrentWeather
|
|
||||||
*
|
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
|
||||||
* MIT Licensed.
|
|
||||||
*
|
|
||||||
* This module is deprecated. Any additional feature will no longer be merged.
|
|
||||||
*/
|
|
||||||
Module.register("currentweather", {
|
|
||||||
// Default module config.
|
|
||||||
defaults: {
|
|
||||||
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,
|
|
||||||
useKMPHwind: false,
|
|
||||||
lang: config.language,
|
|
||||||
decimalSymbol: ".",
|
|
||||||
showHumidity: false,
|
|
||||||
showSun: true,
|
|
||||||
degreeLabel: false,
|
|
||||||
showIndoorTemperature: false,
|
|
||||||
showIndoorHumidity: false,
|
|
||||||
showFeelsLike: true,
|
|
||||||
|
|
||||||
initialLoadDelay: 0, // 0 seconds delay
|
|
||||||
retryDelay: 2500,
|
|
||||||
|
|
||||||
apiVersion: "2.5",
|
|
||||||
apiBase: "https://api.openweathermap.org/data/",
|
|
||||||
weatherEndpoint: "weather",
|
|
||||||
|
|
||||||
appendLocationNameToHeader: true,
|
|
||||||
useLocationAsHeader: false,
|
|
||||||
|
|
||||||
calendarClass: "calendar",
|
|
||||||
tableClass: "large",
|
|
||||||
|
|
||||||
onlyTemp: false,
|
|
||||||
hideTemp: false,
|
|
||||||
roundTemp: false,
|
|
||||||
|
|
||||||
iconTable: {
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// create a variable for the first upcoming calendar event. Used if no location is specified.
|
|
||||||
firstEvent: false,
|
|
||||||
|
|
||||||
// create a variable to hold the location name based on the API result.
|
|
||||||
fetchedLocationName: "",
|
|
||||||
|
|
||||||
// Define required scripts.
|
|
||||||
getScripts: function () {
|
|
||||||
return ["moment.js"];
|
|
||||||
},
|
|
||||||
|
|
||||||
// Define required scripts.
|
|
||||||
getStyles: function () {
|
|
||||||
return ["weather-icons.css", "currentweather.css"];
|
|
||||||
},
|
|
||||||
|
|
||||||
// Define required translations.
|
|
||||||
getTranslations: function () {
|
|
||||||
// The translations for the default modules are defined in the core translation files.
|
|
||||||
// Therefor we can just return false. Otherwise we should have returned a dictionary.
|
|
||||||
// If you're trying to build your own module including translations, check out the documentation.
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Define start sequence.
|
|
||||||
start: function () {
|
|
||||||
Log.info("Starting module: " + this.name);
|
|
||||||
|
|
||||||
// Set locale.
|
|
||||||
moment.locale(config.language);
|
|
||||||
|
|
||||||
this.windSpeed = null;
|
|
||||||
this.windDirection = null;
|
|
||||||
this.windDeg = null;
|
|
||||||
this.sunriseSunsetTime = null;
|
|
||||||
this.sunriseSunsetIcon = null;
|
|
||||||
this.temperature = null;
|
|
||||||
this.indoorTemperature = null;
|
|
||||||
this.indoorHumidity = null;
|
|
||||||
this.weatherType = null;
|
|
||||||
this.feelsLike = null;
|
|
||||||
this.loaded = false;
|
|
||||||
this.scheduleUpdate(this.config.initialLoadDelay);
|
|
||||||
},
|
|
||||||
|
|
||||||
// add extra information of current weather
|
|
||||||
// windDirection, humidity, sunrise and sunset
|
|
||||||
addExtraInfoWeather: function (wrapper) {
|
|
||||||
var small = document.createElement("div");
|
|
||||||
small.className = "normal medium";
|
|
||||||
|
|
||||||
var windIcon = document.createElement("span");
|
|
||||||
windIcon.className = "wi wi-strong-wind dimmed";
|
|
||||||
small.appendChild(windIcon);
|
|
||||||
|
|
||||||
var windSpeed = document.createElement("span");
|
|
||||||
windSpeed.innerHTML = " " + this.windSpeed;
|
|
||||||
small.appendChild(windSpeed);
|
|
||||||
|
|
||||||
if (this.config.showWindDirection) {
|
|
||||||
var windDirection = document.createElement("sup");
|
|
||||||
if (this.config.showWindDirectionAsArrow) {
|
|
||||||
if (this.windDeg !== null) {
|
|
||||||
windDirection.innerHTML = ' <i class="fa fa-long-arrow-down" style="transform:rotate(' + this.windDeg + 'deg);"></i> ';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
windDirection.innerHTML = " " + this.translate(this.windDirection);
|
|
||||||
}
|
|
||||||
small.appendChild(windDirection);
|
|
||||||
}
|
|
||||||
var spacer = document.createElement("span");
|
|
||||||
spacer.innerHTML = " ";
|
|
||||||
small.appendChild(spacer);
|
|
||||||
|
|
||||||
if (this.config.showHumidity) {
|
|
||||||
var humidity = document.createElement("span");
|
|
||||||
humidity.innerHTML = this.humidity;
|
|
||||||
|
|
||||||
var supspacer = document.createElement("sup");
|
|
||||||
supspacer.innerHTML = " ";
|
|
||||||
|
|
||||||
var humidityIcon = document.createElement("sup");
|
|
||||||
humidityIcon.className = "wi wi-humidity humidityIcon";
|
|
||||||
humidityIcon.innerHTML = " ";
|
|
||||||
|
|
||||||
small.appendChild(humidity);
|
|
||||||
small.appendChild(supspacer);
|
|
||||||
small.appendChild(humidityIcon);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.showSun) {
|
|
||||||
var sunriseSunsetIcon = document.createElement("span");
|
|
||||||
sunriseSunsetIcon.className = "wi dimmed " + this.sunriseSunsetIcon;
|
|
||||||
small.appendChild(sunriseSunsetIcon);
|
|
||||||
|
|
||||||
var sunriseSunsetTime = document.createElement("span");
|
|
||||||
sunriseSunsetTime.innerHTML = " " + this.sunriseSunsetTime;
|
|
||||||
small.appendChild(sunriseSunsetTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
wrapper.appendChild(small);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Override dom generator.
|
|
||||||
getDom: function () {
|
|
||||||
var wrapper = document.createElement("div");
|
|
||||||
wrapper.className = this.config.tableClass;
|
|
||||||
|
|
||||||
if (this.config.appid === "") {
|
|
||||||
wrapper.innerHTML = "Please set the correct openweather <i>appid</i> in the config for module: " + this.name + ".";
|
|
||||||
wrapper.className = "dimmed light small";
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.loaded) {
|
|
||||||
wrapper.innerHTML = this.translate("LOADING");
|
|
||||||
wrapper.className = "dimmed light small";
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.onlyTemp === false) {
|
|
||||||
this.addExtraInfoWeather(wrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
var large = document.createElement("div");
|
|
||||||
large.className = "light";
|
|
||||||
|
|
||||||
var degreeLabel = "";
|
|
||||||
if (this.config.units === "metric" || this.config.units === "imperial") {
|
|
||||||
degreeLabel += "°";
|
|
||||||
}
|
|
||||||
if (this.config.degreeLabel) {
|
|
||||||
switch (this.config.units) {
|
|
||||||
case "metric":
|
|
||||||
degreeLabel += "C";
|
|
||||||
break;
|
|
||||||
case "imperial":
|
|
||||||
degreeLabel += "F";
|
|
||||||
break;
|
|
||||||
case "default":
|
|
||||||
degreeLabel += "K";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.decimalSymbol === "") {
|
|
||||||
this.config.decimalSymbol = ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.hideTemp === false) {
|
|
||||||
var weatherIcon = document.createElement("span");
|
|
||||||
weatherIcon.className = "wi weathericon wi-" + this.weatherType;
|
|
||||||
large.appendChild(weatherIcon);
|
|
||||||
|
|
||||||
var temperature = document.createElement("span");
|
|
||||||
temperature.className = "bright";
|
|
||||||
temperature.innerHTML = " " + this.temperature.replace(".", this.config.decimalSymbol) + degreeLabel;
|
|
||||||
large.appendChild(temperature);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.showIndoorTemperature && this.indoorTemperature) {
|
|
||||||
var indoorIcon = document.createElement("span");
|
|
||||||
indoorIcon.className = "fa fa-home";
|
|
||||||
large.appendChild(indoorIcon);
|
|
||||||
|
|
||||||
var indoorTemperatureElem = document.createElement("span");
|
|
||||||
indoorTemperatureElem.className = "bright";
|
|
||||||
indoorTemperatureElem.innerHTML = " " + this.indoorTemperature.replace(".", this.config.decimalSymbol) + degreeLabel;
|
|
||||||
large.appendChild(indoorTemperatureElem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.showIndoorHumidity && this.indoorHumidity) {
|
|
||||||
var indoorHumidityIcon = document.createElement("span");
|
|
||||||
indoorHumidityIcon.className = "fa fa-tint";
|
|
||||||
large.appendChild(indoorHumidityIcon);
|
|
||||||
|
|
||||||
var indoorHumidityElem = document.createElement("span");
|
|
||||||
indoorHumidityElem.className = "bright";
|
|
||||||
indoorHumidityElem.innerHTML = " " + this.indoorHumidity + "%";
|
|
||||||
large.appendChild(indoorHumidityElem);
|
|
||||||
}
|
|
||||||
|
|
||||||
wrapper.appendChild(large);
|
|
||||||
|
|
||||||
if (this.config.showFeelsLike && this.config.onlyTemp === false) {
|
|
||||||
var small = document.createElement("div");
|
|
||||||
small.className = "normal medium";
|
|
||||||
|
|
||||||
var feelsLike = document.createElement("span");
|
|
||||||
feelsLike.className = "dimmed";
|
|
||||||
feelsLike.innerHTML = this.translate("FEELS", {
|
|
||||||
DEGREE: this.feelsLike + degreeLabel
|
|
||||||
});
|
|
||||||
small.appendChild(feelsLike);
|
|
||||||
|
|
||||||
wrapper.appendChild(small);
|
|
||||||
}
|
|
||||||
|
|
||||||
return wrapper;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Override getHeader method.
|
|
||||||
getHeader: function () {
|
|
||||||
if (this.config.useLocationAsHeader && this.config.location !== false) {
|
|
||||||
return this.config.location;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.appendLocationNameToHeader) {
|
|
||||||
if (this.data.header) return this.data.header + " " + this.fetchedLocationName;
|
|
||||||
else return this.fetchedLocationName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.data.header ? this.data.header : "";
|
|
||||||
},
|
|
||||||
|
|
||||||
// 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(this.config.animationSpeed);
|
|
||||||
}
|
|
||||||
if (notification === "INDOOR_HUMIDITY") {
|
|
||||||
this.indoorHumidity = this.roundValue(payload);
|
|
||||||
this.updateDom(this.config.animationSpeed);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/* updateWeather(compliments)
|
|
||||||
* Requests new data from openweather.org.
|
|
||||||
* Calls processWeather on succesfull response.
|
|
||||||
*/
|
|
||||||
updateWeather: function () {
|
|
||||||
if (this.config.appid === "") {
|
|
||||||
Log.error("CurrentWeather: APPID not set!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var url = this.config.apiBase + this.config.apiVersion + "/" + this.config.weatherEndpoint + this.getParams();
|
|
||||||
var self = this;
|
|
||||||
var retry = true;
|
|
||||||
|
|
||||||
var weatherRequest = new XMLHttpRequest();
|
|
||||||
weatherRequest.open("GET", url, true);
|
|
||||||
weatherRequest.onreadystatechange = function () {
|
|
||||||
if (this.readyState === 4) {
|
|
||||||
if (this.status === 200) {
|
|
||||||
self.processWeather(JSON.parse(this.response));
|
|
||||||
} else if (this.status === 401) {
|
|
||||||
self.updateDom(self.config.animationSpeed);
|
|
||||||
|
|
||||||
Log.error(self.name + ": Incorrect APPID.");
|
|
||||||
retry = true;
|
|
||||||
} else {
|
|
||||||
Log.error(self.name + ": Could not load weather.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retry) {
|
|
||||||
self.scheduleUpdate(self.loaded ? -1 : self.config.retryDelay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
weatherRequest.send();
|
|
||||||
},
|
|
||||||
|
|
||||||
/* 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.appid;
|
|
||||||
|
|
||||||
return params;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* processWeather(data)
|
|
||||||
* Uses the received data to set the various values.
|
|
||||||
*
|
|
||||||
* argument data object - Weather information received form openweather.org.
|
|
||||||
*/
|
|
||||||
processWeather: function (data) {
|
|
||||||
if (!data || !data.main || typeof data.main.temp === "undefined") {
|
|
||||||
// Did not receive usable new data.
|
|
||||||
// Maybe this needs a better check?
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.humidity = parseFloat(data.main.humidity);
|
|
||||||
this.temperature = this.roundValue(data.main.temp);
|
|
||||||
this.fetchedLocationName = data.name;
|
|
||||||
this.feelsLike = 0;
|
|
||||||
|
|
||||||
if (this.config.useBeaufort) {
|
|
||||||
this.windSpeed = this.ms2Beaufort(this.roundValue(data.wind.speed));
|
|
||||||
} else if (this.config.useKMPHwind) {
|
|
||||||
this.windSpeed = parseFloat((data.wind.speed * 60 * 60) / 1000).toFixed(0);
|
|
||||||
} else {
|
|
||||||
this.windSpeed = parseFloat(data.wind.speed).toFixed(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ONLY WORKS IF TEMP IN C //
|
|
||||||
var windInMph = parseFloat(data.wind.speed * 2.23694);
|
|
||||||
|
|
||||||
var tempInF = 0;
|
|
||||||
switch (this.config.units) {
|
|
||||||
case "metric":
|
|
||||||
tempInF = 1.8 * this.temperature + 32;
|
|
||||||
break;
|
|
||||||
case "imperial":
|
|
||||||
tempInF = this.temperature;
|
|
||||||
break;
|
|
||||||
case "default":
|
|
||||||
tempInF = 1.8 * (this.temperature - 273.15) + 32;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (windInMph > 3 && tempInF < 50) {
|
|
||||||
// windchill
|
|
||||||
var windChillInF = Math.round(35.74 + 0.6215 * tempInF - 35.75 * Math.pow(windInMph, 0.16) + 0.4275 * tempInF * Math.pow(windInMph, 0.16));
|
|
||||||
var windChillInC = (windChillInF - 32) * (5 / 9);
|
|
||||||
// this.feelsLike = windChillInC.toFixed(0);
|
|
||||||
|
|
||||||
switch (this.config.units) {
|
|
||||||
case "metric":
|
|
||||||
this.feelsLike = windChillInC.toFixed(0);
|
|
||||||
break;
|
|
||||||
case "imperial":
|
|
||||||
this.feelsLike = windChillInF.toFixed(0);
|
|
||||||
break;
|
|
||||||
case "default":
|
|
||||||
this.feelsLike = (windChillInC + 273.15).toFixed(0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (tempInF > 80 && this.humidity > 40) {
|
|
||||||
// heat index
|
|
||||||
var Hindex =
|
|
||||||
-42.379 +
|
|
||||||
2.04901523 * tempInF +
|
|
||||||
10.14333127 * this.humidity -
|
|
||||||
0.22475541 * tempInF * this.humidity -
|
|
||||||
6.83783 * Math.pow(10, -3) * tempInF * tempInF -
|
|
||||||
5.481717 * Math.pow(10, -2) * this.humidity * this.humidity +
|
|
||||||
1.22874 * Math.pow(10, -3) * tempInF * tempInF * this.humidity +
|
|
||||||
8.5282 * Math.pow(10, -4) * tempInF * this.humidity * this.humidity -
|
|
||||||
1.99 * Math.pow(10, -6) * tempInF * tempInF * this.humidity * this.humidity;
|
|
||||||
|
|
||||||
switch (this.config.units) {
|
|
||||||
case "metric":
|
|
||||||
this.feelsLike = parseFloat((Hindex - 32) / 1.8).toFixed(0);
|
|
||||||
break;
|
|
||||||
case "imperial":
|
|
||||||
this.feelsLike = Hindex.toFixed(0);
|
|
||||||
break;
|
|
||||||
case "default":
|
|
||||||
var tc = parseFloat((Hindex - 32) / 1.8) + 273.15;
|
|
||||||
this.feelsLike = tc.toFixed(0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.feelsLike = parseFloat(this.temperature).toFixed(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.windDirection = this.deg2Cardinal(data.wind.deg);
|
|
||||||
this.windDeg = data.wind.deg;
|
|
||||||
this.weatherType = this.config.iconTable[data.weather[0].icon];
|
|
||||||
|
|
||||||
var now = new Date();
|
|
||||||
var sunrise = new Date(data.sys.sunrise * 1000);
|
|
||||||
var sunset = new Date(data.sys.sunset * 1000);
|
|
||||||
|
|
||||||
// The moment().format('h') method has a bug on the Raspberry Pi.
|
|
||||||
// So we need to generate the timestring manually.
|
|
||||||
// See issue: https://github.com/MichMich/MagicMirror/issues/181
|
|
||||||
var sunriseSunsetDateObject = sunrise < now && sunset > now ? sunset : sunrise;
|
|
||||||
var timeString = moment(sunriseSunsetDateObject).format("HH:mm");
|
|
||||||
if (this.config.timeFormat !== 24) {
|
|
||||||
//var hours = sunriseSunsetDateObject.getHours() % 12 || 12;
|
|
||||||
if (this.config.showPeriod) {
|
|
||||||
if (this.config.showPeriodUpper) {
|
|
||||||
//timeString = hours + moment(sunriseSunsetDateObject).format(':mm A');
|
|
||||||
timeString = moment(sunriseSunsetDateObject).format("h:mm A");
|
|
||||||
} else {
|
|
||||||
//timeString = hours + moment(sunriseSunsetDateObject).format(':mm a');
|
|
||||||
timeString = moment(sunriseSunsetDateObject).format("h:mm a");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//timeString = hours + moment(sunriseSunsetDateObject).format(':mm');
|
|
||||||
timeString = moment(sunriseSunsetDateObject).format("h:mm");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sunriseSunsetTime = timeString;
|
|
||||||
this.sunriseSunsetIcon = sunrise < now && sunset > now ? "wi-sunset" : "wi-sunrise";
|
|
||||||
|
|
||||||
this.show(this.config.animationSpeed, { lockString: this.identifier });
|
|
||||||
this.loaded = true;
|
|
||||||
this.updateDom(this.config.animationSpeed);
|
|
||||||
this.sendNotification("CURRENTWEATHER_DATA", { data: data });
|
|
||||||
this.sendNotification("CURRENTWEATHER_TYPE", { type: this.config.iconTable[data.weather[0].icon].replace("-", "_") });
|
|
||||||
},
|
|
||||||
|
|
||||||
/* scheduleUpdate()
|
|
||||||
* Schedule next update.
|
|
||||||
*
|
|
||||||
* argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used.
|
|
||||||
*/
|
|
||||||
scheduleUpdate: function (delay) {
|
|
||||||
var nextLoad = this.config.updateInterval;
|
|
||||||
if (typeof delay !== "undefined" && delay >= 0) {
|
|
||||||
nextLoad = delay;
|
|
||||||
}
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
setTimeout(function () {
|
|
||||||
self.updateWeather();
|
|
||||||
}, nextLoad);
|
|
||||||
},
|
|
||||||
|
|
||||||
/* ms2Beaufort(ms)
|
|
||||||
* Converts m2 to beaufort (windspeed).
|
|
||||||
*
|
|
||||||
* see:
|
|
||||||
* https://www.spc.noaa.gov/faq/tornado/beaufort.html
|
|
||||||
* https://en.wikipedia.org/wiki/Beaufort_scale#Modern_scale
|
|
||||||
*
|
|
||||||
* argument ms number - Windspeed in m/s.
|
|
||||||
*
|
|
||||||
* return number - Windspeed in beaufort.
|
|
||||||
*/
|
|
||||||
ms2Beaufort: function (ms) {
|
|
||||||
var kmh = (ms * 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;
|
|
||||||
},
|
|
||||||
|
|
||||||
deg2Cardinal: function (deg) {
|
|
||||||
if (deg > 11.25 && deg <= 33.75) {
|
|
||||||
return "NNE";
|
|
||||||
} else if (deg > 33.75 && deg <= 56.25) {
|
|
||||||
return "NE";
|
|
||||||
} else if (deg > 56.25 && deg <= 78.75) {
|
|
||||||
return "ENE";
|
|
||||||
} else if (deg > 78.75 && deg <= 101.25) {
|
|
||||||
return "E";
|
|
||||||
} else if (deg > 101.25 && deg <= 123.75) {
|
|
||||||
return "ESE";
|
|
||||||
} else if (deg > 123.75 && deg <= 146.25) {
|
|
||||||
return "SE";
|
|
||||||
} else if (deg > 146.25 && deg <= 168.75) {
|
|
||||||
return "SSE";
|
|
||||||
} else if (deg > 168.75 && deg <= 191.25) {
|
|
||||||
return "S";
|
|
||||||
} else if (deg > 191.25 && deg <= 213.75) {
|
|
||||||
return "SSW";
|
|
||||||
} else if (deg > 213.75 && deg <= 236.25) {
|
|
||||||
return "SW";
|
|
||||||
} else if (deg > 236.25 && deg <= 258.75) {
|
|
||||||
return "WSW";
|
|
||||||
} else if (deg > 258.75 && deg <= 281.25) {
|
|
||||||
return "W";
|
|
||||||
} else if (deg > 281.25 && deg <= 303.75) {
|
|
||||||
return "WNW";
|
|
||||||
} else if (deg > 303.75 && deg <= 326.25) {
|
|
||||||
return "NW";
|
|
||||||
} else if (deg > 326.25 && deg <= 348.75) {
|
|
||||||
return "NNW";
|
|
||||||
} else {
|
|
||||||
return "N";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/* function(temperature)
|
|
||||||
* Rounds a temperature to 1 decimal or integer (depending on config.roundTemp).
|
|
||||||
*
|
|
||||||
* argument temperature number - Temperature.
|
|
||||||
*
|
|
||||||
* return string - Rounded Temperature.
|
|
||||||
*/
|
|
||||||
roundValue: function (temperature) {
|
|
||||||
var decimals = this.config.roundTemp ? 0 : 1;
|
|
||||||
var roundValue = parseFloat(temperature).toFixed(decimals);
|
|
||||||
return roundValue === "-0" ? 0 : roundValue;
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,9 +0,0 @@
|
|||||||
const NodeHelper = require("node_helper");
|
|
||||||
const Log = require("logger");
|
|
||||||
|
|
||||||
module.exports = NodeHelper.create({
|
|
||||||
// Override start method.
|
|
||||||
start: function () {
|
|
||||||
Log.warn(`The module '${this.name}' is deprecated in favor of the 'weather'-module, please refer to the documentation for a migration path`);
|
|
||||||
}
|
|
||||||
});
|
|
@ -4,7 +4,7 @@
|
|||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
*/
|
*/
|
||||||
const defaultModules = ["alert", "calendar", "clock", "compliments", "currentweather", "helloworld", "newsfeed", "weatherforecast", "updatenotification", "weather"];
|
const defaultModules = ["alert", "calendar", "clock", "compliments", "helloworld", "newsfeed", "updatenotification", "weather"];
|
||||||
|
|
||||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||||
if (typeof module !== "undefined") {
|
if (typeof module !== "undefined") {
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
# Module: Weather Forecast
|
|
||||||
|
|
||||||
> :warning: **This module is deprecated in favor of the [weather](https://docs.magicmirror.builders/modules/weather.html) module.**
|
|
||||||
|
|
||||||
The `weatherforecast` module is one of the default modules of the MagicMirror.
|
|
||||||
This module displays the weather forecast for the coming week, including an an icon to display the current conditions, the minimum temperature and the maximum temperature.
|
|
||||||
|
|
||||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/weatherforecast.html).
|
|
@ -1,9 +0,0 @@
|
|||||||
const NodeHelper = require("node_helper");
|
|
||||||
const Log = require("logger");
|
|
||||||
|
|
||||||
module.exports = NodeHelper.create({
|
|
||||||
// Override start method.
|
|
||||||
start: function () {
|
|
||||||
Log.warn(`The module '${this.name}' is deprecated in favor of the 'weather'-module, please refer to the documentation for a migration path`);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,27 +0,0 @@
|
|||||||
.weatherforecast .day {
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weatherforecast .weather-icon {
|
|
||||||
padding-right: 30px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weatherforecast .min-temp {
|
|
||||||
padding-left: 20px;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weatherforecast .rain {
|
|
||||||
padding-left: 20px;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weatherforecast tr.colored .min-temp {
|
|
||||||
color: #bcddff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weatherforecast tr.colored .max-temp {
|
|
||||||
color: #ff8e99;
|
|
||||||
}
|
|
@ -1,518 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
/* Magic Mirror
|
|
||||||
* Module: WeatherForecast
|
|
||||||
*
|
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
|
||||||
* MIT Licensed.
|
|
||||||
*
|
|
||||||
* This module is deprecated. Any additional feature will no longer be merged.
|
|
||||||
*/
|
|
||||||
Module.register("weatherforecast", {
|
|
||||||
// Default module config.
|
|
||||||
defaults: {
|
|
||||||
location: false,
|
|
||||||
locationID: false,
|
|
||||||
lat: false,
|
|
||||||
lon: false,
|
|
||||||
appid: "",
|
|
||||||
units: config.units,
|
|
||||||
maxNumberOfDays: 7,
|
|
||||||
showRainAmount: false,
|
|
||||||
updateInterval: 10 * 60 * 1000, // every 10 minutes
|
|
||||||
animationSpeed: 1000,
|
|
||||||
timeFormat: config.timeFormat,
|
|
||||||
lang: config.language,
|
|
||||||
decimalSymbol: ".",
|
|
||||||
fade: true,
|
|
||||||
fadePoint: 0.25, // Start on 1/4th of the list.
|
|
||||||
colored: false,
|
|
||||||
scale: false,
|
|
||||||
|
|
||||||
initialLoadDelay: 2500, // 2.5 seconds delay. This delay is used to keep the OpenWeather API happy.
|
|
||||||
retryDelay: 2500,
|
|
||||||
|
|
||||||
apiVersion: "2.5",
|
|
||||||
apiBase: "https://api.openweathermap.org/data/",
|
|
||||||
forecastEndpoint: "forecast/daily",
|
|
||||||
excludes: false,
|
|
||||||
|
|
||||||
appendLocationNameToHeader: true,
|
|
||||||
calendarClass: "calendar",
|
|
||||||
tableClass: "small",
|
|
||||||
|
|
||||||
roundTemp: false,
|
|
||||||
|
|
||||||
iconTable: {
|
|
||||||
"01d": "wi-day-sunny",
|
|
||||||
"02d": "wi-day-cloudy",
|
|
||||||
"03d": "wi-cloudy",
|
|
||||||
"04d": "wi-cloudy-windy",
|
|
||||||
"09d": "wi-showers",
|
|
||||||
"10d": "wi-rain",
|
|
||||||
"11d": "wi-thunderstorm",
|
|
||||||
"13d": "wi-snow",
|
|
||||||
"50d": "wi-fog",
|
|
||||||
"01n": "wi-night-clear",
|
|
||||||
"02n": "wi-night-cloudy",
|
|
||||||
"03n": "wi-night-cloudy",
|
|
||||||
"04n": "wi-night-cloudy",
|
|
||||||
"09n": "wi-night-showers",
|
|
||||||
"10n": "wi-night-rain",
|
|
||||||
"11n": "wi-night-thunderstorm",
|
|
||||||
"13n": "wi-night-snow",
|
|
||||||
"50n": "wi-night-alt-cloudy-windy"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// create a variable for the first upcoming calendar event. Used if no location is specified.
|
|
||||||
firstEvent: false,
|
|
||||||
|
|
||||||
// create a variable to hold the location name based on the API result.
|
|
||||||
fetchedLocationName: "",
|
|
||||||
|
|
||||||
// Define required scripts.
|
|
||||||
getScripts: function () {
|
|
||||||
return ["moment.js"];
|
|
||||||
},
|
|
||||||
|
|
||||||
// Define required scripts.
|
|
||||||
getStyles: function () {
|
|
||||||
return ["weather-icons.css", "weatherforecast.css"];
|
|
||||||
},
|
|
||||||
|
|
||||||
// Define required translations.
|
|
||||||
getTranslations: function () {
|
|
||||||
// The translations for the default modules are defined in the core translation files.
|
|
||||||
// Therefor we can just return false. Otherwise we should have returned a dictionary.
|
|
||||||
// If you're trying to build your own module including translations, check out the documentation.
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Define start sequence.
|
|
||||||
start: function () {
|
|
||||||
Log.info("Starting module: " + this.name);
|
|
||||||
|
|
||||||
// Set locale.
|
|
||||||
moment.locale(config.language);
|
|
||||||
|
|
||||||
this.forecast = [];
|
|
||||||
this.loaded = false;
|
|
||||||
this.scheduleUpdate(this.config.initialLoadDelay);
|
|
||||||
|
|
||||||
this.updateTimer = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Override dom generator.
|
|
||||||
getDom: function () {
|
|
||||||
var wrapper = document.createElement("div");
|
|
||||||
|
|
||||||
if (this.config.appid === "" || this.config.appid === "YOUR_OPENWEATHER_API_KEY") {
|
|
||||||
wrapper.innerHTML = "Please set the correct openweather <i>appid</i> in the config for module: " + this.name + ".";
|
|
||||||
wrapper.className = "dimmed light small";
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.loaded) {
|
|
||||||
wrapper.innerHTML = this.translate("LOADING");
|
|
||||||
wrapper.className = "dimmed light small";
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
var table = document.createElement("table");
|
|
||||||
table.className = this.config.tableClass;
|
|
||||||
|
|
||||||
for (var f in this.forecast) {
|
|
||||||
var forecast = this.forecast[f];
|
|
||||||
|
|
||||||
var row = document.createElement("tr");
|
|
||||||
if (this.config.colored) {
|
|
||||||
row.className = "colored";
|
|
||||||
}
|
|
||||||
table.appendChild(row);
|
|
||||||
|
|
||||||
var dayCell = document.createElement("td");
|
|
||||||
dayCell.className = "day";
|
|
||||||
dayCell.innerHTML = forecast.day;
|
|
||||||
row.appendChild(dayCell);
|
|
||||||
|
|
||||||
var iconCell = document.createElement("td");
|
|
||||||
iconCell.className = "bright weather-icon";
|
|
||||||
row.appendChild(iconCell);
|
|
||||||
|
|
||||||
var icon = document.createElement("span");
|
|
||||||
icon.className = "wi weathericon " + forecast.icon;
|
|
||||||
iconCell.appendChild(icon);
|
|
||||||
|
|
||||||
var degreeLabel = "";
|
|
||||||
if (this.config.units === "metric" || this.config.units === "imperial") {
|
|
||||||
degreeLabel += "°";
|
|
||||||
}
|
|
||||||
if (this.config.scale) {
|
|
||||||
switch (this.config.units) {
|
|
||||||
case "metric":
|
|
||||||
degreeLabel += "C";
|
|
||||||
break;
|
|
||||||
case "imperial":
|
|
||||||
degreeLabel += "F";
|
|
||||||
break;
|
|
||||||
case "default":
|
|
||||||
degreeLabel = "K";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.decimalSymbol === "" || this.config.decimalSymbol === " ") {
|
|
||||||
this.config.decimalSymbol = ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxTempCell = document.createElement("td");
|
|
||||||
maxTempCell.innerHTML = forecast.maxTemp.replace(".", this.config.decimalSymbol) + degreeLabel;
|
|
||||||
maxTempCell.className = "align-right bright max-temp";
|
|
||||||
row.appendChild(maxTempCell);
|
|
||||||
|
|
||||||
var minTempCell = document.createElement("td");
|
|
||||||
minTempCell.innerHTML = forecast.minTemp.replace(".", this.config.decimalSymbol) + degreeLabel;
|
|
||||||
minTempCell.className = "align-right min-temp";
|
|
||||||
row.appendChild(minTempCell);
|
|
||||||
|
|
||||||
if (this.config.showRainAmount) {
|
|
||||||
var rainCell = document.createElement("td");
|
|
||||||
if (isNaN(forecast.rain)) {
|
|
||||||
rainCell.innerHTML = "";
|
|
||||||
} else {
|
|
||||||
if (config.units !== "imperial") {
|
|
||||||
rainCell.innerHTML = parseFloat(forecast.rain).toFixed(1).replace(".", this.config.decimalSymbol) + " mm";
|
|
||||||
} else {
|
|
||||||
rainCell.innerHTML = (parseFloat(forecast.rain) / 25.4).toFixed(2).replace(".", this.config.decimalSymbol) + " in";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rainCell.className = "align-right bright rain";
|
|
||||||
row.appendChild(rainCell);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.fade && this.config.fadePoint < 1) {
|
|
||||||
if (this.config.fadePoint < 0) {
|
|
||||||
this.config.fadePoint = 0;
|
|
||||||
}
|
|
||||||
var startingPoint = this.forecast.length * this.config.fadePoint;
|
|
||||||
var steps = this.forecast.length - startingPoint;
|
|
||||||
if (f >= startingPoint) {
|
|
||||||
var currentStep = f - startingPoint;
|
|
||||||
row.style.opacity = 1 - (1 / steps) * currentStep;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return table;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Override getHeader method.
|
|
||||||
getHeader: function () {
|
|
||||||
if (this.config.appendLocationNameToHeader) {
|
|
||||||
if (this.data.header) return this.data.header + " " + this.fetchedLocationName;
|
|
||||||
else return this.fetchedLocationName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.data.header ? this.data.header : "";
|
|
||||||
},
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/* updateWeather(compliments)
|
|
||||||
* Requests new data from openweather.org.
|
|
||||||
* Calls processWeather on successful response.
|
|
||||||
*/
|
|
||||||
updateWeather: function () {
|
|
||||||
if (this.config.appid === "") {
|
|
||||||
Log.error("WeatherForecast: APPID not set!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var url = this.config.apiBase + this.config.apiVersion + "/" + this.config.forecastEndpoint + this.getParams();
|
|
||||||
var self = this;
|
|
||||||
var retry = true;
|
|
||||||
|
|
||||||
var weatherRequest = new XMLHttpRequest();
|
|
||||||
weatherRequest.open("GET", url, true);
|
|
||||||
weatherRequest.onreadystatechange = function () {
|
|
||||||
if (this.readyState === 4) {
|
|
||||||
if (this.status === 200) {
|
|
||||||
self.processWeather(JSON.parse(this.response));
|
|
||||||
} else if (this.status === 401) {
|
|
||||||
self.updateDom(self.config.animationSpeed);
|
|
||||||
|
|
||||||
if (self.config.forecastEndpoint === "forecast/daily") {
|
|
||||||
self.config.forecastEndpoint = "forecast";
|
|
||||||
Log.warn(self.name + ": Your AppID does not support long term forecasts. Switching to fallback endpoint.");
|
|
||||||
}
|
|
||||||
|
|
||||||
retry = true;
|
|
||||||
} else {
|
|
||||||
Log.error(self.name + ": Could not load weather.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retry) {
|
|
||||||
self.scheduleUpdate(self.loaded ? -1 : self.config.retryDelay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
weatherRequest.send();
|
|
||||||
},
|
|
||||||
|
|
||||||
/* 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.lat && this.config.lon) {
|
|
||||||
params += "lat=" + this.config.lat + "&lon=" + this.config.lon;
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
let numberOfDays;
|
|
||||||
if (this.config.forecastEndpoint === "forecast") {
|
|
||||||
numberOfDays = this.config.maxNumberOfDays < 1 || this.config.maxNumberOfDays > 5 ? 5 : this.config.maxNumberOfDays;
|
|
||||||
// don't get forecasts for the next day, as it would not represent the whole day
|
|
||||||
numberOfDays = numberOfDays * 8 - (Math.round(new Date().getHours() / 3) % 8);
|
|
||||||
} else {
|
|
||||||
numberOfDays = this.config.maxNumberOfDays < 1 || this.config.maxNumberOfDays > 17 ? 7 : this.config.maxNumberOfDays;
|
|
||||||
}
|
|
||||||
params += "&cnt=" + numberOfDays;
|
|
||||||
|
|
||||||
params += "&exclude=" + this.config.excludes;
|
|
||||||
params += "&units=" + this.config.units;
|
|
||||||
params += "&lang=" + this.config.lang;
|
|
||||||
params += "&APPID=" + this.config.appid;
|
|
||||||
|
|
||||||
return params;
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* parserDataWeather(data)
|
|
||||||
*
|
|
||||||
* Use the parse to keep the same struct between daily and forecast Endpoint
|
|
||||||
* from openweather.org
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
parserDataWeather: function (data) {
|
|
||||||
if (data.hasOwnProperty("main")) {
|
|
||||||
data["temp"] = { min: data.main.temp_min, max: data.main.temp_max };
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* processWeather(data)
|
|
||||||
* Uses the received data to set the various values.
|
|
||||||
*
|
|
||||||
* argument data object - Weather information received form openweather.org.
|
|
||||||
*/
|
|
||||||
processWeather: function (data, momenttz) {
|
|
||||||
let mom = momenttz ? momenttz : moment; // Exception last.
|
|
||||||
|
|
||||||
// Forcast16 (paid) API endpoint provides this data. Onecall endpoint
|
|
||||||
// does not.
|
|
||||||
if (data.city) {
|
|
||||||
this.fetchedLocationName = data.city.name + ", " + data.city.country;
|
|
||||||
} else if (this.config.location) {
|
|
||||||
this.fetchedLocationName = this.config.location;
|
|
||||||
} else {
|
|
||||||
this.fetchedLocationName = "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
this.forecast = [];
|
|
||||||
var lastDay = null;
|
|
||||||
var forecastData = {};
|
|
||||||
var dayStarts = 8;
|
|
||||||
var dayEnds = 17;
|
|
||||||
|
|
||||||
if (data.city && data.city.sunrise && data.city.sunset) {
|
|
||||||
dayStarts = new Date(mom.unix(data.city.sunrise).locale("en").format("YYYY/MM/DD HH:mm:ss")).getHours();
|
|
||||||
dayEnds = new Date(mom.unix(data.city.sunset).locale("en").format("YYYY/MM/DD HH:mm:ss")).getHours();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle different structs between forecast16 and onecall endpoints
|
|
||||||
var forecastList = null;
|
|
||||||
if (data.list) {
|
|
||||||
forecastList = data.list;
|
|
||||||
} else if (data.daily) {
|
|
||||||
forecastList = data.daily;
|
|
||||||
} else {
|
|
||||||
Log.error("Unexpected forecast data");
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0, count = forecastList.length; i < count; i++) {
|
|
||||||
var forecast = forecastList[i];
|
|
||||||
forecast = this.parserDataWeather(forecast); // hack issue #1017
|
|
||||||
|
|
||||||
var day;
|
|
||||||
var hour;
|
|
||||||
if (forecast.dt_txt) {
|
|
||||||
day = mom(forecast.dt_txt, "YYYY-MM-DD hh:mm:ss").format("ddd");
|
|
||||||
hour = new Date(mom(forecast.dt_txt).locale("en").format("YYYY-MM-DD HH:mm:ss")).getHours();
|
|
||||||
} else {
|
|
||||||
day = mom(forecast.dt, "X").format("ddd");
|
|
||||||
hour = new Date(mom(forecast.dt, "X")).getHours();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (day !== lastDay) {
|
|
||||||
forecastData = {
|
|
||||||
day: day,
|
|
||||||
icon: this.config.iconTable[forecast.weather[0].icon],
|
|
||||||
maxTemp: this.roundValue(forecast.temp.max),
|
|
||||||
minTemp: this.roundValue(forecast.temp.min),
|
|
||||||
rain: this.processRain(forecast, forecastList, mom)
|
|
||||||
};
|
|
||||||
this.forecast.push(forecastData);
|
|
||||||
lastDay = day;
|
|
||||||
|
|
||||||
// Stop processing when maxNumberOfDays is reached
|
|
||||||
if (this.forecast.length === this.config.maxNumberOfDays) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Log.log("Compare max: ", forecast.temp.max, parseFloat(forecastData.maxTemp));
|
|
||||||
forecastData.maxTemp = forecast.temp.max > parseFloat(forecastData.maxTemp) ? this.roundValue(forecast.temp.max) : forecastData.maxTemp;
|
|
||||||
//Log.log("Compare min: ", forecast.temp.min, parseFloat(forecastData.minTemp));
|
|
||||||
forecastData.minTemp = forecast.temp.min < parseFloat(forecastData.minTemp) ? this.roundValue(forecast.temp.min) : forecastData.minTemp;
|
|
||||||
|
|
||||||
// Since we don't want an icon from the start of the day (in the middle of the night)
|
|
||||||
// we update the icon as long as it's somewhere during the day.
|
|
||||||
if (hour > dayStarts && hour < dayEnds) {
|
|
||||||
forecastData.icon = this.config.iconTable[forecast.weather[0].icon];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Log.log(this.forecast);
|
|
||||||
this.show(this.config.animationSpeed, { lockString: this.identifier });
|
|
||||||
this.loaded = true;
|
|
||||||
this.updateDom(this.config.animationSpeed);
|
|
||||||
},
|
|
||||||
|
|
||||||
/* scheduleUpdate()
|
|
||||||
* Schedule next update.
|
|
||||||
*
|
|
||||||
* argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used.
|
|
||||||
*/
|
|
||||||
scheduleUpdate: function (delay) {
|
|
||||||
var nextLoad = this.config.updateInterval;
|
|
||||||
if (typeof delay !== "undefined" && delay >= 0) {
|
|
||||||
nextLoad = delay;
|
|
||||||
}
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
clearTimeout(this.updateTimer);
|
|
||||||
this.updateTimer = setTimeout(function () {
|
|
||||||
self.updateWeather();
|
|
||||||
}, nextLoad);
|
|
||||||
},
|
|
||||||
|
|
||||||
/* ms2Beaufort(ms)
|
|
||||||
* Converts m2 to beaufort (windspeed).
|
|
||||||
*
|
|
||||||
* see:
|
|
||||||
* https://www.spc.noaa.gov/faq/tornado/beaufort.html
|
|
||||||
* https://en.wikipedia.org/wiki/Beaufort_scale#Modern_scale
|
|
||||||
*
|
|
||||||
* argument ms number - Windspeed in m/s.
|
|
||||||
*
|
|
||||||
* return number - Windspeed in beaufort.
|
|
||||||
*/
|
|
||||||
ms2Beaufort: function (ms) {
|
|
||||||
var kmh = (ms * 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;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* function(temperature)
|
|
||||||
* Rounds a temperature to 1 decimal or integer (depending on config.roundTemp).
|
|
||||||
*
|
|
||||||
* argument temperature number - Temperature.
|
|
||||||
*
|
|
||||||
* return string - Rounded Temperature.
|
|
||||||
*/
|
|
||||||
roundValue: function (temperature) {
|
|
||||||
var decimals = this.config.roundTemp ? 0 : 1;
|
|
||||||
var roundValue = parseFloat(temperature).toFixed(decimals);
|
|
||||||
return roundValue === "-0" ? 0 : roundValue;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* processRain(forecast, allForecasts)
|
|
||||||
* Calculates the amount of rain for a whole day even if long term forecasts isn't available for the appid.
|
|
||||||
*
|
|
||||||
* When using the the fallback endpoint forecasts are provided in 3h intervals and the rain-property is an object instead of number.
|
|
||||||
* That object has a property "3h" which contains the amount of rain since the previous forecast in the list.
|
|
||||||
* This code finds all forecasts that is for the same day and sums the amount of rain and returns that.
|
|
||||||
*/
|
|
||||||
processRain: function (forecast, allForecasts, momenttz) {
|
|
||||||
let mom = momenttz ? momenttz : moment; // Exception last.
|
|
||||||
|
|
||||||
//If the amount of rain actually is a number, return it
|
|
||||||
if (!isNaN(forecast.rain)) {
|
|
||||||
return forecast.rain;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Find all forecasts that is for the same day
|
|
||||||
var checkDateTime = forecast.dt_txt ? mom(forecast.dt_txt, "YYYY-MM-DD hh:mm:ss") : mom(forecast.dt, "X");
|
|
||||||
var daysForecasts = allForecasts.filter(function (item) {
|
|
||||||
var itemDateTime = item.dt_txt ? mom(item.dt_txt, "YYYY-MM-DD hh:mm:ss") : mom(item.dt, "X");
|
|
||||||
return itemDateTime.isSame(checkDateTime, "day") && item.rain instanceof Object;
|
|
||||||
});
|
|
||||||
|
|
||||||
//If no rain this day return undefined so it wont be displayed for this day
|
|
||||||
if (daysForecasts.length === 0) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Summarize all the rain from the matching days
|
|
||||||
return daysForecasts
|
|
||||||
.map(function (item) {
|
|
||||||
return Object.values(item.rain)[0];
|
|
||||||
})
|
|
||||||
.reduce(function (a, b) {
|
|
||||||
return a + b;
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
});
|
|
File diff suppressed because it is too large
Load Diff
@ -1,66 +0,0 @@
|
|||||||
/* eslint no-multi-spaces: 0 */
|
|
||||||
describe("Functions module currentweather", function () {
|
|
||||||
// Fake for use by currentweather.js
|
|
||||||
Module = {};
|
|
||||||
config = {};
|
|
||||||
Module.definitions = {};
|
|
||||||
Module.register = function (name, moduleDefinition) {
|
|
||||||
Module.definitions[name] = moduleDefinition;
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeAll(function () {
|
|
||||||
require("../../../modules/default/currentweather/currentweather.js");
|
|
||||||
Module.definitions.currentweather.config = {};
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("roundValue", function () {
|
|
||||||
describe("this.config.roundTemp is true", function () {
|
|
||||||
beforeAll(function () {
|
|
||||||
Module.definitions.currentweather.config.roundTemp = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const values = [
|
|
||||||
// index 0 value
|
|
||||||
// index 1 expect
|
|
||||||
[1, "1"],
|
|
||||||
[1.0, "1"],
|
|
||||||
[1.02, "1"],
|
|
||||||
[10.12, "10"],
|
|
||||||
[2.0, "2"],
|
|
||||||
["2.12", "2"],
|
|
||||||
[10.1, "10"]
|
|
||||||
];
|
|
||||||
|
|
||||||
values.forEach((value) => {
|
|
||||||
it(`for ${value[0]} should be return ${value[1]}`, function () {
|
|
||||||
expect(Module.definitions.currentweather.roundValue(value[0])).toBe(value[1]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("this.config.roundTemp is false", function () {
|
|
||||||
beforeAll(function () {
|
|
||||||
Module.definitions.currentweather.config.roundTemp = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
const values = [
|
|
||||||
// index 0 value
|
|
||||||
// index 1 expect
|
|
||||||
[1, "1.0"],
|
|
||||||
[1.0, "1.0"],
|
|
||||||
[1.02, "1.0"],
|
|
||||||
[10.12, "10.1"],
|
|
||||||
[2.0, "2.0"],
|
|
||||||
["2.12", "2.1"],
|
|
||||||
[10.1, "10.1"],
|
|
||||||
[10.1, "10.1"]
|
|
||||||
];
|
|
||||||
|
|
||||||
values.forEach((value) => {
|
|
||||||
it(`for ${value[0]} should be return ${value[1]}`, function () {
|
|
||||||
expect(Module.definitions.currentweather.roundValue(value[0])).toBe(value[1]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,116 +0,0 @@
|
|||||||
/* eslint no-multi-spaces: 0 */
|
|
||||||
const moment = require("moment-timezone");
|
|
||||||
const data = require("../../configs/data/weatherforecast_data.json");
|
|
||||||
|
|
||||||
describe("Functions module weatherforecast", function () {
|
|
||||||
beforeAll(function () {
|
|
||||||
Module = {};
|
|
||||||
config = {};
|
|
||||||
Module.definitions = {};
|
|
||||||
Module.register = function (name, moduleDefinition) {
|
|
||||||
Module.definitions[name] = moduleDefinition;
|
|
||||||
};
|
|
||||||
require("../../../modules/default/weatherforecast/weatherforecast.js");
|
|
||||||
Module.definitions.weatherforecast.config = {};
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("roundValue", function () {
|
|
||||||
describe("this.config.roundTemp is true", function () {
|
|
||||||
beforeAll(function () {
|
|
||||||
Module.definitions.weatherforecast.config.roundTemp = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const values = [
|
|
||||||
// index 0 value
|
|
||||||
// index 1 expect
|
|
||||||
[1, "1"],
|
|
||||||
[1.0, "1"],
|
|
||||||
[1.02, "1"],
|
|
||||||
[10.12, "10"],
|
|
||||||
[2.0, "2"],
|
|
||||||
["2.12", "2"],
|
|
||||||
[10.1, "10"]
|
|
||||||
];
|
|
||||||
|
|
||||||
values.forEach((value) => {
|
|
||||||
it(`for ${value[0]} should be return ${value[1]}`, function () {
|
|
||||||
expect(Module.definitions.weatherforecast.roundValue(value[0])).toBe(value[1]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("this.config.roundTemp is false", function () {
|
|
||||||
beforeAll(function () {
|
|
||||||
Module.definitions.weatherforecast.config.roundTemp = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
const values = [
|
|
||||||
// index 0 value
|
|
||||||
// index 1 expect
|
|
||||||
[1, "1.0"],
|
|
||||||
[1.0, "1.0"],
|
|
||||||
[1.02, "1.0"],
|
|
||||||
[10.12, "10.1"],
|
|
||||||
[2.0, "2.0"],
|
|
||||||
["2.12", "2.1"],
|
|
||||||
[10.1, "10.1"],
|
|
||||||
[10.1, "10.1"]
|
|
||||||
];
|
|
||||||
|
|
||||||
values.forEach((value) => {
|
|
||||||
it(`for ${value[0]} should be return ${value[1]}`, function () {
|
|
||||||
expect(Module.definitions.weatherforecast.roundValue(value[0])).toBe(value[1]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("forecastIcons", function () {
|
|
||||||
Log = {
|
|
||||||
error: function () {}
|
|
||||||
};
|
|
||||||
|
|
||||||
let originalLocale;
|
|
||||||
let originalTimeZone;
|
|
||||||
beforeAll(function () {
|
|
||||||
originalLocale = moment.locale();
|
|
||||||
originalTimeZone = moment.tz.guess();
|
|
||||||
moment.locale("hi");
|
|
||||||
moment.tz.setDefault("Europe/Warsaw");
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("forecastIcons sunset specified", function () {
|
|
||||||
beforeAll(function () {
|
|
||||||
Module.definitions.weatherforecast.Log = {};
|
|
||||||
Module.definitions.weatherforecast.forecast = [];
|
|
||||||
Module.definitions.weatherforecast.show = Module.definitions.weatherforecast.updateDom = function () {};
|
|
||||||
Module.definitions.weatherforecast.config = Module.definitions.weatherforecast.defaults;
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`returns correct icons with sunset time`, function () {
|
|
||||||
Module.definitions.weatherforecast.processWeather(data.withSunset, moment);
|
|
||||||
let forecastData = Module.definitions.weatherforecast.forecast;
|
|
||||||
expect(forecastData.length).toBe(4);
|
|
||||||
expect(forecastData[2].icon).toBe("wi-rain");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("forecastIcons sunset not specified", function () {
|
|
||||||
beforeAll(function () {
|
|
||||||
Module.definitions.weatherforecast.forecast = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`returns correct icons with out sunset time`, function () {
|
|
||||||
Module.definitions.weatherforecast.processWeather(data.withoutSunset, moment);
|
|
||||||
let forecastData = Module.definitions.weatherforecast.forecast;
|
|
||||||
expect(forecastData.length).toBe(4);
|
|
||||||
expect(forecastData[2].icon).toBe("wi-rain");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(function () {
|
|
||||||
moment.locale(originalLocale);
|
|
||||||
moment.tz.setDefault(originalTimeZone);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
x
Reference in New Issue
Block a user