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",
"camelcase": ["error", {"properties": "never"}],
"no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 1 }],
"no-multi-spaces": "error",
"no-trailing-spaces": ["error", {"ignoreComments": false }],
"no-irregular-whitespace": ["error"]
},

View File

@ -1,22 +1,24 @@
dist: trusty
language: node_js
node_js:
- "10"
- 10
- lts/*
- node
before_install:
- npm i -g npm
before_script:
- yarn danger ci
- npm install grunt-cli -g
- "export DISPLAY=:99.0"
- "export ELECTRON_DISABLE_SANDBOX=1"
- "sh -e /etc/init.d/xvfb start"
- sleep 5
- yarn danger ci
- npm install grunt-cli -g
- "export DISPLAY=:99.0"
- "export ELECTRON_DISABLE_SANDBOX=1"
- "sh -e /etc/init.d/xvfb start"
- sleep 5
script:
- npm run test:e2e
- npm run test:unit
- grunt
- npm run test:lint
- npm run test:e2e
- npm run test:unit
after_script:
- npm list
- npm list
cache:
directories:
- node_modules
directories:
- node_modules

View File

@ -12,19 +12,29 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Deleted
- Remove installers.
- Remove externalized scripts.
- Remove jshint dependency, instead eslint checks your config file now
### Added
- Brazilian translation for "FEELS".
- Ukrainian translation.
- 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.
- The `clock` module now optionally displays sun and moon data, including rise/set times, remaining daylight, and percent of moon illumination.
- 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
- 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)
- 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 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
- 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 in server only mode, use `npm run server`.
- 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
@ -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 `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 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
- Calendar module: option to hide private events
- Add root_path for global vars

View File

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

View File

@ -1,5 +1,3 @@
/* jshint esversion: 6 */
"use strict";
// 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 :
// ["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",
timeFormat: 24,
units: "metric",
@ -63,7 +67,7 @@ var config = {
position: "top_right",
config: {
location: "New York",
locationID: "", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city
locationID: "", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city
appid: "YOUR_OPENWEATHER_API_KEY"
}
},
@ -73,7 +77,7 @@ var config = {
header: "Weather Forecast",
config: {
location: "New York",
locationID: "5128581", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city
locationID: "5128581", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city
appid: "YOUR_OPENWEATHER_API_KEY"
}
},

View File

@ -37,7 +37,7 @@
<div class="region bottom right"><div class="container"></div></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="js/defaults.js"></script>
<script type="text/javascript" src="#CONFIG_FILE#"></script>

View File

@ -14,7 +14,7 @@ var path = require("path");
require("module-alias/register");
// 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.
global.version = JSON.parse(fs.readFileSync("package.json", "utf8")).version;
@ -111,10 +111,10 @@ var App = function() {
var elements = module.split("/");
var moduleName = elements[elements.length - 1];
var moduleFolder = __dirname + "/../modules/" + module;
var moduleFolder = __dirname + "/../modules/" + module;
if (defaultModules.indexOf(moduleName) !== -1) {
moduleFolder = __dirname + "/../modules/default/" + module;
moduleFolder = __dirname + "/../modules/default/" + module;
}
var helperPath = moduleFolder + "/node_helper.js";
@ -262,7 +262,7 @@ var App = function() {
*/
process.on("SIGINT", () => {
console.log("[SIGINT] Received. Shutting down server...");
setTimeout(() => { process.exit(0); }, 3000); // Force quit after 3 seconds
setTimeout(() => { process.exit(0); }, 3000); // Force quit after 3 seconds
this.stop();
process.exit(0);
});
@ -271,7 +271,7 @@ var App = function() {
*/
process.on("SIGTERM", () => {
console.log("[SIGTERM] Received. Shutting down server...");
setTimeout(() => { process.exit(0); }, 3000); // Force quit after 3 seconds
setTimeout(() => { process.exit(0); }, 3000); // Force quit after 3 seconds
this.stop();
process.exit(0);
});

View File

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

View File

@ -83,10 +83,10 @@ var Loader = (function() {
var elements = module.split("/");
var moduleName = elements[elements.length - 1];
var moduleFolder = config.paths.modules + "/" + module;
var moduleFolder = config.paths.modules + "/" + module;
if (defaultModules.indexOf(moduleName) !== -1) {
moduleFolder = config.paths.modules + "/default/" + module;
moduleFolder = config.paths.modules + "/default/" + module;
}
if (moduleData.disabled === true) {
@ -172,7 +172,7 @@ var Loader = (function() {
*/
var loadFile = function(fileName, callback) {
var extension = fileName.slice((Math.max(0, fileName.lastIndexOf(".")) || Infinity) + 1);
var extension = fileName.slice((Math.max(0, fileName.lastIndexOf(".")) || Infinity) + 1);
switch (extension.toLowerCase()) {
case "js":

View File

@ -1,5 +1,4 @@
/* global Log, Loader, Module, config, defaults */
/* jshint -W020, esversion: 6 */
/* Magic Mirror
* Main System
@ -306,7 +305,9 @@ var MM = (function() {
module.showHideTimer = setTimeout(function() {
if (typeof callback === "function") { callback(); }
}, speed);
} else {
// invoke callback
if (typeof callback === "function") { callback(); }
}
};
@ -377,7 +378,7 @@ var MM = (function() {
*
* return array - Filtered collection of modules.
*/
var exceptWithClass = function(className) {
var exceptWithClass = function(className) {
return modulesByClass(className, false);
};
@ -439,10 +440,10 @@ var MM = (function() {
});
};
if (typeof modules.withClass === "undefined") { Object.defineProperty(modules, "withClass", {value: withClass, enumerable: false}); }
if (typeof modules.exceptWithClass === "undefined") { Object.defineProperty(modules, "exceptWithClass", {value: exceptWithClass, enumerable: false}); }
if (typeof modules.exceptModule === "undefined") { Object.defineProperty(modules, "exceptModule", {value: exceptModule, enumerable: false}); }
if (typeof modules.enumerate === "undefined") { Object.defineProperty(modules, "enumerate", {value: enumerate, enumerable: false}); }
if (typeof modules.withClass === "undefined") { Object.defineProperty(modules, "withClass", {value: withClass, enumerable: false}); }
if (typeof modules.exceptWithClass === "undefined") { Object.defineProperty(modules, "exceptWithClass", {value: exceptWithClass, enumerable: false}); }
if (typeof modules.exceptModule === "undefined") { Object.defineProperty(modules, "exceptModule", {value: exceptModule, enumerable: false}); }
if (typeof modules.enumerate === "undefined") { Object.defineProperty(modules, "enumerate", {value: enumerate, enumerable: false}); }
};
return {

View File

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

View File

@ -101,9 +101,9 @@ NodeHelper = Class.extend({
var onevent = socket.onevent;
socket.onevent = function(packet) {
var args = packet.data || [];
onevent.call(this, packet); // original call
onevent.call(this, packet); // original call
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.

View File

@ -7,8 +7,6 @@
var express = require("express");
var app = require("express")();
var server = require("http").Server(app);
var io = require("socket.io")(server);
var path = require("path");
var ipfilter = require("express-ipfilter").IpFilter;
var fs = require("fs");
@ -22,6 +20,18 @@ var Server = function(config, callback) {
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 + " ... ");
server.listen(port, config.address ? config.address : "localhost");

View File

@ -8,15 +8,17 @@ var MMSocket = function(moduleName) {
self.moduleName = moduleName;
// Private Methods
self.socket = io("/" + self.moduleName);
self.socket = io("/" + self.moduleName, {
path: window.location.pathname + "socket.io"
});
var notificationCallback = function() {};
var onevent = self.socket.onevent;
self.socket.onevent = function(packet) {
var args = packet.data || [];
onevent.call(this, packet); // original call
onevent.call(this, packet); // original call
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.

View File

@ -318,7 +318,7 @@ Module.register("calendar", {
}
if(this.config.showEnd){
timeWrapper.innerHTML += "-" ;
timeWrapper.innerHTML += this.capFirst(moment(event.endDate , "x").format(this.config.fullDayEventDateFormat));
timeWrapper.innerHTML += this.capFirst(moment(event.endDate , "x").format(this.config.fullDayEventDateFormat));
}
} else {
if (event.startDate >= new Date()) {

View File

@ -28,7 +28,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
var opts = {
headers: {
"User-Agent": "Mozilla/5.0 (Node.js "+ nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)"
"User-Agent": "Mozilla/5.0 (Node.js "+ nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)"
},
gzip: true
};
@ -249,7 +249,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
showRecurrence = false;
}
endDate = moment(parseInt(startDate.format("x")) + duration, "x");
endDate = moment(parseInt(startDate.format("x")) + duration, "x");
if (startDate.format("x") == endDate.format("x")) {
endDate = endDate.endOf("day");
}

View File

@ -8,7 +8,7 @@
var CalendarFetcher = require("./calendarfetcher.js");
var url = "https://calendar.google.com/calendar/ical/pkm1t2uedjbp0uvq1o7oj1jouo%40group.calendar.google.com/private-08ba559f89eec70dd74bbd887d0a3598/basic.ics"; // Standard test URL
var url = "https://calendar.google.com/calendar/ical/pkm1t2uedjbp0uvq1o7oj1jouo%40group.calendar.google.com/private-08ba559f89eec70dd74bbd887d0a3598/basic.ics"; // Standard test URL
// var url = "https://www.googleapis.com/calendar/v3/calendars/primary/events/"; // URL for Bearer auth (must be configured in Google OAuth2 first)
var fetchInterval = 60 * 60 * 1000;
var maximumEntries = 10;

View File

@ -152,9 +152,9 @@ Module.register("clock",{
}
function formatTime(config, time) {
var formatString = hourSymbol + ':mm';
var formatString = hourSymbol + ":mm";
if (config.showPeriod && config.timeFormat !== 24) {
formatString += config.showPeriodUpper ? 'A' : 'a';
formatString += config.showPeriodUpper ? "A" : "a";
}
return moment(time).format(formatString);
}
@ -167,23 +167,31 @@ Module.register("clock",{
} else if (now.isBefore(sunTimes.sunset)) {
nextEvent = sunTimes.sunset;
} 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;
}
const untilNextEvent = moment.duration(moment(nextEvent).diff(now));
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>' +
'<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>';
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>" +
"<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>";
}
if (this.config.showMoonTimes) {
const moonIllumination = SunCalc.getMoonIllumination(now.toDate());
const moonTimes = SunCalc.getMoonTimes(now, this.config.lat, this.config.lon);
const isVisible = now.isBetween(moonTimes.rise, moonTimes.set);
const illuminatedFractionString = moonIllumination.fraction.toLocaleString(undefined, {style: 'percent'});
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> ' + formatTime(this.config, moonTimes.rise) + '</span>'+
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i> ' + formatTime(this.config, moonTimes.set) + '</span>';
const moonRise = moonTimes.rise;
var moonSet;
if (moment(moonTimes.set).isAfter(moonTimes.rise)) {
moonSet = moonTimes.set;
} 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,
calendarClass: "calendar",
tableClass: "large",
onlyTemp: false,
hideTemp: false,
@ -175,6 +176,7 @@ Module.register("currentweather",{
// Override dom generator.
getDom: function() {
var wrapper = document.createElement("div");
wrapper.className = this.config.tableClass;
if (this.config.appid === "") {
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");
large.className = "large light";
large.className = "light";
var degreeLabel = "";
if (this.config.units === "metric" || this.config.units === "imperial") {
@ -217,7 +219,7 @@ Module.register("currentweather",{
this.config.decimalSymbol = ".";
}
if (this.config.hideTemp === true) {
if (this.config.hideTemp === false) {
var weatherIcon = document.createElement("span");
weatherIcon.className = "wi weathericon " + this.weatherType;
large.appendChild(weatherIcon);

View File

@ -213,7 +213,7 @@ Module.register("newsfeed",{
},
getActiveItemURL: function() {
return typeof this.newsItems[this.activeItem].url === "string" ? this.newsItems[this.activeItem].url : this.newsItems[this.activeItem].url.href;
return typeof this.newsItems[this.activeItem].url === "string" ? this.newsItems[this.activeItem].url : this.newsItems[this.activeItem].url.href;
},
/* registerFeeds()

View File

@ -18,37 +18,30 @@ module.exports = NodeHelper.create({
configureModules: function(modules) {
// 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 + "/../../../"))});
for (moduleName in modules) {
if (defaultModules.indexOf(moduleName) < 0) {
// Default modules are included in the main MagicMirror repo
var moduleFolder = path.normalize(__dirname + "/../../" + moduleName);
var promises = [];
for (moduleName in modules) {
if (!this.ignoreUpdateChecking(moduleName)) {
// Default modules are included in the main MagicMirror repo
var moduleFolder = path.normalize(__dirname + "/../../" + moduleName);
var stat;
try {
//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) {
// Error when directory .git doesn't exist
// This module is not managed with git, skip
continue;
}
var res = function(mn, mf) {
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);
}
}
return Promise.all(promises);
},
socketNotificationReceived: function (notification, payload) {
@ -56,21 +49,35 @@ module.exports = NodeHelper.create({
this.config = payload;
} else if(notification === "MODULES") {
// if this is the 1st time thru the update check process
if(this.updateProcessStarted==false ){
this.updateProcessStarted=true;
this.configureModules(payload);
this.preformFetch();
if (!this.updateProcessStarted) {
this.updateProcessStarted = true;
this.configureModules(payload).then(() => this.performFetch());
}
}
},
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;
simpleGits.forEach(function(sg) {
sg.git.fetch().status(function(err, data) {
simpleGits.forEach((sg) => {
sg.git.fetch().status((err, data) => {
data.module = sg.module;
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) {
data.hash = data2.latest.hash;
self.sendSocketNotification("STATUS", data);
@ -91,8 +98,23 @@ module.exports = NodeHelper.create({
var self = this;
clearTimeout(this.updateTimer);
this.updateTimer = setTimeout(function() {
self.preformFetch();
self.performFetch();
}, 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: {
updateInterval: 10 * 60 * 1000, // every 10 minutes
refreshInterval: 24 * 60 * 60 * 1000, // one day
ignoreModules: []
},
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: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:lint": "grunt --env=test",
"config:check": "node tests/configs/check_config.js",
"lint": "grunt"
"lint": "grunt --env=lint"
},
"repository": {
"type": "git",
@ -47,7 +48,6 @@
"grunt-yamllint": "latest",
"http-auth": "^3.2.3",
"jsdom": "^11.6.2",
"jshint": "^2.10.2",
"mocha": "^7.0.0",
"mocha-each": "^1.1.0",
"mocha-logger": "^1.0.6",

View File

@ -1,5 +1,6 @@
var app = require("../js/app.js");
app.start(function(config) {
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 fs = require("fs");
var Utils = require(__dirname + "/../../js/utils.js");
@ -50,16 +53,15 @@ function checkConfigFile() {
// I'm not sure if all ever is utf-8
fs.readFile(configFileName, "utf-8", function (err, data) {
if (err) { throw err; }
v.JSHINT(data); // Parser by jshint
if (v.JSHINT.errors.length === 0) {
const messages = linter.verify(data, config);
if (messages.length === 0) {
console.log("Your configuration file doesn't contain syntax errors :)");
return true;
} else {
errors = v.JSHINT.data().errors;
errors = messages;
for (var idx in errors) {
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 wait = () => new Promise(res => setTimeout(res, 3000));
// 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() {
describe("Weather module", function() {
let app;
@ -49,7 +42,7 @@ describe.skip("Weather module", function() {
it("should render wind speed and wind direction", async function() {
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);
});
@ -59,7 +52,7 @@ describe.skip("Weather module", function() {
const sunset = moment().startOf("day").unix();
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);
@ -71,7 +64,7 @@ describe.skip("Weather module", function() {
const sunset = moment().endOf("day").unix();
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);
@ -80,7 +73,7 @@ describe.skip("Weather module", function() {
it("should render temperature with icon", async function() {
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);
@ -89,7 +82,7 @@ describe.skip("Weather module", function() {
it("should render feels like temperature", async function() {
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);
});
@ -102,14 +95,14 @@ describe.skip("Weather module", function() {
it("should render useBeaufort = false", async function() {
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);
});
it("should render showWindDirectionAsArrow = true", async function() {
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);
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() {
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);
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() {
const weather = generateWeather();
await setup([weather, template]);
await setup({template, data: weather});
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
},
});
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 .large.light span.bright", "34,7°", 10000);
@ -169,7 +162,7 @@ describe.skip("Weather module", function() {
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 .large.light span.bright", "34,7°", 10000);
@ -192,7 +185,7 @@ describe.skip("Weather module", function() {
it("should render days", async function() {
const weather = generateWeatherForecast();
await setup([weather, template]);
await setup({template, data: weather});
const days = ["Fri", "Sat", "Sun", "Mon", "Tue"];
@ -203,7 +196,7 @@ describe.skip("Weather module", function() {
it("should render icons", async function() {
const weather = generateWeatherForecast();
await setup([weather, template]);
await setup({template, data: weather});
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() {
const weather = generateWeatherForecast();
await setup([weather, template]);
await setup({template, data: weather});
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() {
const weather = generateWeatherForecast();
await setup([weather, template]);
await setup({template, data: weather});
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() {
const weather = generateWeatherForecast();
await setup([weather, template]);
await setup({template, data: weather});
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() {
const weather = generateWeatherForecast();
await setup([weather, template]);
await setup({template, data: weather});
await app.client.waitForExist(".weather table.myTableClass", 10000);
});
it("should render colored rows", async function() {
const weather = generateWeatherForecast();
await setup([weather, template]);
await setup({template, data: weather});
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");
}
function stub(requests, done) {
function stub({template, data}, done) {
window.XMLHttpRequest = function () {
this.open = function (method, url) {
this.method = method;
@ -13,7 +13,7 @@ function plugin (wdInstance, requests) {
this.send = function () {
this.status = 200;
this.readyState = 4;
const response = requests.shift() || [];
const response = this.url.includes(".njk") ? template : data;
this.response = response;
this.responseText = response;
this.onreadystatechange();

View File

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

View File

@ -1,3 +1,4 @@
/* eslint no-multi-spaces: 0 */
var expect = require("chai").expect;
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_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_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_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,8 +3,11 @@
"TODAY": "Bugün",
"TOMORROW": "Yarın",
"DAYAFTERTOMORROW": "İki gün içinde",
"RUNNING": "Biten",
"EMPTY": "Yakında etkinlik yok.",
"WEEK": "Hafta {weekNumber}",
"N": "K",
"NNE": "KKD",
@ -21,5 +24,14 @@
"W": "B",
"WNW": "BKB",
"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",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
"integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
"optional": true,
"requires": {
"is-glob": "^2.0.0"
}
@ -716,7 +717,8 @@
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"optional": true
},
"invert-kv": {
"version": "1.0.0",
@ -735,7 +737,8 @@
"is-buffer": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
"integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw="
"integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=",
"optional": true
},
"is-dotfile": {
"version": "1.0.3",
@ -761,7 +764,8 @@
"is-extglob": {
"version": "1.0.0",
"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": {
"version": "1.0.0",
@ -775,6 +779,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
"integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
"optional": true,
"requires": {
"is-extglob": "^1.0.0"
}
@ -803,7 +808,8 @@
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"optional": true
},
"isobject": {
"version": "2.1.0",
@ -818,6 +824,7 @@
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"optional": true,
"requires": {
"is-buffer": "^1.1.5"
}
@ -883,6 +890,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
"optional": true,
"requires": {
"remove-trailing-separator": "^1.0.1"
}
@ -1031,12 +1039,14 @@
"remove-trailing-separator": {
"version": "1.1.0",
"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": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
"integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo="
"integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
"optional": true
},
"repeat-string": {
"version": "1.6.1",
@ -1047,7 +1057,8 @@
"safe-buffer": {
"version": "5.1.1",
"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": {
"version": "1.0.1",