mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-06-28 20:22:53 +00:00
Update compliments with support for cron type date/time for selections, addition to just date. (#3481)
> - What does the pull request accomplish? Use a list if needed. this change allows uses to configure date/time events for compliments.. also linked site that will build the cron entry.. and example was Happy hour in a pub, on fri/sat between 5 and 7 pm. or just after midnight on Halloween (Boooooo!) I also added testcases for #3478 (and added support for this in MMM-Config), with a custom, drop down selection list of the types.. ) | if this is approved I will update the module doc
This commit is contained in:
parent
974a1da9f0
commit
d9665b35df
@ -32,6 +32,7 @@ Thanks to: @btoconnor, @bugsounet, @JasonStieber, @khassel, @kleinmantara and @W
|
|||||||
|
|
||||||
### Added
|
### 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
|
||||||
- [calendar] Added config option "showEndsOnlyWithDuration" for default calendar
|
- [calendar] Added config option "showEndsOnlyWithDuration" for default calendar
|
||||||
- [compliments] Added `specialDayUnique` config option, defaults to `false` (#3465)
|
- [compliments] Added `specialDayUnique` config option, defaults to `false` (#3465)
|
||||||
- [weather] Provider weathergov: Use `precipitationLast3Hours` if `precipitationLastHour` is `null` (#3124)
|
- [weather] Provider weathergov: Use `precipitationLast3Hours` if `precipitationLastHour` is `null` (#3124)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
/* global Cron */
|
||||||
|
|
||||||
Module.register("compliments", {
|
Module.register("compliments", {
|
||||||
// Module config defaults.
|
// Module config defaults.
|
||||||
defaults: {
|
defaults: {
|
||||||
@ -21,10 +23,12 @@ Module.register("compliments", {
|
|||||||
lastIndexUsed: -1,
|
lastIndexUsed: -1,
|
||||||
// Set currentweather from module
|
// Set currentweather from module
|
||||||
currentWeatherType: "",
|
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.
|
// Define required scripts.
|
||||||
getScripts () {
|
getScripts () {
|
||||||
return ["moment.js"];
|
return ["croner.js", "moment.js"];
|
||||||
},
|
},
|
||||||
|
|
||||||
// Define start sequence.
|
// Define start sequence.
|
||||||
@ -38,12 +42,46 @@ Module.register("compliments", {
|
|||||||
this.config.compliments = JSON.parse(response);
|
this.config.compliments = JSON.parse(response);
|
||||||
this.updateDom();
|
this.updateDom();
|
||||||
}
|
}
|
||||||
|
let minute_sync_delay = 1;
|
||||||
// Schedule update timer.
|
// 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(() => {
|
setInterval(() => {
|
||||||
this.updateDom(this.config.fadeSpeed);
|
this.updateDom(this.config.fadeSpeed);
|
||||||
}, this.config.updateInterval);
|
}, this.config.updateInterval);
|
||||||
},
|
},
|
||||||
|
minute_sync_delay);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a random index for a list of compliments.
|
* Generate a random index for a list of compliments.
|
||||||
@ -75,8 +113,9 @@ Module.register("compliments", {
|
|||||||
* @returns {string[]} array with compliments for the time of the day.
|
* @returns {string[]} array with compliments for the time of the day.
|
||||||
*/
|
*/
|
||||||
complimentArray () {
|
complimentArray () {
|
||||||
const hour = moment().hour();
|
const now = moment();
|
||||||
const date = moment().format("YYYY-MM-DD");
|
const hour = now.hour();
|
||||||
|
const date = now.format("YYYY-MM-DD");
|
||||||
let compliments = [];
|
let compliments = [];
|
||||||
|
|
||||||
// Add time of day compliments
|
// Add time of day compliments
|
||||||
@ -91,20 +130,49 @@ Module.register("compliments", {
|
|||||||
// Add compliments based on weather
|
// Add compliments based on weather
|
||||||
if (this.currentWeatherType in this.config.compliments) {
|
if (this.currentWeatherType in this.config.compliments) {
|
||||||
Array.prototype.push.apply(compliments, this.config.compliments[this.currentWeatherType]);
|
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
|
// Add compliments for anytime
|
||||||
Array.prototype.push.apply(compliments, this.config.compliments.anytime);
|
Array.prototype.push.apply(compliments, this.config.compliments.anytime);
|
||||||
|
|
||||||
// Add compliments for special days
|
// get the list of just date entry keys
|
||||||
for (let entry in this.config.compliments) {
|
let temp_list = Object.keys(this.config.compliments).filter((k) => {
|
||||||
if (new RegExp(entry).test(date)) {
|
if (this.pre_defined_types.includes(k)) return false;
|
||||||
// Only display compliments configured for the day if specialDayUnique is set to true
|
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) {
|
if (this.config.specialDayUnique) {
|
||||||
|
// clear the non-date compliments if any
|
||||||
compliments.length = 0;
|
compliments.length = 0;
|
||||||
}
|
}
|
||||||
Array.prototype.push.apply(compliments, this.config.compliments[entry]);
|
// put the date based compliments on the list
|
||||||
}
|
Array.prototype.push.apply(compliments, date_compliments);
|
||||||
}
|
}
|
||||||
|
|
||||||
return compliments;
|
return compliments;
|
||||||
|
18
tests/configs/modules/compliments/compliments_cron_entry.js
Normal file
18
tests/configs/modules/compliments/compliments_cron_entry.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
let config = {
|
||||||
|
modules: [
|
||||||
|
{
|
||||||
|
module: "compliments",
|
||||||
|
position: "middle_center",
|
||||||
|
config: {
|
||||||
|
specialDayUnique: true,
|
||||||
|
compliments: {
|
||||||
|
anytime: ["just a test"],
|
||||||
|
"00-10 16-19 * * fri": ["just pub time"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||||
|
if (typeof module !== "undefined") { module.exports = config; }
|
@ -0,0 +1,18 @@
|
|||||||
|
let config = {
|
||||||
|
modules: [
|
||||||
|
{
|
||||||
|
module: "compliments",
|
||||||
|
position: "middle_center",
|
||||||
|
config: {
|
||||||
|
specialDayUnique: true,
|
||||||
|
compliments: {
|
||||||
|
anytime: ["just a test"],
|
||||||
|
"* * * * *": ["anytime cron"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||||
|
if (typeof module !== "undefined") { module.exports = config; }
|
@ -77,5 +77,16 @@ describe("Compliments module", () => {
|
|||||||
await expect(doTest(["Special day message"])).resolves.toBe(true);
|
await expect(doTest(["Special day message"])).resolves.toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("cron type key", () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await helpers.startApplication("tests/configs/modules/compliments/compliments_e2e_cron_entry.js");
|
||||||
|
await helpers.getDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("compliments array contains only special value", async () => {
|
||||||
|
await expect(doTest(["anytime cron"])).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -43,5 +43,41 @@ describe("Compliments module", () => {
|
|||||||
await expect(doTest(["Happy new year!"])).resolves.toBe(true);
|
await expect(doTest(["Happy new year!"])).resolves.toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Test only custom date events shown with new property", () => {
|
||||||
|
it("shows 'Special day message' on May 6", async () => {
|
||||||
|
await helpers.startApplication("tests/configs/modules/compliments/compliments_specialDayUnique_true.js", "06 May 2022 10:00:00 GMT");
|
||||||
|
await expect(doTest(["Special day message"])).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Test all date events shown without neww property", () => {
|
||||||
|
it("shows 'any message' on May 6", async () => {
|
||||||
|
await helpers.startApplication("tests/configs/modules/compliments/compliments_specialDayUnique_false.js", "06 May 2022 10:00:00 GMT");
|
||||||
|
await expect(doTest(["Special day message", "Typical message 1", "Typical message 2", "Typical message 3"])).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Test only custom cron date event shown with new property", () => {
|
||||||
|
it("shows 'any message' on May 6", async () => {
|
||||||
|
await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "06 May 2022 17:03:00 GMT");
|
||||||
|
await expect(doTest(["just pub time"])).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Test any event shows after time window", () => {
|
||||||
|
it("shows 'any message' on May 6", async () => {
|
||||||
|
await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "06 May 2022 17:11:00 GMT");
|
||||||
|
await expect(doTest(["just a test"])).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Test any event shows different day", () => {
|
||||||
|
it("shows 'any message' on May 5", async () => {
|
||||||
|
await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "05 May 2022 17:00:00 GMT");
|
||||||
|
await expect(doTest(["just a test"])).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
9
vendor/package-lock.json
generated
vendored
9
vendor/package-lock.json
generated
vendored
@ -11,6 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^6.5.2",
|
"@fortawesome/fontawesome-free": "^6.5.2",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
|
"croner": "^8.0.2",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"moment-timezone": "^0.5.45",
|
"moment-timezone": "^0.5.45",
|
||||||
"nunjucks": "^3.2.4",
|
"nunjucks": "^3.2.4",
|
||||||
@ -50,6 +51,14 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/croner": {
|
||||||
|
"version": "8.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/croner/-/croner-8.0.2.tgz",
|
||||||
|
"integrity": "sha512-HgSdlSUX8mIgDTTiQpWUP4qY4IFRMsduPCYdca34Pelt8MVdxdaDOzreFtCscA6R+cRZd7UbD1CD3uyx6J3X1A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/moment": {
|
"node_modules/moment": {
|
||||||
"version": "2.30.1",
|
"version": "2.30.1",
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||||
|
1
vendor/package.json
vendored
1
vendor/package.json
vendored
@ -13,6 +13,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^6.5.2",
|
"@fortawesome/fontawesome-free": "^6.5.2",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
|
"croner": "^8.0.2",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"moment-timezone": "^0.5.45",
|
"moment-timezone": "^0.5.45",
|
||||||
"nunjucks": "^3.2.4",
|
"nunjucks": "^3.2.4",
|
||||||
|
3
vendor/vendor.js
vendored
3
vendor/vendor.js
vendored
@ -5,7 +5,8 @@ const vendor = {
|
|||||||
"weather-icons-wind.css": "node_modules/weathericons/css/weather-icons-wind.css",
|
"weather-icons-wind.css": "node_modules/weathericons/css/weather-icons-wind.css",
|
||||||
"font-awesome.css": "css/font-awesome.css",
|
"font-awesome.css": "css/font-awesome.css",
|
||||||
"nunjucks.js": "node_modules/nunjucks/browser/nunjucks.min.js",
|
"nunjucks.js": "node_modules/nunjucks/browser/nunjucks.min.js",
|
||||||
"suncalc.js": "node_modules/suncalc/suncalc.js"
|
"suncalc.js": "node_modules/suncalc/suncalc.js",
|
||||||
|
"croner.js": "node_modules/croner/dist/croner.umd.min.js"
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof module !== "undefined") {
|
if (typeof module !== "undefined") {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user