mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-08-21 04:45:17 +00:00
Release 2.27.0 (#3410)
## [2.27.0] - 2024-04-01 Thanks to: @bugsounet, @crazyscot, @illimarkangur, @jkriegshauser, @khassel, @KristjanESPERANTO, @Paranoid93, @rejas, @sdetweil and @vppencilsharpener. This release marks the first release without Michael Teeuw (@michmich). A very special thanks to him for creating MagicMirror and leading the project for so many years. 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 - Output of system information to the console for troubleshooting (#3328 and #3337), ignore errors under aarch64 (#3349) - [chore] Add `eslint-plugin-package-json` to lint the `package.json` files (#3368) - [weather] `showHumidity` config is now a string describing where to show this element. Supported values: "wind", "temp", "feelslike", "below", "none". (#3330) - electron-rebuild test suite for electron and 3rd party modules compatibility (#3392) - Create MM² icon and attach it to electron process (#3407) ### Updated - Update updatenotification (update_helper.js): Recode with pm2 library (#3332) - Removing lodash dependency by replacing merge by spread operator (#3339) - Use node prefix for build-in modules (#3340) - Rework logging colors (#3350) - Update pm2 to v5.3.1 with no allow-ghsas (#3364) - [chore] Update husky and let lint-staged fix ESLint issues - [chore] Update dependencies including electron to v29 (#3357) and node-ical - Update translations for estonian (#3371) - Update electron to v29 and update other dependencies - [calendar] fullDay events over several days now show the left days from the first day on and 'today' on the last day - Update layout of current weather indoor values ### Fixed - Correct apibase of weathergov weatherprovider to match documentation (#2926) - Worked around several issues in the RRULE library that were causing deleted calender events to still show, some initial and recurring events to not show, and some event times to be off an hour. (#3291) - Skip changelog requirement when running tests for dependency updates (#3320) - Display precipitation probability when it is 0% instead of blank/empty (#3345) - [newsfeed] Suppress unsightly animation cases when there are 0 or 1 active news items (#3336) - [newsfeed] Always compute the feed item URL using the same helper function (#3336) - Ignore all custom css files (#3359) - [newsfeed] Fix newsfeed stall issue introduced by #3336 (#3361) - Changed `log.debug` to `log.log` in `app.js` where logLevel is not set because config is not loaded at this time (#3353) - [calendar] deny fetch interval < 60000 and set 60000 in this case (prevent fetch loop failed) (#3382) - added message in case where config.js is missing the module.export line PR #3383 - Fixed an issue where recurring events could extend past their recurrence end date (#3393) - Don't display any `npm WARN <....>` on install (#3399) - Fixed move suncalc dependency to production from dev, as it is used by clock module - [compliments] Fix mirror not responding anymore when no compliments are to be shown (#3385) ### Deleted - Unneeded file headers (#3358) --------- 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: Karsten Hassel <hassel@gmx.de> Co-authored-by: Ross Younger <crazyscot@gmail.com> 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: Paranoid93 <6515818+Paranoid93@users.noreply.github.com>
This commit is contained in:
@@ -1,10 +1,3 @@
|
||||
/* MagicMirror²
|
||||
* AnimateCSS System from https://animate.style/
|
||||
* by @bugsounet
|
||||
* for Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
/* enumeration of animations in Array **/
|
||||
const AnimateCSSIn = [
|
||||
// Attention seekers
|
||||
|
42
js/app.js
42
js/app.js
@@ -1,15 +1,8 @@
|
||||
/* MagicMirror²
|
||||
* The Core App (Server)
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
// Alias modules mentioned in package.js under _moduleAliases.
|
||||
require("module-alias/register");
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const envsub = require("envsub");
|
||||
const Log = require("logger");
|
||||
|
||||
@@ -21,6 +14,9 @@ const defaultModules = require(`${__dirname}/../modules/default/defaultmodules`)
|
||||
global.version = require(`${__dirname}/../package.json`).version;
|
||||
Log.log(`Starting MagicMirror: v${global.version}`);
|
||||
|
||||
// Log system information.
|
||||
Utils.logSystemInformation();
|
||||
|
||||
// global absolute root path
|
||||
global.root_path = path.resolve(`${__dirname}/../`);
|
||||
|
||||
@@ -29,7 +25,7 @@ if (process.env.MM_CONFIG_FILE) {
|
||||
}
|
||||
|
||||
// FIXME: Hotfix Pull Request
|
||||
// https://github.com/MichMich/MagicMirror/pull/673
|
||||
// https://github.com/MagicMirrorOrg/MagicMirror/pull/673
|
||||
if (process.env.MM_PORT) {
|
||||
global.mmPort = process.env.MM_PORT;
|
||||
}
|
||||
@@ -37,10 +33,13 @@ if (process.env.MM_PORT) {
|
||||
// The next part is here to prevent a major exception when there
|
||||
// is no internet connection. This could probable be solved better.
|
||||
process.on("uncaughtException", function (err) {
|
||||
Log.error("Whoops! There was an uncaught exception...");
|
||||
Log.error(err);
|
||||
Log.error("MagicMirror² will not quit, but it might be a good idea to check why this happened. Maybe no internet connection?");
|
||||
Log.error("If you think this really is an issue, please open an issue on GitHub: https://github.com/MichMich/MagicMirror/issues");
|
||||
// ignore strange exceptions under aarch64 coming from systeminformation:
|
||||
if (!err.stack.includes("node_modules/systeminformation")) {
|
||||
Log.error("Whoops! There was an uncaught exception...");
|
||||
Log.error(err);
|
||||
Log.error("MagicMirror² will not quit, but it might be a good idea to check why this happened. Maybe no internet connection?");
|
||||
Log.error("If you think this really is an issue, please open an issue on GitHub: https://github.com/MagicMirrorOrg/MagicMirror/issues");
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -70,7 +69,7 @@ function App () {
|
||||
fs.accessSync(templateFile, fs.F_OK);
|
||||
} catch (err) {
|
||||
templateFile = null;
|
||||
Log.debug("config template file not exists, no envsubst");
|
||||
Log.log("config template file not exists, no envsubst");
|
||||
}
|
||||
|
||||
if (templateFile) {
|
||||
@@ -91,7 +90,7 @@ function App () {
|
||||
envFiles.push(configEnvFile);
|
||||
}
|
||||
} catch (err) {
|
||||
Log.debug(`${configEnvFile} does not exist. ${err.message}`);
|
||||
Log.log(`${configEnvFile} does not exist. ${err.message}`);
|
||||
}
|
||||
|
||||
let options = {
|
||||
@@ -116,15 +115,18 @@ function App () {
|
||||
try {
|
||||
fs.accessSync(configFilename, fs.F_OK);
|
||||
const c = require(configFilename);
|
||||
if (Object.keys(c).length === 0) {
|
||||
Log.error("WARNING! Config file appears empty, maybe missing module.exports last line?");
|
||||
}
|
||||
checkDeprecatedOptions(c);
|
||||
return Object.assign(defaults, c);
|
||||
} catch (e) {
|
||||
if (e.code === "ENOENT") {
|
||||
Log.error(Utils.colors.error("WARNING! Could not find config file. Please create one. Starting with default configuration."));
|
||||
Log.error("WARNING! Could not find config file. Please create one. Starting with default configuration.");
|
||||
} else if (e instanceof ReferenceError || e instanceof SyntaxError) {
|
||||
Log.error(Utils.colors.error(`WARNING! Could not validate config file. Starting with default configuration. Please correct syntax errors at or above this line: ${e.stack}`));
|
||||
Log.error(`WARNING! Could not validate config file. Starting with default configuration. Please correct syntax errors at or above this line: ${e.stack}`);
|
||||
} else {
|
||||
Log.error(Utils.colors.error(`WARNING! Could not load config file. Starting with default configuration. Error found: ${e}`));
|
||||
Log.error(`WARNING! Could not load config file. Starting with default configuration. Error found: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +144,7 @@ function App () {
|
||||
|
||||
const usedDeprecated = deprecatedOptions.filter((option) => userConfig.hasOwnProperty(option));
|
||||
if (usedDeprecated.length > 0) {
|
||||
Log.warn(Utils.colors.warn(`WARNING! Your config is using deprecated options: ${usedDeprecated.join(", ")}. Check README and CHANGELOG for more up-to-date ways of getting the same functionality.`));
|
||||
Log.warn(`WARNING! Your config is using deprecated options: ${usedDeprecated.join(", ")}. Check README and CHANGELOG for more up-to-date ways of getting the same functionality.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,19 +1,12 @@
|
||||
/* MagicMirror²
|
||||
*
|
||||
* Check the configuration file for errors
|
||||
*
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const path = require("node:path");
|
||||
const fs = require("node:fs");
|
||||
const colors = require("ansis");
|
||||
const { Linter } = require("eslint");
|
||||
|
||||
const linter = new Linter();
|
||||
|
||||
const rootPath = path.resolve(`${__dirname}/../`);
|
||||
const Log = require(`${rootPath}/js/logger.js`);
|
||||
const Utils = require(`${rootPath}/js/utils.js`);
|
||||
|
||||
/**
|
||||
* Returns a string with path of configuration file.
|
||||
@@ -33,7 +26,7 @@ function checkConfigFile () {
|
||||
|
||||
// Check if file is present
|
||||
if (fs.existsSync(configFileName) === false) {
|
||||
Log.error(Utils.colors.error("File not found: "), configFileName);
|
||||
Log.error(`File not found: ${configFileName}`);
|
||||
throw new Error("No config file present!");
|
||||
}
|
||||
|
||||
@@ -41,12 +34,12 @@ function checkConfigFile () {
|
||||
try {
|
||||
fs.accessSync(configFileName, fs.F_OK);
|
||||
} catch (e) {
|
||||
Log.error(Utils.colors.error(e));
|
||||
Log.error(e);
|
||||
throw new Error("No permission to access config file!");
|
||||
}
|
||||
|
||||
// Validate syntax of the configuration file.
|
||||
Log.info(Utils.colors.info("Checking file... "), configFileName);
|
||||
Log.info("Checking file... ", configFileName);
|
||||
|
||||
// I'm not sure if all ever is utf-8
|
||||
const configFile = fs.readFileSync(configFileName, "utf-8");
|
||||
@@ -59,9 +52,9 @@ function checkConfigFile () {
|
||||
});
|
||||
|
||||
if (errors.length === 0) {
|
||||
Log.info(Utils.colors.pass("Your configuration file doesn't contain syntax errors :)"));
|
||||
Log.info(colors.green("Your configuration file doesn't contain syntax errors :)"));
|
||||
} else {
|
||||
Log.error(Utils.colors.error("Your configuration file contains syntax errors :("));
|
||||
Log.error(colors.red("Your configuration file contains syntax errors :("));
|
||||
|
||||
for (const error of errors) {
|
||||
Log.error(`Line ${error.line} column ${error.column}: ${error.message}`);
|
||||
|
@@ -1,11 +1,5 @@
|
||||
/* global mmPort */
|
||||
|
||||
/* MagicMirror²
|
||||
* Config Defaults
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const address = "localhost";
|
||||
let port = 8080;
|
||||
if (typeof mmPort !== "undefined") {
|
||||
@@ -26,7 +20,7 @@ const defaults = {
|
||||
zoom: 1,
|
||||
customCss: "css/custom.css",
|
||||
// httpHeaders used by helmet, see https://helmetjs.github.io/. You can add other/more object values by overriding this in config.js,
|
||||
// e.g. you need to add `frameguard: false` for embedding MagicMirror in another website, see https://github.com/MichMich/MagicMirror/issues/2847
|
||||
// e.g. you need to add `frameguard: false` for embedding MagicMirror in another website, see https://github.com/MagicMirrorOrg/MagicMirror/issues/2847
|
||||
httpHeaders: { contentSecurityPolicy: false, crossOriginOpenerPolicy: false, crossOriginEmbedderPolicy: false, crossOriginResourcePolicy: false, originAgentCluster: false },
|
||||
|
||||
// properties for checking if server is alive and has same startup-timestamp, the check is per default enabled
|
||||
|
@@ -1,11 +1,3 @@
|
||||
/* MagicMirror² Deprecated Config Options List
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*
|
||||
* Olex S. original idea this deprecated option
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
configs: ["kioskmode"]
|
||||
};
|
||||
|
@@ -40,6 +40,7 @@ function createWindow () {
|
||||
let electronOptionsDefaults = {
|
||||
width: electronSize.width,
|
||||
height: electronSize.height,
|
||||
icon: "mm2.png",
|
||||
x: 0,
|
||||
y: 0,
|
||||
darkTheme: true,
|
||||
|
@@ -1,11 +1,5 @@
|
||||
/* global defaultModules, vendor */
|
||||
|
||||
/* MagicMirror²
|
||||
* Module and File loaders.
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const Loader = (function () {
|
||||
|
||||
/* Create helper variables */
|
||||
|
43
js/logger.js
43
js/logger.js
@@ -1,19 +1,40 @@
|
||||
/* MagicMirror²
|
||||
* Log
|
||||
*
|
||||
* This logger is very simple, but needs to be extended.
|
||||
* This system can eventually be used to push the log messages to an external target.
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
// This logger is very simple, but needs to be extended.
|
||||
(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
if (process.env.JEST_WORKER_ID === undefined) {
|
||||
const colors = require("ansis");
|
||||
|
||||
// add timestamps in front of log messages
|
||||
require("console-stamp")(console, {
|
||||
pattern: "yyyy-mm-dd HH:MM:ss.l",
|
||||
include: ["debug", "log", "info", "warn", "error"]
|
||||
format: ":date(yyyy-mm-dd HH:MM:ss.l) :label(7) :msg",
|
||||
tokens: {
|
||||
label: (arg) => {
|
||||
const { method, defaultTokens } = arg;
|
||||
let label = defaultTokens.label(arg);
|
||||
if (method === "error") {
|
||||
label = colors.red(label);
|
||||
} else if (method === "warn") {
|
||||
label = colors.yellow(label);
|
||||
} else if (method === "debug") {
|
||||
label = colors.bgBlue(label);
|
||||
} else if (method === "info") {
|
||||
label = colors.blue(label);
|
||||
}
|
||||
return label;
|
||||
},
|
||||
msg: (arg) => {
|
||||
const { method, defaultTokens } = arg;
|
||||
let msg = defaultTokens.msg(arg);
|
||||
if (method === "error") {
|
||||
msg = colors.red(msg);
|
||||
} else if (method === "warn") {
|
||||
msg = colors.yellow(msg);
|
||||
} else if (method === "info") {
|
||||
msg = colors.blue(msg);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// Node, CommonJS-like
|
||||
|
@@ -1,11 +1,5 @@
|
||||
/* global Loader, defaults, Translator, addAnimateCSS, removeAnimateCSS, AnimateCSSIn, AnimateCSSOut */
|
||||
|
||||
/* MagicMirror²
|
||||
* Main System
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const MM = (function () {
|
||||
let modules = [];
|
||||
|
||||
|
@@ -1,11 +1,7 @@
|
||||
/* global Class, cloneObject, Loader, MMSocket, nunjucks, Translator */
|
||||
|
||||
/* MagicMirror²
|
||||
* Module Blueprint.
|
||||
/* Module Blueprint.
|
||||
* @typedef {Object} Module
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const Module = Class.extend({
|
||||
|
||||
|
@@ -1,9 +1,3 @@
|
||||
/* MagicMirror²
|
||||
* Node Helper Superclass
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const express = require("express");
|
||||
const Log = require("logger");
|
||||
const Class = require("./class");
|
||||
|
16
js/server.js
16
js/server.js
@@ -1,13 +1,7 @@
|
||||
/* MagicMirror²
|
||||
* Server
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const fs = require("fs");
|
||||
const http = require("http");
|
||||
const https = require("https");
|
||||
const path = require("path");
|
||||
const fs = require("node:fs");
|
||||
const http = require("node:http");
|
||||
const https = require("node:https");
|
||||
const path = require("node:path");
|
||||
const express = require("express");
|
||||
const ipfilter = require("express-ipfilter").IpFilter;
|
||||
const helmet = require("helmet");
|
||||
@@ -62,7 +56,7 @@ function Server (config) {
|
||||
server.listen(port, config.address || "localhost");
|
||||
|
||||
if (config.ipWhitelist instanceof Array && config.ipWhitelist.length === 0) {
|
||||
Log.warn(Utils.colors.warn("You're using a full whitelist configuration to allow for all IPs"));
|
||||
Log.warn("You're using a full whitelist configuration to allow for all IPs");
|
||||
}
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const Log = require("logger");
|
||||
|
||||
const startUp = new Date();
|
||||
|
@@ -1,11 +1,5 @@
|
||||
/* global io */
|
||||
|
||||
/* MagicMirror²
|
||||
* TODO add description
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const MMSocket = function (moduleName) {
|
||||
if (typeof moduleName !== "string") {
|
||||
throw new Error("Please set the module name for the MMSocket.");
|
||||
|
@@ -1,11 +1,5 @@
|
||||
/* global translations */
|
||||
|
||||
/* MagicMirror²
|
||||
* Translator (l10n)
|
||||
*
|
||||
* By Christopher Fenner https://github.com/CFenner
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const Translator = (function () {
|
||||
|
||||
/**
|
||||
|
37
js/utils.js
37
js/utils.js
@@ -1,16 +1,29 @@
|
||||
/* MagicMirror²
|
||||
* Utils
|
||||
*
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const colors = require("colors/safe");
|
||||
const execSync = require("node:child_process").execSync;
|
||||
const Log = require("logger");
|
||||
const si = require("systeminformation");
|
||||
|
||||
module.exports = {
|
||||
colors: {
|
||||
warn: colors.yellow,
|
||||
error: colors.red,
|
||||
info: colors.blue,
|
||||
pass: colors.green
|
||||
|
||||
async logSystemInformation () {
|
||||
try {
|
||||
let installedNodeVersion = execSync("node -v", { encoding: "utf-8" }).replace("v", "").replace(/(?:\r\n|\r|\n)/g, "");
|
||||
|
||||
const staticData = await si.get({
|
||||
system: "manufacturer, model, raspberry, virtual",
|
||||
osInfo: "platform, distro, release, arch",
|
||||
versions: "kernel, node, npm, pm2"
|
||||
});
|
||||
let systemDataString = "System information:";
|
||||
systemDataString += `\n### SYSTEM: manufacturer: ${staticData["system"]["manufacturer"]}; model: ${staticData["system"]["model"]}; raspberry: ${staticData["system"]["raspberry"]}; virtual: ${staticData["system"]["virtual"]}`;
|
||||
systemDataString += `\n### OS: platform: ${staticData["osInfo"]["platform"]}; distro: ${staticData["osInfo"]["distro"]}; release: ${staticData["osInfo"]["release"]}; arch: ${staticData["osInfo"]["arch"]}; kernel: ${staticData["versions"]["kernel"]}`;
|
||||
systemDataString += `\n### VERSIONS: electron: ${process.versions.electron}; used node: ${staticData["versions"]["node"]}; installed node: ${installedNodeVersion}; npm: ${staticData["versions"]["npm"]}; pm2: ${staticData["versions"]["pm2"]}`;
|
||||
systemDataString += `\n### OTHER: timeZone: ${Intl.DateTimeFormat().resolvedOptions().timeZone}; ELECTRON_ENABLE_GPU: ${process.env.ELECTRON_ENABLE_GPU}`;
|
||||
Log.info(systemDataString);
|
||||
|
||||
// Return is currently only for jest
|
||||
return systemDataString;
|
||||
} catch (e) {
|
||||
Log.error(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user