diff --git a/.gitignore b/.gitignore index b8935c9317..e130a22e8a 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,7 @@ Temporary Items # Ignore all modules except the default modules. /modules/** +!/modules/default !/modules/default/** !/modules/README.md** diff --git a/.travis.yml b/.travis.yml index f2d343426e..da3fb0995f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,4 +4,9 @@ node_js: - "5.1" before_script: - npm install grunt-cli -g -script: grunt +script: +- grunt +- npm test +cache: + directories: + - node_modules diff --git a/CHANGELOG.md b/CHANGELOG.md index bf8e9c7690..ea196b917e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,52 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [2.1.1] - 2017-01-31 + +## [2.1.1] - Unreleased + +**Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install` + +### Changed +- Installer: Use init config.js from config.js.sample. +- Switched out `rrule` package for `rrule-alt` and fixes in `ical.js` in order to fix calendar issues. ([#565](https://github.com/MichMich/MagicMirror/issues/565)) +- Make mouse events pass through the region fullscreen_above to modules below. +- Scaled the splash screen down to make it a bit more subtle. +- Replace HTML tables with markdown tables in README files. +- Added `DAYAFTERTOMORROW`, `UPDATE_NOTIFICATION` and `UPDATE_NOTIFICATION_MODULE` to Finnish translations. +- Run `npm test` on Travis automatically +- Show the splash screen image even when is reboot or halted. +- Added some missing translaton strings in the sv.json file. +- Run task jsonlint to check translation files. ### Added - Added Docker support (Pull Request [#673](https://github.com/MichMich/MagicMirror/pull/673)) +- Calendar-specific support for `maximumEntries`, and ` maximumNumberOfDays` +- Add loaded function to modules, providing an async callback. +- Made default newsfeed module aware of gesture events from [MMM-Gestures](https://github.com/thobach/MMM-Gestures) +- Add use pm2 for manager process into Installer RaspberryPi script +- Russian Translation +- Afrikaans Translation +- Add postinstall script to notify user that MagicMirror installed successfully despite warnings from NPM. +- Init tests using mocha. +- Option to use RegExp in Calendar's titleReplace. +- Hungarian Translation. +- Icelandic Translation. +- Add use a script to prevent when is run by SSH session set DISPLAY enviroment. +- Enable ability to set configuration file by the enviroment variable called MM_CONFIG_FILE. +- Option to give each calendar a different color +- Option for colored min-temp and max-temp +- Add test e2e helloworld +- Add test e2e enviroment +- Add `chai-as-promised` npm module to devDependencies + +### Fixed +- Update .gitignore to not ignore default modules folder. +- Remove white flash on boot up. +- Added `update` in Raspberry Pi installation script. +- Fix an issue where the analog clock looked scrambled. ([#611](https://github.com/MichMich/MagicMirror/issues/611)) +- If units is set to imperial, the showRainAmount option of weatherforecast will show the correct unit. +- Module currentWeather: check if temperature received from api is defined. +- Fix an issue with module hidden status changing to `true` although lock string prevented showing it ## [2.1.0] - 2016-12-31 @@ -123,7 +165,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Added reference to Italian Translation. -- Added the missing NE translation to all languages. [#334](https://github.com/MichMich/MagicMirror/issues/344) +- Added the missing NE translation to all languages. [#344](https://github.com/MichMich/MagicMirror/issues/344) - Added proper User-Agent string to calendar call. ### Changed diff --git a/Gruntfile.js b/Gruntfile.js index ec3128efad..5aa6881147 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -7,8 +7,11 @@ module.exports = function(grunt) { configFile: ".eslintrc.json" }, target: ["js/*.js", "modules/default/*.js", "modules/default/*/*.js", - "serveronly/*.js", "*.js", "!modules/default/alert/notificationFx.js", - "!modules/default/alert/modernizr.custom.js", "!modules/default/alert/classie.js" + "serveronly/*.js", "*.js", "tests/*/*.js", "!modules/default/alert/notificationFx.js", + "!modules/default/alert/modernizr.custom.js", "!modules/default/alert/classie.js", + "config/*", + "translations/translations.js" + ] }, stylelint: { @@ -21,7 +24,7 @@ module.exports = function(grunt) { }, jsonlint: { main: { - src: ["package.json", ".eslintrc.json", ".stylelint"], + src: ["package.json", ".eslintrc.json", ".stylelintrc", "translations/*.json", "modules/default/*/translations/*.json"], options: { reporter: "jshint" } diff --git a/LICENSE.md b/LICENSE.md index d4765af275..09ac7e6fe7 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,7 +1,7 @@ The MIT License (MIT) ===================== -Copyright © 2016 Michael Teeuw +Copyright © 2016-2017 Michael Teeuw Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/README.md b/README.md index 3320e8c68f..a94a7db526 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ MagicMirror² focuses on a modular plugin system and uses [Electron](http://elec - [Configuration](#configuration) - [Modules](#modules) - [Known Issues](#known-issues) -- [community](#community) +- [Community](#community) - [Contributing Guidelines](#contributing-guidelines) ## Usage @@ -101,7 +101,7 @@ Type `git status` to see your changes, if there are any, you can reset them with ## Configuration -1. Duplicate `config/config.js.sample` to `config/config.js`. +1. Duplicate `config/config.js.sample` to `config/config.js`. **Note:** If you used the installer script. This step is already done for you. 2. Modify your required settings. The following properties can be configured: @@ -116,7 +116,7 @@ The following properties can be configured: | `timeFormat` | The form of time notation that will be used. Possible values are `12` or `24`. The default is `24`. | | `units` | The units that will be used in the default weather modules. Possible values are `metric` or `imperial`. The default is `metric`. | | `modules` | An array of active modules. **The array must contain objects. See the next table below for more information.** | -| `electronOptions` | An optional array of Electron (browser) options. This allows configuration of e.g. the browser screen size and position (defaults `.width = 800` & `.height = 600`). Kiosk mode can be enabled by setting `.kiosk = true`, `.autoHideMenuBar = false`, `.fullscreen = false`. More options can be found [here](https://github.com/electron/electron/blob/master/docs/api/browser-window.md). | +| `electronOptions` | An optional array of Electron (browser) options. This allows configuration of e.g. the browser screen size and position (example: `electronOptions: { fullscreen: false, width: 800, height: 600 }`). Kiosk mode can be enabled by setting `.kiosk = true`, `.autoHideMenuBar = false`, `.fullscreen = false`. More options can be found [here](https://github.com/electron/electron/blob/master/docs/api/browser-window.md). | Module configuration: diff --git a/config/config.js.sample b/config/config.js.sample index 913dbc439b..eab2297205 100644 --- a/config/config.js.sample +++ b/config/config.js.sample @@ -8,61 +8,61 @@ var config = { port: 8080, ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], - language: 'en', + language: "en", timeFormat: 24, - units: 'metric', + units: "metric", modules: [ { - module: 'alert', + module: "alert", }, { module: "updatenotification", position: "top_bar" }, { - module: 'clock', - position: 'top_left' + module: "clock", + position: "top_left" }, { - module: 'calendar', - header: 'US Holidays', - position: 'top_left', + module: "calendar", + header: "US Holidays", + position: "top_left", config: { calendars: [ { - symbol: 'calendar-check-o ', - url: 'webcal://www.calendarlabs.com/templates/ical/US-Holidays.ics' + symbol: "calendar-check-o ", + url: "webcal://www.calendarlabs.com/templates/ical/US-Holidays.ics" } ] } }, { - module: 'compliments', - position: 'lower_third' + module: "compliments", + position: "lower_third" }, { - module: 'currentweather', - position: 'top_right', + module: "currentweather", + position: "top_right", config: { - location: 'New York', - locationID: '', //ID from http://www.openweathermap.org - appid: 'YOUR_OPENWEATHER_API_KEY' + location: "New York", + locationID: "", //ID from http://www.openweathermap.org + appid: "YOUR_OPENWEATHER_API_KEY" } }, { - module: 'weatherforecast', - position: 'top_right', - header: 'Weather Forecast', + module: "weatherforecast", + position: "top_right", + header: "Weather Forecast", config: { - location: 'New York', - locationID: '5128581', //ID from http://www.openweathermap.org - appid: 'YOUR_OPENWEATHER_API_KEY' + location: "New York", + locationID: "5128581", //ID from http://www.openweathermap.org + appid: "YOUR_OPENWEATHER_API_KEY" } }, { - module: 'newsfeed', - position: 'bottom_bar', + module: "newsfeed", + position: "bottom_bar", config: { feeds: [ { @@ -79,4 +79,4 @@ var config = { }; /*************** DO NOT EDIT THE LINE BELOW ***************/ -if (typeof module !== 'undefined') {module.exports = config;} +if (typeof module !== "undefined") {module.exports = config;} diff --git a/css/main.css b/css/main.css index 5e63e59630..f1c07fa0aa 100644 --- a/css/main.css +++ b/css/main.css @@ -135,6 +135,11 @@ sup { left: -60px; right: -60px; bottom: -60px; + pointer-events: none; +} + +.region.fullscreen * { + pointer-events: auto; } .region.right { diff --git a/index.html b/index.html index 86a69a4ad5..606f5aac1a 100644 --- a/index.html +++ b/index.html @@ -33,7 +33,7 @@
- + diff --git a/installers/mm.sh b/installers/mm.sh new file mode 100755 index 0000000000..cc6c4bb375 --- /dev/null +++ b/installers/mm.sh @@ -0,0 +1,2 @@ +cd ~/MagicMirror +DISPLAY=:0 npm start diff --git a/installers/pm2_MagicMirror.json b/installers/pm2_MagicMirror.json new file mode 100644 index 0000000000..3f0b27bdb7 --- /dev/null +++ b/installers/pm2_MagicMirror.json @@ -0,0 +1,7 @@ +{ + apps : [{ + name : "MagicMirror", + script : "/home/pi/MagicMirror/installers/mm.sh", + watch : ["/home/pi/MagicMirror/config/config.js"] + }] +} diff --git a/installers/postinstall/postinstall.sh b/installers/postinstall/postinstall.sh new file mode 100644 index 0000000000..dc19df9884 --- /dev/null +++ b/installers/postinstall/postinstall.sh @@ -0,0 +1,2 @@ +echo "\033[32mMagicMirror installation successful!" +exit 0 diff --git a/installers/raspberry.sh b/installers/raspberry.sh index cd995a1e22..718f78b69c 100644 --- a/installers/raspberry.sh +++ b/installers/raspberry.sh @@ -36,6 +36,10 @@ fi function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; } function command_exists () { type "$1" &> /dev/null ;} +# Update before first apt-get +echo -e "\e[96mUpdating packages ...\e[90m" +sudo apt-get update || echo -e "\e[91mUpdate failed, carrying on installation ...\e[90m" + # Installing helper tools echo -e "\e[96mInstalling helper tools ...\e[90m" sudo apt-get install curl wget git build-essential unzip || exit @@ -113,6 +117,9 @@ else exit; fi +# Use sample config for start MagicMirror +cp config/config.js.sample config/config.js + # Check if plymouth is installed (default with PIXEL desktop environment), then install custom splashscreen. echo -e "\e[96mCheck plymouth installation ...\e[0m" if command_exists plymouth; then @@ -141,6 +148,16 @@ else echo -e "\e[93mplymouth is not installed.\e[0m"; fi +# Use pm2 control like a service MagicMirror +read -p "Do you want use pm2 for auto starting of your MagicMirror (y/n)?" choice +if [[ $choice =~ ^[Yy]$ ]] +then + sudo npm install -g pm2 + sudo su -c "env PATH=$PATH:/usr/bin pm2 startup linux -u pi --hp /home/pi" + pm2 start ~/MagicMirror/installers/pm2_MagicMirror.json + pm2 save +fi + echo " " echo -e "\e[92mWe're ready! Run \e[1m\e[97mDISPLAY=:0 npm start\e[0m\e[92m from the ~/MagicMirror directory to start your MagicMirror.\e[0m" echo " " diff --git a/js/app.js b/js/app.js index ba6cd1a9b5..6466fe3f26 100644 --- a/js/app.js +++ b/js/app.js @@ -17,6 +17,10 @@ console.log("Starting MagicMirror: v" + global.version); // global absolute root path global.root_path = path.resolve(__dirname + "/../"); +if (process.env.MM_CONFIG_FILE) { + global.configuration_file = process.env.MM_CONFIG_FILE; +} + // The next part is here to prevent a major exception when there // is no internet connection. This could probable be solved better. process.on("uncaughtException", function (err) { @@ -41,7 +45,14 @@ var App = function() { var loadConfig = function(callback) { console.log("Loading config ..."); var defaults = require(__dirname + "/defaults.js"); + + // For this check proposed to TestSuite + // https://forum.magicmirror.builders/topic/1456/test-suite-for-magicmirror/8 var configFilename = path.resolve(global.root_path + "/config/config.js"); + if (typeof(global.configuration_file) !== "undefined") { + configFilename = path.resolve(global.configuration_file); + } + try { fs.accessSync(configFilename, fs.F_OK); var c = require(configFilename); @@ -66,7 +77,7 @@ var App = function() { * * argument module string - The name of the module (including subpath). */ - var loadModule = function(module) { + var loadModule = function(module, callback) { var elements = module.split("/"); var moduleName = elements[elements.length - 1]; @@ -103,6 +114,10 @@ var App = function() { m.setName(moduleName); m.setPath(path.resolve(moduleFolder)); nodeHelpers.push(m); + + m.loaded(callback); + } else { + callback(); } }; @@ -111,14 +126,24 @@ var App = function() { * * argument module string - The name of the module (including subpath). */ - var loadModules = function(modules) { + var loadModules = function(modules, callback) { console.log("Loading module helpers ..."); - for (var m in modules) { - loadModule(modules[m]); - } + var loadNextModule = function() { + if (modules.length > 0) { + var nextModule = modules[0]; + loadModule(nextModule, function() { + modules = modules.slice(1); + loadNextModule(); + }); + } else { + // All modules are loaded + console.log("All module helpers loaded."); + callback(); + } + }; - console.log("All module helpers loaded."); + loadNextModule(); }; /* cmpVersions(a,b) @@ -164,24 +189,24 @@ var App = function() { } } - loadModules(modules); + loadModules(modules, function() { + var server = new Server(config, function(app, io) { + console.log("Server started ..."); - var server = new Server(config, function(app, io) { - console.log("Server started ..."); + for (var h in nodeHelpers) { + var nodeHelper = nodeHelpers[h]; + nodeHelper.setExpressApp(app); + nodeHelper.setSocketIO(io); + nodeHelper.start(); + } - for (var h in nodeHelpers) { - var nodeHelper = nodeHelpers[h]; - nodeHelper.setExpressApp(app); - nodeHelper.setSocketIO(io); - nodeHelper.start(); - } + console.log("Sockets connected & modules started ..."); - console.log("Sockets connected & modules started ..."); - - if (typeof callback === "function") { - callback(config); - } + if (typeof callback === "function") { + callback(config); + } + }); }); }); }; diff --git a/js/electron.js b/js/electron.js index 173abe6892..1f16092b8e 100644 --- a/js/electron.js +++ b/js/electron.js @@ -28,7 +28,8 @@ function createWindow() { webPreferences: { nodeIntegration: false, zoomFactor: config.zoom - } + }, + backgroundColor: "#000000" } // DEPRECATED: "kioskmode" backwards compatibility, to be removed diff --git a/js/main.js b/js/main.js index 325a7efe24..fde5564d66 100644 --- a/js/main.js +++ b/js/main.js @@ -232,6 +232,8 @@ var MM = (function() { return; } + module.hidden = false; + // If forced show, clean current lockstrings. if (module.lockStrings.length !== 0 && options.force === true) { Log.log("Force show of module: " + module.name); @@ -504,7 +506,7 @@ var MM = (function() { * argument options object - Optional settings for the hide method. */ showModule: function(module, speed, callback, options) { - module.hidden = false; + // do not change module.hidden yet, only if we really show it later showModule(module, speed, callback, options); } }; diff --git a/js/module.js b/js/module.js index 67305a9c1e..dfd5f2bc50 100644 --- a/js/module.js +++ b/js/module.js @@ -415,3 +415,11 @@ Module.register = function (name, moduleDefinition) { Log.log("Module registered: " + name); Module.definitions[name] = moduleDefinition; }; + +if (typeof exports != "undefined") { // For testing purpose only + // A good a idea move the function cmpversions a helper file. + // It's used into other side. + exports._test = { + cmpVersions: cmpVersions + } +} diff --git a/js/server.js b/js/server.js index f1de242053..e7050021d6 100644 --- a/js/server.js +++ b/js/server.js @@ -37,6 +37,7 @@ var Server = function(config, callback) { app.use("/modules", express.static(path.resolve(global.root_path + "/modules"))); app.use("/vendor", express.static(path.resolve(global.root_path + "/vendor"))); app.use("/translations", express.static(path.resolve(global.root_path + "/translations"))); + app.use("/tests/confs", express.static(path.resolve(global.root_path + "/tests/confs"))); app.get("/version", function(req,res) { res.send(global.version); @@ -46,6 +47,12 @@ var Server = function(config, callback) { var html = fs.readFileSync(path.resolve(global.root_path + "/index.html"), {encoding: "utf8"}); html = html.replace("#VERSION#", global.version); + configFile = "config/config.js"; + if (typeof(global.configuration_file) !== "undefined") { + configFile = global.configuration_file; + } + html = html.replace("#CONFIG_FILE#", configFile); + res.send(html); }); diff --git a/modules/README.md b/modules/README.md index 42a31dfa3c..03664b1549 100644 --- a/modules/README.md +++ b/modules/README.md @@ -96,6 +96,21 @@ requiresVersion: "2.1.0", ####`init()` This method is called when a module gets instantiated. In most cases you do not need to subclass this method. +####`loaded(callback)` + +*Introduced in version: 2.1.1.* + +This method is called when a module is loaded. Subsequent modules in the config are not yet loaded. The `callback` function MUST be called when the module is done loading. In most cases you do not need to subclass this method. + +**Example:** +````javascript +loaded: function(callback) { + this.finishLoading(); + Log.log(this.name + ' is loaded!'); + callback(); +} +```` + ####`start()` This method is called when all modules are loaded an the system is ready to boot up. Keep in mind that the dom object for the module is not yet created. The start method is a perfect place to define any additional module properties: diff --git a/modules/default/alert/README.md b/modules/default/alert/README.md index d566313dc7..58be0570f8 100644 --- a/modules/default/alert/README.md +++ b/modules/default/alert/README.md @@ -7,7 +7,7 @@ To use this module, add it to the modules array in the config/config.js file: ``` modules: [ { - module: 'alert', + module: "alert", config: { // The config property is optional. // See 'Configuration options' for more information. @@ -21,52 +21,13 @@ modules: [ The following properties can be configured: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionDescription
effectThe animation effect to use for notifications.
-
Possible values: scale slide genie jelly flip exploader bouncyflip -
Default value: slide -
alert_effectThe animation effect to use for alerts.
-
Possible values: scale slide genie jelly flip exploader bouncyflip -
Default value: jelly -
display_timeTime a notification is displayed in milliseconds.
-
Possible values: int -
Default value: 3500 -
positionPosition where the notifications should be displayed.
-
Possible values: left center right -
Default value: center -
welcome_messageMessage shown at startup.
-
Possible values: string false -
Default value: false (no message at startup) -
+| Option | Description +| ----------------- | ----------- +| `effect` | The animation effect to use for notifications.

**Possible values:** `scale` `slide` `genie` `jelly` `flip` `exploader` `bouncyflip`
**Default value:** `slide` +| `alert_effect` | The animation effect to use for alerts.

**Possible values:** `scale` `slide` `genie` `jelly` `flip` `exploader` `bouncyflip`
**Default value:** `jelly` +| `display_time` | Time a notification is displayed in milliseconds.

**Possible values:** `int`
**Default value:** `3500` +| `position` | Position where the notifications should be displayed.

**Possible values:** `left` `center` `right`
**Default value:** `center` +| `welcome_message` | Message shown at startup.

**Possible values:** `string` `false`
**Default value:** `false` (no message at startup) ## Developer notes @@ -82,83 +43,21 @@ self.sendNotification("SHOW_ALERT", {}); ``` ### Notification params - - - - - - - - - - - - - - - - - - -
OptionDescription
titleThe title of the notification.
-
Possible values: text or html -
messageThe message of the notification.
-
Possible values: text or html -
+| Option | Description +| --------- | ----------- +| `title` | The title of the notification.

**Possible values:** `text` or `html` +| `message` | The message of the notification.

**Possible values:** `text` or `html` + ### Alert params - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionDescription
titleThe title of the alert.
-
Possible values: text or html -
messageThe message of the alert.
-
Possible values: text or html -
imageUrl (optional)Image to show in the alert
-
Possible values: url path -
Default value: none -
imageFA (optional)Font Awesome icon to show in the alert
-
Possible values: See Font Awsome website. -
Default value: none -
imageHeight (optional even with imageUrl set)Height of the image
-
Possible values: intpx -
Default value: 80px -
timer (optional)How long the alert should stay visible in ms. -
Important: If you do not use the timer, it is your duty to hide the alert by using self.sendNotification("HIDE_ALERT");!
-
Possible values: int float -
Default value: none -
+| Option | Description +| ----------------------------------------------- | ----------- +| `title` | The title of the alert.

**Possible values:** `text` or `html` +| `message` | The message of the alert.

**Possible values:** `text` or `html` +| `imageUrl` (optional) | Image to show in the alert

**Possible values:** `url` `path`
**Default value:** `none` +| `imageFA` (optional) | Font Awesome icon to show in the alert

**Possible values:** See [Font Awsome](http://fontawesome.io/icons/) website.
**Default value:** `none` +| `imageHeight` (optional even with imageUrl set) | Height of the image

**Possible values:** `intpx`
**Default value:** `80px` +| `timer` (optional) | How long the alert should stay visible in ms.
**Important:** If you do not use the `timer`, it is your duty to hide the alert by using `self.sendNotification("HIDE_ALERT");`!

**Possible values:** `int` `float`
**Default value:** `none` ## Open Source Licenses ###[NotificationStyles](https://github.com/codrops/NotificationStyles) diff --git a/modules/default/alert/translations/hu.json b/modules/default/alert/translations/hu.json new file mode 100644 index 0000000000..be61836578 --- /dev/null +++ b/modules/default/alert/translations/hu.json @@ -0,0 +1,4 @@ +{ + "sysTitle": "MagicMirror értesítés", + "welcome": "Üdvözöljük, indulás sikeres!" +} diff --git a/modules/default/alert/translations/ru.json b/modules/default/alert/translations/ru.json new file mode 100644 index 0000000000..60ddf3d820 --- /dev/null +++ b/modules/default/alert/translations/ru.json @@ -0,0 +1,4 @@ +{ + "sysTitle": "MagicMirror Уведомление", + "welcome": "Добро пожаловать, старт был успешным!" +} diff --git a/modules/default/calendar/README.md b/modules/default/calendar/README.md index c5745152ee..57a989a8b0 100644 --- a/modules/default/calendar/README.md +++ b/modules/default/calendar/README.md @@ -8,8 +8,8 @@ To use this module, add it to the modules array in the `config/config.js` file: ````javascript modules: [ { - module: 'calendar', - position: 'top_left', // This can be any of the regions. Best results in left or right regions. + module: "calendar", + position: "top_left", // This can be any of the regions. Best results in left or right regions. config: { // The config property is optional. // If no config is set, an example calendar is shown. @@ -24,160 +24,36 @@ modules: [ The following properties can be configured: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionDescription
maximumEntriesThe maximum number of events shown.
-
Possible values: 0 - 100 -
Default value: 10 -
maximumNumberOfDaysThe maximum number of days in the future.
-
Default value: 365 -
displaySymbolDisplay a symbol in front of an entry.
-
Possible values: true or false -
Default value: true -
defaultSymbolThe default symbol.
-
Possible values: See Font Awsome website. -
Default value: calendar -
maxTitleLengthThe maximum title length.
-
Possible values: 10 - 50 -
Default value: 25 -
fetchIntervalHow often does the content needs to be fetched? (Milliseconds)
-
Possible values: 1000 - 86400000 -
Default value: 300000 (5 minutes) -
animationSpeedSpeed of the update animation. (Milliseconds)
-
Possible values:0 - 5000 -
Default value: 2000 (2 seconds) -
fadeFade the future events to black. (Gradient)
-
Possible values: true or false -
Default value: true -
fadePointWhere to start fade?
-
Possible values: 0 (top of the list) - 1 (bottom of list) -
Default value: 0.25 -
calendarsThe list of calendars.
-
Possible values: An array, see calendar configuration below. -
Default value: An example calendar. -
titleReplaceAn object of textual replacements applied to the tile of the event. This allow to remove or replace certains words in the title.
-
Example:
- - titleReplace: {'Birthday of ' : '', 'foo':'bar'} - -
Default value: - - { - "De verjaardag van ": "", - "'s birthday": "" - } - -
displayRepeatingCountTitleShow count title for yearly repeating events (e.g. "X. Birthday", "X. Anniversary")
-
Possible values: true or false -
Default value: false -
dateFormatFormat to use for the date of events (when using absolute dates)
-
Possible values: See Moment.js formats -
Default value: MMM Do (e.g. Jan 18th) -
timeFormatDisplay event times as absolute dates, or relative time
-
Possible values: absolute or relative -
Default value: relative -
getRelativeHow much time (in hours) should be left until calendar events start getting relative?
-
Possible values: 0 (events stay absolute) - 48 (48 hours before the event starts) -
Default value: 6 -
urgencyWhen using a timeFormat of absolute, the urgency setting allows you to display events within a specific time frame as relative - This allows events within a certain time frame to be displayed as relative (in xx days) while others are displayed as absolute dates
-
Possible values: a positive integer representing the number of days for which you want a relative date, for example 7 (for 7 days)
-
Default value: 7 -
broadcastEventsIf this property is set to true, the calendar will broadcast all the events to all other modules with the notification message: CALENDAR_EVENTS. The event objects are stored in an array and contain the following fields: title, startDate, endDate, fullDayEvent, location and geo.
-
Possible values: true, false
-
Default value: true -
hidePrivateHides private calendar events.
-
Possible values: true or false -
Default value: false -
+| Option | Description +| ---------------------------- | ----------- +| `maximumEntries` | The maximum number of events shown. / **Possible values:** `0` - `100`
**Default value:** `10` +| `maximumNumberOfDays` | The maximum number of days in the future.

**Default value:** `365` +| `displaySymbol` | Display a symbol in front of an entry.

**Possible values:** `true` or `false`
**Default value:** `true` +| `defaultSymbol` | The default symbol.

**Possible values:** See [Font Awsome](http://fontawesome.io/icons/) website.
**Default value:** `calendar` +| `maxTitleLength` | The maximum title length.

**Possible values:** `10` - `50`
**Default value:** `25` +| `fetchInterval` | How often does the content needs to be fetched? (Milliseconds)

**Possible values:** `1000` - `86400000`
**Default value:** `300000` (5 minutes) +| `animationSpeed` | Speed of the update animation. (Milliseconds)

**Possible values:**`0` - `5000`
**Default value:** `2000` (2 seconds) +| `fade` | Fade the future events to black. (Gradient)

**Possible values:** `true` or `false`
**Default value:** `true` +| `fadePoint` | Where to start fade?

**Possible values:** `0` (top of the list) - `1` (bottom of list)
**Default value:** `0.25` +| `calendars` | The list of calendars.

**Possible values:** An array, see _calendar configuration_ below.
**Default value:** _An example calendar._ +| `titleReplace` | An object of textual replacements applied to the tile of the event. This allow to remove or replace certains words in the title.

**Example:** `{'Birthday of ' : '', 'foo':'bar'}`
**Default value:** `{ "De verjaardag van ": "", "'s birthday": "" }` +| `displayRepeatingCountTitle` | Show count title for yearly repeating events (e.g. "X. Birthday", "X. Anniversary")

**Possible values:** `true` or `false`
**Default value:** `false` +| `dateFormat` | Format to use for the date of events (when using absolute dates)

**Possible values:** See [Moment.js formats](http://momentjs.com/docs/#/parsing/string-format/)
**Default value:** `MMM Do` (e.g. Jan 18th) +| `timeFormat` | Display event times as absolute dates, or relative time

**Possible values:** `absolute` or `relative`
**Default value:** `relative` +| `getRelative` | How much time (in hours) should be left until calendar events start getting relative?

**Possible values:** `0` (events stay absolute) - `48` (48 hours before the event starts)
**Default value:** `6` +| `urgency` | When using a timeFormat of `absolute`, the `urgency` setting allows you to display events within a specific time frame as `relative`. This allows events within a certain time frame to be displayed as relative (in xx days) while others are displayed as absolute dates

**Possible values:** a positive integer representing the number of days for which you want a relative date, for example `7` (for 7 days)

**Default value:** `7` +| `broadcastEvents` | If this property is set to true, the calendar will broadcast all the events to all other modules with the notification message: `CALENDAR_EVENTS`. The event objects are stored in an array and contain the following fields: `title`, `startDate`, `endDate`, `fullDayEvent`, `location` and `geo`.

**Possible values:** `true`, `false`

**Default value:** `true` +| `hidePrivate` | Hides private calendar events.

**Possible values:** `true` or `false`
**Default value:** `false` ### Calendar configuration The `calendars` property contains an array of the configured calendars. +The `colored` property gives the option for an individual color for each calendar. #### Default value: ````javascript config: { + colored: false, calendars: [ { url: 'http://www.calendarlabs.com/templates/ical/US-Holidays.ics', @@ -189,41 +65,13 @@ config: { #### Calendar configuration options: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionDescription
urlThe url of the calendar .ical. This property is required.
-
Possible values: Any public accessble .ical calendar. -
symbolThe symbol to show in front of an event. This property is optional.
-
Possible values: See Font Awesome website. -
repeatingCountTitleThe count title for yearly repating events in this calendar.
-
Example:
- 'Birthday' -
userThe username for HTTP Basic authentication.
passThe password for HTTP Basic authentication.
+| Option | Description +| --------------------- | ----------- +| `url` | The url of the calendar .ical. This property is required.

**Possible values:** Any public accessble .ical calendar. +| `symbol` | The symbol to show in front of an event. This property is optional.

**Possible values:** See [Font Awesome](http://fontawesome.io/icons/) website. +| `color` | The font color of an event from this calendar. This property should be set if the config is set to colored: true.

**Possible values:** HEX, RGB or RGBA values (#efefef, rgb(242,242,242), rgba(242,242,242,0.5)). +| `repeatingCountTitle` | The count title for yearly repating events in this calendar.

**Example:** `'Birthday'` +| `user` | The username for HTTP Basic authentication. +| `pass` | The password for HTTP Basic authentication. +| `maximumEntries` | The maximum number of events shown. Overrides global setting. **Possible values:** `0` - `100` +| `maximumNumberOfDays` | The maximum number of days in the future. Overrides global setting diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index b636b30b34..72808c1fcc 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -27,6 +27,7 @@ Module.register("calendar", { getRelative: 6, fadePoint: 0.25, // Start on 1/4th of the list. hidePrivate: false, + colored: false, calendars: [ { symbol: "calendar", @@ -68,7 +69,13 @@ Module.register("calendar", { for (var c in this.config.calendars) { var calendar = this.config.calendars[c]; calendar.url = calendar.url.replace("webcal://", "http://"); - this.addCalendar(calendar.url, calendar.user, calendar.pass); + + var calendarConfig = { + maximumEntries: calendar.maximumEntries, + maximumNumberOfDays: calendar.maximumNumberOfDays, + }; + + this.addCalendar(calendar.url, calendar.user, calendar.pass, calendarConfig); } this.calendarData = {}; @@ -114,6 +121,11 @@ Module.register("calendar", { var event = events[e]; var eventWrapper = document.createElement("tr"); + + if (this.config.colored) { + eventWrapper.style.cssText = "color:" + this.colorForUrl(event.url); + } + eventWrapper.className = "normal"; if (this.config.displaySymbol) { @@ -142,7 +154,13 @@ Module.register("calendar", { } titleWrapper.innerHTML = this.titleTransform(event.title) + repeatingCountTitle; - titleWrapper.className = "title bright"; + + if (!this.config.colored) { + titleWrapper.className = "title bright"; + } else { + titleWrapper.className = "title"; + } + eventWrapper.appendChild(titleWrapper); var timeWrapper = document.createElement("td"); @@ -273,8 +291,8 @@ Module.register("calendar", { var event = calendar[e]; if(this.config.hidePrivate) { if(event.class === "PRIVATE") { - // do not add the current event, skip it - continue; + // do not add the current event, skip it + continue; } } event.url = c; @@ -295,11 +313,11 @@ Module.register("calendar", { * * argument url sting - Url to add. */ - addCalendar: function (url, user, pass) { + addCalendar: function (url, user, pass, calendarConfig) { this.sendSocketNotification("ADD_CALENDAR", { url: url, - maximumEntries: this.config.maximumEntries, - maximumNumberOfDays: this.config.maximumNumberOfDays, + maximumEntries: calendarConfig.maximumEntries || this.config.maximumEntries, + maximumNumberOfDays: calendarConfig.maximumNumberOfDays || this.config.maximumNumberOfDays, fetchInterval: this.config.fetchInterval, user: user, pass: pass @@ -323,6 +341,24 @@ Module.register("calendar", { return this.config.defaultSymbol; }, + + /* colorForUrl(url) + * Retrieves the color for a specific url. + * + * argument url sting - Url to look for. + * + * return string - The Color + */ + colorForUrl: function (url) { + for (var c in this.config.calendars) { + var calendar = this.config.calendars[c]; + if (calendar.url === url && typeof calendar.color === "string") { + return calendar.color; + } + } + + return "#fff"; + }, /* countTitleForUrl(url) * Retrieves the name for a specific url. * @@ -360,7 +396,7 @@ Module.register("calendar", { /* capFirst(string) * Capitalize the first letter of a string - * Eeturn capitalized string + * Return capitalized string */ capFirst: function (string) { @@ -379,6 +415,13 @@ Module.register("calendar", { titleTransform: function (title) { for (var needle in this.config.titleReplace) { var replacement = this.config.titleReplace[needle]; + + var regParts = needle.match(/^\/(.+)\/([gim]*)$/); + if (regParts) { + // the parsed pattern is a regexp. + needle = new RegExp(regParts[1], regParts[2]); + } + title = title.replace(needle, replacement); } diff --git a/modules/default/calendar/node_helper.js b/modules/default/calendar/node_helper.js index a1b8db091d..cc5116595e 100644 --- a/modules/default/calendar/node_helper.js +++ b/modules/default/calendar/node_helper.js @@ -12,7 +12,6 @@ var CalendarFetcher = require("./calendarfetcher.js"); module.exports = NodeHelper.create({ // Override start method. start: function() { - var self = this; var events = []; this.fetchers = []; diff --git a/modules/default/calendar/vendor/ical.js/ical.js b/modules/default/calendar/vendor/ical.js/ical.js index 252e426bd0..f60c5357b8 100644 --- a/modules/default/calendar/vendor/ical.js/ical.js +++ b/modules/default/calendar/vendor/ical.js/ical.js @@ -90,7 +90,6 @@ return dt } - var dateParam = function(name){ return function(val, params, curr){ @@ -143,6 +142,16 @@ } } + var exdateParam = function(name){ + return function(val, params, curr){ + var date = dateParam(name)(val, params, curr); + if (date.exdates === undefined) { + date.exdates = []; + } + date.exdates.push(date.exdate); + return date; + } + } var geoParam = function(name){ return function(val, params, curr){ @@ -240,6 +249,7 @@ , 'LOCATION' : storeParam('location') , 'DTSTART' : dateParam('start') , 'DTEND' : dateParam('end') + , 'EXDATE' : exdateParam('exdate') ,' CLASS' : storeParam('class') , 'TRANSP' : storeParam('transparency') , 'GEO' : geoParam('geo') diff --git a/modules/default/calendar/vendor/ical.js/node-ical.js b/modules/default/calendar/vendor/ical.js/node-ical.js index 2f6ef3ef57..c954e72240 100644 --- a/modules/default/calendar/vendor/ical.js/node-ical.js +++ b/modules/default/calendar/vendor/ical.js/node-ical.js @@ -17,7 +17,8 @@ exports.parseFile = function(filename){ } -var rrule = require('rrule').RRule +var rrule = require('rrule-alt').RRule +var rrulestr = rrule.rrulestr ical.objectHandlers['RRULE'] = function(val, params, curr, stack, line){ curr.rrule = line; @@ -26,7 +27,7 @@ ical.objectHandlers['RRULE'] = function(val, params, curr, stack, line){ var originalEnd = ical.objectHandlers['END']; ical.objectHandlers['END'] = function(val, params, curr, stack){ if (curr.rrule) { - var rule = curr.rrule.replace('RRULE:', ''); + var rule = curr.rrule; if (rule.indexOf('DTSTART') === -1) { if (curr.start.length === 8) { @@ -36,10 +37,14 @@ ical.objectHandlers['END'] = function(val, params, curr, stack){ } } - rule += ';DTSTART=' + curr.start.toISOString().replace(/[-:]/g, ''); + rule += ' DTSTART:' + curr.start.toISOString().replace(/[-:]/g, ''); rule = rule.replace(/\.[0-9]{3}/, ''); } - curr.rrule = rrule.fromString(rule); + for (var i in curr.exdates) { + rule += ' EXDATE:' + curr.exdates[i].toISOString().replace(/[-:]/g, ''); + rule = rule.replace(/\.[0-9]{3}/, ''); + } + curr.rrule = rrulestr(rule); } return originalEnd.call(this, val, params, curr, stack); } diff --git a/modules/default/clock/README.md b/modules/default/clock/README.md index 76a979d4ba..5570ff5a67 100644 --- a/modules/default/clock/README.md +++ b/modules/default/clock/README.md @@ -8,8 +8,8 @@ To use this module, add it to the modules array in the `config/config.js` file: ````javascript modules: [ { - module: 'clock', - position: 'top_left', // This can be any of the regions. + module: "clock", + position: "top_left", // This can be any of the regions. config: { // The config property is optional. // See 'Configuration options' for more information. @@ -22,106 +22,18 @@ modules: [ The following properties can be configured: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionDescription
timeFormatUse 12 or 24 hour format.
-
Possible values: 12 or 24 -
Default value: uses value of config.timeFormat -
displaySecondsDisplay seconds.
-
Possible values: true or false -
Default value: true -
showPeriodShow the period (am/pm) with 12 hour format.
-
Possible values: true or false -
Default value: true -
showPeriodUpperShow the period (AM/PM) with 12 hour format as uppercase.
-
Possible values: true or false -
Default value: false -
clockBoldRemove the colon and bold the minutes to make a more modern look.
-
Possible values: true or false -
Default value: false -
showDateTurn off or on the Date section.
-
Possible values: true or false -
Default value: true -
displayTypeDisplay a digital clock, analog clock, or both together.
-
Possible values: digital, analog, or both -
Default value: digital -
analogSizeSpecific to the analog clock. Defines how large the analog display is.
-
Possible values: A positive number of pixels -
Default value: 200px -
analogFaceSpecific to the analog clock. Specifies which clock face to use.
-
Possible values: simple for a simple border, none for no face or border, or face-### (where ### is currently a value between 001 and 012, inclusive) -
Default value: simple -
secondsColorSpecific to the analog clock. Specifies what color to make the 'seconds' hand.
-
Possible values: any HTML RGB Color -
Default value: #888888 -
analogPlacementSpecific to the analog clock. (requires displayType set to 'both') Specifies where the analog clock is in relation to the digital clock
-
Possible values: top, right, bottom, or left -
Default value: bottom -
analogShowDateSpecific to the analog clock. If the clock is used as a separate module and set to analog only, this configures whether a date is also displayed with the clock.
-
Possible values: false, top, or bottom -
Default value: top -
timezoneSpecific a timezone to show clock.
-
Possible examples values: America/New_York, America/Santiago, Etc/GMT+10 -
Default value: none -
+| Option | Description +| ----------------- | ----------- +| `timeFormat` | Use 12 or 24 hour format.

**Possible values:** `12` or `24`
**Default value:** uses value of _config.timeFormat_ +| `displaySeconds` | Display seconds.

**Possible values:** `true` or `false`
**Default value:** `true` +| `showPeriod` | Show the period (am/pm) with 12 hour format.

**Possible values:** `true` or `false`
**Default value:** `true` +| `showPeriodUpper` | Show the period (AM/PM) with 12 hour format as uppercase.

**Possible values:** `true` or `false`
**Default value:** `false` +| `clockBold` | Remove the colon and bold the minutes to make a more modern look.

**Possible values:** `true` or `false`
**Default value:** `false` +| `showDate` | Turn off or on the Date section.

**Possible values:** `true` or `false`
**Default value:** `true` +| `displayType` | Display a digital clock, analog clock, or both together.

**Possible values:** `digital`, `analog`, or `both`
**Default value:** `digital` +| `analogSize` | **Specific to the analog clock.** Defines how large the analog display is.

**Possible values:** A positive number of pixels`
**Default value:** `200px` +| `analogFace` | **Specific to the analog clock.** Specifies which clock face to use.

**Possible values:** `simple` for a simple border, `none` for no face or border, or `face-###` (where ### is currently a value between 001 and 012, inclusive)
**Default value:** `simple` +| `secondsColor` | **Specific to the analog clock.** Specifies what color to make the 'seconds' hand.

**Possible values:** `any HTML RGB Color`
**Default value:** `#888888` +| `analogPlacement` | **Specific to the analog clock. _(requires displayType set to `'both'`)_** Specifies where the analog clock is in relation to the digital clock

**Possible values:** `top`, `right`, `bottom`, or `left`
**Default value:** `bottom` +| `analogShowDate` | **Specific to the analog clock.** If the clock is used as a separate module and set to analog only, this configures whether a date is also displayed with the clock.

**Possible values:** `false`, `top`, or `bottom`
**Default value:** `top` +| `timezone` | Specific a timezone to show clock.

**Possible examples values:** `America/New_York`, `America/Santiago`, `Etc/GMT+10`
**Default value:** `none` diff --git a/modules/default/clock/clock.js b/modules/default/clock/clock.js index c84c133e0a..1882c52e15 100644 --- a/modules/default/clock/clock.js +++ b/modules/default/clock/clock.js @@ -134,6 +134,10 @@ Module.register("clock",{ if (this.config.analogFace != "" && this.config.analogFace != "simple" && this.config.analogFace != "none") { clockCircle.style.background = "url("+ this.data.path + "faces/" + this.config.analogFace + ".svg)"; clockCircle.style.backgroundSize = "100%"; + + // The following line solves issue: https://github.com/MichMich/MagicMirror/issues/611 + clockCircle.style.border = "1px solid black"; + } else if (this.config.analogFace != "none") { clockCircle.style.border = "2px solid white"; } diff --git a/modules/default/compliments/README.md b/modules/default/compliments/README.md index 430c4fbfa5..8ef9e2fb74 100644 --- a/modules/default/compliments/README.md +++ b/modules/default/compliments/README.md @@ -8,8 +8,8 @@ To use this module, add it to the modules array in the `config/config.js` file: ````javascript modules: [ { - module: 'compliments', - position: 'lower_third', // This can be any of the regions. + module: "compliments", + position: "lower_third", // This can be any of the regions. // Best results in one of the middle regions like: lower_third config: { // The config property is optional. @@ -25,48 +25,12 @@ modules: [ The following properties can be configured: - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionDescription
updateIntervalHow often does the compliment have to change? (Milliseconds)
-
Possible values: 1000 - 86400000 -
Default value: 30000 (30 seconds) -
fadeSpeedSpeed of the update animation. (Milliseconds)
-
Possible values:0 - 5000 -
Default value: 4000 (4 seconds) -
complimentsThe list of compliments.
-
Possible values: An object with three arrays: morning, afternoon andevening. See compliment configuration below. -
Default value: See compliment configuration below. -
remoteFileExternal file from which to load the compliments
-
Possible values:Path to a JSON file containing compliments, configured - as per the value of the compliments configuration (see below). An object with three arrays: - morning, afternoon and evening. - compliments.json -
Default value: null (Do not load from file) -
+| Option | Description +| ---------------- | ----------- +| `updateInterval` | How often does the compliment have to change? (Milliseconds)

**Possible values:** `1000` - `86400000`
**Default value:** `30000` (30 seconds) +| `fadeSpeed` | Speed of the update animation. (Milliseconds)

**Possible values:**`0` - `5000`
**Default value:** `4000` (4 seconds) +| `compliments` | The list of compliments.

**Possible values:** An object with three arrays: `morning`, `afternoon` and`evening`. See _compliment configuration_ below.
**Default value:** See _compliment configuration_ below. +| `remoteFile` | External file from which to load the compliments

**Possible values:** Path to a JSON file containing compliments, configured as per the value of the _compliments configuration_ (see below). An object with three arrays: `morning`, `afternoon` and `evening`. - `compliments.json`
**Default value:** `null` (Do not load from file) ### Compliment configuration @@ -74,36 +38,36 @@ The `compliments` property contains an object with three arrays: morningday_sunny -* day_cloudy -* cloudy -* cloudy_windy -* showers -* rain -* thunderstorm -* snow -* fog -* night_clear -* night_cloudy -* night_showers -* night_rain -* night_thunderstorm -* night_snow -* night_alt_cloudy_windy +* `day_sunny` +* `day_cloudy` +* `cloudy` +* `cloudy_windy` +* `showers` +* `rain` +* `thunderstorm` +* `snow` +* `fog` +* `night_clear` +* `night_cloudy` +* `night_showers` +* `night_rain` +* `night_thunderstorm` +* `night_snow` +* `night_alt_cloudy_windy` #### Example use with currentweather module ````javascript config: { compliments: { day_sunny: [ - 'Today is a sunny day', - 'It\'s a beautiful day' + "Today is a sunny day", + "It's a beautiful day" ], snow: [ - 'Snowball battle!' + "Snowball battle!" ], rain: [ - 'Don\'t forget your umbrella' + "Don't forget your umbrella" ] } } @@ -115,19 +79,19 @@ config: { config: { compliments: { morning: [ - 'Good morning, handsome!', - 'Enjoy your day!', - 'How was your sleep?' + "Good morning, handsome!", + "Enjoy your day!", + "How was your sleep?" ], afternoon: [ - 'Hello, beauty!', + "Hello, beauty!", 'You look sexy!', - 'Looking good today!' + "Looking good today!" ], evening: [ - 'Wow, you look hot!', - 'You look nice!', - 'Hi, sexy!' + "Wow, you look hot!", + "You look nice!", + "Hi, sexy!" ] } } diff --git a/modules/default/currentweather/README.md b/modules/default/currentweather/README.md index ccef543cd6..65913d82b6 100644 --- a/modules/default/currentweather/README.md +++ b/modules/default/currentweather/README.md @@ -8,14 +8,14 @@ To use this module, add it to the modules array in the `config/config.js` file: ````javascript modules: [ { - module: 'currentweather', - position: 'top_right', // This can be any of the regions. + module: "currentweather", + position: "top_right", // This can be any of the regions. // Best results in left or right regions. config: { // See 'Configuration options' for more information. - location: 'Amsterdam,Netherlands', - locationID: '', //Location ID from http://openweathermap.org/help/city_list.txt - appid: 'abcde12345abcde12345abcde12345ab' //openweathermap.org API key. + location: "Amsterdam,Netherlands", + locationID: "", //Location ID from http://openweathermap.org/help/city_list.txt + appid: "abcde12345abcde12345abcde12345ab" //openweathermap.org API key. } } ] @@ -26,189 +26,52 @@ modules: [ The following properties can be configured: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionDescription
locationThe location used for weather information.
-
Example: 'Amsterdam,Netherlands' -
Default value: false

- Note: When the location and locationID are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used. -
locationIDLocation ID from OpenWeatherMap This will override anything you put in location.
Leave blank if you want to use location. -
Example: 1234567 -
Default value: false

- Note: When the location and locationID are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used. -
appidThe OpenWeatherMap API key, which can be obtained by creating an OpenWeatherMap account.
-
This value is REQUIRED -
unitsWhat units to use. Specified by config.js
-
Possible values: config.units = Specified by config.js, default = Kelvin, metric = Celsius, imperial =Fahrenheit -
Default value: config.units -
roundTempRound temperature value to nearest integer.
-
Possible values: true (round to integer) or false (display exact value with decimal point) -
Default value: false -
updateIntervalHow often does the content needs to be fetched? (Milliseconds)
-
Possible values: 1000 - 86400000 -
Default value: 600000 (10 minutes) -
animationSpeedSpeed of the update animation. (Milliseconds)
-
Possible values:0 - 5000 -
Default value: 1000 (1 second) -
timeFormatUse 12 or 24 hour format.
-
Possible values: 12 or 24 -
Default value: uses value of config.timeFormat -
showPeriodShow the period (am/pm) with 12 hour format
-
Possible values: true or false -
Default value: true -
showPeriodUpperShow the period (AM/PM) with 12 hour format as uppercase
-
Possible values: true or false -
Default value: false -
showWindDirectionShow the wind direction next to the wind speed.
-
Possible values: true or false -
Default value: true -
showHumidityShow the current humidity
-
Possible values: true or false -
Default value: false -
onlyTempShow only current Temperature and weather icon.
-
Possible values: true or false -
Default value: false -
useBeaufortPick between using the Beaufort scale for wind speed or using the default units.
-
Possible values: true or false -
Default value: true -
langThe language of the days.
-
Possible values: en, nl, ru, etc ... -
Default value: uses value of config.language -
initialLoadDelayThe initial delay before loading. If you have multiple modules that use the same API key, you might want to delay one of the requests. (Milliseconds)
-
Possible values: 1000 - 5000 -
Default value: 0 -
retryDelayThe delay before retrying after a request failure. (Milliseconds)
-
Possible values: 1000 - 60000 -
Default value: 2500 -
apiVersionThe OpenWeatherMap API version to use.
-
Default value: 2.5 -
apiBaseThe OpenWeatherMap base URL.
-
Default value: 'http://api.openweathermap.org/data/' -
weatherEndpointThe OpenWeatherMap API endPoint.
-
Default value: 'weather' -
appendLocationNameToHeaderIf set to true, the returned location name will be appended to the header of the module, if the header is enabled. This is mainly intresting when using calender based weather.
-
Default value: true -
calendarClassThe class for the calender module to base the event based weather information on.
-
Default value: 'calendar' -
iconTableThe conversion table to convert the weather conditions to weather-icons.
-
Default value: iconTable: { - '01d':'wi-day-sunny', - '02d':'wi-day-cloudy', - '03d':'wi-cloudy', - '04d':'wi-cloudy-windy', - '09d':'wi-showers', - '10d':'wi-rain', - '11d':'wi-thunderstorm', - '13d':'wi-snow', - '50d':'wi-fog', - '01n':'wi-night-clear', - '02n':'wi-night-cloudy', - '03n':'wi-night-cloudy', - '04n':'wi-night-cloudy', - '09n':'wi-night-showers', - '10n':'wi-night-rain', - '11n':'wi-night-thunderstorm', - '13n':'wi-night-snow', - '50n':'wi-night-alt-cloudy-windy' - } -
+| Option | Description +| ---------------------------- | ----------- +| `location` | The location used for weather information.

**Example:** `'Amsterdam,Netherlands'`
**Default value:** `false`

**Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used. +| `locationID` | Location ID from [OpenWeatherMap](http://openweathermap.org/help/city_list.txt) **This will override anything you put in location.**
Leave blank if you want to use location.
**Example:** `1234567`
**Default value:** `false`

**Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used. +| `appid` | The [OpenWeatherMap](https://home.openweathermap.org) API key, which can be obtained by creating an OpenWeatherMap account.

This value is **REQUIRED** +| `units` | What units to use. Specified by config.js

**Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` =Fahrenheit
**Default value:** `config.units` +| `roundTemp` | Round temperature value to nearest integer.

**Possible values:** `true` (round to integer) or `false` (display exact value with decimal point)
**Default value:** `false` +| `updateInterval` | How often does the content needs to be fetched? (Milliseconds)

**Possible values:** `1000` - `86400000`
**Default value:** `600000` (10 minutes) +| `animationSpeed` | Speed of the update animation. (Milliseconds)

**Possible values:**`0` - `5000`
**Default value:** `1000` (1 second) +| `timeFormat` | Use 12 or 24 hour format.

**Possible values:** `12` or `24`
**Default value:** uses value of _config.timeFormat_ +| `showPeriod` | Show the period (am/pm) with 12 hour format

**Possible values:** `true` or `false`
**Default value:** `true` +| `showPeriodUpper` | Show the period (AM/PM) with 12 hour format as uppercase

**Possible values:** `true` or `false`
**Default value:** `false` +| `showWindDirection` | Show the wind direction next to the wind speed.

**Possible values:** `true` or `false`
**Default value:** `true` +| `showHumidity` | Show the current humidity

**Possible values:** `true` or `false`
**Default value:** `false` +| `onlyTemp` | Show only current Temperature and weather icon.

**Possible values:** `true` or `false`
**Default value:** `false` +| `useBeaufort` | Pick between using the Beaufort scale for wind speed or using the default units.

**Possible values:** `true` or `false`
**Default value:** `true` +| `lang` | The language of the days.

**Possible values:** `en`, `nl`, `ru`, etc ...
**Default value:** uses value of _config.language_ +| `initialLoadDelay` | The initial delay before loading. If you have multiple modules that use the same API key, you might want to delay one of the requests. (Milliseconds)

**Possible values:** `1000` - `5000`
**Default value:** `0` +| `retryDelay` | The delay before retrying after a request failure. (Milliseconds)

**Possible values:** `1000` - `60000`
**Default value:** `2500` +| `apiVersion` | The OpenWeatherMap API version to use.

**Default value:** `2.5` +| `apiBase` | The OpenWeatherMap base URL.

**Default value:** `'http://api.openweathermap.org/data/'` +| `weatherEndpoint` | The OpenWeatherMap API endPoint.

**Default value:** `'weather'` +| `appendLocationNameToHeader` | If set to `true`, the returned location name will be appended to the header of the module, if the header is enabled. This is mainly intresting when using calender based weather.

**Default value:** `true` +| `calendarClass` | The class for the calender module to base the event based weather information on.

**Default value:** `'calendar'` +| `iconTable` | The conversion table to convert the weather conditions to weather-icons.

**Default value:** view tabel below. + +#### Default Icon Table +````javascript +iconTable: { + '01d': 'wi-day-sunny', + '02d': 'wi-day-cloudy', + '03d': 'wi-cloudy', + '04d': 'wi-cloudy-windy', + '09d': 'wi-showers', + '10d': 'wi-rain', + '11d': 'wi-thunderstorm', + '13d': 'wi-snow', + '50d': 'wi-fog', + '01n': 'wi-night-clear', + '02n': 'wi-night-cloudy', + '03n': 'wi-night-cloudy', + '04n': 'wi-night-cloudy', + '09n': 'wi-night-showers', + '10n': 'wi-night-rain', + '11n': 'wi-night-thunderstorm', + '13n': 'wi-night-snow', + '50n': 'wi-night-alt-cloudy-windy' +} +```` diff --git a/modules/default/currentweather/currentweather.js b/modules/default/currentweather/currentweather.js index e92550eaa2..135af54a47 100644 --- a/modules/default/currentweather/currentweather.js +++ b/modules/default/currentweather/currentweather.js @@ -296,7 +296,7 @@ Module.register("currentweather",{ */ processWeather: function(data) { - if (!data || !data.main || !data.main.temp) { + if (!data || !data.main || typeof data.main.temp === "undefined") { // Did not receive usable new data. // Maybe this needs a better check? return; diff --git a/modules/default/helloworld/README.md b/modules/default/helloworld/README.md index 72e7d023a9..d86fce3d74 100644 --- a/modules/default/helloworld/README.md +++ b/modules/default/helloworld/README.md @@ -6,11 +6,11 @@ To use this module, add it to the modules array in the `config/config.js` file: ````javascript modules: [ { - module: 'helloworld', - position: 'bottom_bar', // This can be any of the regions. + module: "helloworld", + position: "bottom_bar", // This can be any of the regions. config: { // See 'Configuration options' for more information. - text: 'Hello world!' + text: "Hello world!" } } ] @@ -20,23 +20,6 @@ modules: [ The following properties can be configured: - - - - - - - - - - - - - - - - -
OptionDescription
textThe text to display.
-
Example: 'Hello world!' -
Default value: 'Hello world!' -
+| Option | Description +| ------ | ----------- +| `text` | The text to display.

**Example:** `'Hello world!'`
**Default value:** `'Hello world!'` diff --git a/modules/default/newsfeed/README.md b/modules/default/newsfeed/README.md index 58c7b9ae18..bfaa1aade2 100644 --- a/modules/default/newsfeed/README.md +++ b/modules/default/newsfeed/README.md @@ -1,15 +1,16 @@ # Module: News Feed The `newsfeed ` module is one of the default modules of the MagicMirror. -This module displays news headlines based on an RSS feed. +This module displays news headlines based on an RSS feed. Scrolling through news headlines happens time-based (````updateInterval````), but can also be controlled by sending news feed specific notifications to the module. ## Using the module +### Configuration To use this module, add it to the modules array in the `config/config.js` file: ````javascript modules: [ { - module: 'newsfeed', - position: 'bottom_bar', // This can be any of the regions. Best results in center regions. + module: "newsfeed", + position: "bottom_bar", // This can be any of the regions. Best results in center regions. config: { // The config property is optional. // If no config is set, an example calendar is shown. @@ -30,152 +31,51 @@ modules: [ ] ```` -## Configuration options - -The following properties can be configured: +### Notifications +#### Interacting with the module +MagicMirror's [notification mechanism](https://github.com/MichMich/MagicMirror/tree/master/modules#thissendnotificationnotification-payload) allows to send notifications to the `newsfeed` module. The following notifications are supported: +| Notification Identifier | Description +| ----------------------- | ----------- +| `ARTICLE_NEXT` | Shows the next news title (hiding the summary or previously fully displayed article) +| `ARTICLE_PREVIOUS` | Shows the previous news title (hiding the summary or previously fully displayed article) +| `ARTICLE_MORE_DETAILS` | When received the _first time_, shows the corresponding description of the currently displayed news title.
The module expects that the module's configuration option `showDescription` is set to `false` (default value).

When received a _second consecutive time_, shows the full news article in an IFRAME.
This requires that the news page can be embedded in an IFRAME, e.g. doesn't have the HTTP response header [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) set to e.g. `DENY`. +| `ARTICLE_LESS_DETAILS` | Hides the summary or full news article and only displays the news title of the currently viewed news item. - - - - - - - - - +Note the payload of the sent notification event is ignored. - - - - +#### Example +The following example shows how the next news article title can be displayed on the MagicMirror. +````javascript +this.sendNotification('ARTICLE_NEXT'); +```` - - - - - - - - - - - - +#### `newsfeed` specific notification emitting modules +The third party [MMM-Gestures](https://github.com/thobach/MMM-Gestures) module supports above notifications when moving your hand up, down, left or right in front of a gesture sensor attached to the MagicMirror. See module's readme for more details. - - - - - - - - - - - - - - - - - removeStartTags: false, - removeEndTags: false, - startTags: [], - endTags: [] +## Configuration options +The following properties can be configured: - - - - - - - - - - - - - - - - - -
OptionDescription
feedsAn array of feed urls that will be used as source.
- More info about this object can be found below. -
Default value: [ - { - title: "New York Times", - url: "http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml", - encoding: "UTF-8" - } - ] -
showSourceTitleDisplay the title of the source.
-
Possible values: true or false -
Default value: true -
showPublishDateDisplay the publish date of an headline.
-
Possible values: true or false -
Default value: true -
showDescriptionDisplay the description of an item.
-
Possible values: true or false -
Default value: false -
reloadIntervalHow often does the content needs to be fetched? (Milliseconds)
-
Possible values: 1000 - 86400000 -
Default value: 300000 (5 minutes) -
updateIntervalHow often do you want to display a new headline? (Milliseconds)
-
Possible values:1000 - 60000 -
Default value: 10000 (10 seconds) -
animationSpeedSpeed of the update animation. (Milliseconds)
-
Possible values:0 - 5000 -
Default value: 2500 (2.5 seconds) -
maxNewsItemsTotal amount of news items to cycle through. (0 for unlimited)
-
Possible values:0 - ... -
Default value: 0 -
removeStartTagsSome newsfeeds feature tags at the beginning of their titles or descriptions, such as [VIDEO]. - This setting allows for the removal of specified tags from the beginning of an item's description and/or title.
-
Possible values:'title', 'description', 'both' -
startTagsList the tags you would like to have removed at the beginning of the feed item
-
Possible values: ['TAG'] or ['TAG1','TAG2',...] -
removeEndTagsRemove specified tags from the end of an item's description and/or title.
-
Possible values:'title', 'description', 'both' -
endTagsList the tags you would like to have removed at the end of the feed item
-
Possible values: ['TAG'] or ['TAG1','TAG2',...] -
+| Option | Description +| ----------------- | ----------- +| `feeds` | An array of feed urls that will be used as source.
More info about this object can be found below.
**Default value:** `[{ title: "New York Times", url: "http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml", encoding: "UTF-8" }]` +| `showSourceTitle` | Display the title of the source.

**Possible values:** `true` or `false`
**Default value:** `true` +| `showPublishDate` | Display the publish date of an headline.

**Possible values:** `true` or `false`
**Default value:** `true` +| `showDescription` | Display the description of an item.

**Possible values:** `true` or `false`
**Default value:** `false` +| `reloadInterval` | How often does the content needs to be fetched? (Milliseconds)

**Possible values:** `1000` - `86400000`
**Default value:** `300000` (5 minutes) +| `updateInterval` | How often do you want to display a new headline? (Milliseconds)

**Possible values:**`1000` - `60000`
**Default value:** `10000` (10 seconds) +| `animationSpeed` | Speed of the update animation. (Milliseconds)

**Possible values:**`0` - `5000`
**Default value:** `2500` (2.5 seconds) +| `maxNewsItems` | Total amount of news items to cycle through. (0 for unlimited)

**Possible values:**`0` - `...`
**Default value:** `0` +| `removeStartTags` | Some newsfeeds feature tags at the **beginning** of their titles or descriptions, such as _[VIDEO]_. This setting allows for the removal of specified tags from the beginning of an item's description and/or title.

**Possible values:**`'title'`, `'description'`, `'both'` +| `startTags` | List the tags you would like to have removed at the beginning of the feed item

**Possible values:** `['TAG']` or `['TAG1','TAG2',...]` +| `removeEndTags` | Remove specified tags from the **end** of an item's description and/or title.

**Possible values:**`'title'`, `'description'`, `'both'` +| `endTags` | List the tags you would like to have removed at the end of the feed item

**Possible values:** `['TAG']` or `['TAG1','TAG2',...]` The `feeds` property contains an array with multiple objects. These objects have the following properties: - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionDescription
titleThe name of the feed source to be displayed above the news items.
-
This property is optional. -
urlThe url of the feed used for the headlines.
-
Example: 'http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml' -
encodingThe encoding of the news feed.
-
This property is optional. -
Possible values:'UTF-8', 'ISO-8859-1', etc ... -
Default value: 'UTF-8' -
+| Option | Description +| ---------- | ----------- +| `title` | The name of the feed source to be displayed above the news items.

This property is optional. +| `url` | The url of the feed used for the headlines.

**Example:** `'http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml'` +| `encoding` | The encoding of the news feed.

This property is optional.
**Possible values:**`'UTF-8'`, `'ISO-8859-1'`, etc ...
**Default value:** `'UTF-8'` diff --git a/modules/default/newsfeed/fetcher.js b/modules/default/newsfeed/fetcher.js index b7511de9fc..f4fb44d909 100644 --- a/modules/default/newsfeed/fetcher.js +++ b/modules/default/newsfeed/fetcher.js @@ -85,7 +85,12 @@ var Fetcher = function(url, reloadInterval, encoding) { nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]); headers = {"User-Agent": "Mozilla/5.0 (Node.js "+ nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)"} - request({uri: url, encoding: null, headers: headers}).pipe(iconv.decodeStream(encoding)).pipe(parser); + request({uri: url, encoding: null, headers: headers}) + .on("error", function(error) { + fetchFailedCallback(self, error); + scheduleTimer(); + }) + .pipe(iconv.decodeStream(encoding)).pipe(parser); }; diff --git a/modules/default/newsfeed/newsfeed.js b/modules/default/newsfeed/newsfeed.js index 74014208d9..aed917f892 100644 --- a/modules/default/newsfeed/newsfeed.js +++ b/modules/default/newsfeed/newsfeed.js @@ -89,7 +89,8 @@ Module.register("newsfeed",{ if (this.newsItems.length > 0) { - if (this.config.showSourceTitle || this.config.showPublishDate) { + // this.config.showFullArticle is a run-time configuration, triggered by optional notifications + if (!this.config.showFullArticle && (this.config.showSourceTitle || this.config.showPublishDate)) { var sourceAndTimestamp = document.createElement("div"); sourceAndTimestamp.className = "light small dimmed"; @@ -152,10 +153,12 @@ Module.register("newsfeed",{ } - var title = document.createElement("div"); - title.className = "bright medium light"; - title.innerHTML = this.newsItems[this.activeItem].title; - wrapper.appendChild(title); + if(!this.config.showFullArticle){ + var title = document.createElement("div"); + title.className = "bright medium light"; + title.innerHTML = this.newsItems[this.activeItem].title; + wrapper.appendChild(title); + } if (this.config.showDescription) { var description = document.createElement("div"); @@ -164,6 +167,21 @@ Module.register("newsfeed",{ wrapper.appendChild(description); } + if (this.config.showFullArticle) { + var fullArticle = document.createElement("iframe"); + fullArticle.className = ""; + fullArticle.style.width = "100%"; + fullArticle.style.top = "0"; + fullArticle.style.left = "0"; + fullArticle.style.position = "fixed"; + fullArticle.height = window.innerHeight; + fullArticle.style.border = "none"; + fullArticle.src = this.newsItems[this.activeItem].url; + wrapper.appendChild(fullArticle); + } + + + } else { wrapper.innerHTML = this.translate("LOADING"); wrapper.className = "small dimmed"; @@ -256,7 +274,7 @@ Module.register("newsfeed",{ self.updateDom(self.config.animationSpeed); - setInterval(function() { + timer = setInterval(function() { self.activeItem++; self.updateDom(self.config.animationSpeed); }, this.config.updateInterval); @@ -273,5 +291,50 @@ Module.register("newsfeed",{ return string.charAt(0).toUpperCase() + string.slice(1); }, + resetDescrOrFullArticleAndTimer: function() { + this.config.showDescription = false; + this.config.showFullArticle = false; + if(!timer){ + this.scheduleUpdateInterval(); + } + }, + + notificationReceived: function(notification, payload, sender) { + Log.info(this.name + " - received notification: " + notification); + if(notification == "ARTICLE_NEXT"){ + var before = this.activeItem; + this.activeItem++; + if (this.activeItem >= this.newsItems.length) { + this.activeItem = 0; + } + this.resetDescrOrFullArticleAndTimer(); + Log.info(this.name + " - going from article #" + before + " to #" + this.activeItem + " (of " + this.newsItems.length + ")"); + this.updateDom(100); + } else if(notification == "ARTICLE_PREVIOUS"){ + var before = this.activeItem; + this.activeItem--; + if (this.activeItem < 0) { + this.activeItem = this.newsItems.length - 1; + } + this.resetDescrOrFullArticleAndTimer(); + Log.info(this.name + " - going from article #" + before + " to #" + this.activeItem + " (of " + this.newsItems.length + ")"); + this.updateDom(100); + } + // if "more details" is received the first time: show article summary, on second time show full article + else if(notification == "ARTICLE_MORE_DETAILS"){ + this.config.showDescription = !this.config.showDescription; + this.config.showFullArticle = !this.config.showDescription; + clearInterval(timer); + timer = null; + Log.info(this.name + " - showing " + this.config.showDescription ? "article description" : "full article"); + this.updateDom(100); + } else if(notification == "ARTICLE_LESS_DETAILS"){ + this.resetDescrOrFullArticleAndTimer(); + Log.info(this.name + " - showing only article titles again"); + this.updateDom(100); + } else { + Log.info(this.name + " - unknown notification, ignoring: " + notification); + } + }, }); diff --git a/modules/default/updatenotification/README.md b/modules/default/updatenotification/README.md index 7d839087c2..54ba213a9e 100644 --- a/modules/default/updatenotification/README.md +++ b/modules/default/updatenotification/README.md @@ -8,8 +8,8 @@ To use this module, add it to the modules array in the `config/config.js` file: ````javascript modules: [ { - module: 'updatenotification', - position: 'top_center', // This can be any of the regions. + module: "updatenotification", + position: "top_center", // This can be any of the regions. config: { // The config property is optional. // See 'Configuration options' for more information. @@ -22,21 +22,6 @@ modules: [ The following properties can be configured: - - - - - - - - - - - - - - -
OptionDescription
updateIntervalHow often do you want to check for a new version? This value represents the interval in milliseconds.
-
Possible values: Any value above 60000 (1 minute); -
Default value: 600000 (10 minutes); -
\ No newline at end of file +| Option | Description +| ---------------- | ----------- +| `updateInterval` | How often do you want to check for a new version? This value represents the interval in milliseconds.

**Possible values:** Any value above `60000` (1 minute)
**Default value:** `600000` (10 minutes); diff --git a/modules/default/weatherforecast/README.md b/modules/default/weatherforecast/README.md index 2842af54f1..3568f7de15 100644 --- a/modules/default/weatherforecast/README.md +++ b/modules/default/weatherforecast/README.md @@ -8,14 +8,14 @@ To use this module, add it to the modules array in the `config/config.js` file: ````javascript modules: [ { - module: 'weatherforecast', - position: 'top_right', // This can be any of the regions. + module: "weatherforecast", + position: "top_right", // This can be any of the regions. // Best results in left or right regions. config: { // See 'Configuration options' for more information. - location: 'Amsterdam,Netherlands', - locationID: '', //Location ID from http://openweathermap.org/help/city_list.txt - appid: 'abcde12345abcde12345abcde12345ab' //openweathermap.org API key. + location: "Amsterdam,Netherlands", + locationID: "", //Location ID from http://openweathermap.org/help/city_list.txt + appid: "abcde12345abcde12345abcde12345ab" //openweathermap.org API key. } } ] @@ -25,171 +25,50 @@ modules: [ The following properties can be configured: +| Option | Description +| ---------------------------- | ----------- +| `location` | The location used for weather information.

**Example:** `'Amsterdam,Netherlands'`
**Default value:** `false`

**Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used. +| `locationID` | Location ID from [OpenWeatherMap](http://openweathermap.org/help/city_list.txt) **This will override anything you put in location.**
Leave blank if you want to use location.
**Example:** `1234567`
**Default value:** `false`

**Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used. +| `appid` | The [OpenWeatherMap](https://home.openweathermap.org) API key, which can be obtained by creating an OpenWeatherMap account.

This value is **REQUIRED** +| `units` | What units to use. Specified by config.js

**Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` =Fahrenheit
**Default value:** `config.units` +| `roundTemp` | Round temperature values to nearest integer.

**Possible values:** `true` (round to integer) or `false` (display exact value with decimal point)
**Default value:** `false` +| `maxNumberOfDays` | How many days of forecast to return. Specified by config.js

**Possible values:** `1` - `16`
**Default value:** `7` (7 days)
This value is optional. By default the weatherforecast module will return 7 days. +| `showRainAmount` | Should the predicted rain amount be displayed?

**Possible values:** `true` or `false`
**Default value:** `false`
This value is optional. By default the weatherforecast module will not display the predicted amount of rain. +| `updateInterval` | How often does the content needs to be fetched? (Milliseconds)

**Possible values:** `1000` - `86400000`
**Default value:** `600000` (10 minutes) +| `animationSpeed` | Speed of the update animation. (Milliseconds)

**Possible values:**`0` - `5000`
**Default value:** `1000` (1 second) +| `lang` | The language of the days.

**Possible values:** `en`, `nl`, `ru`, etc ...
**Default value:** uses value of _config.language_ +| `fade` | Fade the future events to black. (Gradient)

**Possible values:** `true` or `false`
**Default value:** `true` +| `fadePoint` | Where to start fade?

**Possible values:** `0` (top of the list) - `1` (bottom of list)
**Default value:** `0.25` +| `initialLoadDelay` | The initial delay before loading. If you have multiple modules that use the same API key, you might want to delay one of the requests. (Milliseconds)

**Possible values:** `1000` - `5000`
**Default value:** `2500` (2.5 seconds delay. This delay is used to keep the OpenWeather API happy.) +| `retryDelay` | The delay before retrying after a request failure. (Milliseconds)

**Possible values:** `1000` - `60000`
**Default value:** `2500` +| `apiVersion` | The OpenWeatherMap API version to use.

**Default value:** `2.5` +| `apiBase` | The OpenWeatherMap base URL.

**Default value:** `'http://api.openweathermap.org/data/'` +| `forecastEndpoint` | The OpenWeatherMap API endPoint.

**Default value:** `'forecast/daily'` +| `appendLocationNameToHeader` | If set to `true`, the returned location name will be appended to the header of the module, if the header is enabled. This is mainly intresting when using calender based weather.

**Default value:** `true` +| `calendarClass` | The class for the calender module to base the event based weather information on.

**Default value:** `'calendar'` +| `iconTable` | The conversion table to convert the weather conditions to weather-icons.

**Default value:** view table below + `colored` | If set 'colored' to true the min-temp get a blue tone and the max-temp get a red tone.

**Default value:** `'false'` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionDescription
locationThe location used for weather information.
-
Example: 'Amsterdam,Netherlands' -
Default value: false

- Note: When the location and locationID are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used. -
locationIDLocation ID from OpenWeatherMap This will override anything you put in location.
Leave blank if you want to use location. -
Example: 1234567 -
Default value: false

- Note: When the location and locationID are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used. -
appidThe OpenWeatherMap API key, which can be obtained by creating an OpenWeatherMap account.
-
This value is REQUIRED -
unitsWhat units to use. Specified by config.js
-
Possible values: config.units = Specified by config.js, default = Kelvin, metric = Celsius, imperial =Fahrenheit -
Default value: config.units -
roundTempRound temperature values to nearest integer.
-
Possible values: true (round to integer) or false (display exact value with decimal point) -
Default value: false -
maxNumberOfDaysHow many days of forecast to return. Specified by config.js
-
Possible values: 1 - 16 -
Default value: 7 (7 days) -
This value is optional. By default the weatherforecast module will return 7 days. -
showRainAmountShould the predicted rain amount be displayed?
-
Possible values: true or false -
Default value: false -
This value is optional. By default the weatherforecast module will not display the predicted amount of rain. -
updateIntervalHow often does the content needs to be fetched? (Milliseconds)
-
Possible values: 1000 - 86400000 -
Default value: 600000 (10 minutes) -
animationSpeedSpeed of the update animation. (Milliseconds)
-
Possible values:0 - 5000 -
Default value: 1000 (1 second) -
langThe language of the days.
-
Possible values: en, nl, ru, etc ... -
Default value: uses value of config.language -
fadeFade the future events to black. (Gradient)
-
Possible values: true or false -
Default value: true -
fadePointWhere to start fade?
-
Possible values: 0 (top of the list) - 1 (bottom of list) -
Default value: 0.25 -
initialLoadDelayThe initial delay before loading. If you have multiple modules that use the same API key, you might want to delay one of the requests. (Milliseconds)
-
Possible values: 1000 - 5000 -
Default value: 2500 (2.5 seconds delay. This delay is used to keep the OpenWeather API happy.) -
retryDelayThe delay before retrying after a request failure. (Milliseconds)
-
Possible values: 1000 - 60000 -
Default value: 2500 -
apiVersionThe OpenWeatherMap API version to use.
-
Default value: 2.5 -
apiBaseThe OpenWeatherMap base URL.
-
Default value: 'http://api.openweathermap.org/data/' -
forecastEndpointThe OpenWeatherMap API endPoint.
-
Default value: 'forecast/daily' -
appendLocationNameToHeaderIf set to true, the returned location name will be appended to the header of the module, if the header is enabled. This is mainly intresting when using calender based weather.
-
Default value: true -
calendarClassThe class for the calender module to base the event based weather information on.
-
Default value: 'calendar' -
iconTableThe conversion table to convert the weather conditions to weather-icons.
-
Default value: iconTable: { - '01d':'wi-day-sunny', - '02d':'wi-day-cloudy', - '03d':'wi-cloudy', - '04d':'wi-cloudy-windy', - '09d':'wi-showers', - '10d':'wi-rain', - '11d':'wi-thunderstorm', - '13d':'wi-snow', - '50d':'wi-fog', - '01n':'wi-night-clear', - '02n':'wi-night-cloudy', - '03n':'wi-night-cloudy', - '04n':'wi-night-cloudy', - '09n':'wi-night-showers', - '10n':'wi-night-rain', - '11n':'wi-night-thunderstorm', - '13n':'wi-night-snow', - '50n':'wi-night-alt-cloudy-windy' - } -
+#### Default Icon Table +````javascript +iconTable: { + '01d': 'wi-day-sunny', + '02d': 'wi-day-cloudy', + '03d': 'wi-cloudy', + '04d': 'wi-cloudy-windy', + '09d': 'wi-showers', + '10d': 'wi-rain', + '11d': 'wi-thunderstorm', + '13d': 'wi-snow', + '50d': 'wi-fog', + '01n': 'wi-night-clear', + '02n': 'wi-night-cloudy', + '03n': 'wi-night-cloudy', + '04n': 'wi-night-cloudy', + '09n': 'wi-night-showers', + '10n': 'wi-night-rain', + '11n': 'wi-night-thunderstorm', + '13n': 'wi-night-snow', + '50n': 'wi-night-alt-cloudy-windy' +} +```` diff --git a/modules/default/weatherforecast/weatherforecast.css b/modules/default/weatherforecast/weatherforecast.css index 62c9767f68..85d6568589 100644 --- a/modules/default/weatherforecast/weatherforecast.css +++ b/modules/default/weatherforecast/weatherforecast.css @@ -17,3 +17,11 @@ padding-left: 20px; padding-right: 0; } + +.weatherforecast tr.colored .min-temp { + color: #BCDDFF; +} + +.weatherforecast tr.colored .max-temp { + color: #FF8E99; +} diff --git a/modules/default/weatherforecast/weatherforecast.js b/modules/default/weatherforecast/weatherforecast.js index 9bd15ba8b4..b269a44adc 100644 --- a/modules/default/weatherforecast/weatherforecast.js +++ b/modules/default/weatherforecast/weatherforecast.js @@ -23,6 +23,7 @@ Module.register("weatherforecast",{ lang: config.language, fade: true, fadePoint: 0.25, // Start on 1/4th of the list. + colored: false, initialLoadDelay: 2500, // 2.5 seconds delay. This delay is used to keep the OpenWeather API happy. retryDelay: 2500, @@ -120,6 +121,9 @@ Module.register("weatherforecast",{ var forecast = this.forecast[f]; var row = document.createElement("tr"); + if (this.config.colored) { + row.className = "colored"; + } table.appendChild(row); var dayCell = document.createElement("td"); @@ -150,7 +154,11 @@ Module.register("weatherforecast",{ if (isNaN(forecast.rain)) { rainCell.innerHTML = ""; } else { - rainCell.innerHTML = forecast.rain + " mm"; + if(config.units !== "imperial") { + rainCell.innerHTML = forecast.rain + " mm"; + } else { + rainCell.innerHTML = (parseFloat(forecast.rain) / 25.4).toFixed(2) + " in"; + } } rainCell.className = "align-right bright rain"; row.appendChild(rainCell); diff --git a/modules/node_modules/node_helper/index.js b/modules/node_modules/node_helper/index.js index dc57ef3668..bdeccf8bc2 100644 --- a/modules/node_modules/node_helper/index.js +++ b/modules/node_modules/node_helper/index.js @@ -14,6 +14,11 @@ NodeHelper = Class.extend({ console.log("Initializing new module helper ..."); }, + loaded: function(callback) { + console.log("Module helper loaded: " + this.name); + callback(); + }, + start: function() { console.log("Staring module helper: " + this.name); }, diff --git a/package.json b/package.json index 118c9837b0..c5f6db99e1 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,13 @@ { "name": "magicmirror", - "version": "2.1.0", + "version": "2.1.1", "description": "A modular interface for smart mirrors.", "main": "js/electron.js", "scripts": { - "start": "electron js/electron.js" + "start": "sh run-start.sh", + "postinstall": "sh installers/postinstall/postinstall.sh", + "test": "./node_modules/mocha/bin/mocha $(find tests -path '*js*' ! -ipath '*e2e*')", + "test:e2e": "./node_modules/mocha/bin/mocha tests/e2e --recursive" }, "repository": { "type": "git", @@ -26,12 +29,16 @@ }, "homepage": "https://github.com/MichMich/MagicMirror#readme", "devDependencies": { + "chai": "^3.5.0", + "chai-as-promised": "^6.0.0", "grunt": "latest", "grunt-eslint": "latest", "grunt-jsonlint": "latest", "grunt-markdownlint": "^1.0.13", "grunt-stylelint": "latest", "grunt-yamllint": "latest", + "mocha": "^3.2.0", + "spectron": "^3.4.1", "stylelint-config-standard": "latest", "time-grunt": "latest" }, @@ -44,7 +51,7 @@ "iconv-lite": "latest", "moment": "latest", "request": "^2.78.0", - "rrule": "latest", + "rrule-alt": "^2.2.3", "simple-git": "^1.62.0", "socket.io": "^1.5.1", "valid-url": "latest", diff --git a/run-start.sh b/run-start.sh new file mode 100644 index 0000000000..6c4d400364 --- /dev/null +++ b/run-start.sh @@ -0,0 +1,4 @@ +if [ -z "$DISPLAY" ]; then #If not set DISPLAY is SSH remote or tty + export DISPLAY=:0 # Set by defaul display +fi +electron js/electron.js diff --git a/splashscreen/MagicMirror.script b/splashscreen/MagicMirror.script index 9b6143898b..6e2f5643a9 100644 --- a/splashscreen/MagicMirror.script +++ b/splashscreen/MagicMirror.script @@ -1,7 +1,15 @@ screen_width = Window.GetWidth(); screen_height = Window.GetHeight(); -theme_image = Image("splash.png"); +if (Plymouth.GetMode() != "shutdown") +{ + theme_image = Image("splash.png"); +} +else +{ + theme_image = Image("splash_halt.png"); +} + image_width = theme_image.GetWidth(); image_height = theme_image.GetHeight(); @@ -30,11 +38,8 @@ else image_y = (screen_height - image_height) / 2; } -if (Plymouth.GetMode() != "shutdown") -{ - sprite = Sprite (resized_image); - sprite.SetPosition (image_x, image_y, -100); -} +sprite = Sprite (resized_image); +sprite.SetPosition (image_x, image_y, -100); message_sprite = Sprite(); message_sprite.SetPosition(screen_width * 0.1, screen_height * 0.9, 10000); diff --git a/splashscreen/splash.png b/splashscreen/splash.png index f3da394b8f..b2acc49539 100644 Binary files a/splashscreen/splash.png and b/splashscreen/splash.png differ diff --git a/splashscreen/splash_halt.png b/splashscreen/splash_halt.png new file mode 100644 index 0000000000..dcf9d8be0d Binary files /dev/null and b/splashscreen/splash_halt.png differ diff --git a/tests/confs/env.js b/tests/confs/env.js new file mode 100644 index 0000000000..252934d743 --- /dev/null +++ b/tests/confs/env.js @@ -0,0 +1,25 @@ +/* Magic Mirror Test config sample enviroment + * + * By Rodrigo Ramírez Norambuena https://rodrigoramirez.com + * MIT Licensed. + */ + +var config = { + port: 8080, + ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], + + language: "en", + timeFormat: 24, + units: "metric", + electronOptions: { + webPreferences: { + nodeIntegration: true, + }, + }, + + modules: [ + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") {module.exports = config;} diff --git a/tests/confs/helloworld.js b/tests/confs/helloworld.js new file mode 100644 index 0000000000..6b617cf261 --- /dev/null +++ b/tests/confs/helloworld.js @@ -0,0 +1,32 @@ +/* Magic Mirror Test config sample module hello world + * + * By Rodrigo Ramírez Norambuena https://rodrigoramirez.com + * MIT Licensed. + */ + +var config = { + port: 8080, + ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], + + language: "en", + timeFormat: 24, + units: "metric", + electronOptions: { + webPreferences: { + nodeIntegration: true, + }, + }, + + modules: [ + { + module: "helloworld", + position: "bottom_bar", + config: { + text: "Test HelloWorld Module" + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") {module.exports = config;} diff --git a/tests/e2e/env.js b/tests/e2e/env.js new file mode 100644 index 0000000000..413864993d --- /dev/null +++ b/tests/e2e/env.js @@ -0,0 +1,49 @@ +const Application = require("spectron").Application; +const path = require("path"); +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); + +// Set config sample for use in test +process.env.MM_CONFIG_FILE = "tests/confs/env.js"; + +var electronPath = path.join(__dirname, "../../", "node_modules", ".bin", "electron"); + +if (process.platform === "win32") { + electronPath += ".cmd"; +} + +var appPath = path.join(__dirname, "../../js/electron.js"); + +var app = new Application({ + path: electronPath, + args: [appPath] +}); + +global.before(function () { + chai.should(); + chai.use(chaiAsPromised); +}); + +describe("Test enviroment app electron", function () { + this.timeout(10000); + + beforeEach(function (done) { + app.start().then(function() { done(); } ); + }); + + afterEach(function (done) { + app.stop().then(function() { done(); }); + }); + + + it("open a window app and test if is open", function () { + return app.client.waitUntilWindowLoaded() + .getWindowCount().should.eventually.equal(1); + }); + + it("tests the title", function () { + return app.client.waitUntilWindowLoaded() + .getTitle().should.eventually.equal("Magic Mirror"); + }); + +}); diff --git a/tests/e2e/modules/helloworld.js b/tests/e2e/modules/helloworld.js new file mode 100644 index 0000000000..ebf8a8d16a --- /dev/null +++ b/tests/e2e/modules/helloworld.js @@ -0,0 +1,42 @@ +const Application = require("spectron").Application; +const path = require("path"); +const chai = require("chai"); +const chaiAsPromised = require("chai-as-promised"); + +// Set config sample for use in test +process.env.MM_CONFIG_FILE = "tests/confs/helloworld.js"; + +var electronPath = path.join(__dirname, "../../../", "node_modules", ".bin", "electron"); + +if (process.platform === "win32") { + electronPath += ".cmd"; +} + +var appPath = path.join(__dirname, "../../../js/electron.js"); + +var app = new Application({ + path: electronPath, + args: [appPath] +}); + +global.before(function () { + chai.should(); + chai.use(chaiAsPromised); +}); + +describe("Test helloworld module", function () { + this.timeout(10000); + + beforeEach(function (done) { + app.start().then(function() { done(); } ); + }); + + afterEach(function (done) { + app.stop().then(function() { done(); }); + }); + + it("Test message helloworld module", function () { + return app.client.waitUntilWindowLoaded() + .getText(".helloworld").should.eventually.equal("Test HelloWorld Module"); + }); +}); diff --git a/tests/functions/compare-version.js b/tests/functions/compare-version.js new file mode 100644 index 0000000000..8cc22101e8 --- /dev/null +++ b/tests/functions/compare-version.js @@ -0,0 +1,20 @@ +var chai = require("chai"); +var expect = chai.expect; +var classMM = require("../../js/class.js"); // require for load module.js +var moduleMM = require("../../js/module.js") + +describe("Test function cmpVersions in js/module.js", function() { + + it("should return -1 when comparing 2.1 to 2.2", function() { + expect(moduleMM._test.cmpVersions("2.1", "2.2")).to.equal(-1); + }); + + it("should be return 0 when comparing 2.2 to 2.2", function() { + expect(moduleMM._test.cmpVersions("2.2", "2.2")).to.equal(0); + }); + + it("should be return 1 when comparing 1.1 to 1.0", function() { + expect(moduleMM._test.cmpVersions("1.1", "1.0")).to.equal(1); + }); +}); + diff --git a/tests/global_vars/root_path.js b/tests/global_vars/root_path.js new file mode 100644 index 0000000000..04edc03f45 --- /dev/null +++ b/tests/global_vars/root_path.js @@ -0,0 +1,25 @@ +var fs = require("fs"); +var path = require("path"); +var chai = require("chai"); +var expect = chai.expect; + +describe("Test global.root_path, set in js/app.js", function() { + var appMM = require("../../js/app.js") + + var expectedSubPaths = [ + "modules", + "serveronly", + "js", + "js/app.js", + "js/main.js", + "js/electron.js", + "config" + ]; + + expectedSubPaths.forEach(subpath => { + it(`should contain a file/folder "${subpath}"`, function() { + expect(fs.existsSync(path.join(global.root_path, subpath))).to.equal(true); + }); + }); +}); + diff --git a/translations/af.json b/translations/af.json new file mode 100644 index 0000000000..0995bdf0c7 --- /dev/null +++ b/translations/af.json @@ -0,0 +1,30 @@ +{ + "LOADING": "Besig om te laai …", + + "TODAY": "Vandag", + "TOMORROW": "Môre", + "DAYAFTERTOMORROW": "Oormôre", + "RUNNING": "Eindig in", + "EMPTY": "Geen komende gebeurtenisse.", + + "N": "N", + "NNE": "NNO", + "NE": "NO", + "ENE": "ONO", + "E": "O", + "ESE": "OSO", + "SE": "SO", + "SSE": "SSO", + "S": "S", + "SSW": "SSW", + "SW": "SW", + "WSW": "WSW", + "W": "W", + "WNW": "WNW", + "NW": "NW", + "NNW": "NNW", + + "UPDATE_NOTIFICATION": "MagicMirror² update beskikbaar.", + "UPDATE_NOTIFICATION_MODULE": "Update beskikbaar vir MODULE_NAME module.", + "UPDATE_INFO": "Die huidige installasie is COMMIT_COUNT agter op die BRANCH_NAME branch." +} diff --git a/translations/da.json b/translations/da.json index 4b346437eb..68c544a663 100644 --- a/translations/da.json +++ b/translations/da.json @@ -1,15 +1,12 @@ { - /* GENERAL */ "LOADING": "Indlæser …", - /* CALENDAR */ "TODAY": "I dag", "TOMORROW": "I morgen", "DAYAFTERTOMORROW": "I overmorgen", "RUNNING": "Slutter om", "EMPTY": "Ingen kommende begivenheder.", - /* WEATHER */ "N": "N", "NNE": "NNØ", "NE": "NØ", @@ -25,10 +22,9 @@ "W": "V", "WNW": "VNV", "NW": "NV", - "NNW": "NNV" + "NNW": "NNV", - /* UPDATE INFO */ "UPDATE_NOTIFICATION": "MagicMirror² opdatering tilgængelig.", "UPDATE_NOTIFICATION_MODULE": "Opdatering tilgængelig for MODULE_NAME modulet.", "UPDATE_INFO": "Den nuværende installation er COMMIT_COUNT bagud på BRANCH_NAME branch'en." diff --git a/translations/de.json b/translations/de.json index 54ac5015a0..eb67d41a2e 100644 --- a/translations/de.json +++ b/translations/de.json @@ -1,15 +1,12 @@ { - /* GENERAL */ "LOADING": "Lade …", - /* CALENDAR */ "TODAY": "Heute", "TOMORROW": "Morgen", "DAYAFTERTOMORROW": "Übermorgen", "RUNNING": "noch", "EMPTY": "Keine Termine.", - /* WEATHER */ "N": "N", "NNE": "NNO", "NE": "NO", @@ -27,7 +24,6 @@ "NW": "NW", "NNW": "NNW", - /* UPDATE INFO */ "UPDATE_NOTIFICATION": "Aktualisierung für MagicMirror² verfügbar.", "UPDATE_NOTIFICATION_MODULE": "Aktualisierung für das MODULE_NAME Modul verfügbar.", "UPDATE_INFO": "Die aktuelle Installation ist COMMIT_COUNT hinter dem BRANCH_NAME branch." diff --git a/translations/en.json b/translations/en.json index 45609f6159..460617386e 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1,15 +1,12 @@ { - /* GENERAL */ "LOADING": "Loading …", - /* CALENDAR */ "TODAY": "Today", "TOMORROW": "Tomorrow", "DAYAFTERTOMORROW": "The day after tomorrow", "RUNNING": "Ends in", "EMPTY": "No upcoming events.", - /* WEATHER */ "N": "N", "NNE": "NNE", "NE": "NE", @@ -27,7 +24,6 @@ "NW": "NW", "NNW": "NNW", - /* UPDATE INFO */ "UPDATE_NOTIFICATION": "MagicMirror² update available.", "UPDATE_NOTIFICATION_MODULE": "Update available for MODULE_NAME module.", "UPDATE_INFO": "The current installation is COMMIT_COUNT behind on the BRANCH_NAME branch." diff --git a/translations/es.json b/translations/es.json index 9b80b45aee..914a72ce95 100644 --- a/translations/es.json +++ b/translations/es.json @@ -1,15 +1,12 @@ { - /* GENERAL */ "LOADING": "Cargando …", - /* CALENDAR */ "TODAY": "Hoy", "TOMORROW": "Mañana", "DAYAFTERTOMORROW": "Pasado mañana", "RUNNING": "Termina en", "EMPTY": "No hay eventos programados.", - /* WEATHER */ "N": "N", "NNE": "NNE", "NE": "NE", @@ -27,7 +24,6 @@ "NW": "NO", "NNW": "NNO", - /* UPDATE INFO */ "UPDATE_NOTIFICATION": "MagicMirror² actualización disponible.", "UPDATE_NOTIFICATION_MODULE": "Disponible una actualización para el módulo MODULE_NAME.", "UPDATE_INFO": "Tu actual instalación está COMMIT_COUNT cambios detrás de la rama BRANCH_NAME." diff --git a/translations/fi.json b/translations/fi.json index b753039eac..08bc40601b 100644 --- a/translations/fi.json +++ b/translations/fi.json @@ -1,14 +1,12 @@ { - /* GENERAL */ "LOADING": "Lataa …", - /* CALENDAR */ "TODAY": "Tänään", "TOMORROW": "Huomenna", + "DAYAFTERTOMORROW": "Ylihuomenna", "RUNNING": "Meneillään", "EMPTY": "Ei tulevia tapahtumia.", - /* WEATHER */ "N": "P", "NNE": "PPI", "NE": "PI", @@ -24,5 +22,8 @@ "W": "L", "WNW": "LPL", "NW": "PL", - "NNW": "PPL" + "NNW": "PPL", + + "UPDATE_NOTIFICATION": "MagicMirror² päivitys saatavilla.", + "UPDATE_NOTIFICATION_MODULE": "Päivitys saatavilla moduulille MODULE_NAME." } diff --git a/translations/fr.json b/translations/fr.json index 2701eb084d..d27bc7e60a 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -1,14 +1,11 @@ { - /* GENERAL */ "LOADING": "Chargement …", - /* CALENDAR */ "TODAY": "Aujourd'hui", "TOMORROW": "Demain", "RUNNING": "Se termine dans", "EMPTY": "Aucun RDV.", - /* WEATHER */ "N": "N", "NNE": "NNE", "NE": "NE", diff --git a/translations/fy.json b/translations/fy.json index 44f1b476e9..f1735382f7 100644 --- a/translations/fy.json +++ b/translations/fy.json @@ -1,15 +1,12 @@ { - /* GENERAL */ "LOADING": "Bezich mei laden …", - /* CALENDAR */ "TODAY": "Hjoed", "TOMORROW": "Moarn", "DAYAFTERTOMORROW": "Oaremoarn", "RUNNING": "Einigest oer", "EMPTY": "Gjin plande ôfspraken.", - /* WEATHER */ "N": "N", "NNE": "NNE", "NE": "NE", diff --git a/translations/gr.json b/translations/gr.json index 6b9c92ac16..a8f5ccbddb 100644 --- a/translations/gr.json +++ b/translations/gr.json @@ -1,14 +1,11 @@ { - /* GENERAL */ "LOADING": "Φόρτωση …", - /* CALENDAR */ "TODAY": "Σήμερα", "TOMORROW": "Αύριο", "RUNNING": "Λήγει σε", "EMPTY": "Δεν υπάρχουν προσεχείς εκδηλώσεις.", - /* WEATHER */ "N": "B", "NNE": "BBA", "NE": "BA", diff --git a/translations/hu.json b/translations/hu.json new file mode 100644 index 0000000000..de18d82e7b --- /dev/null +++ b/translations/hu.json @@ -0,0 +1,30 @@ +{ + "LOADING": "Betöltés …", + + "TODAY": "Ma", + "TOMORROW": "Holnap", + "DAYAFTERTOMORROW": "Holnapután", + "RUNNING": "Vége lesz", + "EMPTY": "Nincs közelgő esemény.", + + "N": "É", + "NNE": "ÉÉK", + "NE": "ÉK", + "ENE": "KÉK", + "E": "K", + "ESE": "KDK", + "SE": "DK", + "SSE": "DDK", + "S": "D", + "SSW": "DDNy", + "SW": "DNy", + "WSW": "NyDNy", + "W": "Ny", + "WNW": "NyÉNy", + "NW": "ÉNy", + "NNW": "ÉÉNy", + + "UPDATE_NOTIFICATION": "MagicMirror² elérhető egy frissítés!", + "UPDATE_NOTIFICATION_MODULE": "A frissítés MODULE_NAME modul néven érhető el.", + "UPDATE_INFO": "A jelenlegi telepítés COMMIT_COUNT mögött BRANCH_NAME ágon található." +} diff --git a/translations/is.json b/translations/is.json new file mode 100644 index 0000000000..aabc399730 --- /dev/null +++ b/translations/is.json @@ -0,0 +1,30 @@ +{ + "LOADING": "Hleð upp …", + + "TODAY": "Í dag", + "TOMORROW": "Á morgun", + "DAYAFTERTOMORROW": "Ekki á morgun, heldur hinn", + "RUNNING": "Endar eftir", + "EMPTY": "Ekkert framundan.", + + "N": "N", + "NNE": "NNA", + "NE": "NA", + "ENE": "ANA", + "E": "A", + "ESE": "ASA", + "SE": "SA", + "SSE": "SSA", + "S": "S", + "SSW": "SSV", + "SW": "SV", + "WSW": "VSV", + "W": "V", + "WNW": "VNV", + "NW": "NV", + "NNW": "NNV", + + "UPDATE_NOTIFICATION": "MagicMirror² uppfærsla í boði.", + "UPDATE_NOTIFICATION_MODULE": "Uppfærsla í boði fyrir MODULE_NAME module.", + "UPDATE_INFO": "Núverandi kerfi er COMMIT_COUNT á eftir BRANCH_NAME branchinu." +} diff --git a/translations/it.json b/translations/it.json index bde608b46b..4e3041da3a 100644 --- a/translations/it.json +++ b/translations/it.json @@ -1,14 +1,11 @@ { - /* GENERAL */ "LOADING": "Caricamento in corso …", - /* CALENDAR */ "TODAY": "Oggi", "TOMORROW": "Domani", "RUNNING": "Termina entro", "EMPTY": "Nessun evento in arrivo.", - /* WEATHER */ "N": "N", "NNE": "NNE", "NE": "NE", diff --git a/translations/ja.json b/translations/ja.json index 728495598b..ff15687b62 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -1,14 +1,11 @@ { - /* GENERAL */ - "LOADING": "ローディング …", + "LOADING": "ローディング …", - /* CALENDAR */ "TODAY": "今日", "TOMORROW": "明日", "RUNNING": "で終わります", "EMPTY": "直近のイベントはありません", - /* WEATHER */ "N": "北", "NNE": "北北東", "NE": "北東", diff --git a/translations/nb.json b/translations/nb.json index 48f0ac6fe5..1b57a5fa6d 100644 --- a/translations/nb.json +++ b/translations/nb.json @@ -1,14 +1,11 @@ { - /* GENERAL */ "LOADING": "Laster …", - /* CALENDAR */ "TODAY": "I dag", "TOMORROW": "I morgen", "RUNNING": "Slutter om", "EMPTY": "Ingen kommende arrangementer.", - /* WEATHER */ "N": "N", "NNE": "NNØ", "NE": "NØ", diff --git a/translations/nl.json b/translations/nl.json index 803db06c4b..c615903b49 100644 --- a/translations/nl.json +++ b/translations/nl.json @@ -1,15 +1,12 @@ { - /* GENERAL */ "LOADING": "Bezig met laden …", - /* CALENDAR */ "TODAY": "Vandaag", "TOMORROW": "Morgen", "DAYAFTERTOMORROW": "Overmorgen", "RUNNING": "Eindigt over", "EMPTY": "Geen geplande afspraken.", - /* WEATHER */ "N": "N", "NNE": "NNO", "NE": "NO", @@ -27,7 +24,6 @@ "NW": "NW", "NNW": "NNW", - /* UPDATE INFO */ "UPDATE_NOTIFICATION": "MagicMirror² update beschikbaar.", "UPDATE_NOTIFICATION_MODULE": "Update beschikbaar voor MODULE_NAME module.", "UPDATE_INFO": "De huidige installatie loopt COMMIT_COUNT achter op de BRANCH_NAME branch." diff --git a/translations/nn.json b/translations/nn.json index b7dbe3ea16..107bec9da9 100644 --- a/translations/nn.json +++ b/translations/nn.json @@ -1,14 +1,11 @@ { - /* GENERAL */ "LOADING": "Lastar …", - /* CALENDAR */ "TODAY": "I dag", "TOMORROW": "I morgon", "RUNNING": "Sluttar om", "EMPTY": "Ingen komande hendingar.", - /* WEATHER */ "N": "N", "NNE": "NNA", "NE": "NA", diff --git a/translations/pl.json b/translations/pl.json index 7a47745bea..0c79835f87 100644 --- a/translations/pl.json +++ b/translations/pl.json @@ -1,14 +1,11 @@ { - /* GENERAL */ "LOADING": "Ładowanie …", - /* CALENDAR */ "TODAY": "Dziś", "TOMORROW": "Jutro", "RUNNING": "Koniec za", "EMPTY": "Brak wydarzeń.", - /* WEATHER */ "N": "N", "NNE": "NNE", "NE": "NE", @@ -24,10 +21,9 @@ "W": "W", "WNW": "WNW", "NW": "NW", - "NNW": "NNW" - - /* UPDATE INFO */ + "NNW": "NNW", + "UPDATE_NOTIFICATION": "Dostępna jest aktualizacja MagicMirror².", "UPDATE_NOTIFICATION_MODULE": "Dostępna jest aktualizacja modułu MODULE_NAME.", - "UPDATE_INFO": "The current installation is COMMIT_COUNT behind on the BRANCH_NAME branch." + "UPDATE_INFO": "Zainstalowana wersja odbiega o COMMIT_COUNT commitów od gałęzi BRANCH_NAME." } diff --git a/translations/pt.json b/translations/pt.json index f8797f0f4d..35de1326ef 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -1,14 +1,11 @@ { - /* GENERAL */ "LOADING": "A carregar …", - /* CALENDAR */ "TODAY": "Hoje", "TOMORROW": "Amanhã", "RUNNING": "Termina em", "EMPTY": "Sem eventos a chegar.", - /* WEATHER */ "N": "N", "NNE": "NNE", "NE": "NE", diff --git a/translations/pt_br.json b/translations/pt_br.json index 17d3e63636..92002b549b 100644 --- a/translations/pt_br.json +++ b/translations/pt_br.json @@ -1,14 +1,11 @@ { - /* GENERAL */ "LOADING": "Carregando …", - /* CALENDAR */ "TODAY": "Hoje", "TOMORROW": "Amanhã", "RUNNING": "Acaba em", "EMPTY": "Nenhum evento novo.", - /* WEATHER */ "N": "N", "NNE": "NNE", "NE": "NE", diff --git a/translations/ru.json b/translations/ru.json new file mode 100644 index 0000000000..48c49fd9ee --- /dev/null +++ b/translations/ru.json @@ -0,0 +1,30 @@ +{ + "LOADING": "Загрузка …", + + "TODAY": "Сегодня", + "TOMORROW": "Завтра", + "DAYAFTERTOMORROW": "Послезавтра", + "RUNNING": "Заканчивается через", + "EMPTY": "Нет предстоящих событий", + + "N": "С", + "NNE": "ССВ", + "NE": "СВ", + "ENE": "ВСВ", + "E": "В", + "ESE": "ВЮВ", + "SE": "ЮВ", + "SSE": "ЮЮВ", + "S": "Ю", + "SSW": "ЮЮЗ", + "SW": "ЮЗ", + "WSW": "ЗЮЗ", + "W": "З", + "WNW": "ЗСЗ", + "NW": "СЗ", + "NNW": "ССЗ", + + "UPDATE_NOTIFICATION": "Есть обновление для MagicMirror².", + "UPDATE_NOTIFICATION_MODULE": "Есть обновление для MODULE_NAME модуля.", + "UPDATE_INFO": "Данная инсталляция позади BRANCH_NAME ветки на COMMIT_COUNT коммитов." +} diff --git a/translations/sv.json b/translations/sv.json index 9643a07b43..1fe3d48d32 100644 --- a/translations/sv.json +++ b/translations/sv.json @@ -1,14 +1,12 @@ { - /* GENERAL */ "LOADING": "Laddar …", - /* CALENDAR */ "TODAY": "Idag", "TOMORROW": "Imorgon", + "DAYAFTERTOMORROW": "Iövermorgon", "RUNNING": "Slutar", "EMPTY": "Inga kommande händelser.", - /* WEATHER */ "N": "N", "NNE": "NNO", "NE": "NO", @@ -24,5 +22,9 @@ "W": "V", "WNW": "VNV", "NW": "NV", - "NNW": "NNV" + "NNW": "NNV", + + "UPDATE_NOTIFICATION": "MagicMirror² uppdatering finns tillgänglig.", + "UPDATE_NOTIFICATION_MODULE": "Uppdatering finns tillgänglig av MODULE_NAME modulen.", + "UPDATE_INFO": "Denna installation ligger COMMIT_COUNT steg bakom BRANCH_NAME grenen." } diff --git a/translations/tr.json b/translations/tr.json index a0d61ac706..c79956b86f 100644 --- a/translations/tr.json +++ b/translations/tr.json @@ -1,14 +1,11 @@ { - /* GENERAL */ "LOADING": "Yükleniyor …", - /* CALENDAR */ "TODAY": "Bugün", "TOMORROW": "Yarın", "RUNNING": "Biten", "EMPTY": "Yakında etkinlik yok.", - /* WEATHER */ "N": "K", "NNE": "KKD", "NE": "KD", diff --git a/translations/translations.js b/translations/translations.js index d572c80335..c10da418d0 100644 --- a/translations/translations.js +++ b/translations/translations.js @@ -26,4 +26,8 @@ var translations = { "gr" : "translations/gr.json", // Greek "da" : "translations/da.json", // Danish "tr" : "translations/tr.json", // Turkish + "ru" : "translations/ru.json", // Russian + "af" : "translations/af.json", // Afrikaans + "hu" : "translations/hu.json", // Hungarian + "is" : "translations/is.json", // Icelandic }; diff --git a/translations/zh_cn.json b/translations/zh_cn.json index a8c32d7083..e7f6961610 100644 --- a/translations/zh_cn.json +++ b/translations/zh_cn.json @@ -1,14 +1,11 @@ { - /* GENERAL */ "LOADING": "正在加载 …", - /* CALENDAR */ "TODAY": "今天", "TOMORROW": "明天", "RUNNING": "结束日期", "EMPTY": "没有更多的活动。", - /* WEATHER */ "N": "北风", "NNE": "北偏东风", "NE": "东北风", diff --git a/translations/zh_tw.json b/translations/zh_tw.json index 1a5827bede..550de453ad 100644 --- a/translations/zh_tw.json +++ b/translations/zh_tw.json @@ -1,14 +1,11 @@ { - /* GENERAL */ "LOADING": "正在加載 …", - /* CALENDAR */ "TODAY": "今天", "TOMORROW": "明天", "RUNNING": "結束日期", "EMPTY": "沒有更多的活動。", - /* WEATHER */ "N": "北風", "NNE": "北偏東風", "NE": "東北風",