iobroker.ical 1.16.1 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +12 -9
- package/admin/jsonConfig.json +44 -23
- package/io-package.json +29 -29
- package/main.js +590 -318
- package/package.json +18 -19
package/main.js
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const ical
|
|
4
|
-
const crypto
|
|
5
|
-
const fs
|
|
6
|
-
const path
|
|
7
|
-
const https
|
|
8
|
-
const os
|
|
9
|
-
|
|
10
|
-
const utils
|
|
3
|
+
const ical = require('node-ical');
|
|
4
|
+
const crypto = require('node:crypto');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const https = require('node:https');
|
|
8
|
+
const os = require('node:os');
|
|
9
|
+
|
|
10
|
+
const utils = require('@iobroker/adapter-core');
|
|
11
11
|
const adapterName = require('./package.json').name.split('.').pop();
|
|
12
12
|
|
|
13
|
-
const RRule
|
|
14
|
-
const ce
|
|
15
|
-
const axios
|
|
13
|
+
const RRule = require('rrule').RRule;
|
|
14
|
+
const ce = require('cloneextend');
|
|
15
|
+
const axios = require('axios');
|
|
16
16
|
|
|
17
17
|
let adapter;
|
|
18
|
-
let stopped
|
|
18
|
+
let stopped = false;
|
|
19
19
|
let killTimeout = null;
|
|
20
20
|
|
|
21
21
|
function startAdapter(options) {
|
|
22
22
|
options = options || {};
|
|
23
23
|
|
|
24
|
-
Object.assign(options,{
|
|
25
|
-
name:
|
|
24
|
+
Object.assign(options, {
|
|
25
|
+
name: adapterName,
|
|
26
26
|
unload: function (callback) {
|
|
27
27
|
stopped = true;
|
|
28
28
|
callback();
|
|
29
29
|
},
|
|
30
30
|
ready: function () {
|
|
31
31
|
main();
|
|
32
|
-
}
|
|
32
|
+
},
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
adapter = new utils.Adapter(options);
|
|
@@ -38,36 +38,189 @@ function startAdapter(options) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
// set when ready
|
|
41
|
-
let
|
|
42
|
-
const warn
|
|
43
|
-
const prewarn
|
|
44
|
-
const preprewarn
|
|
45
|
-
|
|
46
|
-
let
|
|
47
|
-
const events
|
|
48
|
-
const dictionary
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
41
|
+
let normal = '';
|
|
42
|
+
const warn = '<span style="font-weight: bold; color: red"><span class="icalWarn">';
|
|
43
|
+
const prewarn = '<span style="font-weight: bold; color: orange"><span class="icalPreWarn">';
|
|
44
|
+
const preprewarn = '<span style="font-weight: bold; color: yellow"><span class="icalPrePreWarn">';
|
|
45
|
+
|
|
46
|
+
let datesArray = [];
|
|
47
|
+
const events = [];
|
|
48
|
+
const dictionary = {
|
|
49
|
+
today: {
|
|
50
|
+
en: 'Today',
|
|
51
|
+
it: 'Oggi',
|
|
52
|
+
es: 'Hoy',
|
|
53
|
+
pl: 'Dzisiaj',
|
|
54
|
+
fr: "Aujourd'hui",
|
|
55
|
+
de: 'Heute',
|
|
56
|
+
ru: 'Сегодня',
|
|
57
|
+
nl: 'Vandaag',
|
|
58
|
+
},
|
|
59
|
+
tomorrow: {
|
|
60
|
+
en: 'Tomorrow',
|
|
61
|
+
it: 'Domani',
|
|
62
|
+
es: 'Mañana',
|
|
63
|
+
pl: 'Jutro',
|
|
64
|
+
fr: 'Demain',
|
|
65
|
+
de: 'Morgen',
|
|
66
|
+
ru: 'Завтра',
|
|
67
|
+
nl: 'Morgen',
|
|
68
|
+
},
|
|
69
|
+
dayafter: {
|
|
70
|
+
en: 'Day After Tomorrow',
|
|
71
|
+
it: 'Dopodomani',
|
|
72
|
+
es: 'Pasado mañana',
|
|
73
|
+
pl: 'Pojutrze',
|
|
74
|
+
fr: 'Après demain',
|
|
75
|
+
de: 'Übermorgen',
|
|
76
|
+
ru: 'Послезавтра',
|
|
77
|
+
nl: 'Overmorgen',
|
|
78
|
+
},
|
|
79
|
+
'3days': {
|
|
80
|
+
en: 'In 3 days',
|
|
81
|
+
it: 'In 3 giorni',
|
|
82
|
+
es: 'En 3 días',
|
|
83
|
+
pl: 'W 3 dni',
|
|
84
|
+
fr: 'Dans 3 jours',
|
|
85
|
+
de: 'In 3 Tagen',
|
|
86
|
+
ru: 'Через 2 дня',
|
|
87
|
+
nl: 'Over 3 dagen',
|
|
88
|
+
},
|
|
89
|
+
'4days': {
|
|
90
|
+
en: 'In 4 days',
|
|
91
|
+
it: 'In 4 giorni',
|
|
92
|
+
es: 'En 4 días',
|
|
93
|
+
pl: 'W 4 dni',
|
|
94
|
+
fr: 'Dans 4 jours',
|
|
95
|
+
de: 'In 4 Tagen',
|
|
96
|
+
ru: 'Через 3 дня',
|
|
97
|
+
nl: 'Over 4 dagen',
|
|
98
|
+
},
|
|
99
|
+
'5days': {
|
|
100
|
+
en: 'In 5 days',
|
|
101
|
+
it: 'In 5 giorni',
|
|
102
|
+
es: 'En 5 días',
|
|
103
|
+
pl: 'W ciągu 5 dni',
|
|
104
|
+
fr: 'Dans 5 jours',
|
|
105
|
+
de: 'In 5 Tagen',
|
|
106
|
+
ru: 'Через 4 дня',
|
|
107
|
+
nl: 'Over 5 dagen',
|
|
108
|
+
},
|
|
109
|
+
'6days': {
|
|
110
|
+
en: 'In 6 days',
|
|
111
|
+
it: 'In 6 giorni',
|
|
112
|
+
es: 'En 6 días',
|
|
113
|
+
pl: 'W ciągu 6 dni',
|
|
114
|
+
fr: 'Dans 6 jours',
|
|
115
|
+
de: 'In 6 Tagen',
|
|
116
|
+
ru: 'Через 5 дней',
|
|
117
|
+
nl: 'Over 6 dagen',
|
|
118
|
+
},
|
|
119
|
+
oneweek: {
|
|
120
|
+
en: 'In one week',
|
|
121
|
+
it: 'In una settimana',
|
|
122
|
+
es: 'En una semana',
|
|
123
|
+
pl: 'W jeden tydzień',
|
|
124
|
+
fr: 'Dans une semaine',
|
|
125
|
+
de: 'In einer Woche',
|
|
126
|
+
ru: 'Через неделю',
|
|
127
|
+
nl: 'Binnen een week',
|
|
128
|
+
},
|
|
129
|
+
'1week_left': {
|
|
130
|
+
en: 'One week left',
|
|
131
|
+
it: 'Manca una settimana',
|
|
132
|
+
es: 'Queda una semana',
|
|
133
|
+
pl: 'Został jeden tydzień',
|
|
134
|
+
fr: 'Reste une semaine',
|
|
135
|
+
de: 'Noch eine Woche',
|
|
136
|
+
ru: 'Ещё неделя',
|
|
137
|
+
nl: 'Over een week',
|
|
138
|
+
},
|
|
139
|
+
'2week_left': {
|
|
140
|
+
en: 'Two weeks left',
|
|
141
|
+
it: 'Due settimane rimaste',
|
|
142
|
+
es: 'Dos semanas restantes',
|
|
143
|
+
pl: 'Zostały dwa tygodnie',
|
|
144
|
+
fr: 'Il reste deux semaines',
|
|
145
|
+
de: 'Noch zwei Wochen',
|
|
146
|
+
ru: 'Ещё две недели',
|
|
147
|
+
nl: 'Over twee weken',
|
|
148
|
+
},
|
|
149
|
+
'3week_left': {
|
|
150
|
+
en: 'Three weeks left',
|
|
151
|
+
it: 'Tre settimane rimanenti',
|
|
152
|
+
es: 'Tres semanas quedan',
|
|
153
|
+
pl: 'Pozostały trzy tygodnie',
|
|
154
|
+
fr: 'Trois semaines restantes',
|
|
155
|
+
de: 'Noch drei Wochen',
|
|
156
|
+
ru: 'Ещё три недели',
|
|
157
|
+
nl: 'Over drie weken',
|
|
158
|
+
},
|
|
159
|
+
'4week_left': {
|
|
160
|
+
en: 'Four weeks left',
|
|
161
|
+
it: 'Quattro settimane rimaste',
|
|
162
|
+
es: 'Cuatro semanas quedan',
|
|
163
|
+
pl: 'Pozostały cztery tygodnie',
|
|
164
|
+
fr: 'Quatre semaines à gauche',
|
|
165
|
+
de: 'Noch vier Wochen',
|
|
166
|
+
ru: 'Ещё три недели',
|
|
167
|
+
nl: 'Over vier weken',
|
|
168
|
+
},
|
|
169
|
+
'5week_left': {
|
|
170
|
+
en: 'Five weeks left',
|
|
171
|
+
it: 'Cinque settimane rimaste',
|
|
172
|
+
es: 'Quedan cinco semanas',
|
|
173
|
+
pl: 'Pozostało pięć tygodni',
|
|
174
|
+
fr: 'Cinq semaines à gauche',
|
|
175
|
+
de: 'Noch fünf Wochen',
|
|
176
|
+
ru: 'Ещё пять недель',
|
|
177
|
+
nl: 'Over vijf weken',
|
|
178
|
+
},
|
|
179
|
+
'6week_left': {
|
|
180
|
+
en: 'Six weeks left',
|
|
181
|
+
it: 'Sei settimane a sinistra',
|
|
182
|
+
es: 'Seis semanas restantes',
|
|
183
|
+
pl: 'Pozostało sześć tygodni',
|
|
184
|
+
fr: 'Six semaines à gauche',
|
|
185
|
+
de: 'Noch sechs Wochen',
|
|
186
|
+
ru: 'Ещё шесть недель',
|
|
187
|
+
nl: 'Over zes weken',
|
|
188
|
+
},
|
|
189
|
+
left: {
|
|
190
|
+
en: 'left',
|
|
191
|
+
it: 'sinistra',
|
|
192
|
+
es: 'izquierda',
|
|
193
|
+
pl: 'lewo',
|
|
194
|
+
fr: 'la gauche',
|
|
195
|
+
de: ' ',
|
|
196
|
+
ru: 'осталось',
|
|
197
|
+
nl: 'over',
|
|
198
|
+
},
|
|
199
|
+
still: { en: ' ', it: '', es: '', pl: '', fr: '', de: 'Noch', ru: ' ', nl: 'nog' },
|
|
200
|
+
days: { en: 'days', it: 'Giorni', es: 'dias', pl: 'dni', fr: 'journées', de: 'Tage', ru: 'дней', nl: 'dagen' },
|
|
201
|
+
day: { en: 'day', it: 'giorno', es: 'día', pl: 'dzień', fr: 'journée', de: 'Tag', ru: 'день', nl: 'dag' },
|
|
202
|
+
hours: { en: 'hours', it: 'ore', es: 'horas', pl: 'godziny', fr: 'heures', de: 'Stunden', ru: 'часов', nl: 'uren' },
|
|
203
|
+
hour: { en: 'hour', it: 'ora', es: 'hora', pl: 'godzina', fr: 'heure', de: 'Stunde', ru: 'час', nl: 'uur' },
|
|
204
|
+
minute: {
|
|
205
|
+
en: 'minute',
|
|
206
|
+
it: 'minuto',
|
|
207
|
+
es: 'minuto',
|
|
208
|
+
pl: 'minuta',
|
|
209
|
+
fr: 'minute',
|
|
210
|
+
de: 'Minute',
|
|
211
|
+
ru: 'минута',
|
|
212
|
+
nl: 'minuut',
|
|
213
|
+
},
|
|
214
|
+
minutes: {
|
|
215
|
+
en: 'minutes',
|
|
216
|
+
it: 'minuti',
|
|
217
|
+
es: 'minutos',
|
|
218
|
+
pl: 'minutos',
|
|
219
|
+
fr: 'minutes',
|
|
220
|
+
de: 'Minuten',
|
|
221
|
+
ru: 'минуты',
|
|
222
|
+
nl: 'minuten',
|
|
223
|
+
},
|
|
71
224
|
};
|
|
72
225
|
|
|
73
226
|
function _(text) {
|
|
@@ -99,14 +252,12 @@ function _(text) {
|
|
|
99
252
|
* 1 : if this > b
|
|
100
253
|
* NaN : if a or b is an illegal date
|
|
101
254
|
*/
|
|
102
|
-
Date.prototype.compare = function(b) {
|
|
255
|
+
Date.prototype.compare = function (b) {
|
|
103
256
|
if (b.constructor !== Date) {
|
|
104
257
|
throw new Error('invalid_date');
|
|
105
258
|
}
|
|
106
259
|
|
|
107
|
-
return
|
|
108
|
-
(this > b) - (this < b) : NaN
|
|
109
|
-
);
|
|
260
|
+
return isFinite(this.valueOf()) && isFinite(b.valueOf()) ? (this > b) - (this < b) : NaN;
|
|
110
261
|
};
|
|
111
262
|
|
|
112
263
|
async function getICal(urlOrFile, user, pass, sslignore, calName, cb) {
|
|
@@ -130,95 +281,105 @@ async function getICal(urlOrFile, user, pass, sslignore, calName, cb) {
|
|
|
130
281
|
cb && cb(`Cannot read file "${urlOrFile}": ${e}`);
|
|
131
282
|
}
|
|
132
283
|
}
|
|
133
|
-
|
|
134
284
|
} else {
|
|
135
285
|
// Find out whether SSL certificate errors shall be ignored
|
|
136
286
|
const options = {
|
|
137
287
|
method: 'get',
|
|
138
|
-
url: urlOrFile
|
|
288
|
+
url: urlOrFile,
|
|
139
289
|
};
|
|
140
290
|
|
|
141
291
|
if (adapter.config.customUserAgentEnabled && adapter.config.customUserAgent) {
|
|
142
292
|
options.headers = {
|
|
143
|
-
'User-Agent': adapter.config.customUserAgent
|
|
293
|
+
'User-Agent': adapter.config.customUserAgent,
|
|
144
294
|
};
|
|
145
295
|
}
|
|
146
296
|
|
|
147
297
|
if (sslignore === 'ignore' || sslignore === 'true' || sslignore === true) {
|
|
148
298
|
options.httpsAgent = new https.Agent({
|
|
149
|
-
rejectUnauthorized: false
|
|
299
|
+
rejectUnauthorized: false,
|
|
150
300
|
});
|
|
151
301
|
}
|
|
152
302
|
|
|
153
303
|
if (user) {
|
|
154
304
|
options.auth = {
|
|
155
305
|
username: user,
|
|
156
|
-
password: pass
|
|
306
|
+
password: pass,
|
|
157
307
|
};
|
|
158
308
|
}
|
|
159
309
|
|
|
160
|
-
const calHash = crypto
|
|
161
|
-
|
|
310
|
+
const calHash = crypto
|
|
311
|
+
.createHash('md5')
|
|
312
|
+
.update(user + pass + urlOrFile)
|
|
313
|
+
.digest('hex');
|
|
314
|
+
const cachedFilename = path.join(os.tmpdir(), `iob-${calHash}.ics`);
|
|
315
|
+
|
|
316
|
+
axios(options)
|
|
317
|
+
.then(function (response) {
|
|
318
|
+
if (response.data) {
|
|
319
|
+
try {
|
|
320
|
+
fs.writeFileSync(cachedFilename, response.data, 'utf-8');
|
|
321
|
+
adapter.log.debug(
|
|
322
|
+
`Successfully cached content for calendar "${urlOrFile}" as ${cachedFilename}`,
|
|
323
|
+
);
|
|
324
|
+
} catch (err) {
|
|
325
|
+
adapter.log.error(`Cannot write cached file: ${err}`);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
cb && cb(null, response.data);
|
|
329
|
+
} else {
|
|
330
|
+
cb && cb(`Error reading from URL "${urlOrFile}": Received no data`);
|
|
331
|
+
}
|
|
332
|
+
})
|
|
333
|
+
.catch(error => {
|
|
334
|
+
let cachedContent;
|
|
335
|
+
let cachedDate;
|
|
336
|
+
|
|
337
|
+
if (error.response) {
|
|
338
|
+
// The request was made and the server responded with a status code
|
|
339
|
+
// that falls out of the range of 2xx
|
|
340
|
+
adapter.log.warn(`Error reading from URL "${urlOrFile}": ${error.response.status}`);
|
|
341
|
+
} else if (error.request) {
|
|
342
|
+
// The request was made but no response was received
|
|
343
|
+
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
|
344
|
+
// http.ClientRequest in node.js
|
|
345
|
+
adapter.log.warn(`Error reading from URL "${urlOrFile}"`);
|
|
346
|
+
} else {
|
|
347
|
+
// Something happened in setting up the request that triggered an Error
|
|
348
|
+
adapter.log.warn(`Error reading from URL "${urlOrFile}": ${error.message}`);
|
|
349
|
+
}
|
|
162
350
|
|
|
163
|
-
axios(options).then(function (response) {
|
|
164
|
-
if (response.data) {
|
|
165
351
|
try {
|
|
166
|
-
fs.
|
|
167
|
-
|
|
352
|
+
if (fs.existsSync(cachedFilename)) {
|
|
353
|
+
cachedContent = fs.readFileSync(cachedFilename, 'utf-8');
|
|
354
|
+
const stat = fs.statSync(cachedFilename);
|
|
355
|
+
cachedDate = stat.mtime;
|
|
356
|
+
}
|
|
168
357
|
} catch (err) {
|
|
169
|
-
|
|
358
|
+
adapter.log.info(`Cannot read cached calendar file for "${urlOrFile}": ${err.message}`);
|
|
170
359
|
}
|
|
171
360
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
cb && cb(`Error reading from URL "${urlOrFile}": Received no data`);
|
|
175
|
-
}
|
|
176
|
-
}).catch(error => {
|
|
177
|
-
let cachedContent;
|
|
178
|
-
let cachedDate;
|
|
179
|
-
|
|
180
|
-
if (error.response) {
|
|
181
|
-
// The request was made and the server responded with a status code
|
|
182
|
-
// that falls out of the range of 2xx
|
|
183
|
-
adapter.log.warn(`Error reading from URL "${urlOrFile}": ${error.response.status}`);
|
|
184
|
-
} else if (error.request) {
|
|
185
|
-
// The request was made but no response was received
|
|
186
|
-
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
|
187
|
-
// http.ClientRequest in node.js
|
|
188
|
-
adapter.log.warn(`Error reading from URL "${urlOrFile}"`);
|
|
189
|
-
} else {
|
|
190
|
-
// Something happened in setting up the request that triggered an Error
|
|
191
|
-
adapter.log.warn(`Error reading from URL "${urlOrFile}": ${error.message}`);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
try {
|
|
195
|
-
if (fs.existsSync(cachedFilename)) {
|
|
196
|
-
cachedContent = fs.readFileSync(cachedFilename, 'utf-8');
|
|
197
|
-
const stat = fs.statSync(cachedFilename);
|
|
198
|
-
cachedDate = stat.mtime;
|
|
361
|
+
if (!cachedContent) {
|
|
362
|
+
return cb && cb(`Cannot read URL: "${urlOrFile}"`);
|
|
199
363
|
}
|
|
200
|
-
} catch (err) {
|
|
201
|
-
adapter.log.info(`Cannot read cached calendar file for "${urlOrFile}": ${err.message}`);
|
|
202
|
-
}
|
|
203
364
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
adapter.log.info(`Use cached File content for "${urlOrFile}" from ${cachedDate}`);
|
|
209
|
-
cb && cb(null, cachedContent);
|
|
210
|
-
});
|
|
365
|
+
adapter.log.info(`Use cached File content for "${urlOrFile}" from ${cachedDate}`);
|
|
366
|
+
cb && cb(null, cachedContent);
|
|
367
|
+
});
|
|
211
368
|
}
|
|
212
369
|
}
|
|
213
370
|
|
|
214
371
|
function checkICal(urlOrFile, user, pass, sslignore, calName, filter, cb) {
|
|
215
|
-
if (stopped)
|
|
372
|
+
if (stopped) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
216
375
|
if (typeof user === 'function') {
|
|
217
376
|
cb = user;
|
|
218
377
|
user = undefined;
|
|
219
378
|
}
|
|
220
379
|
getICal(urlOrFile, user, pass, sslignore, calName, (err, _data) => {
|
|
221
|
-
if (stopped)
|
|
380
|
+
if (stopped) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
222
383
|
if (err || !_data) {
|
|
223
384
|
adapter.log.warn(`Error reading "${urlOrFile}": ${err}`);
|
|
224
385
|
cb(err, calName);
|
|
@@ -229,7 +390,9 @@ function checkICal(urlOrFile, user, pass, sslignore, calName, filter, cb) {
|
|
|
229
390
|
|
|
230
391
|
try {
|
|
231
392
|
ical.parseICS(_data, (err, data) => {
|
|
232
|
-
if (stopped)
|
|
393
|
+
if (stopped) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
233
396
|
if (data) {
|
|
234
397
|
adapter.log.info(`processing URL: ${calName} ${urlOrFile}`);
|
|
235
398
|
adapter.log.debug(JSON.stringify(data));
|
|
@@ -251,8 +414,7 @@ function checkICal(urlOrFile, user, pass, sslignore, calName, filter, cb) {
|
|
|
251
414
|
// clear time
|
|
252
415
|
now2.setHours(0, 0, 0, 0);
|
|
253
416
|
|
|
254
|
-
setImmediate(() =>
|
|
255
|
-
processData(data, realnow, startpreview, endpreview, now2, calName, filter, cb));
|
|
417
|
+
setImmediate(() => processData(data, realnow, startpreview, endpreview, now2, calName, filter, cb));
|
|
256
418
|
} else {
|
|
257
419
|
// Ready with processing
|
|
258
420
|
cb(null, calName);
|
|
@@ -266,7 +428,7 @@ function checkICal(urlOrFile, user, pass, sslignore, calName, filter, cb) {
|
|
|
266
428
|
}
|
|
267
429
|
|
|
268
430
|
function addOffset(time, offset) {
|
|
269
|
-
return new Date(time.getTime() +
|
|
431
|
+
return new Date(time.getTime() + offset * 60 * 1000);
|
|
270
432
|
}
|
|
271
433
|
|
|
272
434
|
function treatAsUTC(date) {
|
|
@@ -276,14 +438,16 @@ function treatAsUTC(date) {
|
|
|
276
438
|
}
|
|
277
439
|
|
|
278
440
|
async function processData(data, realnow, startpreview, endpreview, now2, calName, filter, cb) {
|
|
279
|
-
if (stopped)
|
|
441
|
+
if (stopped) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
280
444
|
let processedEntries = 0;
|
|
281
445
|
for (const k in data) {
|
|
282
446
|
const ev = data[k];
|
|
283
447
|
delete data[k];
|
|
284
448
|
|
|
285
449
|
// only events with summary and a start date are interesting
|
|
286
|
-
if (
|
|
450
|
+
if (ev.summary !== undefined && ev.type === 'VEVENT' && ev.start && ev.start instanceof Date) {
|
|
287
451
|
adapter.log.debug(`ev[${k}]: ${JSON.stringify(ev)}`);
|
|
288
452
|
if (!ev.end || !(ev.end instanceof Date)) {
|
|
289
453
|
ev.end = new Date(ev.start.getTime());
|
|
@@ -297,7 +461,7 @@ async function processData(data, realnow, startpreview, endpreview, now2, calNam
|
|
|
297
461
|
if (ev.datetype === 'date') {
|
|
298
462
|
// If "whole day event" correct the eventlength to full days
|
|
299
463
|
const calcStart = new Date(ev.start.getTime());
|
|
300
|
-
calcStart.setHours(0,0,0,0);
|
|
464
|
+
calcStart.setHours(0, 0, 0, 0);
|
|
301
465
|
let calcEnd = new Date(ev.end.getTime());
|
|
302
466
|
if (calcEnd.getHours() === 0 && calcEnd.getMinutes() === 0 && calcEnd.getSeconds() === 0) {
|
|
303
467
|
// if end id 0:0:0 then it is considered exclusive, so reduce by 1s
|
|
@@ -305,10 +469,12 @@ async function processData(data, realnow, startpreview, endpreview, now2, calNam
|
|
|
305
469
|
calcEnd.setDate(calcEnd.getDate() - 1);
|
|
306
470
|
adapter.log.debug(`Adjust enddate to exclude 0:0:0 for eventlength`);
|
|
307
471
|
}
|
|
308
|
-
calcEnd.setHours(23,59,59,0);
|
|
472
|
+
calcEnd.setHours(23, 59, 59, 0);
|
|
309
473
|
eventLength = treatAsUTC(calcEnd.getTime()) - treatAsUTC(calcStart.getTime());
|
|
310
|
-
eventLength = Math.ceil(eventLength / (
|
|
311
|
-
adapter.log.debug(
|
|
474
|
+
eventLength = Math.ceil(eventLength / (24 * 60 * 60 * 1000)) * 24 * 60 * 60 * 1000;
|
|
475
|
+
adapter.log.debug(
|
|
476
|
+
`Calculated Date Eventlength = ${eventLength} (${eventLength / (24 * 60 * 60 * 1000)} days) for ${calcStart.toString()} - ${calcEnd.toString()}`,
|
|
477
|
+
);
|
|
312
478
|
}
|
|
313
479
|
|
|
314
480
|
const options = RRule.parseString(ev.rrule.toString());
|
|
@@ -319,7 +485,10 @@ async function processData(data, realnow, startpreview, endpreview, now2, calNam
|
|
|
319
485
|
// until=2021-11-09T15:59:59.000Z) so that an event is still considered as TODAY
|
|
320
486
|
// even thought it ends one second before the next scheduled one.
|
|
321
487
|
if (options.until !== undefined && options.dtstart !== undefined) {
|
|
322
|
-
options.until = addOffset(
|
|
488
|
+
options.until = addOffset(
|
|
489
|
+
options.until,
|
|
490
|
+
options.dtstart.getTimezoneOffset() - options.until.getTimezoneOffset(),
|
|
491
|
+
);
|
|
323
492
|
}
|
|
324
493
|
adapter.log.debug(`options: ${JSON.stringify(options)}`);
|
|
325
494
|
|
|
@@ -332,19 +501,21 @@ async function processData(data, realnow, startpreview, endpreview, now2, calNam
|
|
|
332
501
|
if (startpreview < now3) {
|
|
333
502
|
now3 = startpreview;
|
|
334
503
|
}
|
|
335
|
-
adapter.log.debug(
|
|
504
|
+
adapter.log.debug(
|
|
505
|
+
`RRule event:${ev.summary}; start:${ev.start.toString()}; endpreview:${endpreview.toString()}; startpreview:${startpreview.toString()}; now2:${now2.toString()}; now3:${now3.toString()}; rule:${JSON.stringify(rule)}`,
|
|
506
|
+
);
|
|
336
507
|
|
|
337
508
|
let dates = [];
|
|
338
509
|
try {
|
|
339
510
|
dates = rule.between(now3, endpreview, true);
|
|
340
|
-
} catch(e) {
|
|
341
|
-
adapter.log
|
|
511
|
+
} catch (e) {
|
|
512
|
+
adapter.log
|
|
513
|
+
.error(`Issue detected in RRule, event ignored; Please forward debug information to iobroker.ical developer: ${e.stack}
|
|
342
514
|
RRule object: ${JSON.stringify(rule)}
|
|
343
515
|
now3: ${now3}
|
|
344
516
|
endpreview: ${endpreview}
|
|
345
517
|
string: ${ev.rrule.toString()}
|
|
346
|
-
options: ${JSON.stringify(options)}`
|
|
347
|
-
);
|
|
518
|
+
options: ${JSON.stringify(options)}`);
|
|
348
519
|
}
|
|
349
520
|
|
|
350
521
|
adapter.log.debug(`dates: ${JSON.stringify(dates)}`);
|
|
@@ -362,7 +533,7 @@ options: ${JSON.stringify(options)}`
|
|
|
362
533
|
if (ev.datetype === 'date') {
|
|
363
534
|
// make sure to set the time to 00:00:00 so that
|
|
364
535
|
// this event will be recognized as a date event
|
|
365
|
-
ev2.start.setHours(0,0,0,0);
|
|
536
|
+
ev2.start.setHours(0, 0, 0, 0);
|
|
366
537
|
} else if (ev.datetype === 'date-time') {
|
|
367
538
|
// rrule only knows about local time but stores the
|
|
368
539
|
// datetime in zulu (Z) UTC time strings. Thus we need
|
|
@@ -373,7 +544,8 @@ options: ${JSON.stringify(options)}`
|
|
|
373
544
|
|
|
374
545
|
// Set end date based on length in ms
|
|
375
546
|
ev2.end = new Date(ev2.start.getTime() + eventLength);
|
|
376
|
-
if (ev2.start.getTimezoneOffset() !== ev2.end.getTimezoneOffset()) {
|
|
547
|
+
if (ev2.start.getTimezoneOffset() !== ev2.end.getTimezoneOffset()) {
|
|
548
|
+
// DST difference, we need to correct it
|
|
377
549
|
ev2.end = addOffset(ev2.end, ev2.end.getTimezoneOffset() - ev2.start.getTimezoneOffset());
|
|
378
550
|
}
|
|
379
551
|
|
|
@@ -382,8 +554,10 @@ options: ${JSON.stringify(options)}`
|
|
|
382
554
|
// be excluded.
|
|
383
555
|
let checkDate = true;
|
|
384
556
|
if (ev2.exdate) {
|
|
385
|
-
adapter.log.debug(
|
|
386
|
-
|
|
557
|
+
adapter.log.debug(
|
|
558
|
+
` ${i}: Event (exdate: ${JSON.stringify(Object.keys(ev2.exdate))}): ${ev2.start.toString()} ${ev2.end.toString()}`,
|
|
559
|
+
);
|
|
560
|
+
for (const d in ev2.exdate) {
|
|
387
561
|
const dd = new Date(ev2.exdate[d]);
|
|
388
562
|
if (dd.getTime() === ev2.start.getTime()) {
|
|
389
563
|
checkDate = false;
|
|
@@ -392,15 +566,19 @@ options: ${JSON.stringify(options)}`
|
|
|
392
566
|
}
|
|
393
567
|
}
|
|
394
568
|
} else {
|
|
395
|
-
adapter.log.debug(
|
|
569
|
+
adapter.log.debug(
|
|
570
|
+
` ${i}: Event (NO exdate): ${ev2.start.toString()} ${ev2.end.toString()}`,
|
|
571
|
+
);
|
|
396
572
|
}
|
|
397
573
|
|
|
398
574
|
if (checkDate && ev.recurrences) {
|
|
399
|
-
for(const dOri in ev.recurrences) {
|
|
575
|
+
for (const dOri in ev.recurrences) {
|
|
400
576
|
const recurEvent = ev.recurrences[dOri];
|
|
401
577
|
if (recurEvent.recurrenceid.getTime() === ev2.start.getTime()) {
|
|
402
578
|
ev2 = ce.clone(recurEvent);
|
|
403
|
-
adapter.log.debug(
|
|
579
|
+
adapter.log.debug(
|
|
580
|
+
` ${i}: different recurring found replaced with Event:${ev2.start} ${ev2.end}`,
|
|
581
|
+
);
|
|
404
582
|
}
|
|
405
583
|
}
|
|
406
584
|
}
|
|
@@ -413,7 +591,9 @@ options: ${JSON.stringify(options)}`
|
|
|
413
591
|
adapter.log.debug('no RRule events inside the time interval');
|
|
414
592
|
}
|
|
415
593
|
} else {
|
|
416
|
-
adapter.log.debug(
|
|
594
|
+
adapter.log.debug(
|
|
595
|
+
`Single event: ${ev.summary}; start:${ev.start}; end:${ev.end}; endpreview:${endpreview}; startpreview:${startpreview}; realnow:${realnow}`,
|
|
596
|
+
);
|
|
417
597
|
// No RRule event
|
|
418
598
|
await checkDates(ev, endpreview, startpreview, realnow, ' ', calName, filter);
|
|
419
599
|
}
|
|
@@ -426,8 +606,7 @@ options: ${JSON.stringify(options)}`
|
|
|
426
606
|
if (!Object.keys(data).length) {
|
|
427
607
|
cb(null, calName);
|
|
428
608
|
} else {
|
|
429
|
-
setImmediate(() =>
|
|
430
|
-
processData(data, realnow, startpreview, endpreview, now2, calName, filter, cb));
|
|
609
|
+
setImmediate(() => processData(data, realnow, startpreview, endpreview, now2, calName, filter, cb));
|
|
431
610
|
}
|
|
432
611
|
}
|
|
433
612
|
|
|
@@ -438,7 +617,10 @@ async function checkDates(ev, endpreview, startpreview, realnow, rule, calName,
|
|
|
438
617
|
let date;
|
|
439
618
|
|
|
440
619
|
// chech if sub parameter exists for outlook
|
|
441
|
-
if (
|
|
620
|
+
if (
|
|
621
|
+
Object.prototype.hasOwnProperty.call(ev, 'summary') &&
|
|
622
|
+
Object.prototype.hasOwnProperty.call(ev.summary, 'val')
|
|
623
|
+
) {
|
|
442
624
|
// yes -> read reason
|
|
443
625
|
reason = ev.summary.val || '';
|
|
444
626
|
} else {
|
|
@@ -452,18 +634,22 @@ async function checkDates(ev, endpreview, startpreview, realnow, rule, calName,
|
|
|
452
634
|
isPrivate = Object.prototype.hasOwnProperty.call(ev, 'class') && ev.class === 'PRIVATE';
|
|
453
635
|
|
|
454
636
|
// If not start point => ignore it
|
|
455
|
-
if (!ev.start || !ev.start instanceof Date) {
|
|
637
|
+
if (!ev.start || (!ev.start) instanceof Date) {
|
|
456
638
|
return;
|
|
457
639
|
}
|
|
458
640
|
|
|
459
641
|
// If not end point => assume 0:0:0 event and set to same as start
|
|
460
|
-
ev.end = new Date(ev.end.getTime())
|
|
461
|
-
if (!ev.end
|
|
642
|
+
ev.end = new Date(ev.end.getTime());
|
|
643
|
+
if (!ev.end) {
|
|
644
|
+
ev.end = new Date(ev.start.getTime());
|
|
645
|
+
}
|
|
646
|
+
if (!ev.end || (!ev.end) instanceof Date) {
|
|
462
647
|
return;
|
|
463
648
|
}
|
|
464
649
|
|
|
465
650
|
// If full day
|
|
466
|
-
if (
|
|
651
|
+
if (
|
|
652
|
+
ev.start.getHours() === 0 &&
|
|
467
653
|
ev.start.getMinutes() === 0 &&
|
|
468
654
|
ev.start.getSeconds() === 0 &&
|
|
469
655
|
ev.end.getHours() === 0 &&
|
|
@@ -505,32 +691,35 @@ LOCATION:${location}`;
|
|
|
505
691
|
|
|
506
692
|
// Full day
|
|
507
693
|
if (fullDay) {
|
|
508
|
-
|
|
509
694
|
adapter.log.debug(`Event (full day) processing. Start: ${ev.start} End: ${ev.end}`);
|
|
510
695
|
|
|
511
696
|
// event start >= startpreview && < previewtime or end > startpreview && < previewtime ---> display
|
|
512
|
-
if (
|
|
697
|
+
if (
|
|
698
|
+
(ev.start < endpreview && ev.start >= startpreview) ||
|
|
699
|
+
(ev.end > startpreview && ev.end <= endpreview) ||
|
|
700
|
+
(ev.start < realnow && ev.end > realnow)
|
|
701
|
+
) {
|
|
513
702
|
// check only full day events
|
|
514
703
|
if (await checkForEvents(reason, ev, realnow)) {
|
|
515
704
|
date = formatDate(ev.start, ev.end, true, true);
|
|
516
705
|
|
|
517
706
|
insertSorted(datesArray, {
|
|
518
|
-
date:
|
|
519
|
-
event:
|
|
520
|
-
_class:
|
|
521
|
-
_date:
|
|
707
|
+
date: date.text,
|
|
708
|
+
event: reason,
|
|
709
|
+
_class: `ical_${calName} ${date._class}`,
|
|
710
|
+
_date: new Date(ev.start.getTime()),
|
|
522
711
|
// add additional Objects, so iobroker.occ can use it
|
|
523
|
-
_end:
|
|
712
|
+
_end: new Date(ev.end.getTime()),
|
|
524
713
|
_section: ev.description,
|
|
525
|
-
_IDID:
|
|
526
|
-
_allDay:
|
|
714
|
+
_IDID: ev.uid,
|
|
715
|
+
_allDay: true,
|
|
527
716
|
_private: isPrivate,
|
|
528
|
-
_rule:
|
|
717
|
+
_rule: rule,
|
|
529
718
|
location: location,
|
|
530
719
|
// add additional Objects, so iobroker.occ can use it
|
|
531
720
|
_calName: calName,
|
|
532
721
|
_calColor: adapter.config.calendars.find(x => x.name === calName).color,
|
|
533
|
-
_object: ev
|
|
722
|
+
_object: ev,
|
|
534
723
|
});
|
|
535
724
|
|
|
536
725
|
adapter.log.debug(`Event (full day) added : ${JSON.stringify(rule)} ${reason} at ${date.text}`);
|
|
@@ -539,36 +728,41 @@ LOCATION:${location}`;
|
|
|
539
728
|
}
|
|
540
729
|
} else {
|
|
541
730
|
// filtered out, because does not belongs to specified time interval
|
|
542
|
-
adapter.log.debug(
|
|
731
|
+
adapter.log.debug(
|
|
732
|
+
`Event (full day) ${JSON.stringify(rule)} ${reason} at ${ev.start.toString()} filtered out, does not belong to specified time interval`,
|
|
733
|
+
);
|
|
543
734
|
}
|
|
544
735
|
} else {
|
|
545
|
-
|
|
546
736
|
adapter.log.debug(`Event (time) processing. Start: ${ev.start} End: ${ev.end}`);
|
|
547
737
|
|
|
548
738
|
// Event with time
|
|
549
739
|
// Start time >= startpreview && Start time < preview time && End time >= now
|
|
550
|
-
if (
|
|
740
|
+
if (
|
|
741
|
+
(ev.start >= startpreview && ev.start < endpreview && ev.end >= realnow) ||
|
|
742
|
+
(ev.end >= realnow && ev.end <= endpreview) ||
|
|
743
|
+
(ev.start < realnow && ev.end > realnow)
|
|
744
|
+
) {
|
|
551
745
|
// Add to list only if not hidden
|
|
552
746
|
if (await checkForEvents(reason, ev, realnow)) {
|
|
553
747
|
date = formatDate(ev.start, ev.end, true, false);
|
|
554
748
|
|
|
555
749
|
insertSorted(datesArray, {
|
|
556
|
-
date:
|
|
557
|
-
event:
|
|
558
|
-
_class:
|
|
559
|
-
_date:
|
|
750
|
+
date: date.text,
|
|
751
|
+
event: reason,
|
|
752
|
+
_class: `ical_${calName} ${date._class}`,
|
|
753
|
+
_date: new Date(ev.start.getTime()),
|
|
560
754
|
// add additional Objects, so iobroker.occ can use it
|
|
561
|
-
_end:
|
|
755
|
+
_end: new Date(ev.end.getTime()),
|
|
562
756
|
_section: ev.description,
|
|
563
|
-
_IDID:
|
|
564
|
-
_allDay:
|
|
757
|
+
_IDID: ev.uid,
|
|
758
|
+
_allDay: false,
|
|
565
759
|
_private: isPrivate,
|
|
566
|
-
_rule:
|
|
760
|
+
_rule: rule,
|
|
567
761
|
location: location,
|
|
568
762
|
// add additional Objects, so iobroker.occ can use it
|
|
569
763
|
_calName: calName,
|
|
570
764
|
_calColor: adapter.config.calendars.find(x => x.name === calName).color,
|
|
571
|
-
_object: ev
|
|
765
|
+
_object: ev,
|
|
572
766
|
});
|
|
573
767
|
|
|
574
768
|
adapter.log.debug(`Event with time added: ${JSON.stringify(rule)} ${reason} at ${date.text}`);
|
|
@@ -577,7 +771,9 @@ LOCATION:${location}`;
|
|
|
577
771
|
}
|
|
578
772
|
} else {
|
|
579
773
|
// filtered out, because does not belongs to specified time interval
|
|
580
|
-
adapter.log.debug(
|
|
774
|
+
adapter.log.debug(
|
|
775
|
+
`Event ${JSON.stringify(rule)} ${reason} at ${ev.start.toString()} filtered out, because does not belongs to specified time interval`,
|
|
776
|
+
);
|
|
581
777
|
}
|
|
582
778
|
}
|
|
583
779
|
}
|
|
@@ -585,7 +781,7 @@ LOCATION:${location}`;
|
|
|
585
781
|
function colorizeDates(date, today, tomorrow, dayafter, col, calName) {
|
|
586
782
|
const result = {
|
|
587
783
|
prefix: normal,
|
|
588
|
-
suffix:
|
|
784
|
+
suffix: `</span>${adapter.config.colorize ? '</span>' : ''}`,
|
|
589
785
|
};
|
|
590
786
|
const cmpDate = new Date(date.getTime());
|
|
591
787
|
cmpDate.setHours(0, 0, 0, 0);
|
|
@@ -599,50 +795,47 @@ function colorizeDates(date, today, tomorrow, dayafter, col, calName) {
|
|
|
599
795
|
result.prefix = warn;
|
|
600
796
|
// If configured every calendar has own color
|
|
601
797
|
if (adapter.config.everyCalOneColor) {
|
|
602
|
-
result.suffix += `<span style="font-weight:normal${col ?
|
|
798
|
+
result.suffix += `<span style="font-weight:normal${col ? `;color:${col}` : ''}">`;
|
|
603
799
|
} else {
|
|
604
800
|
result.suffix += '<span style="font-weight:normal;color:red">';
|
|
605
801
|
}
|
|
606
802
|
result.suffix += `<span class="icalWarn2 iCal-${calName}2">`;
|
|
607
|
-
} else
|
|
608
|
-
|
|
609
|
-
if (cmpDate.compare(tomorrow) === 0) {
|
|
803
|
+
} else if (cmpDate.compare(tomorrow) === 0) {
|
|
804
|
+
// tomorrow
|
|
610
805
|
result.prefix = prewarn;
|
|
611
806
|
// If configured every calendar has own color
|
|
612
807
|
if (adapter.config.everyCalOneColor) {
|
|
613
|
-
result.suffix += `<span style="font-weight: normal${col ?
|
|
808
|
+
result.suffix += `<span style="font-weight: normal${col ? `; color:${col}` : ''}">`;
|
|
614
809
|
} else {
|
|
615
810
|
result.suffix += '<span style="font-weight: normal; color: orange">';
|
|
616
811
|
}
|
|
617
812
|
result.suffix += `<span class='icalPreWarn2 iCal-${calName}2'>`;
|
|
618
|
-
} else
|
|
619
|
-
|
|
620
|
-
if (cmpDate.compare(dayafter) === 0) {
|
|
813
|
+
} else if (cmpDate.compare(dayafter) === 0) {
|
|
814
|
+
// day after tomorrow
|
|
621
815
|
result.prefix = preprewarn;
|
|
622
816
|
// If configured every calendar has own color
|
|
623
817
|
if (adapter.config.everyCalOneColor) {
|
|
624
|
-
result.suffix += `<span style="font-weight: normal${col ?
|
|
818
|
+
result.suffix += `<span style="font-weight: normal${col ? `; color:${col}` : ''}">`;
|
|
625
819
|
} else {
|
|
626
820
|
result.suffix += '<span style="font-weight: normal; color: yellow">';
|
|
627
821
|
}
|
|
628
822
|
result.suffix += `<span class='icalPrePreWarn2 iCal-${calName}2'>`;
|
|
629
|
-
} else
|
|
630
|
-
|
|
631
|
-
if (cmpDate.compare(today) === -1) {
|
|
823
|
+
} else if (cmpDate.compare(today) === -1) {
|
|
824
|
+
// start time is in the past
|
|
632
825
|
result.prefix = normal;
|
|
633
826
|
// If configured every calendar has own color
|
|
634
827
|
if (adapter.config.everyCalOneColor) {
|
|
635
|
-
result.suffix += `<span style="font-weight: normal${col ?
|
|
828
|
+
result.suffix += `<span style="font-weight: normal${col ? `; color:${col}` : ''}">`;
|
|
636
829
|
} else {
|
|
637
|
-
result.suffix += `<span style="font-weight: normal${adapter.config.defColor ?
|
|
830
|
+
result.suffix += `<span style="font-weight: normal${adapter.config.defColor ? `; color:${adapter.config.defColor}` : ''}">`;
|
|
638
831
|
}
|
|
639
832
|
result.suffix += `<span class='icalNormal2 iCal-${calName}2'>`;
|
|
640
833
|
} else {
|
|
641
834
|
// If configured every calendar has own color
|
|
642
835
|
if (adapter.config.everyCalOneColor) {
|
|
643
|
-
result.suffix += `<span style="font-weight: normal${col ?
|
|
836
|
+
result.suffix += `<span style="font-weight: normal${col ? `; color:${col}` : ''}">`;
|
|
644
837
|
} else {
|
|
645
|
-
result.suffix += `<span style="font-weight: normal${adapter.config.defColor ?
|
|
838
|
+
result.suffix += `<span style="font-weight: normal${adapter.config.defColor ? `; color:${adapter.config.defColor}` : ''}">`;
|
|
646
839
|
}
|
|
647
840
|
result.suffix += `<span class='icalNormal2 iCal-${calName}2'>`;
|
|
648
841
|
}
|
|
@@ -668,12 +861,15 @@ async function checkForEvents(reason, event, realnow) {
|
|
|
668
861
|
let evFound = false;
|
|
669
862
|
if (exactMatchInEventname) {
|
|
670
863
|
// calendar entry must be exactly the same as the event
|
|
671
|
-
if (
|
|
864
|
+
if (reason === ev.name || (ignoreCaseInEventname && reason.toLowerCase() === ev.name.toLowerCase())) {
|
|
672
865
|
evFound = true;
|
|
673
866
|
}
|
|
674
867
|
} else {
|
|
675
868
|
// event is included in the calendar entry
|
|
676
|
-
if (
|
|
869
|
+
if (
|
|
870
|
+
reason.includes(ev.name) ||
|
|
871
|
+
(ignoreCaseInEventname && reason.toLowerCase().includes(ev.name.toLowerCase()))
|
|
872
|
+
) {
|
|
677
873
|
evFound = true;
|
|
678
874
|
}
|
|
679
875
|
}
|
|
@@ -689,25 +885,31 @@ async function checkForEvents(reason, event, realnow) {
|
|
|
689
885
|
// If full day event
|
|
690
886
|
// Follow processing only if event is today
|
|
691
887
|
if (
|
|
692
|
-
((!ev.type || ev.type === 'today') &&
|
|
888
|
+
((!ev.type || ev.type === 'today') &&
|
|
889
|
+
event.end.getTime() > inXDays.getTime() &&
|
|
890
|
+
event.start.getTime() < inXDaysPlusOne.getTime()) ||
|
|
693
891
|
(ev.type === 'now' && event.start <= realnow && realnow <= event.end) ||
|
|
694
892
|
(ev.type === 'later' && event.start > realnow && event.start.getTime() < tomorrow.getTime())
|
|
695
893
|
) {
|
|
696
|
-
adapter.log.debug(
|
|
894
|
+
adapter.log.debug(
|
|
895
|
+
`${ev.type ? ev.type : `day ${ev.day}`} Event with time: ${event.start} ${realnow} ${event.end}`,
|
|
896
|
+
);
|
|
697
897
|
|
|
698
898
|
// If yet processed
|
|
699
899
|
if (ev.processed) {
|
|
700
900
|
// nothing to do
|
|
701
901
|
adapter.log.debug(`Event ${ev.name} already processed`);
|
|
702
902
|
} else {
|
|
703
|
-
adapter.log.debug(
|
|
903
|
+
adapter.log.debug(
|
|
904
|
+
`Checking event ${ev.day} ${ev.type} ${ev.name} = ${ev.processed}, state = ${ev.state}`,
|
|
905
|
+
);
|
|
704
906
|
// Process event
|
|
705
907
|
ev.processed = true;
|
|
706
908
|
if (!ev.state) {
|
|
707
909
|
ev.state = true;
|
|
708
910
|
const name = `events.${ev.day}.${ev.type ? `${ev.type}.` : ''}${shrinkStateName(ev.name)}`;
|
|
709
911
|
adapter.log.info(`Set ${name} to true`);
|
|
710
|
-
await adapter.setStateAsync(name, {val: true, ack: true});
|
|
912
|
+
await adapter.setStateAsync(name, { val: true, ack: true });
|
|
711
913
|
if (ev.id) {
|
|
712
914
|
await setState(ev.id, ev.on, ev.ack);
|
|
713
915
|
}
|
|
@@ -723,10 +925,10 @@ function initEvent(name, display, day, type, id, on, off, ack, callback) {
|
|
|
723
925
|
const obj = {
|
|
724
926
|
name,
|
|
725
927
|
processed: false,
|
|
726
|
-
state:
|
|
928
|
+
state: null,
|
|
727
929
|
display,
|
|
728
930
|
day,
|
|
729
|
-
type
|
|
931
|
+
type,
|
|
730
932
|
};
|
|
731
933
|
|
|
732
934
|
if (type === 'now' && id) {
|
|
@@ -734,7 +936,8 @@ function initEvent(name, display, day, type, id, on, off, ack, callback) {
|
|
|
734
936
|
obj.off = off;
|
|
735
937
|
obj.on = on;
|
|
736
938
|
|
|
737
|
-
if (typeof ack !== 'boolean') {
|
|
939
|
+
if (typeof ack !== 'boolean') {
|
|
940
|
+
// backward compatibility
|
|
738
941
|
ack = true;
|
|
739
942
|
} else {
|
|
740
943
|
ack = !!ack;
|
|
@@ -750,7 +953,7 @@ function initEvent(name, display, day, type, id, on, off, ack, callback) {
|
|
|
750
953
|
adapter.getState(stateName, async (err, state) => {
|
|
751
954
|
if (err || !state) {
|
|
752
955
|
obj.state = false;
|
|
753
|
-
await adapter.setStateAsync(stateName, {val: false, ack: true});
|
|
956
|
+
await adapter.setStateAsync(stateName, { val: false, ack: true });
|
|
754
957
|
await setState(id, off, ack);
|
|
755
958
|
callback && callback(name);
|
|
756
959
|
} else {
|
|
@@ -780,14 +983,16 @@ function syncUserEvents(callback) {
|
|
|
780
983
|
|
|
781
984
|
// Read all actual events
|
|
782
985
|
adapter.getStatesOf('', 'events', async (err, states) => {
|
|
783
|
-
if (stopped)
|
|
986
|
+
if (stopped) {
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
784
989
|
const toAdd = [];
|
|
785
990
|
const toDel = [];
|
|
786
991
|
|
|
787
992
|
if (states) {
|
|
788
993
|
// Add "to delete" all existing events
|
|
789
994
|
for (let j = 0; j < states.length; j++) {
|
|
790
|
-
toDel.push({id: removeNameSpace(states[j]._id), name: states[j].common.name});
|
|
995
|
+
toDel.push({ id: removeNameSpace(states[j]._id), name: states[j].common.name });
|
|
791
996
|
}
|
|
792
997
|
}
|
|
793
998
|
|
|
@@ -796,11 +1001,11 @@ function syncUserEvents(callback) {
|
|
|
796
1001
|
for (let day = 0; day < days; day++) {
|
|
797
1002
|
const name = adapter.config.events[i].name;
|
|
798
1003
|
if (!day) {
|
|
799
|
-
toAdd.push({id: `events.${day}.later.${shrinkStateName(name)}`, name: name});
|
|
800
|
-
toAdd.push({id: `events.${day}.today.${shrinkStateName(name)}`, name: name});
|
|
801
|
-
toAdd.push({id: `events.${day}.now.${shrinkStateName(name)}`, name: name});
|
|
1004
|
+
toAdd.push({ id: `events.${day}.later.${shrinkStateName(name)}`, name: name });
|
|
1005
|
+
toAdd.push({ id: `events.${day}.today.${shrinkStateName(name)}`, name: name });
|
|
1006
|
+
toAdd.push({ id: `events.${day}.now.${shrinkStateName(name)}`, name: name });
|
|
802
1007
|
} else {
|
|
803
|
-
toAdd.push({id: `events.${day}.${shrinkStateName(name)}`, name: name});
|
|
1008
|
+
toAdd.push({ id: `events.${day}.${shrinkStateName(name)}`, name: name });
|
|
804
1009
|
}
|
|
805
1010
|
}
|
|
806
1011
|
}
|
|
@@ -843,13 +1048,13 @@ function syncUserEvents(callback) {
|
|
|
843
1048
|
for (let j = 0; j < states.length; j++) {
|
|
844
1049
|
const event = adapter.config.events[i];
|
|
845
1050
|
const name = shrinkStateName(event.name);
|
|
846
|
-
if (
|
|
1051
|
+
if (
|
|
1052
|
+
states[j].common.name === event.name &&
|
|
847
1053
|
((day > 0 && removeNameSpace(states[j]._id) === `events.${day}.${name}`) ||
|
|
848
|
-
(!day &&
|
|
849
|
-
removeNameSpace(states[j]._id) === `events.${day}.today.${name}` ||
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
)))
|
|
1054
|
+
(!day &&
|
|
1055
|
+
(removeNameSpace(states[j]._id) === `events.${day}.today.${name}` ||
|
|
1056
|
+
removeNameSpace(states[j]._id) === `events.${day}.now.${name}` ||
|
|
1057
|
+
removeNameSpace(states[j]._id) === `events.${day}.later.${name}`)))
|
|
853
1058
|
) {
|
|
854
1059
|
if (event.enabled === 'true') {
|
|
855
1060
|
event.enabled = true;
|
|
@@ -865,9 +1070,11 @@ function syncUserEvents(callback) {
|
|
|
865
1070
|
}
|
|
866
1071
|
|
|
867
1072
|
// if settings does not changed
|
|
868
|
-
if (
|
|
1073
|
+
if (
|
|
1074
|
+
states[j].native &&
|
|
869
1075
|
states[j].native.enabled === event.enabled &&
|
|
870
|
-
states[j].native.display === event.display
|
|
1076
|
+
states[j].native.display === event.display
|
|
1077
|
+
) {
|
|
871
1078
|
// remove it from "toAdd"
|
|
872
1079
|
removeFromToAdd(removeNameSpace(states[j]._id));
|
|
873
1080
|
}
|
|
@@ -882,13 +1089,13 @@ function syncUserEvents(callback) {
|
|
|
882
1089
|
for (let j = 0; j < adapter.config.events.length; j++) {
|
|
883
1090
|
const configItem = adapter.config.events[j];
|
|
884
1091
|
if (configItem.name === toAdd[i].name) {
|
|
885
|
-
if (configItem.enabled === 'true')
|
|
1092
|
+
if (configItem.enabled === 'true') {
|
|
886
1093
|
configItem.enabled = true;
|
|
887
1094
|
}
|
|
888
1095
|
if (configItem.enabled === 'false') {
|
|
889
1096
|
configItem.enabled = false;
|
|
890
1097
|
}
|
|
891
|
-
if (configItem.display === 'true')
|
|
1098
|
+
if (configItem.display === 'true') {
|
|
892
1099
|
configItem.display = true;
|
|
893
1100
|
}
|
|
894
1101
|
if (configItem.display === 'false') {
|
|
@@ -897,19 +1104,18 @@ function syncUserEvents(callback) {
|
|
|
897
1104
|
|
|
898
1105
|
// Add or update state
|
|
899
1106
|
try {
|
|
900
|
-
const id = await adapter.setObjectAsync(toAdd[i].id,
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
});
|
|
1107
|
+
const id = await adapter.setObjectAsync(toAdd[i].id, {
|
|
1108
|
+
type: 'state',
|
|
1109
|
+
common: {
|
|
1110
|
+
name: toAdd[i].name,
|
|
1111
|
+
type: 'boolean',
|
|
1112
|
+
role: 'indicator',
|
|
1113
|
+
},
|
|
1114
|
+
native: {
|
|
1115
|
+
enabled: configItem.enabled,
|
|
1116
|
+
display: configItem.display,
|
|
1117
|
+
},
|
|
1118
|
+
});
|
|
913
1119
|
adapter.log.info(`Event "${id.id}" created`);
|
|
914
1120
|
} catch (err) {
|
|
915
1121
|
adapter.log.warn(`Event "${toAdd[i].id}" could ne be created: ${err}`);
|
|
@@ -931,12 +1137,52 @@ function syncUserEvents(callback) {
|
|
|
931
1137
|
if (event.enabled) {
|
|
932
1138
|
if (!day) {
|
|
933
1139
|
count += 3;
|
|
934
|
-
initEvent(
|
|
935
|
-
|
|
936
|
-
|
|
1140
|
+
initEvent(
|
|
1141
|
+
event.name,
|
|
1142
|
+
event.display,
|
|
1143
|
+
0,
|
|
1144
|
+
'today',
|
|
1145
|
+
null,
|
|
1146
|
+
null,
|
|
1147
|
+
null,
|
|
1148
|
+
null,
|
|
1149
|
+
() => !--count && callback(),
|
|
1150
|
+
);
|
|
1151
|
+
initEvent(
|
|
1152
|
+
event.name,
|
|
1153
|
+
event.display,
|
|
1154
|
+
0,
|
|
1155
|
+
'now',
|
|
1156
|
+
event.id,
|
|
1157
|
+
event.on,
|
|
1158
|
+
event.off,
|
|
1159
|
+
event.ack,
|
|
1160
|
+
() => !--count && callback(),
|
|
1161
|
+
);
|
|
1162
|
+
initEvent(
|
|
1163
|
+
event.name,
|
|
1164
|
+
event.display,
|
|
1165
|
+
0,
|
|
1166
|
+
'later',
|
|
1167
|
+
null,
|
|
1168
|
+
null,
|
|
1169
|
+
null,
|
|
1170
|
+
null,
|
|
1171
|
+
() => !--count && callback(),
|
|
1172
|
+
);
|
|
937
1173
|
} else {
|
|
938
1174
|
count++;
|
|
939
|
-
initEvent(
|
|
1175
|
+
initEvent(
|
|
1176
|
+
event.name,
|
|
1177
|
+
event.display,
|
|
1178
|
+
day,
|
|
1179
|
+
null,
|
|
1180
|
+
null,
|
|
1181
|
+
null,
|
|
1182
|
+
null,
|
|
1183
|
+
null,
|
|
1184
|
+
() => !--count && callback(),
|
|
1185
|
+
);
|
|
940
1186
|
}
|
|
941
1187
|
}
|
|
942
1188
|
}
|
|
@@ -955,15 +1201,15 @@ function buildFilter(filter, filterregex) {
|
|
|
955
1201
|
const list = (filter || '').split(';');
|
|
956
1202
|
for (let i = 0; i < list.length; i++) {
|
|
957
1203
|
const item = list[i].trim();
|
|
958
|
-
if(!item) {
|
|
1204
|
+
if (!item) {
|
|
959
1205
|
continue;
|
|
960
1206
|
}
|
|
961
|
-
if(prep) {
|
|
1207
|
+
if (prep) {
|
|
962
1208
|
prep += '|';
|
|
963
1209
|
}
|
|
964
1210
|
prep += `(${item})`;
|
|
965
1211
|
}
|
|
966
|
-
if(prep) {
|
|
1212
|
+
if (prep) {
|
|
967
1213
|
prep = `/${prep}/g`;
|
|
968
1214
|
}
|
|
969
1215
|
}
|
|
@@ -973,7 +1219,7 @@ function buildFilter(filter, filterregex) {
|
|
|
973
1219
|
const s = prep.split('/');
|
|
974
1220
|
ret = new RegExp(s[1], s[2]);
|
|
975
1221
|
} catch (e) {
|
|
976
|
-
adapter.log.error(`invalid filter
|
|
1222
|
+
adapter.log.error(`invalid filter ${prep}: ${e}`);
|
|
977
1223
|
}
|
|
978
1224
|
}
|
|
979
1225
|
|
|
@@ -996,14 +1242,17 @@ function readAll() {
|
|
|
996
1242
|
for (let i = 0; i < adapter.config.calendars.length; i++) {
|
|
997
1243
|
if (adapter.config.calendars[i].url) {
|
|
998
1244
|
count++;
|
|
999
|
-
adapter.log.debug(
|
|
1245
|
+
adapter.log.debug(
|
|
1246
|
+
`reading calendar from URL: ${adapter.config.calendars[i].url}, color: ${adapter.config.calendars[i].color}`,
|
|
1247
|
+
);
|
|
1000
1248
|
checkICal(
|
|
1001
1249
|
adapter.config.calendars[i].url,
|
|
1002
1250
|
adapter.config.calendars[i].user,
|
|
1003
1251
|
adapter.config.calendars[i].pass,
|
|
1004
1252
|
adapter.config.calendars[i].sslignore,
|
|
1005
1253
|
adapter.config.calendars[i].name,
|
|
1006
|
-
buildFilter(adapter.config.calendars[i].filter, adapter.config.calendars[i].filterregex),
|
|
1254
|
+
buildFilter(adapter.config.calendars[i].filter, adapter.config.calendars[i].filterregex),
|
|
1255
|
+
err => {
|
|
1007
1256
|
if (err) {
|
|
1008
1257
|
errCnt++;
|
|
1009
1258
|
}
|
|
@@ -1021,7 +1270,8 @@ function readAll() {
|
|
|
1021
1270
|
adapter.log.debug('displaying dates because of callback');
|
|
1022
1271
|
displayDates();
|
|
1023
1272
|
}
|
|
1024
|
-
}
|
|
1273
|
+
},
|
|
1274
|
+
);
|
|
1025
1275
|
}
|
|
1026
1276
|
}
|
|
1027
1277
|
}
|
|
@@ -1034,9 +1284,10 @@ function readAll() {
|
|
|
1034
1284
|
}
|
|
1035
1285
|
|
|
1036
1286
|
// Read one calendar
|
|
1287
|
+
/*
|
|
1037
1288
|
function readOne(url) {
|
|
1038
1289
|
datesArray = [];
|
|
1039
|
-
checkICal(url,
|
|
1290
|
+
checkICal(url, err => {
|
|
1040
1291
|
if (err) {
|
|
1041
1292
|
adapter.log.info('Calender could not be processed, Do not clean up events.');
|
|
1042
1293
|
killTimeout && clearTimeout(killTimeout);
|
|
@@ -1049,22 +1300,23 @@ function readOne(url) {
|
|
|
1049
1300
|
displayDates();
|
|
1050
1301
|
});
|
|
1051
1302
|
}
|
|
1303
|
+
*/
|
|
1052
1304
|
|
|
1053
1305
|
function formatDate(_date, _end, withTime, fullDay) {
|
|
1054
|
-
let day
|
|
1306
|
+
let day = _date.getDate();
|
|
1055
1307
|
let month = _date.getMonth() + 1;
|
|
1056
|
-
let year
|
|
1308
|
+
let year = _date.getFullYear();
|
|
1057
1309
|
|
|
1058
|
-
const endday
|
|
1310
|
+
const endday = _end.getDate();
|
|
1059
1311
|
const endmonth = _end.getMonth() + 1;
|
|
1060
|
-
const endyear
|
|
1312
|
+
const endyear = _end.getFullYear();
|
|
1061
1313
|
let _time = '';
|
|
1062
1314
|
const now = new Date();
|
|
1063
1315
|
const alreadyStarted = _date < now && _end > now;
|
|
1064
1316
|
const arrowAlreadyStarted = adapter.config.arrowAlreadyStarted;
|
|
1065
1317
|
|
|
1066
1318
|
if (withTime) {
|
|
1067
|
-
let hours
|
|
1319
|
+
let hours = _date.getHours();
|
|
1068
1320
|
let minutes = _date.getMinutes();
|
|
1069
1321
|
|
|
1070
1322
|
if (adapter.config.fulltime && fullDay) {
|
|
@@ -1073,7 +1325,7 @@ function formatDate(_date, _end, withTime, fullDay) {
|
|
|
1073
1325
|
if (!alreadyStarted) {
|
|
1074
1326
|
if (adapter.config.dataPaddingWithZeros) {
|
|
1075
1327
|
if (hours < 10) {
|
|
1076
|
-
hours
|
|
1328
|
+
hours = `0${hours.toString()}`;
|
|
1077
1329
|
}
|
|
1078
1330
|
}
|
|
1079
1331
|
if (minutes < 10) {
|
|
@@ -1118,7 +1370,9 @@ function formatDate(_date, _end, withTime, fullDay) {
|
|
|
1118
1370
|
start.setHours(0, 0, 1, 0);
|
|
1119
1371
|
const fullTimeDiff = timeDiff;
|
|
1120
1372
|
timeDiff = treatAsUTC(_end.getTime()) - treatAsUTC(start.getTime());
|
|
1121
|
-
adapter.log.debug(
|
|
1373
|
+
adapter.log.debug(
|
|
1374
|
+
` time difference: ${timeDiff} (${_date}-${_end} / ${start}) --> ${timeDiff / (24 * 60 * 60 * 1000)}`,
|
|
1375
|
+
);
|
|
1122
1376
|
if (fullTimeDiff >= 24 * 60 * 60 * 1000) {
|
|
1123
1377
|
_time += `+${Math.floor(timeDiff / (24 * 60 * 60 * 1000))}`;
|
|
1124
1378
|
}
|
|
@@ -1130,17 +1384,19 @@ function formatDate(_date, _end, withTime, fullDay) {
|
|
|
1130
1384
|
}
|
|
1131
1385
|
let _class = '';
|
|
1132
1386
|
const d = new Date();
|
|
1133
|
-
d.setHours(0,0,0,0);
|
|
1387
|
+
d.setHours(0, 0, 0, 0);
|
|
1134
1388
|
const d2 = new Date();
|
|
1135
1389
|
d2.setDate(d.getDate() + 1);
|
|
1136
1390
|
let todayOnly = false;
|
|
1137
|
-
if (
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1391
|
+
if (
|
|
1392
|
+
day === d.getDate() &&
|
|
1393
|
+
month === d.getMonth() + 1 &&
|
|
1394
|
+
year === d.getFullYear() &&
|
|
1395
|
+
endday === d2.getDate() &&
|
|
1396
|
+
endmonth === d2.getMonth() + 1 &&
|
|
1397
|
+
endyear === d2.getFullYear() &&
|
|
1398
|
+
fullDay
|
|
1399
|
+
) {
|
|
1144
1400
|
todayOnly = true;
|
|
1145
1401
|
}
|
|
1146
1402
|
adapter.log.debug(` todayOnly = ${todayOnly}: (${_date}-${_end}), alreadyStarted=${alreadyStarted}`);
|
|
@@ -1150,91 +1406,99 @@ function formatDate(_date, _end, withTime, fullDay) {
|
|
|
1150
1406
|
_class = 'ical_today';
|
|
1151
1407
|
}
|
|
1152
1408
|
|
|
1153
|
-
if (day
|
|
1154
|
-
month === (d.getMonth() + 1) &&
|
|
1155
|
-
year === d.getFullYear()) {
|
|
1409
|
+
if (day === d.getDate() && month === d.getMonth() + 1 && year === d.getFullYear()) {
|
|
1156
1410
|
_class = 'ical_today';
|
|
1157
1411
|
}
|
|
1158
1412
|
|
|
1159
1413
|
d.setDate(d.getDate() + 1);
|
|
1160
|
-
if (day
|
|
1161
|
-
month === (d.getMonth() + 1) &&
|
|
1162
|
-
year === d.getFullYear()) {
|
|
1414
|
+
if (day === d.getDate() && month === d.getMonth() + 1 && year === d.getFullYear()) {
|
|
1163
1415
|
_class = 'ical_tomorrow';
|
|
1164
1416
|
}
|
|
1165
1417
|
|
|
1166
1418
|
d.setDate(d.getDate() + 1);
|
|
1167
|
-
if (day
|
|
1168
|
-
month === (d.getMonth() + 1) &&
|
|
1169
|
-
year === d.getFullYear()) {
|
|
1419
|
+
if (day === d.getDate() && month === d.getMonth() + 1 && year === d.getFullYear()) {
|
|
1170
1420
|
_class = 'ical_dayafter';
|
|
1171
1421
|
}
|
|
1172
1422
|
|
|
1173
1423
|
d.setDate(d.getDate() + 1);
|
|
1174
|
-
if (day
|
|
1175
|
-
month === (d.getMonth() + 1) &&
|
|
1176
|
-
year === d.getFullYear()) {
|
|
1424
|
+
if (day === d.getDate() && month === d.getMonth() + 1 && year === d.getFullYear()) {
|
|
1177
1425
|
_class = 'ical_3days';
|
|
1178
1426
|
}
|
|
1179
1427
|
|
|
1180
1428
|
d.setDate(d.getDate() + 1);
|
|
1181
|
-
if (day
|
|
1182
|
-
month === (d.getMonth() + 1) &&
|
|
1183
|
-
year === d.getFullYear()) {
|
|
1429
|
+
if (day === d.getDate() && month === d.getMonth() + 1 && year === d.getFullYear()) {
|
|
1184
1430
|
_class = 'ical_4days';
|
|
1185
1431
|
}
|
|
1186
1432
|
|
|
1187
1433
|
d.setDate(d.getDate() + 1);
|
|
1188
|
-
if (day
|
|
1189
|
-
month === (d.getMonth() + 1) &&
|
|
1190
|
-
year === d.getFullYear()) {
|
|
1434
|
+
if (day === d.getDate() && month === d.getMonth() + 1 && year === d.getFullYear()) {
|
|
1191
1435
|
_class = 'ical_5days';
|
|
1192
1436
|
}
|
|
1193
1437
|
|
|
1194
1438
|
d.setDate(d.getDate() + 1);
|
|
1195
|
-
if (day
|
|
1196
|
-
month === (d.getMonth() + 1) &&
|
|
1197
|
-
year === d.getFullYear()) {
|
|
1439
|
+
if (day === d.getDate() && month === d.getMonth() + 1 && year === d.getFullYear()) {
|
|
1198
1440
|
_class = 'ical_6days';
|
|
1199
1441
|
}
|
|
1200
1442
|
|
|
1201
1443
|
d.setDate(d.getDate() + 1);
|
|
1202
|
-
if (day
|
|
1203
|
-
month === (d.getMonth() + 1) &&
|
|
1204
|
-
year === d.getFullYear()) {
|
|
1444
|
+
if (day === d.getDate() && month === d.getMonth() + 1 && year === d.getFullYear()) {
|
|
1205
1445
|
_class = 'ical_oneweek';
|
|
1206
1446
|
}
|
|
1207
1447
|
if (adapter.config.replaceDates) {
|
|
1208
|
-
if (_class === 'ical_today')
|
|
1209
|
-
return {
|
|
1448
|
+
if (_class === 'ical_today') {
|
|
1449
|
+
return {
|
|
1450
|
+
text: `${arrowAlreadyStarted && alreadyStarted && !todayOnly ? '→ ' : ''}${_('today')}${_time}`,
|
|
1451
|
+
_class: _class,
|
|
1452
|
+
};
|
|
1210
1453
|
}
|
|
1211
1454
|
if (_class === 'ical_tomorrow') {
|
|
1212
|
-
return {
|
|
1455
|
+
return {
|
|
1456
|
+
text: `${arrowAlreadyStarted && alreadyStarted ? '→ ' : ''}${_('tomorrow')}${_time}`,
|
|
1457
|
+
_class: _class,
|
|
1458
|
+
};
|
|
1213
1459
|
}
|
|
1214
1460
|
if (_class === 'ical_dayafter') {
|
|
1215
|
-
return {
|
|
1461
|
+
return {
|
|
1462
|
+
text: `${arrowAlreadyStarted && alreadyStarted ? '→ ' : ''}${_('dayafter')}${_time}`,
|
|
1463
|
+
_class: _class,
|
|
1464
|
+
};
|
|
1216
1465
|
}
|
|
1217
|
-
if (_class === 'ical_3days')
|
|
1218
|
-
return {
|
|
1466
|
+
if (_class === 'ical_3days') {
|
|
1467
|
+
return {
|
|
1468
|
+
text: `${arrowAlreadyStarted && alreadyStarted ? '→ ' : ''}${_('3days')}${_time}`,
|
|
1469
|
+
_class: _class,
|
|
1470
|
+
};
|
|
1219
1471
|
}
|
|
1220
|
-
if (_class === 'ical_4days')
|
|
1221
|
-
return {
|
|
1472
|
+
if (_class === 'ical_4days') {
|
|
1473
|
+
return {
|
|
1474
|
+
text: `${arrowAlreadyStarted && alreadyStarted ? '→ ' : ''}${_('4days')}${_time}`,
|
|
1475
|
+
_class: _class,
|
|
1476
|
+
};
|
|
1222
1477
|
}
|
|
1223
|
-
if (_class === 'ical_5days')
|
|
1224
|
-
return {
|
|
1478
|
+
if (_class === 'ical_5days') {
|
|
1479
|
+
return {
|
|
1480
|
+
text: `${arrowAlreadyStarted && alreadyStarted ? '→ ' : ''}${_('5days')}${_time}`,
|
|
1481
|
+
_class: _class,
|
|
1482
|
+
};
|
|
1225
1483
|
}
|
|
1226
|
-
if (_class === 'ical_6days')
|
|
1227
|
-
return {
|
|
1484
|
+
if (_class === 'ical_6days') {
|
|
1485
|
+
return {
|
|
1486
|
+
text: `${arrowAlreadyStarted && alreadyStarted ? '→ ' : ''}${_('6days')}${_time}`,
|
|
1487
|
+
_class: _class,
|
|
1488
|
+
};
|
|
1228
1489
|
}
|
|
1229
|
-
if (_class === 'ical_oneweek')
|
|
1230
|
-
return {
|
|
1490
|
+
if (_class === 'ical_oneweek') {
|
|
1491
|
+
return {
|
|
1492
|
+
text: `${arrowAlreadyStarted && alreadyStarted ? '→ ' : ''}${_('oneweek')}${_time}`,
|
|
1493
|
+
_class: _class,
|
|
1494
|
+
};
|
|
1231
1495
|
}
|
|
1232
1496
|
}
|
|
1233
1497
|
} else {
|
|
1234
1498
|
// check if date is in the past and if so we show the end time instead
|
|
1235
1499
|
_class = 'ical_today';
|
|
1236
|
-
const dateDiff =
|
|
1237
|
-
let daysleft = Math.round(
|
|
1500
|
+
const dateDiff = treatAsUTC(_end.getTime()) - treatAsUTC(Date.now());
|
|
1501
|
+
let daysleft = Math.round(dateDiff / (1000 * 60 * 60 * 24));
|
|
1238
1502
|
const hoursleft = Math.round(dateDiff / (1000 * 60 * 60));
|
|
1239
1503
|
const minutesleft = Math.round(dateDiff / (1000 * 60));
|
|
1240
1504
|
|
|
@@ -1245,7 +1509,7 @@ function formatDate(_date, _end, withTime, fullDay) {
|
|
|
1245
1509
|
|
|
1246
1510
|
let text;
|
|
1247
1511
|
if (adapter.config.replaceDates) {
|
|
1248
|
-
const _left =
|
|
1512
|
+
const _left = _('left') !== ' ' ? ` ${_('left')}` : '';
|
|
1249
1513
|
if (daysleft === 42) {
|
|
1250
1514
|
text = _('6week_left');
|
|
1251
1515
|
} else if (daysleft === 35) {
|
|
@@ -1297,9 +1561,9 @@ function formatDate(_date, _end, withTime, fullDay) {
|
|
|
1297
1561
|
if (_end.getHours() === 0 && _end.getMinutes() === 0 && _end.getSeconds() === 0 && fullDay) {
|
|
1298
1562
|
const secondBeforeEnd = new Date(_end.getTime());
|
|
1299
1563
|
secondBeforeEnd.setSeconds(secondBeforeEnd.getSeconds() - 1);
|
|
1300
|
-
day
|
|
1564
|
+
day = secondBeforeEnd.getDate();
|
|
1301
1565
|
month = secondBeforeEnd.getMonth() + 1;
|
|
1302
|
-
year
|
|
1566
|
+
year = secondBeforeEnd.getFullYear();
|
|
1303
1567
|
adapter.log.debug(`Adjust enddate to exclude 0:0:0 end: ${secondBeforeEnd.toString()}`);
|
|
1304
1568
|
} else {
|
|
1305
1569
|
day = _end.getDate();
|
|
@@ -1308,15 +1572,15 @@ function formatDate(_date, _end, withTime, fullDay) {
|
|
|
1308
1572
|
}
|
|
1309
1573
|
|
|
1310
1574
|
if (adapter.config.dataPaddingWithZeros) {
|
|
1311
|
-
if (day < 10)
|
|
1312
|
-
day
|
|
1575
|
+
if (day < 10) {
|
|
1576
|
+
day = `0${day.toString()}`;
|
|
1313
1577
|
}
|
|
1314
1578
|
if (month < 10) {
|
|
1315
1579
|
month = `0${month.toString()}`;
|
|
1316
1580
|
}
|
|
1317
1581
|
}
|
|
1318
1582
|
|
|
1319
|
-
text = `${
|
|
1583
|
+
text = `${arrowAlreadyStarted ? '→ ' : ''}${day}.${month}.`;
|
|
1320
1584
|
if (!adapter.config.hideYear) {
|
|
1321
1585
|
text += year;
|
|
1322
1586
|
}
|
|
@@ -1325,11 +1589,11 @@ function formatDate(_date, _end, withTime, fullDay) {
|
|
|
1325
1589
|
if (adapter.config.fulltime && fullDay) {
|
|
1326
1590
|
text += ` ${adapter.config.fulltime}`;
|
|
1327
1591
|
} else {
|
|
1328
|
-
let endhours
|
|
1592
|
+
let endhours = _end.getHours();
|
|
1329
1593
|
let endminutes = _end.getMinutes();
|
|
1330
1594
|
if (adapter.config.dataPaddingWithZeros) {
|
|
1331
|
-
if (endhours < 10)
|
|
1332
|
-
endhours
|
|
1595
|
+
if (endhours < 10) {
|
|
1596
|
+
endhours = `0${endhours.toString()}`;
|
|
1333
1597
|
}
|
|
1334
1598
|
}
|
|
1335
1599
|
if (endminutes < 10) {
|
|
@@ -1340,12 +1604,12 @@ function formatDate(_date, _end, withTime, fullDay) {
|
|
|
1340
1604
|
}
|
|
1341
1605
|
}
|
|
1342
1606
|
|
|
1343
|
-
return {text: text, _class: _class};
|
|
1607
|
+
return { text: text, _class: _class };
|
|
1344
1608
|
}
|
|
1345
1609
|
|
|
1346
1610
|
if (adapter.config.dataPaddingWithZeros) {
|
|
1347
|
-
if (day < 10)
|
|
1348
|
-
day
|
|
1611
|
+
if (day < 10) {
|
|
1612
|
+
day = `0${day.toString()}`;
|
|
1349
1613
|
}
|
|
1350
1614
|
if (month < 10) {
|
|
1351
1615
|
month = `0${month.toString()}`;
|
|
@@ -1353,8 +1617,8 @@ function formatDate(_date, _end, withTime, fullDay) {
|
|
|
1353
1617
|
}
|
|
1354
1618
|
|
|
1355
1619
|
return {
|
|
1356
|
-
text:
|
|
1357
|
-
_class: _class
|
|
1620
|
+
text: `${day}.${month}${adapter.config.hideYear ? '.' : `.${year}`}${_time}`,
|
|
1621
|
+
_class: _class,
|
|
1358
1622
|
};
|
|
1359
1623
|
}
|
|
1360
1624
|
|
|
@@ -1390,7 +1654,9 @@ async function setState(id, val, ack, cb) {
|
|
|
1390
1654
|
|
|
1391
1655
|
// Show event as text
|
|
1392
1656
|
async function displayDates() {
|
|
1393
|
-
if (stopped)
|
|
1657
|
+
if (stopped) {
|
|
1658
|
+
return;
|
|
1659
|
+
}
|
|
1394
1660
|
|
|
1395
1661
|
let todayEventCounter = 0;
|
|
1396
1662
|
let tomorrowEventCounter = 0;
|
|
@@ -1415,7 +1681,10 @@ async function displayDates() {
|
|
|
1415
1681
|
adapter.log.debug(`displayDates: TODAY - ${datesArray[t].event} (${datesArray[t]._date})`);
|
|
1416
1682
|
todayEventCounter++;
|
|
1417
1683
|
}
|
|
1418
|
-
if (
|
|
1684
|
+
if (
|
|
1685
|
+
datesArray[t]._end.getTime() > tomorrow.getTime() &&
|
|
1686
|
+
datesArray[t]._date.getTime() < dayAfterTomorrow.getTime()
|
|
1687
|
+
) {
|
|
1419
1688
|
adapter.log.debug(`displayDates: TOMORROW - ${datesArray[t].event} (${datesArray[t]._date})`);
|
|
1420
1689
|
tomorrowEventCounter++;
|
|
1421
1690
|
}
|
|
@@ -1427,29 +1696,31 @@ async function displayDates() {
|
|
|
1427
1696
|
|
|
1428
1697
|
adapter.log.debug(`Dates array (data.table): ${JSON.stringify(datesArray)}`);
|
|
1429
1698
|
|
|
1430
|
-
await adapter.setStateAsync('data.table', {val: JSON.stringify(datesArray), ack: true});
|
|
1431
|
-
await adapter.setStateAsync('data.html',
|
|
1432
|
-
await adapter.setStateAsync('data.text',
|
|
1699
|
+
await adapter.setStateAsync('data.table', { val: JSON.stringify(datesArray), ack: true });
|
|
1700
|
+
await adapter.setStateAsync('data.html', { val: brSeparatedList(datesArray), ack: true });
|
|
1701
|
+
await adapter.setStateAsync('data.text', { val: crlfSeparatedList(datesArray), ack: true });
|
|
1433
1702
|
} else {
|
|
1434
|
-
await adapter.setStateAsync('data.table', {val: '[]', ack: true});
|
|
1435
|
-
await adapter.setStateAsync('data.html',
|
|
1436
|
-
await adapter.setStateAsync('data.text',
|
|
1703
|
+
await adapter.setStateAsync('data.table', { val: '[]', ack: true });
|
|
1704
|
+
await adapter.setStateAsync('data.html', { val: '', ack: true });
|
|
1705
|
+
await adapter.setStateAsync('data.text', { val: '', ack: true });
|
|
1437
1706
|
}
|
|
1438
1707
|
|
|
1439
|
-
await adapter.setStateAsync('data.count', {val: todayEventCounter, ack: true});
|
|
1440
|
-
await adapter.setStateAsync('data.countTomorrow', {val: tomorrowEventCounter, ack: true});
|
|
1441
|
-
await adapter.setStateAsync('data.countYesterday', {val: yesterdayEventCounter, ack: true});
|
|
1708
|
+
await adapter.setStateAsync('data.count', { val: todayEventCounter, ack: true });
|
|
1709
|
+
await adapter.setStateAsync('data.countTomorrow', { val: tomorrowEventCounter, ack: true });
|
|
1710
|
+
await adapter.setStateAsync('data.countYesterday', { val: yesterdayEventCounter, ack: true });
|
|
1442
1711
|
|
|
1443
1712
|
// set not processed events to false
|
|
1444
1713
|
for (let j = 0; j < events.length; j++) {
|
|
1445
|
-
adapter.log.debug(
|
|
1714
|
+
adapter.log.debug(
|
|
1715
|
+
`Checking unprocessed event ${events[j].day} ${events[j].type} ${events[j].name} = ${events[j].processed}, state = ${events[j].state}`,
|
|
1716
|
+
);
|
|
1446
1717
|
if (!events[j].processed && events[j].state) {
|
|
1447
1718
|
const ev = events[j];
|
|
1448
1719
|
ev.state = false;
|
|
1449
1720
|
// Set to false
|
|
1450
1721
|
const name = `events.${ev.day}.${ev.type ? `${ev.type}.` : ''}${shrinkStateName(ev.name)}`;
|
|
1451
1722
|
adapter.log.info(`Set ${name} to false`);
|
|
1452
|
-
await adapter.setStateAsync(name, {val: false, ack: true});
|
|
1723
|
+
await adapter.setStateAsync(name, { val: false, ack: true });
|
|
1453
1724
|
await setState(ev.id, ev.off, ev.ack);
|
|
1454
1725
|
}
|
|
1455
1726
|
}
|
|
@@ -1473,7 +1744,7 @@ function insertSorted(arr, element) {
|
|
|
1473
1744
|
if (arr.length === 1) {
|
|
1474
1745
|
arr.push(element);
|
|
1475
1746
|
} else {
|
|
1476
|
-
for (let i = 0; i < arr.length - 1; i++){
|
|
1747
|
+
for (let i = 0; i < arr.length - 1; i++) {
|
|
1477
1748
|
if (arr[i]._date <= element._date && element._date < arr[i + 1]._date) {
|
|
1478
1749
|
arr.splice(i + 1, 0, element);
|
|
1479
1750
|
element = null;
|
|
@@ -1489,8 +1760,8 @@ function insertSorted(arr, element) {
|
|
|
1489
1760
|
}
|
|
1490
1761
|
|
|
1491
1762
|
function brSeparatedList(datesArray) {
|
|
1492
|
-
let text
|
|
1493
|
-
const today
|
|
1763
|
+
let text = '';
|
|
1764
|
+
const today = new Date();
|
|
1494
1765
|
const tomorrow = new Date();
|
|
1495
1766
|
const dayAfter = new Date();
|
|
1496
1767
|
today.setHours(0, 0, 0, 0);
|
|
@@ -1533,8 +1804,8 @@ function brSeparatedList(datesArray) {
|
|
|
1533
1804
|
}
|
|
1534
1805
|
|
|
1535
1806
|
function crlfSeparatedList(datesArray) {
|
|
1536
|
-
let text
|
|
1537
|
-
const today
|
|
1807
|
+
let text = '';
|
|
1808
|
+
const today = new Date();
|
|
1538
1809
|
const tomorrow = new Date();
|
|
1539
1810
|
const dayafter = new Date();
|
|
1540
1811
|
today.setHours(0, 0, 0, 0);
|
|
@@ -1545,14 +1816,16 @@ function crlfSeparatedList(datesArray) {
|
|
|
1545
1816
|
|
|
1546
1817
|
for (let i = 0; i < datesArray.length; i++) {
|
|
1547
1818
|
const date = formatDate(datesArray[i]._date, datesArray[i]._end, true, datesArray[i]._allDay);
|
|
1819
|
+
// TODO why doing all this stuff about color and then its unused?
|
|
1820
|
+
/*
|
|
1548
1821
|
let color = adapter.config.defColor;
|
|
1549
1822
|
for (let j = 0; j < adapter.config.calendars.length; j++) {
|
|
1550
|
-
// TODO why doing all this stuff and then its unused?
|
|
1551
1823
|
if (adapter.config.calendars[j].name === datesArray[i]._calName) {
|
|
1552
1824
|
color = adapter.config.calendars[j].color;
|
|
1553
1825
|
break;
|
|
1554
1826
|
}
|
|
1555
1827
|
}
|
|
1828
|
+
*/
|
|
1556
1829
|
|
|
1557
1830
|
if (text) {
|
|
1558
1831
|
text += '\n';
|
|
@@ -1564,18 +1837,17 @@ function crlfSeparatedList(datesArray) {
|
|
|
1564
1837
|
}
|
|
1565
1838
|
|
|
1566
1839
|
function main() {
|
|
1567
|
-
normal
|
|
1840
|
+
normal = `<span style="font-weight: bold${adapter.config.defColor ? `; color: ${adapter.config.defColor}` : ''}"><span class="icalNormal">`;
|
|
1568
1841
|
|
|
1569
1842
|
adapter.config.language = adapter.config.language || 'en';
|
|
1570
1843
|
adapter.config.daysPast = parseInt(adapter.config.daysPast) || 0;
|
|
1571
1844
|
|
|
1572
1845
|
const helpFilePath = '/UPLOAD_FILES_HERE.txt';
|
|
1573
|
-
adapter.fileExistsAsync(adapter.namespace, helpFilePath)
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
});
|
|
1846
|
+
adapter.fileExistsAsync(adapter.namespace, helpFilePath).then(fileExists => {
|
|
1847
|
+
if (!fileExists) {
|
|
1848
|
+
adapter.writeFileAsync(adapter.namespace, helpFilePath, 'Place your *.ics files in this directory');
|
|
1849
|
+
}
|
|
1850
|
+
});
|
|
1579
1851
|
|
|
1580
1852
|
adapter.delObjectAsync('trigger'); // removed deprecated subscribe state (created in previous versions)
|
|
1581
1853
|
|