mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-06-27 03:39:55 +00:00
## [2.26.0] - 01-01-2024 Thanks to: @bnitkin, @bugsounet, @dependabot, @jkriegshauser, @kaennchenstruggle, @KristjanESPERANTO and @Ybbet. Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not all) of the work on this release as project collaborators. This version would not be there without their effort. Thank you guys! You are awesome! This release also marks the latest release by Michael Teeuw. For more info, please read the following post: [A New Chapter for MagicMirror: The Community Takes the Lead](https://forum.magicmirror.builders/topic/18329/a-new-chapter-for-magicmirror-the-community-takes-the-lead). ### Added - Added update notification updater (for 3rd party modules) - Added node 21 to the test matrix - Added transform object to calendar:customEvents - Added ESLint rules for jest (including jest/expect-expect and jest/no-done-callback) ### Removed - Removed Codecov workflow (not working anymore, other workflow required) (#3107) - Removed titleReplace from calendar, replaced + extended by customEvents (backward compatibility included) (#3249) - Removed failing unit test (#3254) - Removed some unused variables ### Updated - Update electron to v27 and update other dependencies as well as github actions - Update newsfeed: Use `html-to-text` instead of regex for transform description - Review ESLint config (#3269) - Updated dependencies - Clock module: optionally display current moon phase in addition to rise/set times - electron is now per default started without gpu, if needed it must be enabled with new env var `ELECTRON_ENABLE_GPU=1` on startup (#3226) - Replace prettier by stylistic in ESLint config to lint JavaScript (and disable some rules for `config/config.js*` files) - Update node-ical to v0.17.1 and fix tests ### Fixed - Avoid fade out/in on updateDom when many calendars are used - Fix the option eventClass on customEvents. - Fix yr API version in locationforecast and sunrise call (#3227) - Fix cloneObject() function to respect RegExp (#3237) - Fix newsfeed module for feeds using "a10:updated" tag (#3238) - Fix issue template (#3167) - Fix #3256 filter out bad results from rrule.between - Fix calendar events sometimes not respecting deleted events (#3250) - Fix electron loadurl locally on Windows when address "0.0.0.0" (#2550) - Fix updatanotification (update_helper.js): catch error if reponse is not an JSON format (check PM2) - Fix missing typeof in calendar module - Fix style issues after prettier update - Fix calendar test (#3291) by moving "Exdate check" from e2e to electron to run on a Thursday - Fix calendar config params `fetchInterval` and `excludedEvents` were never used from single calendar config (#3297) - Fix MM_PORT variable not used in electron and allow full path for MM_CONFIG_FILE variable (#3302) --------- Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Karsten Hassel <hassel@gmx.de> Co-authored-by: Malte Hallström <46646495+SkySails@users.noreply.github.com> Co-authored-by: Veeck <github@veeck.de> Co-authored-by: veeck <michael@veeck.de> Co-authored-by: dWoolridge <dwoolridge@charter.net> Co-authored-by: Johan <jojjepersson@yahoo.se> Co-authored-by: Dario Mratovich <dario_mratovich@hotmail.com> Co-authored-by: Dario Mratovich <dario.mratovich@outlook.com> Co-authored-by: Magnus <34011212+MagMar94@users.noreply.github.com> Co-authored-by: Naveen <172697+naveensrinivasan@users.noreply.github.com> Co-authored-by: buxxi <buxxi@omfilm.net> Co-authored-by: Thomas Hirschberger <47733292+Tom-Hirschberger@users.noreply.github.com> Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Co-authored-by: Andrés Vanegas Jiménez <142350+angeldeejay@users.noreply.github.com> Co-authored-by: Dave Child <dave@addedbytes.com> Co-authored-by: grenagit <46225780+grenagit@users.noreply.github.com> Co-authored-by: Grena <grena@grenabox.fr> Co-authored-by: Magnus Marthinsen <magmar@online.no> Co-authored-by: Patrick <psieg@users.noreply.github.com> Co-authored-by: Piotr Rajnisz <56397164+rajniszp@users.noreply.github.com> Co-authored-by: Suthep Yonphimai <tomzt@users.noreply.github.com> Co-authored-by: CarJem Generations (Carter Wallace) <cwallacecs@gmail.com> Co-authored-by: Nicholas Fogal <nfogal.misc@gmail.com> Co-authored-by: JakeBinney <126349119+JakeBinney@users.noreply.github.com> Co-authored-by: OWL4C <124401812+OWL4C@users.noreply.github.com> Co-authored-by: Oscar Björkman <17575446+oscarb@users.noreply.github.com> Co-authored-by: Ismar Slomic <ismar@slomic.no> Co-authored-by: Jørgen Veum-Wahlberg <jorgen.wahlberg@amedia.no> Co-authored-by: Eddie Hung <6740044+eddiehung@users.noreply.github.com> Co-authored-by: Bugsounet - Cédric <github@bugsounet.fr> Co-authored-by: bugsounet <bugsounet@bugsounet.fr> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Knapoc <Knapoc@users.noreply.github.com> Co-authored-by: sam detweiler <sdetweil@gmail.com> Co-authored-by: veeck <michael.veeck@nebenan.de> Co-authored-by: Paranoid93 <6515818+Paranoid93@users.noreply.github.com> Co-authored-by: NolanKingdon <27908974+NolanKingdon@users.noreply.github.com> Co-authored-by: J. Kenzal Hunter <kenzal.hunter@gmail.com> Co-authored-by: Teddy <teddy.payet@gmail.com> Co-authored-by: TeddyStarinvest <teddy.payet@starinvest.com> Co-authored-by: martingron <61826403+martingron@users.noreply.github.com> Co-authored-by: dgoth <132394363+dgoth@users.noreply.github.com> Co-authored-by: kaennchenstruggle <54073894+kaennchenstruggle@users.noreply.github.com> Co-authored-by: jkriegshauser <jkriegshauser@gmail.com> Co-authored-by: Ben Nitkin <ben@nitkin.net>
202 lines
6.2 KiB
JavaScript
202 lines
6.2 KiB
JavaScript
/* global WeatherProvider, WeatherObject, WeatherUtils */
|
|
|
|
/* MagicMirror²
|
|
* Module: Weather
|
|
*
|
|
* By Malcolm Oakes https://github.com/maloakes
|
|
* MIT Licensed.
|
|
*
|
|
* This class is a provider for UK Met Office Datapoint.
|
|
*/
|
|
WeatherProvider.register("ukmetoffice", {
|
|
// Set the name of the provider.
|
|
// 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: "UK Met Office",
|
|
|
|
// Set the default config properties that is specific to this provider
|
|
defaults: {
|
|
apiBase: "http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/json/",
|
|
locationID: false,
|
|
apiKey: ""
|
|
},
|
|
|
|
// Overwrite the fetchCurrentWeather method.
|
|
fetchCurrentWeather () {
|
|
this.fetchData(this.getUrl("3hourly"))
|
|
.then((data) => {
|
|
if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location || !data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length === 0) {
|
|
// Did not receive usable new data.
|
|
// Maybe this needs a better check?
|
|
return;
|
|
}
|
|
|
|
this.setFetchedLocation(`${data.SiteRep.DV.Location.name}, ${data.SiteRep.DV.Location.country}`);
|
|
|
|
const currentWeather = this.generateWeatherObjectFromCurrentWeather(data);
|
|
this.setCurrentWeather(currentWeather);
|
|
})
|
|
.catch(function (request) {
|
|
Log.error("Could not load data ... ", request);
|
|
})
|
|
.finally(() => this.updateAvailable());
|
|
},
|
|
|
|
// Overwrite the fetchCurrentWeather method.
|
|
fetchWeatherForecast () {
|
|
this.fetchData(this.getUrl("daily"))
|
|
.then((data) => {
|
|
if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location || !data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length === 0) {
|
|
// Did not receive usable new data.
|
|
// Maybe this needs a better check?
|
|
return;
|
|
}
|
|
|
|
this.setFetchedLocation(`${data.SiteRep.DV.Location.name}, ${data.SiteRep.DV.Location.country}`);
|
|
|
|
const forecast = this.generateWeatherObjectsFromForecast(data);
|
|
this.setWeatherForecast(forecast);
|
|
})
|
|
.catch(function (request) {
|
|
Log.error("Could not load data ... ", request);
|
|
})
|
|
.finally(() => this.updateAvailable());
|
|
},
|
|
|
|
/** UK Met Office Specific Methods - These are not part of the default provider methods */
|
|
/*
|
|
* Gets the complete url for the request
|
|
*/
|
|
getUrl (forecastType) {
|
|
return this.config.apiBase + this.config.locationID + this.getParams(forecastType);
|
|
},
|
|
|
|
/*
|
|
* Generate a WeatherObject based on currentWeatherInformation
|
|
*/
|
|
generateWeatherObjectFromCurrentWeather (currentWeatherData) {
|
|
const currentWeather = new WeatherObject();
|
|
const location = currentWeatherData.SiteRep.DV.Location;
|
|
|
|
// data times are always UTC
|
|
let nowUtc = moment.utc();
|
|
let midnightUtc = nowUtc.clone().startOf("day");
|
|
let timeInMins = nowUtc.diff(midnightUtc, "minutes");
|
|
|
|
// loop round each of the (5) periods, look for today (the first period may be yesterday)
|
|
for (const period of location.Period) {
|
|
const periodDate = moment.utc(period.value.substr(0, 10), "YYYY-MM-DD");
|
|
|
|
// ignore if period is before today
|
|
if (periodDate.isSameOrAfter(moment.utc().startOf("day"))) {
|
|
// check this is the period we want, after today the diff will be -ve
|
|
if (moment().diff(periodDate, "minutes") > 0) {
|
|
// loop round the reports looking for the one we are in
|
|
// $ value specifies the time in minutes-of-the-day: 0, 180, 360,...1260
|
|
for (const rep of period.Rep) {
|
|
const p = rep.$;
|
|
if (timeInMins >= p && timeInMins - 180 < p) {
|
|
// finally got the one we want, so populate weather object
|
|
currentWeather.humidity = rep.H;
|
|
currentWeather.temperature = rep.T;
|
|
currentWeather.feelsLikeTemp = rep.F;
|
|
currentWeather.precipitationProbability = parseInt(rep.Pp);
|
|
currentWeather.windSpeed = WeatherUtils.convertWindToMetric(rep.S);
|
|
currentWeather.windFromDirection = WeatherUtils.convertWindDirection(rep.D);
|
|
currentWeather.weatherType = this.convertWeatherType(rep.W);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// determine the sunrise/sunset times - not supplied in UK Met Office data
|
|
currentWeather.updateSunTime(location.lat, location.lon);
|
|
|
|
return currentWeather;
|
|
},
|
|
|
|
/*
|
|
* Generate WeatherObjects based on forecast information
|
|
*/
|
|
generateWeatherObjectsFromForecast (forecasts) {
|
|
const days = [];
|
|
|
|
// loop round the (5) periods getting the data
|
|
// for each period array, Day is [0], Night is [1]
|
|
for (const period of forecasts.SiteRep.DV.Location.Period) {
|
|
const weather = new WeatherObject();
|
|
|
|
// data times are always UTC
|
|
const dateStr = period.value;
|
|
let periodDate = moment.utc(dateStr.substr(0, 10), "YYYY-MM-DD");
|
|
|
|
// ignore if period is before today
|
|
if (periodDate.isSameOrAfter(moment.utc().startOf("day"))) {
|
|
// populate the weather object
|
|
weather.date = moment.utc(dateStr.substr(0, 10), "YYYY-MM-DD");
|
|
weather.minTemperature = period.Rep[1].Nm;
|
|
weather.maxTemperature = period.Rep[0].Dm;
|
|
weather.weatherType = this.convertWeatherType(period.Rep[0].W);
|
|
weather.precipitationProbability = parseInt(period.Rep[0].PPd);
|
|
|
|
days.push(weather);
|
|
}
|
|
}
|
|
|
|
return days;
|
|
},
|
|
|
|
/*
|
|
* Convert the Met Office icons to a more usable name.
|
|
*/
|
|
convertWeatherType (weatherType) {
|
|
const weatherTypes = {
|
|
0: "night-clear",
|
|
1: "day-sunny",
|
|
2: "night-alt-cloudy",
|
|
3: "day-cloudy",
|
|
5: "fog",
|
|
6: "fog",
|
|
7: "cloudy",
|
|
8: "cloud",
|
|
9: "night-sprinkle",
|
|
10: "day-sprinkle",
|
|
11: "raindrops",
|
|
12: "sprinkle",
|
|
13: "night-alt-showers",
|
|
14: "day-showers",
|
|
15: "rain",
|
|
16: "night-alt-sleet",
|
|
17: "day-sleet",
|
|
18: "sleet",
|
|
19: "night-alt-hail",
|
|
20: "day-hail",
|
|
21: "hail",
|
|
22: "night-alt-snow",
|
|
23: "day-snow",
|
|
24: "snow",
|
|
25: "night-alt-snow",
|
|
26: "day-snow",
|
|
27: "snow",
|
|
28: "night-alt-thunderstorm",
|
|
29: "day-thunderstorm",
|
|
30: "thunderstorm"
|
|
};
|
|
|
|
return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null;
|
|
},
|
|
|
|
/**
|
|
* Generates an url with api parameters based on the config.
|
|
* @param {string} forecastType daily or 3hourly forecast
|
|
* @returns {string} url
|
|
*/
|
|
getParams (forecastType) {
|
|
let params = "?";
|
|
params += `res=${forecastType}`;
|
|
params += `&key=${this.config.apiKey}`;
|
|
return params;
|
|
}
|
|
});
|