mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-08-21 12:55:22 +00:00
@@ -25,6 +25,7 @@
|
||||
"prettier/prettier": "error",
|
||||
"eqeqeq": "error",
|
||||
"no-prototype-builtins": "off",
|
||||
"no-unused-vars": "off"
|
||||
"no-unused-vars": "off",
|
||||
"no-useless-return": "error"
|
||||
}
|
||||
}
|
||||
|
6
.github/CONTRIBUTING.md
vendored
6
.github/CONTRIBUTING.md
vendored
@@ -18,7 +18,7 @@ To run ESLint, use `npm run lint:js`.
|
||||
|
||||
We use [StyleLint](https://stylelint.io) to lint our CSS. Our configuration is in our .stylelintrc file.
|
||||
|
||||
To run StyleLint, use `npm run lint:style`.
|
||||
To run StyleLint, use `npm run lint:css`.
|
||||
|
||||
### Submitting Issues
|
||||
|
||||
@@ -32,9 +32,9 @@ 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/4, Windows, Mac, Linux, System V UNIX).
|
||||
|
||||
**Node Version**: Make sure it's version 10 or later.
|
||||
**Node Version**: Make sure it's version 12 or later (recommended is 14).
|
||||
|
||||
**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 know which version of MagicMirror you are running. It can be found in the `package.json` 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.
|
||||
|
||||
|
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1,2 +1,2 @@
|
||||
github: MichMich
|
||||
custom: ['https://magicmirror.builders/#donate']
|
||||
custom: ["https://magicmirror.builders/#donate"]
|
||||
|
10
.github/ISSUE_TEMPLATE.md
vendored
10
.github/ISSUE_TEMPLATE.md
vendored
@@ -19,8 +19,10 @@ If you are facing an issue or found a bug while trying to install MagicMirror vi
|
||||
|
||||
## 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)
|
||||
If you are facing an issue or found a bug while running MagicMirror inside a Docker container please create an issue in the corresponding repository:
|
||||
|
||||
- karsten13/magicmirror: [https://gitlab.com/khassel/magicmirror](https://gitlab.com/khassel/magicmirror)
|
||||
- (deprecated) bastilimbach/docker-magicmirror: [https://github.com/bastilimbach/docker-MagicMirror](https://github.com/bastilimbach/docker-MagicMirror)
|
||||
|
||||
---
|
||||
|
||||
@@ -31,9 +33,9 @@ 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/4, Windows, Mac, Linux, System V UNIX).
|
||||
|
||||
**Node Version**: Make sure it's version 10 or later.
|
||||
**Node Version**: Make sure it's version 12 or later (recommended is 14).
|
||||
|
||||
**MagicMirror Version**: Please let us now which version of MagicMirror you are running. It can be found in the `package.log` file.
|
||||
**MagicMirror Version**: Please let us know which version of MagicMirror you are running. It can be found in the `package.json` 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.
|
||||
|
||||
|
19
.github/PULL_REQUEST_TEMPLATE.md
vendored
19
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,24 +2,21 @@ Hello and thank you for wanting to contribute to the MagicMirror project
|
||||
|
||||
**Please make sure that you have followed these 4 rules before submitting your Pull Request:**
|
||||
|
||||
> 1) Base your pull requests against the `develop` branch.
|
||||
> 1. Base your pull requests against the `develop` branch.
|
||||
>
|
||||
> 2. Include these infos in the description:
|
||||
>
|
||||
> 2) Include these infos in the description:
|
||||
> * Does the pull request solve a **related** issue?
|
||||
> * If so, can you reference the issue like this `Fixes #<issue_number>`?
|
||||
> * What does the pull request accomplish? Use a list if needed.
|
||||
> * If it includes major visual changes please add screenshots.
|
||||
> - Does the pull request solve a **related** issue?
|
||||
> - If so, can you reference the issue like this `Fixes #<issue_number>`?
|
||||
> - What does the pull request accomplish? Use a list if needed.
|
||||
> - If it includes major visual changes please add screenshots.
|
||||
>
|
||||
>
|
||||
> 3) Please run `npm run lint:prettier` before submitting so that
|
||||
> 3. Please run `npm run lint:prettier` before submitting so that
|
||||
> style issues are fixed.
|
||||
>
|
||||
>
|
||||
> 4) Don't forget to add an entry about your changes to
|
||||
> 4. Don't forget to add an entry about your changes to
|
||||
> the CHANGELOG.md file.
|
||||
|
||||
|
||||
**Note**: Sometimes the development moves very fast. It is highly
|
||||
recommended that you update your branch of `develop` before creating a
|
||||
pull request to send us your changes. This makes everyone's lives
|
||||
|
BIN
.github/header.psd
vendored
BIN
.github/header.psd
vendored
Binary file not shown.
37
.github/workflows/automated-tests.yml
vendored
Normal file
37
.github/workflows/automated-tests.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: "Run Automated Tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, develop]
|
||||
pull_request:
|
||||
branches: [master, develop]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 16.x]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install dependencies and run tests
|
||||
run: |
|
||||
Xvfb :99 -screen 0 1024x768x16 &
|
||||
export DISPLAY=:99
|
||||
npm install
|
||||
touch css/custom.css
|
||||
npm run test:prettier
|
||||
npm run test:js
|
||||
npm run test:css
|
||||
npm run test:unit
|
||||
npm run test:e2e
|
||||
npm run test:electron
|
10
.github/workflows/codecov-test-suites.yml
vendored
10
.github/workflows/codecov-test-suites.yml
vendored
@@ -13,13 +13,17 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: |
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install dependencies and run coverage
|
||||
run: |
|
||||
Xvfb :99 -screen 0 1024x768x16 &
|
||||
export DISPLAY=:99
|
||||
npm ci
|
||||
touch css/custom.css
|
||||
npm run test:coverage
|
||||
- uses: codecov/codecov-action@v1
|
||||
- name: Upload coverage results to codecov
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
file: ./coverage/lcov.info
|
||||
fail_ci_if_error: true
|
||||
|
10
.github/workflows/enforce-changelog.yml
vendored
10
.github/workflows/enforce-changelog.yml
vendored
@@ -11,8 +11,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: dangoslen/changelog-enforcer@v1.6.1
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Enforce changelog️
|
||||
uses: dangoslen/changelog-enforcer@v1.6.1
|
||||
with:
|
||||
changeLogPath: 'CHANGELOG.md'
|
||||
skipLabels: 'Skip Changelog'
|
||||
changeLogPath: "CHANGELOG.md"
|
||||
skipLabels: "Skip Changelog"
|
||||
|
33
.github/workflows/node-ci.js.yml
vendored
33
.github/workflows/node-ci.js.yml
vendored
@@ -1,33 +0,0 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: "Run Automated Tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 16.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: |
|
||||
Xvfb :99 -screen 0 1024x768x16 &
|
||||
export DISPLAY=:99
|
||||
npm install
|
||||
npm run test:prettier
|
||||
npm run test:js
|
||||
npm run test:css
|
||||
npm run test:unit
|
||||
npm run test:e2e
|
1
.husky/.gitignore
vendored
1
.husky/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
_
|
@@ -1,8 +1,4 @@
|
||||
/config
|
||||
/coverage
|
||||
/vendor
|
||||
!/vendor/vendor.js
|
||||
.github
|
||||
.nyc_output
|
||||
package-lock.json
|
||||
*.ts
|
||||
|
33
CHANGELOG.md
33
CHANGELOG.md
@@ -5,6 +5,38 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror²
|
||||
|
||||
## [2.17.0] - 2021-10-01
|
||||
|
||||
Special thanks to the following contributors: @apiontek, @eouia, @jupadin, @khassel and @rejas.
|
||||
|
||||
### Added
|
||||
|
||||
- Added showTime parameter to clock module for enabling/disabling time display in analog clock.
|
||||
- Added custom electron switches from user config (`config.electronSwitches`).
|
||||
- Added unit tests for updatenotification module.
|
||||
|
||||
### Updated
|
||||
|
||||
- Bump electron to v13 (and spectron to v15) and update other dependencies in package.json.
|
||||
- Refactor test configs, use default test config for all tests.
|
||||
- Updated github templates.
|
||||
- Actually test all js and css files when lint script is run.
|
||||
- Update jsdocs and print warnings during testing too.
|
||||
- Update weathergov provider to try fetching not just current, but also foreacst, when API URLs available.
|
||||
- Refactored clock layout.
|
||||
- Refactored methods from weatherproviders into weatherobject (isDaytime, updateSunTime).
|
||||
- Use of `logger.js` in jest tests.
|
||||
- Run prettier over all relevant files.
|
||||
- Move tests needing electron in new category `electron`, use `server only` mode in `e2e` tests.
|
||||
- Update dependencies in package.json.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix undefined error with ignoreToday option in weather module (#2620).
|
||||
- Fix time zone correction in calendar module when the date hour is equal to the time zone correction value (#2632).
|
||||
- Fix black cursor on startup when using electron.
|
||||
- Fix update notification not working for own repository (#2644).
|
||||
|
||||
## [2.16.0] - 2021-07-01
|
||||
|
||||
Special thanks to the following contributors: @210954, @B1gG, @codac, @Crazylegstoo, @daniel, @earlman, @ezeholz, @FrancoisRmn, @jupadin, @khassel, @KristjanESPERANTO, @njwilliams, @oemel09, @r3wald, @rejas, @rico24, Faizan Ahmed.
|
||||
@@ -412,6 +444,7 @@ Special thanks to @sdetweil for all his great contributions!
|
||||
- Update `ical.js` to solve various calendar issues.
|
||||
- Update weather city list url [#1676](https://github.com/MichMich/MagicMirror/issues/1676)
|
||||
- Only update clock once per minute when seconds aren't shown
|
||||
- Update weatherprovider documentation.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@@ -57,7 +57,8 @@ let config = {
|
||||
calendars: [
|
||||
{
|
||||
symbol: "calendar-check",
|
||||
url: "webcal://www.calendarlabs.com/ical-calendar/ics/76/US_Holidays.ics" }
|
||||
url: "webcal://www.calendarlabs.com/ical-calendar/ics/76/US_Holidays.ics"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@@ -16,7 +16,7 @@
|
||||
<!-- custom.css is loaded by the loader.js to make sure it's loaded after the module css files. -->
|
||||
|
||||
<script type="text/javascript">
|
||||
var version = "#VERSION#";
|
||||
window.mmVersion = "#VERSION#";
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
@@ -48,6 +48,7 @@ process.on("uncaughtException", function (err) {
|
||||
*/
|
||||
function App() {
|
||||
let nodeHelpers = [];
|
||||
let httpServer;
|
||||
|
||||
/**
|
||||
* Loads the config file. Combines it with the defaults, and runs the
|
||||
@@ -222,7 +223,7 @@ function App() {
|
||||
}
|
||||
|
||||
loadModules(modules, function () {
|
||||
const server = new Server(config, function (app, io) {
|
||||
httpServer = new Server(config, function (app, io) {
|
||||
Log.log("Server started ...");
|
||||
|
||||
for (let nodeHelper of nodeHelpers) {
|
||||
@@ -253,6 +254,7 @@ function App() {
|
||||
nodeHelper.stop();
|
||||
}
|
||||
}
|
||||
httpServer.close();
|
||||
};
|
||||
|
||||
/**
|
||||
|
20
js/class.js
20
js/class.js
@@ -8,8 +8,8 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
(function () {
|
||||
var initializing = false;
|
||||
var fnTest = /xyz/.test(function () {
|
||||
let initializing = false;
|
||||
const fnTest = /xyz/.test(function () {
|
||||
xyz;
|
||||
})
|
||||
? /\b_super\b/
|
||||
@@ -20,27 +20,27 @@
|
||||
|
||||
// Create a new Class that inherits from this class
|
||||
Class.extend = function (prop) {
|
||||
var _super = this.prototype;
|
||||
let _super = this.prototype;
|
||||
|
||||
// Instantiate a base class (but only create the instance,
|
||||
// don't run the init constructor)
|
||||
initializing = true;
|
||||
var prototype = new this();
|
||||
const prototype = new this();
|
||||
initializing = false;
|
||||
|
||||
// Make a copy of all prototype properties, to prevent reference issues.
|
||||
for (var p in prototype) {
|
||||
for (const p in prototype) {
|
||||
prototype[p] = cloneObject(prototype[p]);
|
||||
}
|
||||
|
||||
// Copy the properties over onto the new prototype
|
||||
for (var name in prop) {
|
||||
for (const name in prop) {
|
||||
// Check if we're overwriting an existing function
|
||||
prototype[name] =
|
||||
typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name])
|
||||
? (function (name, fn) {
|
||||
return function () {
|
||||
var tmp = this._super;
|
||||
const tmp = this._super;
|
||||
|
||||
// Add a new ._super() method that is the same method
|
||||
// but on the super-class
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
// The method only need to be bound temporarily, so we
|
||||
// remove it when we're done executing
|
||||
var ret = fn.apply(this, arguments);
|
||||
const ret = fn.apply(this, arguments);
|
||||
this._super = tmp;
|
||||
|
||||
return ret;
|
||||
@@ -91,8 +91,8 @@ function cloneObject(obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
var temp = obj.constructor(); // give temp the original obj's constructor
|
||||
for (var key in obj) {
|
||||
const temp = obj.constructor(); // give temp the original obj's constructor
|
||||
for (const key in obj) {
|
||||
temp[key] = cloneObject(obj[key]);
|
||||
|
||||
if (key === "lockStrings") {
|
||||
|
@@ -19,7 +19,8 @@ let mainWindow;
|
||||
*
|
||||
*/
|
||||
function createWindow() {
|
||||
app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
|
||||
let electronSwitchesDefaults = ["autoplay-policy", "no-user-gesture-required"];
|
||||
app.commandLine.appendSwitch(...new Set(electronSwitchesDefaults, config.electronSwitches));
|
||||
let electronOptionsDefaults = {
|
||||
width: 800,
|
||||
height: 600,
|
||||
@@ -65,12 +66,17 @@ function createWindow() {
|
||||
if (process.argv.includes("dev")) {
|
||||
if (process.env.JEST_WORKER_ID !== undefined) {
|
||||
// if we are running with jest
|
||||
var devtools = new BrowserWindow(electronOptions);
|
||||
const devtools = new BrowserWindow(electronOptions);
|
||||
mainWindow.webContents.setDevToolsWebContents(devtools.webContents);
|
||||
}
|
||||
mainWindow.webContents.openDevTools();
|
||||
}
|
||||
|
||||
// simulate mouse move to hide black cursor on start
|
||||
mainWindow.webContents.on("dom-ready", (event) => {
|
||||
mainWindow.webContents.sendInputEvent({ type: "mouseMove", x: 0, y: 0 });
|
||||
});
|
||||
|
||||
// Set responders for window events.
|
||||
mainWindow.on("closed", function () {
|
||||
mainWindow = null;
|
||||
@@ -102,7 +108,12 @@ app.on("ready", function () {
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on("window-all-closed", function () {
|
||||
if (process.env.JEST_WORKER_ID !== undefined) {
|
||||
// if we are running with jest
|
||||
app.quit();
|
||||
} else {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", function () {
|
||||
|
33
js/logger.js
33
js/logger.js
@@ -9,12 +9,13 @@
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
if (process.env.JEST_WORKER_ID === undefined) {
|
||||
// add timestamps in front of log messages
|
||||
require("console-stamp")(console, {
|
||||
pattern: "yyyy-mm-dd HH:MM:ss.l",
|
||||
include: ["debug", "log", "info", "warn", "error"]
|
||||
});
|
||||
|
||||
}
|
||||
// Node, CommonJS-like
|
||||
module.exports = factory(root.config);
|
||||
} else {
|
||||
@@ -22,7 +23,18 @@
|
||||
root.Log = factory(root.config);
|
||||
}
|
||||
})(this, function (config) {
|
||||
const logLevel = {
|
||||
let logLevel;
|
||||
let enableLog;
|
||||
if (typeof exports === "object") {
|
||||
// in nodejs and not running with jest
|
||||
enableLog = process.env.JEST_WORKER_ID === undefined;
|
||||
} else {
|
||||
// in browser and not running with jsdom
|
||||
enableLog = typeof window === "object" && window.name !== "jsdom";
|
||||
}
|
||||
|
||||
if (enableLog) {
|
||||
logLevel = {
|
||||
debug: Function.prototype.bind.call(console.debug, console),
|
||||
log: Function.prototype.bind.call(console.log, console),
|
||||
info: Function.prototype.bind.call(console.info, console),
|
||||
@@ -45,6 +57,23 @@
|
||||
});
|
||||
}
|
||||
};
|
||||
} else {
|
||||
logLevel = {
|
||||
debug: function () {},
|
||||
log: function () {},
|
||||
info: function () {},
|
||||
warn: function () {},
|
||||
error: function () {},
|
||||
group: function () {},
|
||||
groupCollapsed: function () {},
|
||||
groupEnd: function () {},
|
||||
time: function () {},
|
||||
timeEnd: function () {},
|
||||
timeStamp: function () {}
|
||||
};
|
||||
|
||||
logLevel.setLogLevel = function () {};
|
||||
}
|
||||
|
||||
return logLevel;
|
||||
});
|
||||
|
@@ -7,7 +7,7 @@
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var Module = Class.extend({
|
||||
const Module = Class.extend({
|
||||
/*********************************************************
|
||||
* All methods (and properties) below can be subclassed. *
|
||||
*********************************************************/
|
||||
@@ -498,8 +498,8 @@ Module.create = function (name) {
|
||||
|
||||
Module.register = function (name, moduleDefinition) {
|
||||
if (moduleDefinition.requiresVersion) {
|
||||
Log.log("Check MagicMirror version for module '" + name + "' - Minimum version: " + moduleDefinition.requiresVersion + " - Current version: " + window.version);
|
||||
if (cmpVersions(window.version, moduleDefinition.requiresVersion) >= 0) {
|
||||
Log.log("Check MagicMirror version for module '" + name + "' - Minimum version: " + moduleDefinition.requiresVersion + " - Current version: " + window.mmVersion);
|
||||
if (cmpVersions(window.mmVersion, moduleDefinition.requiresVersion) >= 0) {
|
||||
Log.log("Version is ok!");
|
||||
} else {
|
||||
Log.warn("Version is incorrect. Skip module: '" + name + "'");
|
||||
@@ -510,6 +510,8 @@ Module.register = function (name, moduleDefinition) {
|
||||
Module.definitions[name] = moduleDefinition;
|
||||
};
|
||||
|
||||
window.Module = Module;
|
||||
|
||||
/**
|
||||
* Compare two semantic version numbers and return the difference.
|
||||
*
|
||||
|
15
js/server.js
15
js/server.js
@@ -23,6 +23,7 @@ const Utils = require("./utils.js");
|
||||
*/
|
||||
function Server(config, callback) {
|
||||
const port = process.env.MM_PORT || config.port;
|
||||
const serverSockets = new Set();
|
||||
|
||||
let server = null;
|
||||
if (config.useHttps) {
|
||||
@@ -42,6 +43,13 @@ function Server(config, callback) {
|
||||
allowEIO3: true
|
||||
});
|
||||
|
||||
server.on("connection", (socket) => {
|
||||
serverSockets.add(socket);
|
||||
socket.on("close", () => {
|
||||
serverSockets.delete(socket);
|
||||
});
|
||||
});
|
||||
|
||||
Log.log(`Starting server on port ${port} ... `);
|
||||
|
||||
server.listen(port, config.address || "localhost");
|
||||
@@ -92,6 +100,13 @@ function Server(config, callback) {
|
||||
if (typeof callback === "function") {
|
||||
callback(app, io);
|
||||
}
|
||||
|
||||
this.close = function () {
|
||||
for (const socket of serverSockets.values()) {
|
||||
socket.destroy();
|
||||
}
|
||||
server.close();
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = Server;
|
||||
|
@@ -6,7 +6,7 @@
|
||||
* By Christopher Fenner https://github.com/CFenner
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var Translator = (function () {
|
||||
const Translator = (function () {
|
||||
/**
|
||||
* Load a JSON file via XHR.
|
||||
*
|
||||
@@ -141,12 +141,7 @@ var Translator = (function () {
|
||||
* The first language defined in translations.js will be used.
|
||||
*/
|
||||
loadCoreTranslationsFallback: function () {
|
||||
// The variable `first` will contain the first
|
||||
// defined translation after the following line.
|
||||
for (var first in translations) {
|
||||
break;
|
||||
}
|
||||
|
||||
let first = Object.keys(translations)[0];
|
||||
if (first) {
|
||||
Log.log("Loading core translation fallback file: " + translations[first]);
|
||||
loadJSON(translations[first], (translations) => {
|
||||
@@ -156,3 +151,5 @@ var Translator = (function () {
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
window.Translator = Translator;
|
||||
|
@@ -1,16 +1,16 @@
|
||||
type ModuleProperties = {
|
||||
defaults?: object,
|
||||
start?(): void,
|
||||
getHeader?(): string,
|
||||
getTemplate?(): string,
|
||||
getTemplateData?(): object,
|
||||
notificationReceived?(notification: string, payload: any, sender: object): void,
|
||||
socketNotificationReceived?(notification: string, payload: any): void,
|
||||
suspend?(): void,
|
||||
resume?(): void,
|
||||
getDom?(): HTMLElement,
|
||||
getStyles?(): string[],
|
||||
[key: string]: any,
|
||||
defaults?: object;
|
||||
start?(): void;
|
||||
getHeader?(): string;
|
||||
getTemplate?(): string;
|
||||
getTemplateData?(): object;
|
||||
notificationReceived?(notification: string, payload: any, sender: object): void;
|
||||
socketNotificationReceived?(notification: string, payload: any): void;
|
||||
suspend?(): void;
|
||||
resume?(): void;
|
||||
getDom?(): HTMLElement;
|
||||
getStyles?(): string[];
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export declare const Module: {
|
||||
@@ -18,14 +18,14 @@ export declare const Module: {
|
||||
};
|
||||
|
||||
export declare const Log: {
|
||||
info(message?: any, ...optionalParams: any[]): void,
|
||||
log(message?: any, ...optionalParams: any[]): void,
|
||||
error(message?: any, ...optionalParams: any[]): void,
|
||||
warn(message?: any, ...optionalParams: any[]): void,
|
||||
group(groupTitle?: string, ...optionalParams: any[]): void,
|
||||
groupCollapsed(groupTitle?: string, ...optionalParams: any[]): void,
|
||||
groupEnd(): void,
|
||||
time(timerName?: string): void,
|
||||
timeEnd(timerName?: string): void,
|
||||
timeStamp(timerName?: string): void,
|
||||
info(message?: any, ...optionalParams: any[]): void;
|
||||
log(message?: any, ...optionalParams: any[]): void;
|
||||
error(message?: any, ...optionalParams: any[]): void;
|
||||
warn(message?: any, ...optionalParams: any[]): void;
|
||||
group(groupTitle?: string, ...optionalParams: any[]): void;
|
||||
groupCollapsed(groupTitle?: string, ...optionalParams: any[]): void;
|
||||
groupEnd(): void;
|
||||
time(timerName?: string): void;
|
||||
timeEnd(timerName?: string): void;
|
||||
timeStamp(timerName?: string): void;
|
||||
};
|
@@ -138,13 +138,14 @@ const CalendarUtils = {
|
||||
return CalendarUtils.isFullDayEvent(event) ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time]));
|
||||
};
|
||||
|
||||
Log.debug("there are " + Object.entries(data).length + " calendar entries");
|
||||
Log.debug("There are " + Object.entries(data).length + " calendar entries.");
|
||||
Object.entries(data).forEach(([key, event]) => {
|
||||
Log.debug("Processing entry...");
|
||||
const now = new Date();
|
||||
const today = moment().startOf("day").toDate();
|
||||
const future = moment().startOf("day").add(config.maximumNumberOfDays, "days").subtract(1, "seconds").toDate(); // Subtract 1 second so that events that start on the middle of the night will not repeat.
|
||||
let past = today;
|
||||
Log.debug("have entries ");
|
||||
|
||||
if (config.includePastEvents) {
|
||||
past = moment().startOf("day").subtract(config.maximumNumberOfDays, "days").toDate();
|
||||
}
|
||||
@@ -159,10 +160,10 @@ const CalendarUtils = {
|
||||
}
|
||||
|
||||
if (event.type === "VEVENT") {
|
||||
Log.debug("\nEvent: " + JSON.stringify(event));
|
||||
let startDate = eventDate(event, "start");
|
||||
let endDate;
|
||||
|
||||
Log.debug("\nevent=" + JSON.stringify(event));
|
||||
if (typeof event.end !== "undefined") {
|
||||
endDate = eventDate(event, "end");
|
||||
} else if (typeof event.duration !== "undefined") {
|
||||
@@ -176,16 +177,21 @@ const CalendarUtils = {
|
||||
}
|
||||
}
|
||||
|
||||
Log.debug(" start=" + startDate.toDate() + " end=" + endDate.toDate());
|
||||
Log.debug("startDate (local): " + startDate.toDate());
|
||||
Log.debug("endDate (local): " + endDate.toDate());
|
||||
|
||||
// calculate the duration of the event for use with recurring events.
|
||||
// Calculate the duration of the event for use with recurring events.
|
||||
let duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x"));
|
||||
Log.debug("duration: " + duration);
|
||||
|
||||
// FIXME: Since the parsed json object from node-ical comes with time information
|
||||
// this check could be removed (?)
|
||||
if (event.start.length === 8) {
|
||||
startDate = startDate.startOf("day");
|
||||
}
|
||||
|
||||
const title = CalendarUtils.getTitleFromEvent(event);
|
||||
Log.debug("title: " + title);
|
||||
|
||||
let excluded = false,
|
||||
dateFilter = null;
|
||||
@@ -260,9 +266,13 @@ const CalendarUtils = {
|
||||
let pastLocal = 0;
|
||||
let futureLocal = 0;
|
||||
if (CalendarUtils.isFullDayEvent(event)) {
|
||||
Log.debug("fullday");
|
||||
// if full day event, only use the date part of the ranges
|
||||
pastLocal = pastMoment.toDate();
|
||||
futureLocal = futureMoment.toDate();
|
||||
|
||||
Log.debug("pastLocal: " + pastLocal);
|
||||
Log.debug("futureLocal: " + futureLocal);
|
||||
} else {
|
||||
// if we want past events
|
||||
if (config.includePastEvents) {
|
||||
@@ -274,9 +284,9 @@ const CalendarUtils = {
|
||||
}
|
||||
futureLocal = futureMoment.toDate(); // future
|
||||
}
|
||||
Log.debug(" between=" + pastLocal + " to " + futureLocal);
|
||||
Log.debug("Search for recurring events between: " + pastLocal + " and " + futureLocal);
|
||||
const dates = rule.between(pastLocal, futureLocal, true, limitFunction);
|
||||
Log.debug("title=" + event.summary + " dates=" + JSON.stringify(dates));
|
||||
Log.debug("Title: " + event.summary + ", with dates: " + JSON.stringify(dates));
|
||||
// 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
|
||||
// had its date changed from outside the range to inside the range. For the time being,
|
||||
@@ -284,6 +294,7 @@ const CalendarUtils = {
|
||||
// because the logic below will filter out any recurrences that don't actually belong within
|
||||
// our display range.
|
||||
// Would be great if there was a better way to handle this.
|
||||
Log.debug("event.recurrences: " + event.recurrences);
|
||||
if (event.recurrences !== undefined) {
|
||||
for (let r in event.recurrences) {
|
||||
// Only add dates that weren't already in the range we added from the rrule so that
|
||||
@@ -296,38 +307,42 @@ const CalendarUtils = {
|
||||
// Loop through the set of date entries to see which recurrences should be added to our event list.
|
||||
for (let d in dates) {
|
||||
let date = dates[d];
|
||||
// ical.js started returning recurrences and exdates as ISOStrings without time information.
|
||||
// .toISOString().substring(0,10) is the method they use to calculate keys, so we'll do the same
|
||||
// (see https://github.com/peterbraden/ical.js/pull/84 )
|
||||
// Remove the time information of each date by using its substring, using the following method:
|
||||
// .toISOString().substring(0,10).
|
||||
// since the date is given as ISOString with YYYY-MM-DDTHH:MM:SS.SSSZ
|
||||
// (see https://momentjs.com/docs/#/displaying/as-iso-string/).
|
||||
const dateKey = date.toISOString().substring(0, 10);
|
||||
let curEvent = event;
|
||||
let showRecurrence = true;
|
||||
|
||||
// get the offset of today where we are processing
|
||||
// this will be the correction we need to apply
|
||||
// Get the offset of today where we are processing
|
||||
// This will be the correction, we need to apply.
|
||||
let nowOffset = new Date().getTimezoneOffset();
|
||||
// for full day events, the time might be off from RRULE/Luxon problem
|
||||
// get time zone offset of the rule calculated event
|
||||
// For full day events, the time might be off from RRULE/Luxon problem
|
||||
// Get time zone offset of the rule calculated event
|
||||
let dateoffset = date.getTimezoneOffset();
|
||||
// reduce the time by the offset
|
||||
|
||||
// Reduce the time by the following offset.
|
||||
Log.debug(" recurring date is " + date + " offset is " + dateoffset);
|
||||
|
||||
let dh = moment(date).format("HH");
|
||||
Log.debug(" recurring date is " + date + " offset is " + dateoffset / 60 + " Hour is " + dh);
|
||||
|
||||
if (CalendarUtils.isFullDayEvent(event)) {
|
||||
Log.debug("fullday");
|
||||
// if the offset is negative, east of GMT where the problem is
|
||||
Log.debug("Fullday");
|
||||
// If the offset is negative (east of GMT), where the problem is
|
||||
if (dateoffset < 0) {
|
||||
// if the date hour is less than the offset
|
||||
if (dh < Math.abs(dateoffset / 60)) {
|
||||
// reduce the time by the offset
|
||||
Log.debug(" recurring date is " + date + " offset is " + dateoffset);
|
||||
// apply the correction to the date/time to get it UTC relative
|
||||
// Remove the offset, independently of the comparison between the date hour and the offset,
|
||||
// since in the case that *date houre < offset*, the *new Date* command will handle this by
|
||||
// representing the day before.
|
||||
|
||||
// Reduce the time by the offset:
|
||||
// Apply the correction to the date/time to get it UTC relative
|
||||
date = new Date(date.getTime() - Math.abs(nowOffset) * 60000);
|
||||
// the duration was calculated way back at the top before we could correct the start time..
|
||||
// fix it for this event entry
|
||||
//duration = 24 * 60 * 60 * 1000;
|
||||
Log.debug("new recurring date1 is " + date);
|
||||
}
|
||||
} else {
|
||||
// if the timezones are the same, correct date if needed
|
||||
if (event.start.tz === moment.tz.guess()) {
|
||||
@@ -348,9 +363,8 @@ const CalendarUtils = {
|
||||
if (dateoffset < 0) {
|
||||
// if the date hour is less than the offset
|
||||
if (dh < Math.abs(dateoffset / 60)) {
|
||||
// reduce the time by the offset
|
||||
Log.debug(" recurring date is " + date + " offset is " + dateoffset);
|
||||
// apply the correction to the date/time to get it UTC relative
|
||||
// Reduce the time by the offset:
|
||||
// Apply the correction to the date/time to get it UTC relative
|
||||
date = new Date(date.getTime() - Math.abs(nowOffset) * 60000);
|
||||
// the duration was calculated way back at the top before we could correct the start time..
|
||||
// fix it for this event entry
|
||||
@@ -373,6 +387,7 @@ const CalendarUtils = {
|
||||
}
|
||||
}
|
||||
startDate = moment(date);
|
||||
Log.debug("Corrected startDate (local): " + startDate.toDate());
|
||||
|
||||
let adjustDays = CalendarUtils.calculateTimezoneAdjustment(event, date);
|
||||
|
||||
@@ -388,7 +403,7 @@ const CalendarUtils = {
|
||||
// This date is an exception date, which means we should skip it in the recurrence pattern.
|
||||
showRecurrence = false;
|
||||
}
|
||||
Log.debug("duration=" + duration);
|
||||
Log.debug("duration: " + duration);
|
||||
|
||||
endDate = moment(parseInt(startDate.format("x")) + duration, "x");
|
||||
if (startDate.format("x") === endDate.format("x")) {
|
||||
@@ -408,7 +423,7 @@ const CalendarUtils = {
|
||||
}
|
||||
|
||||
if (showRecurrence === true) {
|
||||
Log.debug("saving event =" + description);
|
||||
Log.debug("saving event: " + description);
|
||||
addedEvents++;
|
||||
newEvents.push({
|
||||
title: recurrenceTitle,
|
||||
@@ -424,7 +439,7 @@ const CalendarUtils = {
|
||||
});
|
||||
}
|
||||
}
|
||||
// end recurring event parsing
|
||||
// End recurring event parsing.
|
||||
} else {
|
||||
// Single event.
|
||||
const fullDayEvent = isFacebookBirthday ? true : CalendarUtils.isFullDayEvent(event);
|
||||
|
@@ -12,11 +12,14 @@ Module.register("clock", {
|
||||
displayType: "digital", // options: digital, analog, both
|
||||
|
||||
timeFormat: config.timeFormat,
|
||||
timezone: null,
|
||||
|
||||
displaySeconds: true,
|
||||
showPeriod: true,
|
||||
showPeriodUpper: false,
|
||||
clockBold: false,
|
||||
showDate: true,
|
||||
showTime: true,
|
||||
showWeek: false,
|
||||
dateFormat: "dddd, LL",
|
||||
|
||||
@@ -24,9 +27,8 @@ Module.register("clock", {
|
||||
analogSize: "200px",
|
||||
analogFace: "simple", // options: 'none', 'simple', 'face-###' (where ### is 001 to 012 inclusive)
|
||||
analogPlacement: "bottom", // options: 'top', 'bottom', 'left', 'right'
|
||||
analogShowDate: "top", // options: false, 'top', or 'bottom'
|
||||
analogShowDate: "top", // OBSOLETE, can be replaced with analogPlacement and showTime, options: false, 'top', or 'bottom'
|
||||
secondsColor: "#888888",
|
||||
timezone: null,
|
||||
|
||||
showSunTimes: false,
|
||||
showMoonTimes: false,
|
||||
@@ -89,11 +91,20 @@ Module.register("clock", {
|
||||
// Override dom generator.
|
||||
getDom: function () {
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.classList.add("clockGrid");
|
||||
|
||||
/************************************
|
||||
* Create wrappers for analog and digital clock
|
||||
*/
|
||||
const analogWrapper = document.createElement("div");
|
||||
analogWrapper.className = "clockCircle";
|
||||
const digitalWrapper = document.createElement("div");
|
||||
digitalWrapper.className = "digital";
|
||||
digitalWrapper.style.gridArea = "center";
|
||||
|
||||
/************************************
|
||||
* Create wrappers for DIGITAL clock
|
||||
*/
|
||||
|
||||
const dateWrapper = document.createElement("div");
|
||||
const timeWrapper = document.createElement("div");
|
||||
const secondsWrapper = document.createElement("sup");
|
||||
@@ -101,10 +112,11 @@ Module.register("clock", {
|
||||
const sunWrapper = document.createElement("div");
|
||||
const moonWrapper = document.createElement("div");
|
||||
const weekWrapper = document.createElement("div");
|
||||
|
||||
// Style Wrappers
|
||||
dateWrapper.className = "date normal medium";
|
||||
timeWrapper.className = "time bright large light";
|
||||
secondsWrapper.className = "dimmed";
|
||||
secondsWrapper.className = "seconds dimmed";
|
||||
sunWrapper.className = "sun dimmed small";
|
||||
moonWrapper.className = "moon dimmed small";
|
||||
weekWrapper.className = "week dimmed medium";
|
||||
@@ -124,7 +136,7 @@ Module.register("clock", {
|
||||
hourSymbol = "h";
|
||||
}
|
||||
|
||||
if (this.config.clockBold === true) {
|
||||
if (this.config.clockBold) {
|
||||
timeString = now.format(hourSymbol + '[<span class="bold">]mm[</span>]');
|
||||
} else {
|
||||
timeString = now.format(hourSymbol + ":mm");
|
||||
@@ -132,10 +144,10 @@ Module.register("clock", {
|
||||
|
||||
if (this.config.showDate) {
|
||||
dateWrapper.innerHTML = now.format(this.config.dateFormat);
|
||||
digitalWrapper.appendChild(dateWrapper);
|
||||
}
|
||||
if (this.config.showWeek) {
|
||||
weekWrapper.innerHTML = this.translate("WEEK", { weekNumber: now.week() });
|
||||
}
|
||||
|
||||
if (this.config.displayType !== "analog" && this.config.showTime) {
|
||||
timeWrapper.innerHTML = timeString;
|
||||
secondsWrapper.innerHTML = now.format("ss");
|
||||
if (this.config.showPeriodUpper) {
|
||||
@@ -149,6 +161,8 @@ Module.register("clock", {
|
||||
if (this.config.showPeriod && this.config.timeFormat !== 24) {
|
||||
timeWrapper.appendChild(periodWrapper);
|
||||
}
|
||||
digitalWrapper.appendChild(timeWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the time according to the config
|
||||
@@ -165,6 +179,9 @@ Module.register("clock", {
|
||||
return moment(time).format(formatString);
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* Create wrappers for Sun Times, only if specified in config
|
||||
*/
|
||||
if (this.config.showSunTimes) {
|
||||
const sunTimes = SunCalc.getTimes(now, this.config.lat, this.config.lon);
|
||||
const isVisible = now.isBetween(sunTimes.sunrise, sunTimes.sunset);
|
||||
@@ -191,7 +208,12 @@ Module.register("clock", {
|
||||
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i> ' +
|
||||
formatTime(this.config, sunTimes.sunset) +
|
||||
"</span>";
|
||||
digitalWrapper.appendChild(sunWrapper);
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* Create wrappers for Moon Times, only if specified in config
|
||||
*/
|
||||
if (this.config.showMoonTimes) {
|
||||
const moonIllumination = SunCalc.getMoonIllumination(now.toDate());
|
||||
const moonTimes = SunCalc.getMoonTimes(now, this.config.lat, this.config.lon);
|
||||
@@ -217,13 +239,17 @@ Module.register("clock", {
|
||||
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i> ' +
|
||||
(moonSet ? formatTime(this.config, moonSet) : "...") +
|
||||
"</span>";
|
||||
digitalWrapper.appendChild(moonWrapper);
|
||||
}
|
||||
|
||||
if (this.config.showWeek) {
|
||||
weekWrapper.innerHTML = this.translate("WEEK", { weekNumber: now.week() });
|
||||
digitalWrapper.appendChild(weekWrapper);
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* Create wrappers for ANALOG clock, only if specified in config
|
||||
*/
|
||||
const clockCircle = document.createElement("div");
|
||||
|
||||
if (this.config.displayType !== "digital") {
|
||||
// If it isn't 'digital', then an 'analog' clock was also requested
|
||||
|
||||
@@ -236,19 +262,18 @@ Module.register("clock", {
|
||||
hour = ((now.hours() % 12) / 12) * 360 + 90 + minute / 12;
|
||||
|
||||
// Create wrappers
|
||||
clockCircle.className = "clockCircle";
|
||||
clockCircle.style.width = this.config.analogSize;
|
||||
clockCircle.style.height = this.config.analogSize;
|
||||
analogWrapper.style.width = this.config.analogSize;
|
||||
analogWrapper.style.height = this.config.analogSize;
|
||||
|
||||
if (this.config.analogFace !== "" && this.config.analogFace !== "simple" && this.config.analogFace !== "none") {
|
||||
clockCircle.style.background = "url(" + this.data.path + "faces/" + this.config.analogFace + ".svg)";
|
||||
clockCircle.style.backgroundSize = "100%";
|
||||
analogWrapper.style.background = "url(" + this.data.path + "faces/" + this.config.analogFace + ".svg)";
|
||||
analogWrapper.style.backgroundSize = "100%";
|
||||
|
||||
// The following line solves issue: https://github.com/MichMich/MagicMirror/issues/611
|
||||
// clockCircle.style.border = "1px solid black";
|
||||
clockCircle.style.border = "rgba(0, 0, 0, 0.1)"; //Updated fix for Issue 611 where non-black backgrounds are used
|
||||
// analogWrapper.style.border = "1px solid black";
|
||||
analogWrapper.style.border = "rgba(0, 0, 0, 0.1)"; //Updated fix for Issue 611 where non-black backgrounds are used
|
||||
} else if (this.config.analogFace !== "none") {
|
||||
clockCircle.style.border = "2px solid white";
|
||||
analogWrapper.style.border = "2px solid white";
|
||||
}
|
||||
const clockFace = document.createElement("div");
|
||||
clockFace.className = "clockFace";
|
||||
@@ -274,83 +299,27 @@ Module.register("clock", {
|
||||
clockSecond.style.backgroundColor = this.config.secondsColor;
|
||||
clockFace.appendChild(clockSecond);
|
||||
}
|
||||
clockCircle.appendChild(clockFace);
|
||||
analogWrapper.appendChild(clockFace);
|
||||
}
|
||||
|
||||
/*******************************************
|
||||
* Combine wrappers, check for .displayType
|
||||
* Update placement, respect old analogShowDate even if its not needed anymore
|
||||
*/
|
||||
|
||||
if (this.config.displayType === "digital") {
|
||||
// Display only a digital clock
|
||||
wrapper.appendChild(dateWrapper);
|
||||
wrapper.appendChild(timeWrapper);
|
||||
wrapper.appendChild(sunWrapper);
|
||||
wrapper.appendChild(moonWrapper);
|
||||
wrapper.appendChild(weekWrapper);
|
||||
} else if (this.config.displayType === "analog") {
|
||||
if (this.config.displayType === "analog") {
|
||||
// Display only an analog clock
|
||||
|
||||
if (this.config.showWeek) {
|
||||
weekWrapper.style.paddingBottom = "15px";
|
||||
} else {
|
||||
dateWrapper.style.paddingBottom = "15px";
|
||||
}
|
||||
|
||||
if (this.config.analogShowDate === "top") {
|
||||
wrapper.appendChild(dateWrapper);
|
||||
wrapper.appendChild(weekWrapper);
|
||||
wrapper.appendChild(clockCircle);
|
||||
wrapper.classList.add("clockGrid--bottom");
|
||||
} else if (this.config.analogShowDate === "bottom") {
|
||||
wrapper.appendChild(clockCircle);
|
||||
wrapper.appendChild(dateWrapper);
|
||||
wrapper.appendChild(weekWrapper);
|
||||
wrapper.classList.add("clockGrid--top");
|
||||
} else {
|
||||
wrapper.appendChild(clockCircle);
|
||||
//analogWrapper.style.gridArea = "center";
|
||||
}
|
||||
} else if (this.config.displayType === "both") {
|
||||
wrapper.classList.add("clockGrid--" + this.config.analogPlacement);
|
||||
}
|
||||
} else {
|
||||
// Both clocks have been configured, check position
|
||||
const placement = this.config.analogPlacement;
|
||||
|
||||
const analogWrapper = document.createElement("div");
|
||||
analogWrapper.id = "analog";
|
||||
analogWrapper.style.cssFloat = "none";
|
||||
analogWrapper.appendChild(clockCircle);
|
||||
|
||||
const digitalWrapper = document.createElement("div");
|
||||
digitalWrapper.id = "digital";
|
||||
digitalWrapper.style.cssFloat = "none";
|
||||
digitalWrapper.appendChild(dateWrapper);
|
||||
digitalWrapper.appendChild(timeWrapper);
|
||||
digitalWrapper.appendChild(sunWrapper);
|
||||
digitalWrapper.appendChild(moonWrapper);
|
||||
digitalWrapper.appendChild(weekWrapper);
|
||||
|
||||
const appendClocks = (condition, pos1, pos2) => {
|
||||
const padding = [0, 0, 0, 0];
|
||||
padding[placement === condition ? pos1 : pos2] = "20px";
|
||||
analogWrapper.style.padding = padding.join(" ");
|
||||
if (placement === condition) {
|
||||
wrapper.appendChild(analogWrapper);
|
||||
wrapper.appendChild(digitalWrapper);
|
||||
} else {
|
||||
wrapper.appendChild(digitalWrapper);
|
||||
wrapper.appendChild(analogWrapper);
|
||||
}
|
||||
};
|
||||
|
||||
if (placement === "left" || placement === "right") {
|
||||
digitalWrapper.style.display = "inline-block";
|
||||
digitalWrapper.style.verticalAlign = "top";
|
||||
analogWrapper.style.display = "inline-block";
|
||||
|
||||
appendClocks("left", 1, 3);
|
||||
} else {
|
||||
digitalWrapper.style.textAlign = "center";
|
||||
|
||||
appendClocks("top", 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the wrapper to the dom.
|
||||
return wrapper;
|
||||
|
@@ -1,5 +1,26 @@
|
||||
.clockGrid {
|
||||
display: inline-flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.clockGrid--left {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.clockGrid--right {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.clockGrid--top {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.clockGrid--bottom {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.clockCircle {
|
||||
margin: 0 auto;
|
||||
place-self: center;
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
background-size: 100%;
|
||||
|
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: CurrentWeather
|
||||
*
|
||||
|
177
modules/default/updatenotification/git_helper.js
Normal file
177
modules/default/updatenotification/git_helper.js
Normal file
@@ -0,0 +1,177 @@
|
||||
const util = require("util");
|
||||
const exec = util.promisify(require("child_process").exec);
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const Log = require("logger");
|
||||
|
||||
class gitHelper {
|
||||
constructor() {
|
||||
this.gitRepos = [];
|
||||
this.baseDir = path.normalize(__dirname + "/../../../");
|
||||
}
|
||||
|
||||
getRefRegex(branch) {
|
||||
return new RegExp("s*([a-z,0-9]+[.][.][a-z,0-9]+) " + branch, "g");
|
||||
}
|
||||
|
||||
async execShell(command) {
|
||||
let res = { stdout: "", stderr: "" };
|
||||
const { stdout, stderr } = await exec(command);
|
||||
|
||||
res.stdout = stdout;
|
||||
res.stderr = stderr;
|
||||
return res;
|
||||
}
|
||||
|
||||
async isGitRepo(moduleFolder) {
|
||||
let res = await this.execShell("cd " + moduleFolder + " && git remote -v");
|
||||
if (res.stderr) {
|
||||
Log.error("Failed to fetch git data for " + moduleFolder + ": " + res.stderr);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
add(moduleName) {
|
||||
let moduleFolder = this.baseDir;
|
||||
if (moduleName !== "default") {
|
||||
moduleFolder = moduleFolder + "modules/" + moduleName;
|
||||
}
|
||||
try {
|
||||
Log.info("Checking git for module: " + moduleName);
|
||||
// Throws error if file doesn't exist
|
||||
fs.statSync(path.join(moduleFolder, ".git"));
|
||||
// Fetch the git or throw error if no remotes
|
||||
if (this.isGitRepo(moduleFolder)) {
|
||||
// Folder has .git and has at least one git remote, watch this folder
|
||||
this.gitRepos.unshift({ module: moduleName, folder: moduleFolder });
|
||||
}
|
||||
} catch (err) {
|
||||
// Error when directory .git doesn't exist or doesn't have any remotes
|
||||
// This module is not managed with git, skip
|
||||
}
|
||||
}
|
||||
|
||||
async getStatusInfo(repo) {
|
||||
let gitInfo = {
|
||||
module: repo.module,
|
||||
// commits behind:
|
||||
behind: 0,
|
||||
// branch name:
|
||||
current: "",
|
||||
// current hash:
|
||||
hash: "",
|
||||
// remote branch:
|
||||
tracking: "",
|
||||
isBehindInStatus: false
|
||||
};
|
||||
let res;
|
||||
if (repo.module === "default") {
|
||||
// the hash is only needed for the mm repo
|
||||
res = await this.execShell("cd " + repo.folder + " && git rev-parse HEAD");
|
||||
if (res.stderr) {
|
||||
Log.error("Failed to get current commit hash for " + repo.module + ": " + res.stderr);
|
||||
}
|
||||
gitInfo.hash = res.stdout;
|
||||
}
|
||||
if (repo.res) {
|
||||
// mocking
|
||||
res = repo.res;
|
||||
} else {
|
||||
res = await this.execShell("cd " + repo.folder + " && git status -sb");
|
||||
}
|
||||
if (res.stderr) {
|
||||
Log.error("Failed to get git status for " + repo.module + ": " + res.stderr);
|
||||
// exit without git status info
|
||||
return;
|
||||
}
|
||||
// only the first line of stdout is evaluated
|
||||
let status = res.stdout.split("\n")[0];
|
||||
// examples for status:
|
||||
// ## develop...origin/develop
|
||||
// ## master...origin/master [behind 8]
|
||||
status = status.match(/(?![.#])([^.]*)/g);
|
||||
// examples for status:
|
||||
// [ ' develop', 'origin/develop', '' ]
|
||||
// [ ' master', 'origin/master [behind 8]', '' ]
|
||||
gitInfo.current = status[0].trim();
|
||||
status = status[1].split(" ");
|
||||
// examples for status:
|
||||
// [ 'origin/develop' ]
|
||||
// [ 'origin/master', '[behind', '8]' ]
|
||||
gitInfo.tracking = status[0].trim();
|
||||
if (status[2]) {
|
||||
// git fetch was already called before so `git status -sb` delivers already the behind number
|
||||
gitInfo.behind = parseInt(status[2].substring(0, status[2].length - 1));
|
||||
gitInfo.isBehindInStatus = true;
|
||||
}
|
||||
return gitInfo;
|
||||
}
|
||||
|
||||
async getRepoInfo(repo) {
|
||||
let gitInfo;
|
||||
if (repo.gitInfo) {
|
||||
// mocking
|
||||
gitInfo = repo.gitInfo;
|
||||
} else {
|
||||
gitInfo = await this.getStatusInfo(repo);
|
||||
}
|
||||
if (!gitInfo) {
|
||||
return;
|
||||
}
|
||||
if (gitInfo.isBehindInStatus) {
|
||||
return gitInfo;
|
||||
}
|
||||
let res;
|
||||
if (repo.res) {
|
||||
// mocking
|
||||
res = repo.res;
|
||||
} else {
|
||||
res = await this.execShell("cd " + repo.folder + " && git fetch --dry-run");
|
||||
}
|
||||
// example output:
|
||||
// From https://github.com/MichMich/MagicMirror
|
||||
// e40ddd4..06389e3 develop -> origin/develop
|
||||
// here the result is in stderr (this is a git default, don't ask why ...)
|
||||
const matches = res.stderr.match(this.getRefRegex(gitInfo.current));
|
||||
if (!matches || !matches[0]) {
|
||||
// no refs found, nothing to do
|
||||
return;
|
||||
}
|
||||
// get behind with refs
|
||||
try {
|
||||
res = await this.execShell("cd " + repo.folder + " && git rev-list --ancestry-path --count " + matches[0]);
|
||||
gitInfo.behind = parseInt(res.stdout);
|
||||
return gitInfo;
|
||||
} catch (err) {
|
||||
Log.error("Failed to get git revisions for " + repo.module + ": " + err);
|
||||
}
|
||||
}
|
||||
|
||||
async getStatus() {
|
||||
const gitResultList = [];
|
||||
for (let repo of this.gitRepos) {
|
||||
const gitInfo = await this.getStatusInfo(repo);
|
||||
if (gitInfo) {
|
||||
gitResultList.unshift(gitInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return gitResultList;
|
||||
}
|
||||
|
||||
async getRepos() {
|
||||
const gitResultList = [];
|
||||
for (let repo of this.gitRepos) {
|
||||
const gitInfo = await this.getRepoInfo(repo);
|
||||
if (gitInfo) {
|
||||
gitResultList.unshift(gitInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return gitResultList;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.gitHelper = gitHelper;
|
@@ -1,9 +1,5 @@
|
||||
const SimpleGit = require("simple-git");
|
||||
const simpleGits = [];
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const GitHelper = require(__dirname + "/git_helper.js");
|
||||
const defaultModules = require(__dirname + "/../defaultmodules.js");
|
||||
const Log = require("logger");
|
||||
const NodeHelper = require("node_helper");
|
||||
|
||||
module.exports = NodeHelper.create({
|
||||
@@ -12,32 +8,19 @@ module.exports = NodeHelper.create({
|
||||
updateTimer: null,
|
||||
updateProcessStarted: false,
|
||||
|
||||
gitHelper: new GitHelper.gitHelper(),
|
||||
|
||||
start: function () {},
|
||||
|
||||
configureModules: async function (modules) {
|
||||
// Push MagicMirror itself , biggest chance it'll show up last in UI and isn't overwritten
|
||||
// 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: this.createGit(path.normalize(__dirname + "/../../../")) });
|
||||
this.gitHelper.add("default");
|
||||
|
||||
for (let moduleName in modules) {
|
||||
if (!this.ignoreUpdateChecking(moduleName)) {
|
||||
// Default modules are included in the main MagicMirror repo
|
||||
let moduleFolder = path.normalize(__dirname + "/../../" + moduleName);
|
||||
|
||||
try {
|
||||
Log.info("Checking git for module: " + moduleName);
|
||||
// Throws error if file doesn't exist
|
||||
fs.statSync(path.join(moduleFolder, ".git"));
|
||||
// Fetch the git or throw error if no remotes
|
||||
let git = await this.resolveRemote(moduleFolder);
|
||||
// Folder has .git and has at least one git remote, watch this folder
|
||||
simpleGits.unshift({ module: moduleName, git: git });
|
||||
} catch (err) {
|
||||
// Error when directory .git doesn't exist or doesn't have any remotes
|
||||
// This module is not managed with git, skip
|
||||
continue;
|
||||
}
|
||||
this.gitHelper.add(moduleName);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -54,35 +37,9 @@ module.exports = NodeHelper.create({
|
||||
}
|
||||
},
|
||||
|
||||
resolveRemote: async function (moduleFolder) {
|
||||
let git = this.createGit(moduleFolder);
|
||||
let remotes = await git.getRemotes(true);
|
||||
|
||||
if (remotes.length < 1 || remotes[0].name.length < 1) {
|
||||
throw new Error("No valid remote for folder " + moduleFolder);
|
||||
}
|
||||
|
||||
return git;
|
||||
},
|
||||
|
||||
performFetch: async function () {
|
||||
for (let sg of simpleGits) {
|
||||
try {
|
||||
let fetchData = await sg.git.fetch(["--dry-run"]).status();
|
||||
let logData = await sg.git.log({ "-1": null });
|
||||
|
||||
if (logData.latest && "hash" in logData.latest) {
|
||||
this.sendSocketNotification("STATUS", {
|
||||
module: sg.module,
|
||||
behind: fetchData.behind,
|
||||
current: fetchData.current,
|
||||
hash: logData.latest.hash,
|
||||
tracking: fetchData.tracking
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
Log.error("Failed to fetch git data for " + sg.module + ": " + err);
|
||||
}
|
||||
for (let gitInfo of await this.gitHelper.getRepos()) {
|
||||
this.sendSocketNotification("STATUS", gitInfo);
|
||||
}
|
||||
|
||||
this.scheduleNextFetch(this.config.updateInterval);
|
||||
@@ -100,10 +57,6 @@ module.exports = NodeHelper.create({
|
||||
}, delay);
|
||||
},
|
||||
|
||||
createGit: function (folder) {
|
||||
return SimpleGit({ baseDir: folder, timeout: { block: this.config.timeout } });
|
||||
},
|
||||
|
||||
ignoreUpdateChecking: function (moduleName) {
|
||||
// Should not check for updates for default modules
|
||||
if (defaultModules.indexOf(moduleName) >= 0) {
|
||||
|
@@ -2,6 +2,9 @@
|
||||
{% set numSteps = forecast | calcNumSteps %}
|
||||
{% set currentStep = 0 %}
|
||||
<table class="{{ config.tableClass }}">
|
||||
{% if config.ignoreToday %}
|
||||
{% set forecast = forecast.splice(1) %}
|
||||
{% endif %}
|
||||
{% set forecast = forecast.slice(0, numSteps) %}
|
||||
{% for f in forecast %}
|
||||
<tr {% if config.colored %}class="colored"{% endif %} {% if config.fade %}style="opacity: {{ currentStep | opacity(numSteps) }};"{% endif %}>
|
||||
|
@@ -1,148 +1,3 @@
|
||||
# MagicMirror² Weather Module Weather Provider Development Documentation
|
||||
# Weather Module Weather Provider Development Documentation
|
||||
|
||||
This document describes the way to develop your own MagicMirror² weather module weather provider.
|
||||
|
||||
Table of Contents:
|
||||
|
||||
- The weather provider file: yourprovider.js
|
||||
- [Weather provider methods to implement](#weather-provider-methods-to-implement)
|
||||
- [Weather Provider instance methods](#weather-provider-instance-methods)
|
||||
- [WeatherObject](#weatherobject)
|
||||
|
||||
---
|
||||
|
||||
## The weather provider file: yourprovider.js
|
||||
|
||||
This is the script in which the weather provider will be defined. In its most simple form, the weather provider must implement the following:
|
||||
|
||||
```javascript
|
||||
WeatherProvider.register("yourprovider", {
|
||||
providerName: "YourProvider",
|
||||
|
||||
fetchCurrentWeather() {},
|
||||
|
||||
fetchWeatherForecast() {}
|
||||
});
|
||||
```
|
||||
|
||||
### Weather provider methods to implement
|
||||
|
||||
#### `fetchCurrentWeather()`
|
||||
|
||||
This method is called when the weather module tries to fetch the current weather of your provider. The implementation of this method is required for current weather support.
|
||||
The implementation can make use of the already implemented function `this.fetchData(url, method, data);`, which is returning a promise.
|
||||
After the response is processed, the current weather information (as a [WeatherObject](#weatherobject)) needs to be set with `this.setCurrentWeather(currentWeather);`.
|
||||
It will then automatically refresh the module DOM with the new data.
|
||||
|
||||
#### `fetchWeatherForecast()`
|
||||
|
||||
This method is called when the weather module tries to fetch the weather of your provider. The implementation of this method is required for forecast support.
|
||||
The implementation can make use of the already implemented function `this.fetchData(url, method, data);`, which is returning a promise.
|
||||
After the response is processed, the weather forecast information (as an array of [WeatherObject](#weatherobject)s) needs to be set with `this.setWeatherForecast(forecast);`.
|
||||
It will then automatically refresh the module DOM with the new data.
|
||||
|
||||
#### `fetchWeatherHourly()`
|
||||
|
||||
This method is called when the weather module tries to fetch the weather of your provider. The implementation of this method is required for hourly support.
|
||||
The implementation can make use of the already implemented function `this.fetchData(url, method, data);`, which is returning a promise.
|
||||
After the response is processed, the hourly weather forecast information (as an array of [WeatherObject](#weatherobject)s) needs to be set with `this.setWeatherHourly(forecast);`.
|
||||
It will then automatically refresh the module DOM with the new data.
|
||||
|
||||
### Weather Provider instance methods
|
||||
|
||||
#### `init()`
|
||||
|
||||
Called when a weather provider is initialized.
|
||||
|
||||
#### `setConfig(config)`
|
||||
|
||||
Called to set the config, this config is the same as the weather module's config.
|
||||
|
||||
#### `start()`
|
||||
|
||||
Called when the weather provider is about to start.
|
||||
|
||||
#### `currentWeather()`
|
||||
|
||||
This returns a WeatherDay object for the current weather.
|
||||
|
||||
#### `weatherForecast()`
|
||||
|
||||
This returns an array of WeatherDay objects for the weather forecast.
|
||||
|
||||
#### `weatherHourly()`
|
||||
|
||||
This returns an array of WeatherDay objects for the hourly weather forecast.
|
||||
|
||||
#### `fetchedLocation()`
|
||||
|
||||
This returns the name of the fetched location or an empty string.
|
||||
|
||||
#### `setCurrentWeather(currentWeatherObject)`
|
||||
|
||||
Set the currentWeather and notify the delegate that new information is available.
|
||||
|
||||
#### `setWeatherForecast(weatherForecastArray)`
|
||||
|
||||
Set the weatherForecastArray and notify the delegate that new information is available.
|
||||
|
||||
#### `setWeatherHourly(weatherHourlyArray)`
|
||||
|
||||
Set the weatherHourlyArray and notify the delegate that new information is available.
|
||||
|
||||
#### `setFetchedLocation(name)`
|
||||
|
||||
Set the fetched location name.
|
||||
|
||||
#### `updateAvailable()`
|
||||
|
||||
Notify the delegate that new weather is available.
|
||||
|
||||
#### `fetchData(url, method, data)`
|
||||
|
||||
A convenience function to make requests. It returns a promise.
|
||||
|
||||
### WeatherObject
|
||||
|
||||
| Property | Type | Value/Unit |
|
||||
| -------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| units | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| tempUnits | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| windUnits | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| date | `object` | [Moment.js](https://momentjs.com/) object of the time/date. |
|
||||
| windSpeed | `number` | Metric: `meter/second` <br> Imperial: `miles/hour` |
|
||||
| windDirection | `number` | Direction of the wind in degrees. |
|
||||
| sunrise | `object` | [Moment.js](https://momentjs.com/) object of sunrise. |
|
||||
| sunset | `object` | [Moment.js](https://momentjs.com/) object of sunset. |
|
||||
| temperature | `number` | Current temperature |
|
||||
| minTemperature | `number` | Lowest temperature of the day. |
|
||||
| maxTemperature | `number` | Highest temperature of the day. |
|
||||
| weatherType | `string` | Icon name of the weather type. <br> Possible values: [WeatherIcons](https://www.npmjs.com/package/weathericons) |
|
||||
| humidity | `number` | Percentage of humidity |
|
||||
| rain | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
||||
| snow | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
||||
| precipitation | `number` | Metric: `millimeters` <br> Imperial: `inches` <br> UK Met Office provider: `percent` |
|
||||
|
||||
#### Current weather
|
||||
|
||||
For the current weather object the following properties are required:
|
||||
|
||||
- humidity
|
||||
- sunrise
|
||||
- sunset
|
||||
- temperature
|
||||
- units
|
||||
- weatherType
|
||||
- windDirection
|
||||
- windSpeed
|
||||
|
||||
#### Weather forecast
|
||||
|
||||
For the forecast weather object the following properties are required:
|
||||
|
||||
- date
|
||||
- maxTemperature
|
||||
- minTemperature
|
||||
- rain
|
||||
- units
|
||||
- weatherType
|
||||
For how to develop your own weather provider, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/development/weather-provider.html).
|
||||
|
@@ -8,7 +8,8 @@
|
||||
* MIT Licensed
|
||||
*
|
||||
* This class is a provider for Dark Sky.
|
||||
* Note that the Dark Sky API does not provide rainfall. Instead it provides snowfall and precipitation probability
|
||||
* Note that the Dark Sky API does not provide rainfall. Instead it provides
|
||||
* snowfall and precipitation probability
|
||||
*/
|
||||
WeatherProvider.register("darksky", {
|
||||
// Set the name of the provider.
|
||||
@@ -98,7 +99,8 @@ WeatherProvider.register("darksky", {
|
||||
weather.snow = 0;
|
||||
|
||||
// The API will return centimeters if units is 'si' and will return inches for 'us'
|
||||
// Note that the Dark Sky API does not provide rainfall. Instead it provides snowfall and precipitation probability
|
||||
// Note that the Dark Sky API does not provide rainfall.
|
||||
// Instead it provides snowfall and precipitation probability
|
||||
if (forecast.hasOwnProperty("precipAccumulation")) {
|
||||
if (this.config.units === "imperial" && !isNaN(forecast.precipAccumulation)) {
|
||||
weather.snow = forecast.precipAccumulation;
|
||||
|
@@ -134,7 +134,7 @@ WeatherProvider.register("envcanada", {
|
||||
//
|
||||
fetchData: function (url, method = "GET", data = null) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var request = new XMLHttpRequest();
|
||||
const request = new XMLHttpRequest();
|
||||
request.open(method, url, true);
|
||||
request.onreadystatechange = function () {
|
||||
if (this.readyState === 4) {
|
||||
@@ -164,9 +164,7 @@ WeatherProvider.register("envcanada", {
|
||||
// CORS errors when accessing EC
|
||||
//
|
||||
getUrl() {
|
||||
var path = "https://thingproxy.freeboard.io/fetch/https://dd.weather.gc.ca/citypage_weather/xml/" + this.config.provCode + "/" + this.config.siteCode + "_e.xml";
|
||||
|
||||
return path;
|
||||
return "https://thingproxy.freeboard.io/fetch/https://dd.weather.gc.ca/citypage_weather/xml/" + this.config.provCode + "/" + this.config.siteCode + "_e.xml";
|
||||
},
|
||||
|
||||
//
|
||||
@@ -232,7 +230,7 @@ WeatherProvider.register("envcanada", {
|
||||
// Capture the sunrise and sunset values from EC data
|
||||
//
|
||||
|
||||
var sunList = ECdoc.querySelectorAll("siteData riseSet dateTime");
|
||||
const sunList = ECdoc.querySelectorAll("siteData riseSet dateTime");
|
||||
|
||||
currentWeather.sunrise = moment(sunList[1].querySelector("timeStamp").textContent, "YYYYMMDDhhmmss");
|
||||
currentWeather.sunset = moment(sunList[3].querySelector("timeStamp").textContent, "YYYYMMDDhhmmss");
|
||||
@@ -249,14 +247,14 @@ WeatherProvider.register("envcanada", {
|
||||
|
||||
const days = [];
|
||||
|
||||
var weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||
const weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||
|
||||
var foreBaseDates = ECdoc.querySelectorAll("siteData forecastGroup dateTime");
|
||||
var baseDate = foreBaseDates[1].querySelector("timeStamp").textContent;
|
||||
const foreBaseDates = ECdoc.querySelectorAll("siteData forecastGroup dateTime");
|
||||
const baseDate = foreBaseDates[1].querySelector("timeStamp").textContent;
|
||||
|
||||
weather.date = moment(baseDate, "YYYYMMDDhhmmss");
|
||||
|
||||
var foreGroup = ECdoc.querySelectorAll("siteData forecastGroup forecast");
|
||||
const foreGroup = ECdoc.querySelectorAll("siteData forecastGroup forecast");
|
||||
|
||||
// For simplicity, we will only accumulate precipitation and will not try to break out
|
||||
// rain vs snow accumulations
|
||||
@@ -288,9 +286,9 @@ WeatherProvider.register("envcanada", {
|
||||
// where the next day's (aka Tomorrow's) forecast is located in the forecast array.
|
||||
//
|
||||
|
||||
var nextDay = 0;
|
||||
var lastDay = 0;
|
||||
var currentTemp = ECdoc.querySelector("siteData currentConditions temperature").textContent;
|
||||
let nextDay = 0;
|
||||
let lastDay = 0;
|
||||
const currentTemp = ECdoc.querySelector("siteData currentConditions temperature").textContent;
|
||||
|
||||
//
|
||||
// If the first Element is Current Today, look at Current Today and Current Tonight for the current day.
|
||||
@@ -356,10 +354,10 @@ WeatherProvider.register("envcanada", {
|
||||
// iteration looking at the current Element and the next Element.
|
||||
//
|
||||
|
||||
var lastDate = moment(baseDate, "YYYYMMDDhhmmss");
|
||||
let lastDate = moment(baseDate, "YYYYMMDDhhmmss");
|
||||
|
||||
for (var stepDay = nextDay; stepDay < lastDay; stepDay += 2) {
|
||||
var weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||
for (let stepDay = nextDay; stepDay < lastDay; stepDay += 2) {
|
||||
let weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||
|
||||
// Add 1 to the date to reflect the current forecast day we are building
|
||||
|
||||
@@ -402,23 +400,23 @@ WeatherProvider.register("envcanada", {
|
||||
|
||||
// Get local timezone UTC offset so that each hourly time can be calculated properly
|
||||
|
||||
var baseHours = ECdoc.querySelectorAll("siteData hourlyForecastGroup dateTime");
|
||||
var hourOffset = baseHours[1].getAttribute("UTCOffset");
|
||||
const baseHours = ECdoc.querySelectorAll("siteData hourlyForecastGroup dateTime");
|
||||
const hourOffset = baseHours[1].getAttribute("UTCOffset");
|
||||
|
||||
//
|
||||
// The EC hourly forecast is held in a 24-element array - Elements 0 to 23 - with Element 0 holding
|
||||
// the forecast for the next 'on the hour' timeslot. This means the array is a rolling 24 hours.
|
||||
//
|
||||
|
||||
var hourGroup = ECdoc.querySelectorAll("siteData hourlyForecastGroup hourlyForecast");
|
||||
const hourGroup = ECdoc.querySelectorAll("siteData hourlyForecastGroup hourlyForecast");
|
||||
|
||||
for (var stepHour = 0; stepHour < 24; stepHour += 1) {
|
||||
var weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||
for (let stepHour = 0; stepHour < 24; stepHour += 1) {
|
||||
const weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||
|
||||
// Determine local time by applying UTC offset to the forecast timestamp
|
||||
|
||||
var foreTime = moment(hourGroup[stepHour].getAttribute("dateTimeUTC"), "YYYYMMDDhhmmss");
|
||||
var currTime = foreTime.add(hourOffset, "hours");
|
||||
const foreTime = moment(hourGroup[stepHour].getAttribute("dateTimeUTC"), "YYYYMMDDhhmmss");
|
||||
const currTime = foreTime.add(hourOffset, "hours");
|
||||
weather.date = moment(currTime, "X");
|
||||
|
||||
// Capture the temperature
|
||||
@@ -427,7 +425,7 @@ WeatherProvider.register("envcanada", {
|
||||
|
||||
// Capture Likelihood of Precipitation (LOP) and unit-of-measure values
|
||||
|
||||
var precipLOP = hourGroup[stepHour].querySelector("lop").textContent * 1.0;
|
||||
const precipLOP = hourGroup[stepHour].querySelector("lop").textContent * 1.0;
|
||||
|
||||
if (precipLOP > 0) {
|
||||
weather.precipitation = precipLOP;
|
||||
@@ -453,9 +451,9 @@ WeatherProvider.register("envcanada", {
|
||||
//
|
||||
|
||||
setMinMaxTemps(weather, foreGroup, today, fullDay, currentTemp) {
|
||||
var todayTemp = foreGroup[today].querySelector("temperatures temperature").textContent;
|
||||
const todayTemp = foreGroup[today].querySelector("temperatures temperature").textContent;
|
||||
|
||||
var todayClass = foreGroup[today].querySelector("temperatures temperature").getAttribute("class");
|
||||
const todayClass = foreGroup[today].querySelector("temperatures temperature").getAttribute("class");
|
||||
|
||||
//
|
||||
// The following logic is largely aimed at accommodating the Current day's forecast whereby we
|
||||
@@ -500,9 +498,9 @@ WeatherProvider.register("envcanada", {
|
||||
}
|
||||
}
|
||||
|
||||
var nextTemp = foreGroup[today + 1].querySelector("temperatures temperature").textContent;
|
||||
const nextTemp = foreGroup[today + 1].querySelector("temperatures temperature").textContent;
|
||||
|
||||
var nextClass = foreGroup[today + 1].querySelector("temperatures temperature").getAttribute("class");
|
||||
const nextClass = foreGroup[today + 1].querySelector("temperatures temperature").getAttribute("class");
|
||||
|
||||
if (fullDay === true) {
|
||||
if (nextClass === "low") {
|
||||
@@ -513,8 +511,6 @@ WeatherProvider.register("envcanada", {
|
||||
weather.maxTemperature = this.convertTemp(nextTemp);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
//
|
||||
@@ -560,8 +556,6 @@ WeatherProvider.register("envcanada", {
|
||||
weather.precipitation = foreGroup[today].querySelector("abbreviatedForecast pop").textContent;
|
||||
weather.precipitationUnits = foreGroup[today].querySelector("abbreviatedForecast pop").getAttribute("units");
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
//
|
||||
@@ -577,6 +571,7 @@ WeatherProvider.register("envcanada", {
|
||||
return temp;
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// Convert km/h to mph
|
||||
//
|
||||
|
@@ -18,10 +18,10 @@ WeatherProvider.register("openweathermap", {
|
||||
defaults: {
|
||||
apiVersion: "2.5",
|
||||
apiBase: "https://api.openweathermap.org/data/",
|
||||
weatherEndpoint: "",
|
||||
weatherEndpoint: "", // can be "onecall", "forecast" or "weather" (for current)
|
||||
locationID: false,
|
||||
location: false,
|
||||
lat: 0,
|
||||
lat: 0, // the onecall endpoint needs lat / lon values, it doesn'T support the locationId
|
||||
lon: 0,
|
||||
apiKey: ""
|
||||
},
|
||||
@@ -89,7 +89,7 @@ WeatherProvider.register("openweathermap", {
|
||||
/**
|
||||
* Overrides method for setting config to check if endpoint is correct for hourly
|
||||
*
|
||||
* @param config
|
||||
* @param {object} config The configuration object
|
||||
*/
|
||||
setConfig(config) {
|
||||
this.config = config;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/* global WeatherProvider, WeatherObject, SunCalc */
|
||||
/* global WeatherProvider, WeatherObject */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Weather
|
||||
@@ -7,9 +7,8 @@
|
||||
* By BuXXi https://github.com/buxxi
|
||||
* MIT Licensed
|
||||
*
|
||||
* This class is a provider for SMHI (Sweden only).
|
||||
* Note that SMHI doesn't provide sunrise and sundown, use SunCalc to calculate it.
|
||||
* Metric system is the only supported unit.
|
||||
* This class is a provider for SMHI (Sweden only). Metric system is the only
|
||||
* supported unit.
|
||||
*/
|
||||
WeatherProvider.register("smhi", {
|
||||
providerName: "SMHI",
|
||||
@@ -56,11 +55,11 @@ WeatherProvider.register("smhi", {
|
||||
/**
|
||||
* Overrides method for setting config with checks for the precipitationValue being unset or invalid
|
||||
*
|
||||
* @param config
|
||||
* @param {object} config The configuration object
|
||||
*/
|
||||
setConfig(config) {
|
||||
this.config = config;
|
||||
if (!config.precipitationValue || ["pmin", "pmean", "pmedian", "pmax"].indexOf(config.precipitationValue) == -1) {
|
||||
if (!config.precipitationValue || ["pmin", "pmean", "pmedian", "pmax"].indexOf(config.precipitationValue) === -1) {
|
||||
console.log("invalid or not set: " + config.precipitationValue);
|
||||
config.precipitationValue = this.defaults.precipitationValue;
|
||||
}
|
||||
@@ -69,7 +68,8 @@ WeatherProvider.register("smhi", {
|
||||
/**
|
||||
* Of all the times returned find out which one is closest to the current time, should be the first if the data isn't old.
|
||||
*
|
||||
* @param times
|
||||
* @param {object[]} times Array of time objects
|
||||
* @returns {object} The weatherdata closest to the current time
|
||||
*/
|
||||
getClosestToCurrentTime(times) {
|
||||
let now = moment();
|
||||
@@ -85,6 +85,8 @@ WeatherProvider.register("smhi", {
|
||||
|
||||
/**
|
||||
* Get the forecast url for the configured coordinates
|
||||
*
|
||||
* @returns {string} the url for the specified coordinates
|
||||
*/
|
||||
getURL() {
|
||||
let lon = this.config.lon;
|
||||
@@ -97,25 +99,25 @@ WeatherProvider.register("smhi", {
|
||||
* The returned units is always in metric system.
|
||||
* Requires coordinates to determine if its daytime or nighttime to know which icon to use and also to set sunrise and sunset.
|
||||
*
|
||||
* @param weatherData
|
||||
* @param coordinates
|
||||
* @param weatherData
|
||||
* @param coordinates
|
||||
* @param {object} weatherData Weatherdata to convert
|
||||
* @param {object} coordinates Coordinates of the locations of the weather
|
||||
* @returns {WeatherObject} The converted weatherdata at the specified location
|
||||
*/
|
||||
convertWeatherDataToObject(weatherData, coordinates) {
|
||||
let currentWeather = new WeatherObject("metric", "metric", "metric"); //Weather data is only for Sweden and nobody in Sweden would use imperial
|
||||
// Weather data is only for Sweden and nobody in Sweden would use imperial
|
||||
let currentWeather = new WeatherObject("metric", "metric", "metric");
|
||||
|
||||
currentWeather.date = moment(weatherData.validTime);
|
||||
let times = SunCalc.getTimes(currentWeather.date.toDate(), coordinates.lat, coordinates.lon);
|
||||
currentWeather.sunrise = moment(times.sunrise, "X");
|
||||
currentWeather.sunset = moment(times.sunset, "X");
|
||||
currentWeather.updateSunTime(coordinates.lat, coordinates.lon);
|
||||
currentWeather.humidity = this.paramValue(weatherData, "r");
|
||||
currentWeather.temperature = this.paramValue(weatherData, "t");
|
||||
currentWeather.windSpeed = this.paramValue(weatherData, "ws");
|
||||
currentWeather.windDirection = this.paramValue(weatherData, "wd");
|
||||
currentWeather.weatherType = this.convertWeatherType(this.paramValue(weatherData, "Wsymb2"), this.isDayTime(currentWeather));
|
||||
currentWeather.weatherType = this.convertWeatherType(this.paramValue(weatherData, "Wsymb2"), currentWeather.isDayTime());
|
||||
|
||||
//Determine the precipitation amount and category and update the weatherObject with it, the valuetype to use can be configured or uses median as default.
|
||||
// Determine the precipitation amount and category and update the
|
||||
// weatherObject with it, the valuetype to use can be configured or uses
|
||||
// median as default.
|
||||
let precipitationValue = this.paramValue(weatherData, this.config.precipitationValue);
|
||||
switch (this.paramValue(weatherData, "pcat")) {
|
||||
// 0 = No precipitation
|
||||
@@ -143,10 +145,9 @@ WeatherProvider.register("smhi", {
|
||||
/**
|
||||
* Takes all of the data points and converts it to one WeatherObject per day.
|
||||
*
|
||||
* @param allWeatherData
|
||||
* @param coordinates
|
||||
* @param allWeatherData
|
||||
* @param coordinates
|
||||
* @param {object[]} allWeatherData Array of weatherdata
|
||||
* @param {object} coordinates Coordinates of the locations of the weather
|
||||
* @returns {WeatherObject[]} Array of weatherobjects
|
||||
*/
|
||||
convertWeatherDataGroupedByDay(allWeatherData, coordinates) {
|
||||
let currentWeather;
|
||||
@@ -170,7 +171,7 @@ WeatherProvider.register("smhi", {
|
||||
}
|
||||
|
||||
//Keep track of what icons has been used for each hour of daytime and use the middle one for the forecast
|
||||
if (this.isDayTime(weatherObject)) {
|
||||
if (weatherObject.isDayTime()) {
|
||||
dayWeatherTypes.push(weatherObject.weatherType);
|
||||
}
|
||||
if (dayWeatherTypes.length > 0) {
|
||||
@@ -191,37 +192,31 @@ WeatherProvider.register("smhi", {
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolve coordinates from the response data (probably preferably to use this if it's not matching the config values exactly)
|
||||
* Resolve coordinates from the response data (probably preferably to use
|
||||
* this if it's not matching the config values exactly)
|
||||
*
|
||||
* @param data
|
||||
* @param {object} data Response data from the weather service
|
||||
* @returns {{lon, lat}} the lat/long coordinates of the data
|
||||
*/
|
||||
resolveCoordinates(data) {
|
||||
return { lat: data.geometry.coordinates[0][1], lon: data.geometry.coordinates[0][0] };
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the weatherObject is at dayTime.
|
||||
*
|
||||
* @param weatherObject
|
||||
*/
|
||||
isDayTime(weatherObject) {
|
||||
return weatherObject.date.isBetween(weatherObject.sunrise, weatherObject.sunset, undefined, "[]");
|
||||
},
|
||||
|
||||
/**
|
||||
* The distance between the data points is increasing in the data the more distant the prediction is.
|
||||
* Find these gaps and fill them with the previous hours data to make the data returned a complete set.
|
||||
*
|
||||
* @param data
|
||||
* @param {object[]} data Response data from the weather service
|
||||
* @returns {object[]} Given data with filled gaps
|
||||
*/
|
||||
fillInGaps(data) {
|
||||
let result = [];
|
||||
for (const i = 1; i < data.length; i++) {
|
||||
for (let i = 1; i < data.length; i++) {
|
||||
let to = moment(data[i].validTime);
|
||||
let from = moment(data[i - 1].validTime);
|
||||
let hours = moment.duration(to.diff(from)).asHours();
|
||||
// For each hour add a datapoint but change the validTime
|
||||
for (const j = 0; j < hours; j++) {
|
||||
for (let j = 0; j < hours; j++) {
|
||||
let current = Object.assign({}, data[i]);
|
||||
current.validTime = from.clone().add(j, "hours").toISOString();
|
||||
result.push(current);
|
||||
@@ -231,27 +226,24 @@ WeatherProvider.register("smhi", {
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper method to fetch a property from the returned data set.
|
||||
* The returned values is an array with always one value in it.
|
||||
* Helper method to get a property from the returned data set.
|
||||
*
|
||||
* @param currentWeatherData
|
||||
* @param name
|
||||
* @param currentWeatherData
|
||||
* @param name
|
||||
* @param {object} currentWeatherData Weatherdata to get from
|
||||
* @param {string} name The name of the property
|
||||
* @returns {*} The value of the property in the weatherdata
|
||||
*/
|
||||
paramValue(currentWeatherData, name) {
|
||||
return currentWeatherData.parameters.filter((p) => p.name == name).flatMap((p) => p.values)[0];
|
||||
return currentWeatherData.parameters.filter((p) => p.name === name).flatMap((p) => p.values)[0];
|
||||
},
|
||||
|
||||
/**
|
||||
* Map the icon value from SHMI to an icon that MagicMirror understands.
|
||||
* Map the icon value from SMHI to an icon that MagicMirror understands.
|
||||
* Uses different icons depending if its daytime or nighttime.
|
||||
* SHMI's description of what the numeric value means is the comment after the case.
|
||||
* SMHI's description of what the numeric value means is the comment after the case.
|
||||
*
|
||||
* @param input
|
||||
* @param isDayTime
|
||||
* @param input
|
||||
* @param isDayTime
|
||||
* @param {number} input The SMHI icon value
|
||||
* @param {boolean} isDayTime True if the icon should be for daytime, false for nighttime
|
||||
* @returns {string} The icon name for the MagicMirror
|
||||
*/
|
||||
convertWeatherType(input, isDayTime) {
|
||||
switch (input) {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/* global WeatherProvider, WeatherObject, SunCalc */
|
||||
/* global WeatherProvider, WeatherObject */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Weather
|
||||
@@ -116,9 +116,7 @@ WeatherProvider.register("ukmetoffice", {
|
||||
}
|
||||
|
||||
// determine the sunrise/sunset times - not supplied in UK Met Office data
|
||||
let times = this.calcAstroData(location);
|
||||
currentWeather.sunrise = times[0];
|
||||
currentWeather.sunset = times[1];
|
||||
currentWeather.updateSunTime(location.lat, location.lon);
|
||||
|
||||
return currentWeather;
|
||||
},
|
||||
@@ -154,20 +152,6 @@ WeatherProvider.register("ukmetoffice", {
|
||||
return days;
|
||||
},
|
||||
|
||||
/*
|
||||
* calculate the astronomical data
|
||||
*/
|
||||
calcAstroData(location) {
|
||||
const sunTimes = [];
|
||||
|
||||
// determine the sunrise/sunset times
|
||||
let times = SunCalc.getTimes(new Date(), location.lat, location.lon);
|
||||
sunTimes.push(moment(times.sunrise, "X"));
|
||||
sunTimes.push(moment(times.sunset, "X"));
|
||||
|
||||
return sunTimes;
|
||||
},
|
||||
|
||||
/*
|
||||
* Convert the Met Office icons to a more usable name.
|
||||
*/
|
||||
@@ -248,16 +232,16 @@ WeatherProvider.register("ukmetoffice", {
|
||||
return windCardinals.hasOwnProperty(windDirection) ? windCardinals[windDirection] : null;
|
||||
},
|
||||
|
||||
/*
|
||||
/**
|
||||
* Generates an url with api parameters based on the config.
|
||||
*
|
||||
* return String - URL params.
|
||||
* @param {string} forecastType daily or 3hourly forecast
|
||||
* @returns {string} url
|
||||
*/
|
||||
getParams(forecastType) {
|
||||
let params = "?";
|
||||
params += "res=" + forecastType;
|
||||
params += "&key=" + this.config.apiKey;
|
||||
|
||||
return params;
|
||||
}
|
||||
});
|
||||
|
@@ -1,3 +1,5 @@
|
||||
/* global WeatherProvider, WeatherObject */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Weather
|
||||
*
|
||||
@@ -11,9 +13,8 @@
|
||||
* Hourly data for next 2 days ("hourly") - https://www.metoffice.gov.uk/binaries/content/assets/metofficegovuk/pdf/data/global-spot-data-hourly.pdf
|
||||
* 3-hourly data for the next 7 days ("3hourly") - https://www.metoffice.gov.uk/binaries/content/assets/metofficegovuk/pdf/data/global-spot-data-3-hourly.pdf
|
||||
* Daily data for the next 7 days ("daily") - https://www.metoffice.gov.uk/binaries/content/assets/metofficegovuk/pdf/data/global-spot-data-daily.pdf
|
||||
*/
|
||||
|
||||
/* NOTES
|
||||
*
|
||||
* NOTES
|
||||
* This provider requires longitude/latitude coordinates, rather than a location ID (as with the previous Met Office provider)
|
||||
* Provide the following in your config.js file:
|
||||
* weatherProvider: "ukmetofficedatahub",
|
||||
@@ -69,13 +70,11 @@ WeatherProvider.register("ukmetofficedatahub", {
|
||||
// For DataHub requests, the API key/secret are sent in the headers rather than as query strings.
|
||||
// Headers defined according to Data Hub API (https://metoffice.apiconnect.ibmcloud.com/metoffice/production/api)
|
||||
getHeaders() {
|
||||
let headers = {
|
||||
return {
|
||||
accept: "application/json",
|
||||
"x-ibm-client-id": this.config.apiKey,
|
||||
"x-ibm-client-secret": this.config.apiSecret
|
||||
};
|
||||
|
||||
return headers;
|
||||
},
|
||||
|
||||
// Fetch data using supplied URL and request headers
|
||||
@@ -91,7 +90,7 @@ WeatherProvider.register("ukmetofficedatahub", {
|
||||
this.fetchWeather(this.getUrl("hourly"), this.getHeaders())
|
||||
.then((data) => {
|
||||
// Check data is useable
|
||||
if (!data || !data.features || !data.features[0].properties || !data.features[0].properties.timeSeries || data.features[0].properties.timeSeries.length == 0) {
|
||||
if (!data || !data.features || !data.features[0].properties || !data.features[0].properties.timeSeries || data.features[0].properties.timeSeries.length === 0) {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
Log.error("Possibly bad current/hourly data?");
|
||||
@@ -125,7 +124,7 @@ WeatherProvider.register("ukmetofficedatahub", {
|
||||
let nowUtc = moment.utc();
|
||||
|
||||
// Find hour that contains the current time
|
||||
for (hour in forecastDataHours) {
|
||||
for (let hour in forecastDataHours) {
|
||||
let forecastTime = moment.utc(forecastDataHours[hour].time);
|
||||
if (nowUtc.isSameOrAfter(forecastTime) && nowUtc.isBefore(moment(forecastTime.add(1, "h")))) {
|
||||
currentWeather.date = forecastTime;
|
||||
@@ -148,11 +147,9 @@ WeatherProvider.register("ukmetofficedatahub", {
|
||||
}
|
||||
|
||||
// Determine the sunrise/sunset times - (still) not supplied in UK Met Office data
|
||||
// Passes {longitude, latitude, height} to calcAstroData
|
||||
// Could just pass lat/long from this.config, but returned data from MO also contains elevation
|
||||
let times = this.calcAstroData(currentWeatherData.features[0].geometry.coordinates);
|
||||
currentWeather.sunrise = times[0];
|
||||
currentWeather.sunset = times[1];
|
||||
// Passes {longitude, latitude} to SunCalc, could pass height to, but
|
||||
// SunCalc.getTimes doesnt take that into account
|
||||
currentWeather.updateSunTime(this.config.lat, this.config.lon);
|
||||
|
||||
return currentWeather;
|
||||
},
|
||||
@@ -162,7 +159,7 @@ WeatherProvider.register("ukmetofficedatahub", {
|
||||
this.fetchWeather(this.getUrl("daily"), this.getHeaders())
|
||||
.then((data) => {
|
||||
// Check data is useable
|
||||
if (!data || !data.features || !data.features[0].properties || !data.features[0].properties.timeSeries || data.features[0].properties.timeSeries.length == 0) {
|
||||
if (!data || !data.features || !data.features[0].properties || !data.features[0].properties.timeSeries || data.features[0].properties.timeSeries.length === 0) {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
Log.error("Possibly bad forecast data?");
|
||||
@@ -196,7 +193,7 @@ WeatherProvider.register("ukmetofficedatahub", {
|
||||
let today = moment.utc().startOf("date");
|
||||
|
||||
// Go through each day in the forecasts
|
||||
for (day in forecastDataDays) {
|
||||
for (let day in forecastDataDays) {
|
||||
const forecastWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits, this.config.useKmh);
|
||||
|
||||
// Get date of forecast
|
||||
@@ -221,7 +218,6 @@ WeatherProvider.register("ukmetofficedatahub", {
|
||||
|
||||
// Pass on full details so they can be used in custom templates
|
||||
// Note the units of the supplied data when using this (see top of file)
|
||||
|
||||
forecastWeather.rawData = forecastDataDays[day];
|
||||
|
||||
dailyForecasts.push(forecastWeather);
|
||||
@@ -236,18 +232,6 @@ WeatherProvider.register("ukmetofficedatahub", {
|
||||
this.fetchedLocationName = name;
|
||||
},
|
||||
|
||||
// Calculate sunrise/sunset times
|
||||
calcAstroData(location) {
|
||||
const sunTimes = [];
|
||||
|
||||
// Careful to pass values to SunCalc in correct order (latitude, longitude, elevation)
|
||||
let times = SunCalc.getTimes(new Date(), location[1], location[0], location[2]);
|
||||
sunTimes.push(moment(times.sunrise, "X"));
|
||||
sunTimes.push(moment(times.sunset, "X"));
|
||||
|
||||
return sunTimes;
|
||||
},
|
||||
|
||||
// Convert temperatures to Fahrenheit (from degrees C), if required
|
||||
convertTemp(tempInC) {
|
||||
return this.config.tempUnits === "imperial" ? (tempInC * 9) / 5 + 32 : tempInC;
|
||||
@@ -258,11 +242,11 @@ WeatherProvider.register("ukmetofficedatahub", {
|
||||
// To use kilometres per hour, use "kph"
|
||||
// Else assumed imperial and the value is returned in miles per hour (a Met Office user is likely to be UK-based)
|
||||
convertWindSpeed(windInMpS) {
|
||||
if (this.config.windUnits == "mps") {
|
||||
if (this.config.windUnits === "mps") {
|
||||
return windInMpS;
|
||||
}
|
||||
|
||||
if (this.config.windUnits == "kph" || this.config.windUnits == "metric" || this.config.useKmh) {
|
||||
if (this.config.windUnits === "kph" || this.config.windUnits === "metric" || this.config.useKmh) {
|
||||
return windInMpS * 3.6;
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,8 @@
|
||||
* By Andrew Pometti
|
||||
* MIT Licensed
|
||||
*
|
||||
* This class is a provider for Weatherbit, based on Nicholas Hubbard's class for Dark Sky & Vince Peri's class for Weather.gov.
|
||||
* This class is a provider for Weatherbit, based on Nicholas Hubbard's class
|
||||
* for Dark Sky & Vince Peri's class for Weather.gov.
|
||||
*/
|
||||
WeatherProvider.register("weatherbit", {
|
||||
// Set the name of the provider.
|
||||
@@ -89,7 +90,6 @@ WeatherProvider.register("weatherbit", {
|
||||
currentWeather.windSpeed = parseFloat(currentWeatherData.data[0].wind_spd);
|
||||
currentWeather.windDirection = currentWeatherData.data[0].wind_dir;
|
||||
currentWeather.weatherType = this.convertWeatherType(currentWeatherData.data[0].weather.icon);
|
||||
Log.log("Wx Icon: " + currentWeatherData.data[0].weather.icon);
|
||||
currentWeather.sunrise = moment(currentWeatherData.data[0].sunrise, "HH:mm").add(tzOffset, "m");
|
||||
currentWeather.sunset = moment(currentWeatherData.data[0].sunset, "HH:mm").add(tzOffset, "m");
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/* global WeatherProvider, WeatherObject, SunCalc */
|
||||
/* global WeatherProvider, WeatherObject */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Weather
|
||||
@@ -21,7 +21,7 @@ WeatherProvider.register("weathergov", {
|
||||
|
||||
// Set the default config properties that is specific to this provider
|
||||
defaults: {
|
||||
apiBase: "https://api.weatherbit.io/v2.0",
|
||||
apiBase: "https://api.weather.gov/points/",
|
||||
weatherEndpoint: "/forecast",
|
||||
lat: 0,
|
||||
lon: 0
|
||||
@@ -129,7 +129,12 @@ WeatherProvider.register("weathergov", {
|
||||
.finally(() => {
|
||||
// excellent, let's fetch some actual wx data
|
||||
this.configURLs = true;
|
||||
// handle 'forecast' config, fall back to 'current'
|
||||
if (config.type === "forecast") {
|
||||
this.fetchWeatherForecast();
|
||||
} else {
|
||||
this.fetchCurrentWeather();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -153,18 +158,11 @@ WeatherProvider.register("weathergov", {
|
||||
currentWeather.precipitation = this.convertLength(currentWeatherData.precipitationLastHour.value);
|
||||
currentWeather.feelsLikeTemp = this.convertTemp(currentWeatherData.heatIndex.value);
|
||||
|
||||
let isDaytime = true;
|
||||
if (currentWeatherData.icon.includes("day")) {
|
||||
isDaytime = true;
|
||||
} else {
|
||||
isDaytime = false;
|
||||
}
|
||||
currentWeather.weatherType = this.convertWeatherType(currentWeatherData.textDescription, isDaytime);
|
||||
|
||||
// determine the sunrise/sunset times - not supplied in weather.gov data
|
||||
let times = this.calcAstroData(this.config.lat, this.config.lon);
|
||||
currentWeather.sunrise = times[0];
|
||||
currentWeather.sunset = times[1];
|
||||
currentWeather.updateSunTime(this.config.lat, this.config.lon);
|
||||
|
||||
// update weatherType
|
||||
currentWeather.weatherType = this.convertWeatherType(currentWeatherData.textDescription, currentWeather.isDayTime());
|
||||
|
||||
return currentWeather;
|
||||
},
|
||||
@@ -267,20 +265,6 @@ WeatherProvider.register("weathergov", {
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Calculate the astronomical data
|
||||
*/
|
||||
calcAstroData(lat, lon) {
|
||||
const sunTimes = [];
|
||||
|
||||
// determine the sunrise/sunset times
|
||||
let times = SunCalc.getTimes(new Date(), lat, lon);
|
||||
sunTimes.push(moment(times.sunrise, "X"));
|
||||
sunTimes.push(moment(times.sunset, "X"));
|
||||
|
||||
return sunTimes;
|
||||
},
|
||||
|
||||
/*
|
||||
* Convert the icons to a more usable name.
|
||||
*/
|
||||
|
@@ -132,10 +132,6 @@ Module.register("weather", {
|
||||
getTemplateData: function () {
|
||||
const forecast = this.weatherProvider.weatherForecast();
|
||||
|
||||
if (this.config.ignoreToday) {
|
||||
forecast.splice(0, 1);
|
||||
}
|
||||
|
||||
return {
|
||||
config: this.config,
|
||||
current: this.weatherProvider.currentWeather(),
|
||||
|
@@ -1,3 +1,5 @@
|
||||
/* global SunCalc */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Weather
|
||||
*
|
||||
@@ -10,6 +12,14 @@
|
||||
* As soon as we start implementing the forecast, mode properties will be added.
|
||||
*/
|
||||
class WeatherObject {
|
||||
/**
|
||||
* Constructor for a WeatherObject
|
||||
*
|
||||
* @param {string} units what units to use, "imperial" or "metric"
|
||||
* @param {string} tempUnits what tempunits to use
|
||||
* @param {string} windUnits what windunits to use
|
||||
* @param {boolean} useKmh use kmh if true, mps if false
|
||||
*/
|
||||
constructor(units, tempUnits, windUnits, useKmh) {
|
||||
this.units = units;
|
||||
this.tempUnits = tempUnits;
|
||||
@@ -80,8 +90,7 @@ class WeatherObject {
|
||||
}
|
||||
|
||||
kmhWindSpeed() {
|
||||
const windInKmh = this.windUnits === "imperial" ? this.windSpeed * 1.609344 : (this.windSpeed * 60 * 60) / 1000;
|
||||
return windInKmh;
|
||||
return this.windUnits === "imperial" ? this.windSpeed * 1.609344 : (this.windSpeed * 60 * 60) / 1000;
|
||||
}
|
||||
|
||||
nextSunAction() {
|
||||
@@ -113,4 +122,33 @@ class WeatherObject {
|
||||
|
||||
return this.tempUnits === "imperial" ? feelsLike : ((feelsLike - 32) * 5) / 9;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the weatherObject is at dayTime.
|
||||
*
|
||||
* @returns {boolean} true if it is at dayTime
|
||||
*/
|
||||
isDayTime() {
|
||||
return this.date.isBetween(this.sunrise, this.sunset, undefined, "[]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the sunrise / sunset time depending on the location. This can be
|
||||
* used if your provider doesnt provide that data by itself. Then SunCalc
|
||||
* is used here to calculate them according to the location.
|
||||
*
|
||||
* @param {number} lat latitude
|
||||
* @param {number} lon longitude
|
||||
*/
|
||||
updateSunTime(lat, lon) {
|
||||
let now = !this.date ? new Date() : this.date.toDate();
|
||||
let times = SunCalc.getTimes(now, lat, lon);
|
||||
this.sunrise = moment(times.sunrise, "X");
|
||||
this.sunset = moment(times.sunset, "X");
|
||||
}
|
||||
}
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = WeatherObject;
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: WeatherForecast
|
||||
*
|
||||
|
4555
package-lock.json
generated
4555
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
69
package.json
69
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "magicmirror",
|
||||
"version": "2.16.0",
|
||||
"version": "2.17.0",
|
||||
"description": "The open source modular smart mirror platform.",
|
||||
"main": "js/electron.js",
|
||||
"scripts": {
|
||||
@@ -12,16 +12,17 @@
|
||||
"postinstall": "npm run install-fonts && echo \"MagicMirror installation finished successfully! \n\"",
|
||||
"test": "NODE_ENV=test jest -i --forceExit",
|
||||
"test:coverage": "NODE_ENV=test nyc --reporter=lcov --reporter=text jest -i --forceExit",
|
||||
"test:electron": "NODE_ENV=test jest --selectProjects electron -i --forceExit",
|
||||
"test:e2e": "NODE_ENV=test jest --selectProjects e2e -i --forceExit",
|
||||
"test:unit": "NODE_ENV=test jest --selectProjects unit -i --forceExit",
|
||||
"test:prettier": "prettier . --check",
|
||||
"test:js": "eslint js/**/*.js modules/default/**/*.js clientonly/*.js serveronly/*.js translations/*.js vendor/*.js tests/**/*.js config/* --config .eslintrc.json --quiet",
|
||||
"test:css": "stylelint css/main.css modules/default/**/*.css --config .stylelintrc.json",
|
||||
"test:js": "eslint 'js/**/*.js' 'modules/default/**/*.js' 'clientonly/*.js' 'serveronly/*.js' 'translations/*.js' 'vendor/*.js' 'tests/**/*.js' 'config/*' --config .eslintrc.json",
|
||||
"test:css": "stylelint 'css/main.css' 'fonts/*.css' 'modules/default/**/*.css' 'vendor/*.css' --config .stylelintrc.json",
|
||||
"test:calendar": "node ./modules/default/calendar/debug.js",
|
||||
"config:check": "node js/check_config.js",
|
||||
"lint:prettier": "prettier . --write",
|
||||
"lint:js": "eslint js/**/*.js modules/default/**/*.js clientonly/*.js serveronly/*.js translations/*.js vendor/*.js tests/**/*.js config/* --config .eslintrc.json --fix",
|
||||
"lint:css": "stylelint css/main.css modules/default/**/*.css --config .stylelintrc.json --fix",
|
||||
"lint:js": "eslint 'js/**/*.js' 'modules/default/**/*.js' 'clientonly/*.js' 'serveronly/*.js' 'translations/*.js' 'vendor/*.js' 'tests/**/*.js' 'config/*' --config .eslintrc.json --fix",
|
||||
"lint:css": "stylelint 'css/main.css' 'fonts/*.css' 'modules/default/**/*.css' 'vendor/*.css' --config .stylelintrc.json --fix",
|
||||
"lint:staged": "pretty-quick --staged",
|
||||
"prepare": "[ -f node_modules/.bin/husky ] && husky install || echo no husky installed."
|
||||
},
|
||||
@@ -46,32 +47,33 @@
|
||||
"homepage": "https://magicmirror.builders",
|
||||
"devDependencies": {
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-jsdoc": "^35.4.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-jest": "^24.3.6",
|
||||
"eslint-plugin-jest": "^24.4.2",
|
||||
"eslint-plugin-jsdoc": "^36.1.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"express-basic-auth": "^1.2.0",
|
||||
"husky": "^6.0.0",
|
||||
"jest": "27.0.5",
|
||||
"jsdom": "^16.6.0",
|
||||
"husky": "^7.0.2",
|
||||
"jest": "^27.2.2",
|
||||
"jsdom": "^17.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"nyc": "^15.1.0",
|
||||
"prettier": "^2.3.1",
|
||||
"prettier": "^2.4.1",
|
||||
"pretty-quick": "^3.1.1",
|
||||
"sinon": "^11.1.1",
|
||||
"spectron": "^13.0.0",
|
||||
"sinon": "^11.1.2",
|
||||
"spectron": "^15.0.0",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-prettier": "^8.0.2",
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
"stylelint-prettier": "^1.2.0"
|
||||
"stylelint-prettier": "^1.2.0",
|
||||
"suncalc": "^1.8.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"electron": "^11.4.9"
|
||||
"electron": "^13.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"colors": "^1.4.0",
|
||||
"console-stamp": "^3.0.2",
|
||||
"digest-fetch": "^1.2.0",
|
||||
"eslint": "^7.29.0",
|
||||
"console-stamp": "^3.0.3",
|
||||
"digest-fetch": "^1.2.1",
|
||||
"eslint": "^7.32.0",
|
||||
"express": "^4.17.1",
|
||||
"express-ipfilter": "^1.2.0",
|
||||
"feedme": "^2.0.2",
|
||||
@@ -79,10 +81,9 @@
|
||||
"iconv-lite": "^0.6.3",
|
||||
"module-alias": "^2.2.2",
|
||||
"moment": "^2.29.1",
|
||||
"node-fetch": "^2.6.1",
|
||||
"node-fetch": "^2.6.5",
|
||||
"node-ical": "^0.13.0",
|
||||
"simple-git": "^2.40.0",
|
||||
"socket.io": "^4.1.2"
|
||||
"socket.io": "^4.2.0"
|
||||
},
|
||||
"_moduleAliases": {
|
||||
"node_helper": "js/node_helper.js",
|
||||
@@ -96,6 +97,9 @@
|
||||
"projects": [
|
||||
{
|
||||
"displayName": "unit",
|
||||
"moduleNameMapper": {
|
||||
"logger": "<rootDir>/js/logger.js"
|
||||
},
|
||||
"testMatch": [
|
||||
"**/tests/unit/**/*.[jt]s?(x)"
|
||||
],
|
||||
@@ -103,14 +107,31 @@
|
||||
"<rootDir>/tests/unit/mocks"
|
||||
]
|
||||
},
|
||||
{
|
||||
"displayName": "electron",
|
||||
"testMatch": [
|
||||
"**/tests/electron/**/*.[jt]s?(x)"
|
||||
],
|
||||
"testPathIgnorePatterns": [
|
||||
"<rootDir>/tests/electron/modules/mocks",
|
||||
"<rootDir>/tests/electron/global-setup.js",
|
||||
"<rootDir>/tests/electron/modules/basic-auth.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"displayName": "e2e",
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/tests/e2e/mock-console.js"
|
||||
],
|
||||
"testMatch": [
|
||||
"**/tests/e2e/**/*.[jt]s?(x)"
|
||||
],
|
||||
"modulePaths": [
|
||||
"<rootDir>/js/"
|
||||
],
|
||||
"testPathIgnorePatterns": [
|
||||
"<rootDir>/tests/e2e/modules/mocks",
|
||||
"<rootDir>/tests/e2e/global-setup.js"
|
||||
"<rootDir>/tests/e2e/global-setup.js",
|
||||
"<rootDir>/tests/e2e/mock-console.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
21
tests/configs/default.js
Normal file
21
tests/configs/default.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/* Magic Mirror Test default config for modules
|
||||
*
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
exports.configFactory = function (options) {
|
||||
return Object.assign(
|
||||
{
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
},
|
||||
|
||||
modules: []
|
||||
},
|
||||
options
|
||||
);
|
||||
};
|
@@ -3,22 +3,9 @@
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: [],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: []
|
||||
};
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
ipWhitelist: []
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,22 +3,7 @@
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: []
|
||||
};
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory();
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -4,19 +4,6 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "alert",
|
||||
|
@@ -3,19 +3,8 @@
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
@@ -35,7 +24,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,19 +3,8 @@
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
@@ -36,7 +25,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,19 +3,8 @@
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
@@ -35,7 +24,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,19 +3,8 @@
|
||||
* By Rejas
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
@@ -35,7 +24,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,19 +3,8 @@
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
@@ -31,7 +20,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -5,19 +5,8 @@
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
@@ -38,7 +27,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,19 +3,8 @@
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
@@ -33,7 +22,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,19 +3,8 @@
|
||||
* By Rejas
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
@@ -32,7 +21,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -4,18 +4,7 @@
|
||||
* 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,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -4,19 +4,6 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "clock",
|
||||
|
@@ -3,19 +3,6 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "clock",
|
||||
|
@@ -4,18 +4,7 @@
|
||||
* 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,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -4,18 +4,7 @@
|
||||
* 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,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
23
tests/configs/modules/clock/clock_showTime.js
Normal file
23
tests/configs/modules/clock/clock_showTime.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/* Magic Mirror Test config for default clock module
|
||||
*
|
||||
* By Johan Hammar
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
timeFormat: 12,
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "clock",
|
||||
position: "middle_center",
|
||||
config: {
|
||||
showTime: false
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
@@ -4,18 +4,7 @@
|
||||
* 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,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -4,18 +4,8 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "es",
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -4,18 +4,7 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "es",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -4,18 +4,8 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "es",
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -5,18 +5,8 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "es",
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -4,18 +4,7 @@
|
||||
* 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,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -4,18 +4,7 @@
|
||||
* 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,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -4,18 +4,7 @@
|
||||
* 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,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -4,18 +4,7 @@
|
||||
* 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,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -4,22 +4,6 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
fullscreen: false,
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "helloworld",
|
||||
|
@@ -4,19 +4,6 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "helloworld",
|
||||
|
@@ -4,19 +4,6 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "helloworld",
|
||||
|
@@ -4,18 +4,7 @@
|
||||
* 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,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -3,18 +3,7 @@
|
||||
* 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,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -3,18 +3,7 @@
|
||||
* 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,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
|
@@ -4,19 +4,6 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules:
|
||||
// Using exotic content. This is why don't accept go to JSON configuration file
|
||||
(function () {
|
||||
|
@@ -3,21 +3,7 @@
|
||||
* By rejas https://github.com/rejas
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
fullscreen: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
modules: [
|
||||
{
|
||||
module: "compliments",
|
||||
@@ -39,7 +25,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,19 +3,8 @@
|
||||
* By fewieden https://github.com/fewieden
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
@@ -28,7 +17,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,20 +3,7 @@
|
||||
* By fewieden https://github.com/fewieden
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
modules: [
|
||||
{
|
||||
module: "weather",
|
||||
@@ -34,7 +21,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,19 +3,8 @@
|
||||
* By fewieden https://github.com/fewieden
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
units: "imperial",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
@@ -30,7 +19,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,19 +3,8 @@
|
||||
* By fewieden https://github.com/fewieden
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
@@ -30,7 +19,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,19 +3,8 @@
|
||||
* By fewieden https://github.com/fewieden
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
@@ -33,7 +22,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,19 +3,8 @@
|
||||
* By rejas
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
units: "imperial",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
@@ -31,7 +20,7 @@ let config = {
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,22 +3,9 @@
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["x.x.x.x"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: []
|
||||
};
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
ipWhitelist: ["x.x.x.x"]
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -3,22 +3,9 @@
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8090,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: []
|
||||
};
|
||||
let config = require(process.cwd() + "/tests/configs/default.js").configFactory({
|
||||
port: 8090
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
|
@@ -4,18 +4,7 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.10.1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
}
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.10.1"]
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
|
@@ -1,42 +1,13 @@
|
||||
const helpers = require("./global-setup");
|
||||
const fetch = require("node-fetch");
|
||||
const helpers = require("./global-setup");
|
||||
|
||||
describe("Electron app environment", function () {
|
||||
helpers.setupTimeout(this);
|
||||
|
||||
let app = null;
|
||||
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/env.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/default.js");
|
||||
helpers.getDocument(done);
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
return helpers
|
||||
.startApplication({
|
||||
args: ["js/electron.js"]
|
||||
})
|
||||
.then(function (startedApp) {
|
||||
app = startedApp;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return helpers.stopApplication(app);
|
||||
});
|
||||
|
||||
it("should open a browserwindow", async function () {
|
||||
await app.client.waitUntilWindowLoaded();
|
||||
app.browserWindow.focus();
|
||||
expect(await app.client.getWindowCount()).toBe(1);
|
||||
expect(await app.browserWindow.isMinimized()).toBe(false);
|
||||
expect(await app.browserWindow.isDevToolsOpened()).toBe(false);
|
||||
expect(await app.browserWindow.isVisible()).toBe(true);
|
||||
expect(await app.browserWindow.isFocused()).toBe(true);
|
||||
const bounds = await app.browserWindow.getBounds();
|
||||
expect(bounds.width).toBeGreaterThan(0);
|
||||
expect(bounds.height).toBeGreaterThan(0);
|
||||
expect(await app.browserWindow.getTitle()).toBe("MagicMirror²");
|
||||
afterAll(function () {
|
||||
helpers.stopApplication();
|
||||
});
|
||||
|
||||
it("get request from http://localhost:8080 should return 200", function (done) {
|
||||
@@ -52,4 +23,10 @@ describe("Electron app environment", function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should show the title MagicMirror²", function () {
|
||||
const elem = document.querySelector("title");
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.textContent).toBe("MagicMirror²");
|
||||
});
|
||||
});
|
||||
|
@@ -1,10 +1,7 @@
|
||||
const helpers = require("./global-setup");
|
||||
const fetch = require("node-fetch");
|
||||
const helpers = require("./global-setup");
|
||||
|
||||
describe("All font files from roboto.css should be downloadable", function () {
|
||||
helpers.setupTimeout(this);
|
||||
|
||||
let app;
|
||||
const fontFiles = [];
|
||||
// Statements below filters out all 'url' lines in the CSS file
|
||||
const fileContent = require("fs").readFileSync(__dirname + "/../../fonts/roboto.css", "utf8");
|
||||
@@ -18,20 +15,10 @@ describe("All font files from roboto.css should be downloadable", function () {
|
||||
}
|
||||
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/without_modules.js";
|
||||
|
||||
return helpers
|
||||
.startApplication({
|
||||
args: ["js/electron.js"]
|
||||
})
|
||||
.then(function (startedApp) {
|
||||
app = startedApp;
|
||||
helpers.startApplication("tests/configs/without_modules.js");
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(function () {
|
||||
return helpers.stopApplication(app);
|
||||
helpers.stopApplication();
|
||||
});
|
||||
|
||||
test.each(fontFiles)("should return 200 HTTP code for file '%s'", (fontFile, done) => {
|
||||
|
@@ -1,53 +1,32 @@
|
||||
/*
|
||||
* Magic Mirror Global Setup Test Suite
|
||||
*
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const Application = require("spectron").Application;
|
||||
const assert = require("assert");
|
||||
const path = require("path");
|
||||
const EventEmitter = require("events");
|
||||
const jsdom = require("jsdom");
|
||||
|
||||
exports.getElectronPath = function () {
|
||||
let electronPath = path.join(__dirname, "..", "..", "node_modules", ".bin", "electron");
|
||||
if (process.platform === "win32") {
|
||||
electronPath += ".cmd";
|
||||
exports.startApplication = function (configFilename, exec) {
|
||||
jest.resetModules();
|
||||
if (global.app) {
|
||||
global.app.stop();
|
||||
}
|
||||
return electronPath;
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = configFilename;
|
||||
if (exec) exec;
|
||||
global.app = require("app.js");
|
||||
global.app.start();
|
||||
};
|
||||
|
||||
// Set timeout - if this is run as CI Job, increase timeout
|
||||
exports.setupTimeout = function (test) {
|
||||
if (process.env.CI) {
|
||||
jest.setTimeout(30000);
|
||||
} else {
|
||||
jest.setTimeout(10000);
|
||||
exports.stopApplication = function () {
|
||||
if (global.app) {
|
||||
global.app.stop();
|
||||
}
|
||||
};
|
||||
|
||||
exports.startApplication = function (options) {
|
||||
const emitter = new EventEmitter();
|
||||
emitter.setMaxListeners(100);
|
||||
|
||||
options.path = exports.getElectronPath();
|
||||
if (process.env.CI) {
|
||||
options.startTimeout = 30000;
|
||||
}
|
||||
|
||||
const app = new Application(options);
|
||||
return app.start().then(function () {
|
||||
assert.strictEqual(app.isRunning(), true);
|
||||
return app;
|
||||
});
|
||||
};
|
||||
|
||||
exports.stopApplication = function (app) {
|
||||
if (!app || !app.isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return app.stop().then(function () {
|
||||
assert.strictEqual(app.isRunning(), false);
|
||||
exports.getDocument = function (callback, ms) {
|
||||
const url = "http://" + (config.address || "localhost") + ":" + (config.port || "8080");
|
||||
jsdom.JSDOM.fromURL(url, { resources: "usable", runScripts: "dangerously" }).then((dom) => {
|
||||
dom.window.name = "jsdom";
|
||||
dom.window.onload = function () {
|
||||
global.document = dom.window.document;
|
||||
setTimeout(() => {
|
||||
callback();
|
||||
}, ms);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
@@ -1,29 +1,13 @@
|
||||
const helpers = require("./global-setup");
|
||||
const fetch = require("node-fetch");
|
||||
const helpers = require("./global-setup");
|
||||
|
||||
describe("ipWhitelist directive configuration", function () {
|
||||
helpers.setupTimeout(this);
|
||||
|
||||
let app = null;
|
||||
|
||||
beforeEach(function () {
|
||||
return helpers
|
||||
.startApplication({
|
||||
args: ["js/electron.js"]
|
||||
})
|
||||
.then(function (startedApp) {
|
||||
app = startedApp;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return helpers.stopApplication(app);
|
||||
});
|
||||
|
||||
describe("Set ipWhitelist without access", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/noIpWhiteList.js";
|
||||
helpers.startApplication("tests/configs/noIpWhiteList.js");
|
||||
});
|
||||
afterAll(function () {
|
||||
helpers.stopApplication();
|
||||
});
|
||||
|
||||
it("should return 403", function (done) {
|
||||
@@ -36,8 +20,10 @@ describe("ipWhitelist directive configuration", function () {
|
||||
|
||||
describe("Set ipWhitelist []", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/empty_ipWhiteList.js";
|
||||
helpers.startApplication("tests/configs/empty_ipWhiteList.js");
|
||||
});
|
||||
afterAll(function () {
|
||||
helpers.stopApplication();
|
||||
});
|
||||
|
||||
it("should return 200", function (done) {
|
||||
|
21
tests/e2e/mock-console.js
Normal file
21
tests/e2e/mock-console.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Suppresses errors concerning web server already shut down.
|
||||
*
|
||||
* @param {string} err The error message.
|
||||
*/
|
||||
function mockError(err) {
|
||||
if (err.includes("ECONNREFUSED") || err.includes("ECONNRESET") || err.includes("socket hang up") || err.includes("exports is not defined")) {
|
||||
jest.fn();
|
||||
} else {
|
||||
console.dir(err);
|
||||
}
|
||||
}
|
||||
|
||||
global.console = {
|
||||
log: jest.fn(),
|
||||
dir: console.dir,
|
||||
error: mockError,
|
||||
warn: console.warn,
|
||||
info: jest.fn(),
|
||||
debug: console.debug
|
||||
};
|
@@ -1,32 +1,17 @@
|
||||
const helpers = require("../global-setup");
|
||||
|
||||
describe("Alert module", function () {
|
||||
helpers.setupTimeout(this);
|
||||
|
||||
let app = null;
|
||||
|
||||
beforeEach(function () {
|
||||
return helpers
|
||||
.startApplication({
|
||||
args: ["js/electron.js"]
|
||||
})
|
||||
.then(function (startedApp) {
|
||||
app = startedApp;
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/alert/default.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return helpers.stopApplication(app);
|
||||
});
|
||||
|
||||
describe("Default configuration", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/alert/default.js";
|
||||
afterAll(function () {
|
||||
helpers.stopApplication();
|
||||
});
|
||||
|
||||
it("should show the welcome message", function () {
|
||||
return app.client.waitUntilTextExists(".ns-box .ns-box-inner .light.bright.small", "Welcome, start was successful!", 10000);
|
||||
});
|
||||
const elem = document.querySelector(".ns-box .ns-box-inner .light.bright.small");
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.textContent).toContain("Welcome, start was successful!");
|
||||
});
|
||||
});
|
||||
|
@@ -1,86 +1,71 @@
|
||||
const helpers = require("../global-setup");
|
||||
|
||||
describe("Clock set to spanish language module", function () {
|
||||
helpers.setupTimeout(this);
|
||||
afterAll(function () {
|
||||
helpers.stopApplication();
|
||||
});
|
||||
|
||||
let app = null;
|
||||
|
||||
testMatch = async function (element, regex) {
|
||||
await app.client.waitUntilWindowLoaded();
|
||||
const elem = await app.client.$(element);
|
||||
const txt = await elem.getText(element);
|
||||
return expect(txt).toMatch(regex);
|
||||
const testMatch = function (element, regex) {
|
||||
const elem = document.querySelector(element);
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.textContent).toMatch(regex);
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
return helpers
|
||||
.startApplication({
|
||||
args: ["js/electron.js"]
|
||||
})
|
||||
.then(function (startedApp) {
|
||||
app = startedApp;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return helpers.stopApplication(app);
|
||||
});
|
||||
|
||||
describe("with default 24hr clock config", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/es/clock_24hr.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/clock/es/clock_24hr.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("shows date with correct format", async function () {
|
||||
it("shows date with correct format", function () {
|
||||
const dateRegex = /^(?:lunes|martes|miércoles|jueves|viernes|sábado|domingo), \d{1,2} de (?:enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre) de \d{4}$/;
|
||||
return testMatch(".clock .date", dateRegex);
|
||||
testMatch(".clock .date", dateRegex);
|
||||
});
|
||||
|
||||
it("shows time in 24hr format", async function () {
|
||||
it("shows time in 24hr format", function () {
|
||||
const timeRegex = /^(?:2[0-3]|[01]\d):[0-5]\d[0-5]\d$/;
|
||||
return testMatch(".clock .time", timeRegex);
|
||||
testMatch(".clock .time", timeRegex);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with default 12hr clock config", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/es/clock_12hr.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/clock/es/clock_12hr.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("shows date with correct format", async function () {
|
||||
it("shows date with correct format", function () {
|
||||
const dateRegex = /^(?:lunes|martes|miércoles|jueves|viernes|sábado|domingo), \d{1,2} de (?:enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre) de \d{4}$/;
|
||||
return testMatch(".clock .date", dateRegex);
|
||||
testMatch(".clock .date", dateRegex);
|
||||
});
|
||||
|
||||
it("shows time in 12hr format", async function () {
|
||||
it("shows time in 12hr format", function () {
|
||||
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[ap]m$/;
|
||||
return testMatch(".clock .time", timeRegex);
|
||||
testMatch(".clock .time", timeRegex);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with showPeriodUpper config enabled", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/es/clock_showPeriodUpper.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/clock/es/clock_showPeriodUpper.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("shows 12hr time with upper case AM/PM", async function () {
|
||||
it("shows 12hr time with upper case AM/PM", function () {
|
||||
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[AP]M$/;
|
||||
return testMatch(".clock .time", timeRegex);
|
||||
testMatch(".clock .time", timeRegex);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with showWeek config enabled", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/es/clock_showWeek.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/clock/es/clock_showWeek.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("shows week with correct format", async function () {
|
||||
it("shows week with correct format", function () {
|
||||
const weekRegex = /^Semana [0-9]{1,2}$/;
|
||||
return testMatch(".clock .week", weekRegex);
|
||||
testMatch(".clock .week", weekRegex);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -2,120 +2,115 @@ const helpers = require("../global-setup");
|
||||
const moment = require("moment");
|
||||
|
||||
describe("Clock module", function () {
|
||||
helpers.setupTimeout(this);
|
||||
afterAll(function () {
|
||||
helpers.stopApplication();
|
||||
});
|
||||
|
||||
let app = null;
|
||||
|
||||
testMatch = async function (element, regex) {
|
||||
await app.client.waitUntilWindowLoaded();
|
||||
const elem = await app.client.$(element);
|
||||
const txt = await elem.getText(element);
|
||||
return expect(txt).toMatch(regex);
|
||||
const testMatch = function (element, regex) {
|
||||
const elem = document.querySelector(element);
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.textContent).toMatch(regex);
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
return helpers
|
||||
.startApplication({
|
||||
args: ["js/electron.js"]
|
||||
})
|
||||
.then(function (startedApp) {
|
||||
app = startedApp;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return helpers.stopApplication(app);
|
||||
});
|
||||
|
||||
describe("with default 24hr clock config", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_24hr.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/clock/clock_24hr.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("should show the date in the correct format", async function () {
|
||||
it("should show the date in the correct format", function () {
|
||||
const dateRegex = /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}$/;
|
||||
return testMatch(".clock .date", dateRegex);
|
||||
testMatch(".clock .date", dateRegex);
|
||||
});
|
||||
|
||||
it("should show the time in 24hr format", async function () {
|
||||
it("should show the time in 24hr format", function () {
|
||||
const timeRegex = /^(?:2[0-3]|[01]\d):[0-5]\d[0-5]\d$/;
|
||||
return testMatch(".clock .time", timeRegex);
|
||||
testMatch(".clock .time", timeRegex);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with default 12hr clock config", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_12hr.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/clock/clock_12hr.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("should show the date in the correct format", async function () {
|
||||
it("should show the date in the correct format", function () {
|
||||
const dateRegex = /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}$/;
|
||||
return testMatch(".clock .date", dateRegex);
|
||||
testMatch(".clock .date", dateRegex);
|
||||
});
|
||||
|
||||
it("should show the time in 12hr format", async function () {
|
||||
it("should show the time in 12hr format", function () {
|
||||
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[ap]m$/;
|
||||
return testMatch(".clock .time", timeRegex);
|
||||
testMatch(".clock .time", timeRegex);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with showPeriodUpper config enabled", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_showPeriodUpper.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/clock/clock_showPeriodUpper.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("should show 12hr time with upper case AM/PM", async function () {
|
||||
it("should show 12hr time with upper case AM/PM", function () {
|
||||
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[AP]M$/;
|
||||
return testMatch(".clock .time", timeRegex);
|
||||
testMatch(".clock .time", timeRegex);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with displaySeconds config disabled", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_displaySeconds_false.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/clock/clock_displaySeconds_false.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("should show 12hr time without seconds am/pm", async function () {
|
||||
it("should show 12hr time without seconds am/pm", function () {
|
||||
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[ap]m$/;
|
||||
return testMatch(".clock .time", timeRegex);
|
||||
testMatch(".clock .time", timeRegex);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with showTime config disabled", function () {
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/clock/clock_showTime.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("should show not show the time when digital clock is shown", function () {
|
||||
const elem = document.querySelector(".clock .digital .time");
|
||||
expect(elem).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with showWeek config enabled", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_showWeek.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/clock/clock_showWeek.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("should show the week in the correct format", async function () {
|
||||
it("should show the week in the correct format", function () {
|
||||
const weekRegex = /^Week [0-9]{1,2}$/;
|
||||
return testMatch(".clock .week", weekRegex);
|
||||
testMatch(".clock .week", weekRegex);
|
||||
});
|
||||
|
||||
it("should show the week with the correct number of week of year", async function () {
|
||||
it("should show the week with the correct number of week of year", function () {
|
||||
const currentWeekNumber = moment().week();
|
||||
const weekToShow = "Week " + currentWeekNumber;
|
||||
await app.client.waitUntilWindowLoaded();
|
||||
const elem = await app.client.$(".clock .week");
|
||||
const txt = await elem.getText(".clock .week");
|
||||
return expect(txt).toBe(weekToShow);
|
||||
const elem = document.querySelector(".clock .week");
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.textContent).toBe(weekToShow);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with analog clock face enabled", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_analog.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/clock/clock_analog.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("should show the analog clock face", async () => {
|
||||
await app.client.waitUntilWindowLoaded();
|
||||
const clock = await app.client.$$(".clockCircle");
|
||||
return expect(clock.length).toBe(1);
|
||||
it("should show the analog clock face", () => {
|
||||
const elem = document.querySelector(".clockCircle");
|
||||
expect(elem).not.toBe(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,106 +1,87 @@
|
||||
const helpers = require("../global-setup");
|
||||
|
||||
/**
|
||||
* move similar tests in function doTest
|
||||
*
|
||||
* @param {Array} complimentsArray The array of compliments.
|
||||
*/
|
||||
function doTest(complimentsArray) {
|
||||
let elem = document.querySelector(".compliments");
|
||||
expect(elem).not.toBe(null);
|
||||
elem = document.querySelector(".module-content");
|
||||
expect(elem).not.toBe(null);
|
||||
expect(complimentsArray).toContain(elem.textContent);
|
||||
}
|
||||
|
||||
describe("Compliments module", function () {
|
||||
helpers.setupTimeout(this);
|
||||
|
||||
let app = null;
|
||||
|
||||
beforeEach(function () {
|
||||
return helpers
|
||||
.startApplication({
|
||||
args: ["js/electron.js"]
|
||||
})
|
||||
.then(function (startedApp) {
|
||||
app = startedApp;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return helpers.stopApplication(app);
|
||||
afterAll(function () {
|
||||
helpers.stopApplication();
|
||||
});
|
||||
|
||||
describe("parts of days", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/compliments/compliments_parts_day.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/compliments/compliments_parts_day.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("if Morning compliments for that part of day", async function () {
|
||||
it("if Morning compliments for that part of day", function () {
|
||||
const hour = new Date().getHours();
|
||||
if (hour >= 3 && hour < 12) {
|
||||
// if morning check
|
||||
const elem = await app.client.$(".compliments");
|
||||
return elem.getText(".compliments").then(function (text) {
|
||||
expect(["Hi", "Good Morning", "Morning test"]).toContain(text);
|
||||
});
|
||||
doTest(["Hi", "Good Morning", "Morning test"]);
|
||||
}
|
||||
});
|
||||
|
||||
it("if Afternoon show Compliments for that part of day", async function () {
|
||||
it("if Afternoon show Compliments for that part of day", function () {
|
||||
const hour = new Date().getHours();
|
||||
if (hour >= 12 && hour < 17) {
|
||||
// if afternoon check
|
||||
const elem = await app.client.$(".compliments");
|
||||
return elem.getText(".compliments").then(function (text) {
|
||||
expect(["Hello", "Good Afternoon", "Afternoon test"]).toContain(text);
|
||||
});
|
||||
doTest(["Hello", "Good Afternoon", "Afternoon test"]);
|
||||
}
|
||||
});
|
||||
|
||||
it("if Evening show Compliments for that part of day", async function () {
|
||||
it("if Evening show Compliments for that part of day", function () {
|
||||
const hour = new Date().getHours();
|
||||
if (!(hour >= 3 && hour < 12) && !(hour >= 12 && hour < 17)) {
|
||||
// if evening check
|
||||
const elem = await app.client.$(".compliments");
|
||||
return elem.getText(".compliments").then(function (text) {
|
||||
expect(["Hello There", "Good Evening", "Evening test"]).toContain(text);
|
||||
});
|
||||
doTest(["Hello There", "Good Evening", "Evening test"]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Feature anytime in compliments module", function () {
|
||||
describe("Set anytime and empty compliments for morning, evening and afternoon ", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/compliments/compliments_anytime.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/compliments/compliments_anytime.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("Show anytime because if configure empty parts of day compliments and set anytime compliments", async function () {
|
||||
const elem = await app.client.$(".compliments");
|
||||
return elem.getText(".compliments").then(function (text) {
|
||||
expect(["Anytime here"]).toContain(text);
|
||||
});
|
||||
it("Show anytime because if configure empty parts of day compliments and set anytime compliments", function () {
|
||||
doTest(["Anytime here"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Only anytime present in configuration compliments", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/compliments/compliments_only_anytime.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/compliments/compliments_only_anytime.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("Show anytime compliments", async function () {
|
||||
const elem = await app.client.$(".compliments");
|
||||
return elem.getText(".compliments").then(function (text) {
|
||||
expect(["Anytime here"]).toContain(text);
|
||||
});
|
||||
it("Show anytime compliments", function () {
|
||||
doTest(["Anytime here"]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Feature date in compliments module", function () {
|
||||
describe("Set date and empty compliments for anytime, morning, evening and afternoon", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/compliments/compliments_date.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/compliments/compliments_date.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("Show happy new year compliment on new years day", async function () {
|
||||
const elem = await app.client.$(".compliments");
|
||||
return elem.getText(".compliments").then(function (text) {
|
||||
expect(["Happy new year!"]).toContain(text);
|
||||
});
|
||||
it("Show happy new year compliment on new years day", function () {
|
||||
doTest(["Happy new year!"]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,45 +1,33 @@
|
||||
const helpers = require("../global-setup");
|
||||
|
||||
describe("Test helloworld module", function () {
|
||||
helpers.setupTimeout(this);
|
||||
|
||||
let app = null;
|
||||
|
||||
beforeEach(function () {
|
||||
return helpers
|
||||
.startApplication({
|
||||
args: ["js/electron.js"]
|
||||
})
|
||||
.then(function (startedApp) {
|
||||
app = startedApp;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return helpers.stopApplication(app);
|
||||
afterAll(function () {
|
||||
helpers.stopApplication();
|
||||
});
|
||||
|
||||
describe("helloworld set config text", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/helloworld/helloworld.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/helloworld/helloworld.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("Test message helloworld module", async function () {
|
||||
const elem = await app.client.$(".helloworld");
|
||||
return expect(await elem.getText(".helloworld")).toBe("Test HelloWorld Module");
|
||||
it("Test message helloworld module", function () {
|
||||
const elem = document.querySelector(".helloworld");
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.textContent).toContain("Test HelloWorld Module");
|
||||
});
|
||||
});
|
||||
|
||||
describe("helloworld default config text", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/helloworld/helloworld_default.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/helloworld/helloworld_default.js");
|
||||
helpers.getDocument(done, 1000);
|
||||
});
|
||||
|
||||
it("Test message helloworld module", async function () {
|
||||
const elem = await app.client.$(".helloworld");
|
||||
return expect(await elem.getText(".helloworld")).toBe("Hello World!");
|
||||
it("Test message helloworld module", function () {
|
||||
const elem = document.querySelector(".helloworld");
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.textContent).toContain("Hello World!");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,67 +1,63 @@
|
||||
const helpers = require("../global-setup");
|
||||
|
||||
describe("Newsfeed module", function () {
|
||||
helpers.setupTimeout(this);
|
||||
|
||||
let app = null;
|
||||
|
||||
beforeEach(function () {
|
||||
return helpers
|
||||
.startApplication({
|
||||
args: ["js/electron.js"]
|
||||
})
|
||||
.then(function (startedApp) {
|
||||
app = startedApp;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return helpers.stopApplication(app);
|
||||
afterAll(function () {
|
||||
helpers.stopApplication();
|
||||
});
|
||||
|
||||
describe("Default configuration", function () {
|
||||
beforeAll(function () {
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/newsfeed/default.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/newsfeed/default.js");
|
||||
helpers.getDocument(done, 3000);
|
||||
});
|
||||
|
||||
it("should show the newsfeed title", function () {
|
||||
return app.client.waitUntilTextExists(".newsfeed .newsfeed-source", "Rodrigo Ramirez Blog", 10000);
|
||||
const elem = document.querySelector(".newsfeed .newsfeed-source");
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.textContent).toContain("Rodrigo Ramirez Blog");
|
||||
});
|
||||
|
||||
it("should show the newsfeed article", function () {
|
||||
return app.client.waitUntilTextExists(".newsfeed .newsfeed-title", "QPanel", 10000);
|
||||
const elem = document.querySelector(".newsfeed .newsfeed-title");
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.textContent).toContain("QPanel");
|
||||
});
|
||||
|
||||
it("should NOT show the newsfeed description", async () => {
|
||||
await app.client.waitUntilTextExists(".newsfeed .newsfeed-title", "QPanel", 10000);
|
||||
const events = await app.client.$$(".newsfeed .newsfeed-desc");
|
||||
return expect(events.length).toBe(0);
|
||||
it("should NOT show the newsfeed description", () => {
|
||||
const elem = document.querySelector(".newsfeed .newsfeed-desc");
|
||||
expect(elem).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Custom configuration", function () {
|
||||
beforeAll(function () {
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/newsfeed/prohibited_words.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/newsfeed/prohibited_words.js");
|
||||
helpers.getDocument(done, 3000);
|
||||
});
|
||||
|
||||
it("should not show articles with prohibited words", function () {
|
||||
return app.client.waitUntilTextExists(".newsfeed .newsfeed-title", "Problema VirtualBox", 10000);
|
||||
const elem = document.querySelector(".newsfeed .newsfeed-title");
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.textContent).toContain("Problema VirtualBox");
|
||||
});
|
||||
|
||||
it("should show the newsfeed description", async () => {
|
||||
await app.client.waitUntilTextExists(".newsfeed .newsfeed-title", "Problema VirtualBox", 10000);
|
||||
const events = await app.client.$$(".newsfeed .newsfeed-desc");
|
||||
return expect(events.length).toBe(1);
|
||||
it("should show the newsfeed description", () => {
|
||||
const elem = document.querySelector(".newsfeed .newsfeed-desc");
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.textContent.length).not.toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Invalid configuration", function () {
|
||||
beforeAll(function () {
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/newsfeed/incorrect_url.js";
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/newsfeed/incorrect_url.js");
|
||||
helpers.getDocument(done, 3000);
|
||||
});
|
||||
|
||||
it("should show malformed url warning", function () {
|
||||
return app.client.waitUntilTextExists(".newsfeed .small", "Error in the Newsfeed module. Malformed url.", 10000);
|
||||
const elem = document.querySelector(".newsfeed .small");
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.textContent).toContain("Error in the Newsfeed module. Malformed url.");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,38 +1,24 @@
|
||||
const helpers = require("./global-setup");
|
||||
|
||||
describe("Display of modules", function () {
|
||||
helpers.setupTimeout(this);
|
||||
|
||||
let app = null;
|
||||
|
||||
beforeEach(function () {
|
||||
return helpers
|
||||
.startApplication({
|
||||
args: ["js/electron.js"]
|
||||
})
|
||||
.then(function (startedApp) {
|
||||
app = startedApp;
|
||||
beforeAll(function (done) {
|
||||
helpers.startApplication("tests/configs/modules/display.js");
|
||||
helpers.getDocument(done);
|
||||
});
|
||||
afterAll(function () {
|
||||
helpers.stopApplication();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
return helpers.stopApplication(app);
|
||||
it("should show the test header", function () {
|
||||
const elem = document.querySelector("#module_0_helloworld .module-header");
|
||||
expect(elem).not.toBe(null);
|
||||
// textContent gibt hier lowercase zurück, das uppercase wird durch css realisiert, was daher nicht in textContent landet
|
||||
expect(elem.textContent).toBe("test_header");
|
||||
});
|
||||
|
||||
describe("Using helloworld", function () {
|
||||
beforeAll(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/display.js";
|
||||
});
|
||||
|
||||
it("should show the test header", async () => {
|
||||
const elem = await app.client.$("#module_0_helloworld .module-header", 10000);
|
||||
return expect(await elem.getText("#module_0_helloworld .module-header")).toBe("TEST_HEADER");
|
||||
});
|
||||
|
||||
it("should show no header if no header text is specified", async () => {
|
||||
const elem = await app.client.$("#module_1_helloworld .module-header", 10000);
|
||||
return expect(await elem.getText("#module_1_helloworld .module-header")).toBe("");
|
||||
});
|
||||
it("should show no header if no header text is specified", function () {
|
||||
const elem = document.querySelector("#module_1_helloworld .module-header");
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.textContent).toBe("undefined");
|
||||
});
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user