Merge branch 'develop' into develop

This commit is contained in:
sam detweiler 2020-09-22 07:49:19 -05:00 committed by GitHub
commit 934ac886cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1929 additions and 1386 deletions

View File

@ -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²
### 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.)
_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.
- Added support in weatherforecast for OpenWeather onecall API.
- 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
### Updated
@ -29,17 +26,21 @@ _This release is scheduled to be released on 2020-10-01._
- Change incorrect weather.js default properties.
- Cleaned up newsfeed module.
- Cleaned up jsdoc comments.
- Cleaned up clock tests.
- Move lodash into devDependencies, update other dependencies
### Deleted
### 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 calendar display. Account for current timezone. [#2068](https://github.com/MichMich/MagicMirror/issues/2068)
- Fix logLevel being set before loading config.
- 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
@ -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
- Turkish translation.
- Option to configure the size of the currentweather module.
- Changed "Gevoelstemperatuur" to "Voelt als" shorter text.
## [2.10.1] - 2020-01-10

View File

@ -98,6 +98,7 @@ var Loader = (function () {
file: moduleName + ".js",
position: moduleData.position,
header: moduleData.header,
configDeepMerge: typeof moduleData.configDeepMerge === "boolean" ? moduleData.configDeepMerge : false,
config: moduleData.config,
classes: typeof moduleData.classes !== "undefined" ? moduleData.classes + " " + module : module
});

View File

@ -42,6 +42,8 @@ var MM = (function () {
if (typeof module.getHeader() === "undefined" || module.getHeader() !== "") {
moduleHeader.style.display = "none;";
} else {
moduleHeader.style.display = "block;";
}
var moduleContent = document.createElement("div");
@ -218,7 +220,7 @@ var MM = (function () {
headerWrapper[0].innerHTML = newHeader;
if (headerWrapper.length > 0 && newHeader) {
delete headerWrapper[0].style;
headerWrapper[0].style.display = "block";
} else {
headerWrapper[0].style.display = "none";
}

View File

@ -221,16 +221,17 @@ var Module = Class.extend({
this.identifier = data.identifier;
this.hidden = false;
this.setConfig(data.config);
this.setConfig(data.config, data.configDeepMerge);
},
/**
* Set the module config and combine it with the module defaults.
*
* @param {object} config The combined module config.
* @param {boolean} config Merge module config in deep.
*/
setConfig: function (config) {
this.config = Object.assign({}, this.defaults, config);
setConfig: function (config, deep) {
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.create = function (name) {

View File

@ -37,6 +37,8 @@ Module.register("currentweather", {
weatherEndpoint: "weather",
appendLocationNameToHeader: true,
useLocationAsHeader: false,
calendarClass: "calendar",
tableClass: "large",
@ -267,15 +269,16 @@ Module.register("currentweather", {
// Override getHeader method.
getHeader: function () {
if (this.config.appendLocationNameToHeader && this.data.header !== undefined) {
return this.data.header + " " + this.fetchedLocationName;
}
if (this.config.useLocationAsHeader && this.config.location !== false) {
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.

View File

@ -1,5 +1,5 @@
# 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).

View File

@ -1,4 +1,7 @@
{% if current %}
{% if current or weatherData %}
{% if weatherData %}
{% set current = weatherData.current %}
{% endif %}
{% if not config.onlyTemp %}
<div class="normal medium">
<span class="wi wi-strong-wind dimmed"></span>

View File

@ -1,5 +1,10 @@
{% if forecast %}
{% set numSteps = forecast | calcNumSteps %}
{% if forecast or weatherData %}
{% if weatherData %}
{% set forecast = weatherData.days %}
{% set numSteps = forecast | calcNumEntries %}
{% else %}
{% set numSteps = forecast | calcNumSteps %}
{% endif %}
{% set currentStep = 0 %}
<table class="{{ config.tableClass }}">
{% set forecast = forecast.slice(0, numSteps) %}

View 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> -->

View File

@ -35,7 +35,7 @@ WeatherProvider.register("openweathermap", {
.finally(() => this.updateAvailable());
},
// Overwrite the fetchCurrentWeather method.
// Overwrite the fetchWeatherForecast method.
fetchWeatherForecast() {
this.fetchData(this.getUrl())
.then((data) => {
@ -56,6 +56,27 @@ WeatherProvider.register("openweathermap", {
.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 */
/*
* Gets the complete url for the request
@ -95,6 +116,18 @@ WeatherProvider.register("openweathermap", {
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).
*/
@ -221,6 +254,129 @@ WeatherProvider.register("openweathermap", {
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.
*/
@ -256,7 +412,19 @@ WeatherProvider.register("openweathermap", {
*/
getParams() {
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;
} else if (this.config.location) {
params += "q=" + this.config.location;

View File

@ -11,8 +11,10 @@ Module.register("weather", {
defaults: {
weatherProvider: "openweathermap",
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,
locationID: false,
units: config.units,
@ -36,6 +38,7 @@ Module.register("weather", {
showIndoorTemperature: false,
showIndoorHumidity: false,
maxNumberOfDays: 5,
maxEntries: 5,
fade: true,
fadePoint: 0.25, // Start on 1/4th of the list.
@ -73,11 +76,12 @@ Module.register("weather", {
// Override getHeader method.
getHeader: function () {
if (this.config.appendLocationNameToHeader && this.data.header !== undefined && this.weatherProvider) {
return this.data.header + " " + this.weatherProvider.fetchedLocation();
if (this.config.appendLocationNameToHeader && this.weatherProvider) {
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.
@ -124,7 +128,17 @@ Module.register("weather", {
// Select the template depending on the display type.
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.
@ -133,6 +147,7 @@ Module.register("weather", {
config: this.config,
current: this.weatherProvider.currentWeather(),
forecast: this.weatherProvider.weatherForecast(),
weatherData: this.weatherProvider.weatherData(),
indoor: {
humidity: this.indoorHumidity,
temperature: this.indoorTemperature
@ -154,7 +169,9 @@ Module.register("weather", {
}
setTimeout(() => {
if (this.config.type === "forecast") {
if (this.config.weatherEndpoint === "/onecall") {
this.weatherProvider.fetchWeatherData();
} else if (this.config.type === "forecast") {
this.weatherProvider.fetchWeatherForecast();
} else {
this.weatherProvider.fetchCurrentWeather();
@ -206,7 +223,7 @@ Module.register("weather", {
}
}
} 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 = "";
} else {
if (this.config.weatherProvider === "ukmetoffice" || this.config.weatherProvider === "ukmetofficedatahub") {
@ -244,6 +261,13 @@ Module.register("weather", {
}.bind(this)
);
this.nunjucksEnvironment().addFilter(
"calcNumEntries",
function (dataArray) {
return Math.min(dataArray.length, this.config.maxEntries);
}.bind(this)
);
this.nunjucksEnvironment().addFilter(
"opacity",
function (currentStep, numSteps) {

View File

@ -16,6 +16,7 @@ var WeatherProvider = Class.extend({
// Try to not access them directly.
currentWeatherObject: null,
weatherForecastArray: null,
weatherDataObject: null,
fetchedLocationName: null,
// 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.`);
},
// 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.
currentWeather: function () {
return this.currentWeatherObject;
@ -66,6 +73,11 @@ var WeatherProvider = Class.extend({
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.
fetchedLocation: function () {
return this.fetchedLocationName || "";
@ -83,6 +95,11 @@ var WeatherProvider = Class.extend({
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.
setFetchedLocation: function (name) {
this.fetchedLocationName = name;

View File

@ -103,7 +103,7 @@ Module.register("weatherforecast", {
getDom: function () {
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.className = "dimmed light small";
return wrapper;
@ -206,10 +206,11 @@ Module.register("weatherforecast", {
// Override getHeader method.
getHeader: function () {
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.

2724
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -42,25 +42,24 @@
},
"homepage": "https://magicmirror.builders",
"devDependencies": {
"@prantlf/jsonlint": "^10.2.0",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"current-week-number": "^1.0.7",
"danger": "^3.1.3",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-jsdoc": "^30.1.0",
"eslint-plugin-jsdoc": "^30.5.1",
"eslint-plugin-prettier": "^3.1.4",
"http-auth": "^3.2.3",
"husky": "^4.2.5",
"husky": "^4.3.0",
"jsdom": "^11.6.2",
"lodash": "^4.17.20",
"mocha": "^7.1.2",
"mocha-each": "^2.0.1",
"mocha-logger": "^1.0.6",
"nyc": "^15.1.0",
"prettier": "^2.0.5",
"pretty-quick": "^2.0.1",
"prettier": "^2.1.2",
"pretty-quick": "^3.0.2",
"spectron": "^8.0.0",
"stylelint": "^13.6.1",
"stylelint": "^13.7.1",
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^20.0.0",
"stylelint-prettier": "^1.1.2"
@ -69,24 +68,23 @@
"electron": "^6.1.7"
},
"dependencies": {
"colors": "^1.1.2",
"colors": "^1.4.0",
"console-stamp": "^0.2.9",
"eslint": "^7.5.0",
"express": "^4.16.2",
"express-ipfilter": "^1.0.1",
"feedme": "latest",
"eslint": "^7.9.0",
"express": "^4.17.1",
"express-ipfilter": "^1.1.2",
"feedme": "^1.2.0",
"helmet": "^3.23.3",
"iconv-lite": "latest",
"lodash": "^4.17.19",
"module-alias": "^2.2.2",
"moment": "latest",
"node-ical": "^0.12.0",
"request": "^2.88.2",
"rrule": "^2.6.4",
"rrule": "^2.6.6",
"rrule-alt": "^2.2.8",
"simple-git": "^1.85.0",
"socket.io": "^2.1.1",
"valid-url": "latest"
"socket.io": "^2.3.0",
"valid-url": "^1.0.9"
},
"_moduleAliases": {
"node_helper": "js/node_helper.js"

View 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;
}

View 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;
}

View File

@ -20,7 +20,7 @@ var config = {
},
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 () {
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();

View File

@ -1,4 +1,6 @@
const helpers = require("../global-setup");
const expect = require("chai").expect;
const moment = require("moment");
const describe = global.describe;
const it = global.it;
@ -30,12 +32,12 @@ describe("Clock module", function () {
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}$/;
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$/;
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";
});
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}$/;
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$/;
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";
});
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$/;
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";
});
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$/;
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";
});
it("shows week with correct format", function () {
it("should show the week in the correct format", function () {
const weekRegex = /^Week [0-9]{1,2}$/;
return app.client.waitUntilWindowLoaded().getText(".clock .week").should.eventually.match(weekRegex);
});
it("shows week with correct number of week of year", function () {
it("FIXME: if the day is a sunday this not match");
// const currentWeekNumber = require("current-week-number")();
// const weekToShow = "Week " + currentWeekNumber;
// return app.client.waitUntilWindowLoaded()
// .getText(".clock .week").should.eventually.equal(weekToShow);
it("should show the week with the correct number of week of year", function () {
const currentWeekNumber = moment().week();
const weekToShow = "Week " + currentWeekNumber;
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);
});
});
});

View 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);
});
});
});

View File

@ -28,6 +28,5 @@
"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_MULTIPLE": "De huidige installatie loopt {COMMIT_COUNT} commits achter op de {BRANCH_NAME} branch.",
"FEELS": "Gevoelstemperatuur"
"FEELS": "Voelt als"
}

32
vendor/package-lock.json generated vendored
View File

@ -4,9 +4,9 @@
"lockfileVersion": 1,
"dependencies": {
"@fortawesome/fontawesome-free": {
"version": "5.13.1",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.13.1.tgz",
"integrity": "sha512-D819f34FLHeBN/4xvw0HR0u7U2G7RqjPSggXqf7LktsxWQ48VAfGwvMrhcVuaZV2fF069c/619RdgCCms0DHhw=="
"version": "5.14.0",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.14.0.tgz",
"integrity": "sha512-OfdMsF+ZQgdKHP9jUbmDcRrP0eX90XXrsXIdyjLbkmSBzmMXPABB8eobUJtivaupucYaByz6WNe1PI1JuYm3qA=="
},
"a-sync-waterfall": {
"version": "1.0.1",
@ -44,9 +44,9 @@
}
},
"chokidar": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz",
"integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==",
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
"integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
"optional": true,
"requires": {
"anymatch": "~3.1.1",
@ -60,9 +60,9 @@
}
},
"commander": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz",
"integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow=="
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
"integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="
},
"fill-range": {
"version": "7.0.1",
@ -119,9 +119,9 @@
"optional": true
},
"moment": {
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
"version": "2.28.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.28.0.tgz",
"integrity": "sha512-Z5KOjYmnHyd/ukynmFd/WwyXHd7L4J9vTI/nn5Ap9AVUgaAE15VvQ9MOGmJJygEUklupqIrFnor/tjTwRU+tQw=="
},
"moment-timezone": {
"version": "0.5.31",
@ -138,14 +138,14 @@
"optional": true
},
"nunjucks": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.1.tgz",
"integrity": "sha512-LYlVuC1ZNSalQQkLNNPvcgPt2M9FTY9bs39mTCuFXtqh7jWbYzhDlmz2M6onPiXEhdZo+b9anRhc+uBGuJZ2bQ==",
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.2.tgz",
"integrity": "sha512-KUi85OoF2NMygwODAy28Lh9qHmq5hO3rBlbkYoC8v377h4l8Pt5qFjILl0LWpMbOrZ18CzfVVUvIHUIrtED3sA==",
"requires": {
"a-sync-waterfall": "^1.0.0",
"asap": "^2.0.3",
"chokidar": "^3.3.0",
"commander": "^3.0.2"
"commander": "^5.1.0"
}
},
"picomatch": {

6
vendor/package.json vendored
View File

@ -10,10 +10,10 @@
"url": "https://github.com/MichMich/MagicMirror/issues"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^5.13.1",
"moment": "^2.27.0",
"@fortawesome/fontawesome-free": "^5.14.0",
"moment": "^2.28.0",
"moment-timezone": "^0.5.31",
"nunjucks": "^3.2.1",
"nunjucks": "^3.2.2",
"suncalc": "^1.8.0",
"weathericons": "^2.1.0"
}