Merge branch 'develop' of https://github.com/ashishtank/MagicMirror into develop

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
Ashish Tank 2020-11-11 16:03:10 +01:00
commit ef325896c5
16 changed files with 266 additions and 106 deletions

View File

@ -11,11 +11,12 @@ _This release is scheduled to be released on 2021-01-01._
### Added ### Added
- Add Hindi language. - Added new log level "debug" to the logger.
- Add Gujarati language.
### Updated ### Updated
- Weather module - forecast now show TODAY and TOMORROW instead of weekday, to make it easier to understand
### Deleted ### Deleted
### Fixed ### Fixed
@ -23,6 +24,10 @@ _This release is scheduled to be released on 2021-01-01._
- JSON Parse translation files with comments crashing UI. (#2149) - JSON Parse translation files with comments crashing UI. (#2149)
- Calendar parsing where RRULE bug returns wrong date, add Windows timezone name support. (#2145, #2151) - Calendar parsing where RRULE bug returns wrong date, add Windows timezone name support. (#2145, #2151)
- Wrong node-ical version installed (package.json) requested version. (#2153) - Wrong node-ical version installed (package.json) requested version. (#2153)
- Fix calendar fetcher subsequent timing (#2160)
- Rename Greek translation to correct ISO 639-1 alpha-2 code (gr > el). (#2155)
- Add a space after icons of sunrise and sunset (#2169)
- Fix calendar when no DTEND record found in event, startDate overlay when endDate set (#2177)
## [2.13.0] - 2020-10-01 ## [2.13.0] - 2020-10-01
@ -227,7 +232,7 @@ Special thanks to @sdetweil for all his great contributions!
- Option to show event location in calendar - Option to show event location in calendar
- Finnish translation for "Feels" and "Weeks" - Finnish translation for "Feels" and "Weeks"
- Russian translation for “Feels” - Russian translation for "Feels"
- Calendar module: added `nextDaysRelative` config option - Calendar module: added `nextDaysRelative` config option
- Add `broadcastPastEvents` config option for calendars to include events from the past `maximumNumberOfDays` in event broadcasts - Add `broadcastPastEvents` config option for calendars to include events from the past `maximumNumberOfDays` in event broadcasts
- Added feature to broadcast news feed items `NEWS_FEED` and updated news items `NEWS_FEED_UPDATED` in default [newsfeed](https://github.com/MichMich/MagicMirror/tree/develop/modules/default/newsfeed) module (when news is updated) with documented default and `config.js` options in [README.md](https://github.com/MichMich/MagicMirror/blob/develop/modules/default/newsfeed/README.md) - Added feature to broadcast news feed items `NEWS_FEED` and updated news items `NEWS_FEED_UPDATED` in default [newsfeed](https://github.com/MichMich/MagicMirror/tree/develop/modules/default/newsfeed) module (when news is updated) with documented default and `config.js` options in [README.md](https://github.com/MichMich/MagicMirror/blob/develop/modules/default/newsfeed/README.md)

View File

@ -28,7 +28,7 @@ var config = {
httpsCertificate: "", // HTTPS Certificate path, only require when useHttps is true httpsCertificate: "", // HTTPS Certificate path, only require when useHttps is true
language: "en", language: "en",
logLevel: ["INFO", "LOG", "WARN", "ERROR"], logLevel: ["DEBUG", "INFO", "LOG", "WARN", "ERROR"],
timeFormat: 24, timeFormat: 24,
units: "metric", units: "metric",
// serverOnly: true/false/"local" , // serverOnly: true/false/"local" ,

View File

@ -20,10 +20,11 @@
} }
})(this, function (config) { })(this, function (config) {
const logLevel = { const logLevel = {
debug: Function.prototype.bind.call(console.debug, console),
info: Function.prototype.bind.call(console.info, console), info: Function.prototype.bind.call(console.info, console),
log: Function.prototype.bind.call(console.log, console), log: Function.prototype.bind.call(console.log, console),
error: Function.prototype.bind.call(console.error, console),
warn: Function.prototype.bind.call(console.warn, console), warn: Function.prototype.bind.call(console.warn, console),
error: Function.prototype.bind.call(console.error, console),
group: Function.prototype.bind.call(console.group, console), group: Function.prototype.bind.call(console.group, console),
groupCollapsed: Function.prototype.bind.call(console.groupCollapsed, console), groupCollapsed: Function.prototype.bind.call(console.groupCollapsed, console),
groupEnd: Function.prototype.bind.call(console.groupEnd, console), groupEnd: Function.prototype.bind.call(console.groupEnd, console),

View File

@ -58,6 +58,8 @@ Module.register("calendar", {
nextDaysRelative: false nextDaysRelative: false
}, },
requiresVersion: "2.1.0",
// Define required scripts. // Define required scripts.
getStyles: function () { getStyles: function () {
return ["calendar.css", "font-awesome.css"]; return ["calendar.css", "font-awesome.css"];
@ -83,6 +85,12 @@ Module.register("calendar", {
// Set locale. // Set locale.
moment.updateLocale(config.language, this.getLocaleSpecification(config.timeFormat)); moment.updateLocale(config.language, this.getLocaleSpecification(config.timeFormat));
// clear data holder before start
this.calendarData = {};
// indicate no data available yet
this.loaded = false;
for (var c in this.config.calendars) { for (var c in this.config.calendars) {
var calendar = this.config.calendars[c]; var calendar = this.config.calendars[c];
calendar.url = calendar.url.replace("webcal://", "http://"); calendar.url = calendar.url.replace("webcal://", "http://");
@ -112,18 +120,10 @@ Module.register("calendar", {
}; };
} }
// tell helper to start a fetcher for this calendar
// fetcher till cycle
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.loaded = false;
}, },
// Override socket notification handler. // Override socket notification handler.
@ -541,6 +541,8 @@ Module.register("calendar", {
* @param {object} calendarConfig The config of the specific calendar * @param {object} calendarConfig The config of the specific calendar
*/ */
addCalendar: function (url, auth, calendarConfig) { addCalendar: function (url, auth, calendarConfig) {
var self = this;
this.sendSocketNotification("ADD_CALENDAR", { this.sendSocketNotification("ADD_CALENDAR", {
id: this.identifier, id: this.identifier,
url: url, url: url,

View File

@ -32,6 +32,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
let reloadTimer = null; let reloadTimer = null;
let events = []; let events = [];
let debug = false;
let fetchFailedCallback = function () {}; let fetchFailedCallback = function () {};
let eventsReceivedCallback = function () {}; let eventsReceivedCallback = function () {};
@ -110,14 +111,15 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
if (event.type === "VEVENT") { if (event.type === "VEVENT") {
let startDate = eventDate(event, "start"); let startDate = eventDate(event, "start");
let endDate; let endDate;
// console.log("\nevent="+JSON.stringify(event)) // Log.log("\nevent="+JSON.stringify(event))
if (typeof event.end !== "undefined") { if (typeof event.end !== "undefined") {
endDate = eventDate(event, "end"); endDate = eventDate(event, "end");
} else if (typeof event.duration !== "undefined") { } else if (typeof event.duration !== "undefined") {
endDate = startDate.clone().add(moment.duration(event.duration)); endDate = startDate.clone().add(moment.duration(event.duration));
} else { } else {
if (!isFacebookBirthday) { if (!isFacebookBirthday) {
endDate = startDate; // make copy of start date, separate storage area
endDate = moment(startDate.format("x"), "x");
} else { } else {
endDate = moment(startDate).add(1, "days"); endDate = moment(startDate).add(1, "days");
} }
@ -214,7 +216,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
} }
const dates = rule.between(pastLocal, futureLocal, true, limitFunction); const dates = rule.between(pastLocal, futureLocal, true, limitFunction);
// console.log("title="+event.summary.val+" dates="+JSON.stringify(dates)) if (debug) Log.log("title=" + event.summary.val + " dates=" + JSON.stringify(dates));
// The "dates" array contains the set of dates within our desired date range range that are valid // The "dates" array contains the set of dates within our desired date range range that are valid
// for the recurrence rule. *However*, it's possible for us to have a specific recurrence that // for the recurrence rule. *However*, it's possible for us to have a specific recurrence that
// had its date changed from outside the range to inside the range. For the time being, // had its date changed from outside the range to inside the range. For the time being,
@ -234,7 +236,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
// Loop through the set of date entries to see which recurrences should be added to our event list. // Loop through the set of date entries to see which recurrences should be added to our event list.
for (let d in dates) { for (let d in dates) {
const date = dates[d]; let date = dates[d];
// ical.js started returning recurrences and exdates as ISOStrings without time information. // ical.js started returning recurrences and exdates as ISOStrings without time information.
// .toISOString().substring(0,10) is the method they use to calculate keys, so we'll do the same // .toISOString().substring(0,10) is the method they use to calculate keys, so we'll do the same
// (see https://github.com/peterbraden/ical.js/pull/84 ) // (see https://github.com/peterbraden/ical.js/pull/84 )
@ -242,54 +244,25 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
let curEvent = event; let curEvent = event;
let showRecurrence = true; let showRecurrence = true;
// for full day events, the time might be off from RRULE/Luxon problem
if (isFullDayEvent(event)) {
if (debug) Log.log("fullday");
// if the offset is negative, east of GMT where the problem is
if (date.getTimezoneOffset() < 0) {
// get the offset of today when we are processing
// this will be the correction we need to apply
let nowOffset = new Date().getTimezoneOffset();
if (debug) Log.log("now offset is " + nowOffset);
// reduce the time by the offset
if (debug) Log.log(" recurring date is " + date + " offset is " + date.getTimezoneOffset());
// apply the correction to the date/time to get it UTC relative
date = new Date(date.getTime() - Math.abs(nowOffset) * 60000);
if (debug) Log.log("new recurring date is " + date);
}
}
startDate = moment(date); startDate = moment(date);
// console.log("now timezone="+ moment.tz.guess());
// whether we need to adjust for RRULE returning the wrong date, with the right time (forward of URC timezones) let adjustDays = getCorrection(event, date);
let adjustDays = 0;
// if a timezone was specified
if (!event.start.tz) {
event.start.tz = moment.tz.guess();
}
// console.log("tz="+event.start.tz)
if (event.start.tz) {
// if this is a windows timezone
if (event.start.tz.indexOf(" ") > 0) {
// use the lookup table to get theIANA name as moment and date don't know MS timezones
let tz = getIanaTZFromMS(event.start.tz);
// watch out for unregistered windows timezone names
// if we had a successfule lookup
if (tz) {
// change the timezone to the IANA name
event.start.tz = getIanaTZFromMS(event.start.tz);
// console.log("corrected timezone="+event.start.tz)
}
}
// get the start time in that timezone
let mms = moment.tz(moment(event.start), event.start.tz).utcOffset();
// console.log("ms offset="+mms)
// get the specified date in that timezone
let mm = moment.tz(moment(date), event.start.tz);
let mmo = mm.utcOffset();
// console.log("mm ofset="+ mmo+" hour="+mm.format("H")+" event date="+mm.toDate())
// if the offset is greater than 0, east of london
if (mmo > 0) {
let h = parseInt(mm.format("H"));
// check if the event time is less than the offset
if (h > 0 && h < mmo / 60) {
// if so, rrule created a wrong date (utc day, oops, with utc yesterday adjusted time)
// we need to fix that
adjustDays = 24;
// console.log("adjusting date")
}
if (mmo > mms) {
adjustDays += 1;
// console.log("adjust up 1 hour dst change")
} else if (mmo < mms) {
adjustDays -= 1;
//console.log("adjust down 1 hour dst change")
}
}
}
// For each date that we're checking, it's possible that there is a recurrence override for that one day. // For each date that we're checking, it's possible that there is a recurrence override for that one day.
if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) { if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) {
@ -304,7 +277,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
showRecurrence = false; showRecurrence = false;
} }
//console.log("duration="+duration) //Log.log("duration="+duration)
endDate = moment(parseInt(startDate.format("x")) + duration, "x"); endDate = moment(parseInt(startDate.format("x")) + duration, "x");
if (startDate.format("x") === endDate.format("x")) { if (startDate.format("x") === endDate.format("x")) {
@ -327,8 +300,8 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
addedEvents++; addedEvents++;
newEvents.push({ newEvents.push({
title: recurrenceTitle, title: recurrenceTitle,
startDate: (adjustDays ? startDate.subtract(adjustDays, "hours") : startDate).format("x"), startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"),
endDate: (adjustDays ? endDate.subtract(adjustDays, "hours") : endDate).format("x"), endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"),
fullDayEvent: isFullDayEvent(event), fullDayEvent: isFullDayEvent(event),
recurringEvent: true, recurringEvent: true,
class: event.class, class: event.class,
@ -343,7 +316,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
} else { } else {
// Single event. // Single event.
const fullDayEvent = isFacebookBirthday ? true : isFullDayEvent(event); const fullDayEvent = isFacebookBirthday ? true : isFullDayEvent(event);
// console.log("full day event") // Log.log("full day event")
if (includePastEvents) { if (includePastEvents) {
// Past event is too far in the past, so skip. // Past event is too far in the past, so skip.
if (endDate < past) { if (endDate < past) {
@ -376,15 +349,16 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
} }
// if the start and end are the same, then make end the 'end of day' value (start is at 00:00:00) // if the start and end are the same, then make end the 'end of day' value (start is at 00:00:00)
if (fullDayEvent && startDate.format("x") === endDate.format("x")) { if (fullDayEvent && startDate.format("x") === endDate.format("x")) {
//console.log("end same as start") //Log.log("end same as start")
endDate = endDate.endOf("day"); endDate = endDate.endOf("day");
} }
// get correction for date saving and dst change between now and then
let adjustDays = getCorrection(event, startDate.toDate());
// Every thing is good. Add it to the list. // Every thing is good. Add it to the list.
newEvents.push({ newEvents.push({
title: title, title: title,
startDate: startDate.format("x"), startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"),
endDate: endDate.format("x"), endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"),
fullDayEvent: fullDayEvent, fullDayEvent: fullDayEvent,
class: event.class, class: event.class,
location: location, location: location,
@ -406,6 +380,85 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
}); });
}; };
/*
*
* get the time correction, either dst/std or full day in cases where utc time is day before plus offset
*
*/
const getCorrection = function (event, date) {
let adjustHours = 0;
// if a timezone was specified
if (!event.start.tz) {
if (debug) Log.log(" if no tz, guess based on now");
event.start.tz = moment.tz.guess();
}
if (debug) Log.log("initial tz=" + event.start.tz);
// if there is a start date specified
if (event.start.tz) {
// if this is a windows timezone
if (event.start.tz.includes(" ")) {
// use the lookup table to get theIANA name as moment and date don't know MS timezones
let tz = getIanaTZFromMS(event.start.tz);
// watch out for unregistered windows timezone names
// if we had a successfule lookup
if (tz) {
// change the timezone to the IANA name
event.start.tz = tz;
// if(debug) Log.log("corrected timezone="+event.start.tz)
}
}
if (debug) Log.log("corrected tz=" + event.start.tz);
let mmo = 0; // offset from TZ string or calculated
let mm = 0; // date with tz or offset
let mms = 0; // utc offset of created with tz
// if there is still an offset, lookup failed, use it
if (event.start.tz.startsWith("(")) {
const regex = /[+|-]\d*:\d*/;
mmo = event.start.tz.match(regex).toString();
mms = mmo;
if (debug) Log.log("ical offset=" + mmo + " date=" + date);
mm = moment(date);
mm = mm.utcOffset(mmo);
} else {
// get the start time in that timezone
if (debug) Log.log("ttttttt=" + moment(event.start).toDate());
mms = moment.tz(moment(event.start), event.start.tz).utcOffset();
if (debug) Log.log("ms offset=" + mms);
if (debug) Log.log("start date =" + moment.tz(moment(event.start), event.start.tz).toDate());
// get the specified date in that timezone
mm = moment.tz(moment(date), event.start.tz);
if (debug) Log.log("mm=" + mm.toDate());
mmo = mm.utcOffset();
}
if (debug) Log.log("mm ofset=" + mmo + " hour=" + mm.format("H") + " event date=" + mm.toDate());
// if the offset is greater than 0, east of london
if (mmo !== mms) {
// big offset
if (debug) Log.log("offset");
let h = parseInt(mm.format("H"));
// check if the event time is less than the offset
if (h > 0 && h < Math.abs(mmo) / 60) {
// if so, rrule created a wrong date (utc day, oops, with utc yesterday adjusted time)
// we need to fix that
adjustHours = 24;
// if(debug) Log.log("adjusting date")
}
if (Math.abs(mmo) > Math.abs(mms)) {
adjustHours += 1;
if (debug) Log.log("adjust up 1 hour dst change");
} else if (Math.abs(mmo) < Math.abs(mms)) {
adjustHours -= 1;
if (debug) Log.log("adjust down 1 hour dst change");
}
}
}
if (debug) Log.log("adjustHours=" + adjustHours);
return adjustHours;
};
/** /**
* *
* lookup iana tz from windows * lookup iana tz from windows
@ -439,7 +492,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn
* @returns {boolean} True if the event is a fullday event, false otherwise * @returns {boolean} True if the event is a fullday event, false otherwise
*/ */
const isFullDayEvent = function (event) { const isFullDayEvent = function (event) {
if (event.start.length === 8 || event.start.dateOnly) { if (event.start.length === 8 || event.start.dateOnly || event.datetype === "date") {
return true; return true;
} }

View File

@ -70,7 +70,6 @@ module.exports = NodeHelper.create({
} else { } else {
Log.log("Use existing calendar fetcher for url: " + url); Log.log("Use existing calendar fetcher for url: " + url);
fetcher = self.fetchers[identifier + url]; fetcher = self.fetchers[identifier + url];
fetcher.broadcastEvents();
} }
fetcher.startFetch(); fetcher.startFetch();

View File

@ -135,5 +135,103 @@
"UTC+13": { "iana": ["Etc/GMT-13"] }, "UTC+13": { "iana": ["Etc/GMT-13"] },
"Tonga Standard Time": { "iana": ["Pacific/Tongatapu"] }, "Tonga Standard Time": { "iana": ["Pacific/Tongatapu"] },
"Samoa Standard Time": { "iana": ["Pacific/Apia"] }, "Samoa Standard Time": { "iana": ["Pacific/Apia"] },
"Line Islands Standard Time": { "iana": ["Pacific/Kiritimati"] } "Line Islands Standard Time": { "iana": ["Pacific/Kiritimati"] },
"(UTC-12:00) International Date Line West": { "iana": ["Etc/GMT+12"] },
"(UTC-11:00) Midway Island, Samoa": { "iana": ["Pacific/Apia"] },
"(UTC-10:00) Hawaii": { "iana": ["Pacific/Honolulu"] },
"(UTC-09:00) Alaska": { "iana": ["America/Anchorage"] },
"(UTC-08:00) Pacific Time (US & Canada); Tijuana": { "iana": ["America/Los_Angeles"] },
"(UTC-08:00) Pacific Time (US and Canada); Tijuana": { "iana": ["America/Los_Angeles"] },
"(UTC-07:00) Mountain Time (US & Canada)": { "iana": ["America/Denver"] },
"(UTC-07:00) Mountain Time (US and Canada)": { "iana": ["America/Denver"] },
"(UTC-07:00) Chihuahua, La Paz, Mazatlan": { "iana": [null] },
"(UTC-07:00) Arizona": { "iana": ["America/Phoenix"] },
"(UTC-06:00) Central Time (US & Canada)": { "iana": ["America/Chicago"] },
"(UTC-06:00) Central Time (US and Canada)": { "iana": ["America/Chicago"] },
"(UTC-06:00) Saskatchewan": { "iana": ["America/Regina"] },
"(UTC-06:00) Guadalajara, Mexico City, Monterrey": { "iana": [null] },
"(UTC-06:00) Central America": { "iana": ["America/Guatemala"] },
"(UTC-05:00) Eastern Time (US & Canada)": { "iana": ["America/New_York"] },
"(UTC-05:00) Eastern Time (US and Canada)": { "iana": ["America/New_York"] },
"(UTC-05:00) Indiana (East)": { "iana": ["America/Indianapolis"] },
"(UTC-05:00) Bogota, Lima, Quito": { "iana": ["America/Bogota"] },
"(UTC-04:00) Atlantic Time (Canada)": { "iana": ["America/Halifax"] },
"(UTC-04:00) Georgetown, La Paz, San Juan": { "iana": ["America/La_Paz"] },
"(UTC-04:00) Santiago": { "iana": ["America/Santiago"] },
"(UTC-03:30) Newfoundland": { "iana": [null] },
"(UTC-03:00) Brasilia": { "iana": ["America/Sao_Paulo"] },
"(UTC-03:00) Georgetown": { "iana": ["America/Cayenne"] },
"(UTC-03:00) Greenland": { "iana": ["America/Godthab"] },
"(UTC-02:00) Mid-Atlantic": { "iana": [null] },
"(UTC-01:00) Azores": { "iana": ["Atlantic/Azores"] },
"(UTC-01:00) Cape Verde Islands": { "iana": ["Atlantic/Cape_Verde"] },
"(UTC) Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London": { "iana": [null] },
"(UTC) Monrovia, Reykjavik": { "iana": ["Atlantic/Reykjavik"] },
"(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague": { "iana": ["Europe/Budapest"] },
"(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb": { "iana": ["Europe/Warsaw"] },
"(UTC+01:00) Brussels, Copenhagen, Madrid, Paris": { "iana": ["Europe/Paris"] },
"(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna": { "iana": ["Europe/Berlin"] },
"(UTC+01:00) West Central Africa": { "iana": ["Africa/Lagos"] },
"(UTC+02:00) Minsk": { "iana": ["Europe/Chisinau"] },
"(UTC+02:00) Cairo": { "iana": ["Africa/Cairo"] },
"(UTC+02:00) Helsinki, Kiev, Riga, Sofia, Tallinn, Vilnius": { "iana": ["Europe/Kiev"] },
"(UTC+02:00) Athens, Bucharest, Istanbul": { "iana": ["Europe/Bucharest"] },
"(UTC+02:00) Jerusalem": { "iana": ["Asia/Jerusalem"] },
"(UTC+02:00) Harare, Pretoria": { "iana": ["Africa/Johannesburg"] },
"(UTC+03:00) Moscow, St. Petersburg, Volgograd": { "iana": ["Europe/Moscow"] },
"(UTC+03:00) Kuwait, Riyadh": { "iana": ["Asia/Riyadh"] },
"(UTC+03:00) Nairobi": { "iana": ["Africa/Nairobi"] },
"(UTC+03:00) Baghdad": { "iana": ["Asia/Baghdad"] },
"(UTC+03:30) Tehran": { "iana": ["Asia/Tehran"] },
"(UTC+04:00) Abu Dhabi, Muscat": { "iana": ["Asia/Dubai"] },
"(UTC+04:00) Baku, Tbilisi, Yerevan": { "iana": ["Asia/Yerevan"] },
"(UTC+04:30) Kabul": { "iana": [null] },
"(UTC+05:00) Ekaterinburg": { "iana": ["Asia/Yekaterinburg"] },
"(UTC+05:00) Tashkent": { "iana": ["Asia/Tashkent"] },
"(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi": { "iana": ["Asia/Calcutta"] },
"(UTC+05:45) Kathmandu": { "iana": ["Asia/Katmandu"] },
"(UTC+06:00) Astana, Dhaka": { "iana": ["Asia/Almaty"] },
"(UTC+06:00) Sri Jayawardenepura": { "iana": ["Asia/Colombo"] },
"(UTC+06:00) Almaty, Novosibirsk": { "iana": ["Asia/Novosibirsk"] },
"(UTC+06:30) Yangon (Rangoon)": { "iana": ["Asia/Rangoon"] },
"(UTC+07:00) Bangkok, Hanoi, Jakarta": { "iana": ["Asia/Bangkok"] },
"(UTC+07:00) Krasnoyarsk": { "iana": ["Asia/Krasnoyarsk"] },
"(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi": { "iana": ["Asia/Shanghai"] },
"(UTC+08:00) Kuala Lumpur, Singapore": { "iana": ["Asia/Singapore"] },
"(UTC+08:00) Taipei": { "iana": ["Asia/Taipei"] },
"(UTC+08:00) Perth": { "iana": ["Australia/Perth"] },
"(UTC+08:00) Irkutsk, Ulaanbaatar": { "iana": ["Asia/Irkutsk"] },
"(UTC+09:00) Seoul": { "iana": ["Asia/Seoul"] },
"(UTC+09:00) Osaka, Sapporo, Tokyo": { "iana": ["Asia/Tokyo"] },
"(UTC+09:00) Yakutsk": { "iana": ["Asia/Yakutsk"] },
"(UTC+09:30) Darwin": { "iana": ["Australia/Darwin"] },
"(UTC+09:30) Adelaide": { "iana": ["Australia/Adelaide"] },
"(UTC+10:00) Canberra, Melbourne, Sydney": { "iana": ["Australia/Sydney"] },
"(GMT+10:00) Canberra, Melbourne, Sydney": { "iana": ["Australia/Sydney"] },
"(UTC+10:00) Brisbane": { "iana": ["Australia/Brisbane"] },
"(UTC+10:00) Hobart": { "iana": ["Australia/Hobart"] },
"(UTC+10:00) Vladivostok": { "iana": ["Asia/Vladivostok"] },
"(UTC+10:00) Guam, Port Moresby": { "iana": ["Pacific/Port_Moresby"] },
"(UTC+11:00) Magadan, Solomon Islands, New Caledonia": { "iana": ["Pacific/Guadalcanal"] },
"(UTC+12:00) Fiji, Kamchatka, Marshall Is.": { "iana": [null] },
"(UTC+12:00) Auckland, Wellington": { "iana": ["Pacific/Auckland"] },
"(UTC+13:00) Nuku'alofa": { "iana": ["Pacific/Tongatapu"] },
"(UTC-03:00) Buenos Aires": { "iana": ["America/Buenos_Aires"] },
"(UTC+02:00) Beirut": { "iana": ["Asia/Beirut"] },
"(UTC+02:00) Amman": { "iana": ["Asia/Amman"] },
"(UTC-06:00) Guadalajara, Mexico City, Monterrey - New": { "iana": ["America/Mexico_City"] },
"(UTC-07:00) Chihuahua, La Paz, Mazatlan - New": { "iana": ["America/Chihuahua"] },
"(UTC-08:00) Tijuana, Baja California": { "iana": ["America/Tijuana"] },
"(UTC+02:00) Windhoek": { "iana": ["Africa/Windhoek"] },
"(UTC+03:00) Tbilisi": { "iana": ["Asia/Tbilisi"] },
"(UTC-04:00) Manaus": { "iana": ["America/Cuiaba"] },
"(UTC-03:00) Montevideo": { "iana": ["America/Montevideo"] },
"(UTC+04:00) Yerevan": { "iana": [null] },
"(UTC-04:30) Caracas": { "iana": ["America/Caracas"] },
"(UTC) Casablanca": { "iana": ["Africa/Casablanca"] },
"(UTC+05:00) Islamabad, Karachi": { "iana": ["Asia/Karachi"] },
"(UTC+04:00) Port Louis": { "iana": ["Indian/Mauritius"] },
"(UTC) Coordinated Universal Time": { "iana": ["Etc/GMT"] },
"(UTC-04:00) Asuncion": { "iana": ["America/Asuncion"] },
"(UTC+12:00) Petropavlovsk-Kamchatsky": { "iana": [null] }
} }

View File

@ -187,10 +187,10 @@ Module.register("clock", {
'"><i class="fa fa-sun-o" aria-hidden="true"></i> ' + '"><i class="fa fa-sun-o" aria-hidden="true"></i> ' +
untilNextEventString + untilNextEventString +
"</span>" + "</span>" +
'<span><i class="fa fa-arrow-up" aria-hidden="true"></i>' + '<span><i class="fa fa-arrow-up" aria-hidden="true"></i> ' +
formatTime(this.config, sunTimes.sunrise) + formatTime(this.config, sunTimes.sunrise) +
"</span>" + "</span>" +
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i>' + '<span><i class="fa fa-arrow-down" aria-hidden="true"></i> ' +
formatTime(this.config, sunTimes.sunset) + formatTime(this.config, sunTimes.sunset) +
"</span>"; "</span>";
} }

View File

@ -350,7 +350,7 @@ Module.register("newsfeed", {
this.activeItem = 0; this.activeItem = 0;
} }
this.resetDescrOrFullArticleAndTimer(); this.resetDescrOrFullArticleAndTimer();
Log.info(this.name + " - going from article #" + before + " to #" + this.activeItem + " (of " + this.newsItems.length + ")"); Log.debug(this.name + " - going from article #" + before + " to #" + this.activeItem + " (of " + this.newsItems.length + ")");
this.updateDom(100); this.updateDom(100);
} else if (notification === "ARTICLE_PREVIOUS") { } else if (notification === "ARTICLE_PREVIOUS") {
this.activeItem--; this.activeItem--;
@ -358,7 +358,7 @@ Module.register("newsfeed", {
this.activeItem = this.newsItems.length - 1; this.activeItem = this.newsItems.length - 1;
} }
this.resetDescrOrFullArticleAndTimer(); this.resetDescrOrFullArticleAndTimer();
Log.info(this.name + " - going from article #" + before + " to #" + this.activeItem + " (of " + this.newsItems.length + ")"); Log.debug(this.name + " - going from article #" + before + " to #" + this.activeItem + " (of " + this.newsItems.length + ")");
this.updateDom(100); this.updateDom(100);
} }
// if "more details" is received the first time: show article summary, on second time show full article // if "more details" is received the first time: show article summary, on second time show full article
@ -367,8 +367,8 @@ Module.register("newsfeed", {
if (this.config.showFullArticle === true) { if (this.config.showFullArticle === true) {
this.scrollPosition += this.config.scrollLength; this.scrollPosition += this.config.scrollLength;
window.scrollTo(0, this.scrollPosition); window.scrollTo(0, this.scrollPosition);
Log.info(this.name + " - scrolling down"); Log.debug(this.name + " - scrolling down");
Log.info(this.name + " - ARTICLE_MORE_DETAILS, scroll position: " + this.config.scrollLength); Log.debug(this.name + " - ARTICLE_MORE_DETAILS, scroll position: " + this.config.scrollLength);
} else { } else {
this.showFullArticle(); this.showFullArticle();
} }
@ -376,12 +376,12 @@ Module.register("newsfeed", {
if (this.config.showFullArticle === true) { if (this.config.showFullArticle === true) {
this.scrollPosition -= this.config.scrollLength; this.scrollPosition -= this.config.scrollLength;
window.scrollTo(0, this.scrollPosition); window.scrollTo(0, this.scrollPosition);
Log.info(this.name + " - scrolling up"); Log.debug(this.name + " - scrolling up");
Log.info(this.name + " - ARTICLE_SCROLL_UP, scroll position: " + this.config.scrollLength); Log.debug(this.name + " - ARTICLE_SCROLL_UP, scroll position: " + this.config.scrollLength);
} }
} else if (notification === "ARTICLE_LESS_DETAILS") { } else if (notification === "ARTICLE_LESS_DETAILS") {
this.resetDescrOrFullArticleAndTimer(); this.resetDescrOrFullArticleAndTimer();
Log.info(this.name + " - showing only article titles again"); Log.debug(this.name + " - showing only article titles again");
this.updateDom(100); this.updateDom(100);
} else if (notification === "ARTICLE_TOGGLE_FULL") { } else if (notification === "ARTICLE_TOGGLE_FULL") {
if (this.config.showFullArticle) { if (this.config.showFullArticle) {
@ -411,7 +411,7 @@ Module.register("newsfeed", {
} }
clearInterval(this.timer); clearInterval(this.timer);
this.timer = null; this.timer = null;
Log.info(this.name + " - showing " + this.isShowingDescription ? "article description" : "full article"); Log.debug(this.name + " - showing " + this.isShowingDescription ? "article description" : "full article");
this.updateDom(100); this.updateDom(100);
} }
}); });

View File

@ -10,7 +10,13 @@
{% set forecast = forecast.slice(0, numSteps) %} {% set forecast = forecast.slice(0, numSteps) %}
{% for f in forecast %} {% for f in forecast %}
<tr {% if config.colored %}class="colored"{% endif %} {% if config.fade %}style="opacity: {{ currentStep | opacity(numSteps) }};"{% endif %}> <tr {% if config.colored %}class="colored"{% endif %} {% if config.fade %}style="opacity: {{ currentStep | opacity(numSteps) }};"{% endif %}>
{% if (currentStep == 0) %}
<td class="day">{{ "TODAY" | translate }}</td>
{% elif (currentStep == 1) %}
<td class="day">{{ "TOMORROW" | translate }}</td>
{% else %}
<td class="day">{{ f.date.format('ddd') }}</td> <td class="day">{{ f.date.format('ddd') }}</td>
{% endif %}
<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") }}

View File

@ -87,7 +87,7 @@ WeatherProvider.register("ukmetofficedatahub", {
// Did not receive usable new data. // Did not receive usable new data.
// Maybe this needs a better check? // Maybe this needs a better check?
Log.error("Possibly bad current/hourly data?"); Log.error("Possibly bad current/hourly data?");
Log.info(data); Log.error(data);
return; return;
} }
@ -158,7 +158,7 @@ WeatherProvider.register("ukmetofficedatahub", {
// Did not receive usable new data. // Did not receive usable new data.
// Maybe this needs a better check? // Maybe this needs a better check?
Log.error("Possibly bad forecast data?"); Log.error("Possibly bad forecast data?");
Log.info(data); Log.error(data);
return; return;
} }

22
package-lock.json generated
View File

@ -5723,24 +5723,20 @@
"dev": true "dev": true
}, },
"node-ical": { "node-ical": {
"version": "0.12.1", "version": "0.12.3",
"resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.12.1.tgz", "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.12.3.tgz",
"integrity": "sha512-1YlvRyfOs0fUr8KWtgj4r/4VMI0tuBLfVSyrmDk00jLuUkXouiSU0PptEGdJs3U3pibF8ZThU6rVVS/jkcXJBQ==", "integrity": "sha512-MCr71z5mUcF7LuC3iIPKMKcaQ4Mr5cft0Uh00BBnkOrsm6vdS2xTUOKy1vhaYZ1V711LJmSAwQakxBXxLlHY3g==",
"requires": { "requires": {
"moment-timezone": "^0.5.31", "moment-timezone": "^0.5.31",
"request": "^2.88.2", "request": "^2.88.2",
"rrule": "2.6.4", "rrule": "2.6.6",
"uuid": "^3.3.2" "uuid": "^8.3.1"
}, },
"dependencies": { "dependencies": {
"rrule": { "uuid": {
"version": "2.6.4", "version": "8.3.1",
"resolved": "https://registry.npmjs.org/rrule/-/rrule-2.6.4.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
"integrity": "sha512-sLdnh4lmjUqq8liFiOUXD5kWp/FcnbDLPwq5YAc/RrN6120XOPb86Ae5zxF7ttBVq8O3LxjjORMEit1baluahA==", "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
"requires": {
"luxon": "^1.21.3",
"tslib": "^1.10.0"
}
} }
} }
}, },

View File

@ -79,7 +79,7 @@
"iconv-lite": "^0.6.2", "iconv-lite": "^0.6.2",
"module-alias": "^2.2.2", "module-alias": "^2.2.2",
"moment": "^2.28.0", "moment": "^2.28.0",
"node-ical": "^0.12.1", "node-ical": "^0.12.3",
"request": "^2.88.2", "request": "^2.88.2",
"rrule": "^2.6.6", "rrule": "^2.6.6",
"rrule-alt": "^2.2.8", "rrule-alt": "^2.2.8",

View File

@ -186,7 +186,7 @@ describe("Weather module", function () {
const weather = generateWeatherForecast(); const weather = generateWeatherForecast();
await setup({ template, data: weather }); await setup({ template, data: weather });
const days = ["Fri", "Sat", "Sun", "Mon", "Tue"]; const days = ["Today", "Tomorrow", "Sun", "Mon", "Tue"];
for (const [index, day] of days.entries()) { for (const [index, day] of days.entries()) {
await app.client.waitUntilTextExists(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day, 10000); await app.client.waitUntilTextExists(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day, 10000);

View File

@ -25,7 +25,7 @@ var translations = {
"zh-tw": "translations/zh-tw.json", // Traditional Chinese "zh-tw": "translations/zh-tw.json", // Traditional Chinese
ja: "translations/ja.json", // Japanese ja: "translations/ja.json", // Japanese
pl: "translations/pl.json", // Polish pl: "translations/pl.json", // Polish
gr: "translations/gr.json", // Greek el: "translations/el.json", // Greek
da: "translations/da.json", // Danish da: "translations/da.json", // Danish
tr: "translations/tr.json", // Turkish tr: "translations/tr.json", // Turkish
ru: "translations/ru.json", // Russian ru: "translations/ru.json", // Russian