mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-07-01 13:33:15 +00:00
Merge branch 'develop' into develop
This commit is contained in:
commit
934ac886cb
14
CHANGELOG.md
14
CHANGELOG.md
@ -5,10 +5,6 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
|
|
||||||
❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror²
|
❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror²
|
||||||
|
|
||||||
### fixed
|
|
||||||
|
|
||||||
- 2110, 2111, 2118 recurring full day events should not use timezone adjustment. just compare month/day
|
|
||||||
|
|
||||||
## [2.13.0] - Unreleased (Develop Branch - Please add your contributions to this release.)
|
## [2.13.0] - Unreleased (Develop Branch - Please add your contributions to this release.)
|
||||||
|
|
||||||
_This release is scheduled to be released on 2020-10-01._
|
_This release is scheduled to be released on 2020-10-01._
|
||||||
@ -22,6 +18,7 @@ _This release is scheduled to be released on 2020-10-01._
|
|||||||
- Add lithuanian language.
|
- Add lithuanian language.
|
||||||
- Added support in weatherforecast for OpenWeather onecall API.
|
- Added support in weatherforecast for OpenWeather onecall API.
|
||||||
- Added config option to calendar-icons for recurring- and fullday-events
|
- Added config option to calendar-icons for recurring- and fullday-events
|
||||||
|
- Added current, hourly (max 48), and daily (max 7) weather forecasts to weather module via OpenWeatherMap One Call API
|
||||||
- Added eslint-plugin for jsdoc comments
|
- Added eslint-plugin for jsdoc comments
|
||||||
|
|
||||||
### Updated
|
### Updated
|
||||||
@ -29,17 +26,21 @@ _This release is scheduled to be released on 2020-10-01._
|
|||||||
- Change incorrect weather.js default properties.
|
- Change incorrect weather.js default properties.
|
||||||
- Cleaned up newsfeed module.
|
- Cleaned up newsfeed module.
|
||||||
- Cleaned up jsdoc comments.
|
- Cleaned up jsdoc comments.
|
||||||
|
- Cleaned up clock tests.
|
||||||
|
- Move lodash into devDependencies, update other dependencies
|
||||||
|
|
||||||
### Deleted
|
### Deleted
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fix backward compatibility issues for Safari < 11. [#1985](https://github.com/MichMich/MagicMirror/issues/1985)
|
- Fix backward compatibility issues for Safari < 11.
|
||||||
- Fix the use of "maxNumberOfDays" in the module "weatherforecast depending on the endpoint (forecast/daily or forecast)". [#2018](https://github.com/MichMich/MagicMirror/issues/2018)
|
- Fix the use of "maxNumberOfDays" in the module "weatherforecast depending on the endpoint (forecast/daily or forecast)". [#2018](https://github.com/MichMich/MagicMirror/issues/2018)
|
||||||
- Fix calendar display. Account for current timezone. [#2068](https://github.com/MichMich/MagicMirror/issues/2068)
|
- Fix calendar display. Account for current timezone. [#2068](https://github.com/MichMich/MagicMirror/issues/2068)
|
||||||
- Fix logLevel being set before loading config.
|
- Fix logLevel being set before loading config.
|
||||||
- Fix incorrect namespace links in svg clockfaces. [#2072](https://github.com/MichMich/MagicMirror/issues/2072)
|
- Fix incorrect namespace links in svg clockfaces. [#2072](https://github.com/MichMich/MagicMirror/issues/2072)
|
||||||
- Fix weather/providers/weathergov for API guidelines [#2045]
|
- Fix weather/providers/weathergov for API guidelines. [#2045](https://github.com/MichMich/MagicMirror/issues/2045)
|
||||||
|
- Fix "undefined" in weather modules header. [#1985](https://github.com/MichMich/MagicMirror/issues/1985)
|
||||||
|
- Fix #2110, #2111, #2118: Recurring full day events should not use timezone adjustment. Just compare month/day.
|
||||||
|
|
||||||
## [2.12.0] - 2020-07-01
|
## [2.12.0] - 2020-07-01
|
||||||
|
|
||||||
@ -132,6 +133,7 @@ For more information regarding this major change, please check issue [#1860](htt
|
|||||||
- Timestamp in log output now also contains the date
|
- Timestamp in log output now also contains the date
|
||||||
- Turkish translation.
|
- Turkish translation.
|
||||||
- Option to configure the size of the currentweather module.
|
- Option to configure the size of the currentweather module.
|
||||||
|
- Changed "Gevoelstemperatuur" to "Voelt als" shorter text.
|
||||||
|
|
||||||
## [2.10.1] - 2020-01-10
|
## [2.10.1] - 2020-01-10
|
||||||
|
|
||||||
|
@ -98,6 +98,7 @@ var Loader = (function () {
|
|||||||
file: moduleName + ".js",
|
file: moduleName + ".js",
|
||||||
position: moduleData.position,
|
position: moduleData.position,
|
||||||
header: moduleData.header,
|
header: moduleData.header,
|
||||||
|
configDeepMerge: typeof moduleData.configDeepMerge === "boolean" ? moduleData.configDeepMerge : false,
|
||||||
config: moduleData.config,
|
config: moduleData.config,
|
||||||
classes: typeof moduleData.classes !== "undefined" ? moduleData.classes + " " + module : module
|
classes: typeof moduleData.classes !== "undefined" ? moduleData.classes + " " + module : module
|
||||||
});
|
});
|
||||||
|
@ -42,6 +42,8 @@ var MM = (function () {
|
|||||||
|
|
||||||
if (typeof module.getHeader() === "undefined" || module.getHeader() !== "") {
|
if (typeof module.getHeader() === "undefined" || module.getHeader() !== "") {
|
||||||
moduleHeader.style.display = "none;";
|
moduleHeader.style.display = "none;";
|
||||||
|
} else {
|
||||||
|
moduleHeader.style.display = "block;";
|
||||||
}
|
}
|
||||||
|
|
||||||
var moduleContent = document.createElement("div");
|
var moduleContent = document.createElement("div");
|
||||||
@ -218,7 +220,7 @@ var MM = (function () {
|
|||||||
|
|
||||||
headerWrapper[0].innerHTML = newHeader;
|
headerWrapper[0].innerHTML = newHeader;
|
||||||
if (headerWrapper.length > 0 && newHeader) {
|
if (headerWrapper.length > 0 && newHeader) {
|
||||||
delete headerWrapper[0].style;
|
headerWrapper[0].style.display = "block";
|
||||||
} else {
|
} else {
|
||||||
headerWrapper[0].style.display = "none";
|
headerWrapper[0].style.display = "none";
|
||||||
}
|
}
|
||||||
|
49
js/module.js
49
js/module.js
@ -221,16 +221,17 @@ var Module = Class.extend({
|
|||||||
this.identifier = data.identifier;
|
this.identifier = data.identifier;
|
||||||
this.hidden = false;
|
this.hidden = false;
|
||||||
|
|
||||||
this.setConfig(data.config);
|
this.setConfig(data.config, data.configDeepMerge);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the module config and combine it with the module defaults.
|
* Set the module config and combine it with the module defaults.
|
||||||
*
|
*
|
||||||
* @param {object} config The combined module config.
|
* @param {object} config The combined module config.
|
||||||
|
* @param {boolean} config Merge module config in deep.
|
||||||
*/
|
*/
|
||||||
setConfig: function (config) {
|
setConfig: function (config, deep) {
|
||||||
this.config = Object.assign({}, this.defaults, config);
|
this.config = deep ? configMerge({}, this.defaults, config) : Object.assign({}, this.defaults, config);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -440,6 +441,48 @@ var Module = Class.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** Merging MagicMirror (or other) default/config script
|
||||||
|
* merge 2 objects or/with array
|
||||||
|
* using:
|
||||||
|
* -------
|
||||||
|
* this.config = configMerge({}, this.defaults, this.config)
|
||||||
|
* -------
|
||||||
|
* arg1: initial objet
|
||||||
|
* arg2: config model
|
||||||
|
* arg3: config to merge
|
||||||
|
* -------
|
||||||
|
* why using it ?
|
||||||
|
* Object.assign() function don't to all job
|
||||||
|
* it don't merge all thing in deep
|
||||||
|
* -> object in object and array is not merging
|
||||||
|
* -------
|
||||||
|
* @bugsounet
|
||||||
|
* @Todo: idea of Mich determinate what do you want to merge or not
|
||||||
|
*/
|
||||||
|
|
||||||
|
function configMerge(result) {
|
||||||
|
var stack = Array.prototype.slice.call(arguments, 1);
|
||||||
|
var item;
|
||||||
|
var key;
|
||||||
|
while (stack.length) {
|
||||||
|
item = stack.shift();
|
||||||
|
for (key in item) {
|
||||||
|
if (item.hasOwnProperty(key)) {
|
||||||
|
if (typeof result[key] === "object" && result[key] && Object.prototype.toString.call(result[key]) !== "[object Array]") {
|
||||||
|
if (typeof item[key] === "object" && item[key] !== null) {
|
||||||
|
result[key] = configMerge({}, result[key], item[key]);
|
||||||
|
} else {
|
||||||
|
result[key] = item[key];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result[key] = item[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Module.definitions = {};
|
Module.definitions = {};
|
||||||
|
|
||||||
Module.create = function (name) {
|
Module.create = function (name) {
|
||||||
|
@ -37,6 +37,8 @@ Module.register("currentweather", {
|
|||||||
weatherEndpoint: "weather",
|
weatherEndpoint: "weather",
|
||||||
|
|
||||||
appendLocationNameToHeader: true,
|
appendLocationNameToHeader: true,
|
||||||
|
useLocationAsHeader: false,
|
||||||
|
|
||||||
calendarClass: "calendar",
|
calendarClass: "calendar",
|
||||||
tableClass: "large",
|
tableClass: "large",
|
||||||
|
|
||||||
@ -267,15 +269,16 @@ Module.register("currentweather", {
|
|||||||
|
|
||||||
// Override getHeader method.
|
// Override getHeader method.
|
||||||
getHeader: function () {
|
getHeader: function () {
|
||||||
if (this.config.appendLocationNameToHeader && this.data.header !== undefined) {
|
|
||||||
return this.data.header + " " + this.fetchedLocationName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.useLocationAsHeader && this.config.location !== false) {
|
if (this.config.useLocationAsHeader && this.config.location !== false) {
|
||||||
return this.config.location;
|
return this.config.location;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.data.header;
|
if (this.config.appendLocationNameToHeader) {
|
||||||
|
if (this.data.header) return this.data.header + " " + this.fetchedLocationName;
|
||||||
|
else return this.fetchedLocationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.data.header ? this.data.header : "";
|
||||||
},
|
},
|
||||||
|
|
||||||
// Override notification handler.
|
// Override notification handler.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Weather Module
|
# Weather Module
|
||||||
|
|
||||||
This module aims 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 aims 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 fulfill both purposes.
|
||||||
|
|
||||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/weather.html).
|
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/weather.html).
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
{% if current %}
|
{% if current or weatherData %}
|
||||||
|
{% if weatherData %}
|
||||||
|
{% set current = weatherData.current %}
|
||||||
|
{% endif %}
|
||||||
{% if not config.onlyTemp %}
|
{% if not config.onlyTemp %}
|
||||||
<div class="normal medium">
|
<div class="normal medium">
|
||||||
<span class="wi wi-strong-wind dimmed"></span>
|
<span class="wi wi-strong-wind dimmed"></span>
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
{% if forecast %}
|
{% if forecast or weatherData %}
|
||||||
{% set numSteps = forecast | calcNumSteps %}
|
{% if weatherData %}
|
||||||
|
{% set forecast = weatherData.days %}
|
||||||
|
{% set numSteps = forecast | calcNumEntries %}
|
||||||
|
{% else %}
|
||||||
|
{% set numSteps = forecast | calcNumSteps %}
|
||||||
|
{% endif %}
|
||||||
{% set currentStep = 0 %}
|
{% set currentStep = 0 %}
|
||||||
<table class="{{ config.tableClass }}">
|
<table class="{{ config.tableClass }}">
|
||||||
{% set forecast = forecast.slice(0, numSteps) %}
|
{% set forecast = forecast.slice(0, numSteps) %}
|
||||||
|
32
modules/default/weather/hourly.njk
Normal file
32
modules/default/weather/hourly.njk
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{% if hourly or weatherData %}
|
||||||
|
{% if weatherData %}
|
||||||
|
{% set hourly = weatherData.hours %}
|
||||||
|
{% endif %}
|
||||||
|
{% set numSteps = hourly | calcNumEntries %}
|
||||||
|
{% set currentStep = 0 %}
|
||||||
|
<table class="{{ config.tableClass }}">
|
||||||
|
{% set hours = hourly.slice(0, numSteps) %}
|
||||||
|
{% for hour in hours %}
|
||||||
|
<tr {% if config.colored %}class="colored"{% endif %} {% if config.fade %}style="opacity: {{ currentStep | opacity(numSteps) }};"{% endif %}>
|
||||||
|
<td class="day">{{ hour.date | formatTime }}</td>
|
||||||
|
<td class="bright weather-icon"><span class="wi weathericon wi-{{ hour.weatherType }}"></span></td>
|
||||||
|
<td class="align-right bright">
|
||||||
|
{{ hour.temperature | roundValue | unit("temperature") }}
|
||||||
|
</td>
|
||||||
|
{% if config.showPrecipitationAmount %}
|
||||||
|
<td class="align-right bright precipitation">
|
||||||
|
{{ hour.precipitation | unit("precip") }}
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
{% set currentStep = currentStep + 1 %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<div class="dimmed light small">
|
||||||
|
{{ "LOADING" | translate | safe }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Uncomment the line below to see the contents of the `hourly` object. -->
|
||||||
|
<!-- <div style="word-wrap:break-word" class="xsmall dimmed">{{weatherData | dump}}</div> -->
|
@ -35,7 +35,7 @@ WeatherProvider.register("openweathermap", {
|
|||||||
.finally(() => this.updateAvailable());
|
.finally(() => this.updateAvailable());
|
||||||
},
|
},
|
||||||
|
|
||||||
// Overwrite the fetchCurrentWeather method.
|
// Overwrite the fetchWeatherForecast method.
|
||||||
fetchWeatherForecast() {
|
fetchWeatherForecast() {
|
||||||
this.fetchData(this.getUrl())
|
this.fetchData(this.getUrl())
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
@ -56,6 +56,27 @@ WeatherProvider.register("openweathermap", {
|
|||||||
.finally(() => this.updateAvailable());
|
.finally(() => this.updateAvailable());
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Overwrite the fetchWeatherData method.
|
||||||
|
fetchWeatherData() {
|
||||||
|
this.fetchData(this.getUrl())
|
||||||
|
.then((data) => {
|
||||||
|
if (!data) {
|
||||||
|
// Did not receive usable new data.
|
||||||
|
// Maybe this needs a better check?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setFetchedLocation(`(${data.lat},${data.lon})`);
|
||||||
|
|
||||||
|
const weatherData = this.generateWeatherObjectsFromOnecall(data);
|
||||||
|
this.setWeatherData(weatherData);
|
||||||
|
})
|
||||||
|
.catch(function (request) {
|
||||||
|
Log.error("Could not load data ... ", request);
|
||||||
|
})
|
||||||
|
.finally(() => this.updateAvailable());
|
||||||
|
},
|
||||||
|
|
||||||
/** 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
|
||||||
@ -95,6 +116,18 @@ WeatherProvider.register("openweathermap", {
|
|||||||
return days;
|
return days;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate WeatherObjects based on One Call forecast information
|
||||||
|
*/
|
||||||
|
generateWeatherObjectsFromOnecall(data) {
|
||||||
|
if (this.config.weatherEndpoint === "/onecall") {
|
||||||
|
return this.fetchOnecall(data);
|
||||||
|
}
|
||||||
|
// if weatherEndpoint does not match onecall, what should be returned?
|
||||||
|
const weatherData = { current: new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits), hours: [], days: [] };
|
||||||
|
return weatherData;
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fetch forecast information for 3-hourly forecast (available for free subscription).
|
* fetch forecast information for 3-hourly forecast (available for free subscription).
|
||||||
*/
|
*/
|
||||||
@ -221,6 +254,129 @@ WeatherProvider.register("openweathermap", {
|
|||||||
return days;
|
return days;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch One Call forecast information (available for free subscription).
|
||||||
|
* Factors in timezone offsets.
|
||||||
|
* Minutely forecasts are excluded for the moment, see getParams().
|
||||||
|
*/
|
||||||
|
fetchOnecall(data) {
|
||||||
|
let precip = false;
|
||||||
|
|
||||||
|
// get current weather, if requested
|
||||||
|
const current = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||||
|
if (data.hasOwnProperty("current")) {
|
||||||
|
current.date = moment(data.current.dt, "X").utcOffset(data.timezone_offset / 60);
|
||||||
|
current.windSpeed = data.current.wind_speed;
|
||||||
|
current.windDirection = data.current.wind_deg;
|
||||||
|
current.sunrise = moment(data.current.sunrise, "X").utcOffset(data.timezone_offset / 60);
|
||||||
|
current.sunset = moment(data.current.sunset, "X").utcOffset(data.timezone_offset / 60);
|
||||||
|
current.temperature = data.current.temp;
|
||||||
|
current.weatherType = this.convertWeatherType(data.current.weather[0].icon);
|
||||||
|
current.humidity = data.current.humidity;
|
||||||
|
if (data.current.hasOwnProperty("rain") && !isNaN(data.current["rain"]["1h"])) {
|
||||||
|
if (this.config.units === "imperial") {
|
||||||
|
current.rain = data.current["rain"]["1h"] / 25.4;
|
||||||
|
} else {
|
||||||
|
current.rain = data.current["rain"]["1h"];
|
||||||
|
}
|
||||||
|
precip = true;
|
||||||
|
}
|
||||||
|
if (data.current.hasOwnProperty("snow") && !isNaN(data.current["snow"]["1h"])) {
|
||||||
|
if (this.config.units === "imperial") {
|
||||||
|
current.snow = data.current["snow"]["1h"] / 25.4;
|
||||||
|
} else {
|
||||||
|
current.snow = data.current["snow"]["1h"];
|
||||||
|
}
|
||||||
|
precip = true;
|
||||||
|
}
|
||||||
|
if (precip) {
|
||||||
|
current.precipitation = current.rain + current.snow;
|
||||||
|
}
|
||||||
|
current.feelsLikeTemp = data.current.feels_like;
|
||||||
|
}
|
||||||
|
|
||||||
|
let weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||||
|
|
||||||
|
// get hourly weather, if requested
|
||||||
|
const hours = [];
|
||||||
|
if (data.hasOwnProperty("hourly")) {
|
||||||
|
for (const hour of data.hourly) {
|
||||||
|
weather.date = moment(hour.dt, "X").utcOffset(data.timezone_offset / 60);
|
||||||
|
// weather.date = moment(hour.dt, "X").utcOffset(data.timezone_offset/60).format(onecallDailyFormat+","+onecallHourlyFormat);
|
||||||
|
weather.temperature = hour.temp;
|
||||||
|
weather.feelsLikeTemp = hour.feels_like;
|
||||||
|
weather.humidity = hour.humidity;
|
||||||
|
weather.windSpeed = hour.wind_speed;
|
||||||
|
weather.windDirection = hour.wind_deg;
|
||||||
|
weather.weatherType = this.convertWeatherType(hour.weather[0].icon);
|
||||||
|
precip = false;
|
||||||
|
if (hour.hasOwnProperty("rain") && !isNaN(hour.rain["1h"])) {
|
||||||
|
if (this.config.units === "imperial") {
|
||||||
|
weather.rain = hour.rain["1h"] / 25.4;
|
||||||
|
} else {
|
||||||
|
weather.rain = hour.rain["1h"];
|
||||||
|
}
|
||||||
|
precip = true;
|
||||||
|
}
|
||||||
|
if (hour.hasOwnProperty("snow") && !isNaN(hour.snow["1h"])) {
|
||||||
|
if (this.config.units === "imperial") {
|
||||||
|
weather.snow = hour.snow["1h"] / 25.4;
|
||||||
|
} else {
|
||||||
|
weather.snow = hour.snow["1h"];
|
||||||
|
}
|
||||||
|
precip = true;
|
||||||
|
}
|
||||||
|
if (precip) {
|
||||||
|
weather.precipitation = weather.rain + weather.snow;
|
||||||
|
}
|
||||||
|
|
||||||
|
hours.push(weather);
|
||||||
|
weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get daily weather, if requested
|
||||||
|
const days = [];
|
||||||
|
if (data.hasOwnProperty("daily")) {
|
||||||
|
for (const day of data.daily) {
|
||||||
|
weather.date = moment(day.dt, "X").utcOffset(data.timezone_offset / 60);
|
||||||
|
weather.sunrise = moment(day.sunrise, "X").utcOffset(data.timezone_offset / 60);
|
||||||
|
weather.sunset = moment(day.sunset, "X").utcOffset(data.timezone_offset / 60);
|
||||||
|
weather.minTemperature = day.temp.min;
|
||||||
|
weather.maxTemperature = day.temp.max;
|
||||||
|
weather.humidity = day.humidity;
|
||||||
|
weather.windSpeed = day.wind_speed;
|
||||||
|
weather.windDirection = day.wind_deg;
|
||||||
|
weather.weatherType = this.convertWeatherType(day.weather[0].icon);
|
||||||
|
precip = false;
|
||||||
|
if (!isNaN(day.rain)) {
|
||||||
|
if (this.config.units === "imperial") {
|
||||||
|
weather.rain = day.rain / 25.4;
|
||||||
|
} else {
|
||||||
|
weather.rain = day.rain;
|
||||||
|
}
|
||||||
|
precip = true;
|
||||||
|
}
|
||||||
|
if (!isNaN(day.snow)) {
|
||||||
|
if (this.config.units === "imperial") {
|
||||||
|
weather.snow = day.snow / 25.4;
|
||||||
|
} else {
|
||||||
|
weather.snow = day.snow;
|
||||||
|
}
|
||||||
|
precip = true;
|
||||||
|
}
|
||||||
|
if (precip) {
|
||||||
|
weather.precipitation = weather.rain + weather.snow;
|
||||||
|
}
|
||||||
|
|
||||||
|
days.push(weather);
|
||||||
|
weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { current: current, hours: hours, days: days };
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert the OpenWeatherMap icons to a more usable name.
|
* Convert the OpenWeatherMap icons to a more usable name.
|
||||||
*/
|
*/
|
||||||
@ -256,7 +412,19 @@ WeatherProvider.register("openweathermap", {
|
|||||||
*/
|
*/
|
||||||
getParams() {
|
getParams() {
|
||||||
let params = "?";
|
let params = "?";
|
||||||
if (this.config.locationID) {
|
if (this.config.weatherEndpoint === "/onecall") {
|
||||||
|
params += "lat=" + this.config.lat;
|
||||||
|
params += "&lon=" + this.config.lon;
|
||||||
|
if (this.config.type === "current") {
|
||||||
|
params += "&exclude=minutely,hourly,daily";
|
||||||
|
} else if (this.config.type === "hourly") {
|
||||||
|
params += "&exclude=current,minutely,daily";
|
||||||
|
} else if (this.config.type === "daily" || this.config.type === "forecast") {
|
||||||
|
params += "&exclude=current,minutely,hourly";
|
||||||
|
} else {
|
||||||
|
params += "&exclude=minutely";
|
||||||
|
}
|
||||||
|
} else if (this.config.locationID) {
|
||||||
params += "id=" + this.config.locationID;
|
params += "id=" + this.config.locationID;
|
||||||
} else if (this.config.location) {
|
} else if (this.config.location) {
|
||||||
params += "q=" + this.config.location;
|
params += "q=" + this.config.location;
|
||||||
|
@ -11,8 +11,10 @@ Module.register("weather", {
|
|||||||
defaults: {
|
defaults: {
|
||||||
weatherProvider: "openweathermap",
|
weatherProvider: "openweathermap",
|
||||||
roundTemp: false,
|
roundTemp: false,
|
||||||
type: "current", //current, forecast
|
type: "current", // current, forecast, daily (equivalent to forecast), hourly (only with OpenWeatherMap /onecall endpoint)
|
||||||
|
|
||||||
|
lat: 0,
|
||||||
|
lon: 0,
|
||||||
location: false,
|
location: false,
|
||||||
locationID: false,
|
locationID: false,
|
||||||
units: config.units,
|
units: config.units,
|
||||||
@ -36,6 +38,7 @@ Module.register("weather", {
|
|||||||
showIndoorTemperature: false,
|
showIndoorTemperature: false,
|
||||||
showIndoorHumidity: false,
|
showIndoorHumidity: false,
|
||||||
maxNumberOfDays: 5,
|
maxNumberOfDays: 5,
|
||||||
|
maxEntries: 5,
|
||||||
fade: true,
|
fade: true,
|
||||||
fadePoint: 0.25, // Start on 1/4th of the list.
|
fadePoint: 0.25, // Start on 1/4th of the list.
|
||||||
|
|
||||||
@ -73,11 +76,12 @@ Module.register("weather", {
|
|||||||
|
|
||||||
// Override getHeader method.
|
// Override getHeader method.
|
||||||
getHeader: function () {
|
getHeader: function () {
|
||||||
if (this.config.appendLocationNameToHeader && this.data.header !== undefined && this.weatherProvider) {
|
if (this.config.appendLocationNameToHeader && this.weatherProvider) {
|
||||||
return this.data.header + " " + this.weatherProvider.fetchedLocation();
|
if (this.data.header) return this.data.header + " " + this.weatherProvider.fetchedLocation();
|
||||||
|
else return this.weatherProvider.fetchedLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.data.header;
|
return this.data.header ? this.data.header : "";
|
||||||
},
|
},
|
||||||
|
|
||||||
// Start the weather module.
|
// Start the weather module.
|
||||||
@ -124,7 +128,17 @@ Module.register("weather", {
|
|||||||
|
|
||||||
// Select the template depending on the display type.
|
// Select the template depending on the display type.
|
||||||
getTemplate: function () {
|
getTemplate: function () {
|
||||||
return `${this.config.type.toLowerCase()}.njk`;
|
switch (this.config.type.toLowerCase()) {
|
||||||
|
case "current":
|
||||||
|
return `current.njk`;
|
||||||
|
case "hourly":
|
||||||
|
return `hourly.njk`;
|
||||||
|
case "daily":
|
||||||
|
case "forecast":
|
||||||
|
return `forecast.njk`;
|
||||||
|
default:
|
||||||
|
return `${this.config.type.toLowerCase()}.njk`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Add all the data to the template.
|
// Add all the data to the template.
|
||||||
@ -133,6 +147,7 @@ Module.register("weather", {
|
|||||||
config: this.config,
|
config: this.config,
|
||||||
current: this.weatherProvider.currentWeather(),
|
current: this.weatherProvider.currentWeather(),
|
||||||
forecast: this.weatherProvider.weatherForecast(),
|
forecast: this.weatherProvider.weatherForecast(),
|
||||||
|
weatherData: this.weatherProvider.weatherData(),
|
||||||
indoor: {
|
indoor: {
|
||||||
humidity: this.indoorHumidity,
|
humidity: this.indoorHumidity,
|
||||||
temperature: this.indoorTemperature
|
temperature: this.indoorTemperature
|
||||||
@ -154,7 +169,9 @@ Module.register("weather", {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.config.type === "forecast") {
|
if (this.config.weatherEndpoint === "/onecall") {
|
||||||
|
this.weatherProvider.fetchWeatherData();
|
||||||
|
} else if (this.config.type === "forecast") {
|
||||||
this.weatherProvider.fetchWeatherForecast();
|
this.weatherProvider.fetchWeatherForecast();
|
||||||
} else {
|
} else {
|
||||||
this.weatherProvider.fetchCurrentWeather();
|
this.weatherProvider.fetchCurrentWeather();
|
||||||
@ -206,7 +223,7 @@ Module.register("weather", {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (type === "precip") {
|
} else if (type === "precip") {
|
||||||
if (isNaN(value) || value === 0 || value.toFixed(2) === "0.00") {
|
if (value === null || isNaN(value) || value === 0 || value.toFixed(2) === "0.00") {
|
||||||
value = "";
|
value = "";
|
||||||
} else {
|
} else {
|
||||||
if (this.config.weatherProvider === "ukmetoffice" || this.config.weatherProvider === "ukmetofficedatahub") {
|
if (this.config.weatherProvider === "ukmetoffice" || this.config.weatherProvider === "ukmetofficedatahub") {
|
||||||
@ -244,6 +261,13 @@ Module.register("weather", {
|
|||||||
}.bind(this)
|
}.bind(this)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.nunjucksEnvironment().addFilter(
|
||||||
|
"calcNumEntries",
|
||||||
|
function (dataArray) {
|
||||||
|
return Math.min(dataArray.length, this.config.maxEntries);
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
this.nunjucksEnvironment().addFilter(
|
this.nunjucksEnvironment().addFilter(
|
||||||
"opacity",
|
"opacity",
|
||||||
function (currentStep, numSteps) {
|
function (currentStep, numSteps) {
|
||||||
|
@ -16,6 +16,7 @@ var WeatherProvider = Class.extend({
|
|||||||
// Try to not access them directly.
|
// Try to not access them directly.
|
||||||
currentWeatherObject: null,
|
currentWeatherObject: null,
|
||||||
weatherForecastArray: null,
|
weatherForecastArray: null,
|
||||||
|
weatherDataObject: null,
|
||||||
fetchedLocationName: null,
|
fetchedLocationName: null,
|
||||||
|
|
||||||
// The following properties will be set automatically.
|
// The following properties will be set automatically.
|
||||||
@ -56,6 +57,12 @@ var WeatherProvider = Class.extend({
|
|||||||
Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchWeatherForecast method.`);
|
Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchWeatherForecast method.`);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// This method should start the API request to fetch the weather forecast.
|
||||||
|
// This method should definitely be overwritten in the provider.
|
||||||
|
fetchWeatherData: function () {
|
||||||
|
Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchWeatherData method.`);
|
||||||
|
},
|
||||||
|
|
||||||
// This returns a WeatherDay object for the current weather.
|
// This returns a WeatherDay object for the current weather.
|
||||||
currentWeather: function () {
|
currentWeather: function () {
|
||||||
return this.currentWeatherObject;
|
return this.currentWeatherObject;
|
||||||
@ -66,6 +73,11 @@ var WeatherProvider = Class.extend({
|
|||||||
return this.weatherForecastArray;
|
return this.weatherForecastArray;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// This returns an object containing WeatherDay object(s) depending on the type of call.
|
||||||
|
weatherData: function () {
|
||||||
|
return this.weatherDataObject;
|
||||||
|
},
|
||||||
|
|
||||||
// This returns the name of the fetched location or an empty string.
|
// This returns the name of the fetched location or an empty string.
|
||||||
fetchedLocation: function () {
|
fetchedLocation: function () {
|
||||||
return this.fetchedLocationName || "";
|
return this.fetchedLocationName || "";
|
||||||
@ -83,6 +95,11 @@ var WeatherProvider = Class.extend({
|
|||||||
this.weatherForecastArray = weatherForecastArray;
|
this.weatherForecastArray = weatherForecastArray;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Set the weatherDataObject and notify the delegate that new information is available.
|
||||||
|
setWeatherData: function (weatherDataObject) {
|
||||||
|
this.weatherDataObject = weatherDataObject;
|
||||||
|
},
|
||||||
|
|
||||||
// Set the fetched location name.
|
// Set the fetched location name.
|
||||||
setFetchedLocation: function (name) {
|
setFetchedLocation: function (name) {
|
||||||
this.fetchedLocationName = name;
|
this.fetchedLocationName = name;
|
||||||
|
@ -103,7 +103,7 @@ Module.register("weatherforecast", {
|
|||||||
getDom: function () {
|
getDom: function () {
|
||||||
var wrapper = document.createElement("div");
|
var wrapper = document.createElement("div");
|
||||||
|
|
||||||
if (this.config.appid === "") {
|
if (this.config.appid === "" || this.config.appid === "YOUR_OPENWEATHER_API_KEY") {
|
||||||
wrapper.innerHTML = "Please set the correct openweather <i>appid</i> in the config for module: " + this.name + ".";
|
wrapper.innerHTML = "Please set the correct openweather <i>appid</i> in the config for module: " + this.name + ".";
|
||||||
wrapper.className = "dimmed light small";
|
wrapper.className = "dimmed light small";
|
||||||
return wrapper;
|
return wrapper;
|
||||||
@ -206,10 +206,11 @@ Module.register("weatherforecast", {
|
|||||||
// Override getHeader method.
|
// Override getHeader method.
|
||||||
getHeader: function () {
|
getHeader: function () {
|
||||||
if (this.config.appendLocationNameToHeader) {
|
if (this.config.appendLocationNameToHeader) {
|
||||||
return this.data.header + " " + this.fetchedLocationName;
|
if (this.data.header) return this.data.header + " " + this.fetchedLocationName;
|
||||||
|
else return this.fetchedLocationName;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.data.header;
|
return this.data.header ? this.data.header : "";
|
||||||
},
|
},
|
||||||
|
|
||||||
// Override notification handler.
|
// Override notification handler.
|
||||||
|
2724
package-lock.json
generated
2724
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@ -42,25 +42,24 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://magicmirror.builders",
|
"homepage": "https://magicmirror.builders",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@prantlf/jsonlint": "^10.2.0",
|
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"chai-as-promised": "^7.1.1",
|
"chai-as-promised": "^7.1.1",
|
||||||
"current-week-number": "^1.0.7",
|
|
||||||
"danger": "^3.1.3",
|
"danger": "^3.1.3",
|
||||||
"eslint-config-prettier": "^6.11.0",
|
"eslint-config-prettier": "^6.11.0",
|
||||||
"eslint-plugin-jsdoc": "^30.1.0",
|
"eslint-plugin-jsdoc": "^30.5.1",
|
||||||
"eslint-plugin-prettier": "^3.1.4",
|
"eslint-plugin-prettier": "^3.1.4",
|
||||||
"http-auth": "^3.2.3",
|
"http-auth": "^3.2.3",
|
||||||
"husky": "^4.2.5",
|
"husky": "^4.3.0",
|
||||||
"jsdom": "^11.6.2",
|
"jsdom": "^11.6.2",
|
||||||
|
"lodash": "^4.17.20",
|
||||||
"mocha": "^7.1.2",
|
"mocha": "^7.1.2",
|
||||||
"mocha-each": "^2.0.1",
|
"mocha-each": "^2.0.1",
|
||||||
"mocha-logger": "^1.0.6",
|
"mocha-logger": "^1.0.6",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"prettier": "^2.0.5",
|
"prettier": "^2.1.2",
|
||||||
"pretty-quick": "^2.0.1",
|
"pretty-quick": "^3.0.2",
|
||||||
"spectron": "^8.0.0",
|
"spectron": "^8.0.0",
|
||||||
"stylelint": "^13.6.1",
|
"stylelint": "^13.7.1",
|
||||||
"stylelint-config-prettier": "^8.0.2",
|
"stylelint-config-prettier": "^8.0.2",
|
||||||
"stylelint-config-standard": "^20.0.0",
|
"stylelint-config-standard": "^20.0.0",
|
||||||
"stylelint-prettier": "^1.1.2"
|
"stylelint-prettier": "^1.1.2"
|
||||||
@ -69,24 +68,23 @@
|
|||||||
"electron": "^6.1.7"
|
"electron": "^6.1.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"colors": "^1.1.2",
|
"colors": "^1.4.0",
|
||||||
"console-stamp": "^0.2.9",
|
"console-stamp": "^0.2.9",
|
||||||
"eslint": "^7.5.0",
|
"eslint": "^7.9.0",
|
||||||
"express": "^4.16.2",
|
"express": "^4.17.1",
|
||||||
"express-ipfilter": "^1.0.1",
|
"express-ipfilter": "^1.1.2",
|
||||||
"feedme": "latest",
|
"feedme": "^1.2.0",
|
||||||
"helmet": "^3.23.3",
|
"helmet": "^3.23.3",
|
||||||
"iconv-lite": "latest",
|
"iconv-lite": "latest",
|
||||||
"lodash": "^4.17.19",
|
|
||||||
"module-alias": "^2.2.2",
|
"module-alias": "^2.2.2",
|
||||||
"moment": "latest",
|
"moment": "latest",
|
||||||
"node-ical": "^0.12.0",
|
"node-ical": "^0.12.0",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"rrule": "^2.6.4",
|
"rrule": "^2.6.6",
|
||||||
"rrule-alt": "^2.2.8",
|
"rrule-alt": "^2.2.8",
|
||||||
"simple-git": "^1.85.0",
|
"simple-git": "^1.85.0",
|
||||||
"socket.io": "^2.1.1",
|
"socket.io": "^2.3.0",
|
||||||
"valid-url": "latest"
|
"valid-url": "^1.0.9"
|
||||||
},
|
},
|
||||||
"_moduleAliases": {
|
"_moduleAliases": {
|
||||||
"node_helper": "js/node_helper.js"
|
"node_helper": "js/node_helper.js"
|
||||||
|
33
tests/configs/modules/clock/clock_analog.js
Normal file
33
tests/configs/modules/clock/clock_analog.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* Magic Mirror Test config for analog clock face
|
||||||
|
*
|
||||||
|
* MIT Licensed.
|
||||||
|
*/
|
||||||
|
let config = {
|
||||||
|
port: 8080,
|
||||||
|
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||||
|
|
||||||
|
language: "en",
|
||||||
|
timeFormat: 24,
|
||||||
|
units: "metric",
|
||||||
|
electronOptions: {
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
modules: [
|
||||||
|
{
|
||||||
|
module: "clock",
|
||||||
|
position: "middle_center",
|
||||||
|
config: {
|
||||||
|
displayType: "analog",
|
||||||
|
analogFace: "face-006"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||||
|
if (typeof module !== "undefined") {
|
||||||
|
module.exports = config;
|
||||||
|
}
|
42
tests/configs/modules/display.js
Normal file
42
tests/configs/modules/display.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* Magic Mirror Test config for display setters module using the helloworld module
|
||||||
|
*
|
||||||
|
* MIT Licensed.
|
||||||
|
*/
|
||||||
|
var config = {
|
||||||
|
port: 8080,
|
||||||
|
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||||
|
|
||||||
|
language: "en",
|
||||||
|
timeFormat: 24,
|
||||||
|
units: "metric",
|
||||||
|
electronOptions: {
|
||||||
|
fullscreen: false,
|
||||||
|
width: 800,
|
||||||
|
height: 600,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
modules: [
|
||||||
|
{
|
||||||
|
module: "helloworld",
|
||||||
|
position: "top_bar",
|
||||||
|
header: "test_header",
|
||||||
|
config: {
|
||||||
|
text: "Test Display Header"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
module: "helloworld",
|
||||||
|
position: "bottom_bar",
|
||||||
|
config: {
|
||||||
|
text: "Test Hide Header"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||||
|
if (typeof module !== "undefined") {
|
||||||
|
module.exports = config;
|
||||||
|
}
|
@ -20,7 +20,7 @@ var config = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
modules:
|
modules:
|
||||||
// Using exotic content. This is why dont accept go to JSON configuration file
|
// Using exotic content. This is why don't accept go to JSON configuration file
|
||||||
(function () {
|
(function () {
|
||||||
var positions = ["top_bar", "top_left", "top_center", "top_right", "upper_third", "middle_center", "lower_third", "bottom_left", "bottom_center", "bottom_right", "bottom_bar", "fullscreen_above", "fullscreen_below"];
|
var positions = ["top_bar", "top_left", "top_center", "top_right", "upper_third", "middle_center", "lower_third", "bottom_left", "bottom_center", "bottom_right", "bottom_bar", "fullscreen_above", "fullscreen_below"];
|
||||||
var modules = Array();
|
var modules = Array();
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
const helpers = require("../global-setup");
|
const helpers = require("../global-setup");
|
||||||
|
const expect = require("chai").expect;
|
||||||
|
const moment = require("moment");
|
||||||
|
|
||||||
const describe = global.describe;
|
const describe = global.describe;
|
||||||
const it = global.it;
|
const it = global.it;
|
||||||
@ -30,12 +32,12 @@ describe("Clock module", function () {
|
|||||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_24hr.js";
|
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_24hr.js";
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows date with correct format", function () {
|
it("should show the date in the correct format", function () {
|
||||||
const dateRegex = /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}$/;
|
const dateRegex = /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}$/;
|
||||||
return app.client.waitUntilWindowLoaded().getText(".clock .date").should.eventually.match(dateRegex);
|
return app.client.waitUntilWindowLoaded().getText(".clock .date").should.eventually.match(dateRegex);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows time in 24hr format", function () {
|
it("should show the time in 24hr format", function () {
|
||||||
const timeRegex = /^(?:2[0-3]|[01]\d):[0-5]\d[0-5]\d$/;
|
const timeRegex = /^(?:2[0-3]|[01]\d):[0-5]\d[0-5]\d$/;
|
||||||
return app.client.waitUntilWindowLoaded().getText(".clock .time").should.eventually.match(timeRegex);
|
return app.client.waitUntilWindowLoaded().getText(".clock .time").should.eventually.match(timeRegex);
|
||||||
});
|
});
|
||||||
@ -47,12 +49,12 @@ describe("Clock module", function () {
|
|||||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_12hr.js";
|
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_12hr.js";
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows date with correct format", function () {
|
it("should show the date in the correct format", function () {
|
||||||
const dateRegex = /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}$/;
|
const dateRegex = /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}$/;
|
||||||
return app.client.waitUntilWindowLoaded().getText(".clock .date").should.eventually.match(dateRegex);
|
return app.client.waitUntilWindowLoaded().getText(".clock .date").should.eventually.match(dateRegex);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows time in 12hr format", function () {
|
it("should show the time in 12hr format", function () {
|
||||||
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[ap]m$/;
|
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[ap]m$/;
|
||||||
return app.client.waitUntilWindowLoaded().getText(".clock .time").should.eventually.match(timeRegex);
|
return app.client.waitUntilWindowLoaded().getText(".clock .time").should.eventually.match(timeRegex);
|
||||||
});
|
});
|
||||||
@ -64,7 +66,7 @@ describe("Clock module", function () {
|
|||||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_showPeriodUpper.js";
|
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_showPeriodUpper.js";
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows 12hr time with upper case AM/PM", function () {
|
it("should show 12hr time with upper case AM/PM", function () {
|
||||||
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[AP]M$/;
|
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[AP]M$/;
|
||||||
return app.client.waitUntilWindowLoaded().getText(".clock .time").should.eventually.match(timeRegex);
|
return app.client.waitUntilWindowLoaded().getText(".clock .time").should.eventually.match(timeRegex);
|
||||||
});
|
});
|
||||||
@ -76,7 +78,7 @@ describe("Clock module", function () {
|
|||||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_displaySeconds_false.js";
|
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_displaySeconds_false.js";
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows 12hr time without seconds am/pm", function () {
|
it("should show 12hr time without seconds am/pm", function () {
|
||||||
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[ap]m$/;
|
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[ap]m$/;
|
||||||
return app.client.waitUntilWindowLoaded().getText(".clock .time").should.eventually.match(timeRegex);
|
return app.client.waitUntilWindowLoaded().getText(".clock .time").should.eventually.match(timeRegex);
|
||||||
});
|
});
|
||||||
@ -88,17 +90,28 @@ describe("Clock module", function () {
|
|||||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_showWeek.js";
|
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_showWeek.js";
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows week with correct format", function () {
|
it("should show the week in the correct format", function () {
|
||||||
const weekRegex = /^Week [0-9]{1,2}$/;
|
const weekRegex = /^Week [0-9]{1,2}$/;
|
||||||
return app.client.waitUntilWindowLoaded().getText(".clock .week").should.eventually.match(weekRegex);
|
return app.client.waitUntilWindowLoaded().getText(".clock .week").should.eventually.match(weekRegex);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows week with correct number of week of year", function () {
|
it("should show the week with the correct number of week of year", function () {
|
||||||
it("FIXME: if the day is a sunday this not match");
|
const currentWeekNumber = moment().week();
|
||||||
// const currentWeekNumber = require("current-week-number")();
|
const weekToShow = "Week " + currentWeekNumber;
|
||||||
// const weekToShow = "Week " + currentWeekNumber;
|
return app.client.waitUntilWindowLoaded().getText(".clock .week").should.eventually.equal(weekToShow);
|
||||||
// return app.client.waitUntilWindowLoaded()
|
});
|
||||||
// .getText(".clock .week").should.eventually.equal(weekToShow);
|
});
|
||||||
|
|
||||||
|
describe("with analog clock face enabled", function () {
|
||||||
|
before(function () {
|
||||||
|
// Set config sample for use in test
|
||||||
|
process.env.MM_CONFIG_FILE = "tests/configs/modules/clock/clock_analog.js";
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show the analog clock face", async () => {
|
||||||
|
await app.client.waitUntilWindowLoaded(10000);
|
||||||
|
const clock = await app.client.$$(".clockCircle");
|
||||||
|
return expect(clock.length).equals(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
41
tests/e2e/modules_display_spec.js
Normal file
41
tests/e2e/modules_display_spec.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const helpers = require("./global-setup");
|
||||||
|
|
||||||
|
const describe = global.describe;
|
||||||
|
const it = global.it;
|
||||||
|
|
||||||
|
describe("Display of modules", function () {
|
||||||
|
helpers.setupTimeout(this);
|
||||||
|
|
||||||
|
var app = null;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
return helpers
|
||||||
|
.startApplication({
|
||||||
|
args: ["js/electron.js"]
|
||||||
|
})
|
||||||
|
.then(function (startedApp) {
|
||||||
|
app = startedApp;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
return helpers.stopApplication(app);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Using helloworld", function () {
|
||||||
|
before(function () {
|
||||||
|
// Set config sample for use in test
|
||||||
|
process.env.MM_CONFIG_FILE = "tests/configs/modules/display.js";
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show the test header", async () => {
|
||||||
|
await app.client.waitForExist("#module_0_helloworld", 10000);
|
||||||
|
return app.client.element("#module_0_helloworld .module-header").isVisible().should.eventually.equal(true).getText("#module_0_helloworld .module-header").should.eventually.equal("TEST_HEADER");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should show no header if no header text is specified", async () => {
|
||||||
|
await app.client.waitForExist("#module_1_helloworld", 10000);
|
||||||
|
return app.client.element("#module_1_helloworld .module-header").isVisible().should.eventually.equal(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -28,6 +28,5 @@
|
|||||||
"UPDATE_NOTIFICATION_MODULE": "Update beschikbaar voor {MODULE_NAME} module.",
|
"UPDATE_NOTIFICATION_MODULE": "Update beschikbaar voor {MODULE_NAME} module.",
|
||||||
"UPDATE_INFO_SINGLE": "De huidige installatie loopt {COMMIT_COUNT} commit achter op de {BRANCH_NAME} branch.",
|
"UPDATE_INFO_SINGLE": "De huidige installatie loopt {COMMIT_COUNT} commit achter op de {BRANCH_NAME} branch.",
|
||||||
"UPDATE_INFO_MULTIPLE": "De huidige installatie loopt {COMMIT_COUNT} commits achter op de {BRANCH_NAME} branch.",
|
"UPDATE_INFO_MULTIPLE": "De huidige installatie loopt {COMMIT_COUNT} commits achter op de {BRANCH_NAME} branch.",
|
||||||
|
"FEELS": "Voelt als"
|
||||||
"FEELS": "Gevoelstemperatuur"
|
|
||||||
}
|
}
|
||||||
|
32
vendor/package-lock.json
generated
vendored
32
vendor/package-lock.json
generated
vendored
@ -4,9 +4,9 @@
|
|||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": {
|
"@fortawesome/fontawesome-free": {
|
||||||
"version": "5.13.1",
|
"version": "5.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.14.0.tgz",
|
||||||
"integrity": "sha512-D819f34FLHeBN/4xvw0HR0u7U2G7RqjPSggXqf7LktsxWQ48VAfGwvMrhcVuaZV2fF069c/619RdgCCms0DHhw=="
|
"integrity": "sha512-OfdMsF+ZQgdKHP9jUbmDcRrP0eX90XXrsXIdyjLbkmSBzmMXPABB8eobUJtivaupucYaByz6WNe1PI1JuYm3qA=="
|
||||||
},
|
},
|
||||||
"a-sync-waterfall": {
|
"a-sync-waterfall": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@ -44,9 +44,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chokidar": {
|
"chokidar": {
|
||||||
"version": "3.4.0",
|
"version": "3.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
|
||||||
"integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==",
|
"integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"anymatch": "~3.1.1",
|
"anymatch": "~3.1.1",
|
||||||
@ -60,9 +60,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "3.0.2",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
|
||||||
"integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow=="
|
"integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="
|
||||||
},
|
},
|
||||||
"fill-range": {
|
"fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
@ -119,9 +119,9 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"moment": {
|
"moment": {
|
||||||
"version": "2.27.0",
|
"version": "2.28.0",
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.28.0.tgz",
|
||||||
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
|
"integrity": "sha512-Z5KOjYmnHyd/ukynmFd/WwyXHd7L4J9vTI/nn5Ap9AVUgaAE15VvQ9MOGmJJygEUklupqIrFnor/tjTwRU+tQw=="
|
||||||
},
|
},
|
||||||
"moment-timezone": {
|
"moment-timezone": {
|
||||||
"version": "0.5.31",
|
"version": "0.5.31",
|
||||||
@ -138,14 +138,14 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"nunjucks": {
|
"nunjucks": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.2.tgz",
|
||||||
"integrity": "sha512-LYlVuC1ZNSalQQkLNNPvcgPt2M9FTY9bs39mTCuFXtqh7jWbYzhDlmz2M6onPiXEhdZo+b9anRhc+uBGuJZ2bQ==",
|
"integrity": "sha512-KUi85OoF2NMygwODAy28Lh9qHmq5hO3rBlbkYoC8v377h4l8Pt5qFjILl0LWpMbOrZ18CzfVVUvIHUIrtED3sA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"a-sync-waterfall": "^1.0.0",
|
"a-sync-waterfall": "^1.0.0",
|
||||||
"asap": "^2.0.3",
|
"asap": "^2.0.3",
|
||||||
"chokidar": "^3.3.0",
|
"chokidar": "^3.3.0",
|
||||||
"commander": "^3.0.2"
|
"commander": "^5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"picomatch": {
|
"picomatch": {
|
||||||
|
6
vendor/package.json
vendored
6
vendor/package.json
vendored
@ -10,10 +10,10 @@
|
|||||||
"url": "https://github.com/MichMich/MagicMirror/issues"
|
"url": "https://github.com/MichMich/MagicMirror/issues"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^5.13.1",
|
"@fortawesome/fontawesome-free": "^5.14.0",
|
||||||
"moment": "^2.27.0",
|
"moment": "^2.28.0",
|
||||||
"moment-timezone": "^0.5.31",
|
"moment-timezone": "^0.5.31",
|
||||||
"nunjucks": "^3.2.1",
|
"nunjucks": "^3.2.2",
|
||||||
"suncalc": "^1.8.0",
|
"suncalc": "^1.8.0",
|
||||||
"weathericons": "^2.1.0"
|
"weathericons": "^2.1.0"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user