Release 2.29.0 (#3568)

## [2.29.0] - 2024-10-01

Thanks to: @bugsounet, @dkallen78, @jargordon, @khassel,
@KristjanESPERANTO, @MarcLandis, @rejas, @ryan-d-williams, @sdetweil,
@skpanagiotis.

> ⚠️ This release needs nodejs version `v20` or `v22`, minimum version
is `v20.9.0`

### Added

- [compliments] Added support for cron type date/time format entries mm
hh DD MM dow (minutes/hours/days/months and day of week) see
https://crontab.cronhub.io for construction (#3481)
- [core] Check config at every start of MagicMirror² (#3450)
- [core] Add spelling check (cspell): `npm run test:spelling` and handle
spelling issues (#3544)
- [core] removed `config.paths.vendor` (could not work because `vendor`
is hardcoded in `index.html`), renamed `config.paths.modules` to
`config.foreignModulesDir`, added variable `MM_CUSTOMCSS_FILE` which -
if set - overrides `config.customCss`, added variable `MM_MODULES_DIR`
which - if set - overrides `config.foreignModulesDir`, added test for
`MM_MODULES_DIR` (#3530)
- [core] elements are now removed from `index.html` when loading script
or stylesheet files fails
- [core] Added `MODULE_DOM_UPDATED` notification each time the DOM is
re-rendered via `updateDom` (#3534)
- [tests] added minimal needed node version to tests (currently v20.9.0)
to avoid releases with wrong node version info
- [tests] Added `node-libgpiod` library to electron-rebuild tests
(#3563)

### Removed

- [core] removed installer only files (#3492)
- [core] removed raspberry object from systeminformation (#3505)
- [linter] removed `eslint-plugin-import`, because it doesn't support
ESLint v9. We will reenter it later when it does.
- [tests] removed `onoff` library from electron-rebuild tests (#3563)

### Updated

- [weather] Updated `apiVersion` default from 2.5 to 3.0 (#3424)
- [core] Updated dependencies including stylistic-eslint
- [core] nail down `node-ical` version to `0.18.0` with exception
`allow-ghsas: GHSA-8hc4-vh64-cxmj` in `dep-review.yaml` (which should
removed after next `node-ical` update)
- [core] Updated SocketIO catch all to new API
- [core] Allow custom modules positions by scanning index.html for the
defined regions, instead of hard coded (PR #3518 fixes issue #3504)
- [core] Detail optimizations in `config_check.js`
- [core] Updated minimal needed node version in `package.json`
(currently v20.9.0) (#3559) and except for v21 (no security updates)
(#3561)
- [linter] Switch to ESLint v9 and flat config and replace
`eslint-plugin-unicorn` by `@eslint/js`
- [core] fix discovering module positions twice after #3450

### Fixed

- Fixed `checks` badge in README.md
- [weather] Fixed issue with the UK Met Office provider following a
change in their API paths and header info.
- [core] add check for node_helper loading for multiple instances of
same module (#3502)
- [weather] Fixed issue for respecting unit config on broadcasted
notifications
- [tests] Fixes calendar test by moving it from e2e to electron with
fixed date (#3532)
- [calendar] fixed sliceMultiDayEvents getting wrong count and
displaying incorrect entries, Europe/Berlin (#3542)
- [tests] ignore `js/positions.js` when linting (this file is created at
runtime)
- [calendar] fixed sliceMultiDayEvents showing previous day without
config enabled

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Michael Teeuw <michael@xonaymedia.nl>
Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ross Younger <crazyscot@gmail.com>
Co-authored-by: Veeck <github@veeck.de>
Co-authored-by: Bugsounet - Cédric <github@bugsounet.fr>
Co-authored-by: jkriegshauser <joshuakr@nvidia.com>
Co-authored-by: illimarkangur <116028111+illimarkangur@users.noreply.github.com>
Co-authored-by: sam detweiler <sdetweil@gmail.com>
Co-authored-by: vppencilsharpener <tim.pray@gmail.com>
Co-authored-by: veeck <michael.veeck@nebenan.de>
Co-authored-by: Paranoid93 <6515818+Paranoid93@users.noreply.github.com>
Co-authored-by: Brian O'Connor <btoconnor@users.noreply.github.com>
Co-authored-by: WallysWellies <59727507+WallysWellies@users.noreply.github.com>
Co-authored-by: Jason Stieber <jrstieber@gmail.com>
Co-authored-by: jargordon <50050429+jargordon@users.noreply.github.com>
Co-authored-by: Daniel <32464403+dkallen78@users.noreply.github.com>
Co-authored-by: Ryan Williams <65094007+ryan-d-williams@users.noreply.github.com>
Co-authored-by: Panagiotis Skias <panagiotis.skias@gmail.com>
Co-authored-by: Marc Landis <dirk.rettschlag@gmail.com>
This commit is contained in:
Karsten Hassel
2024-10-01 00:02:17 +02:00
committed by GitHub
parent 53fc814ff8
commit 94c3c699e8
84 changed files with 3899 additions and 2830 deletions

View File

@@ -1,3 +1,5 @@
/* global Cron */
Module.register("compliments", {
// Module config defaults.
defaults: {
@@ -21,10 +23,12 @@ Module.register("compliments", {
lastIndexUsed: -1,
// Set currentweather from module
currentWeatherType: "",
cron_regex: /^(((\d+,)+\d+|((\d+|[*])[/]\d+|((JAN|FEB|APR|MA[RY]|JU[LN]|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|APR|MA[RY]|JU[LN]|AUG|SEP|OCT|NOV|DEC))?))|(\d+-\d+)|\d+(-\d+)?[/]\d+(-\d+)?|\d+|[*]|(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?) ?){5}$/i,
date_regex: "[1-9.][0-9.][0-9.]{2}-([0][1-9]|[1][0-2])-([1-2][0-9]|[0][1-9]|[3][0-1])",
pre_defined_types: ["anytime", "morning", "afternoon", "evening"],
// Define required scripts.
getScripts () {
return ["moment.js"];
return ["croner.js", "moment.js"];
},
// Define start sequence.
@@ -38,11 +42,45 @@ Module.register("compliments", {
this.config.compliments = JSON.parse(response);
this.updateDom();
}
let minute_sync_delay = 1;
// loop thru all the configured when events
for (let m of Object.keys(this.config.compliments)) {
// if it is a cron entry
if (this.isCronEntry(m)) {
// we need to synch our interval cycle to the minute
minute_sync_delay = (60 - (moment().second())) * 1000;
break;
}
}
// Schedule update timer. sync to the minute start (if needed), so minute based events happen on the minute start
setTimeout(() => {
setInterval(() => {
this.updateDom(this.config.fadeSpeed);
}, this.config.updateInterval);
},
minute_sync_delay);
},
// Schedule update timer.
setInterval(() => {
this.updateDom(this.config.fadeSpeed);
}, this.config.updateInterval);
// check to see if this entry could be a cron entry wich contains spaces
isCronEntry (entry) {
return entry.includes(" ");
},
/**
* @param {string} cronExpression The cron expression. See https://croner.56k.guru/usage/pattern/
* @param {Date} [timestamp] The timestamp to check. Defaults to the current time.
* @returns {number} The number of seconds until the next cron run.
*/
getSecondsUntilNextCronRun (cronExpression, timestamp = new Date()) {
// Required for seconds precision
const adjustedTimestamp = new Date(timestamp.getTime() - 1000);
// https://www.npmjs.com/package/croner
const cronJob = new Cron(cronExpression);
const nextRunTime = cronJob.nextRun(adjustedTimestamp);
const secondsDelta = (nextRunTime - adjustedTimestamp) / 1000;
return secondsDelta;
},
/**
@@ -75,8 +113,9 @@ Module.register("compliments", {
* @returns {string[]} array with compliments for the time of the day.
*/
complimentArray () {
const hour = moment().hour();
const date = moment().format("YYYY-MM-DD");
const now = moment();
const hour = now.hour();
const date = now.format("YYYY-MM-DD");
let compliments = [];
// Add time of day compliments
@@ -91,22 +130,51 @@ Module.register("compliments", {
// Add compliments based on weather
if (this.currentWeatherType in this.config.compliments) {
Array.prototype.push.apply(compliments, this.config.compliments[this.currentWeatherType]);
// if the predefine list doesn't include it (yet)
if (!this.pre_defined_types.includes(this.currentWeatherType)) {
// add it
this.pre_defined_types.push(this.currentWeatherType);
}
}
// Add compliments for anytime
Array.prototype.push.apply(compliments, this.config.compliments.anytime);
// Add compliments for special days
for (let entry in this.config.compliments) {
if (new RegExp(entry).test(date)) {
// Only display compliments configured for the day if specialDayUnique is set to true
if (this.config.specialDayUnique) {
compliments.length = 0;
}
Array.prototype.push.apply(compliments, this.config.compliments[entry]);
// get the list of just date entry keys
let temp_list = Object.keys(this.config.compliments).filter((k) => {
if (this.pre_defined_types.includes(k)) return false;
else return true;
});
let date_compliments = [];
// Add compliments for special day/times
for (let entry of temp_list) {
// check if this could be a cron type entry
if (this.isCronEntry(entry)) {
// make sure the regex is valid
if (new RegExp(this.cron_regex).test(entry)) {
// check if we are in the time range for the cron entry
if (this.getSecondsUntilNextCronRun(entry, now.set("seconds", 0).toDate()) <= 1) {
// if so, use its notice entries
Array.prototype.push.apply(date_compliments, this.config.compliments[entry]);
}
} else Log.error(`compliments cron syntax invalid=${JSON.stringify(entry)}`);
} else if (new RegExp(entry).test(date)) {
Array.prototype.push.apply(date_compliments, this.config.compliments[entry]);
}
}
// if we found any date compliments
if (date_compliments.length) {
// and the special flag is true
if (this.config.specialDayUnique) {
// clear the non-date compliments if any
compliments.length = 0;
}
// put the date based compliments on the list
Array.prototype.push.apply(compliments, date_compliments);
}
return compliments;
},