mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-06-27 11:50:00 +00:00
commit
a2d7cdcfb4
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@ -44,7 +44,7 @@ When submitting a new issue, please supply the following information:
|
|||||||
|
|
||||||
**Node Version**: Make sure it's version 14 or later (recommended is 16).
|
**Node Version**: Make sure it's version 14 or later (recommended is 16).
|
||||||
|
|
||||||
**MagicMirror Version**: Please let us know which version of MagicMirror you are running. It can be found in the `package.json` 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.
|
**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.
|
||||||
|
|
||||||
|
12
.github/ISSUE_TEMPLATE.md
vendored
12
.github/ISSUE_TEMPLATE.md
vendored
@ -10,16 +10,16 @@ If you're not sure if it's a real bug or if it's just you, please open a topic o
|
|||||||
|
|
||||||
Problems installing or configuring your MagicMirror? Check out: [https://forum.magicmirror.builders/category/10/troubleshooting](https://forum.magicmirror.builders/category/10/troubleshooting)
|
Problems installing or configuring your MagicMirror? Check out: [https://forum.magicmirror.builders/category/10/troubleshooting](https://forum.magicmirror.builders/category/10/troubleshooting)
|
||||||
|
|
||||||
A common problem is that your config file could be invalid. Please run in your MagicMirror directory: `npm run config:check` and see if it reports an error.
|
A common problem is that your config file could be invalid. Please run in your MagicMirror² directory: `npm run config:check` and see if it reports an error.
|
||||||
|
|
||||||
## I found a bug in the MagicMirror installer
|
## I found a bug in the MagicMirror² installer
|
||||||
|
|
||||||
If you are facing an issue or found a bug while trying to install MagicMirror via the installer please report it in the respective GitHub repository:
|
If you are facing an issue or found a bug while trying to install MagicMirror² via the installer please report it in the respective GitHub repository:
|
||||||
[https://github.com/sdetweil/MagicMirror_scripts](https://github.com/sdetweil/MagicMirror_scripts)
|
[https://github.com/sdetweil/MagicMirror_scripts](https://github.com/sdetweil/MagicMirror_scripts)
|
||||||
|
|
||||||
## I found a bug in the MagicMirror Docker image
|
## 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 corresponding repository:
|
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)
|
- 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)
|
- (deprecated) bastilimbach/docker-magicmirror: [https://github.com/bastilimbach/docker-MagicMirror](https://github.com/bastilimbach/docker-MagicMirror)
|
||||||
@ -35,7 +35,7 @@ When submitting a new issue, please supply the following information:
|
|||||||
|
|
||||||
**Node Version**: Make sure it's version 14 or later (recommended is 16).
|
**Node Version**: Make sure it's version 14 or later (recommended is 16).
|
||||||
|
|
||||||
**MagicMirror Version**: Please let us know which version of MagicMirror you are running. It can be found in the `package.json` 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.
|
**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/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,4 +1,4 @@
|
|||||||
Hello and thank you for wanting to contribute to the MagicMirror project
|
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:**
|
**Please make sure that you have followed these 4 rules before submitting your Pull Request:**
|
||||||
|
|
||||||
|
4
.github/workflows/automated-tests.yml
vendored
4
.github/workflows/automated-tests.yml
vendored
@ -33,6 +33,4 @@ jobs:
|
|||||||
npm run test:prettier
|
npm run test:prettier
|
||||||
npm run test:js
|
npm run test:js
|
||||||
npm run test:css
|
npm run test:css
|
||||||
npm run test:unit
|
npm run test
|
||||||
npm run test:e2e
|
|
||||||
npm run test:electron
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
npm run lint:staged
|
[ -f "$(dirname "$0")/_/husky.sh" ] && . "$(dirname "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
if command -v npm &> /dev/null; then
|
||||||
|
npm run lint:staged
|
||||||
|
fi
|
||||||
|
54
CHANGELOG.md
54
CHANGELOG.md
@ -5,6 +5,41 @@ 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².
|
❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror².
|
||||||
|
|
||||||
|
## [2.19.0] - 2022-04-01
|
||||||
|
|
||||||
|
Special thanks to the following contributors: @10bias, @CFenner, @JHWelch, @k1rd3rf, @khassel, @kolbyjack, @krekos, @KristjanESPERANTO, @Nerfzooka, @oraclesean, @oscarb, @philnagel, @rejas, @sdetweil, @shin10, @SiderealArt and @Tom-Hirschberger.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added a config option under the weather module, `absoluteDates`, providing an option to format weather forecast date output with either absolute or relative dates.
|
||||||
|
- Added test for new weather forecast `absoluteDates` porperty.
|
||||||
|
- The modules get a class hidden added/removed if they get hidden/shown which will also toggle pointer-events.
|
||||||
|
- Added new config option `showTitleAsUrl` to newsfeed module. If set, the diplayed title is a link to the article which is useful when running in a browser and you want to read this article.
|
||||||
|
- Added internal cors proxy to get weather providers working without public proxies (fixes #2714). The new url `http(s)://address:port/cors?url=https://whatever-to-proxy` can be used in other modules too.
|
||||||
|
- Added a WeatherProvider for Weatherflow.
|
||||||
|
- Added new env var `ELECTRON_DISABLE_GPU` which disable gpu under electron if set (fixes #2831).
|
||||||
|
- Added missing Czech translations.
|
||||||
|
|
||||||
|
### Updated
|
||||||
|
|
||||||
|
- Deprecated roboto fonts package `roboto-fontface-bower` replaced with `fontsource`.
|
||||||
|
- Update `electron` to v17, `helmet` to v5 (use defaults of v4) and other dependencies
|
||||||
|
- Updates Font Awesome css class to new default style (fixes #2768)
|
||||||
|
- Replaced deprecated modules `currentweather` and `weatherforecast` with dummy modules only displaying that they have to be replaced.
|
||||||
|
- Include all calendar events from the configured date range when broadcasting.
|
||||||
|
- Update Danish and German translation.
|
||||||
|
- Update `node-ical` to v0.15 and added `luxon` as dependency for not breaking the "no-optional" install (see #2718 and #2824).
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Improved and speedup e2e tests, artificial wait after mm start removed.
|
||||||
|
- Improved husky setup not blocking `git commit` if `husky` or `npm` is not installed.
|
||||||
|
- Using a consistent spelling of MagicMirror².
|
||||||
|
- Fix minor console output issue for loading translations (#2814).
|
||||||
|
- Don't adjust startDate for full day events if endDate is in the past.
|
||||||
|
- Fix windspeed conversion error in openweathermap provider. (#2812)
|
||||||
|
- Fix conflicting parms turning off showEnd for full day events. (#2629)
|
||||||
|
|
||||||
## [2.18.0] - 2022-01-01
|
## [2.18.0] - 2022-01-01
|
||||||
|
|
||||||
Special thanks to the following contributors: @AmpioRosso, @eouia, @fewieden, @jupadin, @khassel, @kolbyjack, @KristjanESPERANTO, @MariusVaice, @rejas, @rico24 and @sdetweil.
|
Special thanks to the following contributors: @AmpioRosso, @eouia, @fewieden, @jupadin, @khassel, @kolbyjack, @KristjanESPERANTO, @MariusVaice, @rejas, @rico24 and @sdetweil.
|
||||||
@ -29,6 +64,7 @@ Special thanks to the following contributors: @AmpioRosso, @eouia, @fewieden, @j
|
|||||||
- Added dangerouslyDisableAutoEscaping config option for newsfeed templates (fixes #2712).
|
- Added dangerouslyDisableAutoEscaping config option for newsfeed templates (fixes #2712).
|
||||||
- Added missing shebang to `installers/mm.sh`.
|
- Added missing shebang to `installers/mm.sh`.
|
||||||
- Node versions in templates and github workflows.
|
- Node versions in templates and github workflows.
|
||||||
|
- Updated translations for Traditional Chinese (Taiwan) (zh-tw.json).
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@ -252,7 +288,7 @@ Special thanks to the following contributors: @Alvinger, @AndyPoms, @ashishtank,
|
|||||||
- Rename Greek translation to correct ISO 639-1 alpha-2 code (gr > el). (#2155)
|
- Rename Greek translation to correct ISO 639-1 alpha-2 code (gr > el). (#2155)
|
||||||
- Add a space after icons of sunrise and sunset. (#2169)
|
- Add a space after icons of sunrise and sunset. (#2169)
|
||||||
- Fix calendar when no DTEND record found in event, startDate overlay when endDate set. (#2177)
|
- Fix calendar when no DTEND record found in event, startDate overlay when endDate set. (#2177)
|
||||||
- Fix windspeed convertion error in ukmetoffice weather provider. (#2189)
|
- Fix windspeed conversion error in ukmetoffice weather provider. (#2189)
|
||||||
- Fix console.debug not having timestamps. (#2199)
|
- Fix console.debug not having timestamps. (#2199)
|
||||||
- Fix calendar full day event east of UTC start time. (#2200)
|
- Fix calendar full day event east of UTC start time. (#2200)
|
||||||
- Fix non-fullday recurring rule processing. (#2216)
|
- Fix non-fullday recurring rule processing. (#2216)
|
||||||
@ -275,8 +311,8 @@ Special thanks to the following contributors: @bryanzzhu, @bugsounet, @chamakura
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- `--dry-run` Added option in fetch call within updatenotification node_helper. This is to prevent
|
- `--dry-run` Added option in fetch call within updatenotification node_helper. This is to prevent
|
||||||
MagicMirror from consuming any fetch result. Causes conflict with MMPM when attempting to check
|
MagicMirror² from consuming any fetch result. Causes conflict with MMPM when attempting to check
|
||||||
for updates to MagicMirror and/or MagicMirror modules.
|
for updates to MagicMirror² and/or MagicMirror² modules.
|
||||||
- Test coverage with Istanbul, run it with `npm run test:coverage`.
|
- Test coverage with Istanbul, run it with `npm run test:coverage`.
|
||||||
- Added lithuanian language.
|
- Added lithuanian language.
|
||||||
- Added support in weatherforecast for OpenWeather onecall API.
|
- Added support in weatherforecast for OpenWeather onecall API.
|
||||||
@ -349,7 +385,7 @@ Special thanks to the following contributors: @AndreKoepke, @andrezibaia, @bryan
|
|||||||
|
|
||||||
🚨 READ THIS BEFORE UPDATING 🚨
|
🚨 READ THIS BEFORE UPDATING 🚨
|
||||||
|
|
||||||
In the past years the project has grown a lot. This came with a huge downside: poor maintainability. If I let the project continue the way it was, it would eventually crash and burn. More important: I would completely lose the drive and interest to continue the project. Because of this the decision was made to simplify the core by removing all side features like automatic installers and support for exotic platforms. This release (2.11.0) is the first real release that will reflect (parts) of these changes. As a result of this, some things might break. So before you continue make sure to backup your installation. Your config, your modules or better yet: your full MagicMirror folder. In other words: update at your own risk.
|
In the past years the project has grown a lot. This came with a huge downside: poor maintainability. If I let the project continue the way it was, it would eventually crash and burn. More important: I would completely lose the drive and interest to continue the project. Because of this the decision was made to simplify the core by removing all side features like automatic installers and support for exotic platforms. This release (2.11.0) is the first real release that will reflect (parts) of these changes. As a result of this, some things might break. So before you continue make sure to backup your installation. Your config, your modules or better yet: your full MagicMirror² folder. In other words: update at your own risk.
|
||||||
|
|
||||||
For more information regarding this major change, please check issue [#1860](https://github.com/MichMich/MagicMirror/issues/1860).
|
For more information regarding this major change, please check issue [#1860](https://github.com/MichMich/MagicMirror/issues/1860).
|
||||||
|
|
||||||
@ -550,7 +586,7 @@ Fixed `package.json` version number.
|
|||||||
- Fixed unhandled error on bad git data in updatenotification module [#1285](https://github.com/MichMich/MagicMirror/issues/1285).
|
- Fixed unhandled error on bad git data in updatenotification module [#1285](https://github.com/MichMich/MagicMirror/issues/1285).
|
||||||
- Weather forecast now works with openweathermap in new weather module. Daily data are displayed, see issue [#1504](https://github.com/MichMich/MagicMirror/issues/1504).
|
- Weather forecast now works with openweathermap in new weather module. Daily data are displayed, see issue [#1504](https://github.com/MichMich/MagicMirror/issues/1504).
|
||||||
- Fixed analogue clock border display issue where non-black backgrounds used (previous fix for issue 611)
|
- Fixed analogue clock border display issue where non-black backgrounds used (previous fix for issue 611)
|
||||||
- Fixed compatibility issues caused when modules request different versions of Font Awesome, see issue [#1522](https://github.com/MichMich/MagicMirror/issues/1522). MagicMirror now uses [Font Awesome 5 with v4 shims included for backwards compatibility](https://fontawesome.com/how-to-use/on-the-web/setup/upgrading-from-version-4#shims).
|
- Fixed compatibility issues caused when modules request different versions of Font Awesome, see issue [#1522](https://github.com/MichMich/MagicMirror/issues/1522). MagicMirror² now uses [Font Awesome 5 with v4 shims included for backwards compatibility](https://fontawesome.com/how-to-use/on-the-web/setup/upgrading-from-version-4#shims).
|
||||||
- Installation script problems with raspbian
|
- Installation script problems with raspbian
|
||||||
- Calendar: only show repeating count if the event is actually repeating [#1534](https://github.com/MichMich/MagicMirror/pull/1534)
|
- Calendar: only show repeating count if the event is actually repeating [#1534](https://github.com/MichMich/MagicMirror/pull/1534)
|
||||||
- Calendar: Fix exdate handling when multiple values are specified (comma separated)
|
- Calendar: Fix exdate handling when multiple values are specified (comma separated)
|
||||||
@ -662,7 +698,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
|
|||||||
|
|
||||||
## [2.4.0] - 2018-07-01
|
## [2.4.0] - 2018-07-01
|
||||||
|
|
||||||
⚠️ **Warning:** This release includes an updated version of Electron. This requires a Raspberry Pi configuration change to allow the best performance and prevent the CPU from overheating. Please read the information on the [MagicMirror Wiki](https://github.com/michmich/magicmirror/wiki/configuring-the-raspberry-pi#enable-the-open-gl-driver-to-decrease-electrons-cpu-usage).
|
⚠️ **Warning:** This release includes an updated version of Electron. This requires a Raspberry Pi configuration change to allow the best performance and prevent the CPU from overheating. Please read the information on the [MagicMirror² Wiki](https://github.com/michmich/magicmirror/wiki/configuring-the-raspberry-pi#enable-the-open-gl-driver-to-decrease-electrons-cpu-usage).
|
||||||
|
|
||||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
|
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
|
||||||
|
|
||||||
@ -903,7 +939,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
|
|||||||
- Add use pm2 for manager process into Installer RaspberryPi script.
|
- Add use pm2 for manager process into Installer RaspberryPi script.
|
||||||
- Russian Translation.
|
- Russian Translation.
|
||||||
- Afrikaans Translation.
|
- Afrikaans Translation.
|
||||||
- Add postinstall script to notify user that MagicMirror installed successfully despite warnings from NPM.
|
- Add postinstall script to notify user that MagicMirror² installed successfully despite warnings from NPM.
|
||||||
- Init tests using mocha.
|
- Init tests using mocha.
|
||||||
- Option to use RegExp in Calendar's titleReplace.
|
- Option to use RegExp in Calendar's titleReplace.
|
||||||
- Hungarian Translation.
|
- Hungarian Translation.
|
||||||
@ -966,7 +1002,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
|
|||||||
- Add VSCode IntelliSense support.
|
- Add VSCode IntelliSense support.
|
||||||
- Module API: Add Visibility locking to module system. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules#visibility-locking) for more information.
|
- Module API: Add Visibility locking to module system. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules#visibility-locking) for more information.
|
||||||
- Module API: Method to overwrite the module's header. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules#getheader) for more information.
|
- Module API: Method to overwrite the module's header. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules#getheader) for more information.
|
||||||
- Module API: Option to define the minimum MagicMirror version to run a module. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules#requiresversion) for more information.
|
- Module API: Option to define the minimum MagicMirror² version to run a module. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules#requiresversion) for more information.
|
||||||
- Calendar module now broadcasts the event list to all other modules using the notification system. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules/default/calendar) for more information.
|
- Calendar module now broadcasts the event list to all other modules using the notification system. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules/default/calendar) for more information.
|
||||||
- Possibility to use the calendar feed as the source for the weather (currentweather & weatherforecast) location data. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules/default/weatherforecast) for more information.
|
- Possibility to use the calendar feed as the source for the weather (currentweather & weatherforecast) location data. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules/default/weatherforecast) for more information.
|
||||||
- Added option to show rain amount in the weatherforecast default module
|
- Added option to show rain amount in the weatherforecast default module
|
||||||
@ -1113,6 +1149,6 @@ It includes (but is not limited to) the following features:
|
|||||||
|
|
||||||
## [1.0.0] - 2014-02-16
|
## [1.0.0] - 2014-02-16
|
||||||
|
|
||||||
### Initial release of MagicMirror.
|
### Initial release of MagicMirror
|
||||||
|
|
||||||
This was part of the blogpost: [https://michaelteeuw.nl/post/83916869600/magic-mirror-part-vi-production-of-the](https://michaelteeuw.nl/post/83916869600/magic-mirror-part-vi-production-of-the)
|
This was part of the blogpost: [https://michaelteeuw.nl/post/83916869600/magic-mirror-part-vi-production-of-the](https://michaelteeuw.nl/post/83916869600/magic-mirror-part-vi-production-of-the)
|
||||||
|
@ -40,7 +40,7 @@ Contributions of all kinds are welcome, not only in the form of code but also wi
|
|||||||
- documentation
|
- documentation
|
||||||
- translations
|
- translations
|
||||||
|
|
||||||
For the full contribution guidelines, check out: [https://docs.magicmirror.builders/getting-started/contributing.html](https://docs.magicmirror.builders/getting-started/contributing.html)
|
For the full contribution guidelines, check out: [https://docs.magicmirror.builders/about/contributing.html](https://docs.magicmirror.builders/about/contributing.html)
|
||||||
|
|
||||||
## Enjoying MagicMirror? Consider a donation!
|
## Enjoying MagicMirror? Consider a donation!
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
/* Magic Mirror Config Sample
|
/* MagicMirror² Config Sample
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
*
|
*
|
||||||
* For more information on how you can configure this file
|
* For more information on how you can configure this file
|
||||||
* see https://docs.magicmirror.builders/getting-started/configuration.html#general
|
* see https://docs.magicmirror.builders/configuration/introduction.html
|
||||||
* and https://docs.magicmirror.builders/modules/configuration.html
|
* and https://docs.magicmirror.builders/modules/configuration.html
|
||||||
*/
|
*/
|
||||||
let config = {
|
let config = {
|
||||||
@ -14,7 +14,7 @@ let config = {
|
|||||||
// - "0.0.0.0", "::" to listen on any interface
|
// - "0.0.0.0", "::" to listen on any interface
|
||||||
// Default, when address config is left out or empty, is "localhost"
|
// Default, when address config is left out or empty, is "localhost"
|
||||||
port: 8080,
|
port: 8080,
|
||||||
basePath: "/", // The URL path where MagicMirror is hosted. If you are using a Reverse proxy
|
basePath: "/", // The URL path where MagicMirror² is hosted. If you are using a Reverse proxy
|
||||||
// you must set the sub path here. basePath must end with a /
|
// you must set the sub path here. basePath must end with a /
|
||||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], // Set [] to allow all IP addresses
|
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], // Set [] to allow all IP addresses
|
||||||
// or add a specific IPv4 of 192.168.1.5 :
|
// or add a specific IPv4 of 192.168.1.5 :
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Custom CSS Sample
|
/* MagicMirror² Custom CSS Sample
|
||||||
*
|
*
|
||||||
* Change color and fonts here.
|
* Change color and fonts here.
|
||||||
*
|
*
|
||||||
|
12
css/main.css
12
css/main.css
@ -138,6 +138,14 @@ sup {
|
|||||||
margin-bottom: var(--gap-modules);
|
margin-bottom: var(--gap-modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.module.hidden {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module:not(.hidden) {
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.region.bottom .module {
|
.region.bottom .module {
|
||||||
margin-top: var(--gap-modules);
|
margin-top: var(--gap-modules);
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
@ -170,10 +178,6 @@ sup {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.region.fullscreen * {
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.region.right {
|
.region.right {
|
||||||
right: 0;
|
right: 0;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
29
fonts/package-lock.json
generated
29
fonts/package-lock.json
generated
@ -7,20 +7,31 @@
|
|||||||
"name": "magicmirror-fonts",
|
"name": "magicmirror-fonts",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"roboto-fontface": "^0.10.0"
|
"@fontsource/roboto": "^4.5.5",
|
||||||
|
"@fontsource/roboto-condensed": "^4.5.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/roboto-fontface": {
|
"node_modules/@fontsource/roboto": {
|
||||||
"version": "0.10.0",
|
"version": "4.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-4.5.5.tgz",
|
||||||
"integrity": "sha512-OlwfYEgA2RdboZohpldlvJ1xngOins5d7ejqnIBWr9KaMxsnBqotpptRXTyfNRLnFpqzX6sTDt+X+a+6udnU8g=="
|
"integrity": "sha512-Pe1p+gAO6K0aLxBXlLoJRHVx352tVc/v/7DOnvM3t+FYXb+KUga9aCD1NpnDfd0kKnWXqrZyAXguyyFWDDuphw=="
|
||||||
|
},
|
||||||
|
"node_modules/@fontsource/roboto-condensed": {
|
||||||
|
"version": "4.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fontsource/roboto-condensed/-/roboto-condensed-4.5.6.tgz",
|
||||||
|
"integrity": "sha512-WDN0RlwXa3ADUDJxrB9HEIhrwYdJFLTNw4YemEYSeIPURU5DAHSx2VFXFv69PbM7kV8At5yMTp8bQkL5TBUP3w=="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"roboto-fontface": {
|
"@fontsource/roboto": {
|
||||||
"version": "0.10.0",
|
"version": "4.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-4.5.5.tgz",
|
||||||
"integrity": "sha512-OlwfYEgA2RdboZohpldlvJ1xngOins5d7ejqnIBWr9KaMxsnBqotpptRXTyfNRLnFpqzX6sTDt+X+a+6udnU8g=="
|
"integrity": "sha512-Pe1p+gAO6K0aLxBXlLoJRHVx352tVc/v/7DOnvM3t+FYXb+KUga9aCD1NpnDfd0kKnWXqrZyAXguyyFWDDuphw=="
|
||||||
|
},
|
||||||
|
"@fontsource/roboto-condensed": {
|
||||||
|
"version": "4.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fontsource/roboto-condensed/-/roboto-condensed-4.5.6.tgz",
|
||||||
|
"integrity": "sha512-WDN0RlwXa3ADUDJxrB9HEIhrwYdJFLTNw4YemEYSeIPURU5DAHSx2VFXFv69PbM7kV8At5yMTp8bQkL5TBUP3w=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "magicmirror-fonts",
|
"name": "magicmirror-fonts",
|
||||||
"description": "Package for fonts use by MagicMirror Core.",
|
"description": "Package for fonts use by MagicMirror² Core.",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/MichMich/MagicMirror.git"
|
"url": "git+https://github.com/MichMich/MagicMirror.git"
|
||||||
@ -10,6 +10,7 @@
|
|||||||
"url": "https://github.com/MichMich/MagicMirror/issues"
|
"url": "https://github.com/MichMich/MagicMirror/issues"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"roboto-fontface": "^0.10.0"
|
"@fontsource/roboto": "^4.5.5",
|
||||||
|
"@fontsource/roboto-condensed": "^4.5.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,57 +2,57 @@
|
|||||||
font-family: Roboto;
|
font-family: Roboto;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
src: local("Roboto Thin"), local("Roboto-Thin"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Thin.woff2") format("woff2"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Thin.woff") format("woff");
|
src: local("Roboto Thin"), local("Roboto-Thin"), url("node_modules/@fontsource/roboto/files/roboto-latin-100-normal.woff2") format("woff2"), url("node_modules/@fontsource/roboto/files/roboto-latin-100-normal.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Roboto Condensed";
|
font-family: "Roboto Condensed";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
src: local("Roboto Condensed Light"), local("RobotoCondensed-Light"), url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Light.woff2") format("woff2"),
|
src: local("Roboto Condensed Light"), local("RobotoCondensed-Light"), url("node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-300-normal.woff2") format("woff2"),
|
||||||
url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Light.woff") format("woff");
|
url("node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-300-normal.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Roboto Condensed";
|
font-family: "Roboto Condensed";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: local("Roboto Condensed"), local("RobotoCondensed-Regular"), url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Regular.woff2") format("woff2"),
|
src: local("Roboto Condensed"), local("RobotoCondensed-Regular"), url("node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-400-normal.woff2") format("woff2"),
|
||||||
url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Regular.woff") format("woff");
|
url("node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-400-normal.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Roboto Condensed";
|
font-family: "Roboto Condensed";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
src: local("Roboto Condensed Bold"), local("RobotoCondensed-Bold"), url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Bold.woff2") format("woff2"),
|
src: local("Roboto Condensed Bold"), local("RobotoCondensed-Bold"), url("node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-700-normal.woff2") format("woff2"),
|
||||||
url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Bold.woff") format("woff");
|
url("node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-700-normal.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Roboto;
|
font-family: Roboto;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: local("Roboto"), local("Roboto-Regular"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Regular.woff2") format("woff2"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Regular.woff") format("woff");
|
src: local("Roboto"), local("Roboto-Regular"), url("node_modules/@fontsource/roboto/files/roboto-latin-400-normal.woff2") format("woff2"), url("node_modules/@fontsource/roboto/files/roboto-latin-400-normal.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Roboto;
|
font-family: Roboto;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
src: local("Roboto Medium"), local("Roboto-Medium"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Medium.woff2") format("woff2"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Medium.woff") format("woff");
|
src: local("Roboto Medium"), local("Roboto-Medium"), url("node_modules/@fontsource/roboto/files/roboto-latin-500-normal.woff2") format("woff2"), url("node_modules/@fontsource/roboto/files/roboto-latin-500-normal.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Roboto;
|
font-family: Roboto;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
src: local("Roboto Bold"), local("Roboto-Bold"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Bold.woff2") format("woff2"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Bold.woff") format("woff");
|
src: local("Roboto Bold"), local("Roboto-Bold"), url("node_modules/@fontsource/roboto/files/roboto-latin-700-normal.woff2") format("woff2"), url("node_modules/@fontsource/roboto/files/roboto-latin-700-normal.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Roboto;
|
font-family: Roboto;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
src: local("Roboto Light"), local("Roboto-Light"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Light.woff2") format("woff2"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Light.woff") format("woff");
|
src: local("Roboto Light"), local("Roboto-Light"), url("node_modules/@fontsource/roboto/files/roboto-latin-300-normal.woff2") format("woff2"), url("node_modules/@fontsource/roboto/files/roboto-latin-300-normal.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* The Core App (Server)
|
* The Core App (Server)
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
@ -37,7 +37,7 @@ if (process.env.MM_PORT) {
|
|||||||
process.on("uncaughtException", function (err) {
|
process.on("uncaughtException", function (err) {
|
||||||
Log.error("Whoops! There was an uncaught exception...");
|
Log.error("Whoops! There was an uncaught exception...");
|
||||||
Log.error(err);
|
Log.error(err);
|
||||||
Log.error("MagicMirror will not quit, but it might be a good idea to check why this happened. Maybe no internet connection?");
|
Log.error("MagicMirror² will not quit, but it might be a good idea to check why this happened. Maybe no internet connection?");
|
||||||
Log.error("If you think this really is an issue, please open an issue on GitHub: https://github.com/MichMich/MagicMirror/issues");
|
Log.error("If you think this really is an issue, please open an issue on GitHub: https://github.com/MichMich/MagicMirror/issues");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ function App() {
|
|||||||
let m = new Module();
|
let m = new Module();
|
||||||
|
|
||||||
if (m.requiresVersion) {
|
if (m.requiresVersion) {
|
||||||
Log.log(`Check MagicMirror version for node helper '${moduleName}' - Minimum version: ${m.requiresVersion} - Current version: ${global.version}`);
|
Log.log(`Check MagicMirror² version for node helper '${moduleName}' - Minimum version: ${m.requiresVersion} - Current version: ${global.version}`);
|
||||||
if (cmpVersions(global.version, m.requiresVersion) >= 0) {
|
if (cmpVersions(global.version, m.requiresVersion) >= 0) {
|
||||||
Log.log("Version is ok!");
|
Log.log("Version is ok!");
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
*
|
*
|
||||||
* Check the configuration file for errors
|
* Check the configuration file for errors
|
||||||
*
|
*
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global mmPort */
|
/* global mmPort */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Config Defaults
|
* Config Defaults
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
@ -36,7 +36,7 @@ const defaults = {
|
|||||||
position: "upper_third",
|
position: "upper_third",
|
||||||
classes: "large thin",
|
classes: "large thin",
|
||||||
config: {
|
config: {
|
||||||
text: "Magic Mirror<sup>2</sup>"
|
text: "MagicMirror²"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -59,7 +59,7 @@ const defaults = {
|
|||||||
position: "middle_center",
|
position: "middle_center",
|
||||||
classes: "xsmall",
|
classes: "xsmall",
|
||||||
config: {
|
config: {
|
||||||
text: "If you get this message while your config file is already created,<br>" + "it probably contains an error. To validate your config file run in your MagicMirror directory<br>" + "<pre>npm run config:check</pre>"
|
text: "If you get this message while your config file is already created,<br>" + "it probably contains an error. To validate your config file run in your MagicMirror² directory<br>" + "<pre>npm run config:check</pre>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Deprecated Config Options List
|
/* MagicMirror² Deprecated Config Options List
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -8,6 +8,12 @@ const Log = require("logger");
|
|||||||
let config = process.env.config ? JSON.parse(process.env.config) : {};
|
let config = process.env.config ? JSON.parse(process.env.config) : {};
|
||||||
// Module to control application life.
|
// Module to control application life.
|
||||||
const app = electron.app;
|
const app = electron.app;
|
||||||
|
// If ELECTRON_DISABLE_GPU is set electron is startet with --disable-gpu flag.
|
||||||
|
// See https://www.electronjs.org/docs/latest/tutorial/offscreen-rendering for more info.
|
||||||
|
if (process.env.ELECTRON_DISABLE_GPU !== undefined) {
|
||||||
|
app.disableHardwareAcceleration();
|
||||||
|
}
|
||||||
|
|
||||||
// Module to create native browser window.
|
// Module to create native browser window.
|
||||||
const BrowserWindow = electron.BrowserWindow;
|
const BrowserWindow = electron.BrowserWindow;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global defaultModules, vendor */
|
/* global defaultModules, vendor */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module and File loaders.
|
* Module and File loaders.
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Log
|
* Log
|
||||||
*
|
*
|
||||||
* This logger is very simple, but needs to be extended.
|
* This logger is very simple, but needs to be extended.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global Loader, defaults, Translator */
|
/* global Loader, defaults, Translator */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Main System
|
* Main System
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
@ -245,6 +245,7 @@ const MM = (function () {
|
|||||||
if (moduleWrapper !== null) {
|
if (moduleWrapper !== null) {
|
||||||
moduleWrapper.style.transition = "opacity " + speed / 1000 + "s";
|
moduleWrapper.style.transition = "opacity " + speed / 1000 + "s";
|
||||||
moduleWrapper.style.opacity = 0;
|
moduleWrapper.style.opacity = 0;
|
||||||
|
moduleWrapper.classList.add("hidden");
|
||||||
|
|
||||||
clearTimeout(module.showHideTimer);
|
clearTimeout(module.showHideTimer);
|
||||||
module.showHideTimer = setTimeout(function () {
|
module.showHideTimer = setTimeout(function () {
|
||||||
@ -310,6 +311,7 @@ const MM = (function () {
|
|||||||
moduleWrapper.style.transition = "opacity " + speed / 1000 + "s";
|
moduleWrapper.style.transition = "opacity " + speed / 1000 + "s";
|
||||||
// Restore the position. See hideModule() for more info.
|
// Restore the position. See hideModule() for more info.
|
||||||
moduleWrapper.style.position = "static";
|
moduleWrapper.style.position = "static";
|
||||||
|
moduleWrapper.classList.remove("hidden");
|
||||||
|
|
||||||
updateWrapperStates();
|
updateWrapperStates();
|
||||||
|
|
||||||
@ -478,7 +480,7 @@ const MM = (function () {
|
|||||||
* Main init method.
|
* Main init method.
|
||||||
*/
|
*/
|
||||||
init: function () {
|
init: function () {
|
||||||
Log.info("Initializing MagicMirror.");
|
Log.info("Initializing MagicMirror².");
|
||||||
loadConfig();
|
loadConfig();
|
||||||
|
|
||||||
Log.setLogLevel(config.logLevel);
|
Log.setLogLevel(config.logLevel);
|
||||||
|
14
js/module.js
14
js/module.js
@ -1,6 +1,6 @@
|
|||||||
/* global Class, cloneObject, Loader, MMSocket, nunjucks, Translator */
|
/* global Class, cloneObject, Loader, MMSocket, nunjucks, Translator */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module Blueprint.
|
* Module Blueprint.
|
||||||
* @typedef {Object} Module
|
* @typedef {Object} Module
|
||||||
*
|
*
|
||||||
@ -12,7 +12,7 @@ const Module = Class.extend({
|
|||||||
* All methods (and properties) below can be subclassed. *
|
* All methods (and properties) below can be subclassed. *
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
|
|
||||||
// Set the minimum MagicMirror module version for this module.
|
// Set the minimum MagicMirror² module version for this module.
|
||||||
requiresVersion: "2.0.0",
|
requiresVersion: "2.0.0",
|
||||||
|
|
||||||
// Module config defaults.
|
// Module config defaults.
|
||||||
@ -74,7 +74,7 @@ const Module = Class.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the dom which needs to be displayed. This method is called by the Magic Mirror core.
|
* Generates the dom which needs to be displayed. This method is called by the MagicMirror² core.
|
||||||
* This method can to be subclassed if the module wants to display info on the mirror.
|
* This method can to be subclassed if the module wants to display info on the mirror.
|
||||||
* Alternatively, the getTemplate method could be subclassed.
|
* Alternatively, the getTemplate method could be subclassed.
|
||||||
*
|
*
|
||||||
@ -109,7 +109,7 @@ const Module = Class.extend({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the header string which needs to be displayed if a user has a header configured for this module.
|
* Generates the header string which needs to be displayed if a user has a header configured for this module.
|
||||||
* This method is called by the Magic Mirror core, but only if the user has configured a default header for the module.
|
* This method is called by the MagicMirror² core, but only if the user has configured a default header for the module.
|
||||||
* This method needs to be subclassed if the module wants to display modified headers on the mirror.
|
* This method needs to be subclassed if the module wants to display modified headers on the mirror.
|
||||||
*
|
*
|
||||||
* @returns {string} The header to display above the header.
|
* @returns {string} The header to display above the header.
|
||||||
@ -141,7 +141,7 @@ const Module = Class.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the Magic Mirror core when a notification arrives.
|
* Called by the MagicMirror² core when a notification arrives.
|
||||||
*
|
*
|
||||||
* @param {string} notification The identifier of the notification.
|
* @param {string} notification The identifier of the notification.
|
||||||
* @param {*} payload The payload of the notification.
|
* @param {*} payload The payload of the notification.
|
||||||
@ -434,7 +434,7 @@ const Module = Class.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merging MagicMirror (or other) default/config script by @bugsounet
|
* Merging MagicMirror² (or other) default/config script by @bugsounet
|
||||||
* Merge 2 objects or/with array
|
* Merge 2 objects or/with array
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
@ -498,7 +498,7 @@ Module.create = function (name) {
|
|||||||
|
|
||||||
Module.register = function (name, moduleDefinition) {
|
Module.register = function (name, moduleDefinition) {
|
||||||
if (moduleDefinition.requiresVersion) {
|
if (moduleDefinition.requiresVersion) {
|
||||||
Log.log("Check MagicMirror version for module '" + name + "' - Minimum version: " + moduleDefinition.requiresVersion + " - Current version: " + window.mmVersion);
|
Log.log("Check MagicMirror² version for module '" + name + "' - Minimum version: " + moduleDefinition.requiresVersion + " - Current version: " + window.mmVersion);
|
||||||
if (cmpVersions(window.mmVersion, moduleDefinition.requiresVersion) >= 0) {
|
if (cmpVersions(window.mmVersion, moduleDefinition.requiresVersion) >= 0) {
|
||||||
Log.log("Version is ok!");
|
Log.log("Version is ok!");
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Node Helper Superclass
|
* Node Helper Superclass
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
@ -23,7 +23,7 @@ const NodeHelper = Class.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
/* stop()
|
/* stop()
|
||||||
* Called when the MagicMirror server receives a `SIGINT`
|
* Called when the MagicMirror² server receives a `SIGINT`
|
||||||
* Close any open connections, stop any sub-processes and
|
* Close any open connections, stop any sub-processes and
|
||||||
* gracefully exit the module.
|
* gracefully exit the module.
|
||||||
*
|
*
|
||||||
|
33
js/server.js
33
js/server.js
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Server
|
* Server
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
@ -10,6 +10,7 @@ const path = require("path");
|
|||||||
const ipfilter = require("express-ipfilter").IpFilter;
|
const ipfilter = require("express-ipfilter").IpFilter;
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const helmet = require("helmet");
|
const helmet = require("helmet");
|
||||||
|
const fetch = require("node-fetch");
|
||||||
|
|
||||||
const Log = require("logger");
|
const Log = require("logger");
|
||||||
const Utils = require("./utils.js");
|
const Utils = require("./utils.js");
|
||||||
@ -61,13 +62,14 @@ function Server(config, callback) {
|
|||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
ipfilter(config.ipWhitelist, { mode: config.ipWhitelist.length === 0 ? "deny" : "allow", log: false })(req, res, function (err) {
|
ipfilter(config.ipWhitelist, { mode: config.ipWhitelist.length === 0 ? "deny" : "allow", log: false })(req, res, function (err) {
|
||||||
if (err === undefined) {
|
if (err === undefined) {
|
||||||
|
res.header("Access-Control-Allow-Origin", "*");
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
Log.log(err.message);
|
Log.log(err.message);
|
||||||
res.status(403).send("This device is not allowed to access your mirror. <br> Please check your config.js or config.js.sample to change this.");
|
res.status(403).send("This device is not allowed to access your mirror. <br> Please check your config.js or config.js.sample to change this.");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
app.use(helmet({ contentSecurityPolicy: false }));
|
app.use(helmet({ contentSecurityPolicy: false, crossOriginOpenerPolicy: false, crossOriginEmbedderPolicy: false, crossOriginResourcePolicy: false, originAgentCluster: false }));
|
||||||
|
|
||||||
app.use("/js", express.static(__dirname));
|
app.use("/js", express.static(__dirname));
|
||||||
|
|
||||||
@ -76,6 +78,33 @@ function Server(config, callback) {
|
|||||||
app.use(directory, express.static(path.resolve(global.root_path + directory)));
|
app.use(directory, express.static(path.resolve(global.root_path + directory)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.get("/cors", async function (req, res) {
|
||||||
|
// example: http://localhost:8080/cors?url=https://google.de
|
||||||
|
|
||||||
|
try {
|
||||||
|
const reg = "^/cors.+url=(.*)";
|
||||||
|
let url = "";
|
||||||
|
|
||||||
|
let match = new RegExp(reg, "g").exec(req.url);
|
||||||
|
if (!match) {
|
||||||
|
url = "invalid url: " + req.url;
|
||||||
|
Log.error(url);
|
||||||
|
res.send(url);
|
||||||
|
} else {
|
||||||
|
url = match[1];
|
||||||
|
Log.log("cors url: " + url);
|
||||||
|
const response = await fetch(url, { headers: { "User-Agent": "Mozilla/5.0 MagicMirror/" + global.version } });
|
||||||
|
const header = response.headers.get("Content-Type");
|
||||||
|
const data = await response.text();
|
||||||
|
if (header) res.set("Content-Type", header);
|
||||||
|
res.send(data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Log.error(error);
|
||||||
|
res.send(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.get("/version", function (req, res) {
|
app.get("/version", function (req, res) {
|
||||||
res.send(global.version);
|
res.send(global.version);
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global io */
|
/* global io */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* TODO add description
|
* TODO add description
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global translations */
|
/* global translations */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Translator (l10n)
|
* Translator (l10n)
|
||||||
*
|
*
|
||||||
* By Christopher Fenner https://github.com/CFenner
|
* By Christopher Fenner https://github.com/CFenner
|
||||||
@ -104,7 +104,7 @@ const Translator = (function () {
|
|||||||
* @param {Function} callback Function called when done.
|
* @param {Function} callback Function called when done.
|
||||||
*/
|
*/
|
||||||
load(module, file, isFallback, callback) {
|
load(module, file, isFallback, callback) {
|
||||||
Log.log(`${module.name} - Load translation${isFallback && " fallback"}: ${file}`);
|
Log.log(`${module.name} - Load translation${isFallback ? " fallback" : ""}: ${file}`);
|
||||||
|
|
||||||
if (this.translationsFallback[module.name]) {
|
if (this.translationsFallback[module.name]) {
|
||||||
callback();
|
callback();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Utils
|
* Utils
|
||||||
*
|
*
|
||||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Module: Alert
|
# Module: Alert
|
||||||
|
|
||||||
The alert module is one of the default modules of the MagicMirror. This module displays notifications from other modules.
|
The alert module is one of the default modules of the MagicMirror². This module displays notifications from other modules.
|
||||||
|
|
||||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/alert.html).
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/alert.html).
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global NotificationFx */
|
/* global NotificationFx */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: alert
|
* Module: alert
|
||||||
*
|
*
|
||||||
* By Paul-Vincent Roll https://paulvincentroll.com/
|
* By Paul-Vincent Roll https://paulvincentroll.com/
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
*
|
*
|
||||||
* Copyright 2014, Codrops
|
* Copyright 2014, Codrops
|
||||||
* https://tympanus.net/codrops/
|
* https://tympanus.net/codrops/
|
||||||
|
*
|
||||||
|
* @param {object} window The window object
|
||||||
*/
|
*/
|
||||||
(function (window) {
|
(function (window) {
|
||||||
/**
|
/**
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{% if imageUrl %}
|
{% if imageUrl %}
|
||||||
<img src="{{ imageUrl }}" height="{{ imageHeight }}" style="margin-bottom: 10px;"/>
|
<img src="{{ imageUrl }}" height="{{ imageHeight }}" style="margin-bottom: 10px;"/>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="bright fa fa-{{ imageFA }}" style='margin-bottom: 10px; font-size: {{ imageHeight }};'/></span>
|
<span class="bright fas fa-{{ imageFA }}" style='margin-bottom: 10px; font-size: {{ imageHeight }};'/></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<br/>
|
<br/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"sysTitle": "MagicMirror нотификация",
|
"sysTitle": "MagicMirror² нотификация",
|
||||||
"welcome": "Добре дошли, стартирането беше успешно"
|
"welcome": "Добре дошли, стартирането беше успешно"
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"sysTitle": "MagicMirror Notifikation",
|
"sysTitle": "MagicMirror² Notifikation",
|
||||||
"welcome": "Velkommen, modulet er succesfuldt startet!"
|
"welcome": "Velkommen, modulet er succesfuldt startet!"
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"sysTitle": "MagicMirror Benachrichtigung",
|
"sysTitle": "MagicMirror² Benachrichtigung",
|
||||||
"welcome": "Willkommen, Start war erfolgreich!"
|
"welcome": "Willkommen, Start war erfolgreich!"
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"sysTitle": "MagicMirror Notification",
|
"sysTitle": "MagicMirror² Notification",
|
||||||
"welcome": "Welcome, start was successful!"
|
"welcome": "Welcome, start was successful!"
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"sysTitle": "MagicMirror Notificaciones",
|
"sysTitle": "MagicMirror² Notificaciones",
|
||||||
"welcome": "Bienvenido, ¡se iniciado correctamente!"
|
"welcome": "Bienvenido, ¡se iniciado correctamente!"
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"sysTitle": "MagicMirror Notification",
|
"sysTitle": "MagicMirror² Notification",
|
||||||
"welcome": "Bienvenue, le démarrage a été un succès!"
|
"welcome": "Bienvenue, le démarrage a été un succès!"
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"sysTitle": "MagicMirror értesítés",
|
"sysTitle": "MagicMirror² értesítés",
|
||||||
"welcome": "Üdvözöljük, indulás sikeres!"
|
"welcome": "Üdvözöljük, indulás sikeres!"
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"sysTitle": "MagicMirror Notificatie",
|
"sysTitle": "MagicMirror² Notificatie",
|
||||||
"welcome": "Welkom, Succesvol gestart!"
|
"welcome": "Welkom, Succesvol gestart!"
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"sysTitle": "MagicMirror Уведомление",
|
"sysTitle": "MagicMirror² Уведомление",
|
||||||
"welcome": "Добро пожаловать, старт был успешным!"
|
"welcome": "Добро пожаловать, старт был успешным!"
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Module: Calendar
|
# Module: Calendar
|
||||||
|
|
||||||
The `calendar` module is one of the default modules of the MagicMirror.
|
The `calendar` module is one of the default modules of the MagicMirror².
|
||||||
This module displays events from a public .ical calendar. It can combine multiple calendars.
|
This module displays events from a public .ical calendar. It can combine multiple calendars.
|
||||||
|
|
||||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/calendar.html).
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/calendar.html).
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global cloneObject */
|
/* global cloneObject */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Calendar
|
* Module: Calendar
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
@ -13,7 +13,7 @@ Module.register("calendar", {
|
|||||||
maximumNumberOfDays: 365,
|
maximumNumberOfDays: 365,
|
||||||
limitDays: 0, // Limit the number of days shown, 0 = no limit
|
limitDays: 0, // Limit the number of days shown, 0 = no limit
|
||||||
displaySymbol: true,
|
displaySymbol: true,
|
||||||
defaultSymbol: "calendar", // Fontawesome Symbol see https://fontawesome.com/cheatsheet?from=io
|
defaultSymbol: "calendar-alt", // Fontawesome Symbol see https://fontawesome.com/cheatsheet?from=io
|
||||||
showLocation: false,
|
showLocation: false,
|
||||||
displayRepeatingCountTitle: false,
|
displayRepeatingCountTitle: false,
|
||||||
defaultRepeatingCountTitle: "",
|
defaultRepeatingCountTitle: "",
|
||||||
@ -43,7 +43,7 @@ Module.register("calendar", {
|
|||||||
tableClass: "small",
|
tableClass: "small",
|
||||||
calendars: [
|
calendars: [
|
||||||
{
|
{
|
||||||
symbol: "calendar",
|
symbol: "calendar-alt",
|
||||||
url: "https://www.calendarlabs.com/templates/ical/US-Holidays.ics"
|
url: "https://www.calendarlabs.com/templates/ical/US-Holidays.ics"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -164,7 +164,7 @@ Module.register("calendar", {
|
|||||||
const oneHour = oneMinute * 60;
|
const oneHour = oneMinute * 60;
|
||||||
const oneDay = oneHour * 24;
|
const oneDay = oneHour * 24;
|
||||||
|
|
||||||
const events = this.createEventList();
|
const events = this.createEventList(true);
|
||||||
const wrapper = document.createElement("table");
|
const wrapper = document.createElement("table");
|
||||||
wrapper.className = this.config.tableClass;
|
wrapper.className = this.config.tableClass;
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ Module.register("calendar", {
|
|||||||
const symbols = this.symbolsForEvent(event);
|
const symbols = this.symbolsForEvent(event);
|
||||||
symbols.forEach((s, index) => {
|
symbols.forEach((s, index) => {
|
||||||
const symbol = document.createElement("span");
|
const symbol = document.createElement("span");
|
||||||
symbol.className = "fa fa-fw fa-" + s;
|
symbol.className = "fas fa-fw fa-" + s;
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
symbol.style.paddingLeft = "5px";
|
symbol.style.paddingLeft = "5px";
|
||||||
}
|
}
|
||||||
@ -329,8 +329,7 @@ Module.register("calendar", {
|
|||||||
//subtract one second so that fullDayEvents end at 23:59:59, and not at 0:00:00 one the next day
|
//subtract one second so that fullDayEvents end at 23:59:59, and not at 0:00:00 one the next day
|
||||||
event.endDate -= oneSecond;
|
event.endDate -= oneSecond;
|
||||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat));
|
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat));
|
||||||
}
|
} else if (this.config.getRelative > 0 && event.startDate < now) {
|
||||||
if (this.config.getRelative > 0 && event.startDate < now) {
|
|
||||||
// Ongoing and getRelative is set
|
// Ongoing and getRelative is set
|
||||||
timeWrapper.innerHTML = this.capFirst(
|
timeWrapper.innerHTML = this.capFirst(
|
||||||
this.translate("RUNNING", {
|
this.translate("RUNNING", {
|
||||||
@ -477,9 +476,10 @@ Module.register("calendar", {
|
|||||||
/**
|
/**
|
||||||
* Creates the sorted list of all events.
|
* Creates the sorted list of all events.
|
||||||
*
|
*
|
||||||
|
* @param {boolean} limitNumberOfEntries Whether to filter returned events for display.
|
||||||
* @returns {object[]} Array with events.
|
* @returns {object[]} Array with events.
|
||||||
*/
|
*/
|
||||||
createEventList: function () {
|
createEventList: function (limitNumberOfEntries) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const today = moment().startOf("day");
|
const today = moment().startOf("day");
|
||||||
const future = moment().startOf("day").add(this.config.maximumNumberOfDays, "days").toDate();
|
const future = moment().startOf("day").add(this.config.maximumNumberOfDays, "days").toDate();
|
||||||
@ -490,7 +490,7 @@ Module.register("calendar", {
|
|||||||
for (const e in calendar) {
|
for (const e in calendar) {
|
||||||
const event = JSON.parse(JSON.stringify(calendar[e])); // clone object
|
const event = JSON.parse(JSON.stringify(calendar[e])); // clone object
|
||||||
|
|
||||||
if (event.endDate < now) {
|
if (event.endDate < now && limitNumberOfEntries) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (this.config.hidePrivate) {
|
if (this.config.hidePrivate) {
|
||||||
@ -499,7 +499,7 @@ Module.register("calendar", {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.config.hideOngoing) {
|
if (this.config.hideOngoing && limitNumberOfEntries) {
|
||||||
if (event.startDate < now) {
|
if (event.startDate < now) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -548,6 +548,10 @@ Module.register("calendar", {
|
|||||||
return a.startDate - b.startDate;
|
return a.startDate - b.startDate;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!limitNumberOfEntries) {
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
// Limit the number of days displayed
|
// Limit the number of days displayed
|
||||||
// If limitDays is set > 0, limit display to that number of days
|
// If limitDays is set > 0, limit display to that number of days
|
||||||
if (this.config.limitDays > 0) {
|
if (this.config.limitDays > 0) {
|
||||||
@ -835,22 +839,14 @@ Module.register("calendar", {
|
|||||||
* The all events available in one array, sorted on startdate.
|
* The all events available in one array, sorted on startdate.
|
||||||
*/
|
*/
|
||||||
broadcastEvents: function () {
|
broadcastEvents: function () {
|
||||||
const eventList = [];
|
const eventList = this.createEventList(false);
|
||||||
for (const url in this.calendarData) {
|
for (const event of eventList) {
|
||||||
for (const ev of this.calendarData[url]) {
|
event.symbol = this.symbolsForEvent(event);
|
||||||
const event = cloneObject(ev);
|
event.calendarName = this.calendarNameForUrl(event.url);
|
||||||
event.symbol = this.symbolsForEvent(event);
|
event.color = this.colorForUrl(event.url);
|
||||||
event.calendarName = this.calendarNameForUrl(url);
|
delete event.url;
|
||||||
event.color = this.colorForUrl(url);
|
|
||||||
delete event.url;
|
|
||||||
eventList.push(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eventList.sort(function (a, b) {
|
|
||||||
return a.startDate - b.startDate;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.sendNotification("CALENDAR_EVENTS", eventList);
|
this.sendNotification("CALENDAR_EVENTS", eventList);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Node Helper: Calendar - CalendarFetcher
|
* Node Helper: Calendar - CalendarFetcher
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Calendar Util Methods
|
* Calendar Util Methods
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
@ -470,7 +470,7 @@ const CalendarUtils = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Adjust start date so multiple day events will be displayed as happening today even though they started some days ago already
|
// Adjust start date so multiple day events will be displayed as happening today even though they started some days ago already
|
||||||
if (fullDayEvent && startDate <= today) {
|
if (fullDayEvent && startDate <= today && endDate > today) {
|
||||||
startDate = moment(today);
|
startDate = moment(today);
|
||||||
}
|
}
|
||||||
// if the start and end are the same, then make end the 'end of day' value (start is at 00:00:00)
|
// if the start and end are the same, then make end the 'end of day' value (start is at 00:00:00)
|
||||||
@ -498,23 +498,7 @@ const CalendarUtils = {
|
|||||||
return a.startDate - b.startDate;
|
return a.startDate - b.startDate;
|
||||||
});
|
});
|
||||||
|
|
||||||
// include up to maximumEntries current or upcoming events
|
return newEvents;
|
||||||
// If past events should be included, include all past events
|
|
||||||
const now = moment();
|
|
||||||
let entries = 0;
|
|
||||||
let events = [];
|
|
||||||
for (let ne of newEvents) {
|
|
||||||
if (moment(ne.endDate, "x").isBefore(now)) {
|
|
||||||
if (config.includePastEvents) events.push(ne);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
entries++;
|
|
||||||
// If max events has been saved, skip the rest
|
|
||||||
if (entries > config.maximumEntries) break;
|
|
||||||
events.push(ne);
|
|
||||||
}
|
|
||||||
|
|
||||||
return events;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* CalendarFetcher Tester
|
/* CalendarFetcher Tester
|
||||||
* use this script with `node debug.js` to test the fetcher without the need
|
* use this script with `node debug.js` to test the fetcher without the need
|
||||||
* of starting the MagicMirror core. Adjust the values below to your desire.
|
* of starting the MagicMirror² core. Adjust the values below to your desire.
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Node Helper: Calendar
|
* Node Helper: Calendar
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Module: Clock
|
# Module: Clock
|
||||||
|
|
||||||
The `clock` module is one of the default modules of the MagicMirror.
|
The `clock` module is one of the default modules of the MagicMirror².
|
||||||
This module displays the current date and time. The information will be updated realtime.
|
This module displays the current date and time. The information will be updated realtime.
|
||||||
|
|
||||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/clock.html).
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/clock.html).
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global SunCalc */
|
/* global SunCalc */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Clock
|
* Module: Clock
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
@ -199,13 +199,13 @@ Module.register("clock", {
|
|||||||
sunWrapper.innerHTML =
|
sunWrapper.innerHTML =
|
||||||
'<span class="' +
|
'<span class="' +
|
||||||
(isVisible ? "bright" : "") +
|
(isVisible ? "bright" : "") +
|
||||||
'"><i class="fa fa-sun-o" aria-hidden="true"></i> ' +
|
'"><i class="fas fa-sun" aria-hidden="true"></i> ' +
|
||||||
untilNextEventString +
|
untilNextEventString +
|
||||||
"</span>" +
|
"</span>" +
|
||||||
'<span><i class="fa fa-arrow-up" aria-hidden="true"></i> ' +
|
'<span><i class="fas fa-arrow-up" aria-hidden="true"></i> ' +
|
||||||
formatTime(this.config, sunTimes.sunrise) +
|
formatTime(this.config, sunTimes.sunrise) +
|
||||||
"</span>" +
|
"</span>" +
|
||||||
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i> ' +
|
'<span><i class="fas fa-arrow-down" aria-hidden="true"></i> ' +
|
||||||
formatTime(this.config, sunTimes.sunset) +
|
formatTime(this.config, sunTimes.sunset) +
|
||||||
"</span>";
|
"</span>";
|
||||||
digitalWrapper.appendChild(sunWrapper);
|
digitalWrapper.appendChild(sunWrapper);
|
||||||
@ -230,13 +230,13 @@ Module.register("clock", {
|
|||||||
moonWrapper.innerHTML =
|
moonWrapper.innerHTML =
|
||||||
'<span class="' +
|
'<span class="' +
|
||||||
(isVisible ? "bright" : "") +
|
(isVisible ? "bright" : "") +
|
||||||
'"><i class="fa fa-moon-o" aria-hidden="true"></i> ' +
|
'"><i class="fas fa-moon" aria-hidden="true"></i> ' +
|
||||||
illuminatedFractionString +
|
illuminatedFractionString +
|
||||||
"</span>" +
|
"</span>" +
|
||||||
'<span><i class="fa fa-arrow-up" aria-hidden="true"></i> ' +
|
'<span><i class="fas fa-arrow-up" aria-hidden="true"></i> ' +
|
||||||
(moonRise ? formatTime(this.config, moonRise) : "...") +
|
(moonRise ? formatTime(this.config, moonRise) : "...") +
|
||||||
"</span>" +
|
"</span>" +
|
||||||
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i> ' +
|
'<span><i class="fas fa-arrow-down" aria-hidden="true"></i> ' +
|
||||||
(moonSet ? formatTime(this.config, moonSet) : "...") +
|
(moonSet ? formatTime(this.config, moonSet) : "...") +
|
||||||
"</span>";
|
"</span>";
|
||||||
digitalWrapper.appendChild(moonWrapper);
|
digitalWrapper.appendChild(moonWrapper);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Module: Compliments
|
# Module: Compliments
|
||||||
|
|
||||||
The `compliments` module is one of the default modules of the MagicMirror.
|
The `compliments` module is one of the default modules of the MagicMirror².
|
||||||
This module displays a random compliment.
|
This module displays a random compliment.
|
||||||
|
|
||||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/compliments.html).
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/compliments.html).
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Compliments
|
* Module: Compliments
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
# Module: Current Weather
|
|
||||||
|
|
||||||
> :warning: **This module is deprecated in favor of the [weather](https://docs.magicmirror.builders/modules/weather.html) module.**
|
|
||||||
|
|
||||||
The `currentweather` module is one of the default modules of the MagicMirror.
|
|
||||||
This module displays the current weather, including the windspeed, the sunset or sunrise time, the temperature and an icon to display the current conditions.
|
|
||||||
|
|
||||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/currentweather.html).
|
|
@ -1,15 +0,0 @@
|
|||||||
.currentweather .weathericon,
|
|
||||||
.currentweather .fa-home {
|
|
||||||
font-size: 75%;
|
|
||||||
line-height: 65px;
|
|
||||||
display: inline-block;
|
|
||||||
transform: translate(0, -3px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.currentweather .humidityIcon {
|
|
||||||
padding-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.currentweather .humidity-padding {
|
|
||||||
padding-bottom: 6px;
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: CurrentWeather
|
* Module: CurrentWeather
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
@ -9,592 +9,25 @@
|
|||||||
* This module is deprecated. Any additional feature will no longer be merged.
|
* This module is deprecated. Any additional feature will no longer be merged.
|
||||||
*/
|
*/
|
||||||
Module.register("currentweather", {
|
Module.register("currentweather", {
|
||||||
// Default module config.
|
|
||||||
defaults: {
|
|
||||||
location: false,
|
|
||||||
locationID: false,
|
|
||||||
appid: "",
|
|
||||||
units: config.units,
|
|
||||||
updateInterval: 10 * 60 * 1000, // every 10 minutes
|
|
||||||
animationSpeed: 1000,
|
|
||||||
timeFormat: config.timeFormat,
|
|
||||||
showPeriod: true,
|
|
||||||
showPeriodUpper: false,
|
|
||||||
showWindDirection: true,
|
|
||||||
showWindDirectionAsArrow: false,
|
|
||||||
useBeaufort: true,
|
|
||||||
useKMPHwind: false,
|
|
||||||
lang: config.language,
|
|
||||||
decimalSymbol: ".",
|
|
||||||
showHumidity: false,
|
|
||||||
showSun: true,
|
|
||||||
degreeLabel: false,
|
|
||||||
showIndoorTemperature: false,
|
|
||||||
showIndoorHumidity: false,
|
|
||||||
showFeelsLike: true,
|
|
||||||
|
|
||||||
initialLoadDelay: 0, // 0 seconds delay
|
|
||||||
retryDelay: 2500,
|
|
||||||
|
|
||||||
apiVersion: "2.5",
|
|
||||||
apiBase: "https://api.openweathermap.org/data/",
|
|
||||||
weatherEndpoint: "weather",
|
|
||||||
|
|
||||||
appendLocationNameToHeader: true,
|
|
||||||
useLocationAsHeader: false,
|
|
||||||
|
|
||||||
calendarClass: "calendar",
|
|
||||||
tableClass: "large",
|
|
||||||
|
|
||||||
onlyTemp: false,
|
|
||||||
hideTemp: false,
|
|
||||||
roundTemp: false,
|
|
||||||
|
|
||||||
iconTable: {
|
|
||||||
"01d": "day-sunny",
|
|
||||||
"02d": "day-cloudy",
|
|
||||||
"03d": "cloudy",
|
|
||||||
"04d": "cloudy-windy",
|
|
||||||
"09d": "showers",
|
|
||||||
"10d": "rain",
|
|
||||||
"11d": "thunderstorm",
|
|
||||||
"13d": "snow",
|
|
||||||
"50d": "fog",
|
|
||||||
"01n": "night-clear",
|
|
||||||
"02n": "night-cloudy",
|
|
||||||
"03n": "night-cloudy",
|
|
||||||
"04n": "night-cloudy",
|
|
||||||
"09n": "night-showers",
|
|
||||||
"10n": "night-rain",
|
|
||||||
"11n": "night-thunderstorm",
|
|
||||||
"13n": "night-snow",
|
|
||||||
"50n": "night-alt-cloudy-windy"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// create a variable for the first upcoming calendar event. Used if no location is specified.
|
|
||||||
firstEvent: false,
|
|
||||||
|
|
||||||
// create a variable to hold the location name based on the API result.
|
|
||||||
fetchedLocationName: "",
|
|
||||||
|
|
||||||
// Define required scripts.
|
|
||||||
getScripts: function () {
|
|
||||||
return ["moment.js"];
|
|
||||||
},
|
|
||||||
|
|
||||||
// Define required scripts.
|
|
||||||
getStyles: function () {
|
|
||||||
return ["weather-icons.css", "currentweather.css"];
|
|
||||||
},
|
|
||||||
|
|
||||||
// Define required translations.
|
|
||||||
getTranslations: function () {
|
|
||||||
// The translations for the default modules are defined in the core translation files.
|
|
||||||
// Therefor we can just return false. Otherwise we should have returned a dictionary.
|
|
||||||
// If you're trying to build your own module including translations, check out the documentation.
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Define start sequence.
|
// Define start sequence.
|
||||||
start: function () {
|
start: function () {
|
||||||
Log.info("Starting module: " + this.name);
|
Log.info("Starting module: " + this.name);
|
||||||
|
|
||||||
// Set locale.
|
|
||||||
moment.locale(config.language);
|
|
||||||
|
|
||||||
this.windSpeed = null;
|
|
||||||
this.windDirection = null;
|
|
||||||
this.windDeg = null;
|
|
||||||
this.sunriseSunsetTime = null;
|
|
||||||
this.sunriseSunsetIcon = null;
|
|
||||||
this.temperature = null;
|
|
||||||
this.indoorTemperature = null;
|
|
||||||
this.indoorHumidity = null;
|
|
||||||
this.weatherType = null;
|
|
||||||
this.feelsLike = null;
|
|
||||||
this.loaded = false;
|
|
||||||
this.scheduleUpdate(this.config.initialLoadDelay);
|
|
||||||
},
|
|
||||||
|
|
||||||
// add extra information of current weather
|
|
||||||
// windDirection, humidity, sunrise and sunset
|
|
||||||
addExtraInfoWeather: function (wrapper) {
|
|
||||||
var small = document.createElement("div");
|
|
||||||
small.className = "normal medium";
|
|
||||||
|
|
||||||
var windIcon = document.createElement("span");
|
|
||||||
windIcon.className = "wi wi-strong-wind dimmed";
|
|
||||||
small.appendChild(windIcon);
|
|
||||||
|
|
||||||
var windSpeed = document.createElement("span");
|
|
||||||
windSpeed.innerHTML = " " + this.windSpeed;
|
|
||||||
small.appendChild(windSpeed);
|
|
||||||
|
|
||||||
if (this.config.showWindDirection) {
|
|
||||||
var windDirection = document.createElement("sup");
|
|
||||||
if (this.config.showWindDirectionAsArrow) {
|
|
||||||
if (this.windDeg !== null) {
|
|
||||||
windDirection.innerHTML = ' <i class="fa fa-long-arrow-down" style="transform:rotate(' + this.windDeg + 'deg);"></i> ';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
windDirection.innerHTML = " " + this.translate(this.windDirection);
|
|
||||||
}
|
|
||||||
small.appendChild(windDirection);
|
|
||||||
}
|
|
||||||
var spacer = document.createElement("span");
|
|
||||||
spacer.innerHTML = " ";
|
|
||||||
small.appendChild(spacer);
|
|
||||||
|
|
||||||
if (this.config.showHumidity) {
|
|
||||||
var humidity = document.createElement("span");
|
|
||||||
humidity.innerHTML = this.humidity;
|
|
||||||
|
|
||||||
var supspacer = document.createElement("sup");
|
|
||||||
supspacer.innerHTML = " ";
|
|
||||||
|
|
||||||
var humidityIcon = document.createElement("sup");
|
|
||||||
humidityIcon.className = "wi wi-humidity humidityIcon";
|
|
||||||
humidityIcon.innerHTML = " ";
|
|
||||||
|
|
||||||
small.appendChild(humidity);
|
|
||||||
small.appendChild(supspacer);
|
|
||||||
small.appendChild(humidityIcon);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.showSun) {
|
|
||||||
var sunriseSunsetIcon = document.createElement("span");
|
|
||||||
sunriseSunsetIcon.className = "wi dimmed " + this.sunriseSunsetIcon;
|
|
||||||
small.appendChild(sunriseSunsetIcon);
|
|
||||||
|
|
||||||
var sunriseSunsetTime = document.createElement("span");
|
|
||||||
sunriseSunsetTime.innerHTML = " " + this.sunriseSunsetTime;
|
|
||||||
small.appendChild(sunriseSunsetTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
wrapper.appendChild(small);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Override dom generator.
|
// Override dom generator.
|
||||||
getDom: function () {
|
getDom: function () {
|
||||||
var wrapper = document.createElement("div");
|
var wrapper = document.createElement("div");
|
||||||
wrapper.className = this.config.tableClass;
|
wrapper.className = this.config.tableClass;
|
||||||
|
wrapper.innerHTML =
|
||||||
if (this.config.appid === "") {
|
"<style>text-decoration: none</style>" +
|
||||||
wrapper.innerHTML = "Please set the correct openweather <i>appid</i> in the config for module: " + this.name + ".";
|
"This module is deprecated since release v2.15 and removed with v2.19." +
|
||||||
wrapper.className = "dimmed light small";
|
'<br>Please use the `weather` module as replacement, more info in the <a href="https://docs.magicmirror.builders/modules/weather.html" style="color: #ffffff">documentation</a>.';
|
||||||
return wrapper;
|
wrapper.className = "dimmed light small";
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.loaded) {
|
|
||||||
wrapper.innerHTML = this.translate("LOADING");
|
|
||||||
wrapper.className = "dimmed light small";
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.onlyTemp === false) {
|
|
||||||
this.addExtraInfoWeather(wrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
var large = document.createElement("div");
|
|
||||||
large.className = "light";
|
|
||||||
|
|
||||||
var degreeLabel = "";
|
|
||||||
if (this.config.units === "metric" || this.config.units === "imperial") {
|
|
||||||
degreeLabel += "°";
|
|
||||||
}
|
|
||||||
if (this.config.degreeLabel) {
|
|
||||||
switch (this.config.units) {
|
|
||||||
case "metric":
|
|
||||||
degreeLabel += "C";
|
|
||||||
break;
|
|
||||||
case "imperial":
|
|
||||||
degreeLabel += "F";
|
|
||||||
break;
|
|
||||||
case "default":
|
|
||||||
degreeLabel += "K";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.decimalSymbol === "") {
|
|
||||||
this.config.decimalSymbol = ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.hideTemp === false) {
|
|
||||||
var weatherIcon = document.createElement("span");
|
|
||||||
weatherIcon.className = "wi weathericon wi-" + this.weatherType;
|
|
||||||
large.appendChild(weatherIcon);
|
|
||||||
|
|
||||||
var temperature = document.createElement("span");
|
|
||||||
temperature.className = "bright";
|
|
||||||
temperature.innerHTML = " " + this.temperature.replace(".", this.config.decimalSymbol) + degreeLabel;
|
|
||||||
large.appendChild(temperature);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.showIndoorTemperature && this.indoorTemperature) {
|
|
||||||
var indoorIcon = document.createElement("span");
|
|
||||||
indoorIcon.className = "fa fa-home";
|
|
||||||
large.appendChild(indoorIcon);
|
|
||||||
|
|
||||||
var indoorTemperatureElem = document.createElement("span");
|
|
||||||
indoorTemperatureElem.className = "bright";
|
|
||||||
indoorTemperatureElem.innerHTML = " " + this.indoorTemperature.replace(".", this.config.decimalSymbol) + degreeLabel;
|
|
||||||
large.appendChild(indoorTemperatureElem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.showIndoorHumidity && this.indoorHumidity) {
|
|
||||||
var indoorHumidityIcon = document.createElement("span");
|
|
||||||
indoorHumidityIcon.className = "fa fa-tint";
|
|
||||||
large.appendChild(indoorHumidityIcon);
|
|
||||||
|
|
||||||
var indoorHumidityElem = document.createElement("span");
|
|
||||||
indoorHumidityElem.className = "bright";
|
|
||||||
indoorHumidityElem.innerHTML = " " + this.indoorHumidity + "%";
|
|
||||||
large.appendChild(indoorHumidityElem);
|
|
||||||
}
|
|
||||||
|
|
||||||
wrapper.appendChild(large);
|
|
||||||
|
|
||||||
if (this.config.showFeelsLike && this.config.onlyTemp === false) {
|
|
||||||
var small = document.createElement("div");
|
|
||||||
small.className = "normal medium";
|
|
||||||
|
|
||||||
var feelsLike = document.createElement("span");
|
|
||||||
feelsLike.className = "dimmed";
|
|
||||||
feelsLike.innerHTML = this.translate("FEELS", {
|
|
||||||
DEGREE: this.feelsLike + degreeLabel
|
|
||||||
});
|
|
||||||
small.appendChild(feelsLike);
|
|
||||||
|
|
||||||
wrapper.appendChild(small);
|
|
||||||
}
|
|
||||||
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Override getHeader method.
|
// Override getHeader method.
|
||||||
getHeader: function () {
|
getHeader: function () {
|
||||||
if (this.config.useLocationAsHeader && this.config.location !== false) {
|
return "deprecated currentweather";
|
||||||
return this.config.location;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.appendLocationNameToHeader) {
|
|
||||||
if (this.data.header) return this.data.header + " " + this.fetchedLocationName;
|
|
||||||
else return this.fetchedLocationName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.data.header ? this.data.header : "";
|
|
||||||
},
|
|
||||||
|
|
||||||
// Override notification handler.
|
|
||||||
notificationReceived: function (notification, payload, sender) {
|
|
||||||
if (notification === "DOM_OBJECTS_CREATED") {
|
|
||||||
if (this.config.appendLocationNameToHeader) {
|
|
||||||
this.hide(0, { lockString: this.identifier });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (notification === "CALENDAR_EVENTS") {
|
|
||||||
var senderClasses = sender.data.classes.toLowerCase().split(" ");
|
|
||||||
if (senderClasses.indexOf(this.config.calendarClass.toLowerCase()) !== -1) {
|
|
||||||
this.firstEvent = false;
|
|
||||||
|
|
||||||
for (var e in payload) {
|
|
||||||
var event = payload[e];
|
|
||||||
if (event.location || event.geo) {
|
|
||||||
this.firstEvent = event;
|
|
||||||
//Log.log("First upcoming event with location: ", event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (notification === "INDOOR_TEMPERATURE") {
|
|
||||||
this.indoorTemperature = this.roundValue(payload);
|
|
||||||
this.updateDom(this.config.animationSpeed);
|
|
||||||
}
|
|
||||||
if (notification === "INDOOR_HUMIDITY") {
|
|
||||||
this.indoorHumidity = this.roundValue(payload);
|
|
||||||
this.updateDom(this.config.animationSpeed);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/* updateWeather(compliments)
|
|
||||||
* Requests new data from openweather.org.
|
|
||||||
* Calls processWeather on succesfull response.
|
|
||||||
*/
|
|
||||||
updateWeather: function () {
|
|
||||||
if (this.config.appid === "") {
|
|
||||||
Log.error("CurrentWeather: APPID not set!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var url = this.config.apiBase + this.config.apiVersion + "/" + this.config.weatherEndpoint + this.getParams();
|
|
||||||
var self = this;
|
|
||||||
var retry = true;
|
|
||||||
|
|
||||||
var weatherRequest = new XMLHttpRequest();
|
|
||||||
weatherRequest.open("GET", url, true);
|
|
||||||
weatherRequest.onreadystatechange = function () {
|
|
||||||
if (this.readyState === 4) {
|
|
||||||
if (this.status === 200) {
|
|
||||||
self.processWeather(JSON.parse(this.response));
|
|
||||||
} else if (this.status === 401) {
|
|
||||||
self.updateDom(self.config.animationSpeed);
|
|
||||||
|
|
||||||
Log.error(self.name + ": Incorrect APPID.");
|
|
||||||
retry = true;
|
|
||||||
} else {
|
|
||||||
Log.error(self.name + ": Could not load weather.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retry) {
|
|
||||||
self.scheduleUpdate(self.loaded ? -1 : self.config.retryDelay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
weatherRequest.send();
|
|
||||||
},
|
|
||||||
|
|
||||||
/* getParams(compliments)
|
|
||||||
* Generates an url with api parameters based on the config.
|
|
||||||
*
|
|
||||||
* return String - URL params.
|
|
||||||
*/
|
|
||||||
getParams: function () {
|
|
||||||
var params = "?";
|
|
||||||
if (this.config.locationID) {
|
|
||||||
params += "id=" + this.config.locationID;
|
|
||||||
} else if (this.config.location) {
|
|
||||||
params += "q=" + this.config.location;
|
|
||||||
} else if (this.firstEvent && this.firstEvent.geo) {
|
|
||||||
params += "lat=" + this.firstEvent.geo.lat + "&lon=" + this.firstEvent.geo.lon;
|
|
||||||
} else if (this.firstEvent && this.firstEvent.location) {
|
|
||||||
params += "q=" + this.firstEvent.location;
|
|
||||||
} else {
|
|
||||||
this.hide(this.config.animationSpeed, { lockString: this.identifier });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
params += "&units=" + this.config.units;
|
|
||||||
params += "&lang=" + this.config.lang;
|
|
||||||
params += "&APPID=" + this.config.appid;
|
|
||||||
|
|
||||||
return params;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* processWeather(data)
|
|
||||||
* Uses the received data to set the various values.
|
|
||||||
*
|
|
||||||
* argument data object - Weather information received form openweather.org.
|
|
||||||
*/
|
|
||||||
processWeather: function (data) {
|
|
||||||
if (!data || !data.main || typeof data.main.temp === "undefined") {
|
|
||||||
// Did not receive usable new data.
|
|
||||||
// Maybe this needs a better check?
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.humidity = parseFloat(data.main.humidity);
|
|
||||||
this.temperature = this.roundValue(data.main.temp);
|
|
||||||
this.fetchedLocationName = data.name;
|
|
||||||
this.feelsLike = 0;
|
|
||||||
|
|
||||||
if (this.config.useBeaufort) {
|
|
||||||
this.windSpeed = this.ms2Beaufort(this.roundValue(data.wind.speed));
|
|
||||||
} else if (this.config.useKMPHwind) {
|
|
||||||
this.windSpeed = parseFloat((data.wind.speed * 60 * 60) / 1000).toFixed(0);
|
|
||||||
} else {
|
|
||||||
this.windSpeed = parseFloat(data.wind.speed).toFixed(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ONLY WORKS IF TEMP IN C //
|
|
||||||
var windInMph = parseFloat(data.wind.speed * 2.23694);
|
|
||||||
|
|
||||||
var tempInF = 0;
|
|
||||||
switch (this.config.units) {
|
|
||||||
case "metric":
|
|
||||||
tempInF = 1.8 * this.temperature + 32;
|
|
||||||
break;
|
|
||||||
case "imperial":
|
|
||||||
tempInF = this.temperature;
|
|
||||||
break;
|
|
||||||
case "default":
|
|
||||||
tempInF = 1.8 * (this.temperature - 273.15) + 32;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (windInMph > 3 && tempInF < 50) {
|
|
||||||
// windchill
|
|
||||||
var windChillInF = Math.round(35.74 + 0.6215 * tempInF - 35.75 * Math.pow(windInMph, 0.16) + 0.4275 * tempInF * Math.pow(windInMph, 0.16));
|
|
||||||
var windChillInC = (windChillInF - 32) * (5 / 9);
|
|
||||||
// this.feelsLike = windChillInC.toFixed(0);
|
|
||||||
|
|
||||||
switch (this.config.units) {
|
|
||||||
case "metric":
|
|
||||||
this.feelsLike = windChillInC.toFixed(0);
|
|
||||||
break;
|
|
||||||
case "imperial":
|
|
||||||
this.feelsLike = windChillInF.toFixed(0);
|
|
||||||
break;
|
|
||||||
case "default":
|
|
||||||
this.feelsLike = (windChillInC + 273.15).toFixed(0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (tempInF > 80 && this.humidity > 40) {
|
|
||||||
// heat index
|
|
||||||
var Hindex =
|
|
||||||
-42.379 +
|
|
||||||
2.04901523 * tempInF +
|
|
||||||
10.14333127 * this.humidity -
|
|
||||||
0.22475541 * tempInF * this.humidity -
|
|
||||||
6.83783 * Math.pow(10, -3) * tempInF * tempInF -
|
|
||||||
5.481717 * Math.pow(10, -2) * this.humidity * this.humidity +
|
|
||||||
1.22874 * Math.pow(10, -3) * tempInF * tempInF * this.humidity +
|
|
||||||
8.5282 * Math.pow(10, -4) * tempInF * this.humidity * this.humidity -
|
|
||||||
1.99 * Math.pow(10, -6) * tempInF * tempInF * this.humidity * this.humidity;
|
|
||||||
|
|
||||||
switch (this.config.units) {
|
|
||||||
case "metric":
|
|
||||||
this.feelsLike = parseFloat((Hindex - 32) / 1.8).toFixed(0);
|
|
||||||
break;
|
|
||||||
case "imperial":
|
|
||||||
this.feelsLike = Hindex.toFixed(0);
|
|
||||||
break;
|
|
||||||
case "default":
|
|
||||||
var tc = parseFloat((Hindex - 32) / 1.8) + 273.15;
|
|
||||||
this.feelsLike = tc.toFixed(0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.feelsLike = parseFloat(this.temperature).toFixed(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.windDirection = this.deg2Cardinal(data.wind.deg);
|
|
||||||
this.windDeg = data.wind.deg;
|
|
||||||
this.weatherType = this.config.iconTable[data.weather[0].icon];
|
|
||||||
|
|
||||||
var now = new Date();
|
|
||||||
var sunrise = new Date(data.sys.sunrise * 1000);
|
|
||||||
var sunset = new Date(data.sys.sunset * 1000);
|
|
||||||
|
|
||||||
// The moment().format('h') method has a bug on the Raspberry Pi.
|
|
||||||
// So we need to generate the timestring manually.
|
|
||||||
// See issue: https://github.com/MichMich/MagicMirror/issues/181
|
|
||||||
var sunriseSunsetDateObject = sunrise < now && sunset > now ? sunset : sunrise;
|
|
||||||
var timeString = moment(sunriseSunsetDateObject).format("HH:mm");
|
|
||||||
if (this.config.timeFormat !== 24) {
|
|
||||||
//var hours = sunriseSunsetDateObject.getHours() % 12 || 12;
|
|
||||||
if (this.config.showPeriod) {
|
|
||||||
if (this.config.showPeriodUpper) {
|
|
||||||
//timeString = hours + moment(sunriseSunsetDateObject).format(':mm A');
|
|
||||||
timeString = moment(sunriseSunsetDateObject).format("h:mm A");
|
|
||||||
} else {
|
|
||||||
//timeString = hours + moment(sunriseSunsetDateObject).format(':mm a');
|
|
||||||
timeString = moment(sunriseSunsetDateObject).format("h:mm a");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//timeString = hours + moment(sunriseSunsetDateObject).format(':mm');
|
|
||||||
timeString = moment(sunriseSunsetDateObject).format("h:mm");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sunriseSunsetTime = timeString;
|
|
||||||
this.sunriseSunsetIcon = sunrise < now && sunset > now ? "wi-sunset" : "wi-sunrise";
|
|
||||||
|
|
||||||
this.show(this.config.animationSpeed, { lockString: this.identifier });
|
|
||||||
this.loaded = true;
|
|
||||||
this.updateDom(this.config.animationSpeed);
|
|
||||||
this.sendNotification("CURRENTWEATHER_DATA", { data: data });
|
|
||||||
this.sendNotification("CURRENTWEATHER_TYPE", { type: this.config.iconTable[data.weather[0].icon].replace("-", "_") });
|
|
||||||
},
|
|
||||||
|
|
||||||
/* scheduleUpdate()
|
|
||||||
* Schedule next update.
|
|
||||||
*
|
|
||||||
* argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used.
|
|
||||||
*/
|
|
||||||
scheduleUpdate: function (delay) {
|
|
||||||
var nextLoad = this.config.updateInterval;
|
|
||||||
if (typeof delay !== "undefined" && delay >= 0) {
|
|
||||||
nextLoad = delay;
|
|
||||||
}
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
setTimeout(function () {
|
|
||||||
self.updateWeather();
|
|
||||||
}, nextLoad);
|
|
||||||
},
|
|
||||||
|
|
||||||
/* ms2Beaufort(ms)
|
|
||||||
* Converts m2 to beaufort (windspeed).
|
|
||||||
*
|
|
||||||
* see:
|
|
||||||
* https://www.spc.noaa.gov/faq/tornado/beaufort.html
|
|
||||||
* https://en.wikipedia.org/wiki/Beaufort_scale#Modern_scale
|
|
||||||
*
|
|
||||||
* argument ms number - Windspeed in m/s.
|
|
||||||
*
|
|
||||||
* return number - Windspeed in beaufort.
|
|
||||||
*/
|
|
||||||
ms2Beaufort: function (ms) {
|
|
||||||
var kmh = (ms * 60 * 60) / 1000;
|
|
||||||
var speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
|
|
||||||
for (var beaufort in speeds) {
|
|
||||||
var speed = speeds[beaufort];
|
|
||||||
if (speed > kmh) {
|
|
||||||
return beaufort;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 12;
|
|
||||||
},
|
|
||||||
|
|
||||||
deg2Cardinal: function (deg) {
|
|
||||||
if (deg > 11.25 && deg <= 33.75) {
|
|
||||||
return "NNE";
|
|
||||||
} else if (deg > 33.75 && deg <= 56.25) {
|
|
||||||
return "NE";
|
|
||||||
} else if (deg > 56.25 && deg <= 78.75) {
|
|
||||||
return "ENE";
|
|
||||||
} else if (deg > 78.75 && deg <= 101.25) {
|
|
||||||
return "E";
|
|
||||||
} else if (deg > 101.25 && deg <= 123.75) {
|
|
||||||
return "ESE";
|
|
||||||
} else if (deg > 123.75 && deg <= 146.25) {
|
|
||||||
return "SE";
|
|
||||||
} else if (deg > 146.25 && deg <= 168.75) {
|
|
||||||
return "SSE";
|
|
||||||
} else if (deg > 168.75 && deg <= 191.25) {
|
|
||||||
return "S";
|
|
||||||
} else if (deg > 191.25 && deg <= 213.75) {
|
|
||||||
return "SSW";
|
|
||||||
} else if (deg > 213.75 && deg <= 236.25) {
|
|
||||||
return "SW";
|
|
||||||
} else if (deg > 236.25 && deg <= 258.75) {
|
|
||||||
return "WSW";
|
|
||||||
} else if (deg > 258.75 && deg <= 281.25) {
|
|
||||||
return "W";
|
|
||||||
} else if (deg > 281.25 && deg <= 303.75) {
|
|
||||||
return "WNW";
|
|
||||||
} else if (deg > 303.75 && deg <= 326.25) {
|
|
||||||
return "NW";
|
|
||||||
} else if (deg > 326.25 && deg <= 348.75) {
|
|
||||||
return "NNW";
|
|
||||||
} else {
|
|
||||||
return "N";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/* function(temperature)
|
|
||||||
* Rounds a temperature to 1 decimal or integer (depending on config.roundTemp).
|
|
||||||
*
|
|
||||||
* argument temperature number - Temperature.
|
|
||||||
*
|
|
||||||
* return string - Rounded Temperature.
|
|
||||||
*/
|
|
||||||
roundValue: function (temperature) {
|
|
||||||
var decimals = this.config.roundTemp ? 0 : 1;
|
|
||||||
var roundValue = parseFloat(temperature).toFixed(decimals);
|
|
||||||
return roundValue === "-0" ? 0 : roundValue;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
const NodeHelper = require("node_helper");
|
|
||||||
const Log = require("logger");
|
|
||||||
|
|
||||||
module.exports = NodeHelper.create({
|
|
||||||
// Override start method.
|
|
||||||
start: function () {
|
|
||||||
Log.warn(`The module '${this.name}' is deprecated in favor of the 'weather'-module, please refer to the documentation for a migration path`);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Default Modules List
|
/* MagicMirror² Default Modules List
|
||||||
* Modules listed below can be loaded without the 'default/' prefix. Omitting the default folder name.
|
* Modules listed below can be loaded without the 'default/' prefix. Omitting the default folder name.
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Module: Hello World
|
# Module: Hello World
|
||||||
|
|
||||||
The `helloworld` module is one of the default modules of the MagicMirror. It is a simple way to display a static text on the mirror.
|
The `helloworld` module is one of the default modules of the MagicMirror². It is a simple way to display a static text on the mirror.
|
||||||
|
|
||||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/helloworld.html).
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/helloworld.html).
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: HelloWorld
|
* Module: HelloWorld
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Module: News Feed
|
# Module: News Feed
|
||||||
|
|
||||||
The `newsfeed` module is one of the default modules of the MagicMirror.
|
The `newsfeed` module is one of the default modules of the MagicMirror².
|
||||||
This module displays news headlines based on an RSS feed. Scrolling through news headlines happens time-based (`updateInterval`), but can also be controlled by sending news feed specific notifications to the module.
|
This module displays news headlines based on an RSS feed. Scrolling through news headlines happens time-based (`updateInterval`), but can also be controlled by sending news feed specific notifications to the module.
|
||||||
|
|
||||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/newsfeed.html).
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/newsfeed.html).
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: NewsFeed
|
* Module: NewsFeed
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
@ -20,6 +20,7 @@ Module.register("newsfeed", {
|
|||||||
broadcastNewsFeeds: true,
|
broadcastNewsFeeds: true,
|
||||||
broadcastNewsUpdates: true,
|
broadcastNewsUpdates: true,
|
||||||
showDescription: false,
|
showDescription: false,
|
||||||
|
showTitleAsUrl: false,
|
||||||
wrapTitle: true,
|
wrapTitle: true,
|
||||||
wrapDescription: true,
|
wrapDescription: true,
|
||||||
truncDescription: true,
|
truncDescription: true,
|
||||||
@ -141,6 +142,7 @@ Module.register("newsfeed", {
|
|||||||
sourceTitle: item.sourceTitle,
|
sourceTitle: item.sourceTitle,
|
||||||
publishDate: moment(new Date(item.pubdate)).fromNow(),
|
publishDate: moment(new Date(item.pubdate)).fromNow(),
|
||||||
title: item.title,
|
title: item.title,
|
||||||
|
url: item.url,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
items: items
|
items: items
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,22 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro escapeTitle(title, url, dangerouslyDisableAutoEscaping=false, showTitleAsUrl=false) %}
|
||||||
|
{% if dangerouslyDisableAutoEscaping %}
|
||||||
|
{% if showTitleAsUrl %}
|
||||||
|
<a href="{{ url }}" style="text-decoration:none;color:#ffffff" target="_blank">{{ title | safe }}</a>
|
||||||
|
{% else %}
|
||||||
|
{{ title | safe}}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{% if showTitleAsUrl %}
|
||||||
|
<a href="{{ url }}" style="text-decoration:none;color:#ffffff" target="_blank">{{ title }}</a>
|
||||||
|
{% else %}
|
||||||
|
{{ title }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% if loaded %}
|
{% if loaded %}
|
||||||
{% if config.showAsList %}
|
{% if config.showAsList %}
|
||||||
<ul class="newsfeed-list">
|
<ul class="newsfeed-list">
|
||||||
@ -22,7 +38,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="newsfeed-title bright medium light{{ ' no-wrap' if not config.wrapTitle }}">
|
<div class="newsfeed-title bright medium light{{ ' no-wrap' if not config.wrapTitle }}">
|
||||||
{{ escapeText(item.title, config.dangerouslyDisableAutoEscaping) }}
|
{{ escapeTitle(item.title, item.url, config.dangerouslyDisableAutoEscaping, config.showTitleAsUrl) }}
|
||||||
</div>
|
</div>
|
||||||
{% if config.showDescription %}
|
{% if config.showDescription %}
|
||||||
<div class="newsfeed-desc small light{{ ' no-wrap' if not config.wrapDescription }}">
|
<div class="newsfeed-desc small light{{ ' no-wrap' if not config.wrapDescription }}">
|
||||||
@ -49,7 +65,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="newsfeed-title bright medium light{{ ' no-wrap' if not config.wrapTitle }}">
|
<div class="newsfeed-title bright medium light{{ ' no-wrap' if not config.wrapTitle }}">
|
||||||
{{ escapeText(title, config.dangerouslyDisableAutoEscaping) }}
|
{{ escapeTitle(title, url, config.dangerouslyDisableAutoEscaping, config.showTitleAsUrl) }}
|
||||||
</div>
|
</div>
|
||||||
{% if config.showDescription %}
|
{% if config.showDescription %}
|
||||||
<div class="newsfeed-desc small light{{ ' no-wrap' if not config.wrapDescription }}">
|
<div class="newsfeed-desc small light{{ ' no-wrap' if not config.wrapDescription }}">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Node Helper: Newsfeed - NewsfeedFetcher
|
* Node Helper: Newsfeed - NewsfeedFetcher
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Node Helper: Newsfeed
|
* Node Helper: Newsfeed
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Module: Update Notification
|
# Module: Update Notification
|
||||||
|
|
||||||
The `updatenotification` module is one of the default modules of the MagicMirror.
|
The `updatenotification` module is one of the default modules of the MagicMirror².
|
||||||
This will display a message whenever a new version of the MagicMirror application is available.
|
This will display a message whenever a new version of the MagicMirror² application is available.
|
||||||
|
|
||||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/updatenotification.html).
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/updatenotification.html).
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: UpdateNotification
|
* Module: UpdateNotification
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% if not suspended %}
|
{% if not suspended %}
|
||||||
{% for name, status in moduleList %}
|
{% for name, status in moduleList %}
|
||||||
<div class="small bright">
|
<div class="small bright">
|
||||||
<i class="fa fa-exclamation-circle"></i>
|
<i class="fas fa-exclamation-circle"></i>
|
||||||
<span>
|
<span>
|
||||||
{% set mainTextLabel = "UPDATE_NOTIFICATION" if name === "default" else "UPDATE_NOTIFICATION_MODULE" %}
|
{% set mainTextLabel = "UPDATE_NOTIFICATION" if name === "default" else "UPDATE_NOTIFICATION_MODULE" %}
|
||||||
{{ mainTextLabel | translate({MODULE_NAME: name}) }}
|
{{ mainTextLabel | translate({MODULE_NAME: name}) }}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
{% if config.showWindDirection %}
|
{% if config.showWindDirection %}
|
||||||
<sup>
|
<sup>
|
||||||
{% if config.showWindDirectionAsArrow %}
|
{% if config.showWindDirectionAsArrow %}
|
||||||
<i class="fa fa-long-arrow-up" style="transform:rotate({{ current.windDirection }}deg);"></i>
|
<i class="fas fa-long-arrow-alt-up" style="transform:rotate({{ current.windDirection }}deg);"></i>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ current.cardinalWindDirection() | translate }}
|
{{ current.cardinalWindDirection() | translate }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -47,7 +47,7 @@
|
|||||||
<div class="normal light indoor">
|
<div class="normal light indoor">
|
||||||
{% if config.showIndoorTemperature and indoor.temperature %}
|
{% if config.showIndoorTemperature and indoor.temperature %}
|
||||||
<div>
|
<div>
|
||||||
<span class="fa fa-home"></span>
|
<span class="fas fa-home"></span>
|
||||||
<span class="bright">
|
<span class="bright">
|
||||||
{{ indoor.temperature | roundValue | unit("temperature") | decimalSymbol }}
|
{{ indoor.temperature | roundValue | unit("temperature") | decimalSymbol }}
|
||||||
</span>
|
</span>
|
||||||
@ -55,7 +55,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if config.showIndoorHumidity and indoor.humidity %}
|
{% if config.showIndoorHumidity and indoor.humidity %}
|
||||||
<div>
|
<div>
|
||||||
<span class="fa fa-tint"></span>
|
<span class="fas fa-tint"></span>
|
||||||
<span class="bright">
|
<span class="bright">
|
||||||
{{ indoor.humidity | roundValue | unit("humidity") | decimalSymbol }}
|
{{ indoor.humidity | roundValue | unit("humidity") | decimalSymbol }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
{% set forecast = forecast.slice(0, numSteps) %}
|
{% set forecast = forecast.slice(0, numSteps) %}
|
||||||
{% for f in forecast %}
|
{% for f in forecast %}
|
||||||
<tr {% if config.colored %}class="colored"{% endif %} {% if config.fade %}style="opacity: {{ currentStep | opacity(numSteps) }};"{% endif %}>
|
<tr {% if config.colored %}class="colored"{% endif %} {% if config.fade %}style="opacity: {{ currentStep | opacity(numSteps) }};"{% endif %}>
|
||||||
{% if (currentStep == 0) and config.ignoreToday == false %}
|
{% if (currentStep == 0) and config.ignoreToday == false and config.absoluteDates == false %}
|
||||||
<td class="day">{{ "TODAY" | translate }}</td>
|
<td class="day">{{ "TODAY" | translate }}</td>
|
||||||
{% elif (currentStep == 1) and config.ignoreToday == false %}
|
{% elif (currentStep == 1) and config.ignoreToday == false and config.absoluteDates == false %}
|
||||||
<td class="day">{{ "TOMORROW" | translate }}</td>
|
<td class="day">{{ "TOMORROW" | translate }}</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td class="day">{{ f.date.format('ddd') }}</td>
|
<td class="day">{{ f.date.format('ddd') }}</td>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global WeatherProvider, WeatherObject */
|
/* global WeatherProvider, WeatherObject */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Weather
|
* Module: Weather
|
||||||
* Provider: Dark Sky
|
* Provider: Dark Sky
|
||||||
*
|
*
|
||||||
@ -18,7 +18,8 @@ WeatherProvider.register("darksky", {
|
|||||||
|
|
||||||
// Set the default config properties that is specific to this provider
|
// Set the default config properties that is specific to this provider
|
||||||
defaults: {
|
defaults: {
|
||||||
apiBase: "https://cors-anywhere.herokuapp.com/https://api.darksky.net",
|
useCorsProxy: true,
|
||||||
|
apiBase: "https://api.darksky.net",
|
||||||
weatherEndpoint: "/forecast",
|
weatherEndpoint: "/forecast",
|
||||||
apiKey: "",
|
apiKey: "",
|
||||||
lat: 0,
|
lat: 0,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global WeatherProvider, WeatherObject */
|
/* global WeatherProvider, WeatherObject */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Weather
|
* Module: Weather
|
||||||
* Provider: Environment Canada (EC)
|
* Provider: Environment Canada (EC)
|
||||||
*
|
*
|
||||||
@ -40,6 +40,7 @@ WeatherProvider.register("envcanada", {
|
|||||||
|
|
||||||
// Set the default config properties that is specific to this provider
|
// Set the default config properties that is specific to this provider
|
||||||
defaults: {
|
defaults: {
|
||||||
|
useCorsProxy: true,
|
||||||
siteCode: "s1234567",
|
siteCode: "s1234567",
|
||||||
provCode: "ON"
|
provCode: "ON"
|
||||||
},
|
},
|
||||||
@ -73,7 +74,7 @@ WeatherProvider.register("envcanada", {
|
|||||||
// Override the fetchCurrentWeather method to query EC and construct a Current weather object
|
// Override the fetchCurrentWeather method to query EC and construct a Current weather object
|
||||||
//
|
//
|
||||||
fetchCurrentWeather() {
|
fetchCurrentWeather() {
|
||||||
this.fetchData(this.getUrl(), "GET")
|
this.fetchData(this.getUrl(), "GET", "xml")
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
// Did not receive usable new data.
|
// Did not receive usable new data.
|
||||||
@ -93,7 +94,7 @@ WeatherProvider.register("envcanada", {
|
|||||||
// Override the fetchWeatherForecast method to query EC and construct Forecast weather objects
|
// Override the fetchWeatherForecast method to query EC and construct Forecast weather objects
|
||||||
//
|
//
|
||||||
fetchWeatherForecast() {
|
fetchWeatherForecast() {
|
||||||
this.fetchData(this.getUrl(), "GET")
|
this.fetchData(this.getUrl(), "GET", "xml")
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
// Did not receive usable new data.
|
// Did not receive usable new data.
|
||||||
@ -113,7 +114,7 @@ WeatherProvider.register("envcanada", {
|
|||||||
// Override the fetchWeatherHourly method to query EC and construct Forecast weather objects
|
// Override the fetchWeatherHourly method to query EC and construct Forecast weather objects
|
||||||
//
|
//
|
||||||
fetchWeatherHourly() {
|
fetchWeatherHourly() {
|
||||||
this.fetchData(this.getUrl(), "GET")
|
this.fetchData(this.getUrl(), "GET", "xml")
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
// Did not receive usable new data.
|
// Did not receive usable new data.
|
||||||
@ -129,26 +130,6 @@ WeatherProvider.register("envcanada", {
|
|||||||
.finally(() => this.updateAvailable());
|
.finally(() => this.updateAvailable());
|
||||||
},
|
},
|
||||||
|
|
||||||
//
|
|
||||||
// Override fetchData function to handle XML document (base function assumes JSON)
|
|
||||||
//
|
|
||||||
fetchData: function (url, method = "GET", data = null) {
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
const request = new XMLHttpRequest();
|
|
||||||
request.open(method, url, true);
|
|
||||||
request.onreadystatechange = function () {
|
|
||||||
if (this.readyState === 4) {
|
|
||||||
if (this.status === 200) {
|
|
||||||
resolve(this.responseXML);
|
|
||||||
} else {
|
|
||||||
reject(request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
request.send();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Environment Canada methods - not part of the standard Provider methods
|
// Environment Canada methods - not part of the standard Provider methods
|
||||||
@ -160,11 +141,8 @@ WeatherProvider.register("envcanada", {
|
|||||||
// URL defaults to the Englsih version simply because there is no language dependancy in the data
|
// URL defaults to the Englsih version simply because there is no language dependancy in the data
|
||||||
// being accessed. This is only pertinent when using the EC data elements that contain a textual forecast.
|
// being accessed. This is only pertinent when using the EC data elements that contain a textual forecast.
|
||||||
//
|
//
|
||||||
// Also note that access is supported through a proxy service (thingproxy.freeboard.io) to mitigate
|
|
||||||
// CORS errors when accessing EC
|
|
||||||
//
|
|
||||||
getUrl() {
|
getUrl() {
|
||||||
return "https://thingproxy.freeboard.io/fetch/https://dd.weather.gc.ca/citypage_weather/xml/" + this.config.provCode + "/" + this.config.siteCode + "_e.xml";
|
return "https://dd.weather.gc.ca/citypage_weather/xml/" + this.config.provCode + "/" + this.config.siteCode + "_e.xml";
|
||||||
},
|
},
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global WeatherProvider, WeatherObject */
|
/* global WeatherProvider, WeatherObject */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Weather
|
* Module: Weather
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
@ -128,12 +128,7 @@ WeatherProvider.register("openweathermap", {
|
|||||||
currentWeather.humidity = currentWeatherData.main.humidity;
|
currentWeather.humidity = currentWeatherData.main.humidity;
|
||||||
currentWeather.temperature = currentWeatherData.main.temp;
|
currentWeather.temperature = currentWeatherData.main.temp;
|
||||||
currentWeather.feelsLikeTemp = currentWeatherData.main.feels_like;
|
currentWeather.feelsLikeTemp = currentWeatherData.main.feels_like;
|
||||||
|
currentWeather.windSpeed = currentWeatherData.wind.speed;
|
||||||
if (this.config.windUnits === "metric") {
|
|
||||||
currentWeather.windSpeed = this.config.useKmh ? currentWeatherData.wind.speed * 3.6 : currentWeatherData.wind.speed;
|
|
||||||
} else {
|
|
||||||
currentWeather.windSpeed = currentWeatherData.wind.speed;
|
|
||||||
}
|
|
||||||
currentWeather.windDirection = currentWeatherData.wind.deg;
|
currentWeather.windDirection = currentWeatherData.wind.deg;
|
||||||
currentWeather.weatherType = this.convertWeatherType(currentWeatherData.weather[0].icon);
|
currentWeather.weatherType = this.convertWeatherType(currentWeatherData.weather[0].icon);
|
||||||
currentWeather.sunrise = moment(currentWeatherData.sys.sunrise, "X");
|
currentWeather.sunrise = moment(currentWeatherData.sys.sunrise, "X");
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global WeatherProvider, WeatherObject */
|
/* global WeatherProvider, WeatherObject */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Weather
|
* Module: Weather
|
||||||
* Provider: SMHI
|
* Provider: SMHI
|
||||||
*
|
*
|
||||||
@ -237,7 +237,7 @@ WeatherProvider.register("smhi", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map the icon value from SMHI 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.
|
* Uses different icons depending if its daytime or nighttime.
|
||||||
* SMHI'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.
|
||||||
*
|
*
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global WeatherProvider, WeatherObject */
|
/* global WeatherProvider, WeatherObject */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Weather
|
* Module: Weather
|
||||||
*
|
*
|
||||||
* By Malcolm Oakes https://github.com/maloakes
|
* By Malcolm Oakes https://github.com/maloakes
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global WeatherProvider, WeatherObject */
|
/* global WeatherProvider, WeatherObject */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Weather
|
* Module: Weather
|
||||||
*
|
*
|
||||||
* By Malcolm Oakes https://github.com/maloakes
|
* By Malcolm Oakes https://github.com/maloakes
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global WeatherProvider, WeatherObject */
|
/* global WeatherProvider, WeatherObject */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Weather
|
* Module: Weather
|
||||||
* Provider: Weatherbit
|
* Provider: Weatherbit
|
||||||
*
|
*
|
||||||
|
109
modules/default/weather/providers/weatherflow.js
Normal file
109
modules/default/weather/providers/weatherflow.js
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/* global WeatherProvider, WeatherObject */
|
||||||
|
|
||||||
|
/* MagicMirror²
|
||||||
|
* Module: Weather
|
||||||
|
* Provider: Weatherflow
|
||||||
|
*
|
||||||
|
* By Tobias Dreyem https://github.com/10bias
|
||||||
|
* MIT Licensed
|
||||||
|
*
|
||||||
|
* This class is a provider for Weatherflow.
|
||||||
|
* Note that the Weatherflow API does not provide snowfall.
|
||||||
|
*/
|
||||||
|
|
||||||
|
WeatherProvider.register("weatherflow", {
|
||||||
|
// Set the name of the provider.
|
||||||
|
// Not strictly required, but helps for debugging
|
||||||
|
providerName: "WeatherFlow",
|
||||||
|
|
||||||
|
// Set the default config properties that is specific to this provider
|
||||||
|
defaults: {
|
||||||
|
apiBase: "https://swd.weatherflow.com/swd/rest/",
|
||||||
|
token: "",
|
||||||
|
stationid: ""
|
||||||
|
},
|
||||||
|
|
||||||
|
units: {
|
||||||
|
imperial: {
|
||||||
|
temp: "f",
|
||||||
|
wind: "mph",
|
||||||
|
pressure: "hpa",
|
||||||
|
precip: "in",
|
||||||
|
distance: "mi"
|
||||||
|
},
|
||||||
|
metric: {
|
||||||
|
temp: "c",
|
||||||
|
wind: "kph",
|
||||||
|
pressure: "mb",
|
||||||
|
precip: "mm",
|
||||||
|
distance: "km"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchCurrentWeather() {
|
||||||
|
this.fetchData(this.getUrl())
|
||||||
|
.then((data) => {
|
||||||
|
const currentWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits, this.config.useKmh);
|
||||||
|
currentWeather.date = moment();
|
||||||
|
|
||||||
|
currentWeather.humidity = data.current_conditions.relative_humidity;
|
||||||
|
currentWeather.temperature = data.current_conditions.air_temperature;
|
||||||
|
currentWeather.windSpeed = data.current_conditions.wind_avg;
|
||||||
|
currentWeather.windDirection = data.current_conditions.wind_direction;
|
||||||
|
currentWeather.weatherType = data.forecast.daily[0].icon;
|
||||||
|
currentWeather.sunrise = moment(data.forecast.daily[0].sunrise, "X");
|
||||||
|
currentWeather.sunset = moment(data.forecast.daily[0].sunset, "X");
|
||||||
|
this.setCurrentWeather(currentWeather);
|
||||||
|
})
|
||||||
|
.catch(function (request) {
|
||||||
|
Log.error("Could not load data ... ", request);
|
||||||
|
})
|
||||||
|
.finally(() => this.updateAvailable());
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchWeatherForecast() {
|
||||||
|
this.fetchData(this.getUrl())
|
||||||
|
.then((data) => {
|
||||||
|
const days = [];
|
||||||
|
|
||||||
|
for (const forecast of data.forecast.daily) {
|
||||||
|
const weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits, this.config.useKmh);
|
||||||
|
|
||||||
|
weather.date = moment(forecast.day_start_local, "X");
|
||||||
|
weather.minTemperature = forecast.air_temp_low;
|
||||||
|
weather.maxTemperature = forecast.air_temp_high;
|
||||||
|
weather.weatherType = forecast.icon;
|
||||||
|
weather.snow = 0;
|
||||||
|
|
||||||
|
days.push(weather);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setWeatherForecast(days);
|
||||||
|
})
|
||||||
|
.catch(function (request) {
|
||||||
|
Log.error("Could not load data ... ", request);
|
||||||
|
})
|
||||||
|
.finally(() => this.updateAvailable());
|
||||||
|
},
|
||||||
|
|
||||||
|
// Create a URL from the config and base URL.
|
||||||
|
getUrl() {
|
||||||
|
return (
|
||||||
|
this.config.apiBase +
|
||||||
|
"better_forecast?station_id=" +
|
||||||
|
this.config.stationid +
|
||||||
|
"&units_temp=" +
|
||||||
|
this.units[this.config.units].temp +
|
||||||
|
"&units_wind=" +
|
||||||
|
this.units[this.config.units].wind +
|
||||||
|
"&units_pressure=" +
|
||||||
|
this.units[this.config.units].pressure +
|
||||||
|
"&units_precip=" +
|
||||||
|
this.units[this.config.units].precip +
|
||||||
|
"&units_distance=" +
|
||||||
|
this.units[this.config.units].distance +
|
||||||
|
"&token=" +
|
||||||
|
this.config.token
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -1,6 +1,6 @@
|
|||||||
/* global WeatherProvider, WeatherObject */
|
/* global WeatherProvider, WeatherObject */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Weather
|
* Module: Weather
|
||||||
* Provider: weather.gov
|
* Provider: weather.gov
|
||||||
* https://weather-gov.github.io/api/general-faqs
|
* https://weather-gov.github.io/api/general-faqs
|
||||||
@ -40,7 +40,8 @@ WeatherProvider.register("weathergov", {
|
|||||||
// Called to set the config, this config is the same as the weather module's config.
|
// Called to set the config, this config is the same as the weather module's config.
|
||||||
setConfig: function (config) {
|
setConfig: function (config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
(this.config.apiBase = "https://api.weather.gov"), this.fetchWxGovURLs(this.config);
|
this.config.apiBase = "https://api.weather.gov";
|
||||||
|
this.fetchWxGovURLs(this.config);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Called when the weather provider is about to start.
|
// Called when the weather provider is about to start.
|
||||||
@ -156,8 +157,13 @@ WeatherProvider.register("weathergov", {
|
|||||||
currentWeather.rain = null;
|
currentWeather.rain = null;
|
||||||
currentWeather.snow = null;
|
currentWeather.snow = null;
|
||||||
currentWeather.precipitation = this.convertLength(currentWeatherData.precipitationLastHour.value);
|
currentWeather.precipitation = this.convertLength(currentWeatherData.precipitationLastHour.value);
|
||||||
currentWeather.feelsLikeTemp = this.convertTemp(currentWeatherData.heatIndex.value);
|
if (currentWeatherData.heatIndex.value !== null) {
|
||||||
|
currentWeather.feelsLikeTemp = this.convertTemp(currentWeatherData.heatIndex.value);
|
||||||
|
} else if (currentWeatherData.windChill.value !== null) {
|
||||||
|
currentWeather.feelsLikeTemp = this.convertTemp(currentWeatherData.windChill.value);
|
||||||
|
} else {
|
||||||
|
currentWeather.feelsLikeTemp = this.convertTemp(currentWeatherData.temperature.value);
|
||||||
|
}
|
||||||
// determine the sunrise/sunset times - not supplied in weather.gov data
|
// determine the sunrise/sunset times - not supplied in weather.gov data
|
||||||
currentWeather.updateSunTime(this.config.lat, this.config.lon);
|
currentWeather.updateSunTime(this.config.lat, this.config.lon);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global WeatherProvider */
|
/* global WeatherProvider */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Weather
|
* Module: Weather
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
@ -43,7 +43,8 @@ Module.register("weather", {
|
|||||||
onlyTemp: false,
|
onlyTemp: false,
|
||||||
showPrecipitationAmount: false,
|
showPrecipitationAmount: false,
|
||||||
colored: false,
|
colored: false,
|
||||||
showFeelsLike: true
|
showFeelsLike: true,
|
||||||
|
absoluteDates: false
|
||||||
},
|
},
|
||||||
|
|
||||||
// Module properties.
|
// Module properties.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global SunCalc */
|
/* global SunCalc */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Weather
|
* Module: Weather
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* global Class */
|
/* global Class */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: Weather
|
* Module: Weather
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
@ -111,8 +111,17 @@ const WeatherProvider = Class.extend({
|
|||||||
this.delegate.updateAvailable(this);
|
this.delegate.updateAvailable(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getCorsUrl: function () {
|
||||||
|
if (this.config.mockData || typeof this.config.useCorsProxy === "undefined" || !this.config.useCorsProxy) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return location.protocol + "//" + location.host + "/cors?url=";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// A convenience function to make requests. It returns a promise.
|
// A convenience function to make requests. It returns a promise.
|
||||||
fetchData: function (url, method = "GET", data = null) {
|
fetchData: function (url, method = "GET", type = "json") {
|
||||||
|
url = this.getCorsUrl() + url;
|
||||||
const getData = function (mockData) {
|
const getData = function (mockData) {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
if (mockData) {
|
if (mockData) {
|
||||||
@ -125,7 +134,11 @@ const WeatherProvider = Class.extend({
|
|||||||
request.onreadystatechange = function () {
|
request.onreadystatechange = function () {
|
||||||
if (this.readyState === 4) {
|
if (this.readyState === 4) {
|
||||||
if (this.status === 200) {
|
if (this.status === 200) {
|
||||||
resolve(JSON.parse(this.response));
|
if (type === "xml") {
|
||||||
|
resolve(this.responseXML);
|
||||||
|
} else {
|
||||||
|
resolve(JSON.parse(this.response));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
reject(request);
|
reject(request);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
# Module: Weather Forecast
|
|
||||||
|
|
||||||
> :warning: **This module is deprecated in favor of the [weather](https://docs.magicmirror.builders/modules/weather.html) module.**
|
|
||||||
|
|
||||||
The `weatherforecast` module is one of the default modules of the MagicMirror.
|
|
||||||
This module displays the weather forecast for the coming week, including an an icon to display the current conditions, the minimum temperature and the maximum temperature.
|
|
||||||
|
|
||||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/weatherforecast.html).
|
|
@ -1,9 +0,0 @@
|
|||||||
const NodeHelper = require("node_helper");
|
|
||||||
const Log = require("logger");
|
|
||||||
|
|
||||||
module.exports = NodeHelper.create({
|
|
||||||
// Override start method.
|
|
||||||
start: function () {
|
|
||||||
Log.warn(`The module '${this.name}' is deprecated in favor of the 'weather'-module, please refer to the documentation for a migration path`);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,27 +0,0 @@
|
|||||||
.weatherforecast .day {
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weatherforecast .weather-icon {
|
|
||||||
padding-right: 30px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weatherforecast .min-temp {
|
|
||||||
padding-left: 20px;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weatherforecast .rain {
|
|
||||||
padding-left: 20px;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weatherforecast tr.colored .min-temp {
|
|
||||||
color: #bcddff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weatherforecast tr.colored .max-temp {
|
|
||||||
color: #ff8e99;
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
|
||||||
/* Magic Mirror
|
/* MagicMirror²
|
||||||
* Module: WeatherForecast
|
* Module: CurrentWeather
|
||||||
*
|
*
|
||||||
* By Michael Teeuw https://michaelteeuw.nl
|
* By Michael Teeuw https://michaelteeuw.nl
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
@ -9,510 +9,25 @@
|
|||||||
* This module is deprecated. Any additional feature will no longer be merged.
|
* This module is deprecated. Any additional feature will no longer be merged.
|
||||||
*/
|
*/
|
||||||
Module.register("weatherforecast", {
|
Module.register("weatherforecast", {
|
||||||
// Default module config.
|
|
||||||
defaults: {
|
|
||||||
location: false,
|
|
||||||
locationID: false,
|
|
||||||
lat: false,
|
|
||||||
lon: false,
|
|
||||||
appid: "",
|
|
||||||
units: config.units,
|
|
||||||
maxNumberOfDays: 7,
|
|
||||||
showRainAmount: false,
|
|
||||||
updateInterval: 10 * 60 * 1000, // every 10 minutes
|
|
||||||
animationSpeed: 1000,
|
|
||||||
timeFormat: config.timeFormat,
|
|
||||||
lang: config.language,
|
|
||||||
decimalSymbol: ".",
|
|
||||||
fade: true,
|
|
||||||
fadePoint: 0.25, // Start on 1/4th of the list.
|
|
||||||
colored: false,
|
|
||||||
scale: false,
|
|
||||||
|
|
||||||
initialLoadDelay: 2500, // 2.5 seconds delay. This delay is used to keep the OpenWeather API happy.
|
|
||||||
retryDelay: 2500,
|
|
||||||
|
|
||||||
apiVersion: "2.5",
|
|
||||||
apiBase: "https://api.openweathermap.org/data/",
|
|
||||||
forecastEndpoint: "forecast/daily",
|
|
||||||
excludes: false,
|
|
||||||
|
|
||||||
appendLocationNameToHeader: true,
|
|
||||||
calendarClass: "calendar",
|
|
||||||
tableClass: "small",
|
|
||||||
|
|
||||||
roundTemp: false,
|
|
||||||
|
|
||||||
iconTable: {
|
|
||||||
"01d": "wi-day-sunny",
|
|
||||||
"02d": "wi-day-cloudy",
|
|
||||||
"03d": "wi-cloudy",
|
|
||||||
"04d": "wi-cloudy-windy",
|
|
||||||
"09d": "wi-showers",
|
|
||||||
"10d": "wi-rain",
|
|
||||||
"11d": "wi-thunderstorm",
|
|
||||||
"13d": "wi-snow",
|
|
||||||
"50d": "wi-fog",
|
|
||||||
"01n": "wi-night-clear",
|
|
||||||
"02n": "wi-night-cloudy",
|
|
||||||
"03n": "wi-night-cloudy",
|
|
||||||
"04n": "wi-night-cloudy",
|
|
||||||
"09n": "wi-night-showers",
|
|
||||||
"10n": "wi-night-rain",
|
|
||||||
"11n": "wi-night-thunderstorm",
|
|
||||||
"13n": "wi-night-snow",
|
|
||||||
"50n": "wi-night-alt-cloudy-windy"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// create a variable for the first upcoming calendar event. Used if no location is specified.
|
|
||||||
firstEvent: false,
|
|
||||||
|
|
||||||
// create a variable to hold the location name based on the API result.
|
|
||||||
fetchedLocationName: "",
|
|
||||||
|
|
||||||
// Define required scripts.
|
|
||||||
getScripts: function () {
|
|
||||||
return ["moment.js"];
|
|
||||||
},
|
|
||||||
|
|
||||||
// Define required scripts.
|
|
||||||
getStyles: function () {
|
|
||||||
return ["weather-icons.css", "weatherforecast.css"];
|
|
||||||
},
|
|
||||||
|
|
||||||
// Define required translations.
|
|
||||||
getTranslations: function () {
|
|
||||||
// The translations for the default modules are defined in the core translation files.
|
|
||||||
// Therefor we can just return false. Otherwise we should have returned a dictionary.
|
|
||||||
// If you're trying to build your own module including translations, check out the documentation.
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Define start sequence.
|
// Define start sequence.
|
||||||
start: function () {
|
start: function () {
|
||||||
Log.info("Starting module: " + this.name);
|
Log.info("Starting module: " + this.name);
|
||||||
|
|
||||||
// Set locale.
|
|
||||||
moment.locale(config.language);
|
|
||||||
|
|
||||||
this.forecast = [];
|
|
||||||
this.loaded = false;
|
|
||||||
this.scheduleUpdate(this.config.initialLoadDelay);
|
|
||||||
|
|
||||||
this.updateTimer = null;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Override dom generator.
|
// Override dom generator.
|
||||||
getDom: function () {
|
getDom: function () {
|
||||||
var wrapper = document.createElement("div");
|
var wrapper = document.createElement("div");
|
||||||
|
wrapper.className = this.config.tableClass;
|
||||||
if (this.config.appid === "" || this.config.appid === "YOUR_OPENWEATHER_API_KEY") {
|
wrapper.innerHTML =
|
||||||
wrapper.innerHTML = "Please set the correct openweather <i>appid</i> in the config for module: " + this.name + ".";
|
"<style>text-decoration: none</style>" +
|
||||||
wrapper.className = "dimmed light small";
|
"This module is deprecated since release v2.15 and removed with v2.19." +
|
||||||
return wrapper;
|
'<br>Please use the `weather` module as replacement, more info in the <a href="https://docs.magicmirror.builders/modules/weather.html" style="color: #ffffff">documentation</a>.';
|
||||||
}
|
wrapper.className = "dimmed light small";
|
||||||
|
return wrapper;
|
||||||
if (!this.loaded) {
|
|
||||||
wrapper.innerHTML = this.translate("LOADING");
|
|
||||||
wrapper.className = "dimmed light small";
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
var table = document.createElement("table");
|
|
||||||
table.className = this.config.tableClass;
|
|
||||||
|
|
||||||
for (var f in this.forecast) {
|
|
||||||
var forecast = this.forecast[f];
|
|
||||||
|
|
||||||
var row = document.createElement("tr");
|
|
||||||
if (this.config.colored) {
|
|
||||||
row.className = "colored";
|
|
||||||
}
|
|
||||||
table.appendChild(row);
|
|
||||||
|
|
||||||
var dayCell = document.createElement("td");
|
|
||||||
dayCell.className = "day";
|
|
||||||
dayCell.innerHTML = forecast.day;
|
|
||||||
row.appendChild(dayCell);
|
|
||||||
|
|
||||||
var iconCell = document.createElement("td");
|
|
||||||
iconCell.className = "bright weather-icon";
|
|
||||||
row.appendChild(iconCell);
|
|
||||||
|
|
||||||
var icon = document.createElement("span");
|
|
||||||
icon.className = "wi weathericon " + forecast.icon;
|
|
||||||
iconCell.appendChild(icon);
|
|
||||||
|
|
||||||
var degreeLabel = "";
|
|
||||||
if (this.config.units === "metric" || this.config.units === "imperial") {
|
|
||||||
degreeLabel += "°";
|
|
||||||
}
|
|
||||||
if (this.config.scale) {
|
|
||||||
switch (this.config.units) {
|
|
||||||
case "metric":
|
|
||||||
degreeLabel += "C";
|
|
||||||
break;
|
|
||||||
case "imperial":
|
|
||||||
degreeLabel += "F";
|
|
||||||
break;
|
|
||||||
case "default":
|
|
||||||
degreeLabel = "K";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.decimalSymbol === "" || this.config.decimalSymbol === " ") {
|
|
||||||
this.config.decimalSymbol = ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxTempCell = document.createElement("td");
|
|
||||||
maxTempCell.innerHTML = forecast.maxTemp.replace(".", this.config.decimalSymbol) + degreeLabel;
|
|
||||||
maxTempCell.className = "align-right bright max-temp";
|
|
||||||
row.appendChild(maxTempCell);
|
|
||||||
|
|
||||||
var minTempCell = document.createElement("td");
|
|
||||||
minTempCell.innerHTML = forecast.minTemp.replace(".", this.config.decimalSymbol) + degreeLabel;
|
|
||||||
minTempCell.className = "align-right min-temp";
|
|
||||||
row.appendChild(minTempCell);
|
|
||||||
|
|
||||||
if (this.config.showRainAmount) {
|
|
||||||
var rainCell = document.createElement("td");
|
|
||||||
if (isNaN(forecast.rain)) {
|
|
||||||
rainCell.innerHTML = "";
|
|
||||||
} else {
|
|
||||||
if (config.units !== "imperial") {
|
|
||||||
rainCell.innerHTML = parseFloat(forecast.rain).toFixed(1).replace(".", this.config.decimalSymbol) + " mm";
|
|
||||||
} else {
|
|
||||||
rainCell.innerHTML = (parseFloat(forecast.rain) / 25.4).toFixed(2).replace(".", this.config.decimalSymbol) + " in";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rainCell.className = "align-right bright rain";
|
|
||||||
row.appendChild(rainCell);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.fade && this.config.fadePoint < 1) {
|
|
||||||
if (this.config.fadePoint < 0) {
|
|
||||||
this.config.fadePoint = 0;
|
|
||||||
}
|
|
||||||
var startingPoint = this.forecast.length * this.config.fadePoint;
|
|
||||||
var steps = this.forecast.length - startingPoint;
|
|
||||||
if (f >= startingPoint) {
|
|
||||||
var currentStep = f - startingPoint;
|
|
||||||
row.style.opacity = 1 - (1 / steps) * currentStep;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return table;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Override getHeader method.
|
// Override getHeader method.
|
||||||
getHeader: function () {
|
getHeader: function () {
|
||||||
if (this.config.appendLocationNameToHeader) {
|
return "deprecated weatherforecast";
|
||||||
if (this.data.header) return this.data.header + " " + this.fetchedLocationName;
|
|
||||||
else return this.fetchedLocationName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.data.header ? this.data.header : "";
|
|
||||||
},
|
|
||||||
|
|
||||||
// Override notification handler.
|
|
||||||
notificationReceived: function (notification, payload, sender) {
|
|
||||||
if (notification === "DOM_OBJECTS_CREATED") {
|
|
||||||
if (this.config.appendLocationNameToHeader) {
|
|
||||||
this.hide(0, { lockString: this.identifier });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (notification === "CALENDAR_EVENTS") {
|
|
||||||
var senderClasses = sender.data.classes.toLowerCase().split(" ");
|
|
||||||
if (senderClasses.indexOf(this.config.calendarClass.toLowerCase()) !== -1) {
|
|
||||||
this.firstEvent = false;
|
|
||||||
|
|
||||||
for (var e in payload) {
|
|
||||||
var event = payload[e];
|
|
||||||
if (event.location || event.geo) {
|
|
||||||
this.firstEvent = event;
|
|
||||||
//Log.log("First upcoming event with location: ", event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/* updateWeather(compliments)
|
|
||||||
* Requests new data from openweather.org.
|
|
||||||
* Calls processWeather on successful response.
|
|
||||||
*/
|
|
||||||
updateWeather: function () {
|
|
||||||
if (this.config.appid === "") {
|
|
||||||
Log.error("WeatherForecast: APPID not set!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var url = this.config.apiBase + this.config.apiVersion + "/" + this.config.forecastEndpoint + this.getParams();
|
|
||||||
var self = this;
|
|
||||||
var retry = true;
|
|
||||||
|
|
||||||
var weatherRequest = new XMLHttpRequest();
|
|
||||||
weatherRequest.open("GET", url, true);
|
|
||||||
weatherRequest.onreadystatechange = function () {
|
|
||||||
if (this.readyState === 4) {
|
|
||||||
if (this.status === 200) {
|
|
||||||
self.processWeather(JSON.parse(this.response));
|
|
||||||
} else if (this.status === 401) {
|
|
||||||
self.updateDom(self.config.animationSpeed);
|
|
||||||
|
|
||||||
if (self.config.forecastEndpoint === "forecast/daily") {
|
|
||||||
self.config.forecastEndpoint = "forecast";
|
|
||||||
Log.warn(self.name + ": Your AppID does not support long term forecasts. Switching to fallback endpoint.");
|
|
||||||
}
|
|
||||||
|
|
||||||
retry = true;
|
|
||||||
} else {
|
|
||||||
Log.error(self.name + ": Could not load weather.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retry) {
|
|
||||||
self.scheduleUpdate(self.loaded ? -1 : self.config.retryDelay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
weatherRequest.send();
|
|
||||||
},
|
|
||||||
|
|
||||||
/* getParams(compliments)
|
|
||||||
* Generates an url with api parameters based on the config.
|
|
||||||
*
|
|
||||||
* return String - URL params.
|
|
||||||
*/
|
|
||||||
getParams: function () {
|
|
||||||
var params = "?";
|
|
||||||
if (this.config.locationID) {
|
|
||||||
params += "id=" + this.config.locationID;
|
|
||||||
} else if (this.config.lat && this.config.lon) {
|
|
||||||
params += "lat=" + this.config.lat + "&lon=" + this.config.lon;
|
|
||||||
} else if (this.config.location) {
|
|
||||||
params += "q=" + this.config.location;
|
|
||||||
} else if (this.firstEvent && this.firstEvent.geo) {
|
|
||||||
params += "lat=" + this.firstEvent.geo.lat + "&lon=" + this.firstEvent.geo.lon;
|
|
||||||
} else if (this.firstEvent && this.firstEvent.location) {
|
|
||||||
params += "q=" + this.firstEvent.location;
|
|
||||||
} else {
|
|
||||||
this.hide(this.config.animationSpeed, { lockString: this.identifier });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let numberOfDays;
|
|
||||||
if (this.config.forecastEndpoint === "forecast") {
|
|
||||||
numberOfDays = this.config.maxNumberOfDays < 1 || this.config.maxNumberOfDays > 5 ? 5 : this.config.maxNumberOfDays;
|
|
||||||
// don't get forecasts for the next day, as it would not represent the whole day
|
|
||||||
numberOfDays = numberOfDays * 8 - (Math.round(new Date().getHours() / 3) % 8);
|
|
||||||
} else {
|
|
||||||
numberOfDays = this.config.maxNumberOfDays < 1 || this.config.maxNumberOfDays > 17 ? 7 : this.config.maxNumberOfDays;
|
|
||||||
}
|
|
||||||
params += "&cnt=" + numberOfDays;
|
|
||||||
|
|
||||||
params += "&exclude=" + this.config.excludes;
|
|
||||||
params += "&units=" + this.config.units;
|
|
||||||
params += "&lang=" + this.config.lang;
|
|
||||||
params += "&APPID=" + this.config.appid;
|
|
||||||
|
|
||||||
return params;
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* parserDataWeather(data)
|
|
||||||
*
|
|
||||||
* Use the parse to keep the same struct between daily and forecast Endpoint
|
|
||||||
* from openweather.org
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
parserDataWeather: function (data) {
|
|
||||||
if (data.hasOwnProperty("main")) {
|
|
||||||
data["temp"] = { min: data.main.temp_min, max: data.main.temp_max };
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* processWeather(data)
|
|
||||||
* Uses the received data to set the various values.
|
|
||||||
*
|
|
||||||
* argument data object - Weather information received form openweather.org.
|
|
||||||
*/
|
|
||||||
processWeather: function (data, momenttz) {
|
|
||||||
let mom = momenttz ? momenttz : moment; // Exception last.
|
|
||||||
|
|
||||||
// Forcast16 (paid) API endpoint provides this data. Onecall endpoint
|
|
||||||
// does not.
|
|
||||||
if (data.city) {
|
|
||||||
this.fetchedLocationName = data.city.name + ", " + data.city.country;
|
|
||||||
} else if (this.config.location) {
|
|
||||||
this.fetchedLocationName = this.config.location;
|
|
||||||
} else {
|
|
||||||
this.fetchedLocationName = "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
this.forecast = [];
|
|
||||||
var lastDay = null;
|
|
||||||
var forecastData = {};
|
|
||||||
var dayStarts = 8;
|
|
||||||
var dayEnds = 17;
|
|
||||||
|
|
||||||
if (data.city && data.city.sunrise && data.city.sunset) {
|
|
||||||
dayStarts = new Date(mom.unix(data.city.sunrise).locale("en").format("YYYY/MM/DD HH:mm:ss")).getHours();
|
|
||||||
dayEnds = new Date(mom.unix(data.city.sunset).locale("en").format("YYYY/MM/DD HH:mm:ss")).getHours();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle different structs between forecast16 and onecall endpoints
|
|
||||||
var forecastList = null;
|
|
||||||
if (data.list) {
|
|
||||||
forecastList = data.list;
|
|
||||||
} else if (data.daily) {
|
|
||||||
forecastList = data.daily;
|
|
||||||
} else {
|
|
||||||
Log.error("Unexpected forecast data");
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0, count = forecastList.length; i < count; i++) {
|
|
||||||
var forecast = forecastList[i];
|
|
||||||
forecast = this.parserDataWeather(forecast); // hack issue #1017
|
|
||||||
|
|
||||||
var day;
|
|
||||||
var hour;
|
|
||||||
if (forecast.dt_txt) {
|
|
||||||
day = mom(forecast.dt_txt, "YYYY-MM-DD hh:mm:ss").format("ddd");
|
|
||||||
hour = new Date(mom(forecast.dt_txt).locale("en").format("YYYY-MM-DD HH:mm:ss")).getHours();
|
|
||||||
} else {
|
|
||||||
day = mom(forecast.dt, "X").format("ddd");
|
|
||||||
hour = new Date(mom(forecast.dt, "X")).getHours();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (day !== lastDay) {
|
|
||||||
forecastData = {
|
|
||||||
day: day,
|
|
||||||
icon: this.config.iconTable[forecast.weather[0].icon],
|
|
||||||
maxTemp: this.roundValue(forecast.temp.max),
|
|
||||||
minTemp: this.roundValue(forecast.temp.min),
|
|
||||||
rain: this.processRain(forecast, forecastList, mom)
|
|
||||||
};
|
|
||||||
this.forecast.push(forecastData);
|
|
||||||
lastDay = day;
|
|
||||||
|
|
||||||
// Stop processing when maxNumberOfDays is reached
|
|
||||||
if (this.forecast.length === this.config.maxNumberOfDays) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Log.log("Compare max: ", forecast.temp.max, parseFloat(forecastData.maxTemp));
|
|
||||||
forecastData.maxTemp = forecast.temp.max > parseFloat(forecastData.maxTemp) ? this.roundValue(forecast.temp.max) : forecastData.maxTemp;
|
|
||||||
//Log.log("Compare min: ", forecast.temp.min, parseFloat(forecastData.minTemp));
|
|
||||||
forecastData.minTemp = forecast.temp.min < parseFloat(forecastData.minTemp) ? this.roundValue(forecast.temp.min) : forecastData.minTemp;
|
|
||||||
|
|
||||||
// Since we don't want an icon from the start of the day (in the middle of the night)
|
|
||||||
// we update the icon as long as it's somewhere during the day.
|
|
||||||
if (hour > dayStarts && hour < dayEnds) {
|
|
||||||
forecastData.icon = this.config.iconTable[forecast.weather[0].icon];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Log.log(this.forecast);
|
|
||||||
this.show(this.config.animationSpeed, { lockString: this.identifier });
|
|
||||||
this.loaded = true;
|
|
||||||
this.updateDom(this.config.animationSpeed);
|
|
||||||
},
|
|
||||||
|
|
||||||
/* scheduleUpdate()
|
|
||||||
* Schedule next update.
|
|
||||||
*
|
|
||||||
* argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used.
|
|
||||||
*/
|
|
||||||
scheduleUpdate: function (delay) {
|
|
||||||
var nextLoad = this.config.updateInterval;
|
|
||||||
if (typeof delay !== "undefined" && delay >= 0) {
|
|
||||||
nextLoad = delay;
|
|
||||||
}
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
clearTimeout(this.updateTimer);
|
|
||||||
this.updateTimer = setTimeout(function () {
|
|
||||||
self.updateWeather();
|
|
||||||
}, nextLoad);
|
|
||||||
},
|
|
||||||
|
|
||||||
/* ms2Beaufort(ms)
|
|
||||||
* Converts m2 to beaufort (windspeed).
|
|
||||||
*
|
|
||||||
* see:
|
|
||||||
* https://www.spc.noaa.gov/faq/tornado/beaufort.html
|
|
||||||
* https://en.wikipedia.org/wiki/Beaufort_scale#Modern_scale
|
|
||||||
*
|
|
||||||
* argument ms number - Windspeed in m/s.
|
|
||||||
*
|
|
||||||
* return number - Windspeed in beaufort.
|
|
||||||
*/
|
|
||||||
ms2Beaufort: function (ms) {
|
|
||||||
var kmh = (ms * 60 * 60) / 1000;
|
|
||||||
var speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
|
|
||||||
for (var beaufort in speeds) {
|
|
||||||
var speed = speeds[beaufort];
|
|
||||||
if (speed > kmh) {
|
|
||||||
return beaufort;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 12;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* function(temperature)
|
|
||||||
* Rounds a temperature to 1 decimal or integer (depending on config.roundTemp).
|
|
||||||
*
|
|
||||||
* argument temperature number - Temperature.
|
|
||||||
*
|
|
||||||
* return string - Rounded Temperature.
|
|
||||||
*/
|
|
||||||
roundValue: function (temperature) {
|
|
||||||
var decimals = this.config.roundTemp ? 0 : 1;
|
|
||||||
var roundValue = parseFloat(temperature).toFixed(decimals);
|
|
||||||
return roundValue === "-0" ? 0 : roundValue;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* processRain(forecast, allForecasts)
|
|
||||||
* Calculates the amount of rain for a whole day even if long term forecasts isn't available for the appid.
|
|
||||||
*
|
|
||||||
* When using the the fallback endpoint forecasts are provided in 3h intervals and the rain-property is an object instead of number.
|
|
||||||
* That object has a property "3h" which contains the amount of rain since the previous forecast in the list.
|
|
||||||
* This code finds all forecasts that is for the same day and sums the amount of rain and returns that.
|
|
||||||
*/
|
|
||||||
processRain: function (forecast, allForecasts, momenttz) {
|
|
||||||
let mom = momenttz ? momenttz : moment; // Exception last.
|
|
||||||
|
|
||||||
//If the amount of rain actually is a number, return it
|
|
||||||
if (!isNaN(forecast.rain)) {
|
|
||||||
return forecast.rain;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Find all forecasts that is for the same day
|
|
||||||
var checkDateTime = forecast.dt_txt ? mom(forecast.dt_txt, "YYYY-MM-DD hh:mm:ss") : mom(forecast.dt, "X");
|
|
||||||
var daysForecasts = allForecasts.filter(function (item) {
|
|
||||||
var itemDateTime = item.dt_txt ? mom(item.dt_txt, "YYYY-MM-DD hh:mm:ss") : mom(item.dt, "X");
|
|
||||||
return itemDateTime.isSame(checkDateTime, "day") && item.rain instanceof Object;
|
|
||||||
});
|
|
||||||
|
|
||||||
//If no rain this day return undefined so it wont be displayed for this day
|
|
||||||
if (daysForecasts.length === 0) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Summarize all the rain from the matching days
|
|
||||||
return daysForecasts
|
|
||||||
.map(function (item) {
|
|
||||||
return Object.values(item.rain)[0];
|
|
||||||
})
|
|
||||||
.reduce(function (a, b) {
|
|
||||||
return a + b;
|
|
||||||
}, 0);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
4180
package-lock.json
generated
4180
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
47
package.json
47
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "magicmirror",
|
"name": "magicmirror",
|
||||||
"version": "2.18.0",
|
"version": "2.19.0",
|
||||||
"description": "The open source modular smart mirror platform.",
|
"description": "The open source modular smart mirror platform.",
|
||||||
"main": "js/electron.js",
|
"main": "js/electron.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -9,7 +9,7 @@
|
|||||||
"server": "node ./serveronly",
|
"server": "node ./serveronly",
|
||||||
"install": "echo \"Installing vendor files ...\n\" && cd vendor && npm install --loglevel=error",
|
"install": "echo \"Installing vendor files ...\n\" && cd vendor && npm install --loglevel=error",
|
||||||
"install-fonts": "echo \"Installing fonts ...\n\" && cd fonts && npm install --loglevel=error",
|
"install-fonts": "echo \"Installing fonts ...\n\" && cd fonts && npm install --loglevel=error",
|
||||||
"postinstall": "npm run install-fonts && echo \"MagicMirror installation finished successfully! \n\"",
|
"postinstall": "npm run install-fonts && echo \"MagicMirror² installation finished successfully! \n\"",
|
||||||
"test": "NODE_ENV=test jest -i --forceExit",
|
"test": "NODE_ENV=test jest -i --forceExit",
|
||||||
"test:coverage": "NODE_ENV=test nyc --reporter=lcov --reporter=text 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:electron": "NODE_ENV=test jest --selectProjects electron -i --forceExit",
|
||||||
@ -32,6 +32,7 @@
|
|||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"magic mirror",
|
"magic mirror",
|
||||||
|
"magicmirror",
|
||||||
"smart mirror",
|
"smart mirror",
|
||||||
"mirror UI",
|
"mirror UI",
|
||||||
"modular"
|
"modular"
|
||||||
@ -46,55 +47,57 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://magicmirror.builders",
|
"homepage": "https://magicmirror.builders",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-jest": "^25.3.0",
|
"eslint-plugin-jest": "^26.1.3",
|
||||||
"eslint-plugin-jsdoc": "^37.4.0",
|
"eslint-plugin-jsdoc": "^38.1.3",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"express-basic-auth": "^1.2.1",
|
"express-basic-auth": "^1.2.1",
|
||||||
"husky": "^7.0.4",
|
"husky": "^7.0.4",
|
||||||
"jest": "^27.4.5",
|
"jest": "^27.5.1",
|
||||||
"jsdom": "^19.0.0",
|
"jsdom": "^19.0.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"playwright": "^1.17.1",
|
"playwright": "^1.20.1",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.6.1",
|
||||||
"pretty-quick": "^3.1.3",
|
"pretty-quick": "^3.1.3",
|
||||||
"sinon": "^12.0.1",
|
"sinon": "^13.0.1",
|
||||||
"stylelint": "^14.2.0",
|
"stylelint": "^14.6.1",
|
||||||
"stylelint-config-prettier": "^9.0.3",
|
"stylelint-config-prettier": "^9.0.3",
|
||||||
"stylelint-config-standard": "^24.0.0",
|
"stylelint-config-standard": "^25.0.0",
|
||||||
"stylelint-prettier": "^2.0.0",
|
"stylelint-prettier": "^2.0.0",
|
||||||
"suncalc": "^1.8.0"
|
"suncalc": "^1.9.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"electron": "^16.0.5"
|
"electron": "^17.2.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
"console-stamp": "^3.0.3",
|
"console-stamp": "^3.0.4",
|
||||||
"digest-fetch": "^1.2.1",
|
"digest-fetch": "^1.2.1",
|
||||||
"eslint": "^8.5.0",
|
"eslint": "^8.12.0",
|
||||||
"express": "^4.17.2",
|
"express": "^4.17.3",
|
||||||
"express-ipfilter": "^1.2.0",
|
"express-ipfilter": "^1.2.0",
|
||||||
"feedme": "^2.0.2",
|
"feedme": "^2.0.2",
|
||||||
"helmet": "^4.6.0",
|
"helmet": "^5.0.2",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
|
"luxon": "^1.21.3",
|
||||||
"module-alias": "^2.2.2",
|
"module-alias": "^2.2.2",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"node-fetch": "^2.6.6",
|
"node-fetch": "^2.6.7",
|
||||||
"node-ical": "^0.13.0",
|
"node-ical": "^0.15.1",
|
||||||
"socket.io": "^4.4.0"
|
"socket.io": "^4.4.1"
|
||||||
},
|
},
|
||||||
"_moduleAliases": {
|
"_moduleAliases": {
|
||||||
"node_helper": "js/node_helper.js",
|
"node_helper": "js/node_helper.js",
|
||||||
"logger": "js/logger.js"
|
"logger": "js/logger.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=14"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"verbose": true,
|
"verbose": true,
|
||||||
"testTimeout": 10000,
|
"testTimeout": 20000,
|
||||||
|
"testSequencer": "<rootDir>/tests/configs/test_sequencer.js",
|
||||||
"projects": [
|
"projects": [
|
||||||
{
|
{
|
||||||
"displayName": "unit",
|
"displayName": "unit",
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test default config for modules
|
/* MagicMirror² Test default config for modules
|
||||||
*
|
*
|
||||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test config sample ipWhitelist
|
/* MagicMirror² Test config sample ipWhitelist
|
||||||
*
|
*
|
||||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test config sample module alert
|
/* MagicMirror² Test config sample module alert
|
||||||
*
|
*
|
||||||
* By rejas
|
* By rejas
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test config default calendar with auth by default
|
/* MagicMirror² Test config default calendar with auth by default
|
||||||
*
|
*
|
||||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test config default calendar
|
/* MagicMirror² Test config default calendar
|
||||||
*
|
*
|
||||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test config default calendar with auth by default
|
/* MagicMirror² Test config default calendar with auth by default
|
||||||
*
|
*
|
||||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test config custom calendar
|
/* MagicMirror² Test config custom calendar
|
||||||
*
|
*
|
||||||
* By Rejas
|
* By Rejas
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test config default calendar
|
/* MagicMirror² Test config default calendar
|
||||||
*
|
*
|
||||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test calendar calendar
|
/* MagicMirror² Test calendar calendar
|
||||||
*
|
*
|
||||||
* This configuration is a wrong authentication
|
* This configuration is a wrong authentication
|
||||||
*
|
*
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test config default calendar
|
/* MagicMirror² Test config default calendar
|
||||||
* with authentication old config
|
* with authentication old config
|
||||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test config custom calendar
|
/* MagicMirror² Test config custom calendar
|
||||||
*
|
*
|
||||||
* By Rejas
|
* By Rejas
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test config for default clock module
|
/* MagicMirror² Test config for default clock module
|
||||||
*
|
*
|
||||||
* By Sergey Morozov
|
* By Sergey Morozov
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test config for default clock module
|
/* MagicMirror² Test config for default clock module
|
||||||
*
|
*
|
||||||
* By Sergey Morozov
|
* By Sergey Morozov
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Magic Mirror Test config for analog clock face
|
/* MagicMirror² Test config for analog clock face
|
||||||
*
|
*
|
||||||
* MIT Licensed.
|
* MIT Licensed.
|
||||||
*/
|
*/
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user