@@ -7,6 +7,7 @@
|
|||||||
"curly": "error",
|
"curly": "error",
|
||||||
"camelcase": ["error", {"properties": "never"}],
|
"camelcase": ["error", {"properties": "never"}],
|
||||||
"no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 1 }],
|
"no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 1 }],
|
||||||
|
"no-multi-spaces": "error",
|
||||||
"no-trailing-spaces": ["error", {"ignoreComments": false }],
|
"no-trailing-spaces": ["error", {"ignoreComments": false }],
|
||||||
"no-irregular-whitespace": ["error"]
|
"no-irregular-whitespace": ["error"]
|
||||||
},
|
},
|
||||||
|
21
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,15 +1,28 @@
|
|||||||
Please only submit reproducible issues.
|
## I'm not sure if this is a bug
|
||||||
|
|
||||||
If you're not sure if it's a real bug or if it's just you, please open a topic on the forum: [https://forum.magicmirror.builders/category/15/bug-hunt](https://forum.magicmirror.builders/category/15/bug-hunt)
|
If you're not sure if it's a real bug or if it's just you, please open a topic on the forum: [https://forum.magicmirror.builders/category/15/bug-hunt](https://forum.magicmirror.builders/category/15/bug-hunt)
|
||||||
|
|
||||||
|
## I'm having troubles installing or configuring MagicMirror
|
||||||
Problems installing or configuring your MagicMirror? Check out: [https://forum.magicmirror.builders/category/10/troubleshooting](https://forum.magicmirror.builders/category/10/troubleshooting)
|
Problems installing or configuring your MagicMirror? Check out: [https://forum.magicmirror.builders/category/10/troubleshooting](https://forum.magicmirror.builders/category/10/troubleshooting)
|
||||||
|
|
||||||
|
## I found a bug in the MagicMirror installer
|
||||||
|
If you are facing an issue or found a bug while trying to install MagicMirror via the installer please report it in the respective GitHub repository:
|
||||||
|
[https://github.com/sdetweil/MagicMirror_scripts](https://github.com/sdetweil/MagicMirror_scripts)
|
||||||
|
|
||||||
|
## I found a bug in the MagicMirror Docker image
|
||||||
|
If you are facing an issue or found a bug while running MagicMirror inside a Docker container please create an issue in the GitHub repository of the MagicMirror Docker image:
|
||||||
|
[https://github.com/bastilimbach/docker-MagicMirror](https://github.com/bastilimbach/docker-MagicMirror)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## I found a bug in MagicMirror
|
||||||
|
Please make sure to only submit reproducible issues. You can safely remove everything above the dividing line.
|
||||||
When submitting a new issue, please supply the following information:
|
When submitting a new issue, please supply the following information:
|
||||||
|
|
||||||
**Platform**: Place your platform here... give us your web browser/Electron version *and* your hardware (Raspberry Pi 2/3, Windows, Mac, Linux, System V UNIX).
|
**Platform**: Place your platform here... give us your web browser/Electron version *and* your hardware (Raspberry Pi 2/3, Windows, Mac, Linux, System V UNIX).
|
||||||
|
|
||||||
**Node Version**: Make sure it's version 0.12.13 or later.
|
**Node Version**: Make sure it's version 8 or later.
|
||||||
|
|
||||||
**MagicMirror Version**: Now that the versions have split, tell us if you are using the PHP version (v1) or the newer JavaScript version (v2).
|
**MagicMirror Version**: Please let us now which version of MagicMirror you are running. It can be found in the `package.log` file.
|
||||||
|
|
||||||
**Description**: Provide a detailed description about the issue and include specific details to help us understand the problem. Adding screenshots will help describing the problem.
|
**Description**: Provide a detailed description about the issue and include specific details to help us understand the problem. Adding screenshots will help describing the problem.
|
||||||
|
|
||||||
|
@@ -1,19 +1,22 @@
|
|||||||
dist: trusty
|
dist: trusty
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- "10"
|
- 10
|
||||||
|
- lts/*
|
||||||
|
- node
|
||||||
before_install:
|
before_install:
|
||||||
- npm i -g npm
|
- npm i -g npm
|
||||||
before_script:
|
before_script:
|
||||||
- yarn danger ci
|
- yarn danger ci
|
||||||
- npm install grunt-cli -g
|
- npm install grunt-cli -g
|
||||||
- "export DISPLAY=:99.0"
|
- "export DISPLAY=:99.0"
|
||||||
|
- "export ELECTRON_DISABLE_SANDBOX=1"
|
||||||
- "sh -e /etc/init.d/xvfb start"
|
- "sh -e /etc/init.d/xvfb start"
|
||||||
- sleep 5
|
- sleep 5
|
||||||
script:
|
script:
|
||||||
|
- npm run test:lint
|
||||||
- npm run test:e2e
|
- npm run test:e2e
|
||||||
- npm run test:unit
|
- npm run test:unit
|
||||||
- grunt
|
|
||||||
after_script:
|
after_script:
|
||||||
- npm list
|
- npm list
|
||||||
cache:
|
cache:
|
||||||
|
59
CHANGELOG.md
Executable file → Normal file
@@ -3,7 +3,54 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror² core.
|
❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror²
|
||||||
|
|
||||||
|
## [2.11.0] - 2020-04-01
|
||||||
|
|
||||||
|
🚨 READ THIS BEFORE UPDATING 🚨
|
||||||
|
|
||||||
|
In the past years the project has grown a lot. This came with a huge downside: poor maintainability. If I let the project continue the way it was, it would eventually crash and burn. More important: I would completely lose the drive and interest to continue the project. Because of this the decision was made to simplify the core by removing all side features like automatic installers and support for exotic platforms. This release (2.11.0) is the first real release that will reflect (parts) of these changes. As a result of this, some things might break. So before you continue make sure to backup your installation. Your config, your modules or better yet: your full MagicMirror folder. In other words: update at your own risk.
|
||||||
|
|
||||||
|
For more information regarding this major change, please check issue [#1860](https://github.com/MichMich/MagicMirror/issues/1860).
|
||||||
|
|
||||||
|
### Deleted
|
||||||
|
- Remove installers.
|
||||||
|
- Remove externalized scripts.
|
||||||
|
- Remove jshint dependency, instead eslint checks your config file now
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Brazilian translation for "FEELS".
|
||||||
|
- Ukrainian translation.
|
||||||
|
- Finnish translation for "PRECIP", "UPDATE_INFO_MULTIPLE" and "UPDATE_INFO_SINGLE".
|
||||||
|
- Added the ability to hide the temp label and weather icon in the `currentweather` module to allow showing only information such as wind and sunset/rise.
|
||||||
|
- The `clock` module now optionally displays sun and moon data, including rise/set times, remaining daylight, and percent of moon illumination.
|
||||||
|
- Added Hebrew translation.
|
||||||
|
- Add HTTPS support and update config.js.sample
|
||||||
|
- Run tests on long term support and latest stable version of nodejs
|
||||||
|
- Added the ability to configure a list of modules that shouldn't be update checked.
|
||||||
|
- Run linters on git commits
|
||||||
|
- Added date functionality to compliments: display birthday wishes or celebrate an anniversary
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Force declaration of public ip address in config file (ISSUE #1852)
|
||||||
|
- Fixes `run-start.sh`: If running in docker-container, don't check the environment, just start electron (ISSUE #1859)
|
||||||
|
- Fix calendar time offset for recurring events crossing Daylight Savings Time (ISSUE #1798)
|
||||||
|
- Fix regression in currentweather module causing 'undefined' to show up when config.hideTemp is false
|
||||||
|
- Fix FEELS translation for Croatian
|
||||||
|
- Fixed weather tests [#1840](https://github.com/MichMich/MagicMirror/issues/1840)
|
||||||
|
- Fixed Socket.io can't be used with Reverse Proxy in serveronly mode [#1934](https://github.com/MichMich/MagicMirror/issues/1934)
|
||||||
|
- Fix update checking skipping 3rd party modules the first time
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Remove documentation from core repository and link to new dedicated docs site: [docs.magicmirror.builders](https://docs.magicmirror.builders).
|
||||||
|
- Updated config.js.sample: Corrected some grammar on `config.js.sample` comment section.
|
||||||
|
- Removed `run-start.sh` script and update start commands:
|
||||||
|
- To start using electron, use `npm run start`.
|
||||||
|
- To start in server only mode, use `npm run server`.
|
||||||
|
- Remove redundant logging from modules.
|
||||||
|
- Timestamp in log output now also contains the date
|
||||||
|
- Turkish translation.
|
||||||
|
- Option to configure the size of the currentweather module.
|
||||||
|
|
||||||
## [2.10.1] - 2020-01-10
|
## [2.10.1] - 2020-01-10
|
||||||
|
|
||||||
@@ -12,6 +59,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## [2.10.0] - 2020-01-01
|
## [2.10.0] - 2020-01-01
|
||||||
|
|
||||||
|
Special thanks to @sdetweil for all his great contributions!
|
||||||
|
|
||||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`.
|
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -32,9 +81,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Fixed issue in weatherforecast module where predicted amount of rain was not using the decimal symbol specified in config.js.
|
- Fixed issue in weatherforecast module where predicted amount of rain was not using the decimal symbol specified in config.js.
|
||||||
- Module header now updates correctly, if a module need to dynamically show/hide its header based on a condition.
|
- Module header now updates correctly, if a module need to dynamically show/hide its header based on a condition.
|
||||||
- Fix handling of config.js for serverOnly mode commented out.
|
- Fix handling of config.js for serverOnly mode commented out.
|
||||||
- Fixed issue in calendar module where the debug script didn't work correctly with authentication
|
- Fixed issue in calendar module where the debug script didn't work correctly with authentication.
|
||||||
- Fixed issue that some full day events were not correctly recognized as such
|
- Fixed issue that some full day events were not correctly recognized as such.
|
||||||
- Display full day events lasting multiple days as happening today instead of some days ago if they are still ongoing
|
- Display full day events lasting multiple days as happening today instead of some days ago if they are still ongoing.
|
||||||
|
|
||||||
## [2.9.0] - 2019-10-01
|
## [2.9.0] - 2019-10-01
|
||||||
|
|
||||||
@@ -541,7 +590,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
|
|||||||
- Added option `remoteFile` to compliments module to load compliment array from filesystem.
|
- Added option `remoteFile` to compliments module to load compliment array from filesystem.
|
||||||
- Added option `zoom` to scale the whole mirror display with a given factor.
|
- Added option `zoom` to scale the whole mirror display with a given factor.
|
||||||
- Added option `roundTemp` for currentweather and weatherforecast modules to display temperatures rounded to nearest integer.
|
- Added option `roundTemp` for currentweather and weatherforecast modules to display temperatures rounded to nearest integer.
|
||||||
- Added abilty set the classes option to compliments module for style and text size of compliments.
|
- Added ability set the classes option to compliments module for style and text size of compliments.
|
||||||
- Added ability to configure electronOptions
|
- Added ability to configure electronOptions
|
||||||
- Calendar module: option to hide private events
|
- Calendar module: option to hide private events
|
||||||
- Add root_path for global vars
|
- Add root_path for global vars
|
||||||
|
13
Gruntfile.js
@@ -1,10 +1,10 @@
|
|||||||
module.exports = function(grunt) {
|
module.exports = function(grunt) {
|
||||||
require("time-grunt")(grunt);
|
require("time-grunt")(grunt);
|
||||||
|
var fix = (grunt.option("env") || "lint") === "lint";
|
||||||
grunt.initConfig({
|
grunt.initConfig({
|
||||||
pkg: grunt.file.readJSON("package.json"),
|
|
||||||
eslint: {
|
eslint: {
|
||||||
options: {
|
options: {
|
||||||
fix: "true",
|
fix: fix,
|
||||||
configFile: ".eslintrc.json"
|
configFile: ".eslintrc.json"
|
||||||
},
|
},
|
||||||
target: [
|
target: [
|
||||||
@@ -20,13 +20,13 @@ module.exports = function(grunt) {
|
|||||||
"!modules/default/alert/classie.js",
|
"!modules/default/alert/classie.js",
|
||||||
"config/*",
|
"config/*",
|
||||||
"translations/translations.js",
|
"translations/translations.js",
|
||||||
"vendor/vendor.js",
|
"vendor/vendor.js"
|
||||||
"modules/node_modules/node_helper/index.js"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
stylelint: {
|
stylelint: {
|
||||||
simple: {
|
simple: {
|
||||||
options: {
|
options: {
|
||||||
|
fix: fix,
|
||||||
configFile: ".stylelintrc.json"
|
configFile: ".stylelintrc.json"
|
||||||
},
|
},
|
||||||
src: [
|
src: [
|
||||||
@@ -44,7 +44,6 @@ module.exports = function(grunt) {
|
|||||||
"package.json",
|
"package.json",
|
||||||
".eslintrc.json",
|
".eslintrc.json",
|
||||||
".stylelintrc.json",
|
".stylelintrc.json",
|
||||||
"installers/pm2_MagicMirror.json",
|
|
||||||
"translations/*.json",
|
"translations/*.json",
|
||||||
"modules/default/*/translations/*.json",
|
"modules/default/*/translations/*.json",
|
||||||
"vendor/package.json"
|
"vendor/package.json"
|
||||||
@@ -77,7 +76,8 @@ module.exports = function(grunt) {
|
|||||||
"MD018": false,
|
"MD018": false,
|
||||||
"MD012": false,
|
"MD012": false,
|
||||||
"MD026": false,
|
"MD026": false,
|
||||||
"MD038": false
|
"MD038": false,
|
||||||
|
"MD047": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
src: [
|
src: [
|
||||||
@@ -101,5 +101,6 @@ module.exports = function(grunt) {
|
|||||||
grunt.loadNpmTasks("grunt-jsonlint");
|
grunt.loadNpmTasks("grunt-jsonlint");
|
||||||
grunt.loadNpmTasks("grunt-yamllint");
|
grunt.loadNpmTasks("grunt-yamllint");
|
||||||
grunt.loadNpmTasks("grunt-markdownlint");
|
grunt.loadNpmTasks("grunt-markdownlint");
|
||||||
|
|
||||||
grunt.registerTask("default", ["eslint", "stylelint", "jsonlint", "markdownlint", "yamllint"]);
|
grunt.registerTask("default", ["eslint", "stylelint", "jsonlint", "markdownlint", "yamllint"]);
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
/* jshint esversion: 6 */
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Use separate scope to prevent global scope pollution
|
// Use separate scope to prevent global scope pollution
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
* By Michael Teeuw http://michaelteeuw.nl
|
* By Michael Teeuw http://michaelteeuw.nl
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
*
|
*
|
||||||
* For more information how you can configurate this file
|
* For more information on how you can configure this file
|
||||||
* See https://github.com/MichMich/MagicMirror#configuration
|
* See https://github.com/MichMich/MagicMirror#configuration
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -12,8 +12,8 @@ var config = {
|
|||||||
address: "localhost", // Address to listen on, can be:
|
address: "localhost", // Address to listen on, can be:
|
||||||
// - "localhost", "127.0.0.1", "::1" to listen on loopback interface
|
// - "localhost", "127.0.0.1", "::1" to listen on loopback interface
|
||||||
// - another specific IPv4/6 to listen on a specific interface
|
// - another specific IPv4/6 to listen on a specific interface
|
||||||
// - "", "0.0.0.0", "::" to listen on any interface
|
// - "0.0.0.0", "::" to listen on any interface
|
||||||
// Default, when address config is left out, is "localhost"
|
// Default, when address config is left out or empty, is "localhost"
|
||||||
port: 8080,
|
port: 8080,
|
||||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], // Set [] to allow all IP addresses
|
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], // Set [] to allow all IP addresses
|
||||||
// or add a specific IPv4 of 192.168.1.5 :
|
// or add a specific IPv4 of 192.168.1.5 :
|
||||||
@@ -21,6 +21,10 @@ var config = {
|
|||||||
// or IPv4 range of 192.168.3.0 --> 192.168.3.15 use CIDR format :
|
// or IPv4 range of 192.168.3.0 --> 192.168.3.15 use CIDR format :
|
||||||
// ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.3.0/28"],
|
// ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.3.0/28"],
|
||||||
|
|
||||||
|
useHttps: false, // Support HTTPS or not, default "false" will use HTTP
|
||||||
|
httpsPrivateKey: "", // HTTPS private key path, only require when useHttps is true
|
||||||
|
httpsCertificate: "", // HTTPS Certificate path, only require when useHttps is true
|
||||||
|
|
||||||
language: "en",
|
language: "en",
|
||||||
timeFormat: 24,
|
timeFormat: 24,
|
||||||
units: "metric",
|
units: "metric",
|
||||||
|
@@ -37,7 +37,7 @@
|
|||||||
<div class="region bottom right"><div class="container"></div></div>
|
<div class="region bottom right"><div class="container"></div></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="region fullscreen above"><div class="container"></div></div>
|
<div class="region fullscreen above"><div class="container"></div></div>
|
||||||
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
|
<script type="text/javascript" src="socket.io/socket.io.js"></script>
|
||||||
<script type="text/javascript" src="vendor/node_modules/nunjucks/browser/nunjucks.min.js"></script>
|
<script type="text/javascript" src="vendor/node_modules/nunjucks/browser/nunjucks.min.js"></script>
|
||||||
<script type="text/javascript" src="js/defaults.js"></script>
|
<script type="text/javascript" src="js/defaults.js"></script>
|
||||||
<script type="text/javascript" src="#CONFIG_FILE#"></script>
|
<script type="text/javascript" src="#CONFIG_FILE#"></script>
|
||||||
|
@@ -1,15 +0,0 @@
|
|||||||
const config = require('../config/config.js');const fs=require('fs');
|
|
||||||
for(let m of config.modules){
|
|
||||||
if(!(m.disabled || false)){
|
|
||||||
try {
|
|
||||||
let f=fs.statSync(m.module);
|
|
||||||
if(f.isDirectory()){
|
|
||||||
f1=fs.statSync(m.module+'/package.json');
|
|
||||||
if (f1.isFile()){
|
|
||||||
console.log(m.module);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ex) {}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,183 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Define the tested version of Node.js.
|
|
||||||
NODE_TESTED="v10.1.0"
|
|
||||||
NPM_TESTED="V6.0.0"
|
|
||||||
USER=`whoami`
|
|
||||||
PM2_FILE=pm2_MagicMirror.json
|
|
||||||
mac=$(uname -s)
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
cmd=greadlink
|
|
||||||
else
|
|
||||||
cmd=readlink
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d ~/MagicMirror ]; then
|
|
||||||
# put the log where the script is located
|
|
||||||
logdir=$(dirname $($cmd -f "$0"))
|
|
||||||
# if the script was execute from the web
|
|
||||||
if [[ $logdir != *"MagicMirror/installers"* ]]; then
|
|
||||||
# use the MagicMirror/installers folder
|
|
||||||
cd ~/MagicMirror/installers >/dev/null
|
|
||||||
logdir=$(pwd)
|
|
||||||
cd - >/dev/null
|
|
||||||
fi
|
|
||||||
logfile=$logdir/pm2_setup.log
|
|
||||||
echo the log will be saved in $logfile
|
|
||||||
date +"pm2 setup starting - %a %b %e %H:%M:%S %Z %Y" >>$logfile
|
|
||||||
echo system is $(uname -a) >> $logfile
|
|
||||||
if [ "$mac" == "Darwin" ]; then
|
|
||||||
echo the os is macOS $(sw_vers -productVersion) >> $logfile
|
|
||||||
else
|
|
||||||
echo the os is $(lsb_release -a 2>/dev/null) >> $logfile
|
|
||||||
fi
|
|
||||||
node_installed=$(which node)
|
|
||||||
if [ "$node_installed." == "." ]; then
|
|
||||||
# node not installed
|
|
||||||
echo Installing node >>$logfile
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
brew install node
|
|
||||||
else
|
|
||||||
NODE_STABLE_BRANCH="10.x"
|
|
||||||
curl -sL https://deb.nodesource.com/setup_$NODE_STABLE_BRANCH | sudo -E bash -
|
|
||||||
sudo apt-get install -y nodejs
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
node_version=$(node -v)
|
|
||||||
echo node version $node_version >>$logfile
|
|
||||||
npm_installed=$(which npm)
|
|
||||||
if [ "$npm_installed." == "." ]; then
|
|
||||||
# npm not installed
|
|
||||||
echo Installing npm >>$logfile
|
|
||||||
if [ $mac != 'Darwin' ]; then
|
|
||||||
sudo apt-get install -y npm
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# get latest
|
|
||||||
echo force installing latest npm version via npm >>$logfile
|
|
||||||
#sudo npm i -g npm
|
|
||||||
npm_version=$(npm -v)
|
|
||||||
echo npm version $npm_version >>$logfile
|
|
||||||
# assume pm2 will be found on the path
|
|
||||||
pm2cmd=pm2
|
|
||||||
up=""
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
up="--unsafe-perm"
|
|
||||||
launchctl=launchctl
|
|
||||||
launchctl_path=$(which $launchctl)
|
|
||||||
`export PATH=$PATH:${launchctl_path%/$launchctl}`
|
|
||||||
fi
|
|
||||||
# check to see if already installed
|
|
||||||
pm2_installed=$(which $pm2cmd)
|
|
||||||
if [ "$pm2_installed." != "." ]; then
|
|
||||||
# does it work?
|
|
||||||
echo pm2 installed >> $logfile
|
|
||||||
pm2_fails=$(pm2 list | grep -i -m 1 "App Name" | wc -l )
|
|
||||||
if [ $pm2_fails != 1 ]; then
|
|
||||||
# uninstall it
|
|
||||||
echo pm2 installed, but does not work, uninstalling >> $logfile
|
|
||||||
sudo npm uninstall $up -g pm2
|
|
||||||
# force reinstall
|
|
||||||
pm2_installed=
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# in not installed
|
|
||||||
if [ "$pm2_installed." == "." ]; then
|
|
||||||
# install it.
|
|
||||||
echo pm2 not installed, installing >>$logfile
|
|
||||||
result=$(sudo npm install $up -g pm2)
|
|
||||||
# if this is a mac
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
echo this is a mac, fixup for path >>$logfile
|
|
||||||
# get the location of pm2 install
|
|
||||||
# parse the npm install output to get the command
|
|
||||||
pm2cmd=`echo $result | awk -F - '{print $1}' | tr -d '[:space:]'`
|
|
||||||
c='/pm2'
|
|
||||||
# get the path only
|
|
||||||
echo ${pm2cmd%$c} >installers/pm2path
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# remove MagicMirror if defined
|
|
||||||
$pm2cmd delete MagicMirror >/dev/null 2>&1
|
|
||||||
cd ~/MagicMirror
|
|
||||||
echo get the pm2 platform specific startup command >>$logfile
|
|
||||||
# get the platform specific pm2 startup command
|
|
||||||
v=$($pm2cmd startup | tail -n 1)
|
|
||||||
if [ $mac != 'Darwin' ]; then
|
|
||||||
# check to see if we can get the OS package name (Ubuntu)
|
|
||||||
if [ $(which lsb_release| wc -l) >0 ]; then
|
|
||||||
# fix command
|
|
||||||
# if ubuntu 18.04, pm2 startup gets something wrong
|
|
||||||
if [ $(lsb_release -r | grep -m1 18.04 | wc -l) > 0 ]; then
|
|
||||||
v=$(echo $v | sed 's/\/bin/\/bin:\/bin/')
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo startup command = $v >>$logfile
|
|
||||||
# execute the command returned
|
|
||||||
$v 2>&1 >>$logfile
|
|
||||||
echo pm2 startup command done >>$logfile
|
|
||||||
# is this is mac
|
|
||||||
# need to fix pm2 startup, only on catalina
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
if [ $(sw_vers -productVersion | head -c 6) == '10.15.' ]; then
|
|
||||||
# only do if the faulty tag is present (pm2 may fix this, before the script is fixed)
|
|
||||||
if [ $(grep -m 1 UserName /Users/$USER/Library/LaunchAgents/pm2.$USER.plist | wc -l) -eq 1 ]; then
|
|
||||||
# copy the pm2 startup file config
|
|
||||||
cp /Users/$USER/Library/LaunchAgents/pm2.$USER.plist .
|
|
||||||
# edit out the UserName key/value strings
|
|
||||||
sed -e '/UserName/{N;d;}' pm2.$USER.plist > pm2.$USER.plist.new
|
|
||||||
# copy the file back
|
|
||||||
sudo cp pm2.$USER.plist.new /Users/$USER/Library/LaunchAgents/pm2.$USER.plist
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if the user is no pi, we have to fixup the pm2 json file
|
|
||||||
echo configure the pm2 config file for MagicMirror >>$logfile
|
|
||||||
if [ "$USER" != "pi" ]; then
|
|
||||||
echo the user is not pi >>$logfile
|
|
||||||
# go to the installers folder`
|
|
||||||
cd installers
|
|
||||||
# edit the startup script for the right user
|
|
||||||
echo change mm.sh >>$logfile
|
|
||||||
if [ ! -e mm_temp.sh ]; then
|
|
||||||
echo save copy of mm.sh >> $logfile
|
|
||||||
cp mm.sh mm_temp.sh
|
|
||||||
fi
|
|
||||||
if [ $(grep pi mm_temp.sh | wc -l) -gt 0 ]; then
|
|
||||||
echo change hard coded pi username >> $logfile
|
|
||||||
sed 's/pi/'$USER'/g' mm_temp.sh >mm.sh
|
|
||||||
else
|
|
||||||
echo change relative home path to hard coded path >> $logfile
|
|
||||||
hf=$(echo $HOME |sed 's/\//\\\//g')
|
|
||||||
sed 's/\~/'$hf'/g' mm_temp.sh >mm.sh
|
|
||||||
fi
|
|
||||||
# edit the pms config file for the right user
|
|
||||||
echo change $PM2_FILE >>$logfile
|
|
||||||
sed 's/pi/'$USER'/g' $PM2_FILE > pm2_MagicMirror_new.json
|
|
||||||
# make sure to use the updated file
|
|
||||||
PM2_FILE=pm2_MagicMirror_new.json
|
|
||||||
# if this is a mac
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
# copy the path file to the system paths list
|
|
||||||
sudo cp ./pm2path /etc/paths.d
|
|
||||||
# change the name of the home path for mac
|
|
||||||
sed 's/home/Users/g' $PM2_FILE > pm2_MagicMirror_new1.json
|
|
||||||
# make sure to use the updated file
|
|
||||||
PM2_FILE=pm2_MagicMirror_new1.json
|
|
||||||
fi
|
|
||||||
echo now using this config file $PM2_FILE >>$logfile
|
|
||||||
# go back one cd level
|
|
||||||
cd - >/dev/null
|
|
||||||
fi
|
|
||||||
echo start MagicMirror via pm2 now >>$logfile
|
|
||||||
# tell pm2 to start the app defined in the config file
|
|
||||||
$pm2cmd start $HOME/MagicMirror/installers/$PM2_FILE
|
|
||||||
# tell pm2 to save that configuration, for start at boot
|
|
||||||
echo save MagicMirror pm2 config now >>$logfile
|
|
||||||
$pm2cmd save
|
|
||||||
date +"pm2 setup completed - %a %b %e %H:%M:%S %Z %Y" >>$logfile
|
|
||||||
else
|
|
||||||
echo It appears MagicMirror has not been installed on this system
|
|
||||||
echo please run the installer, "raspberry.sh" first
|
|
||||||
fi
|
|
@@ -1,2 +1,3 @@
|
|||||||
|
# This file is still here to keep PM2 working on older installations.
|
||||||
cd ~/MagicMirror
|
cd ~/MagicMirror
|
||||||
DISPLAY=:0 npm start
|
DISPLAY=:0 npm start
|
||||||
|
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"apps" : [{
|
|
||||||
"name" : "MagicMirror",
|
|
||||||
"script" : "/home/pi/MagicMirror/installers/mm.sh",
|
|
||||||
"watch" : ["/home/pi/MagicMirror/config/config.js"]
|
|
||||||
}]
|
|
||||||
}
|
|
@@ -1,2 +0,0 @@
|
|||||||
echo "\033[32mMagicMirror installation successful!"
|
|
||||||
exit 0
|
|
@@ -1,569 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# This is an installer script for MagicMirror2. It works well enough
|
|
||||||
# that it can detect if you have Node installed, run a binary script
|
|
||||||
# and then download and run MagicMirror2.
|
|
||||||
|
|
||||||
if [ $USER == 'root' ]; then
|
|
||||||
echo Please logon as a user to execute the MagicMirror installation, not root
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "\e[0m"
|
|
||||||
echo '$$\ $$\ $$\ $$\ $$\ $$\ $$$$$$\'
|
|
||||||
echo '$$$\ $$$ | \__| $$$\ $$$ |\__| $$ __$$\'
|
|
||||||
echo '$$$$\ $$$$ | $$$$$$\ $$$$$$\ $$\ $$$$$$$\ $$$$\ $$$$ |$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ \__/ $$ |'
|
|
||||||
echo '$$\$$\$$ $$ | \____$$\ $$ __$$\ $$ |$$ _____|$$\$$\$$ $$ |$$ |$$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\ $$$$$$ |'
|
|
||||||
echo '$$ \$$$ $$ | $$$$$$$ |$$ / $$ |$$ |$$ / $$ \$$$ $$ |$$ |$$ | \__|$$ | \__|$$ / $$ |$$ | \__|$$ ____/'
|
|
||||||
echo '$$ |\$ /$$ |$$ __$$ |$$ | $$ |$$ |$$ | $$ |\$ /$$ |$$ |$$ | $$ | $$ | $$ |$$ | $$ |'
|
|
||||||
echo '$$ | \_/ $$ |\$$$$$$$ |\$$$$$$$ |$$ |\$$$$$$$\ $$ | \_/ $$ |$$ |$$ | $$ | \$$$$$$ |$$ | $$$$$$$$\'
|
|
||||||
echo '\__| \__| \_______| \____$$ |\__| \_______|\__| \__|\__|\__| \__| \______/ \__| \________|'
|
|
||||||
echo ' $$\ $$ |'
|
|
||||||
echo ' \$$$$$$ |'
|
|
||||||
echo ' \______/'
|
|
||||||
echo -e "\e[0m"
|
|
||||||
|
|
||||||
doInstall=1
|
|
||||||
true=1
|
|
||||||
false=0
|
|
||||||
# Define the tested version of Node.js.
|
|
||||||
NODE_TESTED="v10.1.0"
|
|
||||||
NPM_TESTED="V6.0.0"
|
|
||||||
USER=`whoami`
|
|
||||||
PM2_FILE=pm2_MagicMirror.json
|
|
||||||
force_arch=
|
|
||||||
pm2setup=$false
|
|
||||||
|
|
||||||
trim() {
|
|
||||||
local var="$*"
|
|
||||||
# remove leading whitespace characters
|
|
||||||
var="${var#"${var%%[![:space:]]*}"}"
|
|
||||||
# remove trailing whitespace characters
|
|
||||||
var="${var%"${var##*[![:space:]]}"}"
|
|
||||||
echo -n "$var"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mac=$(uname -s)
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
echo this is a mac | tee -a $logfile
|
|
||||||
cmd=greadlink
|
|
||||||
else
|
|
||||||
cmd=readlink
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
# put the log where the script is located
|
|
||||||
logdir=$(dirname $($cmd -f "$0"))
|
|
||||||
# if the script was execute from the web
|
|
||||||
if [[ $logdir != *"MagicMirror/installers"* ]]; then
|
|
||||||
# use the MagicMirror/installers folder, if setup
|
|
||||||
if [ -d MagicMirror ]; then
|
|
||||||
cd ~/MagicMirror/installers >/dev/null
|
|
||||||
logdir=$(pwd)
|
|
||||||
cd - >/dev/null
|
|
||||||
else
|
|
||||||
# use the users home folder if initial install
|
|
||||||
logdir=$HOME
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
logfile=$logdir/install.log
|
|
||||||
echo install log being saved to $logfile
|
|
||||||
|
|
||||||
# Determine which Pi is running.
|
|
||||||
date +"install starting - %a %b %e %H:%M:%S %Z %Y" >>$logfile
|
|
||||||
ARM=$(uname -m)
|
|
||||||
echo installing on $ARM processor system >>$logfile
|
|
||||||
echo the os is $(lsb_release -a 2>/dev/null) >> $logfile
|
|
||||||
# Check the Raspberry Pi version.
|
|
||||||
if [ "$ARM" != "armv7l" ]; then
|
|
||||||
read -p "this appears not to be a Raspberry Pi 2 or 3, do you want to continue installation (y/N)?" choice
|
|
||||||
if [[ $choice =~ ^[Nn]$ ]]; then
|
|
||||||
echo user stopped install on $ARM hardware >>$logfile
|
|
||||||
echo -e "\e[91mSorry, your Raspberry Pi is not supported."
|
|
||||||
echo -e "\e[91mPlease run MagicMirror on a Raspberry Pi 2 or 3."
|
|
||||||
echo -e "\e[91mIf this is a Pi Zero, the setup will configure to run in server only mode wih a local browser."
|
|
||||||
exit;
|
|
||||||
fi
|
|
||||||
#if [ "$ARM" == "armv6l" ]; then
|
|
||||||
# echo forcing armv71 architecture for pi 0 >>$logfile
|
|
||||||
# force_arch=-'--arch=armv7l'
|
|
||||||
# fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Define helper methods.
|
|
||||||
function command_exists () { type "$1" &> /dev/null ;}
|
|
||||||
function verlte() { [ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ];}
|
|
||||||
function verlt() { [ "$1" = "$2" ] && return 1 || verlte $1 $2 ;}
|
|
||||||
|
|
||||||
# Update before first apt-get
|
|
||||||
if [ $mac != 'Darwin' ]; then
|
|
||||||
echo -e "\e[96mUpdating packages ...\e[90m" | tee -a $logfile
|
|
||||||
upgrade=$false
|
|
||||||
update=$(sudo apt-get update 2>&1)
|
|
||||||
echo $update >> $logfile
|
|
||||||
update_rc=$?
|
|
||||||
if [ $update_rc -ne 0 ]; then
|
|
||||||
echo -e "\e[91mUpdate failed, retrying installation ...\e[90m" | tee -a $logfile
|
|
||||||
if [ $(echo $update | grep "apt-secure" | wc -l) -eq 1 ]; then
|
|
||||||
update=$(sudo apt-get update --allow-releaseinfo-change 2>&1)
|
|
||||||
update_rc=$?
|
|
||||||
echo $update >> $logfile
|
|
||||||
if [ $update_rc -ne 0 ]; then
|
|
||||||
echo "second apt-get update failed" $update | tee -a $logfile
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "second apt-get update completed ok" >> $logfile
|
|
||||||
upgrade=$true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "apt-get update completed ok" >> $logfile
|
|
||||||
upgrade=$true
|
|
||||||
fi
|
|
||||||
if [ $upgrade -eq $true ]; then
|
|
||||||
upgrade_result=$(sudo apt-get upgrade 2>&1)
|
|
||||||
upgrade_rc=$?
|
|
||||||
echo apt upgrade result ="rc=$upgrade_rc $upgrade_result" >> $logfile
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Installing helper tools
|
|
||||||
echo -e "\e[96mInstalling helper tools ...\e[90m" | tee -a $logfile
|
|
||||||
sudo apt-get --assume-yes install curl wget git build-essential unzip || exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if we need to install or upgrade Node.js.
|
|
||||||
echo -e "\e[96mCheck current Node installation ...\e[0m" | tee -a $logfile
|
|
||||||
NODE_INSTALL=false
|
|
||||||
if command_exists node; then
|
|
||||||
echo -e "\e[0mNode currently installed. Checking version number." | tee -a $logfile
|
|
||||||
NODE_CURRENT=$(node -v)
|
|
||||||
if [ "$NODE_CURRENT." == "." ]; then
|
|
||||||
NODE_CURRENT="V1.0.0"
|
|
||||||
echo forcing low Node version >> $logfile
|
|
||||||
fi
|
|
||||||
echo -e "\e[0mMinimum Node version: \e[1m$NODE_TESTED\e[0m" | tee -a $logfile
|
|
||||||
echo -e "\e[0mInstalled Node version: \e[1m$NODE_CURRENT\e[0m" | tee -a $logfile
|
|
||||||
if verlte $NODE_CURRENT $NODE_TESTED; then
|
|
||||||
echo -e "\e[96mNode should be upgraded.\e[0m" | tee -a $logfile
|
|
||||||
NODE_INSTALL=true
|
|
||||||
|
|
||||||
# Check if a node process is currenlty running.
|
|
||||||
# If so abort installation.
|
|
||||||
if pgrep "node" > /dev/null; then
|
|
||||||
echo -e "\e[91mA Node process is currently running. Can't upgrade." | tee -a $logfile
|
|
||||||
echo "Please quit all Node processes and restart the installer." | tee -a $logfile
|
|
||||||
echo $(ps -ef | grep node | grep -v \-\-color) | tee -a $logfile
|
|
||||||
exit;
|
|
||||||
fi
|
|
||||||
|
|
||||||
else
|
|
||||||
echo -e "\e[92mNo Node.js upgrade necessary.\e[0m" | tee -a $logfile
|
|
||||||
fi
|
|
||||||
|
|
||||||
else
|
|
||||||
echo -e "\e[93mNode.js is not installed.\e[0m" | tee -a $logfile
|
|
||||||
NODE_INSTALL=true
|
|
||||||
fi
|
|
||||||
# Install or upgrade node if necessary.
|
|
||||||
if $NODE_INSTALL; then
|
|
||||||
|
|
||||||
echo -e "\e[96mInstalling Node.js ...\e[90m" | tee -a $logfile
|
|
||||||
|
|
||||||
# Fetch the latest version of Node.js from the selected branch
|
|
||||||
# The NODE_STABLE_BRANCH variable will need to be manually adjusted when a new branch is released. (e.g. 7.x)
|
|
||||||
# Only tested (stable) versions are recommended as newer versions could break MagicMirror.
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
brew install node
|
|
||||||
else
|
|
||||||
NODE_STABLE_BRANCH="10.x"
|
|
||||||
# sudo apt-get install --only-upgrade libstdc++6
|
|
||||||
node_info=$(curl -sL https://deb.nodesource.com/setup_$NODE_STABLE_BRANCH | sudo -E bash - )
|
|
||||||
echo Node release info = $node_info >> $logfile
|
|
||||||
if [ "$(echo $node_info | grep "not currently supported")." == "." ]; then
|
|
||||||
sudo apt-get install -y nodejs
|
|
||||||
else
|
|
||||||
echo node $NODE_STABLE_BRANCH version installer not available, doing manually >>$logfile
|
|
||||||
# no longer supported install
|
|
||||||
sudo apt-get install -y --only-upgrade libstdc++6 >> $logfile
|
|
||||||
# have to do it manually
|
|
||||||
node_vnum=$(echo $NODE_STABLE_BRANCH | awk -F. '{print $1}')
|
|
||||||
# get the highest release number in the stable branch line for this processor architecture
|
|
||||||
node_ver=$(curl -sL https://unofficial-builds.nodejs.org/download/release/index.tab | grep $ARM | grep -m 1 v$node_vnum | awk '{print $1}')
|
|
||||||
echo latest release in the $NODE_STABLE_BRANCH family for $ARM is $node_ver >> $logfile
|
|
||||||
curl -sL https://unofficial-builds.nodejs.org/download/release/$node_ver/node-$node_ver-linux-$ARM.tar.gz >node_release-$node_ver.tar.gz
|
|
||||||
cd /usr/local
|
|
||||||
echo using release tar file = node_release-$node_ver.tar.gz >> $logfile
|
|
||||||
sudo tar --strip-components 1 -xzf $HOME/node_release-$node_ver.tar.gz
|
|
||||||
cd - >/dev/null
|
|
||||||
rm ./node_release-$node_ver.tar.gz
|
|
||||||
fi
|
|
||||||
# get the new node version number
|
|
||||||
new_ver=$(node -v 2>&1)
|
|
||||||
# if there is a failure to get it due to a missing library
|
|
||||||
if [ $(echo $new_ver | grep "not found" | wc -l) -ne 0 ]; then
|
|
||||||
#
|
|
||||||
sudo apt-get install -y --only-upgrade libstdc++6 >> $logfile
|
|
||||||
fi
|
|
||||||
echo node version is $(node -v 2>&1 >>$logfile)
|
|
||||||
fi
|
|
||||||
echo -e "\e[92mNode.js installation Done! version=$(node -v)\e[0m" | tee -a $logfile
|
|
||||||
fi
|
|
||||||
# Check if we need to install or upgrade npm.
|
|
||||||
echo -e "\e[96mCheck current NPM installation ...\e[0m" | tee -a $logfile
|
|
||||||
NPM_INSTALL=false
|
|
||||||
if command_exists npm; then
|
|
||||||
echo -e "\e[0mNPM currently installed. Checking version number." | tee -a $logfile
|
|
||||||
NPM_CURRENT='V'$(npm -v)
|
|
||||||
echo -e "\e[0mMinimum npm version: \e[1m$NPM_TESTED\e[0m" | tee -a $logfile
|
|
||||||
echo -e "\e[0mInstalled npm version: \e[1m$NPM_CURRENT\e[0m" | tee -a $logfile
|
|
||||||
if verlte $NPM_CURRENT $NPM_TESTED; then
|
|
||||||
echo -e "\e[96mnpm should be upgraded.\e[0m" | tee -a $logfile
|
|
||||||
NPM_INSTALL=true
|
|
||||||
|
|
||||||
# Check if a node process is currently running.
|
|
||||||
# If so abort installation.
|
|
||||||
if pgrep "npm" > /dev/null; then
|
|
||||||
echo -e "\e[91mA npm process is currently running. Can't upgrade." | tee -a $logfile
|
|
||||||
echo "Please quit all npm processes and restart the installer." | tee -a $logfile
|
|
||||||
exit;
|
|
||||||
fi
|
|
||||||
|
|
||||||
else
|
|
||||||
echo -e "\e[92mNo npm upgrade necessary.\e[0m" | tee -a $logfile
|
|
||||||
fi
|
|
||||||
|
|
||||||
else
|
|
||||||
echo -e "\e[93mnpm is not installed.\e[0m" | tee -a $logfile
|
|
||||||
NPM_INSTALL=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install or upgrade node if necessary.
|
|
||||||
if $NPM_INSTALL; then
|
|
||||||
|
|
||||||
echo -e "\e[96mInstalling npm ...\e[90m" | tee -a $logfile
|
|
||||||
|
|
||||||
# Fetch the latest version of npm from the selected branch
|
|
||||||
# The NODE_STABLE_BRANCH variable will need to be manually adjusted when a new branch is released. (e.g. 7.x)
|
|
||||||
# Only tested (stable) versions are recommended as newer versions could break MagicMirror.
|
|
||||||
|
|
||||||
#NODE_STABLE_BRANCH="9.x"
|
|
||||||
#curl -sL https://deb.nodesource.com/setup_$NODE_STABLE_BRANCH | sudo -E bash -
|
|
||||||
#
|
|
||||||
# if this is a mac, npm was installed with node
|
|
||||||
if [ $mac != 'Darwin' ]; then
|
|
||||||
sudo apt-get install -y npm >>$logfile
|
|
||||||
fi
|
|
||||||
# update to the latest.
|
|
||||||
echo upgrading npm to latest >> $logfile
|
|
||||||
sudo npm i -g npm >>$logfile
|
|
||||||
echo -e "\e[92mnpm installation Done! version=V$(npm -v)\e[0m" | tee -a $logfile
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install MagicMirror
|
|
||||||
cd ~
|
|
||||||
if [ $doInstall == 1 ]; then
|
|
||||||
if [ -d "$HOME/MagicMirror" ] ; then
|
|
||||||
echo -e "\e[93mIt seems like MagicMirror is already installed." | tee -a $logfile
|
|
||||||
echo -e "To prevent overwriting, the installer will be aborted." | tee -a $logfile
|
|
||||||
echo -e "Please rename the \e[1m~/MagicMirror\e[0m\e[93m folder and try again.\e[0m" | tee -a $logfile
|
|
||||||
echo ""
|
|
||||||
echo -e "If you want to upgrade your installation run \e[1m\e[97mupgrade-script\e[0m from the ~/MagicMirror/installers directory." | tee -a $logfile
|
|
||||||
echo ""
|
|
||||||
exit;
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "\e[96mCloning MagicMirror ...\e[90m" | tee -a $logfile
|
|
||||||
if git clone --depth=1 https://github.com/MichMich/MagicMirror.git; then
|
|
||||||
echo -e "\e[92mCloning MagicMirror Done!\e[0m" | tee -a $logfile
|
|
||||||
else
|
|
||||||
echo -e "\e[91mUnable to clone MagicMirror." | tee -a $logfile
|
|
||||||
exit;
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd ~/MagicMirror || exit
|
|
||||||
if [ $(grep version package.json | awk -F: '{print $2}') == '"2.9.0",' -a $ARM == 'armv6l' ]; then
|
|
||||||
git fetch https://github.com/MichMich/MagicMirror.git develop >/dev/null 2>&1
|
|
||||||
git branch develop FETCH_HEAD > /dev/null 2>&1
|
|
||||||
git checkout develop > /dev/null 2>&1
|
|
||||||
fi
|
|
||||||
echo -e "\e[96mInstalling dependencies ...\e[90m" | tee -a $logfile
|
|
||||||
if npm install $force_arch; then
|
|
||||||
echo -e "\e[92mDependencies installation Done!\e[0m" | tee -a $logfile
|
|
||||||
else
|
|
||||||
echo -e "\e[91mUnable to install dependencies!" | tee -a $logfile
|
|
||||||
exit;
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Use sample config for start MagicMirror
|
|
||||||
echo setting up initial config.js | tee -a $logfile
|
|
||||||
cp config/config.js.sample config/config.js
|
|
||||||
fi
|
|
||||||
# Check if plymouth is installed (default with PIXEL desktop environment), then install custom splashscreen.
|
|
||||||
echo -e "\e[96mCheck plymouth installation ...\e[0m" | tee -a $logfile
|
|
||||||
if command_exists plymouth; then
|
|
||||||
THEME_DIR="/usr/share/plymouth/themes"
|
|
||||||
echo -e "\e[90mSplashscreen: Checking themes directory.\e[0m" | tee -a $logfile
|
|
||||||
if [ -d $THEME_DIR ]; then
|
|
||||||
echo -e "\e[90mSplashscreen: Create theme directory if not exists.\e[0m" | tee -a $logfile
|
|
||||||
if [ ! -d $THEME_DIR/MagicMirror ]; then
|
|
||||||
sudo mkdir $THEME_DIR/MagicMirror
|
|
||||||
fi
|
|
||||||
|
|
||||||
if sudo cp ~/MagicMirror/splashscreen/splash.png $THEME_DIR/MagicMirror/splash.png && sudo cp ~/MagicMirror/splashscreen/MagicMirror.plymouth $THEME_DIR/MagicMirror/MagicMirror.plymouth && sudo cp ~/MagicMirror/splashscreen/MagicMirror.script $THEME_DIR/MagicMirror/MagicMirror.script; then
|
|
||||||
echo
|
|
||||||
if [ "$(which plymouth-set-default-theme)." != "." ]; then
|
|
||||||
if sudo plymouth-set-default-theme -R MagicMirror; then
|
|
||||||
echo -e "\e[92mSplashscreen: Changed theme to MagicMirror successfully.\e[0m" | tee -a $logfile
|
|
||||||
else
|
|
||||||
echo -e "\e[91mSplashscreen: Couldn't change theme to MagicMirror!\e[0m" | tee -a $logfile
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo -e "\e[91mSplashscreen: Copying theme failed!\e[0m" | tee -a $logfile
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo -e "\e[91mSplashscreen: Themes folder doesn't exist!\e[0m" | tee -a $logfile
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo -e "\e[93mplymouth is not installed.\e[0m" | tee -a $logfile
|
|
||||||
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
|
|
||||||
echo install and setup pm2 | tee -a $logfile
|
|
||||||
# assume pm2 will be found on the path
|
|
||||||
pm2cmd=pm2
|
|
||||||
# check to see if already installed
|
|
||||||
pm2_installed=$(which $pm2cmd)
|
|
||||||
up=""
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
up="--unsafe-perm"
|
|
||||||
launchctl=launchctl
|
|
||||||
launchctl_path=$(which $launchctl)
|
|
||||||
`export PATH=$PATH:${launchctl_path%/$launchctl}`
|
|
||||||
fi
|
|
||||||
# check to see if already installed
|
|
||||||
pm2_installed=$(which $pm2cmd)
|
|
||||||
if [ "$pm2_installed." != "." ]; then
|
|
||||||
# does it work?
|
|
||||||
pm2_fails=$(pm2 list | grep -i -m 1 "App Name" | wc -l )
|
|
||||||
if [ $pm2_fails != 1 ]; then
|
|
||||||
# uninstall it
|
|
||||||
echo pm2 installed, but does not work, uninstalling >> $logfile
|
|
||||||
sudo npm uninstall $up -g pm2 >> $logfile
|
|
||||||
# force reinstall
|
|
||||||
pm2_installed=
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# if not installed
|
|
||||||
if [ "$pm2_installed." == "." ]; then
|
|
||||||
# install it.
|
|
||||||
echo pm2 not installed, installing >>$logfile
|
|
||||||
result=$(sudo npm install $up -g pm2 2>&1)
|
|
||||||
echo pm2 install result $result >>$logfile
|
|
||||||
# if this is a mac
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
echo this is a mac, fixup for path >>$logfile
|
|
||||||
# get the location of pm2 install
|
|
||||||
# parse the npm install output to get the command
|
|
||||||
pm2cmd=`echo $result | awk -F - '{print $1}' | tr -d '[:space:]'`
|
|
||||||
c='/pm2'
|
|
||||||
# get the path only
|
|
||||||
echo ${pm2cmd%$c} >installers/pm2path
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo get the pm2 platform specific startup command >>$logfile
|
|
||||||
# get the platform specific pm2 startup command
|
|
||||||
v=$($pm2cmd startup | tail -n 1)
|
|
||||||
if [ $mac != 'Darwin' ]; then
|
|
||||||
# check to see if we can get the OS package name (Ubuntu)
|
|
||||||
if [ $(which lsb_release| wc -l) >0 ]; then
|
|
||||||
# fix command
|
|
||||||
# if ubuntu 18.04, pm2 startup gets something wrong
|
|
||||||
if [ $(lsb_release -r | grep -m1 18.04 | wc -l) > 0 ]; then
|
|
||||||
v=$(echo $v | sed 's/\/bin/\/bin:\/bin/')
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo startup command = $v >>$logfile
|
|
||||||
# execute the command returned
|
|
||||||
$v 2>&1 >>$logfile
|
|
||||||
echo pm2 startup command done >>$logfile
|
|
||||||
# is this is mac
|
|
||||||
# need to fix pm2 startup, only on catalina
|
|
||||||
if [ $mac == 'Darwin' ];then
|
|
||||||
if [ $(sw_vers -productVersion | head -c 6) == '10.15.' ]; then
|
|
||||||
# only do if the faulty tag is present (pm2 may fix this, before the script is fixed)
|
|
||||||
if [ $(grep -m 1 UserName /Users/$USER/Library/LaunchAgents/pm2.$USER.plist | wc -l) -eq 1 ]; then
|
|
||||||
# copy the pm2 startup file config
|
|
||||||
cp /Users/$USER/Library/LaunchAgents/pm2.$USER.plist .
|
|
||||||
# edit out the UserName key/value strings
|
|
||||||
sed -e '/UserName/{N;d;}' pm2.$USER.plist > pm2.$USER.plist.new
|
|
||||||
# copy the file back
|
|
||||||
sudo cp pm2.$USER.plist.new /Users/$USER/Library/LaunchAgents/pm2.$USER.plist
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# if the user is no pi, we have to fixup the pm2 json file
|
|
||||||
echo configure the pm2 config file for MagicMirror >>$logfile
|
|
||||||
if [ "$USER" != "pi" ]; then
|
|
||||||
echo the user is not pi >>$logfile
|
|
||||||
# go to the installers folder`
|
|
||||||
cd installers
|
|
||||||
# edit the startup script for the right user
|
|
||||||
echo change mm.sh >>$logfile
|
|
||||||
if [ ! -e mm_temp.sh ]; then
|
|
||||||
echo save copy of mm.sh >> $logfile
|
|
||||||
cp mm.sh mm_temp.sh
|
|
||||||
fi
|
|
||||||
if [ $(grep pi mm_temp.sh | wc -l) -gt 0 ]; then
|
|
||||||
echo change hard coded pi username >> $logfile
|
|
||||||
sed 's/pi/'$USER'/g' mm_temp.sh >mm.sh
|
|
||||||
else
|
|
||||||
echo change relative home path to hard coded path >> $logfile
|
|
||||||
hf=$(echo $HOME |sed 's/\//\\\//g')
|
|
||||||
sed 's/\~/'$hf'/g' mm_temp.sh >mm.sh
|
|
||||||
fi
|
|
||||||
# edit the pms config file for the right user
|
|
||||||
echo change $PM2_FILE >>$logfile
|
|
||||||
sed 's/pi/'$USER'/g' $PM2_FILE > pm2_MagicMirror_new.json
|
|
||||||
# make sure to use the updated file
|
|
||||||
PM2_FILE=pm2_MagicMirror_new.json
|
|
||||||
# if this is a mac
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
# copy the path file to the system paths list
|
|
||||||
sudo cp ./pm2path /etc/paths.d
|
|
||||||
# change the name of the home path for mac
|
|
||||||
sed 's/home/Users/g' $PM2_FILE > pm2_MagicMirror_new1.json
|
|
||||||
# make sure to use the updated file
|
|
||||||
PM2_FILE=pm2_MagicMirror_new1.json
|
|
||||||
fi
|
|
||||||
echo now using this config file $PM2_FILE >>$logfile
|
|
||||||
# go back one cd level
|
|
||||||
cd - >/dev/null
|
|
||||||
fi
|
|
||||||
echo start MagicMirror via pm2 now >>$logfile
|
|
||||||
# tell pm2 to start the app defined in the config file
|
|
||||||
$pm2cmd start $HOME/MagicMirror/installers/$PM2_FILE
|
|
||||||
# tell pm2 to save that configuration, for start at boot
|
|
||||||
echo save MagicMirror pm2 config now >>$logfile
|
|
||||||
$pm2cmd save
|
|
||||||
pm2setup=$true
|
|
||||||
fi
|
|
||||||
# Disable Screensaver
|
|
||||||
choice=n
|
|
||||||
read -p "Do you want to disable the screen saver? (y/N)?" choice
|
|
||||||
if [[ $choice =~ ^[Yy]$ ]]; then
|
|
||||||
# if this is a mac
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
# get the current setting
|
|
||||||
setting=$(defaults -currentHost read com.apple.screensaver idleTime)
|
|
||||||
# if its on
|
|
||||||
if [ $setting != 0 ] ; then
|
|
||||||
# turn it off
|
|
||||||
echo disable screensaver via mac profile >> $logfile
|
|
||||||
defaults -currentHost write com.apple.screensaver idleTime 0
|
|
||||||
else
|
|
||||||
echo mac profile screen saver already disabled >> $logfile
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
# find out if some screen saver running
|
|
||||||
|
|
||||||
# get just the running processes and args
|
|
||||||
# just want the program name (1st token)
|
|
||||||
# find the 1st with 'saver' in it (should only be one)
|
|
||||||
# parse with path char, get the last field ( the actual pgm name)
|
|
||||||
|
|
||||||
screen_saver_running=$(ps -A -o args | awk '{print $1}' | grep -m1 [s]aver | awk -F\/ '{print $NF}');
|
|
||||||
|
|
||||||
# if we found something
|
|
||||||
if [ "$screen_saver_running." != "." ]; then
|
|
||||||
# some screensaver running
|
|
||||||
case "$screen_saver_running" in
|
|
||||||
mate-screensaver) echo 'mate screen saver' >>$logfile
|
|
||||||
#killall mate-screensaver >/dev/null 2>&1
|
|
||||||
#$ms -d >/dev/null 2>&1
|
|
||||||
gsettings set org.mate.screensaver lock-enabled false 2>/dev/null
|
|
||||||
gsettings set org.mate.screensaver idle-activation-enabled false 2>/dev/null
|
|
||||||
gsettings set org.mate.screensaver lock_delay 0 2>/dev/null
|
|
||||||
echo " $screen_saver_running disabled" >> $logfile
|
|
||||||
DISPLAY=:0 mate-screensaver >/dev/null 2>&1 &
|
|
||||||
;;
|
|
||||||
gnome-screensaver) echo 'gnome screen saver' >>$logfile
|
|
||||||
gnome_screensaver-command -d >/dev/null 2>&1
|
|
||||||
echo " $screen_saver_running disabled" >> $logfile
|
|
||||||
;;
|
|
||||||
xscreensaver) echo 'xscreensaver running' | tee -a $logfile
|
|
||||||
if [ $(grep -m1 'mode:' ~/.xscreensaver | awk '{print $2}') != 'off' ]; then
|
|
||||||
sed -i 's/$xsetting/mode: off/' ~/.xscreensaver
|
|
||||||
echo " xscreensaver set to off" >> $logfile
|
|
||||||
else
|
|
||||||
echo " xscreensaver already disabled" >> $logfile
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
gsd-screensaver | gsd-screensaver-proxy)
|
|
||||||
setting=$(gsettings get org.gnome.desktop.screensaver lock-enabled)
|
|
||||||
setting1=$(gsettings get org.gnome.desktop.session idle-delay)
|
|
||||||
if [ "$setting $setting1" != 'false uint32 0' ]; then
|
|
||||||
echo disable screensaver via gsettings was $setting and $setting1>> $logfile
|
|
||||||
gsettings set org.gnome.desktop.screensaver lock-enabled false
|
|
||||||
gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
|
|
||||||
gsettings set org.gnome.desktop.session idle-delay 0
|
|
||||||
else
|
|
||||||
echo gsettings screen saver already disabled >> $logfile
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*) echo "some other screensaver $screen_saver_running" found | tee -a $logfile
|
|
||||||
echo "please configure it manually" | tee -a $logfile
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
elif [ -e "/etc/lightdm/lightdm.conf" ]; then
|
|
||||||
# if screen saver NOT already disabled?
|
|
||||||
if [ $(grep 'xserver-command=X -s 0 -dpms' /etc/lightdm/lightdm.conf | wc -l) == 0 ]; then
|
|
||||||
echo install screensaver via lightdm.conf >> $logfile
|
|
||||||
sudo sed -i '/^\[Seat:\*\]/a xserver-command=X -s 0 -dpms' /etc/lightdm/lightdm.conf
|
|
||||||
else
|
|
||||||
echo screensaver via lightdm already disabled >> $logfile
|
|
||||||
fi
|
|
||||||
elif [ $(which gsettings | wc -l) == 1 ]; then
|
|
||||||
setting=$(gsettings get org.gnome.desktop.screensaver lock-enabled)
|
|
||||||
setting1=$(gsettings get org.gnome.desktop.session idle-delay)
|
|
||||||
if [ "$setting $setting1" != 'false uint32 0' ]; then
|
|
||||||
echo disable screensaver via gsettings was $setting and $setting1>> $logfile
|
|
||||||
gsettings set org.gnome.desktop.screensaver lock-enabled false
|
|
||||||
gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
|
|
||||||
gsettings set org.gnome.desktop.session idle-delay 0
|
|
||||||
else
|
|
||||||
echo gsettings screen saver already disabled >> $logfile
|
|
||||||
fi
|
|
||||||
elif [ -d "/etc/xdg/lxsession" ]; then
|
|
||||||
currently_set=$(grep -m1 '\-dpms' /etc/xdg/lxsession/LXDE-pi/autostart)
|
|
||||||
if [ "$currently_set." == "." ]; then
|
|
||||||
echo disable screensaver via lxsession >> $logfile
|
|
||||||
# turn it off for the future
|
|
||||||
sudo su -c "echo -e '@xset s noblank\n@xset s off\n@xset -dpms' >> /etc/xdg/lxsession/LXDE-pi/autostart"
|
|
||||||
# turn it off now
|
|
||||||
export DISPLAY=:0; xset s noblank;xset s off;xset -dpms
|
|
||||||
else
|
|
||||||
echo lxsession screen saver already disabled >> $logfile
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo " "
|
|
||||||
echo -e "unable to disable screen saver, /etc/xdg/lxsession does not exist" | tee -a $logfile
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo " "
|
|
||||||
if [ $pm2setup -eq $true ]; then
|
|
||||||
rmessage="pm2 start MagicMirror"
|
|
||||||
else
|
|
||||||
rmessage="DISPLAY=:0 npm start"
|
|
||||||
fi
|
|
||||||
echo -e "\e[92mWe're ready! Run \e[1m\e[97m$rmessage\e[0m\e[92m from the ~/MagicMirror directory to start your MagicMirror.\e[0m" | tee -a $logfile
|
|
||||||
|
|
||||||
echo " "
|
|
||||||
echo " "
|
|
||||||
|
|
||||||
date +"install completed - %a %b %e %H:%M:%S %Z %Y" >>$logfile
|
|
@@ -1,101 +0,0 @@
|
|||||||
#/bin/bash
|
|
||||||
logfile=~/screensaver.log
|
|
||||||
mac=$(uname -s)
|
|
||||||
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
setting=$(defaults -currentHost read com.apple.screensaver idleTime)
|
|
||||||
if [ $setting != 0 ] ; then
|
|
||||||
echo disable screensaver via mac profile >> $logfile
|
|
||||||
defaults -currentHost write com.apple.screensaver idleTime 0
|
|
||||||
else
|
|
||||||
echo mac profile screen saver already disabled >> $logfile
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
# find out if some screen saver running
|
|
||||||
|
|
||||||
# get just the running processes and args
|
|
||||||
# just want the program name
|
|
||||||
# find the 1st with 'saver' in it (should only be one)
|
|
||||||
# if the process name is a path, parse it and get the last field ( the actual pgm name)
|
|
||||||
|
|
||||||
screen_saver_running=$(ps -A -o args | awk '{print $1}' | grep -m1 [s]aver | awk -F\/ '{print $NF}');
|
|
||||||
|
|
||||||
# if we found something
|
|
||||||
if [ "$screen_saver_running." != "." ]; then
|
|
||||||
# some screensaver running
|
|
||||||
case "$screen_saver_running" in
|
|
||||||
mate-screensaver) echo 'mate screen saver' >>$logfile
|
|
||||||
#killall mate-screensaver >/dev/null 2>&1
|
|
||||||
#ms=$(which mate-screensaver-command)
|
|
||||||
#$ms -d >/dev/null 2>&1
|
|
||||||
gsettings set org.mate.screensaver lock-enabled false 2>/dev/null
|
|
||||||
gsettings set org.mate.screensaver idle-activation-enabled false 2>/dev/null
|
|
||||||
gsettings set org.mate.screensaver lock_delay 0 2>/dev/null
|
|
||||||
echo " $screen_saver_running disabled" >> $logfile
|
|
||||||
DISPLAY=:0 mate-screensaver >/dev/null 2>&1 &
|
|
||||||
;;
|
|
||||||
gnome-screensaver) echo 'gnome screen saver' >>$logfile
|
|
||||||
gnome_screensaver-command -d >/dev/null 2>&1
|
|
||||||
echo " $screen_saver_running disabled" >> $logfile
|
|
||||||
;;
|
|
||||||
xscreensaver) echo 'xscreensaver running' | tee -a $logfile
|
|
||||||
if [ $(grep -m1 'mode:' ~/.xscreensaver | awk '{print $2}') != 'off' ]; then
|
|
||||||
sed -i 's/$xsetting/mode: off/' ~/.xscreensaver
|
|
||||||
echo " xscreensaver set to off" >> $logfile
|
|
||||||
else
|
|
||||||
echo " xscreensaver already disabled" >> $logfile
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
gsd-screensaver | gsd-screensaver-proxy)
|
|
||||||
setting=$(gsettings get org.gnome.desktop.screensaver lock-enabled)
|
|
||||||
setting1=$(gsettings get org.gnome.desktop.session idle-delay)
|
|
||||||
if [ "$setting $setting1" != 'false uint32 0' ]; then
|
|
||||||
echo disable screensaver via gsettings was $setting and $setting1>> $logfile
|
|
||||||
gsettings set org.gnome.desktop.screensaver lock-enabled false
|
|
||||||
gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
|
|
||||||
gsettings set org.gnome.desktop.session idle-delay 0
|
|
||||||
else
|
|
||||||
echo gsettings screen saver already disabled >> $logfile
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*) echo "some other screensaver $screen_saver_running" found | tee -a $logfile
|
|
||||||
echo "please configure it manually" | tee -a $logfile
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
elif [ -e "/etc/lightdm/lightdm.conf" ]; then
|
|
||||||
# if screen saver NOT already disabled?
|
|
||||||
if [ $(grep 'xserver-command=X -s 0 -dpms' /etc/lightdm/lightdm.conf | wc -l) == 0 ]; then
|
|
||||||
echo install screensaver via lightdm.conf >> $logfile
|
|
||||||
sudo sed -i '/^\[Seat:\*\]/a xserver-command=X -s 0 -dpms' /etc/lightdm/lightdm.conf
|
|
||||||
#sudo cp _myconf /etc/lightdm/lightdm.conf
|
|
||||||
#rm _myconf >/dev/null
|
|
||||||
else
|
|
||||||
echo screensaver via lightdm already disabled >> $logfile
|
|
||||||
fi
|
|
||||||
elif [ $(which gsettings | wc -l) == 1 ]; then
|
|
||||||
setting=$(gsettings get org.gnome.desktop.screensaver lock-enabled)
|
|
||||||
setting1=$(gsettings get org.gnome.desktop.session idle-delay)
|
|
||||||
if [ "$setting $setting1" != 'false uint32 0' ]; then
|
|
||||||
echo disable screensaver via gsettings was $setting and $setting1>> $logfile
|
|
||||||
gsettings set org.gnome.desktop.screensaver lock-enabled false
|
|
||||||
gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
|
|
||||||
gsettings set org.gnome.desktop.session idle-delay 0
|
|
||||||
else
|
|
||||||
echo gsettings screen saver already disabled >> $logfile
|
|
||||||
fi
|
|
||||||
elif [ -d "/etc/xdg/lxsession" ]; then
|
|
||||||
currently_set=$(grep -m1 '\-dpms' /etc/xdg/lxsession/LXDE-pi/autostart)
|
|
||||||
if [ "$currently_set." == "." ]; then
|
|
||||||
echo disable screensaver via lxsession >> $logfile
|
|
||||||
# turn it off for the future
|
|
||||||
sudo su -c "echo -e '@xset s noblank\n@xset s off\n@xset -dpms' >> /etc/xdg/lxsession/LXDE-pi/autostart"
|
|
||||||
# turn it off now
|
|
||||||
export DISPLAY=:0; xset s noblank;xset s off;xset -dpms
|
|
||||||
else
|
|
||||||
echo lxsession screen saver already disabled >> $logfile
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo " "
|
|
||||||
echo -e "unable to disable screen saver, /etc/xdg/lxsession does not exist" | tee >>$logfile
|
|
||||||
fi
|
|
||||||
fi
|
|
@@ -1,361 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# only DO npm installs when flag is set to 1
|
|
||||||
# test when set to 0
|
|
||||||
true=1
|
|
||||||
false=0
|
|
||||||
doinstalls=$false
|
|
||||||
force=$false
|
|
||||||
justActive=$true
|
|
||||||
test_run=$true
|
|
||||||
stashed=$false
|
|
||||||
keyFile=package.json
|
|
||||||
forced_arch=
|
|
||||||
git_active_lock='./.git/index.lock'
|
|
||||||
lf=$'\n'
|
|
||||||
git_user_name=
|
|
||||||
git_user_email=
|
|
||||||
|
|
||||||
trim() {
|
|
||||||
local var="$*"
|
|
||||||
# remove leading whitespace characters
|
|
||||||
var="${var#"${var%%[![:space:]]*}"}"
|
|
||||||
# remove trailing whitespace characters
|
|
||||||
var="${var%"${var##*[![:space:]]}"}"
|
|
||||||
echo -n "$var"
|
|
||||||
}
|
|
||||||
# is this a mac
|
|
||||||
mac=$(uname -s)
|
|
||||||
# get the processor architecture
|
|
||||||
arch=$(uname -m)
|
|
||||||
if [ $mac == 'Darwin' ]; then
|
|
||||||
cmd=greadlink
|
|
||||||
else
|
|
||||||
cmd=readlink
|
|
||||||
fi
|
|
||||||
if [ -d ~/MagicMirror ]; then
|
|
||||||
|
|
||||||
# put the log where the script is located
|
|
||||||
logdir=$(dirname $($cmd -f "$0"))
|
|
||||||
# if the script was execute from the web
|
|
||||||
if [[ $logdir != *"MagicMirror/installers"* ]]; then
|
|
||||||
# use the MagicMirror/installers folder
|
|
||||||
cd ~/MagicMirror/installers >/dev/null
|
|
||||||
logdir=$(pwd)
|
|
||||||
cd - >/dev/null
|
|
||||||
fi
|
|
||||||
logfile=$logdir/upgrade.log
|
|
||||||
echo the log will be $logfile
|
|
||||||
echo >>$logfile
|
|
||||||
date +"Upgrade started - %a %b %e %H:%M:%S %Z %Y" >>$logfile
|
|
||||||
echo system is $(uname -a) >> $logfile
|
|
||||||
echo the os is $(lsb_release -a) >> $logfile
|
|
||||||
|
|
||||||
# because of how its executed from the web, p0 gets overlayed with parm
|
|
||||||
# check to see if a parm was passed .. easy apply without editing
|
|
||||||
p0=$0
|
|
||||||
# if not 'bash', and some parm specified
|
|
||||||
if [ $0 != 'bash' -a "$1." != "." ]; then
|
|
||||||
# then executed locally
|
|
||||||
# get the parm
|
|
||||||
p0=$1
|
|
||||||
fi
|
|
||||||
# lowercase it.. watch out, mac stuff doesn't work with tr, etc
|
|
||||||
p0=$(echo $p0 | cut -c 1-5 | awk '{print tolower($0)}' )
|
|
||||||
if [ $p0 == 'apply' ]; then
|
|
||||||
echo user requested to apply changes >>$logfile
|
|
||||||
doinstalls=$true
|
|
||||||
test_run=$false
|
|
||||||
elif [ $p0 == 'force' ]; then
|
|
||||||
echo user requested to force apply changes >>$logfile
|
|
||||||
doinstalls=$true
|
|
||||||
force=$true
|
|
||||||
test_run=$false
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $test_run == $true ]; then
|
|
||||||
echo doing test run = true | tee -a $logfile
|
|
||||||
else
|
|
||||||
echo doing test run = false | tee -a $logfile
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if we want just the modules listed in config.js now
|
|
||||||
if [ $justActive == $true ]; then
|
|
||||||
if [ ! -f ~/MagicMirror/installers/dumpactivemodules.js ]; then
|
|
||||||
echo downloading dumpactivemodules script >> $logfile
|
|
||||||
curl -sL https://www.dropbox.com/s/wwe6bfg2lcjmj43/dumpactivemodules.js?dl=0 > ~/MagicMirror/installers/dumpactivemodules.js
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo update log will be in $logfile
|
|
||||||
# used for parsing the array of module names
|
|
||||||
SAVEIFS=$IFS # Save current IFS
|
|
||||||
IFS=$'\n'
|
|
||||||
|
|
||||||
echo | tee -a $logfile
|
|
||||||
# if the git lock file exists and git is not running
|
|
||||||
if [ -f git_active_lock ]; then
|
|
||||||
# check to see if git is actually running
|
|
||||||
git_running=`ps -ef | grep git | grep -v color | grep -v 'grep git' | wc -l`
|
|
||||||
# if not running
|
|
||||||
if [ git_running == $false ]; then
|
|
||||||
# clean up the dangling lock file
|
|
||||||
echo erasing abandonded git lock file >> $logfile
|
|
||||||
rm git_active_lock >/dev/null 2>&1
|
|
||||||
else
|
|
||||||
# git IS running, we can't proceed
|
|
||||||
echo it appears another instance of git is running | tee -a $logfile
|
|
||||||
# if this is an actual run
|
|
||||||
if [ $doinstalls == $true ]; then
|
|
||||||
# force it back to test run
|
|
||||||
doinstalls = $false
|
|
||||||
test_run=$true
|
|
||||||
echo forcing test run mode | tee -a $logfile
|
|
||||||
echo please resolve git running already and start the update again | tee -a $logfile
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# change to MagicMirror folder
|
|
||||||
cd ~/MagicMirror
|
|
||||||
|
|
||||||
# save custom.css
|
|
||||||
cd css
|
|
||||||
echo "saving custom.css" | tee -a $logfile
|
|
||||||
cp -p custom.css save_custom.css
|
|
||||||
cd - >/dev/null
|
|
||||||
save_alias=$(alias git 2>/dev/null)
|
|
||||||
lang=$(locale | grep LANGUAGE | awk -F= '{print $2}')
|
|
||||||
# make sure git respones are in english, so code works
|
|
||||||
if [ "$lang." != "en_US.UTF-8." ]; then
|
|
||||||
echo not english or locale not set, set git alias >>$logfile
|
|
||||||
if [ "$LC_ALL." == "." ]; then
|
|
||||||
alias git='LANGUAGE=en_US.UTF-8 git' >>$logfile
|
|
||||||
else
|
|
||||||
alias git='LC_ALL=en_US.UTF-8 git' >>$logfile
|
|
||||||
fi
|
|
||||||
#alias >>$logfile
|
|
||||||
fi
|
|
||||||
# get the git remote name
|
|
||||||
remote=$(git remote 2>/dev/null | awk '{print $1}')
|
|
||||||
|
|
||||||
# if remote name set
|
|
||||||
if [ "$remote." != "." ]; then
|
|
||||||
|
|
||||||
echo remote name = $remote >>$logfile
|
|
||||||
|
|
||||||
# get the local and remote package.json versions
|
|
||||||
local_version=$(grep -m1 version package.json | awk -F\" '{print $4}')
|
|
||||||
remote_version=$(curl -s https://raw.githubusercontent.com/MichMich/MagicMirror/master/package.json | grep -m1 version | awk -F\" '{print $4}')
|
|
||||||
|
|
||||||
# only change if they are different
|
|
||||||
if [ "$local_version." != "$remote_version." -o $force == $true -o $test_run == $true ]; then
|
|
||||||
echo upgrading from version $local_version to $remote_version | tee -a $logfile
|
|
||||||
|
|
||||||
# get the latest upgrade
|
|
||||||
echo fetching latest revisions | tee -a $logfile
|
|
||||||
git fetch $remote >/dev/null
|
|
||||||
rc=$?
|
|
||||||
echo git fetch rc=$rc >>$logfile
|
|
||||||
if [ $rc -eq 0 ]; then
|
|
||||||
|
|
||||||
# need to get the current branch
|
|
||||||
current_branch=$(git branch | grep "*" | awk '{print $2}')
|
|
||||||
echo current branch = $current_branch >>$logfile
|
|
||||||
|
|
||||||
git status 2>&1 >>$logfile
|
|
||||||
|
|
||||||
# get the names of the files that are different locally
|
|
||||||
diffs=$(git status 2>&1 | grep modified | awk -F: '{print $2}')
|
|
||||||
|
|
||||||
# split names into an array
|
|
||||||
diffs=($diffs) # split to array $diffs
|
|
||||||
|
|
||||||
# if there are different files (array size greater than zero)
|
|
||||||
if [ ${#diffs[@]} -gt 0 ]; then
|
|
||||||
package_lock=0
|
|
||||||
echo there are "${#diffs[@]}" local files that are different than the master repo | tee -a $logfile
|
|
||||||
echo | tee -a $logfile
|
|
||||||
for file in "${diffs[@]}"
|
|
||||||
do
|
|
||||||
echo "$file" | tee -a $logfile
|
|
||||||
if [ $(echo $file | grep '\-lock.json$' | wc -l) -eq 1 ]; then
|
|
||||||
package_lock=$true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
echo | tee -a $logfile
|
|
||||||
if [ $package_lock -eq 1 ]; then
|
|
||||||
echo "any *-lock.json files do not need to be saved"
|
|
||||||
fi
|
|
||||||
read -p "do you want to save these files for later (Y/n)?" choice
|
|
||||||
echo save/restore files selection = $choice >> $logfile
|
|
||||||
if [[ $choice =~ ^[Yy]$ ]]; then
|
|
||||||
git_user=$(git config --global --get user.email)
|
|
||||||
if [ "git_user." == "." ]; then
|
|
||||||
git_user_name="-c user.name=upgrade_script"
|
|
||||||
git_user_email="-c user.email=script@upgrade.com"
|
|
||||||
fi
|
|
||||||
git git_user_name git_user_email stash >>$logfile
|
|
||||||
stashed=$true
|
|
||||||
else
|
|
||||||
for file in "${diffs[@]}"
|
|
||||||
do
|
|
||||||
f="$(trim "$file")"
|
|
||||||
echo restoring $f from repo >> $logfile
|
|
||||||
if [ $test_run == $false ]; then
|
|
||||||
git checkout HEAD -- $f | tee -a $logfile
|
|
||||||
else
|
|
||||||
echo skipping restore for $f, doing test run | tee -a $logfile
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo no files different from github version >> $logfile
|
|
||||||
fi
|
|
||||||
|
|
||||||
# lets test merge, in memory, no changes to working directory or local repo
|
|
||||||
test_merge_output=$(git merge-tree `git merge-base $current_branch HEAD` HEAD $current_branch | grep "^<<<<<<<\|changed in both")
|
|
||||||
echo "test merge result rc='$test_merge_output' , if empty, no conflicts" >> $logfile
|
|
||||||
|
|
||||||
# if there were no conflicts reported
|
|
||||||
if [ "$test_merge_output." == "." ]; then
|
|
||||||
|
|
||||||
if [ $test_run == $false ]; then
|
|
||||||
# go ahead and merge now
|
|
||||||
echo "executing merge, apply specified" >> $logfile
|
|
||||||
# get the text output of merge
|
|
||||||
merge_output=$(git merge $remote/$current_branch 2>&1)
|
|
||||||
# and its return code
|
|
||||||
merge_result=$?
|
|
||||||
# make any long line readable
|
|
||||||
merge_output=$(echo $merge_output | tr '|' '\n'| sed "s/create/\\${lf}create/g" | sed "s/mode\ change/\\${lf}mode\ change/g")
|
|
||||||
echo -e "merge result rc= $merge_result\n $merge_output">> $logfile
|
|
||||||
else
|
|
||||||
echo "skipping merge, only test run" >> $logfile
|
|
||||||
merge_output=''
|
|
||||||
merge_result=0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if no merge errors
|
|
||||||
if [ $merge_result == 0 ]; then
|
|
||||||
# some updates applied
|
|
||||||
if [ "$merge_output." != 'Already up to date.' -o $test_run == $true ]; then
|
|
||||||
# update any dependencies for base
|
|
||||||
if [ $doinstalls == $true ]; then
|
|
||||||
# if this is a pi zero
|
|
||||||
echo processor architecture is $arch >> $logfile
|
|
||||||
if [ "$arch" == "armv6l" ]; then
|
|
||||||
# force to look like pi 2
|
|
||||||
echo forcing architecture armv7l >>$logfile
|
|
||||||
forced_arch='--arch=armv7l'
|
|
||||||
fi
|
|
||||||
echo "updating MagicMirror runtime, please wait" | tee -a $logfile
|
|
||||||
npm install $forced_arch 2>&1 | tee -a $logfile
|
|
||||||
done_update=`date +"completed - %a %b %e %H:%M:%S %Z %Y"`
|
|
||||||
echo npm install $done_update on base >> $ logfile
|
|
||||||
fi
|
|
||||||
# process updates for modules after base changed
|
|
||||||
cd modules
|
|
||||||
if [ $justActive == $true ]; then
|
|
||||||
# get the list of ACTIVE modules with package.json files
|
|
||||||
mtype=active
|
|
||||||
modules=$(node ../installers/dumpactivemodules.js)
|
|
||||||
else
|
|
||||||
# get the list of INSTALLED modules with package.json files
|
|
||||||
mtype=installed
|
|
||||||
modules=$(find -maxdepth 2 -name 'package.json' -printf "%h\n" | cut -d'/' -f2 )
|
|
||||||
fi
|
|
||||||
modules=($modules) # split to array $modules
|
|
||||||
|
|
||||||
# if the array has entries in it
|
|
||||||
if [ ${#modules[@]} -gt 0 ]; then
|
|
||||||
echo >> $logfile
|
|
||||||
echo "processing dependency changes for $mtype modules with package.json files" | tee -a $logfile
|
|
||||||
echo
|
|
||||||
for module in "${modules[@]}"
|
|
||||||
do
|
|
||||||
echo "processing for module" $module please wait | tee -a $logfile
|
|
||||||
echo '----------------------------------' | tee -a $logfile
|
|
||||||
# change to that directory
|
|
||||||
cd $module
|
|
||||||
# process its dependencies
|
|
||||||
if [ $doinstalls == $true ]; then
|
|
||||||
npm install $forced_arch 2>&1| tee -a $logfile
|
|
||||||
else
|
|
||||||
echo skipped processing for $module, doing test run | tee -a $logfile
|
|
||||||
fi
|
|
||||||
# return to modules folder
|
|
||||||
cd .. >/dev/null
|
|
||||||
echo "processing complete for module" $module | tee -a $logfile
|
|
||||||
echo
|
|
||||||
done
|
|
||||||
else
|
|
||||||
echo "no modules found needing npm refresh" | tee -a $logfile
|
|
||||||
fi
|
|
||||||
# return to Magic Mirror folder
|
|
||||||
cd .. >/dev/null
|
|
||||||
else
|
|
||||||
echo "no changes detected for modules, skipping " | tee -a $logfile
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo there were merge errors | tee -a $logfile
|
|
||||||
echo $merge_output | tee -a %logfile
|
|
||||||
echo you should examine and resolve them | tee -a $logfile
|
|
||||||
echo using the command git log --oneline --decorate | tee -a $logfile
|
|
||||||
git log --oneline --decorate | tee -a $logfile
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "there are merge conflicts to be resolved, no changes have been applied" | tee -a $logfile
|
|
||||||
echo $test_merge_output | tee -a $logfile
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "MagicMirror git fetch failed" | tee -a $logfile
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "local version $local_version already same as master $remote_version" | tee -a $logfile
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Unable to determine upstream git repository" | tee -a $logfile
|
|
||||||
fi
|
|
||||||
# should be in MagicMirror base
|
|
||||||
cd css
|
|
||||||
# restore custom.css
|
|
||||||
echo "restoring custom.css" | tee -a $logfile
|
|
||||||
cp -p save_custom.css custom.css
|
|
||||||
rm save_custom.css
|
|
||||||
cd - >/dev/null
|
|
||||||
if [ "$lang." != "en_US.UTF-8." ]; then
|
|
||||||
if [ "$save_alias." != "." ]; then
|
|
||||||
echo restoring git alias >>$logfile
|
|
||||||
$save_alias >/dev/null
|
|
||||||
else
|
|
||||||
echo removing git alias >>$logfile
|
|
||||||
unalias git >/dev/null
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
IFS=$SAVEIFS # Restore IFS
|
|
||||||
|
|
||||||
if [ $stashed == $true ]; then
|
|
||||||
if [ $test_run == $true ]; then
|
|
||||||
echo test run, restoring files stashed | tee -a $logfile
|
|
||||||
git git_user_name git_user_email stash pop >> $logfile
|
|
||||||
else
|
|
||||||
echo we stashed a set of files that appear changed from the latest repo versions. you should review them | tee -a $logfile
|
|
||||||
git stash show --name-only > installers/stashed_files
|
|
||||||
echo see installers/stashed_files for the list
|
|
||||||
echo
|
|
||||||
echo you can use git checkout "stash@{0}" -- filename to extract one file from the stash
|
|
||||||
echo
|
|
||||||
echo or git stash pop to restore them all
|
|
||||||
echo
|
|
||||||
echo WARNING..
|
|
||||||
echo WARNING.. either will overlay the file just installed by the update
|
|
||||||
echo WARNING..
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# return to original folder
|
|
||||||
cd - >/dev/null
|
|
||||||
date +"Upgrade ended - %a %b %e %H:%M:%S %Z %Y" >>$logfile
|
|
||||||
else
|
|
||||||
echo It appears MagicMirror has not been installed on this system
|
|
||||||
echo please run the installer, "raspberry.sh" first
|
|
||||||
fi
|
|
||||||
|
|
@@ -4,7 +4,6 @@
|
|||||||
* By Michael Teeuw http://michaelteeuw.nl
|
* By Michael Teeuw http://michaelteeuw.nl
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var Server = require(__dirname + "/server.js");
|
var Server = require(__dirname + "/server.js");
|
||||||
var Utils = require(__dirname + "/utils.js");
|
var Utils = require(__dirname + "/utils.js");
|
||||||
@@ -15,7 +14,7 @@ var path = require("path");
|
|||||||
require("module-alias/register");
|
require("module-alias/register");
|
||||||
|
|
||||||
// add timestamps in front of log messages
|
// add timestamps in front of log messages
|
||||||
require("console-stamp")(console, "HH:MM:ss.l");
|
require("console-stamp")(console, "yyyy-mm-dd HH:MM:ss.l");
|
||||||
|
|
||||||
// Get version number.
|
// Get version number.
|
||||||
global.version = JSON.parse(fs.readFileSync("package.json", "utf8")).version;
|
global.version = JSON.parse(fs.readFileSync("package.json", "utf8")).version;
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
/* jshint esversion: 6 */
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const electron = require("electron");
|
const electron = require("electron");
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
/* global Log, Loader, Module, config, defaults */
|
/* global Log, Loader, Module, config, defaults */
|
||||||
/* jshint -W020, esversion: 6 */
|
|
||||||
|
|
||||||
/* Magic Mirror
|
/* Magic Mirror
|
||||||
* Main System
|
* Main System
|
||||||
@@ -306,7 +305,9 @@ var MM = (function() {
|
|||||||
module.showHideTimer = setTimeout(function() {
|
module.showHideTimer = setTimeout(function() {
|
||||||
if (typeof callback === "function") { callback(); }
|
if (typeof callback === "function") { callback(); }
|
||||||
}, speed);
|
}, speed);
|
||||||
|
} else {
|
||||||
|
// invoke callback
|
||||||
|
if (typeof callback === "function") { callback(); }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
11
js/module.js
@@ -151,9 +151,9 @@ var Module = Class.extend({
|
|||||||
*/
|
*/
|
||||||
notificationReceived: function (notification, payload, sender) {
|
notificationReceived: function (notification, payload, sender) {
|
||||||
if (sender) {
|
if (sender) {
|
||||||
Log.log(this.name + " received a module notification: " + notification + " from sender: " + sender.name);
|
// Log.log(this.name + " received a module notification: " + notification + " from sender: " + sender.name);
|
||||||
} else {
|
} else {
|
||||||
Log.log(this.name + " received a system notification: " + notification);
|
// Log.log(this.name + " received a system notification: " + notification);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -417,8 +417,11 @@ var Module = Class.extend({
|
|||||||
callback = callback || function () { };
|
callback = callback || function () { };
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
this.resume();
|
var self = this;
|
||||||
MM.showModule(this, speed, callback, options);
|
MM.showModule(this, speed, function () {
|
||||||
|
self.resume();
|
||||||
|
callback;
|
||||||
|
}, options);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
14
js/server.js
@@ -7,8 +7,6 @@
|
|||||||
|
|
||||||
var express = require("express");
|
var express = require("express");
|
||||||
var app = require("express")();
|
var app = require("express")();
|
||||||
var server = require("http").Server(app);
|
|
||||||
var io = require("socket.io")(server);
|
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
var ipfilter = require("express-ipfilter").IpFilter;
|
var ipfilter = require("express-ipfilter").IpFilter;
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
@@ -22,6 +20,18 @@ var Server = function(config, callback) {
|
|||||||
port = process.env.MM_PORT;
|
port = process.env.MM_PORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var server = null;
|
||||||
|
if(config.useHttps){
|
||||||
|
var options = {
|
||||||
|
key: fs.readFileSync(config.httpsPrivateKey),
|
||||||
|
cert: fs.readFileSync(config.httpsCertificate)
|
||||||
|
};
|
||||||
|
server = require("https").Server(options, app);
|
||||||
|
}else{
|
||||||
|
server = require("http").Server(app);
|
||||||
|
}
|
||||||
|
var io = require("socket.io")(server);
|
||||||
|
|
||||||
console.log("Starting server on port " + port + " ... ");
|
console.log("Starting server on port " + port + " ... ");
|
||||||
|
|
||||||
server.listen(port, config.address ? config.address : "localhost");
|
server.listen(port, config.address ? config.address : "localhost");
|
||||||
|
@@ -8,7 +8,9 @@ var MMSocket = function(moduleName) {
|
|||||||
self.moduleName = moduleName;
|
self.moduleName = moduleName;
|
||||||
|
|
||||||
// Private Methods
|
// Private Methods
|
||||||
self.socket = io("/" + self.moduleName);
|
self.socket = io("/" + self.moduleName, {
|
||||||
|
path: window.location.pathname + "socket.io"
|
||||||
|
});
|
||||||
var notificationCallback = function() {};
|
var notificationCallback = function() {};
|
||||||
|
|
||||||
var onevent = self.socket.onevent;
|
var onevent = self.socket.onevent;
|
||||||
|
@@ -1,721 +0,0 @@
|
|||||||
# MagicMirror² Module Development Documentation
|
|
||||||
|
|
||||||
This document describes the way to develop your own MagicMirror² modules.
|
|
||||||
|
|
||||||
Table of Contents:
|
|
||||||
|
|
||||||
- Module structure
|
|
||||||
- Files
|
|
||||||
|
|
||||||
- The Core module file: modulename.js
|
|
||||||
- Available module instance properties
|
|
||||||
- Subclassable module methods
|
|
||||||
- Module instance methods
|
|
||||||
- Visibility locking
|
|
||||||
|
|
||||||
- The Node Helper: node_helper.js
|
|
||||||
- Available module instance properties
|
|
||||||
- Subclassable module methods
|
|
||||||
- Module instance methods
|
|
||||||
|
|
||||||
- MagicMirror Helper Methods
|
|
||||||
- Module Selection
|
|
||||||
|
|
||||||
- MagicMirror Logger
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
## General Advice
|
|
||||||
|
|
||||||
As MagicMirror has gained huge popularity, so has the number of available modules. For new users and developers alike, it is very time consuming to navigate around the various repositories in order to find out what exactly a certain modules does, how it looks and what it depends on. Unfortunately, this information is rarely available, nor easily obtained without having to install it first.
|
|
||||||
Therefore **we highly recommend you to include the following information in your README file.**
|
|
||||||
|
|
||||||
- A high quality screenshot of your working module
|
|
||||||
- A short, one sentence, clear description what it does (duh!)
|
|
||||||
- What external API's it depends upon, including web links to those
|
|
||||||
- Whether the API/request require a key and the user limitations of those. (Is it free?)
|
|
||||||
|
|
||||||
Surely this also help you get better recognition and feedback for your work.
|
|
||||||
|
|
||||||
## Module structure
|
|
||||||
|
|
||||||
All modules are loaded in the `modules` folder. The default modules are grouped together in the `modules/default` folder. Your module should be placed in a subfolder of `modules`. Note that any file or folder your create in the `modules` folder will be ignored by git, allowing you to upgrade the MagicMirror² without the loss of your files.
|
|
||||||
|
|
||||||
A module can be placed in one single folder. Or multiple modules can be grouped in a subfolder. Note that name of the module must be unique. Even when a module with a similar name is placed in a different folder, they can't be loaded at the same time.
|
|
||||||
|
|
||||||
### Files
|
|
||||||
- **modulename/modulename.js** - This is your core module script.
|
|
||||||
- **modulename/node_helper.js** - This is an optional helper that will be loaded by the node script. The node helper and module script can communicate with each other using an integrated socket system.
|
|
||||||
- **modulename/public** - Any files in this folder can be accessed via the browser on `/modulename/filename.ext`.
|
|
||||||
- **modulename/anyfileorfolder** Any other file or folder in the module folder can be used by the core module script. For example: *modulename/css/modulename.css* would be a good path for your additional module styles.
|
|
||||||
|
|
||||||
## The Core module file: modulename.js
|
|
||||||
This is the script in which the module will be defined. This script is required in order for the module to be used. In it's most simple form, the core module file must contain:
|
|
||||||
````javascript
|
|
||||||
Module.register("modulename",{});
|
|
||||||
````
|
|
||||||
Of course, the above module would not do anything fancy, so it's good to look at one of the simplest modules: **helloworld**:
|
|
||||||
|
|
||||||
````javascript
|
|
||||||
//helloworld.js:
|
|
||||||
|
|
||||||
Module.register("helloworld",{
|
|
||||||
// Default module config.
|
|
||||||
defaults: {
|
|
||||||
text: "Hello World!"
|
|
||||||
},
|
|
||||||
|
|
||||||
// Override dom generator.
|
|
||||||
getDom: function() {
|
|
||||||
var wrapper = document.createElement("div");
|
|
||||||
wrapper.innerHTML = this.config.text;
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
````
|
|
||||||
|
|
||||||
As you can see, the `Module.register()` method takes two arguments: the name of the module and an object with the module properties.
|
|
||||||
|
|
||||||
### Available module instance properties
|
|
||||||
After the module is initialized, the module instance has a few available module properties:
|
|
||||||
|
|
||||||
| Instance Property | Type | Description |
|
|
||||||
|:----------------- |:---- |:----------- |
|
|
||||||
| `this.name` | String | The name of the module. |
|
|
||||||
| `this.identifier` | String | This is a unique identifier for the module instance. |
|
|
||||||
| `this.hidden` | Boolean | This represents if the module is currently hidden (faded away). |
|
|
||||||
| `this.config` | Boolean | The configuration of the module instance as set in the user's `config.js` file. This config will also contain the module's defaults if these properties are not over-written by the user config. |
|
|
||||||
| `this.data` | Object | The data object contain additional metadata about the module instance. (See below) |
|
|
||||||
|
|
||||||
|
|
||||||
The `this.data` data object contain the following metadata:
|
|
||||||
- `data.classes` - The classes which are added to the module dom wrapper.
|
|
||||||
- `data.file` - The filename of the core module file.
|
|
||||||
- `data.path` - The path of the module folder.
|
|
||||||
- `data.header` - The header added to the module.
|
|
||||||
- `data.position` - The position in which the instance will be shown.
|
|
||||||
|
|
||||||
|
|
||||||
#### `defaults: {}`
|
|
||||||
Any properties defined in the defaults object, will be merged with the module config as defined in the user's config.js file. This is the best place to set your modules' configuration defaults. Any of the module configuration properties can be accessed using `this.config.propertyName`, but more about that later.
|
|
||||||
|
|
||||||
#### `requiresVersion:`
|
|
||||||
|
|
||||||
*Introduced in version: 2.1.0.*
|
|
||||||
|
|
||||||
A string that defines the minimum version of the MagicMirror framework. If it is set, the system compares the required version with the users version. If the version of the user is out of date, it won't run the module. Make sure to also set this value in the Node helper.
|
|
||||||
|
|
||||||
**Note:** Since this check is introduced in version 2.1.0, this check will not be run in older versions. Keep this in mind if you get issue reports on your module.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
````javascript
|
|
||||||
requiresVersion: "2.1.0",
|
|
||||||
````
|
|
||||||
|
|
||||||
### Subclassable module methods
|
|
||||||
|
|
||||||
#### `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 and 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:
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
start: function() {
|
|
||||||
this.mySpecialProperty = "So much wow!";
|
|
||||||
Log.log(this.name + ' is started!');
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
#### `getScripts()`
|
|
||||||
**Should return: Array**
|
|
||||||
|
|
||||||
The getScripts method is called to request any additional scripts that need to be loaded. This method should therefore return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.js')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
getScripts: function() {
|
|
||||||
return [
|
|
||||||
'script.js', // will try to load it from the vendor folder, otherwise it will load is from the module folder.
|
|
||||||
'moment.js', // this file is available in the vendor folder, so it doesn't need to be available in the module folder.
|
|
||||||
this.file('anotherfile.js'), // this file will be loaded straight from the module folder.
|
|
||||||
'https://code.jquery.com/jquery-2.2.3.min.js', // this file will be loaded from the jquery servers.
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
````
|
|
||||||
**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore, it's advised not to use any external urls.
|
|
||||||
|
|
||||||
|
|
||||||
#### `getStyles()`
|
|
||||||
**Should return: Array**
|
|
||||||
|
|
||||||
The getStyles method is called to request any additional stylesheets that need to be loaded. This method should therefore return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.css')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
getStyles: function() {
|
|
||||||
return [
|
|
||||||
'script.css', // will try to load it from the vendor folder, otherwise it will load is from the module folder.
|
|
||||||
'font-awesome.css', // this file is available in the vendor folder, so it doesn't need to be available in the module folder.
|
|
||||||
this.file('anotherfile.css'), // this file will be loaded straight from the module folder.
|
|
||||||
'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css', // this file will be loaded from the bootstrapcdn servers.
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
````
|
|
||||||
**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore, it's advised not to use any external URLs.
|
|
||||||
|
|
||||||
#### `getTranslations()`
|
|
||||||
**Should return: Dictionary**
|
|
||||||
|
|
||||||
The getTranslations method is called to request translation files that need to be loaded. This method should therefore return a dictionary with the files to load, identified by the country's short name.
|
|
||||||
|
|
||||||
If the module does not have any module specific translations, the function can just be omitted or return `false`.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
getTranslations: function() {
|
|
||||||
return {
|
|
||||||
en: "translations/en.json",
|
|
||||||
de: "translations/de.json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
````
|
|
||||||
|
|
||||||
#### `getDom()`
|
|
||||||
**Should return:** Dom Object
|
|
||||||
|
|
||||||
Whenever the MagicMirror needs to update the information on screen (because it starts, or because your module asked a refresh using `this.updateDom()`), the system calls the getDom method. This method should therefore return a dom object.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
getDom: function() {
|
|
||||||
var wrapper = document.createElement("div");
|
|
||||||
wrapper.innerHTML = 'Hello world!';
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
````
|
|
||||||
|
|
||||||
#### `getHeader()`
|
|
||||||
**Should return:** String
|
|
||||||
|
|
||||||
Whenever the MagicMirror needs to update the information on screen (because it starts, or because your module asked a refresh using `this.updateDom()`), the system calls the getHeader method to retrieve the module's header. This method should therefor return a string. If this method is not subclassed, this function will return the user's configured header.
|
|
||||||
|
|
||||||
If you want to use the original user's configured header, reference `this.data.header`.
|
|
||||||
|
|
||||||
**NOTE:** If the user did not configure a default header, no header will be displayed and thus this method will not be called.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
getHeader: function() {
|
|
||||||
return this.data.header + ' Foo Bar';
|
|
||||||
}
|
|
||||||
|
|
||||||
````
|
|
||||||
|
|
||||||
#### `notificationReceived(notification, payload, sender)`
|
|
||||||
|
|
||||||
That MagicMirror core has the ability to send notifications to modules. Or even better: the modules have the possibility to send notifications to other modules. When this module is called, it has 3 arguments:
|
|
||||||
|
|
||||||
- `notification` - String - The notification identifier.
|
|
||||||
- `payload` - AnyType - The payload of a notification.
|
|
||||||
- `sender` - Module - The sender of the notification. If this argument is `undefined`, the sender of the notification is the core system.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
notificationReceived: function(notification, payload, sender) {
|
|
||||||
if (sender) {
|
|
||||||
Log.log(this.name + " received a module notification: " + notification + " from sender: " + sender.name);
|
|
||||||
} else {
|
|
||||||
Log.log(this.name + " received a system notification: " + notification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
**Note:** the system sends three notifications when starting up. These notifications could come in handy!
|
|
||||||
|
|
||||||
|
|
||||||
- `ALL_MODULES_STARTED` - All modules are started. You can now send notifications to other modules.
|
|
||||||
- `DOM_OBJECTS_CREATED` - All dom objects are created. The system is now ready to perform visual changes.
|
|
||||||
- `MODULE_DOM_CREATED` - This module's dom has been fully loaded. You can now access your module's dom objects.
|
|
||||||
|
|
||||||
|
|
||||||
#### `socketNotificationReceived: function(notification, payload)`
|
|
||||||
When using a node_helper, the node helper can send your module notifications. When this module is called, it has 2 arguments:
|
|
||||||
|
|
||||||
- `notification` - String - The notification identifier.
|
|
||||||
- `payload` - AnyType - The payload of a notification.
|
|
||||||
|
|
||||||
**Note 1:** When a node helper sends a notification, all modules of that module type receive the same notifications. <br>
|
|
||||||
**Note 2:** The socket connection is established as soon as the module sends its first message using [sendSocketNotification](#thissendsocketnotificationnotification-payload).
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
socketNotificationReceived: function(notification, payload) {
|
|
||||||
Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
|
|
||||||
},
|
|
||||||
````
|
|
||||||
|
|
||||||
#### `suspend()`
|
|
||||||
When a module is hidden (using the `module.hide()` method), the `suspend()` method will be called. By subclassing this method you can perform tasks like halting the update timers.
|
|
||||||
|
|
||||||
#### `resume()`
|
|
||||||
When a module is requested to be shown (using the `module.show()` method), the `resume()` method will be called. By subclassing this method you can perform tasks restarting the update timers.
|
|
||||||
|
|
||||||
|
|
||||||
### Module instance methods
|
|
||||||
|
|
||||||
Each module instance has some handy methods which can be helpful building your module.
|
|
||||||
|
|
||||||
|
|
||||||
#### `this.file(filename)`
|
|
||||||
***filename* String** - The name of the file you want to create the path for.<br>
|
|
||||||
**Returns String**
|
|
||||||
|
|
||||||
If you want to create a path to a file in your module folder, use the `file()` method. It returns the path to the filename given as the attribute. Is method comes in handy when configuring the [getScripts](#getscripts) and [getStyles](#getstyles) methods.
|
|
||||||
|
|
||||||
#### `this.updateDom(speed)`
|
|
||||||
***speed* Number** - Optional. Animation speed in milliseconds.<br>
|
|
||||||
|
|
||||||
Whenever your module need to be updated, call the `updateDom(speed)` method. It requests the MagicMirror core to update its dom object. If you define the speed, the content update will be animated, but only if the content will really change.
|
|
||||||
|
|
||||||
As an example: the clock modules calls this method every second:
|
|
||||||
|
|
||||||
````javascript
|
|
||||||
...
|
|
||||||
start: function() {
|
|
||||||
var self = this;
|
|
||||||
setInterval(function() {
|
|
||||||
self.updateDom(); // no speed defined, so it updates instantly.
|
|
||||||
}, 1000); //perform every 1000 milliseconds.
|
|
||||||
},
|
|
||||||
...
|
|
||||||
````
|
|
||||||
|
|
||||||
#### `this.sendNotification(notification, payload)`
|
|
||||||
***notification* String** - The notification identifier.<br>
|
|
||||||
***payload* AnyType** - Optional. A notification payload.<br>
|
|
||||||
|
|
||||||
If you want to send a notification to all other modules, use the `sendNotification(notification, payload)`. All other modules will receive the message via the [notificationReceived](#notificationreceivednotification-payload-sender) method. In that case, the sender is automatically set to the instance calling the sendNotification method.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
this.sendNotification('MYMODULE_READY_FOR_ACTION', {foo:bar});
|
|
||||||
````
|
|
||||||
|
|
||||||
#### `this.sendSocketNotification(notification, payload)`
|
|
||||||
***notification* String** - The notification identifier.<br>
|
|
||||||
***payload* AnyType** - Optional. A notification payload.<br>
|
|
||||||
|
|
||||||
If you want to send a notification to the node_helper, use the `sendSocketNotification(notification, payload)`. Only the node_helper of this module will receive the socket notification.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
this.sendSocketNotification('SET_CONFIG', this.config);
|
|
||||||
````
|
|
||||||
|
|
||||||
#### `this.hide(speed, callback, options)`
|
|
||||||
***speed* Number** - Optional (Required when setting callback or options), The speed of the hide animation in milliseconds.
|
|
||||||
***callback* Function** - Optional, The callback after the hide animation is finished.
|
|
||||||
***options* Function** - Optional, Object with additional options for the hide action (see below). (*Introduced in version: 2.1.0.*)
|
|
||||||
|
|
||||||
To hide a module, you can call the `hide(speed, callback)` method. You can call the hide method on the module instance itself using `this.hide()`, but of course you can also hide another module using `anOtherModule.hide()`.
|
|
||||||
|
|
||||||
Possible configurable options:
|
|
||||||
|
|
||||||
- `lockString` - String - When setting lock string, the module can not be shown without passing the correct lockstring. This way (multiple) modules can prevent a module from showing. It's considered best practice to use your modules identifier as the locksString: `this.identifier`. See *visibility locking* below.
|
|
||||||
|
|
||||||
|
|
||||||
**Note 1:** If the hide animation is cancelled, for instance because the show method is called before the hide animation was finished, the callback will not be called.<br>
|
|
||||||
**Note 2:** If the hide animation is hijacked (an other method calls hide on the same module), the callback will not be called.<br>
|
|
||||||
**Note 3:** If the dom is not yet created, the hide method won't work. Wait for the `DOM_OBJECTS_CREATED` [notification](#notificationreceivednotification-payload-sender).
|
|
||||||
|
|
||||||
|
|
||||||
#### `this.show(speed, callback, options)`
|
|
||||||
***speed* Number** - Optional (Required when setting callback or options), The speed of the show animation in milliseconds.
|
|
||||||
***callback* Function** - Optional, The callback after the show animation is finished.
|
|
||||||
***options* Function** - Optional, Object with additional options for the show action (see below). (*Introduced in version: 2.1.0.*)
|
|
||||||
|
|
||||||
To show a module, you can call the `show(speed, callback)` method. You can call the show method on the module instance itself using `this.show()`, but of course you can also show another module using `anOtherModule.show()`.
|
|
||||||
|
|
||||||
Possible configurable options:
|
|
||||||
|
|
||||||
- `lockString` - String - When setting lock string, the module can not be shown without passing the correct lockstring. This way (multiple) modules can prevent a module from showing. See *visibility locking* below.
|
|
||||||
- `force` - Boolean - When setting the force tag to `true`, the locking mechanism will be overwritten. Use this option with caution. It's considered best practice to let the usage of the force option be use- configurable. See *visibility locking* below.
|
|
||||||
|
|
||||||
**Note 1:** If the show animation is canceled, for instance because the hide method is called before the show animation was finished, the callback will not be called.<br>
|
|
||||||
**Note 2:** If the show animation is hijacked (an other method calls show on the same module), the callback will not be called.<br>
|
|
||||||
**Note 3:** If the dom is not yet created, the show method won't work. Wait for the `DOM_OBJECTS_CREATED` [notification](#notificationreceivednotification-payload-sender).
|
|
||||||
|
|
||||||
#### Visibility locking
|
|
||||||
|
|
||||||
(*Introduced in version: 2.1.0.*)
|
|
||||||
|
|
||||||
Visibility locking helps the module system to prevent unwanted hide/show actions. The following scenario explains the concept:
|
|
||||||
|
|
||||||
**Module B asks module A to hide:**
|
|
||||||
````javascript
|
|
||||||
moduleA.hide(0, {lockString: "module_b_identifier"});
|
|
||||||
````
|
|
||||||
Module A is now hidden, and has an lock array with the following strings:
|
|
||||||
````javascript
|
|
||||||
moduleA.lockStrings == ["module_b_identifier"]
|
|
||||||
moduleA.hidden == true
|
|
||||||
````
|
|
||||||
**Module C asks module A to hide:**
|
|
||||||
````javascript
|
|
||||||
moduleA.hide(0, {lockString: "module_c_identifier"});
|
|
||||||
````
|
|
||||||
Module A is now hidden, and has an lock array with the following strings:
|
|
||||||
````javascript
|
|
||||||
moduleA.lockStrings == ["module_b_identifier", "module_c_identifier"]
|
|
||||||
moduleA.hidden == true
|
|
||||||
````
|
|
||||||
**Module B asks module A to show:**
|
|
||||||
````javascript
|
|
||||||
moduleA.show(0, {lockString: "module_b_identifier"});
|
|
||||||
````
|
|
||||||
The lockString will be removed from moduleA’s locks array, but since there still is an other lock string available, the module remains hidden:
|
|
||||||
````javascript
|
|
||||||
moduleA.lockStrings == ["module_c_identifier"]
|
|
||||||
moduleA.hidden == true
|
|
||||||
````
|
|
||||||
**Module C asks module A to show:**
|
|
||||||
````javascript
|
|
||||||
moduleA.show(0, {lockString: "module_c_identifier"});
|
|
||||||
````
|
|
||||||
The lockString will be removed from moduleA’s locks array, and since this will result in an empty lock array, the module will be visible:
|
|
||||||
````javascript
|
|
||||||
moduleA.lockStrings == []
|
|
||||||
moduleA.hidden == false
|
|
||||||
````
|
|
||||||
|
|
||||||
**Note:** The locking mechanism can be overwritten by using the force tag:
|
|
||||||
````javascript
|
|
||||||
moduleA.show(0, {force: true});
|
|
||||||
````
|
|
||||||
This will reset the lockstring array, and will show the module.
|
|
||||||
````javascript
|
|
||||||
moduleA.lockStrings == []
|
|
||||||
moduleA.hidden == false
|
|
||||||
````
|
|
||||||
|
|
||||||
Use this `force` method with caution. See `show()` method for more information.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### `this.translate(identifier)`
|
|
||||||
***identifier* String** - Identifier of the string that should be translated.
|
|
||||||
|
|
||||||
The Magic Mirror contains a convenience wrapper for `l18n`. You can use this to automatically serve different translations for your modules based on the user's `language` configuration.
|
|
||||||
|
|
||||||
If no translation is found, a fallback will be used. The fallback sequence is as follows:
|
|
||||||
- 1. Translation as defined in module translation file of the user's preferred language.
|
|
||||||
- 2. Translation as defined in core translation file of the user's preferred language.
|
|
||||||
- 3. Translation as defined in module translation file of the fallback language (the first defined module translation file).
|
|
||||||
- 4. Translation as defined in core translation file of the fallback language (the first defined core translation file).
|
|
||||||
- 5. The key (identifier) of the translation.
|
|
||||||
|
|
||||||
When adding translations to your module, it's a good idea to see if an appropriate translation is already available in the [core translation files](https://github.com/MichMich/MagicMirror/tree/master/translations). This way, your module can benefit from the existing translations.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
this.translate("INFO") //Will return a translated string for the identifier INFO
|
|
||||||
````
|
|
||||||
|
|
||||||
**Example json file:**
|
|
||||||
````javascript
|
|
||||||
{
|
|
||||||
"INFO": "Really important information!"
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
**Note:** although comments are officially not supported in JSON files, MagicMirror allows it by stripping the comments before parsing the JSON file. Comments in translation files could help other translators.
|
|
||||||
|
|
||||||
##### `this.translate(identifier, variables)`
|
|
||||||
***identifier* String** - Identifier of the string that should be translated.
|
|
||||||
***variables* Object** - Object of variables to be used in translation.
|
|
||||||
|
|
||||||
This improved and backwards compatible way to handle translations behaves like the normal translation function and follows the rules described above. It's recommended to use this new format for translating everywhere. It allows translator to change the word order in the sentence to be translated.
|
|
||||||
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
var timeUntilEnd = moment(event.endDate, "x").fromNow(true);
|
|
||||||
this.translate("RUNNING", { "timeUntilEnd": timeUntilEnd) }); // Will return a translated string for the identifier RUNNING, replacing `{timeUntilEnd}` with the contents of the variable `timeUntilEnd` in the order that translator intended.
|
|
||||||
````
|
|
||||||
|
|
||||||
**Example English .json file:**
|
|
||||||
````javascript
|
|
||||||
{
|
|
||||||
"RUNNING": "Ends in {timeUntilEnd}",
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
**Example Finnish .json file:**
|
|
||||||
````javascript
|
|
||||||
{
|
|
||||||
"RUNNING": "Päättyy {timeUntilEnd} päästä",
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
**Note:** The *variables* Object has an special case called `fallback`. It's used to support old translations in translation files that do not have the variables in them. If you are upgrading an old module that had translations that did not support the word order, it is recommended to have the fallback layout.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
var timeUntilEnd = moment(event.endDate, "x").fromNow(true);
|
|
||||||
this.translate("RUNNING", {
|
|
||||||
"fallback": this.translate("RUNNING") + " {timeUntilEnd}"
|
|
||||||
"timeUntilEnd": timeUntilEnd
|
|
||||||
)}); // Will return a translated string for the identifier RUNNING, replacing `{timeUntilEnd}` with the contents of the variable `timeUntilEnd` in the order that translator intended. (has a fallback)
|
|
||||||
````
|
|
||||||
|
|
||||||
**Example Swedish .json file that does not have the variable in it:**
|
|
||||||
````javascript
|
|
||||||
{
|
|
||||||
"RUNNING": "Slutar",
|
|
||||||
}
|
|
||||||
````
|
|
||||||
In this case the `translate`-function will not find any variables in the translation, will look for `fallback` variable and use that if possible to create the translation.
|
|
||||||
|
|
||||||
## The Node Helper: node_helper.js
|
|
||||||
|
|
||||||
The node helper is a Node.js script that is able to do some backend task to support your module. For every module type, only one node helper instance will be created. For example: if your MagicMirror uses two calendar modules, there will be only one calendar node helper instantiated.
|
|
||||||
|
|
||||||
**Note:** Because there is only one node helper per module type, there is no default config available within your module. It's your task to send the desired config from your module to your node helper.
|
|
||||||
|
|
||||||
In it's most simple form, the node_helper.js file must contain:
|
|
||||||
|
|
||||||
````javascript
|
|
||||||
var NodeHelper = require("node_helper");
|
|
||||||
module.exports = NodeHelper.create({});
|
|
||||||
````
|
|
||||||
|
|
||||||
Of course, the above helper would not do anything useful. So with the information above, you should be able to make it a bit more sophisticated.
|
|
||||||
|
|
||||||
### Available module instance properties
|
|
||||||
|
|
||||||
#### `this.name`
|
|
||||||
**String**
|
|
||||||
|
|
||||||
The name of the module
|
|
||||||
|
|
||||||
#### `this.path`
|
|
||||||
**String**
|
|
||||||
|
|
||||||
The path of the module
|
|
||||||
|
|
||||||
#### `this.expressApp`
|
|
||||||
**Express App Instance**
|
|
||||||
|
|
||||||
This is a link to the express instance. It will allow you to define extra routes.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
start: function() {
|
|
||||||
this.expressApp.get('/foobar', function (req, res) {
|
|
||||||
res.send('GET request to /foobar');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
**Note:** By default, a public path to your module's public folder will be created:
|
|
||||||
````javascript
|
|
||||||
this.expressApp.use("/" + this.name, express.static(this.path + "/public"));
|
|
||||||
````
|
|
||||||
|
|
||||||
#### `this.io`
|
|
||||||
**Socket IO Instance**
|
|
||||||
|
|
||||||
This is a link to the IO instance. It will allow you to do some Socket.IO magic. In most cases you won't need this, since the Node Helper has a few convenience methods to make this simple.
|
|
||||||
|
|
||||||
|
|
||||||
#### `requiresVersion:`
|
|
||||||
*Introduced in version: 2.1.0.*
|
|
||||||
|
|
||||||
A string that defines the minimum version of the MagicMirror framework. If it is set, the system compares the required version with the users version. If the version of the user is out of date, it won't run the module.
|
|
||||||
|
|
||||||
**Note:** Since this check is introduced in version 2.1.0, this check will not be run in older versions. Keep this in mind if you get issue reports on your module.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
````javascript
|
|
||||||
requiresVersion: "2.1.0",
|
|
||||||
````
|
|
||||||
|
|
||||||
### Subclassable module methods
|
|
||||||
|
|
||||||
#### `init()`
|
|
||||||
This method is called when a node helper gets instantiated. In most cases you do not need to subclass this method.
|
|
||||||
|
|
||||||
#### `start()`
|
|
||||||
This method is called when all node helpers are loaded and the system is ready to boot up. The start method is a perfect place to define any additional module properties:
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
start: function() {
|
|
||||||
this.mySpecialProperty = "So much wow!";
|
|
||||||
Log.log(this.name + ' is started!');
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
#### `stop()`
|
|
||||||
This method is called when the MagicMirror server receives a `SIGINT` command and is shutting down. This method should include any commands needed to close any open connections, stop any sub-processes and gracefully exit the module.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
stop: function() {
|
|
||||||
console.log("Shutting down MyModule");
|
|
||||||
this.connection.close();
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
#### `socketNotificationReceived: function(notification, payload)`
|
|
||||||
With this method, your node helper can receive notifications from your modules. When this method is called, it has 2 arguments:
|
|
||||||
|
|
||||||
- `notification` - String - The notification identifier.
|
|
||||||
- `payload` - AnyType - The payload of a notification.
|
|
||||||
|
|
||||||
**Note:** The socket connection is established as soon as the module sends its first message using [sendSocketNotification](thissendsocketnotificationnotification-payload).
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
socketNotificationReceived: function(notification, payload) {
|
|
||||||
Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
|
|
||||||
},
|
|
||||||
````
|
|
||||||
|
|
||||||
### Module instance methods
|
|
||||||
|
|
||||||
Each node helper has some handy methods which can be helpful building your module.
|
|
||||||
|
|
||||||
#### `this.sendSocketNotification(notification, payload)`
|
|
||||||
***notification* String** - The notification identifier.<br>
|
|
||||||
***payload* AnyType** - Optional. A notification payload.<br>
|
|
||||||
|
|
||||||
If you want to send a notification to all your modules, use the `sendSocketNotification(notification, payload)`. Only the module of your module type will receive the socket notification.
|
|
||||||
|
|
||||||
**Note:** Since all instances of your module will receive the notifications, it's your task to make sure the right module responds to your messages.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
this.sendSocketNotification('SET_CONFIG', this.config);
|
|
||||||
````
|
|
||||||
|
|
||||||
## MagicMirror Helper Methods
|
|
||||||
|
|
||||||
The core Magic Mirror object: `MM` has some handy method that will help you in controlling your and other modules. Most of the `MM` methods are available via convenience methods on the Module instance.
|
|
||||||
|
|
||||||
### Module selection
|
|
||||||
The only additional method available for your module, is the feature to retrieve references to other modules. This can be used to hide and show other modules.
|
|
||||||
|
|
||||||
#### `MM.getModules()`
|
|
||||||
**Returns Array** - An array with module instances.<br>
|
|
||||||
|
|
||||||
To make a selection of all currently loaded module instances, run the `MM.getModules()` method. It will return an array with all currently loaded module instances. The returned array has a lot of filtering methods. See below for more info.
|
|
||||||
|
|
||||||
**Note:** This method returns an empty array if not all modules are started yet. Wait for the `ALL_MODULES_STARTED` [notification](#notificationreceivednotification-payload-sender).
|
|
||||||
|
|
||||||
|
|
||||||
##### `.withClass(classnames)`
|
|
||||||
***classnames* String or Array** - The class names on which you want to filter.
|
|
||||||
**Returns Array** - An array with module instances.<br>
|
|
||||||
|
|
||||||
If you want to make a selection based on one or more class names, use the withClass method on a result of the `MM.getModules()` method. The argument of the `withClass(classname)` method can be an array, or space separated string.
|
|
||||||
|
|
||||||
**Examples:**
|
|
||||||
````javascript
|
|
||||||
var modules = MM.getModules().withClass('classname');
|
|
||||||
var modules = MM.getModules().withClass('classname1 classname2');
|
|
||||||
var modules = MM.getModules().withClass(['classname1','classname2']);
|
|
||||||
````
|
|
||||||
|
|
||||||
##### `.exceptWithClass(classnames)`
|
|
||||||
***classnames* String or Array** - The class names of the modules you want to remove from the results.
|
|
||||||
**Returns Array** - An array with module instances.<br>
|
|
||||||
|
|
||||||
If you to remove some modules from a selection based on a classname, use the exceptWithClass method on a result of the `MM.getModules()` method. The argument of the `exceptWithClass(classname)` method can be an array, or space separated string.
|
|
||||||
|
|
||||||
**Examples:**
|
|
||||||
````javascript
|
|
||||||
var modules = MM.getModules().exceptWithClass('classname');
|
|
||||||
var modules = MM.getModules().exceptWithClass('classname1 classname2');
|
|
||||||
var modules = MM.getModules().exceptWithClass(['classname1','classname2']);
|
|
||||||
````
|
|
||||||
|
|
||||||
##### `.exceptModule(module)`
|
|
||||||
***module* Module Object** - The reference to a module you want to remove from the results.
|
|
||||||
**Returns Array** - An array with module instances.<br>
|
|
||||||
|
|
||||||
If you to remove a specific module instance from a selection based on a classname, use the exceptWithClass method on a result of the `MM.getModules()` method. This can be helpful if you want to select all module instances except the instance of your module.
|
|
||||||
|
|
||||||
**Examples:**
|
|
||||||
````javascript
|
|
||||||
var modules = MM.getModules().exceptModule(this);
|
|
||||||
````
|
|
||||||
|
|
||||||
Of course, you can combine all of the above filters:
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
````javascript
|
|
||||||
var modules = MM.getModules().withClass('classname1').exceptwithClass('classname2').exceptModule(aModule);
|
|
||||||
````
|
|
||||||
|
|
||||||
##### `.enumerate(callback)`
|
|
||||||
***callback* Function(module)** - The callback run on every instance.
|
|
||||||
|
|
||||||
If you want to perform an action on all selected modules, you can use the `enumerate` function:
|
|
||||||
|
|
||||||
````javascript
|
|
||||||
MM.getModules().enumerate(function(module) {
|
|
||||||
Log.log(module.name);
|
|
||||||
});
|
|
||||||
````
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
To hide all modules except the your module instance, you could write something like:
|
|
||||||
````javascript
|
|
||||||
Module.register("modulename",{
|
|
||||||
//...
|
|
||||||
notificationReceived: function(notification, payload, sender) {
|
|
||||||
if (notification === 'DOM_OBJECTS_CREATED') {
|
|
||||||
MM.getModules().exceptModule(this).enumerate(function(module) {
|
|
||||||
module.hide(1000, function() {
|
|
||||||
//Module hidden.
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//...
|
|
||||||
});
|
|
||||||
````
|
|
||||||
|
|
||||||
## MagicMirror Logger
|
|
||||||
|
|
||||||
The Magic Mirror contains a convenience wrapper for logging. Currently, this logger is a simple proxy to the original `console.log` methods. But it might get additional features in the future. The Loggers is currently only available in the core module file (not in the node_helper).
|
|
||||||
|
|
||||||
**Examples:**
|
|
||||||
````javascript
|
|
||||||
Log.info('error');
|
|
||||||
Log.log('log');
|
|
||||||
Log.error('info');
|
|
||||||
````
|
|
@@ -1,65 +1,4 @@
|
|||||||
# Module: Alert
|
# Module: Alert
|
||||||
The alert module is one of the default modules of the MagicMirror. This module displays notifications from other modules.
|
The alert module is one of the default modules of the MagicMirror. This module displays notifications from other modules.
|
||||||
|
|
||||||
## Usage
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/alert.html).
|
||||||
To use this module, add it to the modules array in the config/config.js file:
|
|
||||||
|
|
||||||
```
|
|
||||||
modules: [
|
|
||||||
{
|
|
||||||
module: "alert",
|
|
||||||
config: {
|
|
||||||
// The config property is optional.
|
|
||||||
// See 'Configuration options' for more information.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration options
|
|
||||||
|
|
||||||
The following properties can be configured:
|
|
||||||
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ----------------- | -----------
|
|
||||||
| `effect` | The animation effect to use for notifications. <br><br> **Possible values:** `scale` `slide` `genie` `jelly` `flip` `exploader` `bouncyflip` <br> **Default value:** `slide`
|
|
||||||
| `alert_effect` | The animation effect to use for alerts. <br><br> **Possible values:** `scale` `slide` `genie` `jelly` `flip` `exploader` `bouncyflip` <br> **Default value:** `jelly`
|
|
||||||
| `display_time` | Time a notification is displayed in milliseconds. <br><br> **Possible values:** `int` <br> **Default value:** `3500`
|
|
||||||
| `position` | Position where the notifications should be displayed. <br><br> **Possible values:** `left` `center` `right` <br> **Default value:** `center`
|
|
||||||
| `welcome_message` | Message shown at startup. <br><br> **Possible values:** `string` `false` <br> **Default value:** `false` (no message at startup)
|
|
||||||
|
|
||||||
|
|
||||||
## Developer notes
|
|
||||||
For notifications use:
|
|
||||||
|
|
||||||
```
|
|
||||||
self.sendNotification("SHOW_ALERT", {type: "notification"});
|
|
||||||
```
|
|
||||||
For alerts use:
|
|
||||||
|
|
||||||
```
|
|
||||||
self.sendNotification("SHOW_ALERT", {});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Notification params
|
|
||||||
| Option | Description
|
|
||||||
| ------------------ | -----------
|
|
||||||
| `title` | The title of the notification. <br><br> **Possible values:** `text` or `html`
|
|
||||||
| `message` | The message of the notification. <br><br> **Possible values:** `text` or `html`
|
|
||||||
| `timer` (optional) | How long the notification should stay visible in ms. <br> If absent, the default `display_time` is used. <br> **Possible values:** `int` `float`
|
|
||||||
|
|
||||||
|
|
||||||
### Alert params
|
|
||||||
| Option | Description
|
|
||||||
| ----------------------------------------------- | -----------
|
|
||||||
| `title` | The title of the alert. <br><br> **Possible values:** `text` or `html`
|
|
||||||
| `message` | The message of the alert. <br><br> **Possible values:** `text` or `html`
|
|
||||||
| `imageUrl` (optional) | Image to show in the alert <br><br> **Possible values:** `url` `path` <br> **Default value:** `none`
|
|
||||||
| `imageFA` (optional) | Font Awesome icon to show in the alert <br><br> **Possible values:** See [Font Awsome](http://fontawesome.io/icons/) website. <br> **Default value:** `none`
|
|
||||||
| `imageHeight` (optional even with imageUrl set) | Height of the image <br><br> **Possible values:** `intpx` <br> **Default value:** `80px`
|
|
||||||
| `timer` (optional) | How long the alert should stay visible in ms. <br> **Important:** If you do not use the `timer`, it is your duty to hide the alert by using `self.sendNotification("HIDE_ALERT");`! <br><br>**Possible values:** `int` `float` <br> **Default value:** `none`
|
|
||||||
|
|
||||||
## Open Source Licenses
|
|
||||||
### [NotificationStyles](https://github.com/codrops/NotificationStyles)
|
|
||||||
See [tympanus.net](http://tympanus.net/codrops/licensing/) for license.
|
|
||||||
|
@@ -2,107 +2,4 @@
|
|||||||
The `calendar` module is one of the default modules of the MagicMirror.
|
The `calendar` module is one of the default modules of the MagicMirror.
|
||||||
This module displays events from a public .ical calendar. It can combine multiple calendars.
|
This module displays events from a public .ical calendar. It can combine multiple calendars.
|
||||||
|
|
||||||
## Using the module
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/calendar.html).
|
||||||
|
|
||||||
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.
|
|
||||||
config: {
|
|
||||||
// The config property is optional.
|
|
||||||
// If no config is set, an example calendar is shown.
|
|
||||||
// See 'Configuration options' for more information.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
````
|
|
||||||
|
|
||||||
## Configuration options
|
|
||||||
|
|
||||||
The following properties can be configured:
|
|
||||||
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ---------------------------- | -----------
|
|
||||||
| `maximumEntries` | The maximum number of events shown. / **Possible values:** `0` - `100` <br> **Default value:** `10`
|
|
||||||
| `maximumNumberOfDays` | The maximum number of days in the future. <br><br> **Default value:** `365`
|
|
||||||
| `displaySymbol` | Display a symbol in front of an entry. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `defaultSymbol` | The default symbol. <br><br> **Possible values:** See [Font Awsome](http://fontawesome.io/icons/) website. <br> **Default value:** `calendar`
|
|
||||||
| `showLocation` | Whether to show event locations. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `maxTitleLength` | The maximum title length. <br><br> **Possible values:** `10` - `50` <br> **Default value:** `25`
|
|
||||||
| `wrapEvents` | Wrap event titles to multiple lines. Breaks lines at the length defined by `maxTitleLength`. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `maxTitleLines` | The maximum number of lines a title will wrap vertically before being cut (Only enabled if `wrapEvents` is also enabled). <br><br> **Possible values:** `0` - `10` <br> **Default value:** `3`
|
|
||||||
| `fetchInterval` | How often does the content needs to be fetched? (Milliseconds) <br><br> **Possible values:** `1000` - `86400000` <br> **Default value:** `300000` (5 minutes)
|
|
||||||
| `animationSpeed` | Speed of the update animation. (Milliseconds) <br><br> **Possible values:** `0` - `5000` <br> **Default value:** `2000` (2 seconds)
|
|
||||||
| `fade` | Fade the future events to black. (Gradient) <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `fadePoint` | Where to start fade? <br><br> **Possible values:** `0` (top of the list) - `1` (bottom of list) <br> **Default value:** `0.25`
|
|
||||||
| `tableClass` | Name of the classes issued from `main.css`. <br><br> **Possible values:** xsmall, small, medium, large, xlarge. <br> **Default value:** _small._
|
|
||||||
| `calendars` | The list of calendars. <br><br> **Possible values:** An array, see _calendar configuration_ below. <br> **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. <br><br> **Example:** `{'Birthday of ' : '', 'foo':'bar'}` <br> **Default value:** `{ "De verjaardag van ": "", "'s birthday": "" }`
|
|
||||||
| `displayRepeatingCountTitle` | Show count title for yearly repeating events (e.g. "X. Birthday", "X. Anniversary") <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `dateFormat` | Format to use for the date of events (when using absolute dates) <br><br> **Possible values:** See [Moment.js formats](http://momentjs.com/docs/#/parsing/string-format/) <br> **Default value:** `MMM Do` (e.g. Jan 18th)
|
|
||||||
| `dateEndFormat` | Format to use for the end time of events <br><br> **Possible values:** See [Moment.js formats](http://momentjs.com/docs/#/parsing/string-format/) <br> **Default value:** `HH:mm` (e.g. 16:30)
|
|
||||||
| `showEnd` | Show end time of events <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `fullDayEventDateFormat` | Format to use for the date of full day events (when using absolute dates) <br><br> **Possible values:** See [Moment.js formats](http://momentjs.com/docs/#/parsing/string-format/) <br> **Default value:** `MMM Do` (e.g. Jan 18th)
|
|
||||||
| `timeFormat` | Display event times as absolute dates, or relative time, or using absolute date headers with times for each event next to it <br><br> **Possible values:** `absolute` or `relative` or `dateheaders` <br> **Default value:** `relative`
|
|
||||||
| `showEnd` | Display the end of a date as well <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `getRelative` | How much time (in hours) should be left until calendar events start getting relative? <br><br> **Possible values:** `0` (events stay absolute) - `48` (48 hours before the event starts) <br> **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 <br><br> **Possible values:** a positive integer representing the number of days for which you want a relative date, for example `7` (for 7 days) <br><br> **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`. <br><br> **Possible values:** `true`, `false` <br><br> **Default value:** `true`
|
|
||||||
| `hidePrivate` | Hides private calendar events. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `hideOngoing` | Hides calendar events that have already started. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `excludedEvents` | An array of words / phrases from event titles that will be excluded from being shown. <br><br>Additionally advanced filter objects can be passed in. Below is the configuration for the advance filtering object.<br>**Required**<br>`filterBy` - string used to determine if filter is applied.<br>**Optional**<br>`until` - Time before an event to display it Ex: [`'3 days'`, `'2 months'`, `'1 week'`]<br>`caseSensitive` - By default, excludedEvents are case insensitive, set this to true to enforce case sensitivity<br>`regex` - set to `true` if filterBy is a regex. For those not familiar with regex it is used for pattern matching, please see [here](https://regexr.com/) for more info.<br><br> **Example:** `['Birthday', 'Hide This Event', {filterBy: 'Payment', until: '6 days', caseSensitive: true}, {filterBy: '^[0-9]{1,}.*', regex: true}]` <br> **Default value:** `[]`
|
|
||||||
| `broadcastPastEvents` | If this is set to true, events from the past `maximumNumberOfDays` will be included in event broadcasts <br> **Default value:** `false`
|
|
||||||
| `sliceMultiDayEvents` | If this is set to true, events exceeding at least one midnight will be sliced into separate events including a counter like (1/2). This is especially helpful in "dateheaders" mode. Events will be sliced at midnight, end time for all events but the last will be 23:59 <br> **Default value:** `true`
|
|
||||||
| `nextDaysRelative ` | If this is set to true, the appointments of today and tomorrow are displayed relatively, even if the timeformat is set to absolute. <br> **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.
|
|
||||||
The `coloredSymbolOnly` property will apply color to the symbol only, not the whole line. This is only applicable when `colored` is also enabled.
|
|
||||||
|
|
||||||
#### Default value:
|
|
||||||
````javascript
|
|
||||||
config: {
|
|
||||||
colored: false,
|
|
||||||
coloredSymbolOnly: false,
|
|
||||||
calendars: [
|
|
||||||
{
|
|
||||||
url: 'http://www.calendarlabs.com/templates/ical/US-Holidays.ics',
|
|
||||||
symbol: 'calendar',
|
|
||||||
auth: {
|
|
||||||
user: 'username',
|
|
||||||
pass: 'superstrongpassword',
|
|
||||||
method: 'basic'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
#### Calendar configuration options:
|
|
||||||
| Option | Description
|
|
||||||
| --------------------- | -----------
|
|
||||||
| `url` | The url of the calendar .ical. This property is required. <br><br> **Possible values:** Any public accessble .ical calendar.
|
|
||||||
| `symbol` | The symbol to show in front of an event. This property is optional. <br><br> **Possible values:** See [Font Awesome](http://fontawesome.io/icons/) website. To have multiple symbols you can define them in an array e.g. `["calendar", "plane"]`
|
|
||||||
| `color` | The font color of an event from this calendar. This property should be set if the config is set to colored: true. <br><br> **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. <br><br> **Example:** `'Birthday'`
|
|
||||||
| `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
|
|
||||||
| `name` | The name of the calendar. Included in event broadcasts as `calendarName`.
|
|
||||||
| `auth` | The object containing options for authentication against the calendar.
|
|
||||||
| `symbolClass` | Add a class to the cell of symbol.
|
|
||||||
| `titleClass` | Add a class to the title's cell.
|
|
||||||
| `timeClass` | Add a class to the time's cell.
|
|
||||||
| `broadcastPastEvents` | Whether to include past events from this calendar. Overrides global setting
|
|
||||||
|
|
||||||
|
|
||||||
#### Calendar authentication options:
|
|
||||||
| Option | Description
|
|
||||||
| --------------------- | -----------
|
|
||||||
| `user` | The username for HTTP authentication.
|
|
||||||
| `pass` | The password for HTTP authentication. (If you use Bearer authentication, this should be your BearerToken.)
|
|
||||||
| `method` | Which authentication method should be used. HTTP Basic, Digest and Bearer authentication methods are supported. Basic authentication is used by default if this option is omitted. **Possible values:** `digest`, `basic`, `bearer` **Default value:** `basic`
|
|
||||||
|
@@ -138,8 +138,6 @@ Module.register("calendar", {
|
|||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
} else if (notification === "INCORRECT_URL") {
|
} else if (notification === "INCORRECT_URL") {
|
||||||
Log.error("Calendar Error. Incorrect url: " + payload.url);
|
Log.error("Calendar Error. Incorrect url: " + payload.url);
|
||||||
} else {
|
|
||||||
Log.log("Calendar received an unknown socket notification: " + notification);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateDom(this.config.animationSpeed);
|
this.updateDom(this.config.animationSpeed);
|
||||||
|
@@ -184,7 +184,14 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||||||
|
|
||||||
// For recurring events, get the set of start dates that fall within the range
|
// For recurring events, get the set of start dates that fall within the range
|
||||||
// of dates we"re looking for.
|
// of dates we"re looking for.
|
||||||
var dates = rule.between(past, future, true, limitFunction);
|
// kblankenship1989 - to fix issue #1798, converting all dates to locale time first, then converting back to UTC time
|
||||||
|
var pastLocal = moment(past).subtract(past.getTimezoneOffset(), "minutes").toDate();
|
||||||
|
var futureLocal = moment(future).subtract(future.getTimezoneOffset(), "minutes").toDate();
|
||||||
|
var datesLocal = rule.between(pastLocal, futureLocal, true, limitFunction);
|
||||||
|
var dates = datesLocal.map(function(dateLocal) {
|
||||||
|
var date = moment(dateLocal).add(dateLocal.getTimezoneOffset(), "minutes").toDate();
|
||||||
|
return date;
|
||||||
|
});
|
||||||
|
|
||||||
// The "dates" array contains the set of dates within our desired date range range that are valid
|
// The "dates" array contains the set of dates within our desired date range range that are valid
|
||||||
// for the recurrence rule. *However*, it"s possible for us to have a specific recurrence that
|
// for the recurrence rule. *However*, it"s possible for us to have a specific recurrence that
|
||||||
|
@@ -26,6 +26,17 @@ exports.parseFile = function(filename){
|
|||||||
|
|
||||||
var rrule = require('rrule').RRule
|
var rrule = require('rrule').RRule
|
||||||
|
|
||||||
|
function getLocaleISOString(date) {
|
||||||
|
var year = date.getFullYear().toString(10).padStart(4,'0');
|
||||||
|
var month = (date.getMonth() + 1).toString(10).padStart(2,'0');
|
||||||
|
var day = date.getDate().toString(10).padStart(2,'0');
|
||||||
|
var hour = date.getHours().toString(10).padStart(2,'0');
|
||||||
|
var minute = date.getMinutes().toString(10).padStart(2,'0');
|
||||||
|
var second = date.getSeconds().toString(10).padStart(2,'0');
|
||||||
|
|
||||||
|
return `${year}${month}${day}T${hour}${minute}${second}Z`;
|
||||||
|
}
|
||||||
|
|
||||||
ical.objectHandlers['RRULE'] = function(val, params, curr, stack, line){
|
ical.objectHandlers['RRULE'] = function(val, params, curr, stack, line){
|
||||||
curr.rrule = line;
|
curr.rrule = line;
|
||||||
return curr
|
return curr
|
||||||
@@ -50,8 +61,8 @@ ical.objectHandlers['END'] = function (val, params, curr, stack) {
|
|||||||
|
|
||||||
if (typeof curr.start.toISOString === 'function') {
|
if (typeof curr.start.toISOString === 'function') {
|
||||||
try {
|
try {
|
||||||
rule += ';DTSTART=' + curr.start.toISOString().replace(/[-:]/g, '');
|
// kblankenship1989 - to fix issue #1798, converting all dates to locale time first, then converting back to UTC time
|
||||||
rule = rule.replace(/\.[0-9]{3}/, '');
|
rule += ';DTSTART=' + getLocaleISOString(curr.start);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("ERROR when trying to convert to ISOString", error);
|
console.error("ERROR when trying to convert to ISOString", error);
|
||||||
}
|
}
|
||||||
|
@@ -2,56 +2,4 @@
|
|||||||
The `clock` module is one of the default modules of the MagicMirror.
|
The `clock` module is one of the default modules of the MagicMirror.
|
||||||
This module displays the current date and time. The information will be updated realtime.
|
This module displays the current date and time. The information will be updated realtime.
|
||||||
|
|
||||||
## Screenshot
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/clock.html).
|
||||||
|
|
||||||
- Current time
|
|
||||||

|
|
||||||
|
|
||||||
## Using the module
|
|
||||||
|
|
||||||
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.
|
|
||||||
config: {
|
|
||||||
// The config property is optional.
|
|
||||||
// See 'Configuration options' for more information.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
````
|
|
||||||
|
|
||||||
## Configuration options
|
|
||||||
|
|
||||||
The following properties can be configured:
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ----------------- | -----------
|
|
||||||
| `timeFormat` | Use 12 or 24 hour format. <br><br> **Possible values:** `12` or `24` <br> **Default value:** uses value of _config.timeFormat_
|
|
||||||
| `displaySeconds` | Display seconds. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `showPeriod` | Show the period (am/pm) with 12 hour format. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `showPeriodUpper` | Show the period (AM/PM) with 12 hour format as uppercase. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `clockBold` | Remove the colon and bold the minutes to make a more modern look. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `showDate` | Turn off or on the Date section. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `showWeek` | Turn off or on the Week section. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `dateFormat` | Configure the date format as you like. <br><br> **Possible values:** [Docs](http://momentjs.com/docs/#/displaying/format/) <br> **Default value:** `"dddd, LL"`
|
|
||||||
| `displayType` | Display a digital clock, analog clock, or both together. <br><br> **Possible values:** `digital`, `analog`, or `both` <br> **Default value:** `digital`
|
|
||||||
| `analogSize` | **Specific to the analog clock.** Defines how large the analog display is. <br><br> **Possible values:** A positive number of pixels` <br> **Default value:** `200px`
|
|
||||||
| `analogFace` | **Specific to the analog clock.** Specifies which clock face to use. <br><br> **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) <br> **Default value:** `simple`
|
|
||||||
| `secondsColor` | **Specific to the analog clock.** Specifies what color to make the 'seconds' hand. <br><br> **Possible values:** `any HTML RGB Color` <br> **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 <br><br> **Possible values:** `top`, `right`, `bottom`, or `left` <br> **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. <br><br> **Possible values:** `false`, `top`, or `bottom` <br> **Default value:** `top`
|
|
||||||
| `timezone` | Specific a timezone to show clock. <br><br> **Possible examples values:** `America/New_York`, `America/Santiago`, `Etc/GMT+10` <br> **Default value:** `none`. See more informations about configuration value [here](https://momentjs.com/timezone/docs/#/data-formats/packed-format/)
|
|
||||||
|
|
||||||
## Notifications
|
|
||||||
|
|
||||||
The clock makes use of the built-in [Notification Mechanism](https://github.com/michMich/MagicMirror/wiki/notifications) to relay notifications to all modules.
|
|
||||||
|
|
||||||
Current notifications are:
|
|
||||||
|
|
||||||
| Notification | Description
|
|
||||||
| ----------------- | -----------
|
|
||||||
| `CLOCK_SECOND` | A second has elapsed. <br> *Parameter*: second value
|
|
||||||
| `CLOCK_MINUTE` | A minute has elapsed <br> *Parameter*: minute value
|
|
||||||
|
@@ -26,10 +26,15 @@ Module.register("clock",{
|
|||||||
analogShowDate: "top", // options: false, 'top', or 'bottom'
|
analogShowDate: "top", // options: false, 'top', or 'bottom'
|
||||||
secondsColor: "#888888",
|
secondsColor: "#888888",
|
||||||
timezone: null,
|
timezone: null,
|
||||||
|
|
||||||
|
showSunTimes: false,
|
||||||
|
showMoonTimes: false,
|
||||||
|
lat: 47.630539,
|
||||||
|
lon: -122.344147,
|
||||||
},
|
},
|
||||||
// Define required scripts.
|
// Define required scripts.
|
||||||
getScripts: function() {
|
getScripts: function() {
|
||||||
return ["moment.js", "moment-timezone.js"];
|
return ["moment.js", "moment-timezone.js", "suncalc.js"];
|
||||||
},
|
},
|
||||||
// Define styles.
|
// Define styles.
|
||||||
getStyles: function() {
|
getStyles: function() {
|
||||||
@@ -93,11 +98,15 @@ Module.register("clock",{
|
|||||||
var timeWrapper = document.createElement("div");
|
var timeWrapper = document.createElement("div");
|
||||||
var secondsWrapper = document.createElement("sup");
|
var secondsWrapper = document.createElement("sup");
|
||||||
var periodWrapper = document.createElement("span");
|
var periodWrapper = document.createElement("span");
|
||||||
|
var sunWrapper = document.createElement("div");
|
||||||
|
var moonWrapper = document.createElement("div");
|
||||||
var weekWrapper = document.createElement("div");
|
var weekWrapper = document.createElement("div");
|
||||||
// Style Wrappers
|
// Style Wrappers
|
||||||
dateWrapper.className = "date normal medium";
|
dateWrapper.className = "date normal medium";
|
||||||
timeWrapper.className = "time bright large light";
|
timeWrapper.className = "time bright large light";
|
||||||
secondsWrapper.className = "dimmed";
|
secondsWrapper.className = "dimmed";
|
||||||
|
sunWrapper.className = "sun dimmed small";
|
||||||
|
moonWrapper.className = "moon dimmed small";
|
||||||
weekWrapper.className = "week dimmed medium";
|
weekWrapper.className = "week dimmed medium";
|
||||||
|
|
||||||
// Set content of wrappers.
|
// Set content of wrappers.
|
||||||
@@ -142,6 +151,49 @@ Module.register("clock",{
|
|||||||
timeWrapper.appendChild(periodWrapper);
|
timeWrapper.appendChild(periodWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatTime(config, time) {
|
||||||
|
var formatString = hourSymbol + ":mm";
|
||||||
|
if (config.showPeriod && config.timeFormat !== 24) {
|
||||||
|
formatString += config.showPeriodUpper ? "A" : "a";
|
||||||
|
}
|
||||||
|
return moment(time).format(formatString);
|
||||||
|
}
|
||||||
|
if (this.config.showSunTimes) {
|
||||||
|
const sunTimes = SunCalc.getTimes(now, this.config.lat, this.config.lon);
|
||||||
|
const isVisible = now.isBetween(sunTimes.sunrise, sunTimes.sunset);
|
||||||
|
var nextEvent;
|
||||||
|
if (now.isBefore(sunTimes.sunrise)) {
|
||||||
|
nextEvent = sunTimes.sunrise;
|
||||||
|
} else if (now.isBefore(sunTimes.sunset)) {
|
||||||
|
nextEvent = sunTimes.sunset;
|
||||||
|
} else {
|
||||||
|
const tomorrowSunTimes = SunCalc.getTimes(now.clone().add(1, "day"), this.config.lat, this.config.lon);
|
||||||
|
nextEvent = tomorrowSunTimes.sunrise;
|
||||||
|
}
|
||||||
|
const untilNextEvent = moment.duration(moment(nextEvent).diff(now));
|
||||||
|
const untilNextEventString = untilNextEvent.hours() + "h " + untilNextEvent.minutes() + "m";
|
||||||
|
sunWrapper.innerHTML = "<span class=\"" + (isVisible ? "bright" : "") + "\"><i class=\"fa fa-sun-o\" aria-hidden=\"true\"></i> " + untilNextEventString + "</span>" +
|
||||||
|
"<span><i class=\"fa fa-arrow-up\" aria-hidden=\"true\"></i>" + formatTime(this.config, sunTimes.sunrise) + "</span>" +
|
||||||
|
"<span><i class=\"fa fa-arrow-down\" aria-hidden=\"true\"></i>" + formatTime(this.config, sunTimes.sunset) + "</span>";
|
||||||
|
}
|
||||||
|
if (this.config.showMoonTimes) {
|
||||||
|
const moonIllumination = SunCalc.getMoonIllumination(now.toDate());
|
||||||
|
const moonTimes = SunCalc.getMoonTimes(now, this.config.lat, this.config.lon);
|
||||||
|
const moonRise = moonTimes.rise;
|
||||||
|
var moonSet;
|
||||||
|
if (moment(moonTimes.set).isAfter(moonTimes.rise)) {
|
||||||
|
moonSet = moonTimes.set;
|
||||||
|
} else {
|
||||||
|
const nextMoonTimes = SunCalc.getMoonTimes(now.clone().add(1, "day"), this.config.lat, this.config.lon);
|
||||||
|
moonSet = nextMoonTimes.set;
|
||||||
|
}
|
||||||
|
const isVisible = now.isBetween(moonRise, moonSet) || moonTimes.alwaysUp === true;
|
||||||
|
const illuminatedFractionString = Math.round(moonIllumination.fraction * 100) + "%";
|
||||||
|
moonWrapper.innerHTML = "<span class=\"" + (isVisible ? "bright" : "") + "\"><i class=\"fa fa-moon-o\" aria-hidden=\"true\"></i> " + illuminatedFractionString + "</span>" +
|
||||||
|
"<span><i class=\"fa fa-arrow-up\" aria-hidden=\"true\"></i> " + (moonRise ? formatTime(this.config, moonRise) : "...") + "</span>"+
|
||||||
|
"<span><i class=\"fa fa-arrow-down\" aria-hidden=\"true\"></i> " + (moonSet ? formatTime(this.config, moonSet) : "...") + "</span>";
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************
|
/****************************************************************
|
||||||
* Create wrappers for ANALOG clock, only if specified in config
|
* Create wrappers for ANALOG clock, only if specified in config
|
||||||
*/
|
*/
|
||||||
@@ -210,6 +262,8 @@ Module.register("clock",{
|
|||||||
// Display only a digital clock
|
// Display only a digital clock
|
||||||
wrapper.appendChild(dateWrapper);
|
wrapper.appendChild(dateWrapper);
|
||||||
wrapper.appendChild(timeWrapper);
|
wrapper.appendChild(timeWrapper);
|
||||||
|
wrapper.appendChild(sunWrapper);
|
||||||
|
wrapper.appendChild(moonWrapper);
|
||||||
wrapper.appendChild(weekWrapper);
|
wrapper.appendChild(weekWrapper);
|
||||||
} else if (this.config.displayType === "analog") {
|
} else if (this.config.displayType === "analog") {
|
||||||
// Display only an analog clock
|
// Display only an analog clock
|
||||||
@@ -244,6 +298,8 @@ Module.register("clock",{
|
|||||||
digitalWrapper.style.cssFloat = "none";
|
digitalWrapper.style.cssFloat = "none";
|
||||||
digitalWrapper.appendChild(dateWrapper);
|
digitalWrapper.appendChild(dateWrapper);
|
||||||
digitalWrapper.appendChild(timeWrapper);
|
digitalWrapper.appendChild(timeWrapper);
|
||||||
|
digitalWrapper.appendChild(sunWrapper);
|
||||||
|
digitalWrapper.appendChild(moonWrapper);
|
||||||
digitalWrapper.appendChild(weekWrapper);
|
digitalWrapper.appendChild(weekWrapper);
|
||||||
|
|
||||||
var appendClocks = function(condition, pos1, pos2) {
|
var appendClocks = function(condition, pos1, pos2) {
|
||||||
|
Before Width: | Height: | Size: 30 KiB |
@@ -66,3 +66,13 @@
|
|||||||
-ms-transform-origin: 50% 100%;
|
-ms-transform-origin: 50% 100%;
|
||||||
transform-origin: 50% 100%;
|
transform-origin: 50% 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.module.clock .sun,
|
||||||
|
.module.clock .moon {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module.clock .sun > *,
|
||||||
|
.module.clock .moon > * {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
@@ -2,150 +2,4 @@
|
|||||||
The `compliments` module is one of the default modules of the MagicMirror.
|
The `compliments` module is one of the default modules of the MagicMirror.
|
||||||
This module displays a random compliment.
|
This module displays a random compliment.
|
||||||
|
|
||||||
## Screenshots
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/compliments.html).
|
||||||
- Compliments Screenshot
|
|
||||||

|
|
||||||
|
|
||||||
## Using the module
|
|
||||||
|
|
||||||
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.
|
|
||||||
// Best results in one of the middle regions like: lower_third
|
|
||||||
config: {
|
|
||||||
// The config property is optional.
|
|
||||||
// If no config is set, the default compliments are shown.
|
|
||||||
// See 'Configuration options' for more information.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
````
|
|
||||||
|
|
||||||
## Configuration options
|
|
||||||
|
|
||||||
The following properties can be configured:
|
|
||||||
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ---------------- | -----------
|
|
||||||
| `updateInterval` | How often does the compliment have to change? (Milliseconds) <br><br> **Possible values:** `1000` - `86400000` <br> **Default value:** `30000` (30 seconds)
|
|
||||||
| `fadeSpeed` | Speed of the update animation. (Milliseconds) <br><br> **Possible values:**`0` - `5000` <br> **Default value:** `4000` (4 seconds)
|
|
||||||
| `compliments` | The list of compliments. <br><br> **Possible values:** An object with four arrays: `morning`, `afternoon`, `evening` and `anytime`. See _compliment configuration_ below. <br> **Default value:** See _compliment configuration_ below.
|
|
||||||
| `remoteFile` | External file from which to load the compliments <br><br> **Possible values:** Path or URL (starting with `http://` or `https://`) to a JSON file containing compliments, configured as per the value of the _compliments configuration_ (see below). An object with four arrays: `morning`, `afternoon`, `evening` and `anytime`. - `compliments.json` <br> **Default value:** `null` (Do not load from file)
|
|
||||||
| `classes` | Override the CSS classes of the div showing the compliments <br><br> **Default value:** `thin xlarge bright`
|
|
||||||
| `morningStartTime` | Time in hours (in 24 format), after which the mode of "morning" will begin <br> **Possible values:** `0` - `24` <br><br> **Default value:** `3`
|
|
||||||
| `morningEndTime` | Time in hours (in 24 format), after which the mode of "morning" will end <br> **Possible values:** `0` - `24` <br><br> **Default value:** `12`
|
|
||||||
| `afternoonStartTime` | Time in hours (in 24 format), after which the mode "afternoon" will begin <br> **Possible values:** `0` - `24` <br><br> **Default value:** `12`
|
|
||||||
| `afternoonEndTime` | Time in hours (in 24 format), after which the mode "afternoon" will end <br> **Possible values:** `0` - `24` <br><br> **Default value:** `17`
|
|
||||||
|
|
||||||
All the rest of the time that does not fall into the morningStartTime-morningEndTime and afternoonStartTime-afternoonEndTime ranges is considered "evening".
|
|
||||||
|
|
||||||
### Compliment configuration
|
|
||||||
|
|
||||||
The `compliments` property contains an object with four arrays: <code>morning</code>, <code>afternoon</code>, <code>evening</code> and <code>anytime</code>. Based on the time of the day, the compliments will be picked out of one of these arrays. The arrays contain one or multiple compliments.
|
|
||||||
|
|
||||||
|
|
||||||
If use the currentweather is possible use a actual weather for set compliments. The availables properties are:
|
|
||||||
- `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"
|
|
||||||
],
|
|
||||||
snow: [
|
|
||||||
"Snowball battle!"
|
|
||||||
],
|
|
||||||
rain: [
|
|
||||||
"Don't forget your umbrella"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
|
|
||||||
#### Default value:
|
|
||||||
````javascript
|
|
||||||
config: {
|
|
||||||
compliments: {
|
|
||||||
anytime: [
|
|
||||||
"Hey there sexy!"
|
|
||||||
],
|
|
||||||
morning: [
|
|
||||||
"Good morning, handsome!",
|
|
||||||
"Enjoy your day!",
|
|
||||||
"How was your sleep?"
|
|
||||||
],
|
|
||||||
afternoon: [
|
|
||||||
"Hello, beauty!",
|
|
||||||
"You look sexy!",
|
|
||||||
"Looking good today!"
|
|
||||||
],
|
|
||||||
evening: [
|
|
||||||
"Wow, you look hot!",
|
|
||||||
"You look nice!",
|
|
||||||
"Hi, sexy!"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
#### Multi-line compliments:
|
|
||||||
Use `\n` to split compliment text into multiple lines, e.g. `First line.\nSecond line.` will be shown as:
|
|
||||||
```
|
|
||||||
First line.
|
|
||||||
Second line.
|
|
||||||
```
|
|
||||||
|
|
||||||
### External Compliment File
|
|
||||||
You may specify an external file that contains the three compliment arrays. This is particularly useful if you have a
|
|
||||||
large number of compliments and do not wish to crowd your `config.js` file with a large array of compliments.
|
|
||||||
Adding the `remoteFile` variable will override an array you specify in the configuration file.
|
|
||||||
|
|
||||||
This file must be straight JSON. Note that the array names need quotes
|
|
||||||
around them ("morning", "afternoon", "evening", "snow", "rain", etc.).
|
|
||||||
#### Example compliments.json file:
|
|
||||||
````json
|
|
||||||
{
|
|
||||||
"anytime" : [
|
|
||||||
"Hey there sexy!"
|
|
||||||
],
|
|
||||||
"morning" : [
|
|
||||||
"Good morning, sunshine!",
|
|
||||||
"Who needs coffee when you have your smile?",
|
|
||||||
"Go get 'em, Tiger!"
|
|
||||||
],
|
|
||||||
"afternoon" : [
|
|
||||||
"Hitting your stride!",
|
|
||||||
"You are making a difference!",
|
|
||||||
"You're more fun than bubble wrap!"
|
|
||||||
],
|
|
||||||
"evening" : [
|
|
||||||
"You made someone smile today, I know it.",
|
|
||||||
"You are making a difference.",
|
|
||||||
"The day was better for your efforts."
|
|
||||||
]
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
|
||||||
|
@@ -28,6 +28,9 @@ Module.register("compliments", {
|
|||||||
"Wow, you look hot!",
|
"Wow, you look hot!",
|
||||||
"You look nice!",
|
"You look nice!",
|
||||||
"Hi, sexy!"
|
"Hi, sexy!"
|
||||||
|
],
|
||||||
|
"....-01-01": [
|
||||||
|
"Happy new year!"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
updateInterval: 30000,
|
updateInterval: 30000,
|
||||||
@@ -37,7 +40,8 @@ Module.register("compliments", {
|
|||||||
morningEndTime: 12,
|
morningEndTime: 12,
|
||||||
afternoonStartTime: 12,
|
afternoonStartTime: 12,
|
||||||
afternoonEndTime: 17,
|
afternoonEndTime: 17,
|
||||||
random: true
|
random: true,
|
||||||
|
mockDate: null
|
||||||
},
|
},
|
||||||
lastIndexUsed:-1,
|
lastIndexUsed:-1,
|
||||||
// Set currentweather from module
|
// Set currentweather from module
|
||||||
@@ -102,6 +106,7 @@ Module.register("compliments", {
|
|||||||
*/
|
*/
|
||||||
complimentArray: function() {
|
complimentArray: function() {
|
||||||
var hour = moment().hour();
|
var hour = moment().hour();
|
||||||
|
var date = this.config.mockDate ? this.config.mockDate : moment().format("YYYY-MM-DD");
|
||||||
var compliments;
|
var compliments;
|
||||||
|
|
||||||
if (hour >= this.config.morningStartTime && hour < this.config.morningEndTime && this.config.compliments.hasOwnProperty("morning")) {
|
if (hour >= this.config.morningStartTime && hour < this.config.morningEndTime && this.config.compliments.hasOwnProperty("morning")) {
|
||||||
@@ -122,6 +127,12 @@ Module.register("compliments", {
|
|||||||
|
|
||||||
compliments.push.apply(compliments, this.config.compliments.anytime);
|
compliments.push.apply(compliments, this.config.compliments.anytime);
|
||||||
|
|
||||||
|
for (entry in this.config.compliments) {
|
||||||
|
if (new RegExp(entry).test(date)) {
|
||||||
|
compliments.push.apply(compliments, this.config.compliments[entry]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return compliments;
|
return compliments;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -151,19 +162,19 @@ Module.register("compliments", {
|
|||||||
// get the current time of day compliments list
|
// get the current time of day compliments list
|
||||||
var compliments = this.complimentArray();
|
var compliments = this.complimentArray();
|
||||||
// variable for index to next message to display
|
// variable for index to next message to display
|
||||||
let index=0
|
let index = 0;
|
||||||
// are we randomizing
|
// are we randomizing
|
||||||
if(this.config.random){
|
if(this.config.random){
|
||||||
// yes
|
// yes
|
||||||
index = this.randomIndex(compliments);
|
index = this.randomIndex(compliments);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
// no, sequetial
|
// no, sequential
|
||||||
// if doing sequential, don't fall off the end
|
// if doing sequential, don't fall off the end
|
||||||
index = (this.lastIndexUsed >= (compliments.length-1))?0: ++this.lastIndexUsed
|
index = (this.lastIndexUsed >= (compliments.length-1))?0: ++this.lastIndexUsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
return compliments[index];
|
return compliments[index] || "";
|
||||||
},
|
},
|
||||||
|
|
||||||
// Override dom generator.
|
// Override dom generator.
|
||||||
@@ -173,15 +184,15 @@ Module.register("compliments", {
|
|||||||
// get the compliment text
|
// get the compliment text
|
||||||
var complimentText = this.randomCompliment();
|
var complimentText = this.randomCompliment();
|
||||||
// split it into parts on newline text
|
// split it into parts on newline text
|
||||||
var parts= complimentText.split('\n')
|
var parts = complimentText.split("\n");
|
||||||
// create a span to hold it all
|
// create a span to hold it all
|
||||||
var compliment=document.createElement('span')
|
var compliment = document.createElement("span");
|
||||||
// process all the parts of the compliment text
|
// process all the parts of the compliment text
|
||||||
for (part of parts){
|
for (part of parts){
|
||||||
// create a text element for each part
|
// create a text element for each part
|
||||||
compliment.appendChild(document.createTextNode(part))
|
compliment.appendChild(document.createTextNode(part));
|
||||||
// add a break `
|
// add a break `
|
||||||
compliment.appendChild(document.createElement('BR'))
|
compliment.appendChild(document.createElement("BR"));
|
||||||
}
|
}
|
||||||
// remove the last break
|
// remove the last break
|
||||||
compliment.lastElementChild.remove();
|
compliment.lastElementChild.remove();
|
||||||
|
Before Width: | Height: | Size: 26 KiB |
@@ -2,88 +2,4 @@
|
|||||||
The `currentweather` module is one of the default modules of the MagicMirror.
|
The `currentweather` module is one of the default modules of the MagicMirror.
|
||||||
This module displays the current weather, including the windspeed, the sunset or sunrise time, the temperature and an icon to display the current conditions.
|
This module displays the current weather, including the windspeed, the sunset or sunrise time, the temperature and an icon to display the current conditions.
|
||||||
|
|
||||||
## Screenshot
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/currentweather.html).
|
||||||
|
|
||||||
- Current weather screenshot
|
|
||||||

|
|
||||||
|
|
||||||
## Using the module
|
|
||||||
|
|
||||||
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.
|
|
||||||
// Best results in left or right regions.
|
|
||||||
config: {
|
|
||||||
// See 'Configuration options' for more information.
|
|
||||||
location: "Amsterdam,Netherlands",
|
|
||||||
locationID: "", //Location ID from http://bulk.openweathermap.org/sample/city.list.json.gz
|
|
||||||
appid: "abcde12345abcde12345abcde12345ab" //openweathermap.org API key.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
````
|
|
||||||
|
|
||||||
## Configuration options
|
|
||||||
|
|
||||||
The following properties can be configured:
|
|
||||||
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ---------------------------- | -----------
|
|
||||||
| `location` | The location used for weather information. <br><br> **Example:** `'Amsterdam,Netherlands'` <br> **Default value:** `false` <br><br> **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](https://openweathermap.org/find) **This will override anything you put in location.** <br> Leave blank if you want to use location. <br> **Example:** `1234567` <br> **Default value:** `false` <br><br> **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. <br><br> This value is **REQUIRED**
|
|
||||||
| `units` | What units to use. Specified by config.js <br><br> **Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` =Fahrenheit <br> **Default value:** `config.units`
|
|
||||||
| `roundTemp` | Round temperature value to nearest integer. <br><br> **Possible values:** `true` (round to integer) or `false` (display exact value with decimal point) <br> **Default value:** `false`
|
|
||||||
| `degreeLabel` | Show the degree label for your chosen units (Metric = C, Imperial = F, Kelvins = K). <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `updateInterval` | How often does the content needs to be fetched? (Milliseconds) <br><br> **Possible values:** `1000` - `86400000` <br> **Default value:** `600000` (10 minutes)
|
|
||||||
| `animationSpeed` | Speed of the update animation. (Milliseconds) <br><br> **Possible values:**`0` - `5000` <br> **Default value:** `1000` (1 second)
|
|
||||||
| `timeFormat` | Use 12 or 24 hour format. <br><br> **Possible values:** `12` or `24` <br> **Default value:** uses value of _config.timeFormat_
|
|
||||||
| `showPeriod` | Show the period (am/pm) with 12 hour format <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `showPeriodUpper` | Show the period (AM/PM) with 12 hour format as uppercase <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `showWindDirection` | Show the wind direction next to the wind speed. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `showWindDirectionAsArrow` | Show the wind direction as an arrow instead of abbreviation <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `showHumidity` | Show the current humidity <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `showIndoorTemperature` | If you have another module that emits the INDOOR_TEMPERATURE notification, the indoor temperature will be displayed <br> **Default value:** `false`
|
|
||||||
| `onlyTemp` | Show only current Temperature and weather icon without windspeed, sunset, sunrise time and feels like. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `showFeelsLike` | Shows the Feels like temperature weather. <br><br> **Possible values:**`true` or `false`<br>**Default value:** `true`
|
|
||||||
| `useKMPHwind` | Uses KMPH as units for windspeed. <br><br> **Possible values:**`true` or `false`<br>**Default value:** `false`
|
|
||||||
| `useBeaufort` | Pick between using the Beaufort scale for wind speed or using the default units. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `lang` | The language of the days. <br><br> **Possible values:** `en`, `nl`, `ru`, etc ... <br> **Default value:** uses value of _config.language_
|
|
||||||
| `decimalSymbol` | The decimal symbol to use.<br><br> **Possible values:** `.`, `,` or any other symbol.<br> **Default value:** `.`
|
|
||||||
| `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) <br><br> **Possible values:** `1000` - `5000` <br> **Default value:** `0`
|
|
||||||
| `retryDelay` | The delay before retrying after a request failure. (Milliseconds) <br><br> **Possible values:** `1000` - `60000` <br> **Default value:** `2500`
|
|
||||||
| `apiVersion` | The OpenWeatherMap API version to use. <br><br> **Default value:** `2.5`
|
|
||||||
| `apiBase` | The OpenWeatherMap base URL. <br><br> **Default value:** `'http://api.openweathermap.org/data/'`
|
|
||||||
| `weatherEndpoint` | The OpenWeatherMap API endPoint. <br><br> **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. <br><br> **Default value:** `true`
|
|
||||||
| `useLocationAsHeader` | If set to `true` and location is given a value, the value of location will be used as the header. This is useful if `locationName` was not returned. <br><br> **Default value:** `false`
|
|
||||||
| `calendarClass` | The class for the calender module to base the event based weather information on. <br><br> **Default value:** `'calendar'`
|
|
||||||
| `iconTable` | The conversion table to convert the weather conditions to weather-icons. <br><br> **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'
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
@@ -42,8 +42,10 @@ Module.register("currentweather",{
|
|||||||
|
|
||||||
appendLocationNameToHeader: true,
|
appendLocationNameToHeader: true,
|
||||||
calendarClass: "calendar",
|
calendarClass: "calendar",
|
||||||
|
tableClass: "large",
|
||||||
|
|
||||||
onlyTemp: false,
|
onlyTemp: false,
|
||||||
|
hideTemp: false,
|
||||||
roundTemp: false,
|
roundTemp: false,
|
||||||
|
|
||||||
iconTable: {
|
iconTable: {
|
||||||
@@ -174,6 +176,7 @@ Module.register("currentweather",{
|
|||||||
// Override dom generator.
|
// Override dom generator.
|
||||||
getDom: function() {
|
getDom: function() {
|
||||||
var wrapper = document.createElement("div");
|
var wrapper = document.createElement("div");
|
||||||
|
wrapper.className = this.config.tableClass;
|
||||||
|
|
||||||
if (this.config.appid === "") {
|
if (this.config.appid === "") {
|
||||||
wrapper.innerHTML = "Please set the correct openweather <i>appid</i> in the config for module: " + this.name + ".";
|
wrapper.innerHTML = "Please set the correct openweather <i>appid</i> in the config for module: " + this.name + ".";
|
||||||
@@ -192,11 +195,7 @@ Module.register("currentweather",{
|
|||||||
}
|
}
|
||||||
|
|
||||||
var large = document.createElement("div");
|
var large = document.createElement("div");
|
||||||
large.className = "large light";
|
large.className = "light";
|
||||||
|
|
||||||
var weatherIcon = document.createElement("span");
|
|
||||||
weatherIcon.className = "wi weathericon " + this.weatherType;
|
|
||||||
large.appendChild(weatherIcon);
|
|
||||||
|
|
||||||
var degreeLabel = "";
|
var degreeLabel = "";
|
||||||
if (this.config.units === "metric" || this.config.units === "imperial") {
|
if (this.config.units === "metric" || this.config.units === "imperial") {
|
||||||
@@ -220,10 +219,16 @@ Module.register("currentweather",{
|
|||||||
this.config.decimalSymbol = ".";
|
this.config.decimalSymbol = ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.config.hideTemp === false) {
|
||||||
|
var weatherIcon = document.createElement("span");
|
||||||
|
weatherIcon.className = "wi weathericon " + this.weatherType;
|
||||||
|
large.appendChild(weatherIcon);
|
||||||
|
|
||||||
var temperature = document.createElement("span");
|
var temperature = document.createElement("span");
|
||||||
temperature.className = "bright";
|
temperature.className = "bright";
|
||||||
temperature.innerHTML = " " + this.temperature.replace(".", this.config.decimalSymbol) + degreeLabel;
|
temperature.innerHTML = " " + this.temperature.replace(".", this.config.decimalSymbol) + degreeLabel;
|
||||||
large.appendChild(temperature);
|
large.appendChild(temperature);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.config.showIndoorTemperature && this.indoorTemperature) {
|
if (this.config.showIndoorTemperature && this.indoorTemperature) {
|
||||||
var indoorIcon = document.createElement("span");
|
var indoorIcon = document.createElement("span");
|
||||||
|
Before Width: | Height: | Size: 38 KiB |
@@ -1,25 +1,4 @@
|
|||||||
# Module: Hello World
|
# Module: Hello World
|
||||||
The `helloworld` module is one of the default modules of the MagicMirror. It is a simple way to display a static text on the mirror.
|
The `helloworld` module is one of the default modules of the MagicMirror. It is a simple way to display a static text on the mirror.
|
||||||
## Using the module
|
|
||||||
|
|
||||||
To use this module, add it to the modules array in the `config/config.js` file:
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/helloworld.html).
|
||||||
````javascript
|
|
||||||
modules: [
|
|
||||||
{
|
|
||||||
module: "helloworld",
|
|
||||||
position: "bottom_bar", // This can be any of the regions.
|
|
||||||
config: {
|
|
||||||
// See 'Configuration options' for more information.
|
|
||||||
text: "Hello world!"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
````
|
|
||||||
|
|
||||||
## Configuration options
|
|
||||||
|
|
||||||
The following properties can be configured:
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ------ | -----------
|
|
||||||
| `text` | The text to display. <br><br> **Example:** `'Hello world!'` <br> **Default value:** `'Hello world!'`
|
|
||||||
|
@@ -2,106 +2,4 @@
|
|||||||
The `newsfeed ` module is one of the default modules of the MagicMirror.
|
The `newsfeed ` module is one of the default modules of the MagicMirror.
|
||||||
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.
|
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.
|
||||||
|
|
||||||
## Screenshot
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/newsfeed.html).
|
||||||
- News Feed Screenshot using the NYT
|
|
||||||

|
|
||||||
|
|
||||||
## 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.
|
|
||||||
config: {
|
|
||||||
// The config property is optional.
|
|
||||||
// If no config is set, an example calendar is shown.
|
|
||||||
// See 'Configuration options' for more information.
|
|
||||||
|
|
||||||
feeds: [
|
|
||||||
{
|
|
||||||
title: "New York Times",
|
|
||||||
url: "http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "BBC",
|
|
||||||
url: "http://feeds.bbci.co.uk/news/video_and_audio/news_front_page/rss.xml?edition=uk",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
````
|
|
||||||
|
|
||||||
### 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. <br> The module expects that the module's configuration option `showDescription` is set to `false` (default value). <br><br> When received a _second consecutive time_, shows the full news article in an IFRAME. <br> 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`.<br><br>When received the _next consecutive times_, reloads the page and scrolls down by `scrollLength` pixels to paginate through the article.
|
|
||||||
| `ARTICLE_LESS_DETAILS` | Hides the summary or full news article and only displays the news title of the currently viewed news item.
|
|
||||||
| `ARTICLE_TOGGLE_FULL` | Toggles article in fullscreen.
|
|
||||||
| `ARTICLE_INFO_REQUEST` | Causes `newsfeed` to respond with the notification `ARTICLE_INFO_RESPONSE`, the payload of which provides the `title`, `source`, `date`, `desc` and `url` of the current news title.
|
|
||||||
|
|
||||||
#### Notifications sent by the module
|
|
||||||
MagicMirror's [notification mechanism](https://github.com/MichMich/MagicMirror/tree/master/modules#thissendnotificationnotification-payload) can also be used to send notifications from the current module to all other modules. The following notifications are broadcasted from this module:
|
|
||||||
|
|
||||||
| Notification Identifier | Description
|
|
||||||
| ----------------------- | -----------
|
|
||||||
| `NEWS_FEED` | Broadcast the current list of news items.
|
|
||||||
| `NEWS_FEED_UPDATE` | Broadcasts the list of updates news items.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Configuration options
|
|
||||||
|
|
||||||
The following properties can be configured:
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ------------------ | -----------
|
|
||||||
| `feeds` | An array of feed urls that will be used as source. <br> More info about this object can be found below. <br> **Default value:** `[{ title: "New York Times", url: "http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml", encoding: "UTF-8" }]`<br>You can add `reloadInterval` option to set particular reloadInterval to a feed.
|
|
||||||
| `showSourceTitle` | Display the title of the source. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `showPublishDate` | Display the publish date of an headline. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `broadcastNewsFeeds` | Gives the ability to broadcast news feeds to all modules, by using ```sendNotification()``` when set to `true`, rather than ```sendSocketNotification()``` when `false` <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `broadcastNewsUpdates` | Gives the ability to broadcast news feed updates to all modules <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `showDescription` | Display the description of an item. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `wrapTitle` | Wrap the title of the item to multiple lines. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `wrapDescription` | Wrap the description of the item to multiple lines. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `truncDescription` | Truncate description? <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `lengthDescription`| How many characters to be displayed for a truncated description? <br><br> **Possible values:** `1` - `500` <br> **Default value:** `400`
|
|
||||||
| `hideLoading` | Hide module instead of showing LOADING status. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `reloadInterval` | How often does the content needs to be fetched? (Milliseconds) <br><br> **Possible values:** `1000` - `86400000` <br> **Default value:** `300000` (5 minutes)
|
|
||||||
| `updateInterval` | How often do you want to display a new headline? (Milliseconds) <br><br> **Possible values:**`1000` - `60000` <br> **Default value:** `10000` (10 seconds)
|
|
||||||
| `animationSpeed` | Speed of the update animation. (Milliseconds) <br><br> **Possible values:**`0` - `5000` <br> **Default value:** `2500` (2.5 seconds)
|
|
||||||
| `maxNewsItems` | Total amount of news items to cycle through. (0 for unlimited) <br><br> **Possible values:**`0` - `...` <br> **Default value:** `0`
|
|
||||||
| `ignoreOldItems` | Ignore news items that are outdated. <br><br> **Possible values:**`true` or `false` <br> **Default value:** `false`
|
|
||||||
| `ignoreOlderThan` | How old should news items be before they are considered outdated? (Milliseconds) <br><br> **Possible values:**`1` - `...` <br> **Default value:** `86400000` (1 day)
|
|
||||||
| `removeStartTags` | Some news feeds 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. <br><br> **Possible values:**`'title'`, `'description'`, `'both'`
|
|
||||||
| `startTags` | List the tags you would like to have removed at the beginning of the feed item <br><br> **Possible values:** `['TAG']` or `['TAG1','TAG2',...]`
|
|
||||||
| `removeEndTags` | Remove specified tags from the **end** of an item's description and/or title. <br><br> **Possible values:**`'title'`, `'description'`, `'both'`
|
|
||||||
| `endTags` | List the tags you would like to have removed at the end of the feed item <br><br> **Possible values:** `['TAG']` or `['TAG1','TAG2',...]`
|
|
||||||
| `prohibitedWords` | Remove news feed item if one of these words is found anywhere in the title (case insensitive and greedy matching) <br><br> **Possible values:** `['word']` or `['word1','word2',...]`
|
|
||||||
| `scrollLength` | Scrolls the full news article page by a given number of pixels when a `ARTICLE_MORE_DETAILS` notification is received and the full news article is already displayed.<br><br> **Possible values:** `1` or `10000` <br> **Default value:** `500`
|
|
||||||
| `logFeedWarnings` | Log warnings when there is an error parsing a news article. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
|
|
||||||
The `feeds` property contains an array with multiple objects. These objects have the following properties:
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ---------- | -----------
|
|
||||||
| `title` | The name of the feed source to be displayed above the news items. <br><br> This property is optional.
|
|
||||||
| `url` | The url of the feed used for the headlines. <br><br> **Example:** `'http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml'`
|
|
||||||
| `encoding` | The encoding of the news feed. <br><br> This property is optional. <br> **Possible values:**`'UTF-8'`, `'ISO-8859-1'`, etc ... <br> **Default value:** `'UTF-8'`
|
|
||||||
|
@@ -367,7 +367,6 @@ Module.register("newsfeed",{
|
|||||||
},
|
},
|
||||||
|
|
||||||
notificationReceived: function(notification, payload, sender) {
|
notificationReceived: function(notification, payload, sender) {
|
||||||
Log.info(this.name + " - received notification: " + notification);
|
|
||||||
if(notification === "ARTICLE_NEXT"){
|
if(notification === "ARTICLE_NEXT"){
|
||||||
var before = this.activeItem;
|
var before = this.activeItem;
|
||||||
this.activeItem++;
|
this.activeItem++;
|
||||||
@@ -425,8 +424,6 @@ Module.register("newsfeed",{
|
|||||||
desc: this.newsItems[this.activeItem].description,
|
desc: this.newsItems[this.activeItem].description,
|
||||||
url: this.getActiveItemURL()
|
url: this.getActiveItemURL()
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
Log.info(this.name + " - unknown notification, ignoring: " + notification);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 44 KiB |
@@ -2,26 +2,4 @@
|
|||||||
The `updatenotification` module is one of the default modules of the MagicMirror.
|
The `updatenotification` module is one of the default modules of the MagicMirror.
|
||||||
This will display a message whenever a new version of the MagicMirror application is available.
|
This will display a message whenever a new version of the MagicMirror application is available.
|
||||||
|
|
||||||
## Using the module
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/updatenotification.html).
|
||||||
|
|
||||||
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.
|
|
||||||
config: {
|
|
||||||
// The config property is optional.
|
|
||||||
// See 'Configuration options' for more information.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
````
|
|
||||||
|
|
||||||
## Configuration options
|
|
||||||
|
|
||||||
The following properties can be configured:
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ---------------- | -----------
|
|
||||||
| `updateInterval` | How often do you want to check for a new version? This value represents the interval in milliseconds. <br><br> **Possible values:** Any value above `60000` (1 minute) <br> **Default value:** `600000` (10 minutes);
|
|
||||||
|
@@ -18,37 +18,30 @@ module.exports = NodeHelper.create({
|
|||||||
configureModules: function(modules) {
|
configureModules: function(modules) {
|
||||||
|
|
||||||
// Push MagicMirror itself , biggest chance it'll show up last in UI and isn't overwritten
|
// Push MagicMirror itself , biggest chance it'll show up last in UI and isn't overwritten
|
||||||
// others will be added in front, asynchronously
|
// others will be added in front
|
||||||
|
// this method returns promises so we can't wait for every one to resolve before continuing
|
||||||
simpleGits.push({"module": "default", "git": SimpleGit(path.normalize(__dirname + "/../../../"))});
|
simpleGits.push({"module": "default", "git": SimpleGit(path.normalize(__dirname + "/../../../"))});
|
||||||
|
|
||||||
|
var promises = [];
|
||||||
|
|
||||||
for (moduleName in modules) {
|
for (moduleName in modules) {
|
||||||
if (defaultModules.indexOf(moduleName) < 0) {
|
if (!this.ignoreUpdateChecking(moduleName)) {
|
||||||
// Default modules are included in the main MagicMirror repo
|
// Default modules are included in the main MagicMirror repo
|
||||||
var moduleFolder = path.normalize(__dirname + "/../../" + moduleName);
|
var moduleFolder = path.normalize(__dirname + "/../../" + moduleName);
|
||||||
|
|
||||||
var stat;
|
|
||||||
try {
|
try {
|
||||||
//console.log("checking git for module="+moduleName)
|
//console.log("checking git for module="+moduleName)
|
||||||
stat = fs.statSync(path.join(moduleFolder, ".git"));
|
let stat = fs.statSync(path.join(moduleFolder, ".git"));
|
||||||
|
promises.push(this.resolveRemote(moduleName, moduleFolder));
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
// Error when directory .git doesn't exist
|
// Error when directory .git doesn't exist
|
||||||
// This module is not managed with git, skip
|
// This module is not managed with git, skip
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var res = function(mn, mf) {
|
return Promise.all(promises);
|
||||||
var git = SimpleGit(mf);
|
|
||||||
git.getRemotes(true, function(err, remotes) {
|
|
||||||
if (remotes.length < 1 || remotes[0].name.length < 1) {
|
|
||||||
// No valid remote for folder, skip
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Folder has .git and has at least one git remote, watch this folder
|
|
||||||
simpleGits.unshift({"module": mn, "git": git});
|
|
||||||
});
|
|
||||||
}(moduleName, moduleFolder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
socketNotificationReceived: function (notification, payload) {
|
socketNotificationReceived: function (notification, payload) {
|
||||||
@@ -56,21 +49,35 @@ module.exports = NodeHelper.create({
|
|||||||
this.config = payload;
|
this.config = payload;
|
||||||
} else if(notification === "MODULES") {
|
} else if(notification === "MODULES") {
|
||||||
// if this is the 1st time thru the update check process
|
// if this is the 1st time thru the update check process
|
||||||
if(this.updateProcessStarted==false ){
|
if (!this.updateProcessStarted) {
|
||||||
this.updateProcessStarted = true;
|
this.updateProcessStarted = true;
|
||||||
this.configureModules(payload);
|
this.configureModules(payload).then(() => this.performFetch());
|
||||||
this.preformFetch();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
preformFetch() {
|
resolveRemote: function(moduleName, moduleFolder) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var git = SimpleGit(moduleFolder);
|
||||||
|
git.getRemotes(true, (err, remotes) => {
|
||||||
|
if (remotes.length < 1 || remotes[0].name.length < 1) {
|
||||||
|
// No valid remote for folder, skip
|
||||||
|
return resolve();
|
||||||
|
}
|
||||||
|
// Folder has .git and has at least one git remote, watch this folder
|
||||||
|
simpleGits.unshift({"module": moduleName, "git": git});
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
performFetch: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
simpleGits.forEach(function(sg) {
|
simpleGits.forEach((sg) => {
|
||||||
sg.git.fetch().status(function(err, data) {
|
sg.git.fetch().status((err, data) => {
|
||||||
data.module = sg.module;
|
data.module = sg.module;
|
||||||
if (!err) {
|
if (!err) {
|
||||||
sg.git.log({"-1": null}, function(err, data2) {
|
sg.git.log({"-1": null}, (err, data2) => {
|
||||||
if (!err && data2.latest && "hash" in data2.latest) {
|
if (!err && data2.latest && "hash" in data2.latest) {
|
||||||
data.hash = data2.latest.hash;
|
data.hash = data2.latest.hash;
|
||||||
self.sendSocketNotification("STATUS", data);
|
self.sendSocketNotification("STATUS", data);
|
||||||
@@ -91,8 +98,23 @@ module.exports = NodeHelper.create({
|
|||||||
var self = this;
|
var self = this;
|
||||||
clearTimeout(this.updateTimer);
|
clearTimeout(this.updateTimer);
|
||||||
this.updateTimer = setTimeout(function() {
|
this.updateTimer = setTimeout(function() {
|
||||||
self.preformFetch();
|
self.performFetch();
|
||||||
}, delay);
|
}, delay);
|
||||||
|
},
|
||||||
|
|
||||||
|
ignoreUpdateChecking: function(moduleName) {
|
||||||
|
// Should not check for updates for default modules
|
||||||
|
if (defaultModules.indexOf(moduleName) >= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not check for updates for ignored modules
|
||||||
|
if (this.config.ignoreModules.indexOf(moduleName) >= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The rest of the modules that passes should check for updates
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -3,6 +3,7 @@ Module.register("updatenotification", {
|
|||||||
defaults: {
|
defaults: {
|
||||||
updateInterval: 10 * 60 * 1000, // every 10 minutes
|
updateInterval: 10 * 60 * 1000, // every 10 minutes
|
||||||
refreshInterval: 24 * 60 * 60 * 1000, // one day
|
refreshInterval: 24 * 60 * 60 * 1000, // one day
|
||||||
|
ignoreModules: []
|
||||||
},
|
},
|
||||||
|
|
||||||
suspended: false,
|
suspended: false,
|
||||||
|
@@ -2,119 +2,4 @@
|
|||||||
|
|
||||||
This module is aimed to be the replacement for the current `currentweather` and `weatherforcast` modules. The module will be configurable to be used as a current weather view, or to show the forecast. This way the module can be used twice to fullfil both purposes.
|
This module is aimed to be the replacement for the current `currentweather` and `weatherforcast` modules. The module will be configurable to be used as a current weather view, or to show the forecast. This way the module can be used twice to fullfil both purposes.
|
||||||
|
|
||||||
The biggest change is the use of weather providers. This way we are not bound to one API source. And users can choose which API they want to use as their source.
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/weather.html).
|
||||||
|
|
||||||
The module is in a very early stage, and needs a lot of work. It's API isn't set in stone, so keep that in mind when you want to contribute.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
 
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
To use this module, add it to the modules array in the `config/config.js` file:
|
|
||||||
|
|
||||||
````javascript
|
|
||||||
modules: [
|
|
||||||
{
|
|
||||||
module: "weather",
|
|
||||||
position: "top_right",
|
|
||||||
config: {
|
|
||||||
// See 'Configuration options' for more information.
|
|
||||||
type: 'current'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
````
|
|
||||||
|
|
||||||
## Configuration options
|
|
||||||
|
|
||||||
The following properties can be configured:
|
|
||||||
|
|
||||||
### General options
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ---------------------------- | -----------
|
|
||||||
| `weatherProvider` | Which weather provider should be used. <br><br> **Possible values:** `openweathermap` , `darksky` , `weathergov` or `ukmetoffice`<br> **Default value:** `openweathermap`
|
|
||||||
| `type` | Which type of weather data should be displayed. <br><br> **Possible values:** `current` or `forecast` <br> **Default value:** `current`
|
|
||||||
| `units` | What units to use. Specified by config.js <br><br> **Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` = Fahrenheit <br> **Default value:** `config.units`
|
|
||||||
| `tempUnits` | What units to use for temperature. If specified overrides `units` setting. Specified by config.js <br><br> **Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` = Fahrenheit <br> **Default value:** `units`
|
|
||||||
| `windUnits` | What units to use for wind speed. If specified overrides `units` setting. Specified by config.js <br><br> **Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` = Fahrenheit <br> **Default value:** `units`
|
|
||||||
| `roundTemp` | Round temperature value to nearest integer. <br><br> **Possible values:** `true` (round to integer) or `false` (display exact value with decimal point) <br> **Default value:** `false`
|
|
||||||
| `degreeLabel` | Show the degree label for your chosen units (Metric = C, Imperial = F, Kelvin = K). <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `updateInterval` | How often does the content needs to be fetched? (Milliseconds) <br><br> **Possible values:** `1000` - `86400000` <br> **Default value:** `600000` (10 minutes)
|
|
||||||
| `animationSpeed` | Speed of the update animation. (Milliseconds) <br><br> **Possible values:** `0` - `5000` <br> **Default value:** `1000` (1 second)
|
|
||||||
| `timeFormat` | Use 12 or 24 hour format. <br><br> **Possible values:** `12` or `24` <br> **Default value:** uses value of _config.timeFormat_
|
|
||||||
| `showPeriod` | Show the period (am/pm) with 12 hour format <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `showPeriodUpper` | Show the period (AM/PM) with 12 hour format as uppercase <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `lang` | The language of the days. <br><br> **Possible values:** `en`, `nl`, `ru`, etc ... <br> **Default value:** uses value of _config.language_
|
|
||||||
| `decimalSymbol` | The decimal symbol to use.<br><br> **Possible values:** `.`, `,` or any other symbol.<br> **Default value:** `.`
|
|
||||||
| `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) <br><br> **Possible values:** `1000` - `5000` <br> **Default value:** `0`
|
|
||||||
| `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 interesting when using calender based weather. <br><br> **Default value:** `true`
|
|
||||||
| `calendarClass` | The class for the calender module to base the event based weather information on. <br><br> **Default value:** `'calendar'`
|
|
||||||
|
|
||||||
#### Current weather options
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ---------------------------- | -----------
|
|
||||||
| `onlyTemp` | Show only current Temperature and weather icon without windspeed, sunset, sunrise time and feels like. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `useBeaufort` | Pick between using the Beaufort scale for wind speed or using the default units. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `showWindDirection` | Show the wind direction next to the wind speed. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `showWindDirectionAsArrow` | Show the wind direction as an arrow instead of abbreviation <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `showHumidity` | Show the current humidity <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `showIndoorTemperature` | If you have another module that emits the `INDOOR_TEMPERATURE` notification, the indoor temperature will be displayed <br> **Default value:** `false`
|
|
||||||
| `showIndoorHumidity` | If you have another module that emits the `INDOOR_HUMIDITY` notification, the indoor humidity will be displayed <br> **Default value:** `false`
|
|
||||||
| `showFeelsLike` | Shows the Feels like temperature weather. <br><br> **Possible values:** `true` or `false`<br>**Default value:** `true`
|
|
||||||
|
|
||||||
#### Weather forecast options
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ---------------------------- | -----------
|
|
||||||
| `tableClass` | The class for the forecast table. <br><br> **Default value:** `'small'`
|
|
||||||
| `colored` | If set to `true`, the min and max temperature are color coded. <br><br> **Default value:** `false`
|
|
||||||
| `showPrecipitationAmount` | Show the amount of rain/snow in the forecast <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
|
||||||
| `fade` | Fade the future events to black. (Gradient) <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `fadePoint` | Where to start fade? <br><br> **Possible values:** `0` (top of the list) - `1` (bottom of list) <br> **Default value:** `0.25`
|
|
||||||
| `maxNumberOfDays` | How many days of forecast to return. Specified by config.js <br><br> **Possible values:** `1` - `16` <br> **Default value:** `5` (5 days) <br> This value is optional. By default the weatherforecast module will return 5 days.
|
|
||||||
|
|
||||||
### Openweathermap options
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ---------------------------- | -----------
|
|
||||||
| `apiVersion` | The OpenWeatherMap API version to use. <br><br> **Default value:** `2.5`
|
|
||||||
| `apiBase` | The OpenWeatherMap base URL. <br><br> **Default value:** `'http://api.openweathermap.org/data/'`
|
|
||||||
| `weatherEndpoint` | The OpenWeatherMap API endPoint. <br><br> **Possible values:** `/weather`, `/forecast` (free users) or `/forecast/daily` (paying users or old apiKey only) <br> **Default value:** `'/weather'`
|
|
||||||
| `locationID` | Location ID from [OpenWeatherMap](https://openweathermap.org/find) **This will override anything you put in location.** <br> Leave blank if you want to use location. <br> **Example:** `1234567` <br> **Default value:** `false` <br><br> **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.
|
|
||||||
| `location` | The location used for weather information. <br><br> **Example:** `'Amsterdam,Netherlands'` <br> **Default value:** `false` <br><br> **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.
|
|
||||||
| `apiKey` | The [OpenWeatherMap](https://home.openweathermap.org) API key, which can be obtained by creating an OpenWeatherMap account. <br><br> This value is **REQUIRED**
|
|
||||||
|
|
||||||
### Darksky options
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ---------------------------- | -----------
|
|
||||||
| `apiBase` | The DarkSky base URL. The darksky api has disabled [cors](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), therefore a proxy is required. <br><br> **Possible value:** `'https://cors-anywhere.herokuapp.com/https://api.darksky.net'` <br> This value is **REQUIRED**
|
|
||||||
| `weatherEndpoint` | The DarkSky API endPoint. <br><br> **Possible values:** `/forecast` <br> This value is **REQUIRED**
|
|
||||||
| `apiKey` | The [DarkSky](https://darksky.net/dev/register) API key, which can be obtained by creating an DarkSky account. <br><br> This value is **REQUIRED**
|
|
||||||
| `lat` | The geo coordinate latitude. <br><br> This value is **REQUIRED**
|
|
||||||
| `lon` | The geo coordinate longitude. <br><br> This value is **REQUIRED**
|
|
||||||
|
|
||||||
### Weather.gov options
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ---------------------------- | -----------
|
|
||||||
| `apiBase` | The weather.gov base URL. <br><br> **Possible value:** `'https://api.weather.gov/points/'` <br> This value is **REQUIRED**
|
|
||||||
| `weatherEndpoint` | The weather.gov API endPoint. <br><br> **Possible values:** `/forecast` for forecast and `/forecast/hourly` for current. <br> This value is **REQUIRED**
|
|
||||||
| `lat` | The geo coordinate latitude. <br><br> This value is **REQUIRED**
|
|
||||||
| `lon` | The geo coordinate longitude. <br><br> This value is **REQUIRED**
|
|
||||||
|
|
||||||
### UK Met Office options
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ---------------------------- | -----------
|
|
||||||
| `apiBase` | The UKMO base URL. <br><br> **Possible value:** `'http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/json/'` <br> This value is **REQUIRED**
|
|
||||||
| `locationId` | The UKMO API location code. <br><br> **Possible values:** `322942` <br> This value is **REQUIRED**
|
|
||||||
| `apiKey` | The [UK Met Office](https://www.metoffice.gov.uk/datapoint/getting-started) API key, which can be obtained by creating an UKMO Datapoint account. <br><br> This value is **REQUIRED**
|
|
||||||
|
|
||||||
## API Provider Development
|
|
||||||
|
|
||||||
If you want to add another API provider checkout the [Guide](providers).
|
|
||||||
|
Before Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 16 KiB |
@@ -2,81 +2,4 @@
|
|||||||
The `weatherforecast` module is one of the default modules of the MagicMirror.
|
The `weatherforecast` module is one of the default modules of the MagicMirror.
|
||||||
This module displays the weather forecast for the coming week, including an an icon to display the current conditions, the minimum temperature and the maximum temperature.
|
This module displays the weather forecast for the coming week, including an an icon to display the current conditions, the minimum temperature and the maximum temperature.
|
||||||
|
|
||||||
## Screenshots
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/weatherforecast.html).
|
||||||
|
|
||||||
- 5 day forecast
|
|
||||||

|
|
||||||
|
|
||||||
## Using the module
|
|
||||||
|
|
||||||
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.
|
|
||||||
// Best results in left or right regions.
|
|
||||||
config: {
|
|
||||||
// See 'Configuration options' for more information.
|
|
||||||
location: "Amsterdam,Netherlands",
|
|
||||||
locationID: "", //Location ID from http://bulk.openweathermap.org/sample/city.list.json.gz
|
|
||||||
appid: "abcde12345abcde12345abcde12345ab" //openweathermap.org API key.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
````
|
|
||||||
|
|
||||||
## Configuration options
|
|
||||||
|
|
||||||
The following properties can be configured:
|
|
||||||
|
|
||||||
| Option | Description
|
|
||||||
| ---------------------------- | -----------
|
|
||||||
| `location` | The location used for weather information. <br><br> **Example:** `'Amsterdam,Netherlands'` <br> **Default value:** `false` <br><br> **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](https://openweathermap.org/find) **This will override anything you put in location.** <br> Leave blank if you want to use location. <br> **Example:** `1234567` <br> **Default value:** `false` <br><br> **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. <br><br> This value is **REQUIRED**
|
|
||||||
| `units` | What units to use. Specified by config.js <br><br> **Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` =Fahrenheit <br> **Default value:** `config.units`
|
|
||||||
| `roundTemp` | Round temperature values to nearest integer. <br><br> **Possible values:** `true` (round to integer) or `false` (display exact value with decimal point) <br> **Default value:** `false`
|
|
||||||
| `maxNumberOfDays` | How many days of forecast to return. Specified by config.js <br><br> **Possible values:** `1` - `16` <br> **Default value:** `7` (7 days) <br> This value is optional. By default the weatherforecast module will return 7 days.
|
|
||||||
| `showRainAmount` | Should the predicted rain amount be displayed? <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false` <br> 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) <br><br> **Possible values:** `1000` - `86400000` <br> **Default value:** `600000` (10 minutes)
|
|
||||||
| `animationSpeed` | Speed of the update animation. (Milliseconds) <br><br> **Possible values:** `0` - `5000` <br> **Default value:** `1000` (1 second)
|
|
||||||
| `lang` | The language of the days. <br><br> **Possible values:** `en`, `nl`, `ru`, etc ... <br> **Default value:** uses value of _config.language_
|
|
||||||
| `decimalSymbol` | The decimal symbol to use.<br><br> **Possible values:** `.`, `,` or any other symbol.<br> **Default value:** `.`
|
|
||||||
| `fade` | Fade the future events to black. (Gradient) <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
|
||||||
| `fadePoint` | Where to start fade? <br><br> **Possible values:** `0` (top of the list) - `1` (bottom of list) <br> **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) <br><br> **Possible values:** `1000` - `5000` <br> **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) <br><br> **Possible values:** `1000` - `60000` <br> **Default value:** `2500`
|
|
||||||
| `apiVersion` | The OpenWeatherMap API version to use. <br><br> **Default value:** `2.5`
|
|
||||||
| `apiBase` | The OpenWeatherMap base URL. <br><br> **Default value:** `'http://api.openweathermap.org/data/'`
|
|
||||||
| `forecastEndpoint` | The OpenWeatherMap API endPoint. <br><br> **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. <br><br> **Default value:** `true`
|
|
||||||
| `calendarClass` | The class for the calendar module to base the event based weather information on. <br><br> **Default value:** `'calendar'`
|
|
||||||
| `tableClass` | Name of the classes issued from `main.css`. <br><br> **Possible values:** xsmall, small, medium, large, xlarge. <br> **Default value:** _small._
|
|
||||||
| `iconTable` | The conversion table to convert the weather conditions to weather-icons. <br><br> **Default value:** view table below
|
|
||||||
| `colored` | If set `colored` to `true` the min-temp gets a blue tone and the max-temp gets a red tone. <br><br> **Default value:** `'false'`
|
|
||||||
| `scale ` | If set to `true` the module will display `C` for Celsius degrees and `F` for Fahrenheit degrees after the number, based on the value of the `units` option, otherwise only the ° character is displayed. <br><br> **Default value:** `false`
|
|
||||||
|
|
||||||
#### 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'
|
|
||||||
}
|
|
||||||
````
|
|
||||||
|
Before Width: | Height: | Size: 84 KiB |
4070
package-lock.json
generated
21
package.json
@@ -1,18 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "magicmirror",
|
"name": "magicmirror",
|
||||||
"version": "2.10.1",
|
"version": "2.11.0",
|
||||||
"description": "The open source modular smart mirror platform.",
|
"description": "The open source modular smart mirror platform.",
|
||||||
"main": "js/electron.js",
|
"main": "js/electron.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "./run-start.sh",
|
"start": "DISPLAY=\"${DISPLAY:=:0}\" ./node_modules/.bin/electron js/electron.js",
|
||||||
"install": "cd vendor && npm install",
|
"server": "node ./serveronly",
|
||||||
"install-fonts": "cd fonts && npm install",
|
"install": "echo \"Installing vendor files ...\n\" && cd vendor && npm install --loglevel=error",
|
||||||
"postinstall": "sh untrack-css.sh && sh installers/postinstall/postinstall.sh && npm run install-fonts",
|
"install-fonts": "echo \"Installing fonts ...\n\" && cd fonts && npm install --loglevel=error",
|
||||||
|
"postinstall": "npm run install-fonts && echo \"MagicMirror installation finished successfully! \n\"",
|
||||||
"test": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests --recursive",
|
"test": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests --recursive",
|
||||||
"test:unit": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests/unit --recursive",
|
"test:unit": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests/unit --recursive",
|
||||||
"test:e2e": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests/e2e --recursive",
|
"test:e2e": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests/e2e --recursive",
|
||||||
|
"test:lint": "grunt --env=test",
|
||||||
"config:check": "node tests/configs/check_config.js",
|
"config:check": "node tests/configs/check_config.js",
|
||||||
"lint": "grunt"
|
"lint": "grunt --env=lint"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -46,17 +48,16 @@
|
|||||||
"grunt-yamllint": "latest",
|
"grunt-yamllint": "latest",
|
||||||
"http-auth": "^3.2.3",
|
"http-auth": "^3.2.3",
|
||||||
"jsdom": "^11.6.2",
|
"jsdom": "^11.6.2",
|
||||||
"jshint": "^2.10.2",
|
"mocha": "^7.0.0",
|
||||||
"mocha": "^4.1.0",
|
|
||||||
"mocha-each": "^1.1.0",
|
"mocha-each": "^1.1.0",
|
||||||
"mocha-logger": "^1.0.6",
|
"mocha-logger": "^1.0.6",
|
||||||
"spectron": "^3.8.0",
|
"spectron": "^8.0.0",
|
||||||
"stylelint": "latest",
|
"stylelint": "latest",
|
||||||
"stylelint-config-standard": "latest",
|
"stylelint-config-standard": "latest",
|
||||||
"time-grunt": "latest"
|
"time-grunt": "latest"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"electron": "^3.0.13"
|
"electron": "^6.1.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"colors": "^1.1.2",
|
"colors": "^1.1.2",
|
||||||
|
67
run-start.sh
@@ -1,67 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# use bash instead of sh
|
|
||||||
./untrack-css.sh
|
|
||||||
|
|
||||||
if [ -z "$DISPLAY" ]; then #If not set DISPLAY is SSH remote or tty
|
|
||||||
export DISPLAY=:0 # Set by default display
|
|
||||||
fi
|
|
||||||
# get the processor architecture
|
|
||||||
arch=$(uname -m)
|
|
||||||
false='false'
|
|
||||||
|
|
||||||
# get the config option, if any
|
|
||||||
# only check non comment lines
|
|
||||||
serveronly=$(grep -v '^\s//' config/config.js | grep -i serveronly: | awk '{print tolower($2)}' | tr -d ,\"\')
|
|
||||||
# set default if not defined in config
|
|
||||||
serveronly=${serveronly:-false}
|
|
||||||
# check for xwindows running
|
|
||||||
xorg=$(pgrep Xorg)
|
|
||||||
#check for macOS
|
|
||||||
mac=$(uname)
|
|
||||||
#
|
|
||||||
# if the user requested serveronly OR
|
|
||||||
# electron support for armv6l has been dropped OR
|
|
||||||
# system is in text mode
|
|
||||||
#
|
|
||||||
if [ "$serveronly." != "false." -o "$arch" == "armv6l" ] || [ "$xorg." == "." -a $mac != 'Darwin' ]; then
|
|
||||||
|
|
||||||
# if user explicitly configured to run server only (no ui local)
|
|
||||||
# OR there is no xwindows running, so no support for browser graphics
|
|
||||||
if [ "$serveronly." == "true." -o "$xorg." == "." ]; then
|
|
||||||
# start server mode,
|
|
||||||
node serveronly
|
|
||||||
else
|
|
||||||
# start the server in the background
|
|
||||||
# wait for server to be ready
|
|
||||||
# need bash for this
|
|
||||||
exec 3< <(node serveronly)
|
|
||||||
|
|
||||||
# Read the output of server line by line until one line 'point your browser'
|
|
||||||
while read line; do
|
|
||||||
case "$line" in
|
|
||||||
*point\ your\ browser*)
|
|
||||||
echo $line
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo $line
|
|
||||||
#sleep .25
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done <&3
|
|
||||||
|
|
||||||
# Close the file descriptor
|
|
||||||
exec 3<&-
|
|
||||||
|
|
||||||
# lets use chrome to display here now
|
|
||||||
# get the server port address from the ready message
|
|
||||||
port=$(echo $line | awk -F\: '{print $4}')
|
|
||||||
# start chromium
|
|
||||||
echo "Starting chromium browser now, have patience, it takes a minute"
|
|
||||||
chromium-browser -noerrdialogs -kiosk -start_maximized --disable-infobars --app=http://localhost:$port --ignore-certificate-errors-spki-list --ignore-ssl-errors --ignore-certificate-errors 2>/dev/null
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
# we can use electron directly
|
|
||||||
electron js/electron.js $1;
|
|
||||||
fi
|
|
@@ -1,5 +1,6 @@
|
|||||||
var app = require("../js/app.js");
|
var app = require("../js/app.js");
|
||||||
app.start(function(config) {
|
app.start(function(config) {
|
||||||
var bindAddress = config.address ? config.address : "localhost";
|
var bindAddress = config.address ? config.address : "localhost";
|
||||||
console.log("\nReady to go! Please point your browser to: http://" + bindAddress + ":" + config.port);
|
var httpType = config.useHttps ? "https" : "http";
|
||||||
|
console.log("\nReady to go! Please point your browser to: " + httpType + "://" + bindAddress + ":" + config.port);
|
||||||
});
|
});
|
||||||
|
@@ -9,7 +9,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var v = require("jshint");
|
const Linter = require("eslint").Linter;
|
||||||
|
const linter = new Linter();
|
||||||
|
const config = require(__dirname + "/../../.eslintrc.json");
|
||||||
|
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var Utils = require(__dirname + "/../../js/utils.js");
|
var Utils = require(__dirname + "/../../js/utils.js");
|
||||||
@@ -50,16 +53,15 @@ function checkConfigFile() {
|
|||||||
// I'm not sure if all ever is utf-8
|
// I'm not sure if all ever is utf-8
|
||||||
fs.readFile(configFileName, "utf-8", function (err, data) {
|
fs.readFile(configFileName, "utf-8", function (err, data) {
|
||||||
if (err) { throw err; }
|
if (err) { throw err; }
|
||||||
v.JSHINT(data); // Parser by jshint
|
const messages = linter.verify(data, config);
|
||||||
|
if (messages.length === 0) {
|
||||||
if (v.JSHINT.errors.length === 0) {
|
|
||||||
console.log("Your configuration file doesn't contain syntax errors :)");
|
console.log("Your configuration file doesn't contain syntax errors :)");
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
errors = v.JSHINT.data().errors;
|
errors = messages;
|
||||||
for (var idx in errors) {
|
for (var idx in errors) {
|
||||||
error = errors[idx];
|
error = errors[idx];
|
||||||
console.log("Line", error.line, "col", error.character, error.reason);
|
console.log("Line", error.line, "col", error.column, error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
41
tests/configs/modules/compliments/compliments_date.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/* Magic Mirror Test config compliments with date type
|
||||||
|
*
|
||||||
|
* By Rejas
|
||||||
|
*
|
||||||
|
* MIT Licensed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
port: 8080,
|
||||||
|
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||||
|
|
||||||
|
language: "en",
|
||||||
|
timeFormat: 12,
|
||||||
|
units: "metric",
|
||||||
|
electronOptions: {
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
modules: [
|
||||||
|
{
|
||||||
|
module: "compliments",
|
||||||
|
position: "middle_center",
|
||||||
|
config: {
|
||||||
|
mockDate: "2020-01-01",
|
||||||
|
compliments: {
|
||||||
|
morning: [],
|
||||||
|
afternoon: [],
|
||||||
|
evening: [],
|
||||||
|
"....-01-01": [
|
||||||
|
"Happy new year!"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||||
|
if (typeof module !== "undefined") {module.exports = config;}
|
@@ -89,4 +89,19 @@ describe("Compliments module", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Feature date in compliments module", function() {
|
||||||
|
describe("Set date and empty compliments for anytime, morning, evening and afternoon", function() {
|
||||||
|
before(function() {
|
||||||
|
// Set config sample for use in test
|
||||||
|
process.env.MM_CONFIG_FILE = "tests/configs/modules/compliments/compliments_date.js";
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Show happy new year compliment on new years day", function() {
|
||||||
|
return app.client.waitUntilWindowLoaded().getText(".compliments").then(function(text) {
|
||||||
|
expect(text).to.be.oneOf(["Happy new year!"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
const _ = require('lodash');
|
const _ = require("lodash");
|
||||||
|
|
||||||
function generateWeather(extendedData = {}) {
|
function generateWeather(extendedData = {}) {
|
||||||
return JSON.stringify(_.merge({}, {
|
return JSON.stringify(_.merge({}, {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
const _ = require('lodash');
|
const _ = require("lodash");
|
||||||
|
|
||||||
function generateWeatherForecast(extendedData = {}) {
|
function generateWeatherForecast(extendedData = {}) {
|
||||||
return JSON.stringify(_.merge({}, {
|
return JSON.stringify(_.merge({}, {
|
||||||
|
@@ -8,9 +8,8 @@ const helpers = require("../global-setup");
|
|||||||
|
|
||||||
const {generateWeather, generateWeatherForecast} = require("./mocks");
|
const {generateWeather, generateWeatherForecast} = require("./mocks");
|
||||||
|
|
||||||
const wait = () => new Promise(res => setTimeout(res, 3000));
|
|
||||||
|
|
||||||
describe("Weather module", function() {
|
describe("Weather module", function() {
|
||||||
|
|
||||||
let app;
|
let app;
|
||||||
|
|
||||||
helpers.setupTimeout(this);
|
helpers.setupTimeout(this);
|
||||||
@@ -43,7 +42,7 @@ describe("Weather module", function() {
|
|||||||
|
|
||||||
it("should render wind speed and wind direction", async function() {
|
it("should render wind speed and wind direction", async function() {
|
||||||
const weather = generateWeather();
|
const weather = generateWeather();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
return app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "6 WSW", 10000);
|
return app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "6 WSW", 10000);
|
||||||
});
|
});
|
||||||
@@ -53,7 +52,7 @@ describe("Weather module", function() {
|
|||||||
const sunset = moment().startOf("day").unix();
|
const sunset = moment().startOf("day").unix();
|
||||||
|
|
||||||
const weather = generateWeather({sys: {sunrise, sunset}});
|
const weather = generateWeather({sys: {sunrise, sunset}});
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
await app.client.waitForExist(".weather .normal.medium span.wi.dimmed.wi-sunrise", 10000);
|
await app.client.waitForExist(".weather .normal.medium span.wi.dimmed.wi-sunrise", 10000);
|
||||||
|
|
||||||
@@ -65,7 +64,7 @@ describe("Weather module", function() {
|
|||||||
const sunset = moment().endOf("day").unix();
|
const sunset = moment().endOf("day").unix();
|
||||||
|
|
||||||
const weather = generateWeather({sys: {sunrise, sunset}});
|
const weather = generateWeather({sys: {sunrise, sunset}});
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
await app.client.waitForExist(".weather .normal.medium span.wi.dimmed.wi-sunset", 10000);
|
await app.client.waitForExist(".weather .normal.medium span.wi.dimmed.wi-sunset", 10000);
|
||||||
|
|
||||||
@@ -74,7 +73,7 @@ describe("Weather module", function() {
|
|||||||
|
|
||||||
it("should render temperature with icon", async function() {
|
it("should render temperature with icon", async function() {
|
||||||
const weather = generateWeather();
|
const weather = generateWeather();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
await app.client.waitForExist(".weather .large.light span.wi.weathericon.wi-snow", 10000);
|
await app.client.waitForExist(".weather .large.light span.wi.weathericon.wi-snow", 10000);
|
||||||
|
|
||||||
@@ -83,7 +82,7 @@ describe("Weather module", function() {
|
|||||||
|
|
||||||
it("should render feels like temperature", async function() {
|
it("should render feels like temperature", async function() {
|
||||||
const weather = generateWeather();
|
const weather = generateWeather();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
return app.client.waitUntilTextExists(".weather .normal.medium span.dimmed", "Feels like -5.6°", 10000);
|
return app.client.waitUntilTextExists(".weather .normal.medium span.dimmed", "Feels like -5.6°", 10000);
|
||||||
});
|
});
|
||||||
@@ -96,14 +95,14 @@ describe("Weather module", function() {
|
|||||||
|
|
||||||
it("should render useBeaufort = false", async function() {
|
it("should render useBeaufort = false", async function() {
|
||||||
const weather = generateWeather();
|
const weather = generateWeather();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
return app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "12", 10000);
|
return app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "12", 10000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render showWindDirectionAsArrow = true", async function() {
|
it("should render showWindDirectionAsArrow = true", async function() {
|
||||||
const weather = generateWeather();
|
const weather = generateWeather();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
await app.client.waitForExist(".weather .normal.medium sup i.fa-long-arrow-up", 10000);
|
await app.client.waitForExist(".weather .normal.medium sup i.fa-long-arrow-up", 10000);
|
||||||
const element = await app.client.getHTML(".weather .normal.medium sup i.fa-long-arrow-up");
|
const element = await app.client.getHTML(".weather .normal.medium sup i.fa-long-arrow-up");
|
||||||
@@ -113,7 +112,7 @@ describe("Weather module", function() {
|
|||||||
|
|
||||||
it("should render showHumidity = true", async function() {
|
it("should render showHumidity = true", async function() {
|
||||||
const weather = generateWeather();
|
const weather = generateWeather();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(3)", "93", 10000);
|
await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(3)", "93", 10000);
|
||||||
return app.client.waitForExist(".weather .normal.medium sup i.wi-humidity", 10000);
|
return app.client.waitForExist(".weather .normal.medium sup i.wi-humidity", 10000);
|
||||||
@@ -121,7 +120,7 @@ describe("Weather module", function() {
|
|||||||
|
|
||||||
it("should render degreeLabel = true", async function() {
|
it("should render degreeLabel = true", async function() {
|
||||||
const weather = generateWeather();
|
const weather = generateWeather();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
await app.client.waitUntilTextExists(".weather .large.light span.bright", "1°C", 10000);
|
await app.client.waitUntilTextExists(".weather .large.light span.bright", "1°C", 10000);
|
||||||
|
|
||||||
@@ -145,7 +144,7 @@ describe("Weather module", function() {
|
|||||||
speed: 11.8 * 2.23694
|
speed: 11.8 * 2.23694
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "6 WSW", 10000);
|
await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "6 WSW", 10000);
|
||||||
await app.client.waitUntilTextExists(".weather .large.light span.bright", "34,7°", 10000);
|
await app.client.waitUntilTextExists(".weather .large.light span.bright", "34,7°", 10000);
|
||||||
@@ -163,7 +162,7 @@ describe("Weather module", function() {
|
|||||||
speed: 11.8 * 2.23694
|
speed: 11.8 * 2.23694
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(3)", "93,7", 10000);
|
await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(3)", "93,7", 10000);
|
||||||
await app.client.waitUntilTextExists(".weather .large.light span.bright", "34,7°", 10000);
|
await app.client.waitUntilTextExists(".weather .large.light span.bright", "34,7°", 10000);
|
||||||
@@ -186,7 +185,7 @@ describe("Weather module", function() {
|
|||||||
|
|
||||||
it("should render days", async function() {
|
it("should render days", async function() {
|
||||||
const weather = generateWeatherForecast();
|
const weather = generateWeatherForecast();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
const days = ["Fri", "Sat", "Sun", "Mon", "Tue"];
|
const days = ["Fri", "Sat", "Sun", "Mon", "Tue"];
|
||||||
|
|
||||||
@@ -197,7 +196,7 @@ describe("Weather module", function() {
|
|||||||
|
|
||||||
it("should render icons", async function() {
|
it("should render icons", async function() {
|
||||||
const weather = generateWeatherForecast();
|
const weather = generateWeatherForecast();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
const icons = ["day-cloudy", "rain", "day-sunny", "day-sunny", "day-sunny"];
|
const icons = ["day-cloudy", "rain", "day-sunny", "day-sunny", "day-sunny"];
|
||||||
|
|
||||||
@@ -208,7 +207,7 @@ describe("Weather module", function() {
|
|||||||
|
|
||||||
it("should render max temperatures", async function() {
|
it("should render max temperatures", async function() {
|
||||||
const weather = generateWeatherForecast();
|
const weather = generateWeatherForecast();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
const temperatures = ["24.4°", "21.0°", "22.9°", "23.4°", "20.6°"];
|
const temperatures = ["24.4°", "21.0°", "22.9°", "23.4°", "20.6°"];
|
||||||
|
|
||||||
@@ -219,7 +218,7 @@ describe("Weather module", function() {
|
|||||||
|
|
||||||
it("should render min temperatures", async function() {
|
it("should render min temperatures", async function() {
|
||||||
const weather = generateWeatherForecast();
|
const weather = generateWeatherForecast();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
const temperatures = ["15.3°", "13.6°", "13.8°", "13.9°", "10.9°"];
|
const temperatures = ["15.3°", "13.6°", "13.8°", "13.9°", "10.9°"];
|
||||||
|
|
||||||
@@ -230,7 +229,7 @@ describe("Weather module", function() {
|
|||||||
|
|
||||||
it("should render fading of rows", async function() {
|
it("should render fading of rows", async function() {
|
||||||
const weather = generateWeatherForecast();
|
const weather = generateWeatherForecast();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
const opacities = [1, 1, 0.8, 0.5333333333333333, 0.2666666666666667];
|
const opacities = [1, 1, 0.8, 0.5333333333333333, 0.2666666666666667];
|
||||||
|
|
||||||
@@ -250,14 +249,14 @@ describe("Weather module", function() {
|
|||||||
|
|
||||||
it("should render custom table class", async function() {
|
it("should render custom table class", async function() {
|
||||||
const weather = generateWeatherForecast();
|
const weather = generateWeatherForecast();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
await app.client.waitForExist(".weather table.myTableClass", 10000);
|
await app.client.waitForExist(".weather table.myTableClass", 10000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render colored rows", async function() {
|
it("should render colored rows", async function() {
|
||||||
const weather = generateWeatherForecast();
|
const weather = generateWeatherForecast();
|
||||||
await setup([weather, template]);
|
await setup({template, data: weather});
|
||||||
|
|
||||||
await app.client.waitForExist(".weather table.myTableClass", 10000);
|
await app.client.waitForExist(".weather table.myTableClass", 10000);
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@ describe("Vendors", function () {
|
|||||||
var app = null;
|
var app = null;
|
||||||
|
|
||||||
before(function () {
|
before(function () {
|
||||||
|
process.env.MM_CONFIG_FILE = "tests/configs/env.js";
|
||||||
return helpers.startApplication({
|
return helpers.startApplication({
|
||||||
args: ["js/electron.js"]
|
args: ["js/electron.js"]
|
||||||
}).then(function (startedApp) { app = startedApp; });
|
}).then(function (startedApp) { app = startedApp; });
|
||||||
@@ -26,19 +27,12 @@ describe("Vendors", function () {
|
|||||||
|
|
||||||
describe("Get list vendors", function () {
|
describe("Get list vendors", function () {
|
||||||
|
|
||||||
before(function () {
|
|
||||||
process.env.MM_CONFIG_FILE = "tests/configs/env.js";
|
|
||||||
});
|
|
||||||
|
|
||||||
var vendors = require(__dirname + "/../../vendor/vendor.js");
|
var vendors = require(__dirname + "/../../vendor/vendor.js");
|
||||||
Object.keys(vendors).forEach(vendor => {
|
Object.keys(vendors).forEach(vendor => {
|
||||||
it(`should return 200 HTTP code for vendor "${vendor}"`, function () {
|
it(`should return 200 HTTP code for vendor "${vendor}"`, function () {
|
||||||
urlVendor = "http://localhost:8080/vendor/" + vendors[vendor];
|
urlVendor = "http://localhost:8080/vendor/" + vendors[vendor];
|
||||||
request.get(urlVendor, function (err, res, body) {
|
request.get(urlVendor, function (err, res, body) {
|
||||||
if (!err)
|
|
||||||
expect(res.statusCode).to.equal(200);
|
expect(res.statusCode).to.equal(200);
|
||||||
else
|
|
||||||
mlog.pending(`There error vendor 200 test ${err}`);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -47,10 +41,7 @@ describe("Vendors", function () {
|
|||||||
it(`should return 404 HTTP code for vendor https://localhost/"${vendor}"`, function() {
|
it(`should return 404 HTTP code for vendor https://localhost/"${vendor}"`, function() {
|
||||||
urlVendor = "http://localhost:8080/" + vendors[vendor];
|
urlVendor = "http://localhost:8080/" + vendors[vendor];
|
||||||
request.get(urlVendor, function (err, res, body) {
|
request.get(urlVendor, function (err, res, body) {
|
||||||
if (!err)
|
|
||||||
expect(res.statusCode).to.equal(404);
|
expect(res.statusCode).to.equal(404);
|
||||||
else
|
|
||||||
mlog.pending(`There error vendor 404 test ${err}`);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
4
tests/node_modules/webdriverajaxstub/index.js
generated
vendored
@@ -3,7 +3,7 @@ function plugin (wdInstance, requests) {
|
|||||||
throw new Error("You can't use WebdriverAjaxStub with this version of WebdriverIO");
|
throw new Error("You can't use WebdriverAjaxStub with this version of WebdriverIO");
|
||||||
}
|
}
|
||||||
|
|
||||||
function stub(requests, done) {
|
function stub({template, data}, done) {
|
||||||
window.XMLHttpRequest = function () {
|
window.XMLHttpRequest = function () {
|
||||||
this.open = function (method, url) {
|
this.open = function (method, url) {
|
||||||
this.method = method;
|
this.method = method;
|
||||||
@@ -13,7 +13,7 @@ function plugin (wdInstance, requests) {
|
|||||||
this.send = function () {
|
this.send = function () {
|
||||||
this.status = 200;
|
this.status = 200;
|
||||||
this.readyState = 4;
|
this.readyState = 4;
|
||||||
const response = requests.shift() || [];
|
const response = this.url.includes(".njk") ? template : data;
|
||||||
this.response = response;
|
this.response = response;
|
||||||
this.responseText = response;
|
this.responseText = response;
|
||||||
this.onreadystatechange();
|
this.onreadystatechange();
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint no-multi-spaces: 0 */
|
||||||
var expect = require("chai").expect;
|
var expect = require("chai").expect;
|
||||||
|
|
||||||
describe("Functions module currentweather", function() {
|
describe("Functions module currentweather", function() {
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint no-multi-spaces: 0 */
|
||||||
var expect = require("chai").expect;
|
var expect = require("chai").expect;
|
||||||
|
|
||||||
describe("Functions module weatherforecast", function() {
|
describe("Functions module weatherforecast", function() {
|
||||||
|
@@ -28,6 +28,9 @@
|
|||||||
|
|
||||||
"UPDATE_NOTIFICATION": "MagicMirror² päivitys saatavilla.",
|
"UPDATE_NOTIFICATION": "MagicMirror² päivitys saatavilla.",
|
||||||
"UPDATE_NOTIFICATION_MODULE": "Päivitys saatavilla moduulille {MODULE_NAME}.",
|
"UPDATE_NOTIFICATION_MODULE": "Päivitys saatavilla moduulille {MODULE_NAME}.",
|
||||||
|
"UPDATE_INFO_SINGLE": "Nykyasennus on {COMMIT_COUNT} muutoksen jäljessä {BRANCH_NAME} haaraan nähden.",
|
||||||
|
"UPDATE_INFO_MULTIPLE": "Nykyasennus on {COMMIT_COUNT} muutosta jäljessä {BRANCH_NAME} haaraan nähden.",
|
||||||
|
|
||||||
"FEELS": "Tuntuu kuin"
|
"FEELS": "Tuntuu kuin",
|
||||||
|
"PRECIP": "Sateen todennäköisyys"
|
||||||
}
|
}
|
||||||
|
36
translations/he.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"LOADING": "טוען...",
|
||||||
|
|
||||||
|
"TODAY": "היום",
|
||||||
|
"TOMORROW": "מחר",
|
||||||
|
"DAYAFTERTOMORROW": "בעוד יומיים",
|
||||||
|
"RUNNING": "מסתיים ב",
|
||||||
|
"EMPTY": "אין ארועים",
|
||||||
|
|
||||||
|
"WEEK": "{weekNumber} שבוע",
|
||||||
|
|
||||||
|
"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_SINGLE": "ההתקנה הנוכחית נמצאת מאחור הענף {BRANCH_NAME} ב-{COMMIT_COUNT} מופע",
|
||||||
|
"UPDATE_INFO_MULTIPLE": "ההתקנה הנוכחית נמצאת מאחור הענף {BRANCH_NAME} ב-{COMMIT_COUNT} מופעים",
|
||||||
|
|
||||||
|
"FEELS": "מרגיש כמו",
|
||||||
|
"PRECIP": "משקעים"
|
||||||
|
}
|
@@ -31,5 +31,5 @@
|
|||||||
"UPDATE_INFO_SINGLE": "Instalirana verzija {COMMIT_COUNT} commit kasni za branch-om {BRANCH_NAME}.",
|
"UPDATE_INFO_SINGLE": "Instalirana verzija {COMMIT_COUNT} commit kasni za branch-om {BRANCH_NAME}.",
|
||||||
"UPDATE_INFO_MULTIPLE": "Instalirana verzija {COMMIT_COUNT} commit-ova kasni za branch-om {BRANCH_NAME}.",
|
"UPDATE_INFO_MULTIPLE": "Instalirana verzija {COMMIT_COUNT} commit-ova kasni za branch-om {BRANCH_NAME}.",
|
||||||
|
|
||||||
"FEELS": "Osjeća"
|
"FEELS": "Osjećaj"
|
||||||
}
|
}
|
||||||
|
@@ -31,5 +31,6 @@
|
|||||||
"UPDATE_INFO_SINGLE": "Zainstalowana wersja odbiega o {COMMIT_COUNT} commit od gałęzi {BRANCH_NAME}.",
|
"UPDATE_INFO_SINGLE": "Zainstalowana wersja odbiega o {COMMIT_COUNT} commit od gałęzi {BRANCH_NAME}.",
|
||||||
"UPDATE_INFO_MULTIPLE": "Zainstalowana wersja odbiega o {COMMIT_COUNT} commitów od gałęzi {BRANCH_NAME}.",
|
"UPDATE_INFO_MULTIPLE": "Zainstalowana wersja odbiega o {COMMIT_COUNT} commitów od gałęzi {BRANCH_NAME}.",
|
||||||
|
|
||||||
"FEELS": "Odczuwalna"
|
"FEELS": "Odczuwalna",
|
||||||
|
"PRECIP": "Szansa opadów"
|
||||||
}
|
}
|
||||||
|
@@ -26,5 +26,8 @@
|
|||||||
"UPDATE_NOTIFICATION": "Nova atualização para MagicMirror disponível.",
|
"UPDATE_NOTIFICATION": "Nova atualização para MagicMirror disponível.",
|
||||||
"UPDATE_NOTIFICATION_MODULE": "Atualização para o módulo {MODULE_NAME} disponível.",
|
"UPDATE_NOTIFICATION_MODULE": "Atualização para o módulo {MODULE_NAME} disponível.",
|
||||||
"UPDATE_INFO_SINGLE": "Sua versão atual é a {COMMIT_COUNT} commit dentro do seguinte branch {BRANCH_NAME}.",
|
"UPDATE_INFO_SINGLE": "Sua versão atual é a {COMMIT_COUNT} commit dentro do seguinte branch {BRANCH_NAME}.",
|
||||||
"UPDATE_INFO_MULTIPLE": "Sua versão atual é a {COMMIT_COUNT} commits dentro do seguinte branch {BRANCH_NAME}."
|
"UPDATE_INFO_MULTIPLE": "Sua versão atual é a {COMMIT_COUNT} commits dentro do seguinte branch {BRANCH_NAME}.",
|
||||||
|
|
||||||
|
"FEELS": "Percebida",
|
||||||
|
"PRECIP": "PoP"
|
||||||
}
|
}
|
||||||
|
@@ -3,9 +3,12 @@
|
|||||||
|
|
||||||
"TODAY": "Bugün",
|
"TODAY": "Bugün",
|
||||||
"TOMORROW": "Yarın",
|
"TOMORROW": "Yarın",
|
||||||
|
"DAYAFTERTOMORROW": "İki gün içinde",
|
||||||
"RUNNING": "Biten",
|
"RUNNING": "Biten",
|
||||||
"EMPTY": "Yakında etkinlik yok.",
|
"EMPTY": "Yakında etkinlik yok.",
|
||||||
|
|
||||||
|
"WEEK": "Hafta {weekNumber}",
|
||||||
|
|
||||||
"N": "K",
|
"N": "K",
|
||||||
"NNE": "KKD",
|
"NNE": "KKD",
|
||||||
"NE": "KD",
|
"NE": "KD",
|
||||||
@@ -21,5 +24,14 @@
|
|||||||
"W": "B",
|
"W": "B",
|
||||||
"WNW": "BKB",
|
"WNW": "BKB",
|
||||||
"NW": "KB",
|
"NW": "KB",
|
||||||
"NNW": "KKB"
|
"NNW": "KKB",
|
||||||
|
|
||||||
|
"UPDATE_NOTIFICATION": "MagicMirror² güncellemesi mevcut.",
|
||||||
|
"UPDATE_NOTIFICATION_MODULE": "{MODULE_NAME} modulü için güncelleme mevcut.",
|
||||||
|
"UPDATE_INFO_SINGLE": "Sahip olduğunuz kurulum {BRANCH_NAME} branchinden {COMMIT_COUNT} commit geridedir.",
|
||||||
|
"UPDATE_INFO_MULTIPLE": "Sahip olduğunuz kurulum {BRANCH_NAME} branchinden {COMMIT_COUNT} commit geridedir.",
|
||||||
|
|
||||||
|
"FEELS": "Hissedilen",
|
||||||
|
"PRECIP": "Yağış"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -41,7 +41,9 @@ var translations = {
|
|||||||
"hr" : "translations/hr.json", // Croatian
|
"hr" : "translations/hr.json", // Croatian
|
||||||
"sk" : "translations/sk.json", // Slovak
|
"sk" : "translations/sk.json", // Slovak
|
||||||
"tlh" : "translations/tlh.json", // Klingon
|
"tlh" : "translations/tlh.json", // Klingon
|
||||||
"ms-my" : "translations/ms-my.json" // Malay
|
"ms-my" : "translations/ms-my.json", // Malay
|
||||||
|
"he" : "translations/he.json", // Hebrew
|
||||||
|
"uk" : "translations/uk.json" // Ukrainian
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof module !== "undefined") {module.exports = translations;}
|
if (typeof module !== "undefined") {module.exports = translations;}
|
||||||
|
36
translations/uk.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"LOADING": "Завантаження …",
|
||||||
|
|
||||||
|
"TODAY": "Сьогодні",
|
||||||
|
"TOMORROW": "Завтра",
|
||||||
|
"DAYAFTERTOMORROW": "Післязавтра",
|
||||||
|
"RUNNING": "Закінчується через",
|
||||||
|
"EMPTY": "Немає найближчих подій",
|
||||||
|
|
||||||
|
"WEEK": "Тиждень {weekNumber}",
|
||||||
|
|
||||||
|
"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_SINGLE": "Поточна версія на {COMMIT_COUNT} комміт позаду від гілки {BRANCH_NAME}.",
|
||||||
|
"UPDATE_INFO_MULTIPLE": "Поточна інсталяція на {COMMIT_COUNT} комітів позаду від гілки {BRANCH_NAME}.",
|
||||||
|
|
||||||
|
"FEELS": "Відчувається як",
|
||||||
|
"PRECIP": "Опади"
|
||||||
|
}
|
@@ -1,11 +0,0 @@
|
|||||||
# Long history here
|
|
||||||
# https://github.com/MichMich/MagicMirror/pull/1540
|
|
||||||
STATUS_CUSTOM_CSS=$(git ls-files -v css/custom.css| awk '{print $1}')
|
|
||||||
|
|
||||||
if [ "$STATUS_CUSTOM_CSS" = "H" ]; then
|
|
||||||
echo "We'll remove from the repository the css/custom.css"
|
|
||||||
echo "This script apply git update-index --skip-worktree css/custom.css"
|
|
||||||
git update-index --skip-worktree css/custom.css
|
|
||||||
git rm --cached css/custom.css
|
|
||||||
fi
|
|
||||||
|
|