mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-07-01 13:33:15 +00:00
commit
e6fb18df56
@ -1,6 +1,6 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- "7"
|
- "8"
|
||||||
before_script:
|
before_script:
|
||||||
- yarn danger ci
|
- yarn danger ci
|
||||||
- npm install grunt-cli -g
|
- npm install grunt-cli -g
|
||||||
|
101
CHANGELOG.md
101
CHANGELOG.md
@ -5,6 +5,71 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror² core.
|
||||||
|
|
||||||
|
## [2.8.0] - Unreleased
|
||||||
|
|
||||||
|
*This release is scheduled to be released on 2019-04-01.*
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Updated
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
## [2.7.0] - 2019-04-01
|
||||||
|
|
||||||
|
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`. If you are having issues running Electron, make sure your [Raspbian is up to date](https://www.raspberrypi.org/documentation/raspbian/updating.md).
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Italian translation for "Feels"
|
||||||
|
- Basic Klingon (tlhIngan Hol) translations
|
||||||
|
- Disabled the screensaver on raspbian with installation script
|
||||||
|
- Added option to truncate the number of vertical lines a calendar item can span if `wrapEvents` is enabled.
|
||||||
|
- Danish translation for "Feels" and "Weeks"
|
||||||
|
- Added option to split multiple day events in calendar to separate numbered events
|
||||||
|
- Slovakian translation
|
||||||
|
- Alerts now can contain Font Awesome icons
|
||||||
|
- Notifications display time can be set in request
|
||||||
|
- Newsfeed: added support for `ARTICLE_INFO_REQUEST` notification
|
||||||
|
- Add `name` config option for calendars to be sent along with event broadcasts
|
||||||
|
|
||||||
|
### Updated
|
||||||
|
- Bumped the Electron dependency to v3.0.13 to support the most recent Raspbian. [#1500](https://github.com/MichMich/MagicMirror/issues/1500)
|
||||||
|
- Updated modernizr code in alert module, fixed a small typo there too
|
||||||
|
- More verbose error message on console if the config is malformed
|
||||||
|
- Updated installer script to install Node.js version 10.x
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed temperature displays in currentweather and weatherforecast modules [#1503](https://github.com/MichMich/MagicMirror/issues/1503), [#1511](https://github.com/MichMich/MagicMirror/issues/1511).
|
||||||
|
- 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).
|
||||||
|
- 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).
|
||||||
|
- 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: Fix exdate handling when multiple values are specified (comma separated)
|
||||||
|
- Calendar: Fix relative date handling for fulldate events, calculate difference always from start of day [#1572](https://github.com/MichMich/MagicMirror/issues/1572)
|
||||||
|
- Fix null dereference in moduleNeedsUpdate when the module isn't visible
|
||||||
|
- Calendar: Fixed event end times by setting default calendarEndTime to "LT" (Local time format). [#1479]
|
||||||
|
- Calendar: Fixed missing calendar fetchers after server process restarts [#1589](https://github.com/MichMich/MagicMirror/issues/1589)
|
||||||
|
- Notification: fixed background color (was white text on white background)
|
||||||
|
- Use getHeader instead of data.header when creating the DOM so overwriting the function also propagates into it
|
||||||
|
- Fix documentation of `useKMPHwind` option in currentweather
|
||||||
|
|
||||||
|
### New weather module
|
||||||
|
- Fixed weather forecast table display [#1499](https://github.com/MichMich/MagicMirror/issues/1499).
|
||||||
|
- Dimmed loading indicator for weather forecast.
|
||||||
|
- Implemented config option `decimalSymbol` [#1499](https://github.com/MichMich/MagicMirror/issues/1499).
|
||||||
|
- Aligned indoor values in current weather vertical [#1499](https://github.com/MichMich/MagicMirror/issues/1499).
|
||||||
|
- Added humidity support to nunjuck unit filter.
|
||||||
|
- Do not display degree symbol for temperature in Kelvin [#1503](https://github.com/MichMich/MagicMirror/issues/1503).
|
||||||
|
- Weather forecast now works with openweathermap for both, `/forecast` and `/forecast/daily`, in new weather module. If you use the `/forecast`-weatherEndpoint, the hourly data are converted to daily data, see issues [#1504](https://github.com/MichMich/MagicMirror/issues/1504), [#1513](https://github.com/MichMich/MagicMirror/issues/1513).
|
||||||
|
- Added fade, fadePoint and maxNumberOfDays properties to the forecast mode [#1516](https://github.com/MichMich/MagicMirror/issues/1516)
|
||||||
|
- Fixed Loading string and decimalSymbol string replace [#1538](https://github.com/MichMich/MagicMirror/issues/1538)
|
||||||
|
- Show Snow amounts in new weather module [#1545](https://github.com/MichMich/MagicMirror/issues/1545)
|
||||||
|
- Added weather.gov as a new weather provider for US locations
|
||||||
|
|
||||||
## [2.6.0] - 2019-01-01
|
## [2.6.0] - 2019-01-01
|
||||||
|
|
||||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`. If you are having issues updating, make sure you are running the latest version of Node.
|
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`. If you are having issues updating, make sure you are running the latest version of Node.
|
||||||
@ -12,6 +77,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
### ✨ Experimental ✨
|
### ✨ Experimental ✨
|
||||||
- New default [module weather](modules/default/weather). This module will eventually replace the current `currentweather` and `weatherforecast` modules. The new module is still pretty experimental, but it's included so you can give it a try and help us improve this module. Please give us you feedback using [this forum post](https://forum.magicmirror.builders/topic/9335/default-weather-module-refactoring).
|
- New default [module weather](modules/default/weather). This module will eventually replace the current `currentweather` and `weatherforecast` modules. The new module is still pretty experimental, but it's included so you can give it a try and help us improve this module. Please give us you feedback using [this forum post](https://forum.magicmirror.builders/topic/9335/default-weather-module-refactoring).
|
||||||
|
|
||||||
|
A huge, huge, huge thanks to user @fewieden for all his hard work on the new `weather` module!
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Possibility to add classes to the cell of symbol, title and time of the events of calendar.
|
- Possibility to add classes to the cell of symbol, title and time of the events of calendar.
|
||||||
- Font-awesome 5, still has 4 for backwards compatibility.
|
- Font-awesome 5, still has 4 for backwards compatibility.
|
||||||
@ -60,7 +127,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Fixed gzip encoded calendar loading issue #1400.
|
- Fixed gzip encoded calendar loading issue #1400.
|
||||||
- Mixup between german and spanish translation for newsfeed.
|
- Mixup between german and spanish translation for newsfeed.
|
||||||
- Fixed close dates to be absolute, if no configured in the config.js - module Calendar
|
- Fixed close dates to be absolute, if no configured in the config.js - module Calendar
|
||||||
- Fixed the UpdateNotification module message about new commits in the repository, so they can be correctly localized in singular and plural form.
|
- Fixed the updatenotification module message about new commits in the repository, so they can be correctly localized in singular and plural form.
|
||||||
- Fix for weatherforecast rainfall rounding [#1374](https://github.com/MichMich/MagicMirror/issues/1374)
|
- Fix for weatherforecast rainfall rounding [#1374](https://github.com/MichMich/MagicMirror/issues/1374)
|
||||||
- Fix calendar parsing issue for Midori on RasperryPi Zero w, related to issue #694.
|
- Fix calendar parsing issue for Midori on RasperryPi Zero w, related to issue #694.
|
||||||
- Fix weather city ID link in sample config
|
- Fix weather city ID link in sample config
|
||||||
@ -210,7 +277,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Add `clientonly` script to start only the electron client for a remote server.
|
- Add `clientonly` script to start only the electron client for a remote server.
|
||||||
- Add symbol and color properties of event when `CALENDAR_EVENTS` notification is broadcasted from `default/calendar` module.
|
- Add symbol and color properties of event when `CALENDAR_EVENTS` notification is broadcasted from `default/calendar` module.
|
||||||
- Add `.vscode/` folder to `.gitignore` to keep custom Visual Studio Code config out of git.
|
- Add `.vscode/` folder to `.gitignore` to keep custom Visual Studio Code config out of git.
|
||||||
- Add unit test the capitalizeFirstLetter function of newfeed module.
|
- Add unit test the capitalizeFirstLetter function of newsfeed module.
|
||||||
- Add new unit tests for function `shorten` in calendar module.
|
- Add new unit tests for function `shorten` in calendar module.
|
||||||
- Add new unit tests for function `getLocaleSpecification` in calendar module.
|
- Add new unit tests for function `getLocaleSpecification` in calendar module.
|
||||||
- Add unit test for js/class.js.
|
- Add unit test for js/class.js.
|
||||||
@ -231,7 +298,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Set version of the `express-ipfilter` on 0.3.1.
|
- Set version of the `express-ipfilter` on 0.3.1.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed issue with incorrect allignment of analog clock when displayed in the center column of the MM.
|
- Fixed issue with incorrect alignment of analog clock when displayed in the center column of the MM.
|
||||||
- Fixed ipWhitelist behaviour to make empty whitelist ([]) allow any and all hosts access to the MM.
|
- Fixed ipWhitelist behaviour to make empty whitelist ([]) allow any and all hosts access to the MM.
|
||||||
- Fixed issue with calendar module where 'excludedEvents' count towards 'maximumEntries'.
|
- Fixed issue with calendar module where 'excludedEvents' count towards 'maximumEntries'.
|
||||||
- Fixed issue with calendar module where global configuration of maximumEntries was not overridden by calendar specific config (see module doc).
|
- Fixed issue with calendar module where global configuration of maximumEntries was not overridden by calendar specific config (see module doc).
|
||||||
@ -255,7 +322,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Add unit test calendar_modules function capFirst.
|
- Add unit test calendar_modules function capFirst.
|
||||||
- Add test for check if exists the directories present in defaults modules.
|
- Add test for check if exists the directories present in defaults modules.
|
||||||
- Add support for showing wind direction as an arrow instead of abbreviation in currentWeather module.
|
- Add support for showing wind direction as an arrow instead of abbreviation in currentWeather module.
|
||||||
- Add support for writing translation fucntions to support flexible word order
|
- Add support for writing translation functions to support flexible word order
|
||||||
- Add test for check if exits the directories present in defaults modules.
|
- Add test for check if exits the directories present in defaults modules.
|
||||||
- Add calendar option to set a separate date format for full day events.
|
- Add calendar option to set a separate date format for full day events.
|
||||||
- Add ability for `currentweather` module to display indoor temperature via INDOOR_TEMPERATURE notification
|
- Add ability for `currentweather` module to display indoor temperature via INDOOR_TEMPERATURE notification
|
||||||
@ -274,7 +341,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Fix double message about port when server is starting
|
- Fix double message about port when server is starting
|
||||||
- Corrected Swedish translations for TODAY/TOMORROW/DAYAFTERTOMORROW.
|
- Corrected Swedish translations for TODAY/TOMORROW/DAYAFTERTOMORROW.
|
||||||
- Removed unused import from js/electron.js
|
- Removed unused import from js/electron.js
|
||||||
- Made calendar.js respect config.timeFormat irrespecive of locale setting.
|
- Made calendar.js respect config.timeFormat irrespective of locale setting.
|
||||||
- Fixed alignment of analog clock when a large calendar is displayed in the same side bar.
|
- Fixed alignment of analog clock when a large calendar is displayed in the same side bar.
|
||||||
|
|
||||||
## [2.1.1] - 2017-04-01
|
## [2.1.1] - 2017-04-01
|
||||||
@ -292,7 +359,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Added `DAYAFTERTOMORROW`, `UPDATE_NOTIFICATION` and `UPDATE_NOTIFICATION_MODULE` to Finnish translations.
|
- Added `DAYAFTERTOMORROW`, `UPDATE_NOTIFICATION` and `UPDATE_NOTIFICATION_MODULE` to Finnish translations.
|
||||||
- Run `npm test` on Travis automatically.
|
- Run `npm test` on Travis automatically.
|
||||||
- Show the splash screen image even when is reboot or halted.
|
- Show the splash screen image even when is reboot or halted.
|
||||||
- Added some missing translaton strings in the sv.json file.
|
- Added some missing translation strings in the sv.json file.
|
||||||
- Run task jsonlint to check translation files.
|
- Run task jsonlint to check translation files.
|
||||||
- Restructured Test Suite.
|
- Restructured Test Suite.
|
||||||
|
|
||||||
@ -309,12 +376,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Option to use RegExp in Calendar's titleReplace.
|
- Option to use RegExp in Calendar's titleReplace.
|
||||||
- Hungarian Translation.
|
- Hungarian Translation.
|
||||||
- Icelandic Translation.
|
- Icelandic Translation.
|
||||||
- Add use a script to prevent when is run by SSH session set DISPLAY enviroment.
|
- Add use a script to prevent when is run by SSH session set DISPLAY environment.
|
||||||
- Enable ability to set configuration file by the enviroment variable called MM_CONFIG_FILE.
|
- Enable ability to set configuration file by the environment variable called MM_CONFIG_FILE.
|
||||||
- Option to give each calendar a different color.
|
- Option to give each calendar a different color.
|
||||||
- Option for colored min-temp and max-temp.
|
- Option for colored min-temp and max-temp.
|
||||||
- Add test e2e helloworld.
|
- Add test e2e helloworld.
|
||||||
- Add test e2e enviroment.
|
- Add test e2e environment.
|
||||||
- Add `chai-as-promised` npm module to devDependencies.
|
- Add `chai-as-promised` npm module to devDependencies.
|
||||||
- Basic set of tests for clock module.
|
- Basic set of tests for clock module.
|
||||||
- Run e2e test in Travis.
|
- Run e2e test in Travis.
|
||||||
@ -332,10 +399,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Added tests for Translations, dev argument, version, dev console.
|
- Added tests for Translations, dev argument, version, dev console.
|
||||||
- Added test anytime feature compliments module.
|
- Added test anytime feature compliments module.
|
||||||
- Added test ipwhitelist configuration directive.
|
- Added test ipwhitelist configuration directive.
|
||||||
- Added test for calendar module: default, basic-auth, backward compability, fail-basic-auth.
|
- Added test for calendar module: default, basic-auth, backward compatibility, fail-basic-auth.
|
||||||
- Added meta tags to support fullscreen mode on iOS (for server mode)
|
- Added meta tags to support fullscreen mode on iOS (for server mode)
|
||||||
- Added `ignoreOldItems` and `ignoreOlderThan` options to the News Feed module
|
- Added `ignoreOldItems` and `ignoreOlderThan` options to the News Feed module
|
||||||
- Added test for MM_PORT enviroment variable.
|
- Added test for MM_PORT environment variable.
|
||||||
- Added a configurable Week section to the clock module.
|
- Added a configurable Week section to the clock module.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
@ -347,7 +414,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Module currentWeather: check if temperature received from api is defined.
|
- Module currentWeather: check if temperature received from api is defined.
|
||||||
- Fix an issue with module hidden status changing to `true` although lock string prevented showing it.
|
- Fix an issue with module hidden status changing to `true` although lock string prevented showing it.
|
||||||
- Fix newsfeed module bug (removeStartTags)
|
- Fix newsfeed module bug (removeStartTags)
|
||||||
- Fix when is set MM_PORT enviroment variable.
|
- Fix when is set MM_PORT environment variable.
|
||||||
- Fixed missing animation on `this.show(speed)` when module is alone in a region.
|
- Fixed missing animation on `this.show(speed)` when module is alone in a region.
|
||||||
|
|
||||||
## [2.1.0] - 2016-12-31
|
## [2.1.0] - 2016-12-31
|
||||||
@ -369,8 +436,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- 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 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 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
|
||||||
- Add module `updatenotification` to get an update whenever a new version is availabe. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules/default/updatenotification) for more information.
|
- Add module `updatenotification` to get an update whenever a new version is available. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules/default/updatenotification) for more information.
|
||||||
- Add the abilty to set timezone on the date display in the Clock Module
|
- Add the ability to set timezone on the date display in the Clock Module
|
||||||
- Ability to set date format in calendar module
|
- Ability to set date format in calendar module
|
||||||
- Possibility to use currentweather for the compliments
|
- Possibility to use currentweather for the compliments
|
||||||
- Added option `disabled` for modules.
|
- Added option `disabled` for modules.
|
||||||
@ -409,7 +476,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Added ability to define "the day after tomorrow" for calendar events (Definition for German and Dutch already included).
|
- Added ability to define "the day after tomorrow" for calendar events (Definition for German and Dutch already included).
|
||||||
- Added CII Badge (we are compliant with the CII Best Practices)
|
- Added CII Badge (we are compliant with the CII Best Practices)
|
||||||
- Add support for doing http basic auth when loading calendars
|
- Add support for doing http basic auth when loading calendars
|
||||||
- Add the abilty to turn off and on the date display in the Clock Module
|
- Add the ability to turn off and on the date display in the Clock Module
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fix typo in installer.
|
- Fix typo in installer.
|
||||||
@ -432,8 +499,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Prevent `getModules()` selectors from returning duplicate entries.
|
- Prevent `getModules()` selectors from returning duplicate entries.
|
||||||
- Append endpoints of weather modules with `/` to retreive the correct data. (Issue [#337](https://github.com/MichMich/MagicMirror/issues/337))
|
- Append endpoints of weather modules with `/` to retrieve the correct data. (Issue [#337](https://github.com/MichMich/MagicMirror/issues/337))
|
||||||
- Corrected grammer in `module.js` from 'suspend' to 'suspended'.
|
- Corrected grammar in `module.js` from 'suspend' to 'suspended'.
|
||||||
- Fixed openweathermap.org URL in config sample.
|
- Fixed openweathermap.org URL in config sample.
|
||||||
- Prevent currentweather module from crashing when received data object is incorrect.
|
- Prevent currentweather module from crashing when received data object is incorrect.
|
||||||
- Fix issue where translation loading prevented the UI start-up when the language was set to 'en'. (Issue [#388](https://github.com/MichMich/MagicMirror/issues/388))
|
- Fix issue where translation loading prevented the UI start-up when the language was set to 'en'. (Issue [#388](https://github.com/MichMich/MagicMirror/issues/388))
|
||||||
|
21
README.md
21
README.md
@ -7,7 +7,6 @@
|
|||||||
<a href="http://choosealicense.com/licenses/mit"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
|
<a href="http://choosealicense.com/licenses/mit"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
|
||||||
<a href="https://travis-ci.org/MichMich/MagicMirror"><img src="https://travis-ci.org/MichMich/MagicMirror.svg" alt="Travis"></a>
|
<a href="https://travis-ci.org/MichMich/MagicMirror"><img src="https://travis-ci.org/MichMich/MagicMirror.svg" alt="Travis"></a>
|
||||||
<a href="https://snyk.io/test/github/MichMich/MagicMirror"><img src="https://snyk.io/test/github/MichMich/MagicMirror/badge.svg" alt="Known Vulnerabilities" data-canonical-src="https://snyk.io/test/github/MichMich/MagicMirror" style="max-width:100%;"></a>
|
<a href="https://snyk.io/test/github/MichMich/MagicMirror"><img src="https://snyk.io/test/github/MichMich/MagicMirror/badge.svg" alt="Known Vulnerabilities" data-canonical-src="https://snyk.io/test/github/MichMich/MagicMirror" style="max-width:100%;"></a>
|
||||||
<a href="http://slack.magicmirror.builders"><img src="http://slack.magicmirror.builders:3000/badge.svg" alt="Slack Status"></a>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
**MagicMirror²** is an open source modular smart mirror platform. With a growing list of installable modules, the **MagicMirror²** allows you to convert your hallway or bathroom mirror into your personal assistant. **MagicMirror²** is built by the creator of [the original MagicMirror](http://michaelteeuw.nl/tagged/magicmirror) with the incredible help of a [growing community of contributors](https://github.com/MichMich/MagicMirror/graphs/contributors).
|
**MagicMirror²** is an open source modular smart mirror platform. With a growing list of installable modules, the **MagicMirror²** allows you to convert your hallway or bathroom mirror into your personal assistant. **MagicMirror²** is built by the creator of [the original MagicMirror](http://michaelteeuw.nl/tagged/magicmirror) with the incredible help of a [growing community of contributors](https://github.com/MichMich/MagicMirror/graphs/contributors).
|
||||||
@ -16,18 +15,22 @@ MagicMirror² focuses on a modular plugin system and uses [Electron](http://elec
|
|||||||
|
|
||||||
## Table Of Contents
|
## Table Of Contents
|
||||||
|
|
||||||
|
- [Table Of Contents](#table-of-contents)
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
- [Raspberry Pi](#raspberry-pi)
|
- [Raspberry Pi](#raspberry-pi)
|
||||||
- [General](#general)
|
- [Automatic Installation (Raspberry Pi only!)](#automatic-installation-raspberry-pi-only)
|
||||||
|
- [Manual Installation](#manual-installation)
|
||||||
- [Server Only](#server-only)
|
- [Server Only](#server-only)
|
||||||
- [Client Only](#client-only)
|
- [Client Only](#client-only)
|
||||||
- [Docker](#docker)
|
- [Docker](#docker)
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
|
- [Raspberry Specific](#raspberry-specific)
|
||||||
|
- [General](#general)
|
||||||
- [Modules](#modules)
|
- [Modules](#modules)
|
||||||
- [Updating](#updating)
|
- [Updating](#updating)
|
||||||
- [Known Issues](#known-issues)
|
|
||||||
- [Community](#community)
|
- [Community](#community)
|
||||||
- [Contributing Guidelines](#contributing-guidelines)
|
- [Contributing Guidelines](#contributing-guidelines)
|
||||||
|
- [Enjoying MagicMirror? Consider a donation!](#enjoying-magicmirror-consider-a-donation)
|
||||||
- [Manifesto](#manifesto)
|
- [Manifesto](#manifesto)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@ -125,7 +128,7 @@ The following wiki links are helpful for the initial configuration of your Magic
|
|||||||
**Note:** If you used the installer script. This step is already done for you.
|
**Note:** If you used the installer script. This step is already done for you.
|
||||||
|
|
||||||
2. Modify your required settings. \
|
2. Modify your required settings. \
|
||||||
Note: You'll can check your configuration running `npm run config:check` in `/home/pi/MagicMirror`.
|
Note: You can check your configuration running `npm run config:check` in `/home/pi/MagicMirror`.
|
||||||
|
|
||||||
|
|
||||||
The following properties can be configured:
|
The following properties can be configured:
|
||||||
@ -199,6 +202,16 @@ Please keep the following in mind:
|
|||||||
|
|
||||||
Thanks for your help in making MagicMirror² better!
|
Thanks for your help in making MagicMirror² better!
|
||||||
|
|
||||||
|
|
||||||
|
## Enjoying MagicMirror? Consider a donation!
|
||||||
|
|
||||||
|
MagicMirror² is opensource and free. That doesn't mean we don't need any money.
|
||||||
|
|
||||||
|
Please consider a donation to help us cover the ongoing costs like webservers and email services.
|
||||||
|
If we recieve enough donations we might even be able to free up some working hours and spend some extra time improving the MagicMirror² core.
|
||||||
|
|
||||||
|
To donate, please follow [this](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=G5D8E9MR5DTD2&source=url) link.
|
||||||
|
|
||||||
## Manifesto
|
## Manifesto
|
||||||
|
|
||||||
A real Manifesto is still to be written. Till then, Michael's response on [one of the repository issues](https://github.com/MichMich/MagicMirror/issues/1174) gives a great summary:
|
A real Manifesto is still to be written. Till then, Michael's response on [one of the repository issues](https://github.com/MichMich/MagicMirror/issues/1174) gives a great summary:
|
||||||
|
@ -47,7 +47,7 @@ sudo apt-get --assume-yes install curl wget git build-essential unzip || exit
|
|||||||
# Check if we need to install or upgrade Node.js.
|
# Check if we need to install or upgrade Node.js.
|
||||||
echo -e "\e[96mCheck current Node installation ...\e[0m"
|
echo -e "\e[96mCheck current Node installation ...\e[0m"
|
||||||
NODE_INSTALL=false
|
NODE_INSTALL=false
|
||||||
if command_exists node; then
|
if command_exists node && command_exists npm; then
|
||||||
echo -e "\e[0mNode currently installed. Checking version number.";
|
echo -e "\e[0mNode currently installed. Checking version number.";
|
||||||
NODE_CURRENT=$(node -v)
|
NODE_CURRENT=$(node -v)
|
||||||
echo -e "\e[0mMinimum Node version: \e[1m$NODE_TESTED\e[0m"
|
echo -e "\e[0mMinimum Node version: \e[1m$NODE_TESTED\e[0m"
|
||||||
@ -82,7 +82,7 @@ if $NODE_INSTALL; then
|
|||||||
# The NODE_STABLE_BRANCH variable will need to be manually adjusted when a new branch is released. (e.g. 7.x)
|
# The NODE_STABLE_BRANCH variable will need to be manually adjusted when a new branch is released. (e.g. 7.x)
|
||||||
# Only tested (stable) versions are recommended as newer versions could break MagicMirror.
|
# Only tested (stable) versions are recommended as newer versions could break MagicMirror.
|
||||||
|
|
||||||
NODE_STABLE_BRANCH="9.x"
|
NODE_STABLE_BRANCH="10.x"
|
||||||
curl -sL https://deb.nodesource.com/setup_$NODE_STABLE_BRANCH | sudo -E bash -
|
curl -sL https://deb.nodesource.com/setup_$NODE_STABLE_BRANCH | sudo -E bash -
|
||||||
sudo apt-get install -y nodejs
|
sudo apt-get install -y nodejs
|
||||||
echo -e "\e[92mNode.js installation Done!\e[0m"
|
echo -e "\e[92mNode.js installation Done!\e[0m"
|
||||||
@ -152,9 +152,19 @@ fi
|
|||||||
read -p "Do you want use pm2 for auto starting of your MagicMirror (y/N)?" choice
|
read -p "Do you want use pm2 for auto starting of your MagicMirror (y/N)?" choice
|
||||||
if [[ $choice =~ ^[Yy]$ ]]; then
|
if [[ $choice =~ ^[Yy]$ ]]; then
|
||||||
sudo npm install -g pm2
|
sudo npm install -g pm2
|
||||||
sudo su -c "env PATH=$PATH:/usr/bin pm2 startup linux -u pi --hp /home/pi"
|
if [[ "$(ps --no-headers -o comm 1)" =~ systemd ]]; then #Checking for systemd
|
||||||
pm2 start ~/MagicMirror/installers/pm2_MagicMirror.json
|
sudo pm2 startup systemd -u pi --hp /home/pi
|
||||||
pm2 save
|
else
|
||||||
|
sudo su -c "env PATH=$PATH:/usr/bin pm2 startup linux -u pi --hp /home/pi"
|
||||||
|
fi
|
||||||
|
pm2 start ~/MagicMirror/installers/pm2_MagicMirror.json
|
||||||
|
pm2 save
|
||||||
|
fi
|
||||||
|
# Disable Screensaver
|
||||||
|
read -p "Do you want to disable the screen saver? (y/N)?" choice
|
||||||
|
if [[ $choice =~ ^[Yy]$ ]]; then
|
||||||
|
sudo su -c "echo -e '@xset s noblank\n@xset s off\n@xset -dpms' >> /etc/xdg/lxsession/LXDE-pi/autostart"
|
||||||
|
export DISPLAY=:0; xset s noblank;xset s off;xset -dpms
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo " "
|
echo " "
|
||||||
|
@ -70,7 +70,7 @@ var App = function() {
|
|||||||
if (e.code == "ENOENT") {
|
if (e.code == "ENOENT") {
|
||||||
console.error(Utils.colors.error("WARNING! Could not find config file. Please create one. Starting with default configuration."));
|
console.error(Utils.colors.error("WARNING! Could not find config file. Please create one. Starting with default configuration."));
|
||||||
} else if (e instanceof ReferenceError || e instanceof SyntaxError) {
|
} else if (e instanceof ReferenceError || e instanceof SyntaxError) {
|
||||||
console.error(Utils.colors.error("WARNING! Could not validate config file. Please correct syntax errors. Starting with default configuration."));
|
console.error(Utils.colors.error("WARNING! Could not validate config file. Starting with default configuration. Please correct syntax errors at or above this line: " + e.stack));
|
||||||
} else {
|
} else {
|
||||||
console.error(Utils.colors.error("WARNING! Could not load config file. Starting with default configuration. Error found: " + e));
|
console.error(Utils.colors.error("WARNING! Could not load config file. Starting with default configuration. Error found: " + e));
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,9 @@ var MM = (function() {
|
|||||||
dom.opacity = 0;
|
dom.opacity = 0;
|
||||||
wrapper.appendChild(dom);
|
wrapper.appendChild(dom);
|
||||||
|
|
||||||
if (typeof module.data.header !== "undefined" && module.data.header !== "") {
|
if (typeof module.getHeader() !== "undefined" && module.getHeader() !== "") {
|
||||||
var moduleHeader = document.createElement("header");
|
var moduleHeader = document.createElement("header");
|
||||||
moduleHeader.innerHTML = module.data.header;
|
moduleHeader.innerHTML = module.getHeader();
|
||||||
moduleHeader.className = "module-header";
|
moduleHeader.className = "module-header";
|
||||||
dom.appendChild(moduleHeader);
|
dom.appendChild(moduleHeader);
|
||||||
}
|
}
|
||||||
@ -173,6 +173,10 @@ var MM = (function() {
|
|||||||
*/
|
*/
|
||||||
var moduleNeedsUpdate = function(module, newHeader, newContent) {
|
var moduleNeedsUpdate = function(module, newHeader, newContent) {
|
||||||
var moduleWrapper = document.getElementById(module.identifier);
|
var moduleWrapper = document.getElementById(module.identifier);
|
||||||
|
if (moduleWrapper === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var contentWrapper = moduleWrapper.getElementsByClassName("module-content");
|
var contentWrapper = moduleWrapper.getElementsByClassName("module-content");
|
||||||
var headerWrapper = moduleWrapper.getElementsByClassName("module-header");
|
var headerWrapper = moduleWrapper.getElementsByClassName("module-header");
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ var Module = Class.extend({
|
|||||||
/* setData(data)
|
/* setData(data)
|
||||||
* Set the module data.
|
* Set the module data.
|
||||||
*
|
*
|
||||||
* argument data obejct - Module data.
|
* argument data object - Module data.
|
||||||
*/
|
*/
|
||||||
setData: function (data) {
|
setData: function (data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
@ -226,7 +226,7 @@ var Module = Class.extend({
|
|||||||
/* setConfig(config)
|
/* setConfig(config)
|
||||||
* Set the module config and combine it with the module defaults.
|
* Set the module config and combine it with the module defaults.
|
||||||
*
|
*
|
||||||
* argument config obejct - Module config.
|
* argument config object - Module config.
|
||||||
*/
|
*/
|
||||||
setConfig: function (config) {
|
setConfig: function (config) {
|
||||||
this.config = Object.assign({}, this.defaults, config);
|
this.config = Object.assign({}, this.defaults, config);
|
||||||
|
@ -33,8 +33,8 @@ Therefore **we highly recommend you to include the following information in your
|
|||||||
|
|
||||||
- A high quality screenshot of your working module
|
- A high quality screenshot of your working module
|
||||||
- A short, one sentence, clear description what it does (duh!)
|
- A short, one sentence, clear description what it does (duh!)
|
||||||
- What external API's it depend on, including web links to those
|
- What external API's it depends upon, including web links to those
|
||||||
- Wheteher the API/request require a key and the user limitations of those. (Is it free?)
|
- Whether the API/request require a key and the user limitations of those. (Is it free?)
|
||||||
|
|
||||||
Surely this also help you get better recognition and feedback for your work.
|
Surely this also help you get better recognition and feedback for your work.
|
||||||
|
|
||||||
@ -46,8 +46,8 @@ A module can be placed in one single folder. Or multiple modules can be grouped
|
|||||||
|
|
||||||
### Files
|
### Files
|
||||||
- **modulename/modulename.js** - This is your core module script.
|
- **modulename/modulename.js** - This is your core module script.
|
||||||
- **modulename/node_helper.js** - This is an optional helper that will be loaded by the node script. The node helper and module script can communicate with each other using an intergrated socket system.
|
- **modulename/node_helper.js** - This is an optional helper that will be loaded by the node script. The node helper and module script can communicate with each other using an integrated socket system.
|
||||||
- **modulename/public** - Any files in this folder can be accesed via the browser on `/modulename/filename.ext`.
|
- **modulename/public** - Any files in this folder can be accessed via the browser on `/modulename/filename.ext`.
|
||||||
- **modulename/anyfileorfolder** Any other file or folder in the module folder can be used by the core module script. For example: *modulename/css/modulename.css* would be a good path for your additional module styles.
|
- **modulename/anyfileorfolder** Any other file or folder in the module folder can be used by the core module script. For example: *modulename/css/modulename.css* would be a good path for your additional module styles.
|
||||||
|
|
||||||
## The Core module file: modulename.js
|
## The Core module file: modulename.js
|
||||||
@ -89,7 +89,7 @@ After the module is initialized, the module instance has a few available module
|
|||||||
| `this.data` | Object | The data object contain additional metadata about the module instance. (See below) |
|
| `this.data` | Object | The data object contain additional metadata about the module instance. (See below) |
|
||||||
|
|
||||||
|
|
||||||
The `this.data` data object contain the follwoing metadata:
|
The `this.data` data object contain the following metadata:
|
||||||
- `data.classes` - The classes which are added to the module dom wrapper.
|
- `data.classes` - The classes which are added to the module dom wrapper.
|
||||||
- `data.file` - The filename of the core module file.
|
- `data.file` - The filename of the core module file.
|
||||||
- `data.path` - The path of the module folder.
|
- `data.path` - The path of the module folder.
|
||||||
@ -98,7 +98,7 @@ The `this.data` data object contain the follwoing metadata:
|
|||||||
|
|
||||||
|
|
||||||
#### `defaults: {}`
|
#### `defaults: {}`
|
||||||
Any properties defined in the defaults object, will be merged with the module config as defined in the user's config.js file. This is the best place to set your modules's configuration defaults. Any of the module configuration properties can be accessed using `this.config.propertyName`, but more about that later.
|
Any properties defined in the defaults object, will be merged with the module config as defined in the user's config.js file. This is the best place to set your modules' configuration defaults. Any of the module configuration properties can be accessed using `this.config.propertyName`, but more about that later.
|
||||||
|
|
||||||
#### `requiresVersion:`
|
#### `requiresVersion:`
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ loaded: function(callback) {
|
|||||||
````
|
````
|
||||||
|
|
||||||
#### `start()`
|
#### `start()`
|
||||||
This method is called when all modules are loaded an the system is ready to boot up. Keep in mind that the dom object for the module is not yet created. The start method is a perfect place to define any additional module properties:
|
This method is called when all modules are loaded and the system is ready to boot up. Keep in mind that the dom object for the module is not yet created. The start method is a perfect place to define any additional module properties:
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
````javascript
|
````javascript
|
||||||
@ -161,7 +161,7 @@ getScripts: function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
````
|
````
|
||||||
**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore it's advised not to use any external urls.
|
**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore, it's advised not to use any external urls.
|
||||||
|
|
||||||
|
|
||||||
#### `getStyles()`
|
#### `getStyles()`
|
||||||
@ -174,14 +174,14 @@ The getStyles method is called to request any additional stylesheets that need t
|
|||||||
getStyles: function() {
|
getStyles: function() {
|
||||||
return [
|
return [
|
||||||
'script.css', // will try to load it from the vendor folder, otherwise it will load is from the module folder.
|
'script.css', // will try to load it from the vendor folder, otherwise it will load is from the module folder.
|
||||||
'font-awesome.css', // this file is available in the vendor folder, so it doesn't need to be avialable in the module folder.
|
'font-awesome.css', // this file is available in the vendor folder, so it doesn't need to be available in the module folder.
|
||||||
this.file('anotherfile.css'), // this file will be loaded straight from the module folder.
|
this.file('anotherfile.css'), // this file will be loaded straight from the module folder.
|
||||||
'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css', // this file will be loaded from the bootstrapcdn servers.
|
'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css', // this file will be loaded from the bootstrapcdn servers.
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
````
|
````
|
||||||
**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore it's advised not to use any external urls.
|
**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore, it's advised not to use any external URLs.
|
||||||
|
|
||||||
#### `getTranslations()`
|
#### `getTranslations()`
|
||||||
**Should return: Dictionary**
|
**Should return: Dictionary**
|
||||||
@ -239,7 +239,7 @@ That MagicMirror core has the ability to send notifications to modules. Or even
|
|||||||
|
|
||||||
- `notification` - String - The notification identifier.
|
- `notification` - String - The notification identifier.
|
||||||
- `payload` - AnyType - The payload of a notification.
|
- `payload` - AnyType - The payload of a notification.
|
||||||
- `sender` - Module - The sender of the notification. If this argument is `undefined`, the sender of the notififiction is the core system.
|
- `sender` - Module - The sender of the notification. If this argument is `undefined`, the sender of the notification is the core system.
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
````javascript
|
````javascript
|
||||||
@ -346,7 +346,7 @@ Possible configurable options:
|
|||||||
- `lockString` - String - When setting lock string, the module can not be shown without passing the correct lockstring. This way (multiple) modules can prevent a module from showing. It's considered best practice to use your modules identifier as the locksString: `this.identifier`. See *visibility locking* below.
|
- `lockString` - String - When setting lock string, the module can not be shown without passing the correct lockstring. This way (multiple) modules can prevent a module from showing. It's considered best practice to use your modules identifier as the locksString: `this.identifier`. See *visibility locking* below.
|
||||||
|
|
||||||
|
|
||||||
**Note 1:** If the hide animation is canceled, for instance because the show method is called before the hide animation was finished, the callback will not be called.<br>
|
**Note 1:** If the hide animation is cancelled, for instance because the show method is called before the hide animation was finished, the callback will not be called.<br>
|
||||||
**Note 2:** If the hide animation is hijacked (an other method calls hide on the same module), the callback will not be called.<br>
|
**Note 2:** If the hide animation is hijacked (an other method calls hide on the same module), the callback will not be called.<br>
|
||||||
**Note 3:** If the dom is not yet created, the hide method won't work. Wait for the `DOM_OBJECTS_CREATED` [notification](#notificationreceivednotification-payload-sender).
|
**Note 3:** If the dom is not yet created, the hide method won't work. Wait for the `DOM_OBJECTS_CREATED` [notification](#notificationreceivednotification-payload-sender).
|
||||||
|
|
||||||
@ -371,7 +371,7 @@ Possible configurable options:
|
|||||||
|
|
||||||
(*Introduced in version: 2.1.0.*)
|
(*Introduced in version: 2.1.0.*)
|
||||||
|
|
||||||
Visiblity locking helps the module system to prevent unwanted hide/show actions. The following scenario explains the concept:
|
Visibility locking helps the module system to prevent unwanted hide/show actions. The following scenario explains the concept:
|
||||||
|
|
||||||
**Module B asks module A to hide:**
|
**Module B asks module A to hide:**
|
||||||
````javascript
|
````javascript
|
||||||
@ -436,7 +436,7 @@ If no translation is found, a fallback will be used. The fallback sequence is as
|
|||||||
- 4. Translation as defined in core translation file of the fallback language (the first defined core translation file).
|
- 4. Translation as defined in core translation file of the fallback language (the first defined core translation file).
|
||||||
- 5. The key (identifier) of the translation.
|
- 5. The key (identifier) of the translation.
|
||||||
|
|
||||||
When adding translations to your module, it's a good idea to see if an apropriate translation is already available in the [core translation files](https://github.com/MichMich/MagicMirror/tree/master/translations). This way, your module can benefit from the existing translations.
|
When adding translations to your module, it's a good idea to see if an appropriate translation is already available in the [core translation files](https://github.com/MichMich/MagicMirror/tree/master/translations). This way, your module can benefit from the existing translations.
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
````javascript
|
````javascript
|
||||||
@ -490,7 +490,7 @@ this.translate("RUNNING", {
|
|||||||
)}); // Will return a translated string for the identifier RUNNING, replacing `{timeUntilEnd}` with the contents of the variable `timeUntilEnd` in the order that translator intended. (has a fallback)
|
)}); // Will return a translated string for the identifier RUNNING, replacing `{timeUntilEnd}` with the contents of the variable `timeUntilEnd` in the order that translator intended. (has a fallback)
|
||||||
````
|
````
|
||||||
|
|
||||||
**Example swedish .json file that does not have the variable in it:**
|
**Example Swedish .json file that does not have the variable in it:**
|
||||||
````javascript
|
````javascript
|
||||||
{
|
{
|
||||||
"RUNNING": "Slutar",
|
"RUNNING": "Slutar",
|
||||||
|
@ -43,10 +43,11 @@ self.sendNotification("SHOW_ALERT", {});
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Notification params
|
### Notification params
|
||||||
| Option | Description
|
| Option | Description
|
||||||
| --------- | -----------
|
| ------------------ | -----------
|
||||||
| `title` | The title of the notification. <br><br> **Possible values:** `text` or `html`
|
| `title` | The title of the notification. <br><br> **Possible values:** `text` or `html`
|
||||||
| `message` | The message of the notification. <br><br> **Possible values:** `text` or `html`
|
| `message` | The message of the notification. <br><br> **Possible values:** `text` or `html`
|
||||||
|
| `timer` (optional) | How long the notification should stay visible in ms. <br> If absent, the default `display_time` is used. <br> **Possible values:** `int` `float`
|
||||||
|
|
||||||
|
|
||||||
### Alert params
|
### Alert params
|
||||||
@ -61,4 +62,4 @@ self.sendNotification("SHOW_ALERT", {});
|
|||||||
|
|
||||||
## Open Source Licenses
|
## Open Source Licenses
|
||||||
### [NotificationStyles](https://github.com/codrops/NotificationStyles)
|
### [NotificationStyles](https://github.com/codrops/NotificationStyles)
|
||||||
See [ympanus.net](http://tympanus.net/codrops/licensing/) for license.
|
See [tympanus.net](http://tympanus.net/codrops/licensing/) for license.
|
||||||
|
@ -24,7 +24,7 @@ Module.register("alert",{
|
|||||||
return ["classie.js", "modernizr.custom.js", "notificationFx.js"];
|
return ["classie.js", "modernizr.custom.js", "notificationFx.js"];
|
||||||
},
|
},
|
||||||
getStyles: function() {
|
getStyles: function() {
|
||||||
return ["ns-default.css"];
|
return ["ns-default.css", "font-awesome.css"];
|
||||||
},
|
},
|
||||||
// Define required translations.
|
// Define required translations.
|
||||||
getTranslations: function() {
|
getTranslations: function() {
|
||||||
@ -51,7 +51,7 @@ Module.register("alert",{
|
|||||||
message: msg,
|
message: msg,
|
||||||
layout: "growl",
|
layout: "growl",
|
||||||
effect: this.config.effect,
|
effect: this.config.effect,
|
||||||
ttl: this.config.display_time
|
ttl: message.timer !== undefined ? message.timer : this.config.display_time
|
||||||
}).show();
|
}).show();
|
||||||
},
|
},
|
||||||
show_alert: function(params, sender) {
|
show_alert: function(params, sender) {
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,7 +1,7 @@
|
|||||||
/* Based on work by http://tympanus.net/codrops/licensing/ */
|
/* Based on work by http://tympanus.net/codrops/licensing/ */
|
||||||
|
|
||||||
.ns-box {
|
.ns-box {
|
||||||
background: #fff;
|
background-color: rgba(0, 0, 0, 0.93);
|
||||||
padding: 17px;
|
padding: 17px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
@ -12,7 +12,10 @@
|
|||||||
display: table;
|
display: table;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
border-width: 1px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ns-alert {
|
.ns-alert {
|
||||||
|
@ -32,6 +32,7 @@ The following properties can be configured:
|
|||||||
| `defaultSymbol` | The default symbol. <br><br> **Possible values:** See [Font Awsome](http://fontawesome.io/icons/) website. <br> **Default value:** `calendar`
|
| `defaultSymbol` | The default symbol. <br><br> **Possible values:** See [Font Awsome](http://fontawesome.io/icons/) website. <br> **Default value:** `calendar`
|
||||||
| `maxTitleLength` | The maximum title length. <br><br> **Possible values:** `10` - `50` <br> **Default value:** `25`
|
| `maxTitleLength` | The maximum title length. <br><br> **Possible values:** `10` - `50` <br> **Default value:** `25`
|
||||||
| `wrapEvents` | Wrap event titles to multiple lines. Breaks lines at the length defined by `maxTitleLength`. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
| `wrapEvents` | Wrap event titles to multiple lines. Breaks lines at the length defined by `maxTitleLength`. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
||||||
|
| `maxTitleLines` | The maximum number of lines a title will wrap vertically before being cut (Only enabled if `wrapEvents` is also enabled). <br><br> **Possible values:** `0` - `10` <br> **Default value:** `3`
|
||||||
| `fetchInterval` | How often does the content needs to be fetched? (Milliseconds) <br><br> **Possible values:** `1000` - `86400000` <br> **Default value:** `300000` (5 minutes)
|
| `fetchInterval` | How often does the content needs to be fetched? (Milliseconds) <br><br> **Possible values:** `1000` - `86400000` <br> **Default value:** `300000` (5 minutes)
|
||||||
| `animationSpeed` | Speed of the update animation. (Milliseconds) <br><br> **Possible values:** `0` - `5000` <br> **Default value:** `2000` (2 seconds)
|
| `animationSpeed` | Speed of the update animation. (Milliseconds) <br><br> **Possible values:** `0` - `5000` <br> **Default value:** `2000` (2 seconds)
|
||||||
| `fade` | Fade the future events to black. (Gradient) <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
| `fade` | Fade the future events to black. (Gradient) <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
||||||
@ -52,6 +53,8 @@ The following properties can be configured:
|
|||||||
| `hidePrivate` | Hides private calendar events. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
| `hidePrivate` | Hides private calendar events. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
||||||
| `hideOngoing` | Hides calendar events that have already started. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
| `hideOngoing` | Hides calendar events that have already started. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
||||||
| `excludedEvents` | An array of words / phrases from event titles that will be excluded from being shown. <br><br>Additionally advanced filter objects can be passed in. Below is the configuration for the advance filtering object.<br>**Required**<br>`filterBy` - string used to determine if filter is applied.<br>**Optional**<br>`until` - Time before an event to display it Ex: [`'3 days'`, `'2 months'`, `'1 week'`]<br>`caseSensitive` - By default, excludedEvents are case insensitive, set this to true to enforce case sensitivity<br>`regex` - set to `true` if filterBy is a regex. For those not familiar with regex it is used for pattern matching, please see [here](https://regexr.com/) for more info.<br><br> **Example:** `['Birthday', 'Hide This Event', {filterBy: 'Payment', until: '6 days', caseSensitive: true}, {filterBy: '^[0-9]{1,}.*', regex: true}]` <br> **Default value:** `[]`
|
| `excludedEvents` | An array of words / phrases from event titles that will be excluded from being shown. <br><br>Additionally advanced filter objects can be passed in. Below is the configuration for the advance filtering object.<br>**Required**<br>`filterBy` - string used to determine if filter is applied.<br>**Optional**<br>`until` - Time before an event to display it Ex: [`'3 days'`, `'2 months'`, `'1 week'`]<br>`caseSensitive` - By default, excludedEvents are case insensitive, set this to true to enforce case sensitivity<br>`regex` - set to `true` if filterBy is a regex. For those not familiar with regex it is used for pattern matching, please see [here](https://regexr.com/) for more info.<br><br> **Example:** `['Birthday', 'Hide This Event', {filterBy: 'Payment', until: '6 days', caseSensitive: true}, {filterBy: '^[0-9]{1,}.*', regex: true}]` <br> **Default value:** `[]`
|
||||||
|
| `sliceMultiDayEvents` | If this is set to true, events exceeding at least one midnight will be sliced into separate events including a counter like (1/2). This is especially helpful in "dateheaders" mode. Events will be sliced at midnight, end time for all events but the last will be 23:59 **Default value:** `true`
|
||||||
|
|
||||||
|
|
||||||
### Calendar configuration
|
### Calendar configuration
|
||||||
|
|
||||||
@ -87,6 +90,7 @@ config: {
|
|||||||
| `repeatingCountTitle` | The count title for yearly repating events in this calendar. <br><br> **Example:** `'Birthday'`
|
| `repeatingCountTitle` | The count title for yearly repating events in this calendar. <br><br> **Example:** `'Birthday'`
|
||||||
| `maximumEntries` | The maximum number of events shown. Overrides global setting. **Possible values:** `0` - `100`
|
| `maximumEntries` | The maximum number of events shown. Overrides global setting. **Possible values:** `0` - `100`
|
||||||
| `maximumNumberOfDays` | The maximum number of days in the future. Overrides global setting
|
| `maximumNumberOfDays` | The maximum number of days in the future. Overrides global setting
|
||||||
|
| `name` | The name of the calendar. Included in event broadcasts as `calendarName`.
|
||||||
| `auth` | The object containing options for authentication against the calendar.
|
| `auth` | The object containing options for authentication against the calendar.
|
||||||
| `symbolClass` | Add a class to the cell of symbol.
|
| `symbolClass` | Add a class to the cell of symbol.
|
||||||
| `titleClass` | Add a class to the title's cell.
|
| `titleClass` | Add a class to the title's cell.
|
||||||
|
@ -19,13 +19,14 @@ Module.register("calendar", {
|
|||||||
defaultRepeatingCountTitle: "",
|
defaultRepeatingCountTitle: "",
|
||||||
maxTitleLength: 25,
|
maxTitleLength: 25,
|
||||||
wrapEvents: false, // wrap events to multiple lines breaking at maxTitleLength
|
wrapEvents: false, // wrap events to multiple lines breaking at maxTitleLength
|
||||||
|
maxTitleLines: 3,
|
||||||
fetchInterval: 5 * 60 * 1000, // Update every 5 minutes.
|
fetchInterval: 5 * 60 * 1000, // Update every 5 minutes.
|
||||||
animationSpeed: 2000,
|
animationSpeed: 2000,
|
||||||
fade: true,
|
fade: true,
|
||||||
urgency: 7,
|
urgency: 7,
|
||||||
timeFormat: "relative",
|
timeFormat: "relative",
|
||||||
dateFormat: "MMM Do",
|
dateFormat: "MMM Do",
|
||||||
dateEndFormat: "HH:mm",
|
dateEndFormat: "LT",
|
||||||
fullDayEventDateFormat: "MMM Do",
|
fullDayEventDateFormat: "MMM Do",
|
||||||
showEnd: false,
|
showEnd: false,
|
||||||
getRelative: 6,
|
getRelative: 6,
|
||||||
@ -46,12 +47,13 @@ Module.register("calendar", {
|
|||||||
"'s birthday": ""
|
"'s birthday": ""
|
||||||
},
|
},
|
||||||
broadcastEvents: true,
|
broadcastEvents: true,
|
||||||
excludedEvents: []
|
excludedEvents: [],
|
||||||
|
sliceMultiDayEvents: false
|
||||||
},
|
},
|
||||||
|
|
||||||
// Define required scripts.
|
// Define required scripts.
|
||||||
getStyles: function () {
|
getStyles: function () {
|
||||||
return ["calendar.css", "font-awesome5.css", "font-awesome5.v4shims.css"];
|
return ["calendar.css", "font-awesome.css"];
|
||||||
},
|
},
|
||||||
|
|
||||||
// Define required scripts.
|
// Define required scripts.
|
||||||
@ -103,6 +105,13 @@ Module.register("calendar", {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.addCalendar(calendar.url, calendar.auth, calendarConfig);
|
this.addCalendar(calendar.url, calendar.auth, calendarConfig);
|
||||||
|
|
||||||
|
// Trigger ADD_CALENDAR every fetchInterval to make sure there is always a calendar
|
||||||
|
// fetcher running on the server side.
|
||||||
|
var self = this;
|
||||||
|
setInterval(function() {
|
||||||
|
self.addCalendar(calendar.url, calendar.auth, calendarConfig);
|
||||||
|
}, self.config.fetchInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.calendarData = {};
|
this.calendarData = {};
|
||||||
@ -220,7 +229,7 @@ Module.register("calendar", {
|
|||||||
var titleWrapper = document.createElement("td"),
|
var titleWrapper = document.createElement("td"),
|
||||||
repeatingCountTitle = "";
|
repeatingCountTitle = "";
|
||||||
|
|
||||||
if (this.config.displayRepeatingCountTitle) {
|
if (this.config.displayRepeatingCountTitle && event.firstYear !== undefined) {
|
||||||
|
|
||||||
repeatingCountTitle = this.countTitleForUrl(event.url);
|
repeatingCountTitle = this.countTitleForUrl(event.url);
|
||||||
|
|
||||||
@ -296,12 +305,12 @@ Module.register("calendar", {
|
|||||||
if (this.config.timeFormat === "absolute") {
|
if (this.config.timeFormat === "absolute") {
|
||||||
if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
|
if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
|
||||||
// This event falls within the config.urgency period that the user has set
|
// This event falls within the config.urgency period that the user has set
|
||||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").from(moment().format("YYYYMMDD")));
|
||||||
} else {
|
} else {
|
||||||
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 {
|
} else {
|
||||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").from(moment().format("YYYYMMDD")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(this.config.showEnd){
|
if(this.config.showEnd){
|
||||||
@ -447,7 +456,31 @@ Module.register("calendar", {
|
|||||||
}
|
}
|
||||||
event.url = c;
|
event.url = c;
|
||||||
event.today = event.startDate >= today && event.startDate < (today + 24 * 60 * 60 * 1000);
|
event.today = event.startDate >= today && event.startDate < (today + 24 * 60 * 60 * 1000);
|
||||||
events.push(event);
|
|
||||||
|
/* if sliceMultiDayEvents is set to true, multiday events (events exceeding at least one midnight) are sliced into days,
|
||||||
|
* otherwise, esp. in dateheaders mode it is not clear how long these events are.
|
||||||
|
*/
|
||||||
|
if (this.config.sliceMultiDayEvents) {
|
||||||
|
var midnight = moment(event.startDate, "x").clone().startOf("day").add(1, "day").format("x"); //next midnight
|
||||||
|
var count = 1;
|
||||||
|
var maxCount = Math.ceil(((event.endDate - 1) - moment(event.startDate, "x").endOf("day").format("x"))/(1000*60*60*24)) + 1
|
||||||
|
if (event.endDate > midnight) {
|
||||||
|
while (event.endDate > midnight) {
|
||||||
|
var nextEvent = JSON.parse(JSON.stringify(event)); //make a copy without reference to the original event
|
||||||
|
nextEvent.startDate = midnight;
|
||||||
|
event.endDate = midnight;
|
||||||
|
event.title += " (" + count + "/" + maxCount + ")";
|
||||||
|
events.push(event);
|
||||||
|
event = nextEvent;
|
||||||
|
count += 1;
|
||||||
|
midnight = moment(midnight, "x").add(1, "day").format("x"); //move further one day for next split
|
||||||
|
}
|
||||||
|
event.title += " ("+count+"/"+maxCount+")";
|
||||||
|
}
|
||||||
|
events.push(event);
|
||||||
|
} else {
|
||||||
|
events.push(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,6 +569,17 @@ Module.register("calendar", {
|
|||||||
return this.getCalendarProperty(url, "timeClass", "");
|
return this.getCalendarProperty(url, "timeClass", "");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/* calendarNameForUrl(url)
|
||||||
|
* Retrieves the calendar name for a specific url.
|
||||||
|
*
|
||||||
|
* argument url string - Url to look for.
|
||||||
|
*
|
||||||
|
* return string - The name of the calendar
|
||||||
|
*/
|
||||||
|
calendarNameForUrl: function (url) {
|
||||||
|
return this.getCalendarProperty(url, "name", "");
|
||||||
|
},
|
||||||
|
|
||||||
/* colorForUrl(url)
|
/* colorForUrl(url)
|
||||||
* Retrieves the color for a specific url.
|
* Retrieves the color for a specific url.
|
||||||
*
|
*
|
||||||
@ -584,9 +628,10 @@ Module.register("calendar", {
|
|||||||
* @param {string} string Text string to shorten
|
* @param {string} string Text string to shorten
|
||||||
* @param {number} maxLength The max length of the string
|
* @param {number} maxLength The max length of the string
|
||||||
* @param {boolean} wrapEvents Wrap the text after the line has reached maxLength
|
* @param {boolean} wrapEvents Wrap the text after the line has reached maxLength
|
||||||
|
* @param {number} maxTitleLines The max number of vertical lines before cutting event title
|
||||||
* @returns {string} The shortened string
|
* @returns {string} The shortened string
|
||||||
*/
|
*/
|
||||||
shorten: function (string, maxLength, wrapEvents) {
|
shorten: function (string, maxLength, wrapEvents, maxTitleLines) {
|
||||||
if (typeof string !== "string") {
|
if (typeof string !== "string") {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -595,12 +640,21 @@ Module.register("calendar", {
|
|||||||
var temp = "";
|
var temp = "";
|
||||||
var currentLine = "";
|
var currentLine = "";
|
||||||
var words = string.split(" ");
|
var words = string.split(" ");
|
||||||
|
var line = 0;
|
||||||
|
|
||||||
for (var i = 0; i < words.length; i++) {
|
for (var i = 0; i < words.length; i++) {
|
||||||
var word = words[i];
|
var word = words[i];
|
||||||
if (currentLine.length + word.length < (typeof maxLength === "number" ? maxLength : 25) - 1) { // max - 1 to account for a space
|
if (currentLine.length + word.length < (typeof maxLength === "number" ? maxLength : 25) - 1) { // max - 1 to account for a space
|
||||||
currentLine += (word + " ");
|
currentLine += (word + " ");
|
||||||
} else {
|
} else {
|
||||||
|
line++;
|
||||||
|
if (line > maxTitleLines - 1) {
|
||||||
|
if (i < words.length) {
|
||||||
|
currentLine += "…";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (currentLine.length > 0) {
|
if (currentLine.length > 0) {
|
||||||
temp += (currentLine + "<br>" + word + " ");
|
temp += (currentLine + "<br>" + word + " ");
|
||||||
} else {
|
} else {
|
||||||
@ -651,7 +705,7 @@ Module.register("calendar", {
|
|||||||
title = title.replace(needle, replacement);
|
title = title.replace(needle, replacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
title = this.shorten(title, this.config.maxTitleLength, this.config.wrapEvents);
|
title = this.shorten(title, this.config.maxTitleLength, this.config.wrapEvents, this.config.maxTitleLines);
|
||||||
return title;
|
return title;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -666,6 +720,7 @@ Module.register("calendar", {
|
|||||||
for (var e in calendar) {
|
for (var e in calendar) {
|
||||||
var event = cloneObject(calendar[e]);
|
var event = cloneObject(calendar[e]);
|
||||||
event.symbol = this.symbolsForUrl(url);
|
event.symbol = this.symbolsForUrl(url);
|
||||||
|
event.calendarName = this.calendarNameForUrl(url);
|
||||||
event.color = this.colorForUrl(url);
|
event.color = this.colorForUrl(url);
|
||||||
delete event.url;
|
delete event.url;
|
||||||
eventList.push(event);
|
eventList.push(event);
|
||||||
|
@ -273,7 +273,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||||||
/* isFullDayEvent(event)
|
/* isFullDayEvent(event)
|
||||||
* Checks if an event is a fullday event.
|
* Checks if an event is a fullday event.
|
||||||
*
|
*
|
||||||
* argument event obejct - The event object to check.
|
* argument event object - The event object to check.
|
||||||
*
|
*
|
||||||
* return bool - The event is a fullday event.
|
* return bool - The event is a fullday event.
|
||||||
*/
|
*/
|
||||||
|
78
modules/default/calendar/vendor/ical.js/ical.js
vendored
78
modules/default/calendar/vendor/ical.js/ical.js
vendored
@ -80,16 +80,45 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var addTZ = function(dt, name, params){
|
var addTZ = function(dt, params){
|
||||||
var p = parseParams(params);
|
var p = parseParams(params);
|
||||||
|
|
||||||
if (params && p){
|
if (params && p && dt){
|
||||||
dt[name].tz = p.TZID
|
dt.tz = p.TZID
|
||||||
}
|
}
|
||||||
|
|
||||||
return dt
|
return dt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parseTimestamp = function(val){
|
||||||
|
//typical RFC date-time format
|
||||||
|
var comps = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(Z)?$/.exec(val);
|
||||||
|
if (comps !== null) {
|
||||||
|
if (comps[7] == 'Z'){ // GMT
|
||||||
|
return new Date(Date.UTC(
|
||||||
|
parseInt(comps[1], 10),
|
||||||
|
parseInt(comps[2], 10)-1,
|
||||||
|
parseInt(comps[3], 10),
|
||||||
|
parseInt(comps[4], 10),
|
||||||
|
parseInt(comps[5], 10),
|
||||||
|
parseInt(comps[6], 10 )
|
||||||
|
));
|
||||||
|
// TODO add tz
|
||||||
|
} else {
|
||||||
|
return new Date(
|
||||||
|
parseInt(comps[1], 10),
|
||||||
|
parseInt(comps[2], 10)-1,
|
||||||
|
parseInt(comps[3], 10),
|
||||||
|
parseInt(comps[4], 10),
|
||||||
|
parseInt(comps[5], 10),
|
||||||
|
parseInt(comps[6], 10)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
var dateParam = function(name){
|
var dateParam = function(name){
|
||||||
return function(val, params, curr){
|
return function(val, params, curr){
|
||||||
|
|
||||||
@ -108,37 +137,24 @@
|
|||||||
comps[3]
|
comps[3]
|
||||||
);
|
);
|
||||||
|
|
||||||
return addTZ(curr, name, params);
|
curr[name] = addTZ(curr[name], params);
|
||||||
|
return curr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
curr[name] = []
|
||||||
|
val.split(',').forEach(function(val){
|
||||||
|
var newDate = parseTimestamp(val);
|
||||||
|
curr[name].push(addTZ(newDate, params));
|
||||||
|
});
|
||||||
|
|
||||||
//typical RFC date-time format
|
if (curr[name].length === 0){
|
||||||
var comps = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(Z)?$/.exec(val);
|
delete curr[name];
|
||||||
if (comps !== null) {
|
} else if (curr[name].length === 1){
|
||||||
if (comps[7] == 'Z'){ // GMT
|
curr[name] = curr[name][0];
|
||||||
curr[name] = new Date(Date.UTC(
|
|
||||||
parseInt(comps[1], 10),
|
|
||||||
parseInt(comps[2], 10)-1,
|
|
||||||
parseInt(comps[3], 10),
|
|
||||||
parseInt(comps[4], 10),
|
|
||||||
parseInt(comps[5], 10),
|
|
||||||
parseInt(comps[6], 10 )
|
|
||||||
));
|
|
||||||
// TODO add tz
|
|
||||||
} else {
|
|
||||||
curr[name] = new Date(
|
|
||||||
parseInt(comps[1], 10),
|
|
||||||
parseInt(comps[2], 10)-1,
|
|
||||||
parseInt(comps[3], 10),
|
|
||||||
parseInt(comps[4], 10),
|
|
||||||
parseInt(comps[5], 10),
|
|
||||||
parseInt(comps[6], 10)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return addTZ(curr, name, params)
|
return curr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +164,11 @@
|
|||||||
if (date.exdates === undefined) {
|
if (date.exdates === undefined) {
|
||||||
date.exdates = [];
|
date.exdates = [];
|
||||||
}
|
}
|
||||||
date.exdates.push(date.exdate);
|
if (Array.isArray(date.exdate)){
|
||||||
|
date.exdates = date.exdates.concat(date.exdate);
|
||||||
|
} else {
|
||||||
|
date.exdates.push(date.exdate);
|
||||||
|
}
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,8 @@ Module.register("clock",{
|
|||||||
clockCircle.style.backgroundSize = "100%";
|
clockCircle.style.backgroundSize = "100%";
|
||||||
|
|
||||||
// The following line solves issue: https://github.com/MichMich/MagicMirror/issues/611
|
// The following line solves issue: https://github.com/MichMich/MagicMirror/issues/611
|
||||||
clockCircle.style.border = "1px solid black";
|
// clockCircle.style.border = "1px solid black";
|
||||||
|
clockCircle.style.border = "rgba(0, 0, 0, 0.1)"; //Updated fix for Issue 611 where non-black backgrounds are used
|
||||||
|
|
||||||
} else if (this.config.analogFace != "none") {
|
} else if (this.config.analogFace != "none") {
|
||||||
clockCircle.style.border = "2px solid white";
|
clockCircle.style.border = "2px solid white";
|
||||||
|
@ -50,7 +50,7 @@ The following properties can be configured:
|
|||||||
| `showIndoorTemperature` | If you have another module that emits the INDOOR_TEMPERATURE notification, the indoor temperature will be displayed <br> **Default value:** `false`
|
| `showIndoorTemperature` | If you have another module that emits the INDOOR_TEMPERATURE notification, the indoor temperature will be displayed <br> **Default value:** `false`
|
||||||
| `onlyTemp` | Show only current Temperature and weather icon without windspeed, sunset, sunrise time and feels like. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
| `onlyTemp` | Show only current Temperature and weather icon without windspeed, sunset, sunrise time and feels like. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
||||||
| `showFeelsLike` | Shows the Feels like temperature weather. <br><br> **Possible values:**`true` or `false`<br>**Default value:** `true`
|
| `showFeelsLike` | Shows the Feels like temperature weather. <br><br> **Possible values:**`true` or `false`<br>**Default value:** `true`
|
||||||
| `useKMPHWind` | Uses KMPH as units for windspeed. <br><br> **Possible values:**`true` or `false`<br>**Default value:** `false`
|
| `useKMPHwind` | Uses KMPH as units for windspeed. <br><br> **Possible values:**`true` or `false`<br>**Default value:** `false`
|
||||||
| `useBeaufort` | Pick between using the Beaufort scale for wind speed or using the default units. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
| `useBeaufort` | Pick between using the Beaufort scale for wind speed or using the default units. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
||||||
| `lang` | The language of the days. <br><br> **Possible values:** `en`, `nl`, `ru`, etc ... <br> **Default value:** uses value of _config.language_
|
| `lang` | The language of the days. <br><br> **Possible values:** `en`, `nl`, `ru`, etc ... <br> **Default value:** uses value of _config.language_
|
||||||
| `decimalSymbol` | The decimal symbol to use.<br><br> **Possible values:** `.`, `,` or any other symbol.<br> **Default value:** `.`
|
| `decimalSymbol` | The decimal symbol to use.<br><br> **Possible values:** `.`, `,` or any other symbol.<br> **Default value:** `.`
|
||||||
|
@ -71,7 +71,7 @@ Module.register("currentweather",{
|
|||||||
firstEvent: false,
|
firstEvent: false,
|
||||||
|
|
||||||
// create a variable to hold the location name based on the API result.
|
// create a variable to hold the location name based on the API result.
|
||||||
fetchedLocatioName: "",
|
fetchedLocationName: "",
|
||||||
|
|
||||||
// Define required scripts.
|
// Define required scripts.
|
||||||
getScripts: function() {
|
getScripts: function() {
|
||||||
@ -198,16 +198,19 @@ Module.register("currentweather",{
|
|||||||
large.appendChild(weatherIcon);
|
large.appendChild(weatherIcon);
|
||||||
|
|
||||||
var degreeLabel = "";
|
var degreeLabel = "";
|
||||||
if (this.config.degreeLabel) {
|
if (this.config.units === "metric" || this.config.units === "imperial") {
|
||||||
switch (this.config.units ) {
|
degreeLabel += "°";
|
||||||
|
}
|
||||||
|
if(this.config.degreeLabel) {
|
||||||
|
switch(this.config.units) {
|
||||||
case "metric":
|
case "metric":
|
||||||
degreeLabel = "C";
|
degreeLabel += "C";
|
||||||
break;
|
break;
|
||||||
case "imperial":
|
case "imperial":
|
||||||
degreeLabel = "F";
|
degreeLabel += "F";
|
||||||
break;
|
break;
|
||||||
case "default":
|
case "default":
|
||||||
degreeLabel = "K";
|
degreeLabel += "K";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,7 +221,7 @@ Module.register("currentweather",{
|
|||||||
|
|
||||||
var temperature = document.createElement("span");
|
var temperature = document.createElement("span");
|
||||||
temperature.className = "bright";
|
temperature.className = "bright";
|
||||||
temperature.innerHTML = " " + this.temperature.replace(".", this.config.decimalSymbol) + "°" + degreeLabel;
|
temperature.innerHTML = " " + this.temperature.replace(".", this.config.decimalSymbol) + degreeLabel;
|
||||||
large.appendChild(temperature);
|
large.appendChild(temperature);
|
||||||
|
|
||||||
if (this.config.showIndoorTemperature && this.indoorTemperature) {
|
if (this.config.showIndoorTemperature && this.indoorTemperature) {
|
||||||
@ -228,7 +231,7 @@ Module.register("currentweather",{
|
|||||||
|
|
||||||
var indoorTemperatureElem = document.createElement("span");
|
var indoorTemperatureElem = document.createElement("span");
|
||||||
indoorTemperatureElem.className = "bright";
|
indoorTemperatureElem.className = "bright";
|
||||||
indoorTemperatureElem.innerHTML = " " + this.indoorTemperature.replace(".", this.config.decimalSymbol) + "°" + degreeLabel;
|
indoorTemperatureElem.innerHTML = " " + this.indoorTemperature.replace(".", this.config.decimalSymbol) + degreeLabel;
|
||||||
large.appendChild(indoorTemperatureElem);
|
large.appendChild(indoorTemperatureElem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +254,7 @@ Module.register("currentweather",{
|
|||||||
|
|
||||||
var feelsLike = document.createElement("span");
|
var feelsLike = document.createElement("span");
|
||||||
feelsLike.className = "dimmed";
|
feelsLike.className = "dimmed";
|
||||||
feelsLike.innerHTML = this.translate("FEELS") + " " + this.feelsLike + "°" + degreeLabel;
|
feelsLike.innerHTML = this.translate("FEELS") + " " + this.feelsLike + degreeLabel;
|
||||||
small.appendChild(feelsLike);
|
small.appendChild(feelsLike);
|
||||||
|
|
||||||
wrapper.appendChild(small);
|
wrapper.appendChild(small);
|
||||||
@ -262,8 +265,8 @@ Module.register("currentweather",{
|
|||||||
|
|
||||||
// Override getHeader method.
|
// Override getHeader method.
|
||||||
getHeader: function() {
|
getHeader: function() {
|
||||||
if (this.config.appendLocationNameToHeader) {
|
if (this.config.appendLocationNameToHeader && this.data.header !== undefined) {
|
||||||
return this.data.header + " " + this.fetchedLocatioName;
|
return this.data.header + " " + this.fetchedLocationName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.data.header;
|
return this.data.header;
|
||||||
|
@ -46,6 +46,7 @@ MagicMirror's [notification mechanism](https://github.com/MichMich/MagicMirror/t
|
|||||||
| `ARTICLE_MORE_DETAILS` | When received the _first time_, shows the corresponding description of the currently displayed news title. <br> The module expects that the module's configuration option `showDescription` is set to `false` (default value). <br><br> When received a _second consecutive time_, shows the full news article in an IFRAME. <br> This requires that the news page can be embedded in an IFRAME, e.g. doesn't have the HTTP response header [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) set to e.g. `DENY`.<br><br>When received the _next consecutive times_, reloads the page and scrolls down by `scrollLength` pixels to paginate through the article.
|
| `ARTICLE_MORE_DETAILS` | When received the _first time_, shows the corresponding description of the currently displayed news title. <br> The module expects that the module's configuration option `showDescription` is set to `false` (default value). <br><br> When received a _second consecutive time_, shows the full news article in an IFRAME. <br> This requires that the news page can be embedded in an IFRAME, e.g. doesn't have the HTTP response header [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) set to e.g. `DENY`.<br><br>When received the _next consecutive times_, reloads the page and scrolls down by `scrollLength` pixels to paginate through the article.
|
||||||
| `ARTICLE_LESS_DETAILS` | Hides the summary or full news article and only displays the news title of the currently viewed news item.
|
| `ARTICLE_LESS_DETAILS` | Hides the summary or full news article and only displays the news title of the currently viewed news item.
|
||||||
| `ARTICLE_TOGGLE_FULL` | Toogles article in fullscreen.
|
| `ARTICLE_TOGGLE_FULL` | Toogles article in fullscreen.
|
||||||
|
| `ARTICLE_INFO_REQUEST` | Causes `newsfeed` to respond with the notification `ARTICLE_INFO_RESPONSE`, the payload of which provides the `title`, `source`, `date`, `desc` and `url` of the current news title.
|
||||||
|
|
||||||
Note the payload of the sent notification event is ignored.
|
Note the payload of the sent notification event is ignored.
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ Module.register("newsfeed",{
|
|||||||
fullArticle.style.top = "0";
|
fullArticle.style.top = "0";
|
||||||
fullArticle.style.left = "0";
|
fullArticle.style.left = "0";
|
||||||
fullArticle.style.border = "none";
|
fullArticle.style.border = "none";
|
||||||
fullArticle.src = typeof this.newsItems[this.activeItem].url === "string" ? this.newsItems[this.activeItem].url : this.newsItems[this.activeItem].url.href;
|
fullArticle.src = this.getActiveItemURL()
|
||||||
fullArticle.style.zIndex = 1;
|
fullArticle.style.zIndex = 1;
|
||||||
wrapper.appendChild(fullArticle);
|
wrapper.appendChild(fullArticle);
|
||||||
}
|
}
|
||||||
@ -210,6 +210,10 @@ Module.register("newsfeed",{
|
|||||||
return wrapper;
|
return wrapper;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getActiveItemURL: function() {
|
||||||
|
return typeof this.newsItems[this.activeItem].url === "string" ? this.newsItems[this.activeItem].url : this.newsItems[this.activeItem].url.href;
|
||||||
|
},
|
||||||
|
|
||||||
/* registerFeeds()
|
/* registerFeeds()
|
||||||
* registers the feeds to be used by the backend.
|
* registers the feeds to be used by the backend.
|
||||||
*/
|
*/
|
||||||
@ -387,6 +391,14 @@ Module.register("newsfeed",{
|
|||||||
} else {
|
} else {
|
||||||
this.showFullArticle();
|
this.showFullArticle();
|
||||||
}
|
}
|
||||||
|
} else if (notification === "ARTICLE_INFO_REQUEST"){
|
||||||
|
this.sendNotification("ARTICLE_INFO_RESPONSE", {
|
||||||
|
title: this.newsItems[this.activeItem].title,
|
||||||
|
source: this.newsItems[this.activeItem].sourceTitle,
|
||||||
|
date: this.newsItems[this.activeItem].pubdate,
|
||||||
|
desc: this.newsItems[this.activeItem].description,
|
||||||
|
url: this.getActiveItemURL()
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Log.info(this.name + " - unknown notification, ignoring: " + notification);
|
Log.info(this.name + " - unknown notification, ignoring: " + notification);
|
||||||
}
|
}
|
||||||
|
@ -65,8 +65,10 @@ module.exports = NodeHelper.create({
|
|||||||
data.module = sg.module;
|
data.module = sg.module;
|
||||||
if (!err) {
|
if (!err) {
|
||||||
sg.git.log({"-1": null}, function(err, data2) {
|
sg.git.log({"-1": null}, function(err, data2) {
|
||||||
data.hash = data2.latest.hash;
|
if (!err && data2.latest && "hash" in data2.latest) {
|
||||||
self.sendSocketNotification("STATUS", data);
|
data.hash = data2.latest.hash;
|
||||||
|
self.sendSocketNotification("STATUS", data);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
This module is aimed to be the replacement for the current `currentweather` and `weatherforcast` modules. The module will be configurable to be used as a current weather view, or to show the forecast. This way the module can be used twice to fullfil both purposes.
|
This module is aimed to be the replacement for the current `currentweather` and `weatherforcast` modules. The module will be configurable to be used as a current weather view, or to show the forecast. This way the module can be used twice to fullfil both purposes.
|
||||||
|
|
||||||
The biggest cange is the use of weather providers. This way we are not bound to one API source. And users can choose which API they want to use as their source.
|
The biggest change is the use of weather providers. This way we are not bound to one API source. And users can choose which API they want to use as their source.
|
||||||
|
|
||||||
The module is in a very early stage, and needs a lot of work. It's API isn't set in stone, so keep that in mind when you want to contribute.
|
The module is in a very early stage, and needs a lot of work. It's API isn't set in stone, so keep that in mind when you want to contribute.
|
||||||
|
|
||||||
@ -35,21 +35,21 @@ The following properties can be configured:
|
|||||||
|
|
||||||
| Option | Description
|
| Option | Description
|
||||||
| ---------------------------- | -----------
|
| ---------------------------- | -----------
|
||||||
| `weatherProvider` | Which weather provider should be used. <br><br> **Possible values:** `openweathermap` and `darksky` <br> **Default value:** `openweathermap`
|
| `weatherProvider` | Which weather provider should be used. <br><br> **Possible values:** `openweathermap` , `darksky` , or `weathergov` <br> **Default value:** `openweathermap`
|
||||||
| `type` | Which type of weather data should be displayed. <br><br> **Possible values:** `current` and `forecast` <br> **Default value:** `current`
|
| `type` | Which type of weather data should be displayed. <br><br> **Possible values:** `current` or `forecast` <br> **Default value:** `current`
|
||||||
| `units` | What units to use. Specified by config.js <br><br> **Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` =Fahrenheit <br> **Default value:** `config.units`
|
| `units` | What units to use. Specified by config.js <br><br> **Possible values:** `config.units` = Specified by config.js, `default` = Kelvin, `metric` = Celsius, `imperial` = Fahrenheit <br> **Default value:** `config.units`
|
||||||
| `roundTemp` | Round temperature value to nearest integer. <br><br> **Possible values:** `true` (round to integer) or `false` (display exact value with decimal point) <br> **Default value:** `false`
|
| `roundTemp` | Round temperature value to nearest integer. <br><br> **Possible values:** `true` (round to integer) or `false` (display exact value with decimal point) <br> **Default value:** `false`
|
||||||
| `degreeLabel` | Show the degree label for your chosen units (Metric = C, Imperial = F, Kelvins = K). <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
| `degreeLabel` | Show the degree label for your chosen units (Metric = C, Imperial = F, Kelvin = K). <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
||||||
| `updateInterval` | How often does the content needs to be fetched? (Milliseconds) <br><br> **Possible values:** `1000` - `86400000` <br> **Default value:** `600000` (10 minutes)
|
| `updateInterval` | How often does the content needs to be fetched? (Milliseconds) <br><br> **Possible values:** `1000` - `86400000` <br> **Default value:** `600000` (10 minutes)
|
||||||
| `animationSpeed` | Speed of the update animation. (Milliseconds) <br><br> **Possible values:**`0` - `5000` <br> **Default value:** `1000` (1 second)
|
| `animationSpeed` | Speed of the update animation. (Milliseconds) <br><br> **Possible values:** `0` - `5000` <br> **Default value:** `1000` (1 second)
|
||||||
| `timeFormat` | Use 12 or 24 hour format. <br><br> **Possible values:** `12` or `24` <br> **Default value:** uses value of _config.timeFormat_
|
| `timeFormat` | Use 12 or 24 hour format. <br><br> **Possible values:** `12` or `24` <br> **Default value:** uses value of _config.timeFormat_
|
||||||
| `showPeriod` | Show the period (am/pm) with 12 hour format <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
| `showPeriod` | Show the period (am/pm) with 12 hour format <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
||||||
| `showPeriodUpper` | Show the period (AM/PM) with 12 hour format as uppercase <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
| `showPeriodUpper` | Show the period (AM/PM) with 12 hour format as uppercase <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
||||||
| `lang` | The language of the days. <br><br> **Possible values:** `en`, `nl`, `ru`, etc ... <br> **Default value:** uses value of _config.language_
|
| `lang` | The language of the days. <br><br> **Possible values:** `en`, `nl`, `ru`, etc ... <br> **Default value:** uses value of _config.language_
|
||||||
| `decimalSymbol` | The decimal symbol to use.<br><br> **Possible values:** `.`, `,` or any other symbol.<br> **Default value:** `.`
|
| `decimalSymbol` | The decimal symbol to use.<br><br> **Possible values:** `.`, `,` or any other symbol.<br> **Default value:** `.`
|
||||||
| `initialLoadDelay` | The initial delay before loading. If you have multiple modules that use the same API key, you might want to delay one of the requests. (Milliseconds) <br><br> **Possible values:** `1000` - `5000` <br> **Default value:** `0`
|
| `initialLoadDelay` | The initial delay before loading. If you have multiple modules that use the same API key, you might want to delay one of the requests. (Milliseconds) <br><br> **Possible values:** `1000` - `5000` <br> **Default value:** `0`
|
||||||
| `appendLocationNameToHeader` | If set to `true`, the returned location name will be appended to the header of the module, if the header is enabled. This is mainly intresting when using calender based weather. <br><br> **Default value:** `true`
|
| `appendLocationNameToHeader` | If set to `true`, the returned location name will be appended to the header of the module, if the header is enabled. This is mainly interesting when using calender based weather. <br><br> **Default value:** `true`
|
||||||
| `calendarClass` | The class for the calender module to base the event based weather information on. <br><br> **Default value:** `'calendar'`
|
| `calendarClass` | The class for the calender module to base the event based weather information on. <br><br> **Default value:** `'calendar'`
|
||||||
|
|
||||||
#### Current weather options
|
#### Current weather options
|
||||||
|
|
||||||
@ -62,36 +62,48 @@ The following properties can be configured:
|
|||||||
| `showHumidity` | Show the current humidity <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
| `showHumidity` | Show the current humidity <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
||||||
| `showIndoorTemperature` | If you have another module that emits the `INDOOR_TEMPERATURE` notification, the indoor temperature will be displayed <br> **Default value:** `false`
|
| `showIndoorTemperature` | If you have another module that emits the `INDOOR_TEMPERATURE` notification, the indoor temperature will be displayed <br> **Default value:** `false`
|
||||||
| `showIndoorHumidity` | If you have another module that emits the `INDOOR_HUMIDITY` notification, the indoor humidity will be displayed <br> **Default value:** `false`
|
| `showIndoorHumidity` | If you have another module that emits the `INDOOR_HUMIDITY` notification, the indoor humidity will be displayed <br> **Default value:** `false`
|
||||||
| `showFeelsLike` | Shows the Feels like temperature weather. <br><br> **Possible values:**`true` or `false`<br>**Default value:** `true`
|
| `showFeelsLike` | Shows the Feels like temperature weather. <br><br> **Possible values:** `true` or `false`<br>**Default value:** `true`
|
||||||
|
|
||||||
#### Weather forecast options
|
#### Weather forecast options
|
||||||
|
|
||||||
| Option | Description
|
| Option | Description
|
||||||
| ---------------------------- | -----------
|
| ---------------------------- | -----------
|
||||||
| `tableClass` | The class for the forecast table. <br><br> **Default value:** `'small'`
|
| `tableClass` | The class for the forecast table. <br><br> **Default value:** `'small'`
|
||||||
| `colored` | If set to `true`, the min and max temperature are color coded. <br><br> **Default value:** `false`
|
| `colored` | If set to `true`, the min and max temperature are color coded. <br><br> **Default value:** `false`
|
||||||
| `showRainAmount` | Show the amount of rain in the forecast <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
| `showPrecipitationAmount` | Show the amount of rain/snow in the forecast <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
|
||||||
|
| `fade` | Fade the future events to black. (Gradient) <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
|
||||||
|
| `fadePoint` | Where to start fade? <br><br> **Possible values:** `0` (top of the list) - `1` (bottom of list) <br> **Default value:** `0.25`
|
||||||
|
| `maxNumberOfDays` | How many days of forecast to return. Specified by config.js <br><br> **Possible values:** `1` - `16` <br> **Default value:** `5` (5 days) <br> This value is optional. By default the weatherforecast module will return 5 days.
|
||||||
|
|
||||||
### Openweathermap options
|
### Openweathermap options
|
||||||
|
|
||||||
| Option | Description
|
| Option | Description
|
||||||
| ---------------------------- | -----------
|
| ---------------------------- | -----------
|
||||||
| `apiVersion` | The OpenWeatherMap API version to use. <br><br> **Default value:** `2.5`
|
| `apiVersion` | The OpenWeatherMap API version to use. <br><br> **Default value:** `2.5`
|
||||||
| `apiBase` | The OpenWeatherMap base URL. <br><br> **Default value:** `'http://api.openweathermap.org/data/'`
|
| `apiBase` | The OpenWeatherMap base URL. <br><br> **Default value:** `'http://api.openweathermap.org/data/'`
|
||||||
| `weatherEndpoint` | The OpenWeatherMap API endPoint. <br><br> **Possible values:** `/weather` or `/forecast/daily` <br> **Default value:** `'/weather'`
|
| `weatherEndpoint` | The OpenWeatherMap API endPoint. <br><br> **Possible values:** `/weather`, `/forecast` (free users) or `/forecast/daily` (paying users or old apiKey only) <br> **Default value:** `'/weather'`
|
||||||
| `locationID` | Location ID from [OpenWeatherMap](https://openweathermap.org/find) **This will override anything you put in location.** <br> Leave blank if you want to use location. <br> **Example:** `1234567` <br> **Default value:** `false` <br><br> **Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used.
|
| `locationID` | Location ID from [OpenWeatherMap](https://openweathermap.org/find) **This will override anything you put in location.** <br> Leave blank if you want to use location. <br> **Example:** `1234567` <br> **Default value:** `false` <br><br> **Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used.
|
||||||
| `location` | The location used for weather information. <br><br> **Example:** `'Amsterdam,Netherlands'` <br> **Default value:** `false` <br><br> **Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used.
|
| `location` | The location used for weather information. <br><br> **Example:** `'Amsterdam,Netherlands'` <br> **Default value:** `false` <br><br> **Note:** When the `location` and `locationID` are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used.
|
||||||
| `apiKey` | The [OpenWeatherMap](https://home.openweathermap.org) API key, which can be obtained by creating an OpenWeatherMap account. <br><br> This value is **REQUIRED**
|
| `apiKey` | The [OpenWeatherMap](https://home.openweathermap.org) API key, which can be obtained by creating an OpenWeatherMap account. <br><br> This value is **REQUIRED**
|
||||||
|
|
||||||
### Darksky options
|
### Darksky options
|
||||||
|
|
||||||
| Option | Description
|
| Option | Description
|
||||||
| ---------------------------- | -----------
|
| ---------------------------- | -----------
|
||||||
| `apiBase` | The DarkSky base URL. The darksky api has disabled [cors](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), therefore a proxy is required. <br><br> **Possible value:** `'https://cors-anywhere.herokuapp.com/https://api.darksky.net'` <br> This value is **REQUIRED**
|
| `apiBase` | The DarkSky base URL. The darksky api has disabled [cors](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), therefore a proxy is required. <br><br> **Possible value:** `'https://cors-anywhere.herokuapp.com/https://api.darksky.net'` <br> This value is **REQUIRED**
|
||||||
| `weatherEndpoint` | The DarkSky API endPoint. <br><br> **Possible values:** `/forecast` <br> This value is **REQUIRED**
|
| `weatherEndpoint` | The DarkSky API endPoint. <br><br> **Possible values:** `/forecast` <br> This value is **REQUIRED**
|
||||||
| `apiKey` | The [DarkSky](https://darksky.net/dev/register) API key, which can be obtained by creating an DarkSky account. <br><br> This value is **REQUIRED**
|
| `apiKey` | The [DarkSky](https://darksky.net/dev/register) API key, which can be obtained by creating an DarkSky account. <br><br> This value is **REQUIRED**
|
||||||
| `lat` | The geo coordinate latitude. <br><br> This value is **REQUIRED**
|
| `lat` | The geo coordinate latitude. <br><br> This value is **REQUIRED**
|
||||||
| `lon` | The geo coordinate longitude. <br><br> This value is **REQUIRED**
|
| `lon` | The geo coordinate longitude. <br><br> This value is **REQUIRED**
|
||||||
|
|
||||||
|
### Weather.gov options
|
||||||
|
|
||||||
|
| Option | Description
|
||||||
|
| ---------------------------- | -----------
|
||||||
|
| `apiBase` | The weather.gov base URL. <br><br> **Possible value:** `'https://api.weather.gov/points/'` <br> This value is **REQUIRED**
|
||||||
|
| `weatherEndpoint` | The weather.gov API endPoint. <br><br> **Possible values:** `/forecast` for forecast and `/forecast/hourly` for current. <br> This value is **REQUIRED**
|
||||||
|
| `lat` | The geo coordinate latitude. <br><br> This value is **REQUIRED**
|
||||||
|
| `lon` | The geo coordinate longitude. <br><br> This value is **REQUIRED**
|
||||||
|
|
||||||
## API Provider Development
|
## API Provider Development
|
||||||
|
|
||||||
|
@ -4,31 +4,30 @@
|
|||||||
<span class="wi wi-strong-wind dimmed"></span>
|
<span class="wi wi-strong-wind dimmed"></span>
|
||||||
<span>
|
<span>
|
||||||
{% if config.useBeaufort %}
|
{% if config.useBeaufort %}
|
||||||
{{current.beaufortWindSpeed() | round}}
|
{{ current.beaufortWindSpeed() | round }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{current.windSpeed | round}}
|
{{ current.windSpeed | round }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% 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="fa fa-long-arrow-up" style="transform:rotate({{ current.windDirection }}deg);"></i>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{current.cardinalWindDirection() | translate}}
|
{{ current.cardinalWindDirection() | translate }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</sup>
|
</sup>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
{% if config.showHumidity and current.humidity %}
|
{% if config.showHumidity and current.humidity %}
|
||||||
<span>{{ current.humidity }}</span><sup> <i class="wi wi-humidity humidityIcon"></i></sup>
|
<span>{{ current.humidity | decimalSymbol }}</span><sup> <i class="wi wi-humidity humidityIcon"></i></sup>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="wi dimmed wi-{{current.nextSunAction()}}"></span>
|
<span class="wi dimmed wi-{{ current.nextSunAction() }}"></span>
|
||||||
<span>
|
<span>
|
||||||
{% if current.nextSunAction() == "sunset" %}
|
{% if current.nextSunAction() == "sunset" %}
|
||||||
{{current.sunset | formatTime}}
|
{{ current.sunset | formatTime }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{current.sunrise | formatTime}}
|
{{ current.sunrise | formatTime }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -36,33 +35,39 @@
|
|||||||
<div class="large light">
|
<div class="large light">
|
||||||
<span class="wi weathericon wi-{{current.weatherType}}"></span>
|
<span class="wi weathericon wi-{{current.weatherType}}"></span>
|
||||||
<span class="bright">
|
<span class="bright">
|
||||||
{{current.temperature | roundValue | unit("temperature")}}
|
{{ current.temperature | roundValue | unit("temperature") | decimalSymbol }}
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="normal light indoor">
|
||||||
{% if config.showIndoorTemperature and indoor.temperature %}
|
{% if config.showIndoorTemperature and indoor.temperature %}
|
||||||
<span class="fa fa-home"></span>
|
<div>
|
||||||
<span class="bright">
|
<span class="fa fa-home"></span>
|
||||||
{{indoor.temperature | roundValue | unit("temperature")}}
|
<span class="bright">
|
||||||
</span>
|
{{ indoor.temperature | roundValue | unit("temperature") | decimalSymbol }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if config.showIndoorHumidity and indoor.humidity %}
|
{% if config.showIndoorHumidity and indoor.humidity %}
|
||||||
<span class="fa fa-tint"></span>
|
<div>
|
||||||
<span class="bright">
|
<span class="fa fa-tint"></span>
|
||||||
{{indoor.humidity | roundValue}}%
|
<span class="bright">
|
||||||
</span>
|
{{ indoor.humidity | roundValue | unit("humidity") | decimalSymbol }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if config.showFeelsLike and not config.onlyTemp %}
|
{% if config.showFeelsLike and not config.onlyTemp %}
|
||||||
<div class="normal medium">
|
<div class="normal medium">
|
||||||
<span class="dimmed">
|
<span class="dimmed">
|
||||||
{{ "FEELS" | translate }} {{ current.feelsLike() | roundValue | unit("temperature") }}
|
{{ "FEELS" | translate }} {{ current.feelsLike() | roundValue | unit("temperature") | decimalSymbol }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="dimmed light small">
|
<div class="dimmed light small">
|
||||||
{{"LOADING" | translate}}
|
{{ "LOADING" | translate | safe }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- Unclomment the line below to see the contents of the `current` object. -->
|
<!-- Uncomment the line below to see the contents of the `current` object. -->
|
||||||
<!-- <div style="word-wrap:break-word" class="xsmall dimmed">{{current | dump}}</div> -->
|
<!-- <div style="word-wrap:break-word" class="xsmall dimmed">{{current | dump}}</div> -->
|
||||||
|
@ -1,26 +1,32 @@
|
|||||||
{% if forecast %}
|
{% if forecast %}
|
||||||
<table class="{{config.tableClass}}">
|
{% set numSteps = forecast | calcNumSteps %}
|
||||||
|
{% set currentStep = 0 %}
|
||||||
|
<table class="{{ config.tableClass }}">
|
||||||
|
{% set forecast = forecast.slice(0, numSteps) %}
|
||||||
{% for f in forecast %}
|
{% for f in forecast %}
|
||||||
<tr {% if config.colored %}class="colored"{% endif %}>
|
<tr {% if config.colored %}class="colored"{% endif %} {% if config.fade %}style="opacity: {{ currentStep | opacity(numSteps) }};"{% endif %}>
|
||||||
<td class="day">{{f.date.format('ddd')}}</td>
|
<td class="day">{{ f.date.format('ddd') }}</td>
|
||||||
<td class="bright weather-icon"><span class="wi weathericon wi-{{f.weatherType}}"></span></td>
|
<td class="bright weather-icon"><span class="wi weathericon wi-{{ f.weatherType }}"></span></td>
|
||||||
<td class="align-right bright max-temp">
|
<td class="align-right bright max-temp">
|
||||||
{{f.maxTemperature | roundValue | unit("temperature")}}
|
{{ f.maxTemperature | roundValue | unit("temperature") }}
|
||||||
</td>
|
</td>
|
||||||
<td class="align-right min-temp">
|
<td class="align-right min-temp">
|
||||||
{{f.minTemperature | roundValue | unit("temperature")}}
|
{{ f.minTemperature | roundValue | unit("temperature") }}
|
||||||
</td>
|
</td>
|
||||||
{% if config.showRainAmount %}
|
{% if config.showPrecipitationAmount %}
|
||||||
<td class="align-right bright rain">
|
<td class="align-right bright precipitation">
|
||||||
{{f.rain | unit("rain")}}
|
{{ f.precipitation | unit("precip") }}
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
|
{% set currentStep = currentStep + 1 %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{"LOADING" | translate}}
|
<div class="dimmed light small">
|
||||||
|
{{ "LOADING" | translate | safe }}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- Unclomment the line below to see the contents of the `current` object. -->
|
<!-- Uncomment the line below to see the contents of the `current` object. -->
|
||||||
<!-- <div style="word-wrap:break-word" class="xsmall dimmed">{{forecast | dump}}</div> -->
|
<!-- <div style="word-wrap:break-word" class="xsmall dimmed">{{forecast | dump}}</div> -->
|
||||||
|
@ -85,7 +85,7 @@ Notify the delegate that new weather is available.
|
|||||||
|
|
||||||
#### `fetchData(url, method, data)`
|
#### `fetchData(url, method, data)`
|
||||||
|
|
||||||
A convinience function to make requests. It returns a promise.
|
A convenience function to make requests. It returns a promise.
|
||||||
|
|
||||||
### WeatherObject
|
### WeatherObject
|
||||||
|
|
||||||
@ -103,6 +103,8 @@ A convinience function to make requests. It returns a promise.
|
|||||||
| weatherType | `string` | Icon name of the weather type. <br> Possible values: [WeatherIcons](https://www.npmjs.com/package/weathericons) |
|
| weatherType | `string` | Icon name of the weather type. <br> Possible values: [WeatherIcons](https://www.npmjs.com/package/weathericons) |
|
||||||
| humidity | `number` | Percentage of humidity |
|
| humidity | `number` | Percentage of humidity |
|
||||||
| rain | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
| rain | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
||||||
|
| snow | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
||||||
|
| precipitation | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
||||||
|
|
||||||
#### Current weather
|
#### Current weather
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
* MIT Licensed
|
* MIT Licensed
|
||||||
*
|
*
|
||||||
* This class is a provider for Dark Sky.
|
* This class is a provider for Dark Sky.
|
||||||
|
* Note that the Dark Sky API does not provide rainfall. Instead it provides snowfall and precipitation probability
|
||||||
*/
|
*/
|
||||||
WeatherProvider.register("darksky", {
|
WeatherProvider.register("darksky", {
|
||||||
// Set the name of the provider.
|
// Set the name of the provider.
|
||||||
@ -81,12 +82,20 @@ WeatherProvider.register("darksky", {
|
|||||||
weather.minTemperature = forecast.temperatureMin;
|
weather.minTemperature = forecast.temperatureMin;
|
||||||
weather.maxTemperature = forecast.temperatureMax;
|
weather.maxTemperature = forecast.temperatureMax;
|
||||||
weather.weatherType = this.convertWeatherType(forecast.icon);
|
weather.weatherType = this.convertWeatherType(forecast.icon);
|
||||||
if (this.config.units === "metric" && !isNaN(forecast.precipAccumulation)) {
|
weather.snow = 0;
|
||||||
weather.rain = forecast.precipAccumulation * 10;
|
|
||||||
} else {
|
// The API will return centimeters if units is 'si' and will return inches for 'us'
|
||||||
weather.rain = forecast.precipAccumulation;
|
// Note that the Dark Sky API does not provide rainfall. Instead it provides snowfall and precipitation probability
|
||||||
|
if (forecast.hasOwnProperty("precipAccumulation")) {
|
||||||
|
if (this.config.units === "imperial" && !isNaN(forecast.precipAccumulation)) {
|
||||||
|
weather.snow = forecast.precipAccumulation;
|
||||||
|
} else if (!isNaN(forecast.precipAccumulation)) {
|
||||||
|
weather.snow = forecast.precipAccumulation * 10;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
weather.precipitation = weather.snow;
|
||||||
|
|
||||||
days.push(weather);
|
days.push(weather);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +56,6 @@ WeatherProvider.register("openweathermap", {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** OpenWeatherMap Specific Methods - These are not part of the default provider methods */
|
/** OpenWeatherMap Specific Methods - These are not part of the default provider methods */
|
||||||
/*
|
/*
|
||||||
* Gets the complete url for the request
|
* Gets the complete url for the request
|
||||||
@ -87,6 +85,105 @@ WeatherProvider.register("openweathermap", {
|
|||||||
* Generate WeatherObjects based on forecast information
|
* Generate WeatherObjects based on forecast information
|
||||||
*/
|
*/
|
||||||
generateWeatherObjectsFromForecast(forecasts) {
|
generateWeatherObjectsFromForecast(forecasts) {
|
||||||
|
|
||||||
|
if (this.config.weatherEndpoint == "/forecast") {
|
||||||
|
return this.fetchForecastHourly(forecasts);
|
||||||
|
} else if (this.config.weatherEndpoint == "/forecast/daily") {
|
||||||
|
return this.fetchForecastDaily(forecasts);
|
||||||
|
}
|
||||||
|
// if weatherEndpoint does not match forecast or forecast/daily, what should be returned?
|
||||||
|
const days = [new WeatherObject(this.config.units)];
|
||||||
|
return days;
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fetch forecast information for 3-hourly forecast (available for free subscription).
|
||||||
|
*/
|
||||||
|
fetchForecastHourly(forecasts) {
|
||||||
|
// initial variable declaration
|
||||||
|
const days = [];
|
||||||
|
// variables for temperature range and rain
|
||||||
|
let minTemp = [];
|
||||||
|
let maxTemp = [];
|
||||||
|
let rain = 0;
|
||||||
|
let snow = 0;
|
||||||
|
// variable for date
|
||||||
|
let date = "";
|
||||||
|
let weather = new WeatherObject(this.config.units);
|
||||||
|
|
||||||
|
for (const forecast of forecasts) {
|
||||||
|
|
||||||
|
if (date !== moment(forecast.dt, "X").format("YYYY-MM-DD")) {
|
||||||
|
// calculate minimum/maximum temperature, specify rain amount
|
||||||
|
weather.minTemperature = Math.min.apply(null, minTemp);
|
||||||
|
weather.maxTemperature = Math.max.apply(null, maxTemp);
|
||||||
|
weather.rain = rain;
|
||||||
|
weather.snow = snow;
|
||||||
|
weather.precipitation = weather.rain + weather.snow;
|
||||||
|
// push weather information to days array
|
||||||
|
days.push(weather);
|
||||||
|
// create new weather-object
|
||||||
|
weather = new WeatherObject(this.config.units);
|
||||||
|
|
||||||
|
minTemp = [];
|
||||||
|
maxTemp = [];
|
||||||
|
rain = 0;
|
||||||
|
snow = 0;
|
||||||
|
|
||||||
|
// set new date
|
||||||
|
date = moment(forecast.dt, "X").format("YYYY-MM-DD");
|
||||||
|
|
||||||
|
// specify date
|
||||||
|
weather.date = moment(forecast.dt, "X");
|
||||||
|
|
||||||
|
// If the first value of today is later than 17:00, we have an icon at least!
|
||||||
|
weather.weatherType = this.convertWeatherType(forecast.weather[0].icon);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moment(forecast.dt, "X").format("H") >= 8 && moment(forecast.dt, "X").format("H") <= 17) {
|
||||||
|
weather.weatherType = this.convertWeatherType(forecast.weather[0].icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the same day as before
|
||||||
|
// add values from forecast to corresponding variables
|
||||||
|
minTemp.push(forecast.main.temp_min);
|
||||||
|
maxTemp.push(forecast.main.temp_max);
|
||||||
|
|
||||||
|
if (forecast.hasOwnProperty("rain")) {
|
||||||
|
if (this.config.units === "imperial" && !isNaN(forecast.rain["3h"])) {
|
||||||
|
rain += forecast.rain["3h"] / 25.4;
|
||||||
|
} else if (!isNaN(forecast.rain["3h"])) {
|
||||||
|
rain += forecast.rain["3h"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forecast.hasOwnProperty("snow")) {
|
||||||
|
if (this.config.units === "imperial" && !isNaN(forecast.snow["3h"])) {
|
||||||
|
snow += forecast.snow["3h"] / 25.4;
|
||||||
|
} else if (!isNaN(forecast.snow["3h"])) {
|
||||||
|
snow += forecast.snow["3h"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// last day
|
||||||
|
// calculate minimum/maximum temperature, specify rain amount
|
||||||
|
weather.minTemperature = Math.min.apply(null, minTemp);
|
||||||
|
weather.maxTemperature = Math.max.apply(null, maxTemp);
|
||||||
|
weather.rain = rain;
|
||||||
|
weather.snow = snow;
|
||||||
|
weather.precipitation = weather.rain + weather.snow;
|
||||||
|
// push weather information to days array
|
||||||
|
days.push(weather);
|
||||||
|
return days.slice(1);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fetch forecast information for daily forecast (available for paid subscription or old apiKey).
|
||||||
|
*/
|
||||||
|
fetchForecastDaily(forecasts) {
|
||||||
|
// initial variable declaration
|
||||||
const days = [];
|
const days = [];
|
||||||
|
|
||||||
for (const forecast of forecasts) {
|
for (const forecast of forecasts) {
|
||||||
@ -96,16 +193,35 @@ WeatherProvider.register("openweathermap", {
|
|||||||
weather.minTemperature = forecast.temp.min;
|
weather.minTemperature = forecast.temp.min;
|
||||||
weather.maxTemperature = forecast.temp.max;
|
weather.maxTemperature = forecast.temp.max;
|
||||||
weather.weatherType = this.convertWeatherType(forecast.weather[0].icon);
|
weather.weatherType = this.convertWeatherType(forecast.weather[0].icon);
|
||||||
if (this.config.units === "imperial" && !isNaN(forecast.rain)) {
|
weather.rain = 0;
|
||||||
weather.rain = forecast.rain / 25.4
|
weather.snow = 0;
|
||||||
} else {
|
|
||||||
weather.rain = forecast.rain;
|
// forecast.rain not available if amount is zero
|
||||||
|
// The API always returns in millimeters
|
||||||
|
if (forecast.hasOwnProperty("rain")) {
|
||||||
|
if (this.config.units === "imperial" && !isNaN(forecast.rain)) {
|
||||||
|
weather.rain = forecast.rain / 25.4;
|
||||||
|
} else if (!isNaN(forecast.rain)) {
|
||||||
|
weather.rain = forecast.rain;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// forecast.snow not available if amount is zero
|
||||||
|
// The API always returns in millimeters
|
||||||
|
if (forecast.hasOwnProperty("snow")) {
|
||||||
|
if (this.config.units === "imperial" && !isNaN(forecast.snow)) {
|
||||||
|
weather.snow = forecast.snow / 25.4;
|
||||||
|
} else if (!isNaN(forecast.snow)) {
|
||||||
|
weather.snow = forecast.snow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
weather.precipitation = weather.rain + weather.snow;
|
||||||
|
|
||||||
days.push(weather);
|
days.push(weather);
|
||||||
}
|
}
|
||||||
|
|
||||||
return days;
|
return days;
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
256
modules/default/weather/providers/weathergov.js
Normal file
256
modules/default/weather/providers/weathergov.js
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
/* global WeatherProvider, WeatherObject */
|
||||||
|
|
||||||
|
/* Magic Mirror
|
||||||
|
* Module: Weather
|
||||||
|
* Provider: weather.gov
|
||||||
|
*
|
||||||
|
* By Vince Peri
|
||||||
|
* MIT Licensed.
|
||||||
|
*
|
||||||
|
* This class is a provider for weather.gov.
|
||||||
|
* Note that this is only for US locations (lat and lon) and does not require an API key
|
||||||
|
* Since it is free, there are some items missing - like sunrise, sunset, humidity, etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
WeatherProvider.register("weathergov", {
|
||||||
|
|
||||||
|
// Set the name of the provider.
|
||||||
|
// This isn't strictly necessary, since it will fallback to the provider identifier
|
||||||
|
// But for debugging (and future alerts) it would be nice to have the real name.
|
||||||
|
providerName: "Weather.gov",
|
||||||
|
|
||||||
|
// Overwrite the fetchCurrentWeather method.
|
||||||
|
fetchCurrentWeather() {
|
||||||
|
this.fetchData(this.getUrl())
|
||||||
|
.then(data => {
|
||||||
|
if (!data || !data.properties || !data.properties.periods || !data.properties.periods.length) {
|
||||||
|
// Did not receive usable new data.
|
||||||
|
// Maybe this needs a better check?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentWeather = this.generateWeatherObjectFromCurrentWeather(data.properties.periods[0]);
|
||||||
|
this.setCurrentWeather(currentWeather);
|
||||||
|
})
|
||||||
|
.catch(function(request) {
|
||||||
|
Log.error("Could not load data ... ", request);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// Overwrite the fetchCurrentWeather method.
|
||||||
|
fetchWeatherForecast() {
|
||||||
|
this.fetchData(this.getUrl())
|
||||||
|
.then(data => {
|
||||||
|
if (!data || !data.properties || !data.properties.periods || !data.properties.periods.length) {
|
||||||
|
// Did not receive usable new data.
|
||||||
|
// Maybe this needs a better check?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const forecast = this.generateWeatherObjectsFromForecast(data.properties.periods);
|
||||||
|
this.setWeatherForecast(forecast);
|
||||||
|
})
|
||||||
|
.catch(function(request) {
|
||||||
|
Log.error("Could not load data ... ", request);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Weather.gov Specific Methods - These are not part of the default provider methods */
|
||||||
|
/*
|
||||||
|
* Gets the complete url for the request
|
||||||
|
*/
|
||||||
|
getUrl() {
|
||||||
|
return this.config.apiBase + this.config.lat + "," + this.config.lon + this.config.weatherEndpoint;
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate a WeatherObject based on currentWeatherInformation
|
||||||
|
*/
|
||||||
|
generateWeatherObjectFromCurrentWeather(currentWeatherData) {
|
||||||
|
const currentWeather = new WeatherObject(this.config.units);
|
||||||
|
|
||||||
|
currentWeather.temperature = currentWeatherData.temperature;
|
||||||
|
currentWeather.windSpeed = currentWeatherData.windSpeed.split(" ", 1);
|
||||||
|
currentWeather.windDirection = this.convertDirectiontoDegrees(currentWeatherData.windDirection);
|
||||||
|
currentWeather.weatherType = this.convertWeatherType(currentWeatherData.shortForecast, currentWeatherData.isDaytime);
|
||||||
|
|
||||||
|
return currentWeather;
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate WeatherObjects based on forecast information
|
||||||
|
*/
|
||||||
|
generateWeatherObjectsFromForecast(forecasts) {
|
||||||
|
return this.fetchForecastDaily(forecasts);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fetch forecast information for daily forecast.
|
||||||
|
*/
|
||||||
|
fetchForecastDaily(forecasts) {
|
||||||
|
// initial variable declaration
|
||||||
|
const days = [];
|
||||||
|
// variables for temperature range and rain
|
||||||
|
let minTemp = [];
|
||||||
|
let maxTemp = [];
|
||||||
|
// variable for date
|
||||||
|
let date = "";
|
||||||
|
let weather = new WeatherObject(this.config.units);
|
||||||
|
weather.precipitation = 0;
|
||||||
|
|
||||||
|
for (const forecast of forecasts) {
|
||||||
|
|
||||||
|
if (date !== moment(forecast.startTime).format("YYYY-MM-DD")) {
|
||||||
|
|
||||||
|
// calculate minimum/maximum temperature, specify rain amount
|
||||||
|
weather.minTemperature = Math.min.apply(null, minTemp);
|
||||||
|
weather.maxTemperature = Math.max.apply(null, maxTemp);
|
||||||
|
|
||||||
|
// push weather information to days array
|
||||||
|
days.push(weather);
|
||||||
|
// create new weather-object
|
||||||
|
weather = new WeatherObject(this.config.units);
|
||||||
|
|
||||||
|
minTemp = [];
|
||||||
|
maxTemp = [];
|
||||||
|
weather.precipitation = 0;
|
||||||
|
|
||||||
|
// set new date
|
||||||
|
date = moment(forecast.startTime).format("YYYY-MM-DD");
|
||||||
|
|
||||||
|
// specify date
|
||||||
|
weather.date = moment(forecast.startTime);
|
||||||
|
|
||||||
|
// If the first value of today is later than 17:00, we have an icon at least!
|
||||||
|
weather.weatherType = this.convertWeatherType(forecast.shortForecast, forecast.isDaytime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moment(forecast.startTime).format("H") >= 8 && moment(forecast.startTime).format("H") <= 17) {
|
||||||
|
weather.weatherType = this.convertWeatherType(forecast.shortForecast, forecast.isDaytime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the same day as before
|
||||||
|
// add values from forecast to corresponding variables
|
||||||
|
minTemp.push(forecast.temperature);
|
||||||
|
maxTemp.push(forecast.temperature);
|
||||||
|
}
|
||||||
|
|
||||||
|
// last day
|
||||||
|
// calculate minimum/maximum temperature, specify rain amount
|
||||||
|
weather.minTemperature = Math.min.apply(null, minTemp);
|
||||||
|
weather.maxTemperature = Math.max.apply(null, maxTemp);
|
||||||
|
|
||||||
|
// push weather information to days array
|
||||||
|
days.push(weather);
|
||||||
|
return days.slice(1);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert the icons to a more usable name.
|
||||||
|
*/
|
||||||
|
convertWeatherType(weatherType, isDaytime) {
|
||||||
|
//https://w1.weather.gov/xml/current_obs/weather.php
|
||||||
|
// There are way too many types to create, so lets just look for certain strings
|
||||||
|
|
||||||
|
if (weatherType.includes("Cloudy") || weatherType.includes("Partly")) {
|
||||||
|
if (isDaytime) {
|
||||||
|
return "day-cloudy";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "night-cloudy";
|
||||||
|
} else if (weatherType.includes("Overcast")) {
|
||||||
|
if (isDaytime) {
|
||||||
|
return "cloudy";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "night-cloudy";
|
||||||
|
} else if (weatherType.includes("Freezing") || weatherType.includes("Ice")) {
|
||||||
|
return "rain-mix";
|
||||||
|
} else if (weatherType.includes("Snow")) {
|
||||||
|
if (isDaytime) {
|
||||||
|
return "snow";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "night-snow";
|
||||||
|
} else if (weatherType.includes("Thunderstorm")) {
|
||||||
|
if (isDaytime) {
|
||||||
|
return "thunderstorm";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "night-thunderstorm";
|
||||||
|
} else if (weatherType.includes("Showers")) {
|
||||||
|
if (isDaytime) {
|
||||||
|
return "showers";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "night-showers";
|
||||||
|
} else if (weatherType.includes("Rain") || weatherType.includes("Drizzle")) {
|
||||||
|
if (isDaytime) {
|
||||||
|
return "rain";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "night-rain";
|
||||||
|
} else if (weatherType.includes("Breezy") || weatherType.includes("Windy")) {
|
||||||
|
if (isDaytime) {
|
||||||
|
return "cloudy-windy";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "night-alt-cloudy-windy";
|
||||||
|
} else if (weatherType.includes("Fair") || weatherType.includes("Clear") || weatherType.includes("Few") || weatherType.includes("Sunny")) {
|
||||||
|
if (isDaytime) {
|
||||||
|
return "day-sunny";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "night-clear";
|
||||||
|
} else if (weatherType.includes("Dust") || weatherType.includes("Sand")) {
|
||||||
|
return "dust";
|
||||||
|
} else if (weatherType.includes("Fog")) {
|
||||||
|
return "fog";
|
||||||
|
} else if (weatherType.includes("Smoke")) {
|
||||||
|
return "smoke";
|
||||||
|
} else if (weatherType.includes("Haze")) {
|
||||||
|
return "day-haze";
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
Convert the direction into Degrees
|
||||||
|
*/
|
||||||
|
convertDirectiontoDegrees(direction) {
|
||||||
|
if (direction === "NNE"){
|
||||||
|
return 33.75;
|
||||||
|
} else if (direction === "NE") {
|
||||||
|
return 56.25;
|
||||||
|
} else if (direction === "ENE") {
|
||||||
|
return 78.75;
|
||||||
|
} else if (direction === "E") {
|
||||||
|
return 101.25;
|
||||||
|
} else if (direction === "ESE") {
|
||||||
|
return 123.75;
|
||||||
|
} else if (direction === "SE") {
|
||||||
|
return 146.25;
|
||||||
|
} else if (direction === "SSE") {
|
||||||
|
return 168.75;
|
||||||
|
} else if (direction === "S") {
|
||||||
|
return 191.25;
|
||||||
|
} else if (direction === "SSW") {
|
||||||
|
return 213.75;
|
||||||
|
} else if (direction === "SW") {
|
||||||
|
return 236.25;
|
||||||
|
} else if (direction === "WSW") {
|
||||||
|
return 258.75;
|
||||||
|
} else if (direction === "W") {
|
||||||
|
return 281.25;
|
||||||
|
} else if (direction === "WNW") {
|
||||||
|
return 303.75;
|
||||||
|
} else if (direction === "NW") {
|
||||||
|
return 326.25;
|
||||||
|
} else if (direction === "NNW") {
|
||||||
|
return 348.75;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -31,11 +31,15 @@
|
|||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.weather .rain {
|
.weather .precipitation {
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.weather tr .weathericon {
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
.weather tr.colored .min-temp {
|
.weather tr.colored .min-temp {
|
||||||
color: #bcddff;
|
color: #bcddff;
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,12 @@ Module.register("weather",{
|
|||||||
lang: config.language,
|
lang: config.language,
|
||||||
showHumidity: false,
|
showHumidity: false,
|
||||||
degreeLabel: false,
|
degreeLabel: false,
|
||||||
|
decimalSymbol: ".",
|
||||||
showIndoorTemperature: false,
|
showIndoorTemperature: false,
|
||||||
showIndoorHumidity: false,
|
showIndoorHumidity: false,
|
||||||
|
maxNumberOfDays: 5,
|
||||||
|
fade: true,
|
||||||
|
fadePoint: 0.25, // Start on 1/4th of the list.
|
||||||
|
|
||||||
initialLoadDelay: 0, // 0 seconds delay
|
initialLoadDelay: 0, // 0 seconds delay
|
||||||
retryDelay: 2500,
|
retryDelay: 2500,
|
||||||
@ -45,7 +49,7 @@ Module.register("weather",{
|
|||||||
tableClass: "small",
|
tableClass: "small",
|
||||||
|
|
||||||
onlyTemp: false,
|
onlyTemp: false,
|
||||||
showRainAmount: true,
|
showPrecipitationAmount: false,
|
||||||
colored: false,
|
colored: false,
|
||||||
showFeelsLike: true
|
showFeelsLike: true
|
||||||
},
|
},
|
||||||
@ -58,7 +62,7 @@ Module.register("weather",{
|
|||||||
return ["font-awesome.css", "weather-icons.css", "weather.css"];
|
return ["font-awesome.css", "weather-icons.css", "weather.css"];
|
||||||
},
|
},
|
||||||
|
|
||||||
// Return the scripts that are nessecery for the weather module.
|
// Return the scripts that are necessary for the weather module.
|
||||||
getScripts: function () {
|
getScripts: function () {
|
||||||
return [
|
return [
|
||||||
"moment.js",
|
"moment.js",
|
||||||
@ -184,7 +188,9 @@ Module.register("weather",{
|
|||||||
|
|
||||||
this.nunjucksEnvironment().addFilter("unit", function (value, type) {
|
this.nunjucksEnvironment().addFilter("unit", function (value, type) {
|
||||||
if (type === "temperature") {
|
if (type === "temperature") {
|
||||||
value += "°";
|
if (this.config.units === "metric" || this.config.units === "imperial") {
|
||||||
|
value += "°";
|
||||||
|
}
|
||||||
if (this.config.degreeLabel) {
|
if (this.config.degreeLabel) {
|
||||||
if (this.config.units === "metric") {
|
if (this.config.units === "metric") {
|
||||||
value += "C";
|
value += "C";
|
||||||
@ -194,12 +200,14 @@ Module.register("weather",{
|
|||||||
value += "K";
|
value += "K";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (type === "rain") {
|
} else if (type === "precip") {
|
||||||
if (isNaN(value)) {
|
if (isNaN(value) || value === 0 || value.toFixed(2) === "0.00") {
|
||||||
value = "";
|
value = "";
|
||||||
} else {
|
} else {
|
||||||
value = `${value.toFixed(2)} ${this.config.units === "imperial" ? "in" : "mm"}`;
|
value = `${value.toFixed(2)} ${this.config.units === "imperial" ? "in" : "mm"}`;
|
||||||
}
|
}
|
||||||
|
} else if (type === "humidity") {
|
||||||
|
value += "%"
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
@ -208,5 +216,30 @@ Module.register("weather",{
|
|||||||
this.nunjucksEnvironment().addFilter("roundValue", function(value) {
|
this.nunjucksEnvironment().addFilter("roundValue", function(value) {
|
||||||
return this.roundValue(value);
|
return this.roundValue(value);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
|
this.nunjucksEnvironment().addFilter("decimalSymbol", function(value) {
|
||||||
|
return value.toString().replace(/\./g, this.config.decimalSymbol);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
this.nunjucksEnvironment().addFilter("calcNumSteps", function(forecast) {
|
||||||
|
return Math.min(forecast.length, this.config.maxNumberOfDays);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
this.nunjucksEnvironment().addFilter("opacity", function(currentStep, numSteps) {
|
||||||
|
if (this.config.fade && this.config.fadePoint < 1) {
|
||||||
|
if (this.config.fadePoint < 0) {
|
||||||
|
this.config.fadePoint = 0;
|
||||||
|
}
|
||||||
|
var startingPoint = numSteps * this.config.fadePoint;
|
||||||
|
var numFadesteps = numSteps - startingPoint;
|
||||||
|
if (currentStep >= startingPoint) {
|
||||||
|
return 1 - (currentStep - startingPoint) / numFadesteps;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -26,6 +26,8 @@ class WeatherObject {
|
|||||||
this.weatherType = null;
|
this.weatherType = null;
|
||||||
this.humidity = null;
|
this.humidity = null;
|
||||||
this.rain = null;
|
this.rain = null;
|
||||||
|
this.snow = null;
|
||||||
|
this.precipitation = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
cardinalWindDirection() {
|
cardinalWindDirection() {
|
||||||
|
@ -142,14 +142,17 @@ Module.register("weatherforecast",{
|
|||||||
icon.className = "wi weathericon " + forecast.icon;
|
icon.className = "wi weathericon " + forecast.icon;
|
||||||
iconCell.appendChild(icon);
|
iconCell.appendChild(icon);
|
||||||
|
|
||||||
var degreeLabel = "°";
|
var degreeLabel = "";
|
||||||
|
if (this.config.units === "metric" || this.config.units === "imperial") {
|
||||||
|
degreeLabel += "°";
|
||||||
|
}
|
||||||
if(this.config.scale) {
|
if(this.config.scale) {
|
||||||
switch(this.config.units) {
|
switch(this.config.units) {
|
||||||
case "metric":
|
case "metric":
|
||||||
degreeLabel += " C";
|
degreeLabel += "C";
|
||||||
break;
|
break;
|
||||||
case "imperial":
|
case "imperial":
|
||||||
degreeLabel += " F";
|
degreeLabel += "F";
|
||||||
break;
|
break;
|
||||||
case "default":
|
case "default":
|
||||||
degreeLabel = "K";
|
degreeLabel = "K";
|
||||||
|
715
package-lock.json
generated
715
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "magicmirror",
|
"name": "magicmirror",
|
||||||
"version": "2.6.0",
|
"version": "2.8.0-develop",
|
||||||
"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": {
|
||||||
@ -58,7 +58,7 @@
|
|||||||
"ajv": "6.5.5",
|
"ajv": "6.5.5",
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
"colors": "^1.1.2",
|
"colors": "^1.1.2",
|
||||||
"electron": "^2.0.16",
|
"electron": "^3.0.13",
|
||||||
"express": "^4.16.2",
|
"express": "^4.16.2",
|
||||||
"express-ipfilter": "0.3.1",
|
"express-ipfilter": "0.3.1",
|
||||||
"feedme": "latest",
|
"feedme": "latest",
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
var app = require("../js/app.js");
|
var app = require("../js/app.js");
|
||||||
app.start(function(config) {
|
app.start(function(config) {
|
||||||
console.log("");
|
|
||||||
var bindAddress = config.address ? config.address : "localhost";
|
var bindAddress = config.address ? config.address : "localhost";
|
||||||
console.log("Ready to go! Please point your browser to: http://" + bindAddress + ":" + config.port);
|
console.log("\nReady to go! Please point your browser to: http://" + bindAddress + ":" + config.port);
|
||||||
});
|
});
|
||||||
|
@ -36,7 +36,7 @@ describe("Electron app environment", function() {
|
|||||||
it("should open a browserwindow", function() {
|
it("should open a browserwindow", function() {
|
||||||
return app.client
|
return app.client
|
||||||
.waitUntilWindowLoaded()
|
.waitUntilWindowLoaded()
|
||||||
.browserWindow.focus()
|
// .browserWindow.focus()
|
||||||
.getWindowCount()
|
.getWindowCount()
|
||||||
.should.eventually.equal(1)
|
.should.eventually.equal(1)
|
||||||
.browserWindow.isMinimized()
|
.browserWindow.isMinimized()
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
"DAYAFTERTOMORROW": "I overmorgen",
|
"DAYAFTERTOMORROW": "I overmorgen",
|
||||||
"RUNNING": "Slutter om",
|
"RUNNING": "Slutter om",
|
||||||
"EMPTY": "Ingen kommende begivenheder.",
|
"EMPTY": "Ingen kommende begivenheder.",
|
||||||
|
"FEELS": "Føles som",
|
||||||
|
"WEEK": "Uge {weekNumber}",
|
||||||
|
|
||||||
"N": "N",
|
"N": "N",
|
||||||
"NNE": "NNØ",
|
"NNE": "NNØ",
|
||||||
|
@ -29,5 +29,7 @@
|
|||||||
"UPDATE_NOTIFICATION": "E' disponibile un aggiornamento di MagicMirror².",
|
"UPDATE_NOTIFICATION": "E' disponibile un aggiornamento di MagicMirror².",
|
||||||
"UPDATE_NOTIFICATION_MODULE": "E' disponibile un aggiornamento del modulo {MODULE_NAME}.",
|
"UPDATE_NOTIFICATION_MODULE": "E' disponibile un aggiornamento del modulo {MODULE_NAME}.",
|
||||||
"UPDATE_INFO_SINGLE": "L'installazione è {COMMIT_COUNT} commit indietro rispetto all'attuale branch {BRANCH_NAME}.",
|
"UPDATE_INFO_SINGLE": "L'installazione è {COMMIT_COUNT} commit indietro rispetto all'attuale branch {BRANCH_NAME}.",
|
||||||
"UPDATE_INFO_MULTIPLE": "L'installazione è {COMMIT_COUNT} commits indietro rispetto all'attuale branch {BRANCH_NAME}."
|
"UPDATE_INFO_MULTIPLE": "L'installazione è {COMMIT_COUNT} commits indietro rispetto all'attuale branch {BRANCH_NAME}.",
|
||||||
|
|
||||||
|
"FEELS": "Percepiti"
|
||||||
}
|
}
|
||||||
|
33
translations/sk.json
Normal file
33
translations/sk.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"LOADING": "Načítanie …",
|
||||||
|
|
||||||
|
"TODAY": "Dnes",
|
||||||
|
"TOMORROW": "Zajtra",
|
||||||
|
"DAYAFTERTOMORROW": "Pozajtra",
|
||||||
|
"RUNNING": "Končí o",
|
||||||
|
"EMPTY": "Žiadne nadchádzajúce udalosti.",
|
||||||
|
|
||||||
|
"WEEK": "{weekNumber}. týždeň",
|
||||||
|
|
||||||
|
"N": "S",
|
||||||
|
"NNE": "SSV",
|
||||||
|
"NE": "SV",
|
||||||
|
"ENE": "VSV",
|
||||||
|
"E": "V",
|
||||||
|
"ESE": "VJV",
|
||||||
|
"SE": "JV",
|
||||||
|
"SSE": "JJV",
|
||||||
|
"S": "J",
|
||||||
|
"SSW": "JJZ",
|
||||||
|
"SW": "JZ",
|
||||||
|
"WSW": "ZJZ",
|
||||||
|
"W": "Z",
|
||||||
|
"WNW": "ZSZ",
|
||||||
|
"NW": "SZ",
|
||||||
|
"NNW": "SSZ",
|
||||||
|
|
||||||
|
"UPDATE_NOTIFICATION": "Dostupná aktualizácia pre MagicMirror².",
|
||||||
|
"UPDATE_NOTIFICATION_MODULE": "Dostupná aktualizácia pre modul {MODULE_NAME}.",
|
||||||
|
"UPDATE_INFO_SINGLE": "Súčasná inštalácia je na vetve {BRANCH_NAME} pozadu o {COMMIT_COUNT} commit.",
|
||||||
|
"UPDATE_INFO_MULTIPLE": "Súčasná inštalácia je na vetve {BRANCH_NAME} pozadu o {COMMIT_COUNT} commitov."
|
||||||
|
}
|
35
translations/tlh.json
Normal file
35
translations/tlh.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"LOADING": "loS …",
|
||||||
|
|
||||||
|
"TODAY": "DaHjaj",
|
||||||
|
"TOMORROW": "wa'leS",
|
||||||
|
"DAYAFTERTOMORROW": "cha'leS",
|
||||||
|
"RUNNING": "Dor",
|
||||||
|
"EMPTY": "Sumbe' wanI'.",
|
||||||
|
|
||||||
|
"WEEK": "Hogh (terran) {weekNumber}",
|
||||||
|
|
||||||
|
"N": "N",
|
||||||
|
"NNE": "NNE",
|
||||||
|
"NE": "NE",
|
||||||
|
"ENE": "ENE",
|
||||||
|
"E": "chan",
|
||||||
|
"ESE": "ESE",
|
||||||
|
"SE": "SE",
|
||||||
|
"SSE": "SSE",
|
||||||
|
"S": "S",
|
||||||
|
"SSW": "SSW",
|
||||||
|
"SW": "tIng",
|
||||||
|
"WSW": "WSW",
|
||||||
|
"W": "W",
|
||||||
|
"WNW": "WNW",
|
||||||
|
"NW": "'ev",
|
||||||
|
"NNW": "NNW",
|
||||||
|
|
||||||
|
"UPDATE_NOTIFICATION": " De'chu' MagicMirror² lI'laH.",
|
||||||
|
"UPDATE_NOTIFICATION_MODULE": "bobcho' {MODULE_NAME} lI'laH De'chu.",
|
||||||
|
"UPDATE_INFO_SINGLE": "The current installation is {COMMIT_COUNT} commit behind on the {BRANCH_NAME} branch.",
|
||||||
|
"UPDATE_INFO_MULTIPLE": "The current installation is {COMMIT_COUNT} commits behind on the {BRANCH_NAME} branch.",
|
||||||
|
|
||||||
|
"FEELS": "jem"
|
||||||
|
}
|
@ -38,7 +38,9 @@ var translations = {
|
|||||||
"cy" : "translations/cy.json", // Welsh (Cymraeg)
|
"cy" : "translations/cy.json", // Welsh (Cymraeg)
|
||||||
"bg" : "translations/bg.json", // Bulgarian
|
"bg" : "translations/bg.json", // Bulgarian
|
||||||
"cs" : "translations/cs.json", // Czech
|
"cs" : "translations/cs.json", // Czech
|
||||||
"hr" : "translations/hr.json" // Croatian
|
"hr" : "translations/hr.json", // Croatian
|
||||||
|
"sk" : "translations/sk.json", // Slovak
|
||||||
|
"tlh" : "translations/tlh.json" // Klingon
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof module !== "undefined") {module.exports = translations;}
|
if (typeof module !== "undefined") {module.exports = translations;}
|
||||||
|
2
vendor/css/font-awesome.css
vendored
Normal file
2
vendor/css/font-awesome.css
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@import url("../node_modules/@fortawesome/fontawesome-free/css/all.min.css");
|
||||||
|
@import url("../node_modules/@fortawesome/fontawesome-free/css/v4-shims.min.css");
|
71
vendor/package-lock.json
generated
vendored
71
vendor/package-lock.json
generated
vendored
@ -193,11 +193,6 @@
|
|||||||
"repeat-string": "^1.5.2"
|
"repeat-string": "^1.5.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"font-awesome": {
|
|
||||||
"version": "4.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
|
|
||||||
"integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM="
|
|
||||||
},
|
|
||||||
"for-in": {
|
"for-in": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
||||||
@ -230,7 +225,8 @@
|
|||||||
},
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"aproba": {
|
"aproba": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
@ -248,11 +244,13 @@
|
|||||||
},
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@ -265,15 +263,18 @@
|
|||||||
},
|
},
|
||||||
"code-point-at": {
|
"code-point-at": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@ -376,7 +377,8 @@
|
|||||||
},
|
},
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
@ -386,6 +388,7 @@
|
|||||||
"is-fullwidth-code-point": {
|
"is-fullwidth-code-point": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
@ -398,17 +401,20 @@
|
|||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.1",
|
"safe-buffer": "^5.1.1",
|
||||||
"yallist": "^3.0.0"
|
"yallist": "^3.0.0"
|
||||||
@ -425,6 +431,7 @@
|
|||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
@ -497,7 +504,8 @@
|
|||||||
},
|
},
|
||||||
"number-is-nan": {
|
"number-is-nan": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
@ -507,6 +515,7 @@
|
|||||||
"once": {
|
"once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
@ -582,7 +591,8 @@
|
|||||||
},
|
},
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"safer-buffer": {
|
"safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
@ -612,6 +622,7 @@
|
|||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
@ -629,6 +640,7 @@
|
|||||||
"strip-ansi": {
|
"strip-ansi": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "^2.0.0"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
@ -667,11 +679,13 @@
|
|||||||
},
|
},
|
||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"bundled": true
|
"bundled": true,
|
||||||
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -689,6 +703,7 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
|
||||||
"integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
|
"integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-glob": "^2.0.0"
|
"is-glob": "^2.0.0"
|
||||||
}
|
}
|
||||||
@ -702,7 +717,8 @@
|
|||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"invert-kv": {
|
"invert-kv": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@ -721,7 +737,8 @@
|
|||||||
"is-buffer": {
|
"is-buffer": {
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
|
||||||
"integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw="
|
"integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"is-dotfile": {
|
"is-dotfile": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
@ -747,7 +764,8 @@
|
|||||||
"is-extglob": {
|
"is-extglob": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
|
||||||
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA="
|
"integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"is-fullwidth-code-point": {
|
"is-fullwidth-code-point": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@ -761,6 +779,7 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
|
||||||
"integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
|
"integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-extglob": "^1.0.0"
|
"is-extglob": "^1.0.0"
|
||||||
}
|
}
|
||||||
@ -789,7 +808,8 @@
|
|||||||
"isarray": {
|
"isarray": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"isobject": {
|
"isobject": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
@ -804,6 +824,7 @@
|
|||||||
"version": "3.2.2",
|
"version": "3.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
|
||||||
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
|
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-buffer": "^1.1.5"
|
"is-buffer": "^1.1.5"
|
||||||
}
|
}
|
||||||
@ -869,6 +890,7 @@
|
|||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
|
||||||
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
|
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"remove-trailing-separator": "^1.0.1"
|
"remove-trailing-separator": "^1.0.1"
|
||||||
}
|
}
|
||||||
@ -1017,12 +1039,14 @@
|
|||||||
"remove-trailing-separator": {
|
"remove-trailing-separator": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
||||||
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
|
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"repeat-element": {
|
"repeat-element": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
|
||||||
"integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo="
|
"integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"repeat-string": {
|
"repeat-string": {
|
||||||
"version": "1.6.1",
|
"version": "1.6.1",
|
||||||
@ -1033,7 +1057,8 @@
|
|||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
|
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"set-immediate-shim": {
|
"set-immediate-shim": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
|
1
vendor/package.json
vendored
1
vendor/package.json
vendored
@ -11,7 +11,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^5.3.1",
|
"@fortawesome/fontawesome-free": "^5.3.1",
|
||||||
"font-awesome": "^4.7.0",
|
|
||||||
"moment": "^2.17.1",
|
"moment": "^2.17.1",
|
||||||
"moment-timezone": "^0.5.11",
|
"moment-timezone": "^0.5.11",
|
||||||
"nunjucks": "^3.0.1",
|
"nunjucks": "^3.0.1",
|
||||||
|
4
vendor/vendor.js
vendored
4
vendor/vendor.js
vendored
@ -12,9 +12,7 @@ var vendor = {
|
|||||||
"moment-timezone.js" : "node_modules/moment-timezone/builds/moment-timezone-with-data.js",
|
"moment-timezone.js" : "node_modules/moment-timezone/builds/moment-timezone-with-data.js",
|
||||||
"weather-icons.css": "node_modules/weathericons/css/weather-icons.css",
|
"weather-icons.css": "node_modules/weathericons/css/weather-icons.css",
|
||||||
"weather-icons-wind.css": "node_modules/weathericons/css/weather-icons-wind.css",
|
"weather-icons-wind.css": "node_modules/weathericons/css/weather-icons-wind.css",
|
||||||
"font-awesome.css": "node_modules/font-awesome/css/font-awesome.min.css",
|
"font-awesome.css": "css/font-awesome.css",
|
||||||
"font-awesome5.css": "node_modules/@fortawesome/fontawesome-free/css/all.min.css",
|
|
||||||
"font-awesome5.v4shims.css": "node_modules/@fortawesome/fontawesome-free/css/v4-shims.min.css",
|
|
||||||
"nunjucks.js": "node_modules/nunjucks/browser/nunjucks.min.js"
|
"nunjucks.js": "node_modules/nunjucks/browser/nunjucks.min.js"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user