Merge branch 'develop' of https://github.com/MichMich/MagicMirror into develop

This commit is contained in:
Michael Teeuw 2020-04-01 09:58:55 +02:00
commit f7f4e92e0a
35 changed files with 618 additions and 445 deletions

View File

@ -7,6 +7,7 @@
"curly": "error", "curly": "error",
"camelcase": ["error", {"properties": "never"}], "camelcase": ["error", {"properties": "never"}],
"no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 1 }], "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 1 }],
"no-multi-spaces": "error",
"no-trailing-spaces": ["error", {"ignoreComments": false }], "no-trailing-spaces": ["error", {"ignoreComments": false }],
"no-irregular-whitespace": ["error"] "no-irregular-whitespace": ["error"]
}, },

View File

@ -1,7 +1,9 @@
dist: trusty dist: trusty
language: node_js language: node_js
node_js: node_js:
- "10" - 10
- lts/*
- node
before_install: before_install:
- npm i -g npm - npm i -g npm
before_script: before_script:
@ -12,9 +14,9 @@ before_script:
- "sh -e /etc/init.d/xvfb start" - "sh -e /etc/init.d/xvfb start"
- sleep 5 - sleep 5
script: script:
- npm run test:lint
- npm run test:e2e - npm run test:e2e
- npm run test:unit - npm run test:unit
- grunt
after_script: after_script:
- npm list - npm list
cache: cache:

View File

@ -12,19 +12,29 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Deleted ### Deleted
- Remove installers. - Remove installers.
- Remove externalized scripts. - Remove externalized scripts.
- Remove jshint dependency, instead eslint checks your config file now
### Added ### Added
- Brazilian translation for "FEELS".
- Ukrainian translation. - Ukrainian translation.
- Finnish translation for "PRECIP", "UPDATE_INFO_MULTIPLE" and "UPDATE_INFO_SINGLE". - Finnish translation for "PRECIP", "UPDATE_INFO_MULTIPLE" and "UPDATE_INFO_SINGLE".
- Added the ability to hide the temp label and weather icon in the `currentweather` module to allow showing only information such as wind and sunset/rise. - Added the ability to hide the temp label and weather icon in the `currentweather` module to allow showing only information such as wind and sunset/rise.
- The `clock` module now optionally displays sun and moon data, including rise/set times, remaining daylight, and percent of moon illumination. - The `clock` module now optionally displays sun and moon data, including rise/set times, remaining daylight, and percent of moon illumination.
- Added Hebrew translation. - Added Hebrew translation.
- Add HTTPS support and update config.js.sample
- Run tests on long term support and latest stable version of nodejs
- Added the ability to configure a list of modules that shouldn't be update checked.
- Run linters on git commits
### Fixed ### Fixed
- Force declaration of public ip adress in config file (ISSUE #1852) - Force declaration of public ip address in config file (ISSUE #1852)
- Fixes `run-start.sh`: If running in docker-container, don't check the environment, just start electron (ISSUE #1859) - Fixes `run-start.sh`: If running in docker-container, don't check the environment, just start electron (ISSUE #1859)
- Fix calendar time offset for recurring events crossing Daylight Savings Time (ISSUE #1798) - Fix calendar time offset for recurring events crossing Daylight Savings Time (ISSUE #1798)
- Fix regression in currentweather module causing 'undefined' to show up when config.hideTemp is false - Fix regression in currentweather module causing 'undefined' to show up when config.hideTemp is false
- Fix FEELS translation for Croatian
- Fixed weather tests [#1840](https://github.com/MichMich/MagicMirror/issues/1840)
- Fixed Socket.io can't be used with Reverse Proxy in serveronly mode [#1934](https://github.com/MichMich/MagicMirror/issues/1934)
- Fix update checking skipping 3rd party modules the first time
### Changed ### Changed
- Remove documentation from core repository and link to new dedicated docs site: [docs.magicmirror.builders](https://docs.magicmirror.builders). - Remove documentation from core repository and link to new dedicated docs site: [docs.magicmirror.builders](https://docs.magicmirror.builders).
@ -33,6 +43,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- To start using electron, use `npm run start`. - To start using electron, use `npm run start`.
- To start in server only mode, use `npm run server`. - To start in server only mode, use `npm run server`.
- Remove redundant logging from modules. - Remove redundant logging from modules.
- Timestamp in log output now also contains the date
- Turkish translation.
- Option to configure the size of the currentweather module.
## [2.10.1] - 2020-01-10 ## [2.10.1] - 2020-01-10
@ -572,7 +585,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- Added option `remoteFile` to compliments module to load compliment array from filesystem. - Added option `remoteFile` to compliments module to load compliment array from filesystem.
- Added option `zoom` to scale the whole mirror display with a given factor. - Added option `zoom` to scale the whole mirror display with a given factor.
- Added option `roundTemp` for currentweather and weatherforecast modules to display temperatures rounded to nearest integer. - Added option `roundTemp` for currentweather and weatherforecast modules to display temperatures rounded to nearest integer.
- Added abilty set the classes option to compliments module for style and text size of compliments. - Added ability set the classes option to compliments module for style and text size of compliments.
- Added ability to configure electronOptions - Added ability to configure electronOptions
- Calendar module: option to hide private events - Calendar module: option to hide private events
- Add root_path for global vars - Add root_path for global vars

View File

@ -1,10 +1,10 @@
module.exports = function(grunt) { module.exports = function(grunt) {
require("time-grunt")(grunt); require("time-grunt")(grunt);
var fix = (grunt.option("env") || "lint") === "lint";
grunt.initConfig({ grunt.initConfig({
pkg: grunt.file.readJSON("package.json"),
eslint: { eslint: {
options: { options: {
fix: "true", fix: fix,
configFile: ".eslintrc.json" configFile: ".eslintrc.json"
}, },
target: [ target: [
@ -20,13 +20,13 @@ module.exports = function(grunt) {
"!modules/default/alert/classie.js", "!modules/default/alert/classie.js",
"config/*", "config/*",
"translations/translations.js", "translations/translations.js",
"vendor/vendor.js", "vendor/vendor.js"
"modules/node_modules/node_helper/index.js"
] ]
}, },
stylelint: { stylelint: {
simple: { simple: {
options: { options: {
fix: fix,
configFile: ".stylelintrc.json" configFile: ".stylelintrc.json"
}, },
src: [ src: [
@ -44,7 +44,6 @@ module.exports = function(grunt) {
"package.json", "package.json",
".eslintrc.json", ".eslintrc.json",
".stylelintrc.json", ".stylelintrc.json",
"installers/pm2_MagicMirror.json",
"translations/*.json", "translations/*.json",
"modules/default/*/translations/*.json", "modules/default/*/translations/*.json",
"vendor/package.json" "vendor/package.json"
@ -102,5 +101,6 @@ module.exports = function(grunt) {
grunt.loadNpmTasks("grunt-jsonlint"); grunt.loadNpmTasks("grunt-jsonlint");
grunt.loadNpmTasks("grunt-yamllint"); grunt.loadNpmTasks("grunt-yamllint");
grunt.loadNpmTasks("grunt-markdownlint"); grunt.loadNpmTasks("grunt-markdownlint");
grunt.registerTask("default", ["eslint", "stylelint", "jsonlint", "markdownlint", "yamllint"]); grunt.registerTask("default", ["eslint", "stylelint", "jsonlint", "markdownlint", "yamllint"]);
}; };

View File

@ -1,5 +1,3 @@
/* jshint esversion: 6 */
"use strict"; "use strict";
// Use separate scope to prevent global scope pollution // Use separate scope to prevent global scope pollution

View File

@ -21,6 +21,10 @@ var config = {
// or IPv4 range of 192.168.3.0 --> 192.168.3.15 use CIDR format : // or IPv4 range of 192.168.3.0 --> 192.168.3.15 use CIDR format :
// ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.3.0/28"], // ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.3.0/28"],
useHttps: false, // Support HTTPS or not, default "false" will use HTTP
httpsPrivateKey: "", // HTTPS private key path, only require when useHttps is true
httpsCertificate: "", // HTTPS Certificate path, only require when useHttps is true
language: "en", language: "en",
timeFormat: 24, timeFormat: 24,
units: "metric", units: "metric",

View File

@ -37,7 +37,7 @@
<div class="region bottom right"><div class="container"></div></div> <div class="region bottom right"><div class="container"></div></div>
</div> </div>
<div class="region fullscreen above"><div class="container"></div></div> <div class="region fullscreen above"><div class="container"></div></div>
<script type="text/javascript" src="/socket.io/socket.io.js"></script> <script type="text/javascript" src="socket.io/socket.io.js"></script>
<script type="text/javascript" src="vendor/node_modules/nunjucks/browser/nunjucks.min.js"></script> <script type="text/javascript" src="vendor/node_modules/nunjucks/browser/nunjucks.min.js"></script>
<script type="text/javascript" src="js/defaults.js"></script> <script type="text/javascript" src="js/defaults.js"></script>
<script type="text/javascript" src="#CONFIG_FILE#"></script> <script type="text/javascript" src="#CONFIG_FILE#"></script>

View File

@ -14,7 +14,7 @@ var path = require("path");
require("module-alias/register"); require("module-alias/register");
// add timestamps in front of log messages // add timestamps in front of log messages
require("console-stamp")(console, "HH:MM:ss.l"); require("console-stamp")(console, "yyyy-mm-dd HH:MM:ss.l");
// Get version number. // Get version number.
global.version = JSON.parse(fs.readFileSync("package.json", "utf8")).version; global.version = JSON.parse(fs.readFileSync("package.json", "utf8")).version;

View File

@ -1,5 +1,3 @@
/* jshint esversion: 6 */
"use strict"; "use strict";
const electron = require("electron"); const electron = require("electron");

View File

@ -1,5 +1,4 @@
/* global Log, Loader, Module, config, defaults */ /* global Log, Loader, Module, config, defaults */
/* jshint -W020, esversion: 6 */
/* Magic Mirror /* Magic Mirror
* Main System * Main System
@ -306,7 +305,9 @@ var MM = (function() {
module.showHideTimer = setTimeout(function() { module.showHideTimer = setTimeout(function() {
if (typeof callback === "function") { callback(); } if (typeof callback === "function") { callback(); }
}, speed); }, speed);
} else {
// invoke callback
if (typeof callback === "function") { callback(); }
} }
}; };

View File

@ -417,8 +417,11 @@ var Module = Class.extend({
callback = callback || function () { }; callback = callback || function () { };
options = options || {}; options = options || {};
this.resume(); var self = this;
MM.showModule(this, speed, callback, options); MM.showModule(this, speed, function () {
self.resume();
callback;
}, options);
} }
}); });

View File

@ -7,8 +7,6 @@
var express = require("express"); var express = require("express");
var app = require("express")(); var app = require("express")();
var server = require("http").Server(app);
var io = require("socket.io")(server);
var path = require("path"); var path = require("path");
var ipfilter = require("express-ipfilter").IpFilter; var ipfilter = require("express-ipfilter").IpFilter;
var fs = require("fs"); var fs = require("fs");
@ -22,6 +20,18 @@ var Server = function(config, callback) {
port = process.env.MM_PORT; port = process.env.MM_PORT;
} }
var server = null;
if(config.useHttps){
var options = {
key: fs.readFileSync(config.httpsPrivateKey),
cert: fs.readFileSync(config.httpsCertificate)
};
server = require("https").Server(options, app);
}else{
server = require("http").Server(app);
}
var io = require("socket.io")(server);
console.log("Starting server on port " + port + " ... "); console.log("Starting server on port " + port + " ... ");
server.listen(port, config.address ? config.address : "localhost"); server.listen(port, config.address ? config.address : "localhost");

View File

@ -8,7 +8,9 @@ var MMSocket = function(moduleName) {
self.moduleName = moduleName; self.moduleName = moduleName;
// Private Methods // Private Methods
self.socket = io("/" + self.moduleName); self.socket = io("/" + self.moduleName, {
path: window.location.pathname + "socket.io"
});
var notificationCallback = function() {}; var notificationCallback = function() {};
var onevent = self.socket.onevent; var onevent = self.socket.onevent;

View File

@ -152,9 +152,9 @@ Module.register("clock",{
} }
function formatTime(config, time) { function formatTime(config, time) {
var formatString = hourSymbol + ':mm'; var formatString = hourSymbol + ":mm";
if (config.showPeriod && config.timeFormat !== 24) { if (config.showPeriod && config.timeFormat !== 24) {
formatString += config.showPeriodUpper ? 'A' : 'a'; formatString += config.showPeriodUpper ? "A" : "a";
} }
return moment(time).format(formatString); return moment(time).format(formatString);
} }
@ -167,23 +167,31 @@ Module.register("clock",{
} else if (now.isBefore(sunTimes.sunset)) { } else if (now.isBefore(sunTimes.sunset)) {
nextEvent = sunTimes.sunset; nextEvent = sunTimes.sunset;
} else { } else {
const tomorrowSunTimes = SunCalc.getTimes(now.clone().add(1, 'day'), this.config.lat, this.config.lon); const tomorrowSunTimes = SunCalc.getTimes(now.clone().add(1, "day"), this.config.lat, this.config.lon);
nextEvent = tomorrowSunTimes.sunrise; nextEvent = tomorrowSunTimes.sunrise;
} }
const untilNextEvent = moment.duration(moment(nextEvent).diff(now)); const untilNextEvent = moment.duration(moment(nextEvent).diff(now));
const untilNextEventString = untilNextEvent.hours() + 'h ' + untilNextEvent.minutes() + 'm'; const untilNextEventString = untilNextEvent.hours() + "h " + untilNextEvent.minutes() + "m";
sunWrapper.innerHTML = '<span class="' + (isVisible ? 'bright' : '') + '"><i class="fa fa-sun-o" aria-hidden="true"></i> ' + untilNextEventString + '</span>' + sunWrapper.innerHTML = "<span class=\"" + (isVisible ? "bright" : "") + "\"><i class=\"fa fa-sun-o\" aria-hidden=\"true\"></i> " + untilNextEventString + "</span>" +
'<span><i class="fa fa-arrow-up" aria-hidden="true"></i>' + formatTime(this.config, sunTimes.sunrise) + '</span>' + "<span><i class=\"fa fa-arrow-up\" aria-hidden=\"true\"></i>" + formatTime(this.config, sunTimes.sunrise) + "</span>" +
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i>' + formatTime(this.config, sunTimes.sunset) + '</span>'; "<span><i class=\"fa fa-arrow-down\" aria-hidden=\"true\"></i>" + formatTime(this.config, sunTimes.sunset) + "</span>";
} }
if (this.config.showMoonTimes) { if (this.config.showMoonTimes) {
const moonIllumination = SunCalc.getMoonIllumination(now.toDate()); const moonIllumination = SunCalc.getMoonIllumination(now.toDate());
const moonTimes = SunCalc.getMoonTimes(now, this.config.lat, this.config.lon); const moonTimes = SunCalc.getMoonTimes(now, this.config.lat, this.config.lon);
const isVisible = now.isBetween(moonTimes.rise, moonTimes.set); const moonRise = moonTimes.rise;
const illuminatedFractionString = moonIllumination.fraction.toLocaleString(undefined, {style: 'percent'}); var moonSet;
moonWrapper.innerHTML = '<span class="' + (isVisible ? 'bright' : '') + '"><i class="fa fa-moon-o" aria-hidden="true"></i> ' + illuminatedFractionString + '</span>' + if (moment(moonTimes.set).isAfter(moonTimes.rise)) {
'<span><i class="fa fa-arrow-up" aria-hidden="true"></i> ' + formatTime(this.config, moonTimes.rise) + '</span>'+ moonSet = moonTimes.set;
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i> ' + formatTime(this.config, moonTimes.set) + '</span>'; } else {
const nextMoonTimes = SunCalc.getMoonTimes(now.clone().add(1, "day"), this.config.lat, this.config.lon);
moonSet = nextMoonTimes.set;
}
const isVisible = now.isBetween(moonRise, moonSet) || moonTimes.alwaysUp === true;
const illuminatedFractionString = Math.round(moonIllumination.fraction * 100) + "%";
moonWrapper.innerHTML = "<span class=\"" + (isVisible ? "bright" : "") + "\"><i class=\"fa fa-moon-o\" aria-hidden=\"true\"></i> " + illuminatedFractionString + "</span>" +
"<span><i class=\"fa fa-arrow-up\" aria-hidden=\"true\"></i> " + (moonRise ? formatTime(this.config, moonRise) : "...") + "</span>"+
"<span><i class=\"fa fa-arrow-down\" aria-hidden=\"true\"></i> " + (moonSet ? formatTime(this.config, moonSet) : "...") + "</span>";
} }
/**************************************************************** /****************************************************************

View File

@ -42,6 +42,7 @@ Module.register("currentweather",{
appendLocationNameToHeader: true, appendLocationNameToHeader: true,
calendarClass: "calendar", calendarClass: "calendar",
tableClass: "large",
onlyTemp: false, onlyTemp: false,
hideTemp: false, hideTemp: false,
@ -175,6 +176,7 @@ Module.register("currentweather",{
// Override dom generator. // Override dom generator.
getDom: function() { getDom: function() {
var wrapper = document.createElement("div"); var wrapper = document.createElement("div");
wrapper.className = this.config.tableClass;
if (this.config.appid === "") { if (this.config.appid === "") {
wrapper.innerHTML = "Please set the correct openweather <i>appid</i> in the config for module: " + this.name + "."; wrapper.innerHTML = "Please set the correct openweather <i>appid</i> in the config for module: " + this.name + ".";
@ -193,7 +195,7 @@ Module.register("currentweather",{
} }
var large = document.createElement("div"); var large = document.createElement("div");
large.className = "large light"; large.className = "light";
var degreeLabel = ""; var degreeLabel = "";
if (this.config.units === "metric" || this.config.units === "imperial") { if (this.config.units === "metric" || this.config.units === "imperial") {
@ -217,7 +219,7 @@ Module.register("currentweather",{
this.config.decimalSymbol = "."; this.config.decimalSymbol = ".";
} }
if (this.config.hideTemp === true) { if (this.config.hideTemp === false) {
var weatherIcon = document.createElement("span"); var weatherIcon = document.createElement("span");
weatherIcon.className = "wi weathericon " + this.weatherType; weatherIcon.className = "wi weathericon " + this.weatherType;
large.appendChild(weatherIcon); large.appendChild(weatherIcon);

View File

@ -18,37 +18,30 @@ module.exports = NodeHelper.create({
configureModules: function(modules) { configureModules: function(modules) {
// Push MagicMirror itself , biggest chance it'll show up last in UI and isn't overwritten // Push MagicMirror itself , biggest chance it'll show up last in UI and isn't overwritten
// others will be added in front, asynchronously // others will be added in front
// this method returns promises so we can't wait for every one to resolve before continuing
simpleGits.push({"module": "default", "git": SimpleGit(path.normalize(__dirname + "/../../../"))}); simpleGits.push({"module": "default", "git": SimpleGit(path.normalize(__dirname + "/../../../"))});
var promises = [];
for (moduleName in modules) { for (moduleName in modules) {
if (defaultModules.indexOf(moduleName) < 0) { if (!this.ignoreUpdateChecking(moduleName)) {
// Default modules are included in the main MagicMirror repo // Default modules are included in the main MagicMirror repo
var moduleFolder = path.normalize(__dirname + "/../../" + moduleName); var moduleFolder = path.normalize(__dirname + "/../../" + moduleName);
var stat;
try { try {
//console.log("checking git for module="+moduleName) //console.log("checking git for module="+moduleName)
stat = fs.statSync(path.join(moduleFolder, ".git")); let stat = fs.statSync(path.join(moduleFolder, ".git"));
promises.push(this.resolveRemote(moduleName, moduleFolder));
} catch(err) { } catch(err) {
// Error when directory .git doesn't exist // Error when directory .git doesn't exist
// This module is not managed with git, skip // This module is not managed with git, skip
continue; continue;
} }
}
}
var res = function(mn, mf) { return Promise.all(promises);
var git = SimpleGit(mf);
git.getRemotes(true, function(err, remotes) {
if (remotes.length < 1 || remotes[0].name.length < 1) {
// No valid remote for folder, skip
return;
}
// Folder has .git and has at least one git remote, watch this folder
simpleGits.unshift({"module": mn, "git": git});
});
}(moduleName, moduleFolder);
}
}
}, },
socketNotificationReceived: function (notification, payload) { socketNotificationReceived: function (notification, payload) {
@ -56,21 +49,35 @@ module.exports = NodeHelper.create({
this.config = payload; this.config = payload;
} else if(notification === "MODULES") { } else if(notification === "MODULES") {
// if this is the 1st time thru the update check process // if this is the 1st time thru the update check process
if(this.updateProcessStarted==false ){ if (!this.updateProcessStarted) {
this.updateProcessStarted = true; this.updateProcessStarted = true;
this.configureModules(payload); this.configureModules(payload).then(() => this.performFetch());
this.preformFetch();
} }
} }
}, },
preformFetch() { resolveRemote: function(moduleName, moduleFolder) {
return new Promise((resolve, reject) => {
var git = SimpleGit(moduleFolder);
git.getRemotes(true, (err, remotes) => {
if (remotes.length < 1 || remotes[0].name.length < 1) {
// No valid remote for folder, skip
return resolve();
}
// Folder has .git and has at least one git remote, watch this folder
simpleGits.unshift({"module": moduleName, "git": git});
resolve();
});
});
},
performFetch: function() {
var self = this; var self = this;
simpleGits.forEach(function(sg) { simpleGits.forEach((sg) => {
sg.git.fetch().status(function(err, data) { sg.git.fetch().status((err, data) => {
data.module = sg.module; data.module = sg.module;
if (!err) { if (!err) {
sg.git.log({"-1": null}, function(err, data2) { sg.git.log({"-1": null}, (err, data2) => {
if (!err && data2.latest && "hash" in data2.latest) { if (!err && data2.latest && "hash" in data2.latest) {
data.hash = data2.latest.hash; data.hash = data2.latest.hash;
self.sendSocketNotification("STATUS", data); self.sendSocketNotification("STATUS", data);
@ -91,8 +98,23 @@ module.exports = NodeHelper.create({
var self = this; var self = this;
clearTimeout(this.updateTimer); clearTimeout(this.updateTimer);
this.updateTimer = setTimeout(function() { this.updateTimer = setTimeout(function() {
self.preformFetch(); self.performFetch();
}, delay); }, delay);
},
ignoreUpdateChecking: function(moduleName) {
// Should not check for updates for default modules
if (defaultModules.indexOf(moduleName) >= 0) {
return true;
}
// Should not check for updates for ignored modules
if (this.config.ignoreModules.indexOf(moduleName) >= 0) {
return true;
}
// The rest of the modules that passes should check for updates
return false;
} }
}); });

View File

@ -3,6 +3,7 @@ Module.register("updatenotification", {
defaults: { defaults: {
updateInterval: 10 * 60 * 1000, // every 10 minutes updateInterval: 10 * 60 * 1000, // every 10 minutes
refreshInterval: 24 * 60 * 60 * 1000, // one day refreshInterval: 24 * 60 * 60 * 1000, // one day
ignoreModules: []
}, },
suspended: false, suspended: false,

684
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,9 @@
"test": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests --recursive", "test": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests --recursive",
"test:unit": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests/unit --recursive", "test:unit": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests/unit --recursive",
"test:e2e": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests/e2e --recursive", "test:e2e": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests/e2e --recursive",
"test:lint": "grunt --env=test",
"config:check": "node tests/configs/check_config.js", "config:check": "node tests/configs/check_config.js",
"lint": "grunt" "lint": "grunt --env=lint"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -47,7 +48,6 @@
"grunt-yamllint": "latest", "grunt-yamllint": "latest",
"http-auth": "^3.2.3", "http-auth": "^3.2.3",
"jsdom": "^11.6.2", "jsdom": "^11.6.2",
"jshint": "^2.10.2",
"mocha": "^7.0.0", "mocha": "^7.0.0",
"mocha-each": "^1.1.0", "mocha-each": "^1.1.0",
"mocha-logger": "^1.0.6", "mocha-logger": "^1.0.6",

View File

@ -1,5 +1,6 @@
var app = require("../js/app.js"); var app = require("../js/app.js");
app.start(function(config) { app.start(function(config) {
var bindAddress = config.address ? config.address : "localhost"; var bindAddress = config.address ? config.address : "localhost";
console.log("\nReady to go! Please point your browser to: http://" + bindAddress + ":" + config.port); var httpType = config.useHttps ? "https" : "http";
console.log("\nReady to go! Please point your browser to: " + httpType + "://" + bindAddress + ":" + config.port);
}); });

View File

@ -9,7 +9,10 @@
* *
*/ */
var v = require("jshint"); const Linter = require("eslint").Linter;
const linter = new Linter();
const config = require(__dirname + "/../../.eslintrc.json");
var path = require("path"); var path = require("path");
var fs = require("fs"); var fs = require("fs");
var Utils = require(__dirname + "/../../js/utils.js"); var Utils = require(__dirname + "/../../js/utils.js");
@ -50,16 +53,15 @@ function checkConfigFile() {
// 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) { fs.readFile(configFileName, "utf-8", function (err, data) {
if (err) { throw err; } if (err) { throw err; }
v.JSHINT(data); // Parser by jshint const messages = linter.verify(data, config);
if (messages.length === 0) {
if (v.JSHINT.errors.length === 0) {
console.log("Your configuration file doesn't contain syntax errors :)"); console.log("Your configuration file doesn't contain syntax errors :)");
return true; return true;
} else { } else {
errors = v.JSHINT.data().errors; errors = messages;
for (var idx in errors) { for (var idx in errors) {
error = errors[idx]; error = errors[idx];
console.log("Line", error.line, "col", error.character, error.reason); console.log("Line", error.line, "col", error.column, error.message);
} }
} }
}); });

View File

@ -8,14 +8,7 @@ const helpers = require("../global-setup");
const {generateWeather, generateWeatherForecast} = require("./mocks"); const {generateWeather, generateWeatherForecast} = require("./mocks");
const wait = () => new Promise(res => setTimeout(res, 3000)); describe("Weather module", function() {
// See issue: https://github.com/MichMich/MagicMirror/issues/1840
// Skipping the weather tests for now since these seem to give issues.
// Please send a PR if you know how to fix these. Thanks!
describe.skip("Weather module", function() {
let app; let app;
@ -49,7 +42,7 @@ describe.skip("Weather module", function() {
it("should render wind speed and wind direction", async function() { it("should render wind speed and wind direction", async function() {
const weather = generateWeather(); const weather = generateWeather();
await setup([weather, template]); await setup({template, data: weather});
return app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "6 WSW", 10000); return app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "6 WSW", 10000);
}); });
@ -59,7 +52,7 @@ describe.skip("Weather module", function() {
const sunset = moment().startOf("day").unix(); const sunset = moment().startOf("day").unix();
const weather = generateWeather({sys: {sunrise, sunset}}); const weather = generateWeather({sys: {sunrise, sunset}});
await setup([weather, template]); await setup({template, data: weather});
await app.client.waitForExist(".weather .normal.medium span.wi.dimmed.wi-sunrise", 10000); await app.client.waitForExist(".weather .normal.medium span.wi.dimmed.wi-sunrise", 10000);
@ -71,7 +64,7 @@ describe.skip("Weather module", function() {
const sunset = moment().endOf("day").unix(); const sunset = moment().endOf("day").unix();
const weather = generateWeather({sys: {sunrise, sunset}}); const weather = generateWeather({sys: {sunrise, sunset}});
await setup([weather, template]); await setup({template, data: weather});
await app.client.waitForExist(".weather .normal.medium span.wi.dimmed.wi-sunset", 10000); await app.client.waitForExist(".weather .normal.medium span.wi.dimmed.wi-sunset", 10000);
@ -80,7 +73,7 @@ describe.skip("Weather module", function() {
it("should render temperature with icon", async function() { it("should render temperature with icon", async function() {
const weather = generateWeather(); const weather = generateWeather();
await setup([weather, template]); await setup({template, data: weather});
await app.client.waitForExist(".weather .large.light span.wi.weathericon.wi-snow", 10000); await app.client.waitForExist(".weather .large.light span.wi.weathericon.wi-snow", 10000);
@ -89,7 +82,7 @@ describe.skip("Weather module", function() {
it("should render feels like temperature", async function() { it("should render feels like temperature", async function() {
const weather = generateWeather(); const weather = generateWeather();
await setup([weather, template]); await setup({template, data: weather});
return app.client.waitUntilTextExists(".weather .normal.medium span.dimmed", "Feels like -5.6°", 10000); return app.client.waitUntilTextExists(".weather .normal.medium span.dimmed", "Feels like -5.6°", 10000);
}); });
@ -102,14 +95,14 @@ describe.skip("Weather module", function() {
it("should render useBeaufort = false", async function() { it("should render useBeaufort = false", async function() {
const weather = generateWeather(); const weather = generateWeather();
await setup([weather, template]); await setup({template, data: weather});
return app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "12", 10000); return app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "12", 10000);
}); });
it("should render showWindDirectionAsArrow = true", async function() { it("should render showWindDirectionAsArrow = true", async function() {
const weather = generateWeather(); const weather = generateWeather();
await setup([weather, template]); await setup({template, data: weather});
await app.client.waitForExist(".weather .normal.medium sup i.fa-long-arrow-up", 10000); await app.client.waitForExist(".weather .normal.medium sup i.fa-long-arrow-up", 10000);
const element = await app.client.getHTML(".weather .normal.medium sup i.fa-long-arrow-up"); const element = await app.client.getHTML(".weather .normal.medium sup i.fa-long-arrow-up");
@ -119,7 +112,7 @@ describe.skip("Weather module", function() {
it("should render showHumidity = true", async function() { it("should render showHumidity = true", async function() {
const weather = generateWeather(); const weather = generateWeather();
await setup([weather, template]); await setup({template, data: weather});
await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(3)", "93", 10000); await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(3)", "93", 10000);
return app.client.waitForExist(".weather .normal.medium sup i.wi-humidity", 10000); return app.client.waitForExist(".weather .normal.medium sup i.wi-humidity", 10000);
@ -127,7 +120,7 @@ describe.skip("Weather module", function() {
it("should render degreeLabel = true", async function() { it("should render degreeLabel = true", async function() {
const weather = generateWeather(); const weather = generateWeather();
await setup([weather, template]); await setup({template, data: weather});
await app.client.waitUntilTextExists(".weather .large.light span.bright", "1°C", 10000); await app.client.waitUntilTextExists(".weather .large.light span.bright", "1°C", 10000);
@ -151,7 +144,7 @@ describe.skip("Weather module", function() {
speed: 11.8 * 2.23694 speed: 11.8 * 2.23694
}, },
}); });
await setup([weather, template]); await setup({template, data: weather});
await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "6 WSW", 10000); await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "6 WSW", 10000);
await app.client.waitUntilTextExists(".weather .large.light span.bright", "34,7°", 10000); await app.client.waitUntilTextExists(".weather .large.light span.bright", "34,7°", 10000);
@ -169,7 +162,7 @@ describe.skip("Weather module", function() {
speed: 11.8 * 2.23694 speed: 11.8 * 2.23694
}, },
}); });
await setup([weather, template]); await setup({template, data: weather});
await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(3)", "93,7", 10000); await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(3)", "93,7", 10000);
await app.client.waitUntilTextExists(".weather .large.light span.bright", "34,7°", 10000); await app.client.waitUntilTextExists(".weather .large.light span.bright", "34,7°", 10000);
@ -192,7 +185,7 @@ describe.skip("Weather module", function() {
it("should render days", async function() { it("should render days", async function() {
const weather = generateWeatherForecast(); const weather = generateWeatherForecast();
await setup([weather, template]); await setup({template, data: weather});
const days = ["Fri", "Sat", "Sun", "Mon", "Tue"]; const days = ["Fri", "Sat", "Sun", "Mon", "Tue"];
@ -203,7 +196,7 @@ describe.skip("Weather module", function() {
it("should render icons", async function() { it("should render icons", async function() {
const weather = generateWeatherForecast(); const weather = generateWeatherForecast();
await setup([weather, template]); await setup({template, data: weather});
const icons = ["day-cloudy", "rain", "day-sunny", "day-sunny", "day-sunny"]; const icons = ["day-cloudy", "rain", "day-sunny", "day-sunny", "day-sunny"];
@ -214,7 +207,7 @@ describe.skip("Weather module", function() {
it("should render max temperatures", async function() { it("should render max temperatures", async function() {
const weather = generateWeatherForecast(); const weather = generateWeatherForecast();
await setup([weather, template]); await setup({template, data: weather});
const temperatures = ["24.4°", "21.0°", "22.9°", "23.4°", "20.6°"]; const temperatures = ["24.4°", "21.0°", "22.9°", "23.4°", "20.6°"];
@ -225,7 +218,7 @@ describe.skip("Weather module", function() {
it("should render min temperatures", async function() { it("should render min temperatures", async function() {
const weather = generateWeatherForecast(); const weather = generateWeatherForecast();
await setup([weather, template]); await setup({template, data: weather});
const temperatures = ["15.3°", "13.6°", "13.8°", "13.9°", "10.9°"]; const temperatures = ["15.3°", "13.6°", "13.8°", "13.9°", "10.9°"];
@ -236,7 +229,7 @@ describe.skip("Weather module", function() {
it("should render fading of rows", async function() { it("should render fading of rows", async function() {
const weather = generateWeatherForecast(); const weather = generateWeatherForecast();
await setup([weather, template]); await setup({template, data: weather});
const opacities = [1, 1, 0.8, 0.5333333333333333, 0.2666666666666667]; const opacities = [1, 1, 0.8, 0.5333333333333333, 0.2666666666666667];
@ -256,14 +249,14 @@ describe.skip("Weather module", function() {
it("should render custom table class", async function() { it("should render custom table class", async function() {
const weather = generateWeatherForecast(); const weather = generateWeatherForecast();
await setup([weather, template]); await setup({template, data: weather});
await app.client.waitForExist(".weather table.myTableClass", 10000); await app.client.waitForExist(".weather table.myTableClass", 10000);
}); });
it("should render colored rows", async function() { it("should render colored rows", async function() {
const weather = generateWeatherForecast(); const weather = generateWeatherForecast();
await setup([weather, template]); await setup({template, data: weather});
await app.client.waitForExist(".weather table.myTableClass", 10000); await app.client.waitForExist(".weather table.myTableClass", 10000);

View File

@ -3,7 +3,7 @@ function plugin (wdInstance, requests) {
throw new Error("You can't use WebdriverAjaxStub with this version of WebdriverIO"); throw new Error("You can't use WebdriverAjaxStub with this version of WebdriverIO");
} }
function stub(requests, done) { function stub({template, data}, done) {
window.XMLHttpRequest = function () { window.XMLHttpRequest = function () {
this.open = function (method, url) { this.open = function (method, url) {
this.method = method; this.method = method;
@ -13,7 +13,7 @@ function plugin (wdInstance, requests) {
this.send = function () { this.send = function () {
this.status = 200; this.status = 200;
this.readyState = 4; this.readyState = 4;
const response = requests.shift() || []; const response = this.url.includes(".njk") ? template : data;
this.response = response; this.response = response;
this.responseText = response; this.responseText = response;
this.onreadystatechange(); this.onreadystatechange();

View File

@ -1,3 +1,4 @@
/* eslint no-multi-spaces: 0 */
var expect = require("chai").expect; var expect = require("chai").expect;
describe("Functions module currentweather", function() { describe("Functions module currentweather", function() {

View File

@ -1,3 +1,4 @@
/* eslint no-multi-spaces: 0 */
var expect = require("chai").expect; var expect = require("chai").expect;
describe("Functions module weatherforecast", function() { describe("Functions module weatherforecast", function() {

View File

@ -31,5 +31,5 @@
"UPDATE_INFO_SINGLE": "Instalirana verzija {COMMIT_COUNT} commit kasni za branch-om {BRANCH_NAME}.", "UPDATE_INFO_SINGLE": "Instalirana verzija {COMMIT_COUNT} commit kasni za branch-om {BRANCH_NAME}.",
"UPDATE_INFO_MULTIPLE": "Instalirana verzija {COMMIT_COUNT} commit-ova kasni za branch-om {BRANCH_NAME}.", "UPDATE_INFO_MULTIPLE": "Instalirana verzija {COMMIT_COUNT} commit-ova kasni za branch-om {BRANCH_NAME}.",
"FEELS": "Osjeća" "FEELS": "Osjećaj"
} }

View File

@ -26,5 +26,8 @@
"UPDATE_NOTIFICATION": "Nova atualização para MagicMirror disponível.", "UPDATE_NOTIFICATION": "Nova atualização para MagicMirror disponível.",
"UPDATE_NOTIFICATION_MODULE": "Atualização para o módulo {MODULE_NAME} disponível.", "UPDATE_NOTIFICATION_MODULE": "Atualização para o módulo {MODULE_NAME} disponível.",
"UPDATE_INFO_SINGLE": "Sua versão atual é a {COMMIT_COUNT} commit dentro do seguinte branch {BRANCH_NAME}.", "UPDATE_INFO_SINGLE": "Sua versão atual é a {COMMIT_COUNT} commit dentro do seguinte branch {BRANCH_NAME}.",
"UPDATE_INFO_MULTIPLE": "Sua versão atual é a {COMMIT_COUNT} commits dentro do seguinte branch {BRANCH_NAME}." "UPDATE_INFO_MULTIPLE": "Sua versão atual é a {COMMIT_COUNT} commits dentro do seguinte branch {BRANCH_NAME}.",
"FEELS": "Percebida",
"PRECIP": "PoP"
} }

View File

@ -3,9 +3,12 @@
"TODAY": "Bugün", "TODAY": "Bugün",
"TOMORROW": "Yarın", "TOMORROW": "Yarın",
"DAYAFTERTOMORROW": "İki gün içinde",
"RUNNING": "Biten", "RUNNING": "Biten",
"EMPTY": "Yakında etkinlik yok.", "EMPTY": "Yakında etkinlik yok.",
"WEEK": "Hafta {weekNumber}",
"N": "K", "N": "K",
"NNE": "KKD", "NNE": "KKD",
"NE": "KD", "NE": "KD",
@ -21,5 +24,14 @@
"W": "B", "W": "B",
"WNW": "BKB", "WNW": "BKB",
"NW": "KB", "NW": "KB",
"NNW": "KKB" "NNW": "KKB",
"UPDATE_NOTIFICATION": "MagicMirror² güncellemesi mevcut.",
"UPDATE_NOTIFICATION_MODULE": "{MODULE_NAME} modulü için güncelleme mevcut.",
"UPDATE_INFO_SINGLE": "Sahip olduğunuz kurulum {BRANCH_NAME} branchinden {COMMIT_COUNT} commit geridedir.",
"UPDATE_INFO_MULTIPLE": "Sahip olduğunuz kurulum {BRANCH_NAME} branchinden {COMMIT_COUNT} commit geridedir.",
"FEELS": "Hissedilen",
"PRECIP": "Yağış"
} }

25
vendor/package-lock.json generated vendored
View File

@ -703,6 +703,7 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
"integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
"optional": true,
"requires": { "requires": {
"is-glob": "^2.0.0" "is-glob": "^2.0.0"
} }
@ -716,7 +717,8 @@
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"optional": true
}, },
"invert-kv": { "invert-kv": {
"version": "1.0.0", "version": "1.0.0",
@ -735,7 +737,8 @@
"is-buffer": { "is-buffer": {
"version": "1.1.5", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
"integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=" "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=",
"optional": true
}, },
"is-dotfile": { "is-dotfile": {
"version": "1.0.3", "version": "1.0.3",
@ -761,7 +764,8 @@
"is-extglob": { "is-extglob": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
"optional": true
}, },
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "1.0.0", "version": "1.0.0",
@ -775,6 +779,7 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
"integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
"optional": true,
"requires": { "requires": {
"is-extglob": "^1.0.0" "is-extglob": "^1.0.0"
} }
@ -803,7 +808,8 @@
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"optional": true
}, },
"isobject": { "isobject": {
"version": "2.1.0", "version": "2.1.0",
@ -818,6 +824,7 @@
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"optional": true,
"requires": { "requires": {
"is-buffer": "^1.1.5" "is-buffer": "^1.1.5"
} }
@ -883,6 +890,7 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
"optional": true,
"requires": { "requires": {
"remove-trailing-separator": "^1.0.1" "remove-trailing-separator": "^1.0.1"
} }
@ -1031,12 +1039,14 @@
"remove-trailing-separator": { "remove-trailing-separator": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
"optional": true
}, },
"repeat-element": { "repeat-element": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
"integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
"optional": true
}, },
"repeat-string": { "repeat-string": {
"version": "1.6.1", "version": "1.6.1",
@ -1047,7 +1057,8 @@
"safe-buffer": { "safe-buffer": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"optional": true
}, },
"set-immediate-shim": { "set-immediate-shim": {
"version": "1.0.1", "version": "1.0.1",