Merge pull request #9 from MichMich/develop

Develop
This commit is contained in:
ashishtank 2021-01-10 16:03:49 +01:00 committed by GitHub
commit 3eda8af671
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 419 additions and 2276 deletions

View File

@ -23,9 +23,9 @@ If you are facing an issue or found a bug while running MagicMirror inside a Doc
Please make sure to only submit reproducible issues. You can safely remove everything above the dividing line. Please make sure to only submit reproducible issues. You can safely remove everything above the dividing line.
When submitting a new issue, please supply the following information: When submitting a new issue, please supply the following information:
**Platform**: Place your platform here... give us your web browser/Electron version _and_ your hardware (Raspberry Pi 2/3, Windows, Mac, Linux, System V UNIX). **Platform**: Place your platform here... give us your web browser/Electron version _and_ your hardware (Raspberry Pi 2/3/4, Windows, Mac, Linux, System V UNIX).
**Node Version**: Make sure it's version 8 or later. **Node Version**: Make sure it's version 10 or later.
**MagicMirror Version**: Please let us now which version of MagicMirror you are running. It can be found in the `package.log` file. **MagicMirror Version**: Please let us now which version of MagicMirror you are running. It can be found in the `package.log` file.

View File

@ -0,0 +1,24 @@
# This workflow runs the automated test and uploads the coverage results to codecov.io
name: "Run Codecov Tests"
on:
push:
branches: [ master, develop ]
pull_request:
branches: [ master, develop ]
jobs:
run-and-upload-coverage-report:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: |
Xvfb :99 -screen 0 1024x768x16 &
export DISPLAY=:99
npm ci
npm run test:coverage
- uses: codecov/codecov-action@v1
with:
file: ./coverage/lcov.info
fail_ci_if_error: true

View File

@ -1,10 +1,12 @@
# This workflow enforces the update of a changelog file on every pull request
name: "Enforce Changelog" name: "Enforce Changelog"
on: on:
pull_request: pull_request:
types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]
jobs: jobs:
# Enforces the update of a changelog file on every pull request
check: check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -1,7 +1,7 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Automated Tests name: "Run Automated Tests"
on: on:
push: push:
@ -11,13 +11,10 @@ on:
jobs: jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
node-version: [10.x, 12.x, 14.x] node-version: [10.x, 12.x, 14.x]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}

View File

@ -5,9 +5,35 @@ This project adheres to [Semantic Versioning](https://semver.org/).
❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror² ❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror²
## [2.14.0] - Unreleased (Develop Branch) ## [2.15.0] - Unreleased (Develop Branch)
_This release is scheduled to be released on 2021-01-01._ _This release is scheduled to be released on 2021-04-01._
### Added
- Added GitHub workflows for automated testing and changelog enforcement.
- Add CodeCov badge to Readme.
### Updated
- Updated markdown files.
- Cleaned up old code on server side.
- Convert `-0` to `0` when displaying temperature.
### Removed
- Removed danger.js library.
### Fixed
- Added default log levels to stop calendar log spamming.
- Fix socket.io cors errors, see [breaking change since socket.io v3](https://socket.io/docs/v3/handling-cors/)
## [2.14.0] - 2021-01-01
Special thanks to the following contributors: @Alvinger, @AndyPoms, @ashishtank, @bluemanos, @flopp999, @jakemulley, @jakobsarwary1, @marvai-vgtu, @mirontoli, @rejas, @sdetweil, @Snille & @Sub028.
**Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`.
### Added ### Added
@ -17,13 +43,12 @@ _This release is scheduled to be released on 2021-01-01._
- Added Weatherbit as a provider to Weather module. - Added Weatherbit as a provider to Weather module.
- Added SMHI as a provider to Weather module. - Added SMHI as a provider to Weather module.
- Added Hindi & Gujarati translation. - Added Hindi & Gujarati translation.
- Added optional support for DEGREE position in Feels like translation - Added optional support for DEGREE position in Feels like translation.
- Added support for variables in nunjucks templates for translate filter - Added support for variables in nunjucks templates for translate filter.
- Chuvash translation. - Added Chuvash translation.
- Calendar: new options "limitDays" and "coloredEvents". - Calendar: new options "limitDays" and "coloredEvents".
- Added new option "limitDays" - limit the number of discreet days displayed. - Added new option "limitDays" - limit the number of discreet days displayed.
- Added new option "customEvents" - use custom symbol/color based on keyword in event title. - Added new option "customEvents" - use custom symbol/color based on keyword in event title.
- Added GitHub workflows for automated testing and changelog enforcement.
### Updated ### Updated
@ -33,6 +58,12 @@ _This release is scheduled to be released on 2021-01-01._
- Update dependencies eslint, feedme, simple-git and socket.io to latest versions. - Update dependencies eslint, feedme, simple-git and socket.io to latest versions.
- Update lithuanian translation. - Update lithuanian translation.
- Update config sample. - Update config sample.
- Highlight required version mismatch.
- No select Text for TouchScreen use.
- Corrected logic for timeFormat "relative" and "absolute".
- Added missing function call in module.show()
- Translator variables can have falsy values (e.g. empty string)
- Fix issue with weather module with DEGREE label in FEELS like
### Deleted ### Deleted
@ -40,7 +71,6 @@ _This release is scheduled to be released on 2021-01-01._
### Fixed ### Fixed
- No select Text for TouchScreen use
- JSON Parse translation files with comments crashing UI. (#2149) - JSON Parse translation files with comments crashing UI. (#2149)
- Calendar parsing where RRULE bug returns wrong date, add Windows timezone name support. (#2145, #2151) - Calendar parsing where RRULE bug returns wrong date, add Windows timezone name support. (#2145, #2151)
- Wrong node-ical version installed (package.json) requested version. (#2153) - Wrong node-ical version installed (package.json) requested version. (#2153)
@ -53,18 +83,14 @@ _This release is scheduled to be released on 2021-01-01._
- Fix calendar full day event east of UTC start time. (#2200) - Fix calendar full day event east of UTC start time. (#2200)
- Fix non-fullday recurring rule processing. (#2216) - Fix non-fullday recurring rule processing. (#2216)
- Catch errors when parsing calendar data with ical. (#2022) - Catch errors when parsing calendar data with ical. (#2022)
- Corrected logic for timeFormat "relative" and "absolute".
- Fix Default Alert Module does not hide black overlay when alert is dismissed manually. (#2228) - Fix Default Alert Module does not hide black overlay when alert is dismissed manually. (#2228)
- Weather module - Always displays night icons when local is other then English. (#2221) - Weather module - Always displays night icons when local is other then English. (#2221)
- update Node-ical 0.12.4 , fix invalid RRULE format in cal entries - Update Node-ical 0.12.4 , fix invalid RRULE format in cal entries
- fix package.json for optional electron dependency (2378) - Fix package.json for optional electron dependency (2378)
- update node-ical version again, 0.12.5, change RRULE fix (#2371, #2379) - Update node-ical version again, 0.12.5, change RRULE fix (#2371, #2379)
- Added missing function call in module.show() - Remove undefined objects from modules array (#2382)
- remove undefined objects from modules array (#2382) - Update node-ical version again, 0.12.7, change RRULE fix (#2371, #2379), node-ical now throws error (which we catch)
- update node-ical version again, 0.12.7, change RRULE fix (#2371, #2379), node-ical now throws error (which we catch) - Update simple-git version to 2.31 unhandled promise rejection (#2383)
- update simple-git version to 2.31 unhandled promise rejection (#2383)
- Translator variables can have falsy values (e.g. empty string)
- Fix issue with weather module with DEGREE label in FEELS like
## [2.13.0] - 2020-10-01 ## [2.13.0] - 2020-10-01

View File

@ -1,6 +1,6 @@
# The MIT License (MIT) # The MIT License (MIT)
Copyright © 2016-2020 Michael Teeuw Copyright © 2016-2021 Michael Teeuw
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation

View File

@ -1,9 +1,10 @@
![MagicMirror²: The open source modular smart mirror platform. ](.github/header.png) ![MagicMirror²: The open source modular smart mirror platform. ](.github/header.png)
<p align="center"> <p style="text-align: center">
<a href="https://david-dm.org/MichMich/MagicMirror"><img src="https://david-dm.org/MichMich/MagicMirror.svg" alt="Dependency Status"></a> <a href="https://david-dm.org/MichMich/MagicMirror"><img src="https://david-dm.org/MichMich/MagicMirror.svg" alt="Dependency Status"></a>
<a href="https://david-dm.org/MichMich/MagicMirror#info=devDependencies"><img src="https://david-dm.org/MichMich/MagicMirror/dev-status.svg" alt="devDependency Status"></a> <a href="https://david-dm.org/MichMich/MagicMirror#info=devDependencies"><img src="https://david-dm.org/MichMich/MagicMirror/dev-status.svg" alt="devDependency Status"></a>
<a href="https://bestpractices.coreinfrastructure.org/projects/347"><img src="https://bestpractices.coreinfrastructure.org/projects/347/badge"></a> <a href="https://bestpractices.coreinfrastructure.org/projects/347"><img src="https://bestpractices.coreinfrastructure.org/projects/347/badge" alt="CLI Best Practices"></a>
<a href="https://codecov.io/gh/MichMich/MagicMirror"><img src="https://codecov.io/gh/MichMich/MagicMirror/branch/master/graph/badge.svg?token=LEG1KitZR6"/></a>
<a href="https://choosealicense.com/licenses/mit"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a> <a href="https://choosealicense.com/licenses/mit"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
<a href="https://github.com/MichMich/MagicMirror/actions?query=workflow%3A%22Automated+Tests%22"><img src="https://github.com/MichMich/MagicMirror/workflows/Automated%20Tests/badge.svg" alt="Tests"></a> <a href="https://github.com/MichMich/MagicMirror/actions?query=workflow%3A%22Automated+Tests%22"><img src="https://github.com/MichMich/MagicMirror/workflows/Automated%20Tests/badge.svg" alt="Tests"></a>
</p> </p>
@ -38,7 +39,6 @@ If we receive enough donations we might even be able to free up some working hou
To donate, please follow [this](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=G5D8E9MR5DTD2&source=url) link. To donate, please follow [this](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=G5D8E9MR5DTD2&source=url) link.
<p align="center"> <p style="text-align: center">
<br>
<a href="https://forum.magicmirror.builders/topic/728/magicmirror-is-voted-number-1-in-the-magpi-top-50"><img src="https://magicmirror.builders/img/magpi-best-watermark-custom.png" width="150" alt="MagPi Top 50"></a> <a href="https://forum.magicmirror.builders/topic/728/magicmirror-is-voted-number-1-in-the-magpi-top-50"><img src="https://magicmirror.builders/img/magpi-best-watermark-custom.png" width="150" alt="MagPi Top 50"></a>
</p> </p>

View File

@ -1,17 +0,0 @@
import { danger, fail, warn } from "danger";
// Check if the CHANGELOG.md file has been edited
// Fail the build and post a comment reminding submitters to do so if it wasn't changed
if (!danger.git.modified_files.includes("CHANGELOG.md")) {
warn("Please include an updated `CHANGELOG.md` file.<br>This way we can keep track of all the contributions.");
}
// Check if the PR request is send to the master branch.
// This should only be done by MichMich.
if (danger.github.pr.base.ref === "master" && danger.github.pr.user.login !== "MichMich") {
// Check if the PR body or title includes the text: #accepted.
// If not, the PR will fail.
if ((danger.github.pr.body + danger.github.pr.title).includes("#accepted")) {
fail("Please send all your pull requests to the `develop` branch.<br>Pull requests on the `master` branch will not be accepted.");
}
}

126
js/app.js
View File

@ -4,22 +4,22 @@
* By Michael Teeuw https://michaelteeuw.nl * By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed. * MIT Licensed.
*/ */
var fs = require("fs"); const fs = require("fs");
var path = require("path"); const path = require("path");
var Log = require(__dirname + "/logger.js"); const Log = require(`${__dirname}/logger`);
var Server = require(__dirname + "/server.js"); const Server = require(`${__dirname}/server`);
var Utils = require(__dirname + "/utils.js"); const Utils = require(`${__dirname}/utils`);
var defaultModules = require(__dirname + "/../modules/default/defaultmodules.js"); const defaultModules = require(`${__dirname}/../modules/default/defaultmodules`);
// Alias modules mentioned in package.js under _moduleAliases. // Alias modules mentioned in package.js under _moduleAliases.
require("module-alias/register"); require("module-alias/register");
// Get version number. // Get version number.
global.version = JSON.parse(fs.readFileSync("package.json", "utf8")).version; global.version = require(`${__dirname}/../package.json`).version;
Log.log("Starting MagicMirror: v" + global.version); Log.log("Starting MagicMirror: v" + global.version);
// global absolute root path // global absolute root path
global.root_path = path.resolve(__dirname + "/../"); global.root_path = path.resolve(`${__dirname}/../`);
if (process.env.MM_CONFIG_FILE) { if (process.env.MM_CONFIG_FILE) {
global.configuration_file = process.env.MM_CONFIG_FILE; global.configuration_file = process.env.MM_CONFIG_FILE;
@ -45,8 +45,8 @@ process.on("uncaughtException", function (err) {
* *
* @class * @class
*/ */
var App = function () { function App() {
var nodeHelpers = []; let nodeHelpers = [];
/** /**
* Loads the config file. Combines it with the defaults, and runs the * Loads the config file. Combines it with the defaults, and runs the
@ -54,34 +54,31 @@ var App = function () {
* *
* @param {Function} callback Function to be called after loading the config * @param {Function} callback Function to be called after loading the config
*/ */
var loadConfig = function (callback) { function loadConfig(callback) {
Log.log("Loading config ..."); Log.log("Loading config ...");
var defaults = require(__dirname + "/defaults.js"); const defaults = require(`${__dirname}/defaults`);
// For this check proposed to TestSuite // For this check proposed to TestSuite
// https://forum.magicmirror.builders/topic/1456/test-suite-for-magicmirror/8 // https://forum.magicmirror.builders/topic/1456/test-suite-for-magicmirror/8
var configFilename = path.resolve(global.root_path + "/config/config.js"); const configFilename = path.resolve(global.configuration_file || `${global.root_path}/config/config.js`);
if (typeof global.configuration_file !== "undefined") {
configFilename = path.resolve(global.configuration_file);
}
try { try {
fs.accessSync(configFilename, fs.F_OK); fs.accessSync(configFilename, fs.F_OK);
var c = require(configFilename); const c = require(configFilename);
checkDeprecatedOptions(c); checkDeprecatedOptions(c);
var config = Object.assign(defaults, c); const config = Object.assign(defaults, c);
callback(config); callback(config);
} catch (e) { } catch (e) {
if (e.code === "ENOENT") { if (e.code === "ENOENT") {
Log.error(Utils.colors.error("WARNING! Could not find config file. Please create one. Starting with default configuration.")); Log.error(Utils.colors.error("WARNING! Could not find config file. Please create one. Starting with default configuration."));
} else if (e instanceof ReferenceError || e instanceof SyntaxError) { } 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(Utils.colors.error(`WARNING! Could not validate config file. Starting with default configuration. Please correct syntax errors at or above this line: ${e.stack}`));
} else { } else {
Log.error(Utils.colors.error("WARNING! Could not load config file. Starting with default configuration. Error found: " + e)); Log.error(Utils.colors.error(`WARNING! Could not load config file. Starting with default configuration. Error found: ${e}`));
} }
callback(defaults); callback(defaults);
} }
}; }
/** /**
* Checks the config for deprecated options and throws a warning in the logs * Checks the config for deprecated options and throws a warning in the logs
@ -89,21 +86,15 @@ var App = function () {
* *
* @param {object} userConfig The user config * @param {object} userConfig The user config
*/ */
var checkDeprecatedOptions = function (userConfig) { function checkDeprecatedOptions(userConfig) {
var deprecated = require(global.root_path + "/js/deprecated.js"); const deprecated = require(`${global.root_path}/js/deprecated`);
var deprecatedOptions = deprecated.configs; const deprecatedOptions = deprecated.configs;
var usedDeprecated = []; const usedDeprecated = deprecatedOptions.filter((option) => userConfig.hasOwnProperty(option));
deprecatedOptions.forEach(function (option) {
if (userConfig.hasOwnProperty(option)) {
usedDeprecated.push(option);
}
});
if (usedDeprecated.length > 0) { 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(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.`));
}
} }
};
/** /**
* Loads a specific module. * Loads a specific module.
@ -111,35 +102,35 @@ var App = function () {
* @param {string} module The name of the module (including subpath). * @param {string} module The name of the module (including subpath).
* @param {Function} callback Function to be called after loading * @param {Function} callback Function to be called after loading
*/ */
var loadModule = function (module, callback) { function loadModule(module, callback) {
var elements = module.split("/"); const elements = module.split("/");
var moduleName = elements[elements.length - 1]; const moduleName = elements[elements.length - 1];
var moduleFolder = __dirname + "/../modules/" + module; let moduleFolder = `${__dirname}/../modules/${module}`;
if (defaultModules.indexOf(moduleName) !== -1) { if (defaultModules.includes(moduleName)) {
moduleFolder = __dirname + "/../modules/default/" + module; moduleFolder = `${__dirname}/../modules/default/${module}`;
} }
var helperPath = moduleFolder + "/node_helper.js"; const helperPath = `${moduleFolder}/node_helper.js`;
var loadModule = true; let loadHelper = true;
try { try {
fs.accessSync(helperPath, fs.R_OK); fs.accessSync(helperPath, fs.R_OK);
} catch (e) { } catch (e) {
loadModule = false; loadHelper = false;
Log.log("No helper found for module: " + moduleName + "."); Log.log(`No helper found for module: ${moduleName}.`);
} }
if (loadModule) { if (loadHelper) {
var Module = require(helperPath); const Module = require(helperPath);
var m = new Module(); let m = new Module();
if (m.requiresVersion) { if (m.requiresVersion) {
Log.log("Check MagicMirror version for node helper '" + moduleName + "' - Minimum version: " + m.requiresVersion + " - Current version: " + global.version); Log.log(`Check MagicMirror version for node helper '${moduleName}' - Minimum version: ${m.requiresVersion} - Current version: ${global.version}`);
if (cmpVersions(global.version, m.requiresVersion) >= 0) { if (cmpVersions(global.version, m.requiresVersion) >= 0) {
Log.log("Version is ok!"); Log.log("Version is ok!");
} else { } else {
Log.log("Version is incorrect. Skip module: '" + moduleName + "'"); Log.warn(`Version is incorrect. Skip module: '${moduleName}'`);
return; return;
} }
} }
@ -152,7 +143,7 @@ var App = function () {
} else { } else {
callback(); callback();
} }
}; }
/** /**
* Loads all modules. * Loads all modules.
@ -160,12 +151,12 @@ var App = function () {
* @param {Module[]} modules All modules to be loaded * @param {Module[]} modules All modules to be loaded
* @param {Function} callback Function to be called after loading * @param {Function} callback Function to be called after loading
*/ */
var loadModules = function (modules, callback) { function loadModules(modules, callback) {
Log.log("Loading module helpers ..."); Log.log("Loading module helpers ...");
var loadNextModule = function () { function loadNextModule() {
if (modules.length > 0) { if (modules.length > 0) {
var nextModule = modules[0]; const nextModule = modules[0];
loadModule(nextModule, function () { loadModule(nextModule, function () {
modules = modules.slice(1); modules = modules.slice(1);
loadNextModule(); loadNextModule();
@ -175,10 +166,10 @@ var App = function () {
Log.log("All module helpers loaded."); Log.log("All module helpers loaded.");
callback(); callback();
} }
}; }
loadNextModule(); loadNextModule();
}; }
/** /**
* Compare two semantic version numbers and return the difference. * Compare two semantic version numbers and return the difference.
@ -190,11 +181,11 @@ var App = function () {
* number if a is smaller and 0 if they are the same * number if a is smaller and 0 if they are the same
*/ */
function cmpVersions(a, b) { function cmpVersions(a, b) {
var i, diff; let i, diff;
var regExStrip0 = /(\.0+)+$/; const regExStrip0 = /(\.0+)+$/;
var segmentsA = a.replace(regExStrip0, "").split("."); const segmentsA = a.replace(regExStrip0, "").split(".");
var segmentsB = b.replace(regExStrip0, "").split("."); const segmentsB = b.replace(regExStrip0, "").split(".");
var l = Math.min(segmentsA.length, segmentsB.length); const l = Math.min(segmentsA.length, segmentsB.length);
for (i = 0; i < l; i++) { for (i = 0; i < l; i++) {
diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10); diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10);
@ -219,21 +210,19 @@ var App = function () {
Log.setLogLevel(config.logLevel); Log.setLogLevel(config.logLevel);
var modules = []; let modules = [];
for (var m in config.modules) { for (const module of config.modules) {
var module = config.modules[m]; if (!modules.includes(module.module) && !module.disabled) {
if (modules.indexOf(module.module) === -1 && !module.disabled) {
modules.push(module.module); modules.push(module.module);
} }
} }
loadModules(modules, function () { loadModules(modules, function () {
var server = new Server(config, function (app, io) { const server = new Server(config, function (app, io) {
Log.log("Server started ..."); Log.log("Server started ...");
for (var h in nodeHelpers) { for (let nodeHelper of nodeHelpers) {
var nodeHelper = nodeHelpers[h];
nodeHelper.setExpressApp(app); nodeHelper.setExpressApp(app);
nodeHelper.setSocketIO(io); nodeHelper.setSocketIO(io);
nodeHelper.start(); nodeHelper.start();
@ -256,8 +245,7 @@ var App = function () {
* Added to fix #1056 * Added to fix #1056
*/ */
this.stop = function () { this.stop = function () {
for (var h in nodeHelpers) { for (const nodeHelper of nodeHelpers) {
var nodeHelper = nodeHelpers[h];
if (typeof nodeHelper.stop === "function") { if (typeof nodeHelper.stop === "function") {
nodeHelper.stop(); nodeHelper.stop();
} }
@ -292,6 +280,6 @@ var App = function () {
this.stop(); this.stop();
process.exit(0); process.exit(0);
}); });
}; }
module.exports = new App(); module.exports = new App();

View File

@ -11,9 +11,9 @@ const linter = new Linter();
const path = require("path"); const path = require("path");
const fs = require("fs"); const fs = require("fs");
const rootPath = path.resolve(__dirname + "/../"); const rootPath = path.resolve(`${__dirname}/../`);
const Log = require(rootPath + "/js/logger.js"); const Log = require(`${rootPath}/js/logger.js`);
const Utils = require(rootPath + "/js/utils.js"); const Utils = require(`${rootPath}/js/utils.js`);
/** /**
* Returns a string with path of configuration file. * Returns a string with path of configuration file.
@ -23,11 +23,7 @@ const Utils = require(rootPath + "/js/utils.js");
*/ */
function getConfigFile() { function getConfigFile() {
// FIXME: This function should be in core. Do you want refactor me ;) ?, be good! // FIXME: This function should be in core. Do you want refactor me ;) ?, be good!
let configFileName = path.resolve(rootPath + "/config/config.js"); return path.resolve(process.env.MM_CONFIG_FILE || `${rootPath}/config/config.js`);
if (process.env.MM_CONFIG_FILE) {
configFileName = path.resolve(process.env.MM_CONFIG_FILE);
}
return configFileName;
} }
/** /**
@ -54,21 +50,18 @@ function checkConfigFile() {
Log.info(Utils.colors.info("Checking file... "), configFileName); Log.info(Utils.colors.info("Checking file... "), configFileName);
// I'm not sure if all ever is utf-8 // I'm not sure if all ever is utf-8
fs.readFile(configFileName, "utf-8", function (err, data) { const configFile = fs.readFileSync(configFileName, "utf-8");
if (err) {
throw err; const errors = linter.verify(configFile);
} if (errors.length === 0) {
const messages = linter.verify(data);
if (messages.length === 0) {
Log.info(Utils.colors.pass("Your configuration file doesn't contain syntax errors :)")); Log.info(Utils.colors.pass("Your configuration file doesn't contain syntax errors :)"));
} else { } else {
Log.error(Utils.colors.error("Your configuration file contains syntax errors :(")); Log.error(Utils.colors.error("Your configuration file contains syntax errors :("));
// In case the there errors show messages and return
messages.forEach((error) => { for (const error of errors) {
Log.error("Line", error.line, "col", error.column, error.message); Log.error(`Line ${error.line} column ${error.column}: ${error.message}`);
}); }
} }
});
} }
checkConfigFile(); checkConfigFile();

View File

@ -20,6 +20,7 @@ var defaults = {
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
language: "en", language: "en",
logLevel: ["INFO", "LOG", "WARN", "ERROR"],
timeFormat: 24, timeFormat: 24,
units: "metric", units: "metric",
zoom: 1, zoom: 1,

View File

@ -6,11 +6,6 @@
* Olex S. original idea this deprecated option * Olex S. original idea this deprecated option
*/ */
var deprecated = { module.exports = {
configs: ["kioskmode"] configs: ["kioskmode"]
}; };
/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {
module.exports = deprecated;
}

View File

@ -5,7 +5,7 @@ const core = require("./app.js");
const Log = require("./logger.js"); const Log = require("./logger.js");
// Config // Config
var config = process.env.config ? JSON.parse(process.env.config) : {}; let config = process.env.config ? JSON.parse(process.env.config) : {};
// Module to control application life. // Module to control application life.
const app = electron.app; const app = electron.app;
// Module to create native browser window. // Module to create native browser window.
@ -20,7 +20,7 @@ let mainWindow;
*/ */
function createWindow() { function createWindow() {
app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required"); app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
var electronOptionsDefaults = { let electronOptionsDefaults = {
width: 800, width: 800,
height: 600, height: 600,
x: 0, x: 0,
@ -42,7 +42,7 @@ function createWindow() {
electronOptionsDefaults.autoHideMenuBar = true; electronOptionsDefaults.autoHideMenuBar = true;
} }
var electronOptions = Object.assign({}, electronOptionsDefaults, config.electronOptions); const electronOptions = Object.assign({}, electronOptionsDefaults, config.electronOptions);
// Create the browser window. // Create the browser window.
mainWindow = new BrowserWindow(electronOptions); mainWindow = new BrowserWindow(electronOptions);
@ -50,14 +50,14 @@ function createWindow() {
// and load the index.html of the app. // and load the index.html of the app.
// If config.address is not defined or is an empty string (listening on all interfaces), connect to localhost // If config.address is not defined or is an empty string (listening on all interfaces), connect to localhost
var prefix; let prefix;
if (config["tls"] !== null && config["tls"]) { if (config["tls"] !== null && config["tls"]) {
prefix = "https://"; prefix = "https://";
} else { } else {
prefix = "http://"; prefix = "http://";
} }
var address = (config.address === void 0) | (config.address === "") ? (config.address = "localhost") : config.address; let address = (config.address === void 0) | (config.address === "") ? (config.address = "localhost") : config.address;
mainWindow.loadURL(`${prefix}${address}:${config.port}`); mainWindow.loadURL(`${prefix}${address}:${config.port}`);
// Open the DevTools if run with "npm start dev" // Open the DevTools if run with "npm start dev"
@ -125,7 +125,7 @@ app.on("before-quit", (event) => {
// Start the core application if server is run on localhost // Start the core application if server is run on localhost
// This starts all node helpers and starts the webserver. // This starts all node helpers and starts the webserver.
if (["localhost", "127.0.0.1", "::1", "::ffff:127.0.0.1", undefined].indexOf(config.address) > -1) { if (["localhost", "127.0.0.1", "::1", "::ffff:127.0.0.1", undefined].includes(config.address)) {
core.start(function (c) { core.start(function (c) {
config = c; config = c;
}); });

View File

@ -510,7 +510,7 @@ Module.register = function (name, moduleDefinition) {
if (cmpVersions(window.version, moduleDefinition.requiresVersion) >= 0) { if (cmpVersions(window.version, moduleDefinition.requiresVersion) >= 0) {
Log.log("Version is ok!"); Log.log("Version is ok!");
} else { } else {
Log.log("Version is incorrect. Skip module: '" + name + "'"); Log.warn("Version is incorrect. Skip module: '" + name + "'");
return; return;
} }
} }

View File

@ -8,18 +8,18 @@ const Class = require("./class.js");
const Log = require("./logger.js"); const Log = require("./logger.js");
const express = require("express"); const express = require("express");
var NodeHelper = Class.extend({ const NodeHelper = Class.extend({
init: function () { init() {
Log.log("Initializing new module helper ..."); Log.log("Initializing new module helper ...");
}, },
loaded: function (callback) { loaded(callback) {
Log.log("Module helper loaded: " + this.name); Log.log(`Module helper loaded: ${this.name}`);
callback(); callback();
}, },
start: function () { start() {
Log.log("Starting module helper: " + this.name); Log.log(`Starting module helper: ${this.name}`);
}, },
/* stop() /* stop()
@ -28,8 +28,8 @@ var NodeHelper = Class.extend({
* gracefully exit the module. * gracefully exit the module.
* *
*/ */
stop: function () { stop() {
Log.log("Stopping module helper: " + this.name); Log.log(`Stopping module helper: ${this.name}`);
}, },
/* socketNotificationReceived(notification, payload) /* socketNotificationReceived(notification, payload)
@ -38,8 +38,8 @@ var NodeHelper = Class.extend({
* argument notification string - The identifier of the notification. * argument notification string - The identifier of the notification.
* argument payload mixed - The payload of the notification. * argument payload mixed - The payload of the notification.
*/ */
socketNotificationReceived: function (notification, payload) { socketNotificationReceived(notification, payload) {
Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload); Log.log(`${this.name} received a socket notification: ${notification} - Payload: ${payload}`);
}, },
/* setName(name) /* setName(name)
@ -47,7 +47,7 @@ var NodeHelper = Class.extend({
* *
* argument name string - Module name. * argument name string - Module name.
*/ */
setName: function (name) { setName(name) {
this.name = name; this.name = name;
}, },
@ -56,7 +56,7 @@ var NodeHelper = Class.extend({
* *
* argument path string - Module path. * argument path string - Module path.
*/ */
setPath: function (path) { setPath(path) {
this.path = path; this.path = path;
}, },
@ -66,7 +66,7 @@ var NodeHelper = Class.extend({
* argument notification string - The identifier of the notification. * argument notification string - The identifier of the notification.
* argument payload mixed - The payload of the notification. * argument payload mixed - The payload of the notification.
*/ */
sendSocketNotification: function (notification, payload) { sendSocketNotification(notification, payload) {
this.io.of(this.name).emit(notification, payload); this.io.of(this.name).emit(notification, payload);
}, },
@ -76,11 +76,10 @@ var NodeHelper = Class.extend({
* *
* argument app Express app - The Express app object. * argument app Express app - The Express app object.
*/ */
setExpressApp: function (app) { setExpressApp(app) {
this.expressApp = app; this.expressApp = app;
var publicPath = this.path + "/public"; app.use(`/${this.name}`, express.static(`${this.path}/public`));
app.use("/" + this.name, express.static(publicPath));
}, },
/* setSocketIO(io) /* setSocketIO(io)
@ -89,27 +88,25 @@ var NodeHelper = Class.extend({
* *
* argument io Socket.io - The Socket io object. * argument io Socket.io - The Socket io object.
*/ */
setSocketIO: function (io) { setSocketIO(io) {
var self = this; this.io = io;
self.io = io;
Log.log("Connecting socket for: " + this.name); Log.log(`Connecting socket for: ${this.name}`);
var namespace = this.name;
io.of(namespace).on("connection", function (socket) { io.of(this.name).on("connection", (socket) => {
// add a catch all event. // add a catch all event.
var onevent = socket.onevent; const onevent = socket.onevent;
socket.onevent = function (packet) { socket.onevent = function (packet) {
var args = packet.data || []; const args = packet.data || [];
onevent.call(this, packet); // original call onevent.call(this, packet); // original call
packet.data = ["*"].concat(args); packet.data = ["*"].concat(args);
onevent.call(this, packet); // additional call to catch-all onevent.call(this, packet); // additional call to catch-all
}; };
// register catch all. // register catch all.
socket.on("*", function (notification, payload) { socket.on("*", (notification, payload) => {
if (notification !== "*") { if (notification !== "*") {
//Log.log('received message in namespace: ' + namespace); this.socketNotificationReceived(notification, payload);
self.socketNotificationReceived(notification, payload);
} }
}); });
}); });
@ -120,7 +117,4 @@ NodeHelper.create = function (moduleDefinition) {
return NodeHelper.extend(moduleDefinition); return NodeHelper.extend(moduleDefinition);
}; };
/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {
module.exports = NodeHelper; module.exports = NodeHelper;
}

View File

@ -4,25 +4,22 @@
* By Michael Teeuw https://michaelteeuw.nl * By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed. * MIT Licensed.
*/ */
var express = require("express"); const express = require("express");
var app = require("express")(); const app = require("express")();
var path = require("path"); const path = require("path");
var ipfilter = require("express-ipfilter").IpFilter; const ipfilter = require("express-ipfilter").IpFilter;
var fs = require("fs"); const fs = require("fs");
var helmet = require("helmet"); const helmet = require("helmet");
var Log = require("./logger.js"); const Log = require("./logger.js");
var Utils = require("./utils.js"); const Utils = require("./utils.js");
var Server = function (config, callback) { function Server(config, callback) {
var port = config.port; const port = process.env.MM_PORT || config.port;
if (process.env.MM_PORT) {
port = process.env.MM_PORT;
}
var server = null; let server = null;
if (config.useHttps) { if (config.useHttps) {
var options = { const options = {
key: fs.readFileSync(config.httpsPrivateKey), key: fs.readFileSync(config.httpsPrivateKey),
cert: fs.readFileSync(config.httpsCertificate) cert: fs.readFileSync(config.httpsCertificate)
}; };
@ -30,18 +27,20 @@ var Server = function (config, callback) {
} else { } else {
server = require("http").Server(app); server = require("http").Server(app);
} }
var io = require("socket.io")(server); const io = require("socket.io")(server, {
cors: {}
});
Log.log("Starting server on port " + port + " ... "); Log.log(`Starting server on port ${port} ... `);
server.listen(port, config.address ? config.address : "localhost"); server.listen(port, config.address || "localhost");
if (config.ipWhitelist instanceof Array && config.ipWhitelist.length === 0) { 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(Utils.colors.warn("You're using a full whitelist configuration to allow for all IPs"));
} }
app.use(function (req, res, next) { app.use(function (req, res, next) {
var result = ipfilter(config.ipWhitelist, { mode: config.ipWhitelist.length === 0 ? "deny" : "allow", log: false })(req, res, function (err) { ipfilter(config.ipWhitelist, { mode: config.ipWhitelist.length === 0 ? "deny" : "allow", log: false })(req, res, function (err) {
if (err === undefined) { if (err === undefined) {
return next(); return next();
} }
@ -52,10 +51,9 @@ var Server = function (config, callback) {
app.use(helmet({ contentSecurityPolicy: false })); app.use(helmet({ contentSecurityPolicy: false }));
app.use("/js", express.static(__dirname)); app.use("/js", express.static(__dirname));
var directories = ["/config", "/css", "/fonts", "/modules", "/vendor", "/translations", "/tests/configs"];
var directory; const directories = ["/config", "/css", "/fonts", "/modules", "/vendor", "/translations", "/tests/configs"];
for (var i in directories) { for (const directory of directories) {
directory = directories[i];
app.use(directory, express.static(path.resolve(global.root_path + directory))); app.use(directory, express.static(path.resolve(global.root_path + directory)));
} }
@ -68,10 +66,10 @@ var Server = function (config, callback) {
}); });
app.get("/", function (req, res) { app.get("/", function (req, res) {
var html = fs.readFileSync(path.resolve(global.root_path + "/index.html"), { encoding: "utf8" }); let html = fs.readFileSync(path.resolve(`${global.root_path}/index.html`), { encoding: "utf8" });
html = html.replace("#VERSION#", global.version); html = html.replace("#VERSION#", global.version);
var configFile = "config/config.js"; let configFile = "config/config.js";
if (typeof global.configuration_file !== "undefined") { if (typeof global.configuration_file !== "undefined") {
configFile = global.configuration_file; configFile = global.configuration_file;
} }
@ -83,6 +81,6 @@ var Server = function (config, callback) {
if (typeof callback === "function") { if (typeof callback === "function") {
callback(app, io); callback(app, io);
} }
}; }
module.exports = Server; module.exports = Server;

View File

@ -4,9 +4,9 @@
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com * By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
* MIT Licensed. * MIT Licensed.
*/ */
var colors = require("colors/safe"); const colors = require("colors/safe");
var Utils = { module.exports = {
colors: { colors: {
warn: colors.yellow, warn: colors.yellow,
error: colors.red, error: colors.red,
@ -14,7 +14,3 @@ var Utils = {
pass: colors.green pass: colors.green
} }
}; };
if (typeof module !== "undefined") {
module.exports = Utils;
}

View File

@ -593,6 +593,7 @@ Module.register("currentweather", {
*/ */
roundValue: function (temperature) { roundValue: function (temperature) {
var decimals = this.config.roundTemp ? 0 : 1; var decimals = this.config.roundTemp ? 0 : 1;
return parseFloat(temperature).toFixed(decimals); var roundValue = parseFloat(temperature).toFixed(decimals);
return roundValue === "-0" ? 0 : roundValue;
} }
}); });

View File

@ -178,7 +178,8 @@ Module.register("weather", {
roundValue: function (temperature) { roundValue: function (temperature) {
var decimals = this.config.roundTemp ? 0 : 1; var decimals = this.config.roundTemp ? 0 : 1;
return parseFloat(temperature).toFixed(decimals); var roundValue = parseFloat(temperature).toFixed(decimals);
return roundValue === "-0" ? 0 : roundValue;
}, },
addFilters() { addFilters() {

View File

@ -462,7 +462,8 @@ Module.register("weatherforecast", {
*/ */
roundValue: function (temperature) { roundValue: function (temperature) {
var decimals = this.config.roundTemp ? 0 : 1; var decimals = this.config.roundTemp ? 0 : 1;
return parseFloat(temperature).toFixed(decimals); var roundValue = parseFloat(temperature).toFixed(decimals);
return roundValue === "-0" ? 0 : roundValue;
}, },
/* processRain(forecast, allForecasts) /* processRain(forecast, allForecasts)

2213
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "magicmirror", "name": "magicmirror",
"version": "2.14.0-develop", "version": "2.15.0-develop",
"description": "The open source modular smart mirror platform.", "description": "The open source modular smart mirror platform.",
"main": "js/electron.js", "main": "js/electron.js",
"scripts": { "scripts": {
@ -10,11 +10,11 @@
"install-fonts": "echo \"Installing fonts ...\n\" && cd fonts && npm install --loglevel=error", "install-fonts": "echo \"Installing fonts ...\n\" && cd fonts && npm install --loglevel=error",
"postinstall": "npm run install-fonts && echo \"MagicMirror installation finished successfully! \n\"", "postinstall": "npm run install-fonts && echo \"MagicMirror installation finished successfully! \n\"",
"test": "NODE_ENV=test mocha tests --recursive", "test": "NODE_ENV=test mocha tests --recursive",
"test:coverage": "NODE_ENV=test nyc mocha tests --recursive --timeout=3000", "test:coverage": "NODE_ENV=test nyc --reporter=lcov --reporter=text mocha tests --recursive --timeout=3000",
"test:e2e": "NODE_ENV=test mocha tests/e2e --recursive", "test:e2e": "NODE_ENV=test mocha tests/e2e --recursive",
"test:unit": "NODE_ENV=test mocha tests/unit --recursive", "test:unit": "NODE_ENV=test mocha tests/unit --recursive",
"test:prettier": "prettier --check **/*.{js,css,json,md,yml}", "test:prettier": "prettier --check **/*.{js,css,json,md,yml}",
"test:js": "eslint *.js js/**/*.js modules/default/**/*.js clientonly/*.js serveronly/*.js translations/*.js vendor/*.js tests/**/*.js config/* --config .eslintrc.json --quiet", "test:js": "eslint js/**/*.js modules/default/**/*.js clientonly/*.js serveronly/*.js translations/*.js vendor/*.js tests/**/*.js config/* --config .eslintrc.json --quiet",
"test:css": "stylelint css/main.css modules/default/**/*.css --config .stylelintrc.json", "test:css": "stylelint css/main.css modules/default/**/*.css --config .stylelintrc.json",
"test:calendar": "node ./modules/default/calendar/debug.js", "test:calendar": "node ./modules/default/calendar/debug.js",
"config:check": "node js/check_config.js", "config:check": "node js/check_config.js",
@ -44,12 +44,11 @@
"devDependencies": { "devDependencies": {
"chai": "^4.2.0", "chai": "^4.2.0",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"danger": "^10.5.4", "eslint-config-prettier": "^7.1.0",
"eslint-config-prettier": "^7.0.0", "eslint-plugin-jsdoc": "^30.7.13",
"eslint-plugin-jsdoc": "^30.7.8", "eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-prettier": "^3.2.0",
"express-basic-auth": "^1.2.0", "express-basic-auth": "^1.2.0",
"husky": "^4.3.5", "husky": "^4.3.6",
"jsdom": "^16.4.0", "jsdom": "^16.4.0",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"mocha": "^8.2.1", "mocha": "^8.2.1",
@ -70,11 +69,11 @@
"dependencies": { "dependencies": {
"colors": "^1.4.0", "colors": "^1.4.0",
"console-stamp": "^3.0.0-rc4.2", "console-stamp": "^3.0.0-rc4.2",
"eslint": "^7.15.0", "eslint": "^7.17.0",
"express": "^4.17.1", "express": "^4.17.1",
"express-ipfilter": "^1.1.2", "express-ipfilter": "^1.1.2",
"feedme": "^2.0.2", "feedme": "^2.0.2",
"helmet": "^4.2.0", "helmet": "^4.3.1",
"ical": "^0.8.0", "ical": "^0.8.0",
"iconv-lite": "^0.6.2", "iconv-lite": "^0.6.2",
"module-alias": "^2.2.2", "module-alias": "^2.2.2",
@ -84,12 +83,15 @@
"rrule": "^2.6.6", "rrule": "^2.6.6",
"rrule-alt": "^2.2.8", "rrule-alt": "^2.2.8",
"simple-git": "^2.31.0", "simple-git": "^2.31.0",
"socket.io": "^3.0.4", "socket.io": "^3.0.5",
"valid-url": "^1.0.9" "valid-url": "^1.0.9"
}, },
"_moduleAliases": { "_moduleAliases": {
"node_helper": "js/node_helper.js" "node_helper": "js/node_helper.js"
}, },
"engines": {
"node": ">=10"
},
"husky": { "husky": {
"hooks": { "hooks": {
"pre-commit": "pretty-quick --staged" "pre-commit": "pretty-quick --staged"

View File

@ -3,12 +3,12 @@ const path = require("path");
const expect = require("chai").expect; const expect = require("chai").expect;
const vm = require("vm"); const vm = require("vm");
before(function () { const basedir = path.join(__dirname, "../../..");
var basedir = path.join(__dirname, "../../..");
var fileName = "js/app.js"; before(function () {
var filePath = path.join(basedir, fileName); const fileName = "js/app.js";
var code = fs.readFileSync(filePath); const filePath = path.join(basedir, fileName);
const code = fs.readFileSync(filePath);
this.sandbox = { this.sandbox = {
module: {}, module: {},
@ -36,22 +36,12 @@ before(function () {
vm.runInNewContext(code, this.sandbox, fileName); vm.runInNewContext(code, this.sandbox, fileName);
}); });
after(function () {
//console.log(global);
});
describe("Default modules set in modules/default/defaultmodules.js", function () { describe("Default modules set in modules/default/defaultmodules.js", function () {
var expectedDefaultModules = ["alert", "calendar", "clock", "compliments", "currentweather", "helloworld", "newsfeed", "weatherforecast", "updatenotification"]; const expectedDefaultModules = require("../../../modules/default/defaultmodules");
expectedDefaultModules.forEach((defaultModule) => { for (const defaultModule of expectedDefaultModules) {
it(`contains default module "${defaultModule}"`, function () {
expect(this.sandbox.defaultModules).to.include(defaultModule);
});
});
expectedDefaultModules.forEach((defaultModule) => {
it(`contains a folder for modules/default/${defaultModule}"`, function () { it(`contains a folder for modules/default/${defaultModule}"`, function () {
expect(fs.existsSync(path.join(this.sandbox.global.root_path, "modules/default", defaultModule))).to.equal(true); expect(fs.existsSync(path.join(this.sandbox.global.root_path, "modules/default", defaultModule))).to.equal(true);
}); });
}); }
}); });