iobroker.device-watcher 2.15.3 → 2.15.11
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/README.md +16 -21
- package/admin/jsonConfig.json5 +1 -1
- package/io-package.json +92 -53
- package/lib/adapterArray.js +14 -10
- package/lib/crud.js +96 -9
- package/lib/tools.js +20 -4
- package/lib/translations.js +1 -1
- package/main.js +109 -44
- package/package.json +9 -11
package/README.md
CHANGED
|
@@ -191,27 +191,22 @@ This adapter would not have been possible without the great work of Christian Be
|
|
|
191
191
|
Placeholder for the next version (at the beginning of the line):
|
|
192
192
|
### **WORK IN PROGRESS**
|
|
193
193
|
-->
|
|
194
|
-
### 2.15.
|
|
195
|
-
* (arteck)
|
|
196
|
-
|
|
197
|
-
### 2.15.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
* (arteck)
|
|
203
|
-
|
|
204
|
-
### 2.15.
|
|
205
|
-
* (arteck)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
* (arteck)
|
|
209
|
-
* (arteck)
|
|
210
|
-
* (arteck) fixed all count devices
|
|
211
|
-
* (arteck) corrected finder
|
|
212
|
-
|
|
213
|
-
### 2.14.1 (2025-11-13)
|
|
214
|
-
* (arteck) corr cronparser
|
|
194
|
+
### 2.15.11 (2026-05-06)
|
|
195
|
+
* (arteck)
|
|
196
|
+
|
|
197
|
+
### 2.15.10 (2026-05-06)
|
|
198
|
+
- (copilot) Adapter requires node.js >= 22 now
|
|
199
|
+
* (arteck) fix adapter crash after delete a device
|
|
200
|
+
|
|
201
|
+
### 2.15.9 (2026-04-22)
|
|
202
|
+
* (arteck) new xsense (v. 0.4.0) structure, plz update before
|
|
203
|
+
|
|
204
|
+
### 2.15.8 (2026-04-18)
|
|
205
|
+
* (arteck) fix cronParserLib.parseExpression message
|
|
206
|
+
|
|
207
|
+
### 2.15.7 (2026-04-18)
|
|
208
|
+
* (arteck) fix matter
|
|
209
|
+
* (arteck) fix cronParserLib.parseExpression message
|
|
215
210
|
|
|
216
211
|
## License
|
|
217
212
|
|
package/admin/jsonConfig.json5
CHANGED
package/io-package.json
CHANGED
|
@@ -1,59 +1,98 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "device-watcher",
|
|
4
|
-
"version": "2.15.
|
|
4
|
+
"version": "2.15.11",
|
|
5
5
|
"news": {
|
|
6
|
-
"2.15.
|
|
7
|
-
"en": "
|
|
8
|
-
"de": "
|
|
9
|
-
"ru": "
|
|
10
|
-
"pt": "
|
|
11
|
-
"nl": "
|
|
12
|
-
"fr": "
|
|
13
|
-
"it": "
|
|
14
|
-
"es": "
|
|
15
|
-
"pl": "
|
|
16
|
-
"uk": "
|
|
17
|
-
"zh-cn": "
|
|
18
|
-
},
|
|
19
|
-
"2.15.
|
|
20
|
-
"en": "
|
|
21
|
-
"de": "
|
|
22
|
-
"ru": "
|
|
23
|
-
"pt": "
|
|
24
|
-
"nl": "
|
|
25
|
-
"fr": "
|
|
26
|
-
"it": "
|
|
27
|
-
"es": "
|
|
28
|
-
"pl": "
|
|
29
|
-
"uk": "
|
|
30
|
-
"zh-cn": "
|
|
31
|
-
},
|
|
32
|
-
"2.15.
|
|
33
|
-
"en": "
|
|
34
|
-
"de": "
|
|
35
|
-
"ru": "
|
|
36
|
-
"pt": "
|
|
37
|
-
"nl": "
|
|
38
|
-
"fr": "
|
|
39
|
-
"it": "
|
|
40
|
-
"es": "
|
|
41
|
-
"pl": "
|
|
42
|
-
"uk": "
|
|
43
|
-
"zh-cn": "
|
|
44
|
-
},
|
|
45
|
-
"2.15.
|
|
46
|
-
"en": "
|
|
47
|
-
"de": "
|
|
48
|
-
"ru": "
|
|
49
|
-
"pt": "
|
|
50
|
-
"nl": "
|
|
51
|
-
"fr": "
|
|
52
|
-
"it": "
|
|
53
|
-
"es": "
|
|
54
|
-
"pl": "
|
|
55
|
-
"uk": "
|
|
56
|
-
"zh-cn": "
|
|
6
|
+
"2.15.11": {
|
|
7
|
+
"en": "",
|
|
8
|
+
"de": "",
|
|
9
|
+
"ru": "",
|
|
10
|
+
"pt": "",
|
|
11
|
+
"nl": "",
|
|
12
|
+
"fr": "",
|
|
13
|
+
"it": "",
|
|
14
|
+
"es": "",
|
|
15
|
+
"pl": "",
|
|
16
|
+
"uk": "",
|
|
17
|
+
"zh-cn": ""
|
|
18
|
+
},
|
|
19
|
+
"2.15.10": {
|
|
20
|
+
"en": "Adapter requires node.js >= 22 now\nfix adapter crash after delete a device",
|
|
21
|
+
"de": "Adapter benötigt node.js >= 22 jetzt\nfix adapter crash nach dem löschen eines geräts",
|
|
22
|
+
"ru": "Адаптер требует node.js >= 22 сейчас\nисправить сбой адаптера после удаления устройства",
|
|
23
|
+
"pt": "Adaptador requer nod.js >= 22 agora\ncorrigir falha do adaptador após apagar um dispositivo",
|
|
24
|
+
"nl": "Voor de adapter zijn node.js < 22 nu nodig\nfix adapter crash na het verwijderen van een apparaat",
|
|
25
|
+
"fr": "Adaptateur nécessite node.js >= 22 maintenant\ncorrection du crash de l'adaptateur après suppression d'un périphérique",
|
|
26
|
+
"it": "Adattatore richiede node.js >= 22 ora\nfissare l'arresto dell'adattatore dopo eliminare un dispositivo",
|
|
27
|
+
"es": "Adaptador requiere node.js ю= 22 ahora\najuste de bloqueo del adaptador después de eliminar un dispositivo",
|
|
28
|
+
"pl": "Adapter wymaga node.js > = 22\nnaprawić awarię adaptera po usunięciu urządzenia",
|
|
29
|
+
"uk": "Адаптер вимагає node.js >= 22 тепер\nвиправлено аварійний перехід після видалення пристрою",
|
|
30
|
+
"zh-cn": "适配器需要节点.js 现在22\n删除设备后修复适配器崩溃"
|
|
31
|
+
},
|
|
32
|
+
"2.15.9": {
|
|
33
|
+
"en": "new xsense (v. 0.4.0) structure, plz update before",
|
|
34
|
+
"de": "neue xsense (v. 0.4.0) struktur, plz update vor",
|
|
35
|
+
"ru": "новая структура xsense (v. 0.4.0), обновление plz до",
|
|
36
|
+
"pt": "nova estrutura xsense (v. 0.4.0), atualização plz antes",
|
|
37
|
+
"nl": "nieuwe xsense (v. 0.4.0) structuur, plz update voor",
|
|
38
|
+
"fr": "nouvelle structure xsense (v. 0.4.0), mise à jour plz",
|
|
39
|
+
"it": "nuova struttura xsense (v. 0.4.0), aggiornamento plz prima",
|
|
40
|
+
"es": "nueva estructura xsense (v. 0.4.0)",
|
|
41
|
+
"pl": "nowa struktura xsense (v. 0.4.0), aktualizacja plz przed",
|
|
42
|
+
"uk": "новий xsense (v. 0.4.0) структура, оновлення plz перед",
|
|
43
|
+
"zh-cn": "新的 xsense (v. 04. 0) 结构, plz 更新前"
|
|
44
|
+
},
|
|
45
|
+
"2.15.8": {
|
|
46
|
+
"en": "fix cronParserLib.parseExpression message",
|
|
47
|
+
"de": "cronParserLib.parseExpressionsnachricht",
|
|
48
|
+
"ru": "исправить сообщение cronParserLib.parse",
|
|
49
|
+
"pt": "corrigir cronParserLib.parseExpression message",
|
|
50
|
+
"nl": "fix cronParserLib.parseExpressiebericht",
|
|
51
|
+
"fr": "correction du message cronParserLib.parseExpression",
|
|
52
|
+
"it": "correzione cronParserLib.parseExpression messaggio",
|
|
53
|
+
"es": "fijar cronParserLib.parse",
|
|
54
|
+
"pl": "naprawić wiadomość cronParserLib.parseExpression",
|
|
55
|
+
"uk": "виправлено cronParserLib.parseExpression повідомлення",
|
|
56
|
+
"zh-cn": "修补 cronParserLib.parsepression 信件"
|
|
57
|
+
},
|
|
58
|
+
"2.15.7": {
|
|
59
|
+
"en": "fix matter \n",
|
|
60
|
+
"de": "fixkosten\n",
|
|
61
|
+
"ru": "исправлять\n",
|
|
62
|
+
"pt": "corrigir a matéria\n",
|
|
63
|
+
"nl": "fix materie\n",
|
|
64
|
+
"fr": "fixer la matière\n",
|
|
65
|
+
"it": "risolvere la questione\n",
|
|
66
|
+
"es": "arregla la materia\n",
|
|
67
|
+
"pl": "materia stabilna\n",
|
|
68
|
+
"uk": "фіксувати матерію\n",
|
|
69
|
+
"zh-cn": "固定事项\n"
|
|
70
|
+
},
|
|
71
|
+
"2.15.6": {
|
|
72
|
+
"en": "Adapter requires admin >= 7.7.22 now\nfix instanz restart\nfix cronParserLib.parseExpression message\nfix group in zigbee2mqtt",
|
|
73
|
+
"de": "Adapter benötigt admin >= 7.7.22 jetzt\ninstanz restart\ncronParserLib.parseExpressionsnachricht\nfix group in zigbee2mqtt",
|
|
74
|
+
"ru": "Адаптер требует администратора >= 7.7.22\nвосстановление instanz\nисправить сообщение cronParserLib.parse\nфиксированная группа в zigbee2mqtt",
|
|
75
|
+
"pt": "Adaptador requer admin >= 7.7.22 agora\ncorrigir instanz reiniciar\ncorrigir cronParserLib.parseExpression message\ngrupo de correção em zigbee2mqtt",
|
|
76
|
+
"nl": "Adapter vereist admin < 7.7.22 nu\nfix instanz herstart\nfix cronParserLib.parseExpressiebericht\nfix groep in zigbee2mqtt",
|
|
77
|
+
"fr": "Adaptateur nécessite admin >= 7.7.22 maintenant\ncorrection du redémarrage de l'instanz\ncorrection du message cronParserLib.parseExpression\nfixer le groupe dans zigbee2mqtt",
|
|
78
|
+
"it": "Adattatore richiede admin >= 7.7.22 ora\nfix instanz riavvio\ncorrezione cronParserLib.parseExpression messaggio\ngruppo di correzione in zigbee2mqt",
|
|
79
|
+
"es": "El adaptador requiere administrador= 7.7.22 ahora\nfijación instanz restart\nfijar cronParserLib.parse\ngrupo de fijación en zigbee2mqt",
|
|
80
|
+
"pl": "Adapter wymaga admin > = 7.7.22\nfix instanz restart\nnaprawić wiadomość cronParserLib.parseExpression\nfix group in zigbee2mqtt",
|
|
81
|
+
"uk": "Адаптер вимагає адмін >= 7.7.22 тепер\nвиправити instanz перезавантаження\nвиправлено cronParserLib.parseExpression повідомлення\nфіксувати групу в zigbee2mqtt",
|
|
82
|
+
"zh-cn": "适任者需要管理员 \\ 7.7.22 现在\n修复即时状态重新启动\n修补 cronParserLib.parsepression 信件\n以zigbee2mqtt为单位的固定组"
|
|
83
|
+
},
|
|
84
|
+
"2.15.5": {
|
|
85
|
+
"en": "fix admin",
|
|
86
|
+
"de": "admin",
|
|
87
|
+
"ru": "исправить admin",
|
|
88
|
+
"pt": "corrigir administrador",
|
|
89
|
+
"nl": "fix admin",
|
|
90
|
+
"fr": "réparer l'administration",
|
|
91
|
+
"it": "fix admin",
|
|
92
|
+
"es": "admin",
|
|
93
|
+
"pl": "fix admin",
|
|
94
|
+
"uk": "адмін",
|
|
95
|
+
"zh-cn": "固定管理员"
|
|
57
96
|
}
|
|
58
97
|
},
|
|
59
98
|
"titleLang": {
|
|
@@ -137,7 +176,7 @@
|
|
|
137
176
|
],
|
|
138
177
|
"globalDependencies": [
|
|
139
178
|
{
|
|
140
|
-
"admin": ">=7.
|
|
179
|
+
"admin": ">=7.7.22"
|
|
141
180
|
}
|
|
142
181
|
],
|
|
143
182
|
"messages": [
|
package/lib/adapterArray.js
CHANGED
|
@@ -374,15 +374,18 @@ const adapterArray = {
|
|
|
374
374
|
},
|
|
375
375
|
matter: {
|
|
376
376
|
adapterKey: 'matterDevices',
|
|
377
|
-
|
|
377
|
+
// Selektor: matter.0.controller.<nodeId>.info.connection (boolean, true = online)
|
|
378
|
+
selektor: 'matter.*.controller.*.info.connection',
|
|
379
|
+
timeSelector: '.info.connection',
|
|
378
380
|
adapterID: 'matter',
|
|
379
|
-
adapter: '
|
|
381
|
+
adapter: 'Matter',
|
|
380
382
|
rssiState: 'none',
|
|
381
|
-
battery: '
|
|
382
|
-
|
|
383
|
-
|
|
383
|
+
battery: 'none',
|
|
384
|
+
battery2: 'none',
|
|
385
|
+
reach: '.info.connection',
|
|
386
|
+
isLowBat: 'none',
|
|
384
387
|
id: 'none',
|
|
385
|
-
upgrade: '
|
|
388
|
+
upgrade: '.info.updateAvailable',
|
|
386
389
|
},
|
|
387
390
|
maxcube: {
|
|
388
391
|
adapterKey: 'maxcubeDevices',
|
|
@@ -512,6 +515,7 @@ const adapterArray = {
|
|
|
512
515
|
battery: 'none',
|
|
513
516
|
reach: '.alive',
|
|
514
517
|
isLowBat: 'none',
|
|
518
|
+
upgrade: 'none',
|
|
515
519
|
},
|
|
516
520
|
proxmox: {
|
|
517
521
|
adapterKey: 'proxmoxDevices',
|
|
@@ -737,7 +741,7 @@ const adapterArray = {
|
|
|
737
741
|
},
|
|
738
742
|
xsense: {
|
|
739
743
|
adapterKey: 'xsenseDevices',
|
|
740
|
-
selektor: 'xsense
|
|
744
|
+
selektor: 'xsense.*.*.online',
|
|
741
745
|
timeSelector: '.online',
|
|
742
746
|
adapterID: 'xsense',
|
|
743
747
|
adapter: 'XSense',
|
|
@@ -772,8 +776,8 @@ const adapterArray = {
|
|
|
772
776
|
},
|
|
773
777
|
zigbee2MQTT: {
|
|
774
778
|
adapterKey: 'zigbee2mqttDevices',
|
|
775
|
-
selektor: 'zigbee2mqtt.*.
|
|
776
|
-
timeSelector: '.
|
|
779
|
+
selektor: 'zigbee2mqtt.*.available',
|
|
780
|
+
timeSelector: '.last_seen',
|
|
777
781
|
adapterID: 'zigbee2MQTT',
|
|
778
782
|
adapter: 'Zigbee2MQTT',
|
|
779
783
|
battery: '.battery',
|
|
@@ -793,7 +797,7 @@ const adapterArray = {
|
|
|
793
797
|
rssiState: 'none',
|
|
794
798
|
isLowBat: '.Battery.isLow',
|
|
795
799
|
},
|
|
796
|
-
|
|
800
|
+
zwavews: {
|
|
797
801
|
adapterKey: 'zwavewsDevices',
|
|
798
802
|
selektor: 'zwavews.*.ready',
|
|
799
803
|
timeSelector: '.status',
|
package/lib/crud.js
CHANGED
|
@@ -1071,6 +1071,44 @@ async function createData(adaptr, i) {
|
|
|
1071
1071
|
if (id.endsWith('.')) {
|
|
1072
1072
|
continue;
|
|
1073
1073
|
}
|
|
1074
|
+
|
|
1075
|
+
// matter: nur matter.<inst>.controller.<nodeId>.info.connection zulassen
|
|
1076
|
+
if (adapterID === 'matter') {
|
|
1077
|
+
const parts = id.split('.');
|
|
1078
|
+
if (parts.length !== 6 || parts[2] !== 'controller' || parts[4] !== 'info' || parts[5] !== 'connection') {
|
|
1079
|
+
continue;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// zigbee2MQTT: Bridge/Coordinator/System-Objekte ausfiltern (kein echter Gerätepfad)
|
|
1084
|
+
if (adapterID === 'zigbee2MQTT') {
|
|
1085
|
+
const parts = id.split('.');
|
|
1086
|
+
// Pfad muss mindestens 4 Teile haben: zigbee2mqtt.0.<device>.<state>
|
|
1087
|
+
if (parts.length < 4) {
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
// Bridge und System-Objekte ausschließen
|
|
1091
|
+
const deviceSegment = parts[2].toLowerCase();
|
|
1092
|
+
if (deviceSegment === 'bridge' || deviceSegment === 'coordinator' || deviceSegment === 'info' || deviceSegment === 'groups') {
|
|
1093
|
+
continue;
|
|
1094
|
+
}
|
|
1095
|
+
// Zigbee2MQTT Gruppen ausschließen: Gruppen haben native.type === 'group'
|
|
1096
|
+
// Gruppen liegen direkt auf Instanz-Ebene z.B. zigbee2mqtt.0.büro
|
|
1097
|
+
const currDevStr = id.slice(0, id.lastIndexOf('.'));
|
|
1098
|
+
const deviceObj = await adaptr.getForeignObjectAsync(currDevStr);
|
|
1099
|
+
if (deviceObj && deviceObj.native && deviceObj.native.type === 'group') {
|
|
1100
|
+
adaptr.log.debug(`[createData zigbee2MQTT] Skipping group (native.type): ${currDevStr}`);
|
|
1101
|
+
continue;
|
|
1102
|
+
}
|
|
1103
|
+
// Fallback: Gruppen haben weder last_seen noch link_quality → kein echtes Gerät
|
|
1104
|
+
const hasLastSeen = await tools.getInitValue(adaptr, `${currDevStr}.last_seen`);
|
|
1105
|
+
const hasLinkQuality = await tools.getInitValue(adaptr, `${currDevStr}.link_quality`);
|
|
1106
|
+
if (hasLastSeen === undefined && hasLinkQuality === undefined) {
|
|
1107
|
+
adaptr.log.debug(`[createData zigbee2MQTT] Skipping group (no last_seen/link_quality): ${currDevStr}`);
|
|
1108
|
+
continue;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1074
1112
|
const mainSelector = id;
|
|
1075
1113
|
|
|
1076
1114
|
/*=============================================
|
|
@@ -1192,17 +1230,39 @@ async function createData(adaptr, i) {
|
|
|
1192
1230
|
deviceBatteryState = await tools.getInitValue(adaptr, deviceBatteryStateDP);
|
|
1193
1231
|
break;
|
|
1194
1232
|
|
|
1233
|
+
case 'matter': {
|
|
1234
|
+
// matter: Batterie liegt unter shortCurrDeviceString.PowerSource-N.BATTERY
|
|
1235
|
+
// N ist variabel -> dynamisch per getObjectViewAsync suchen
|
|
1236
|
+
const matterBattPrefix = `${shortCurrDeviceString}.PowerSource-`;
|
|
1237
|
+
const matterBattList = await adaptr.getObjectViewAsync('system', 'state', {
|
|
1238
|
+
startkey: `${matterBattPrefix}`,
|
|
1239
|
+
endkey: `${matterBattPrefix}\u9999`,
|
|
1240
|
+
});
|
|
1241
|
+
// ersten BATTERY-State nehmen
|
|
1242
|
+
const matterBattRow = matterBattList.rows.find((r) => r.id.endsWith('.BATTERY'));
|
|
1243
|
+
if (matterBattRow) {
|
|
1244
|
+
deviceBatteryStateDP = matterBattRow.id;
|
|
1245
|
+
deviceBatteryState = await tools.getInitValue(adaptr, deviceBatteryStateDP);
|
|
1246
|
+
}
|
|
1247
|
+
break;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1195
1250
|
default:
|
|
1196
1251
|
deviceBatteryStateDP = currDeviceString + adaptr.selAdapter[i].battery;
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1252
|
+
if (adaptr.selAdapter[i].battery === 'none') {
|
|
1253
|
+
// adapter has no battery percentage DP (e.g. hmiP) – skip lookups
|
|
1254
|
+
deviceBatteryState = undefined;
|
|
1255
|
+
} else {
|
|
1201
1256
|
deviceBatteryState = await tools.getInitValue(adaptr, deviceBatteryStateDP);
|
|
1202
1257
|
|
|
1203
1258
|
if (deviceBatteryState === undefined) {
|
|
1204
|
-
deviceBatteryStateDP = currDeviceString + adaptr.selAdapter[i].
|
|
1259
|
+
deviceBatteryStateDP = currDeviceString + adaptr.selAdapter[i].battery2;
|
|
1205
1260
|
deviceBatteryState = await tools.getInitValue(adaptr, deviceBatteryStateDP);
|
|
1261
|
+
|
|
1262
|
+
if (deviceBatteryState === undefined) {
|
|
1263
|
+
deviceBatteryStateDP = currDeviceString + adaptr.selAdapter[i].battery3;
|
|
1264
|
+
deviceBatteryState = await tools.getInitValue(adaptr, deviceBatteryStateDP);
|
|
1265
|
+
}
|
|
1206
1266
|
}
|
|
1207
1267
|
}
|
|
1208
1268
|
break;
|
|
@@ -1221,6 +1281,21 @@ async function createData(adaptr, i) {
|
|
|
1221
1281
|
deviceLowBatState = await tools.getInitValue(adaptr, isLowBatDP);
|
|
1222
1282
|
}
|
|
1223
1283
|
}
|
|
1284
|
+
|
|
1285
|
+
// matter: LOWBAT dynamisch über PowerSource-N.LOWBAT suchen
|
|
1286
|
+
if (adapterID === 'matter' && deviceLowBatState === undefined) {
|
|
1287
|
+
const matterLowBatPrefix = `${shortCurrDeviceString}.PowerSource-`;
|
|
1288
|
+
const matterLowBatList = await adaptr.getObjectViewAsync('system', 'state', {
|
|
1289
|
+
startkey: matterLowBatPrefix,
|
|
1290
|
+
endkey: `${matterLowBatPrefix}\u9999`,
|
|
1291
|
+
});
|
|
1292
|
+
const matterLowBatRow = matterLowBatList.rows.find((r) => r.id.endsWith('.LOWBAT'));
|
|
1293
|
+
if (matterLowBatRow) {
|
|
1294
|
+
isLowBatDP = matterLowBatRow.id;
|
|
1295
|
+
deviceLowBatState = await tools.getInitValue(adaptr, isLowBatDP);
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1224
1299
|
if (deviceLowBatState === undefined) {
|
|
1225
1300
|
isLowBatDP = 'none';
|
|
1226
1301
|
}
|
|
@@ -1229,9 +1304,15 @@ async function createData(adaptr, i) {
|
|
|
1229
1304
|
faultReportingState = await tools.getInitValue(adaptr, faultReportingDP);
|
|
1230
1305
|
|
|
1231
1306
|
//subscribe to states
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1307
|
+
if (!deviceBatteryStateDP.endsWith('undefined')) {
|
|
1308
|
+
adaptr.subscribeForeignStates(deviceBatteryStateDP);
|
|
1309
|
+
}
|
|
1310
|
+
if (!isLowBatDP.endsWith('undefined') && isLowBatDP !== 'none') {
|
|
1311
|
+
adaptr.subscribeForeignStates(isLowBatDP);
|
|
1312
|
+
}
|
|
1313
|
+
if (!faultReportingDP.endsWith('undefined')) {
|
|
1314
|
+
adaptr.subscribeForeignStates(faultReportingDP);
|
|
1315
|
+
}
|
|
1235
1316
|
|
|
1236
1317
|
const batteryData = await adaptr.getBatteryData(deviceBatteryState, deviceLowBatState, faultReportingState, adapterID);
|
|
1237
1318
|
|
|
@@ -1313,7 +1394,13 @@ async function createData(adaptr, i) {
|
|
|
1313
1394
|
isUpgradable = await adaptr.checkDeviceUpdate(adapterID, deviceUpdateSelector);
|
|
1314
1395
|
}
|
|
1315
1396
|
|
|
1316
|
-
|
|
1397
|
+
if (!deviceUpdateDP.endsWith('undefined')) {
|
|
1398
|
+
if (!deviceUpdateDP.endsWith('none')) {
|
|
1399
|
+
if (!deviceUpdateDP.endsWith('null')) {
|
|
1400
|
+
adaptr.subscribeForeignStates(deviceUpdateDP);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1317
1404
|
}
|
|
1318
1405
|
|
|
1319
1406
|
/*=============================================
|
package/lib/tools.js
CHANGED
|
@@ -11,7 +11,15 @@ async function isDisabledDevice(adaptr, treeDP) {
|
|
|
11
11
|
|
|
12
12
|
const device = await adaptr.getForeignObject(treeDP);
|
|
13
13
|
|
|
14
|
-
if (device
|
|
14
|
+
if (!device || !device.common) {
|
|
15
|
+
return isDisabled;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const desc = device.common.desc ?? '';
|
|
19
|
+
const descStr = typeof desc === 'object' ? JSON.stringify(desc) : String(desc);
|
|
20
|
+
const deviceRemoved = device.native && device.native.deviceRemoved === true;
|
|
21
|
+
|
|
22
|
+
if (deviceRemoved || descStr.includes('disabled') || descStr.includes('Deaktiviert')) {
|
|
15
23
|
isDisabled = true;
|
|
16
24
|
}
|
|
17
25
|
return isDisabled;
|
|
@@ -114,11 +122,19 @@ async function countDevices(adaptr) {
|
|
|
114
122
|
*/
|
|
115
123
|
async function checkLastContact(adaptr) {
|
|
116
124
|
for (const [deviceID, deviceData] of adaptr.listAllDevicesRaw.entries()) {
|
|
117
|
-
if (deviceData.instanceDeviceConnected !== false && deviceData.instanceDeviceConnected
|
|
118
|
-
deviceData.UnreachState = await
|
|
125
|
+
if (deviceData.instanceDeviceConnected !== false && deviceData.instanceDeviceConnected !== undefined) {
|
|
126
|
+
deviceData.UnreachState = await getInitValue(adaptr, deviceData.UnreachDP);
|
|
119
127
|
|
|
120
128
|
const gefundenerAdapter = Object.values(adapterArray).find((adapter) => adapter.adapterID === deviceData.adapterID);
|
|
129
|
+
if (!gefundenerAdapter) {
|
|
130
|
+
adaptr.log.warn(`[checkLastContact] - adapter not found for adapterID: ${deviceData.adapterID}, skipping device ${deviceID}`);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
121
133
|
const silentEnabled = Object.values(adaptr.config.tableDevices).find((adapter) => adapter.adapterKey === gefundenerAdapter.adapterKey);
|
|
134
|
+
if (!silentEnabled) {
|
|
135
|
+
adaptr.log.warn(`[checkLastContact] - device config not found for adapterKey: ${gefundenerAdapter.adapterKey}, skipping device ${deviceID}`);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
122
138
|
|
|
123
139
|
const oldContactState = deviceData.Status;
|
|
124
140
|
|
|
@@ -138,7 +154,7 @@ async function checkLastContact(adaptr) {
|
|
|
138
154
|
}
|
|
139
155
|
if (adaptr.config.checkSendOfflineMsg && oldContactState !== deviceData.Status && !adaptr.blacklistNotify.includes(deviceData.Path)) {
|
|
140
156
|
// check if the generally deviceData connected state is for a while true
|
|
141
|
-
if (await
|
|
157
|
+
if (await getTimestampConnectionDP(adaptr, deviceData.instanceDeviceConnectionDP, 50000)) {
|
|
142
158
|
await adaptr.sendStateNotifications('Devices', 'onlineStateDevice', deviceID, silentEnabled.telegramSilent);
|
|
143
159
|
}
|
|
144
160
|
}
|
package/lib/translations.js
CHANGED
package/main.js
CHANGED
|
@@ -118,7 +118,7 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
118
118
|
// create list with enabled adapters for monitor devices
|
|
119
119
|
for (const device of Object.values(this.config.tableDevices)) {
|
|
120
120
|
if (device.enabled) {
|
|
121
|
-
for (const [
|
|
121
|
+
for (const [_adapterName, adapter] of Object.entries(adapterArray)) {
|
|
122
122
|
if (String(adapter.adapterKey).toLowerCase() === String(device.adapterKey).toLowerCase()) {
|
|
123
123
|
this.selAdapter.push(adapter);
|
|
124
124
|
this.adapterSelected.push(adapter.adapterKey);
|
|
@@ -325,6 +325,14 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
325
325
|
if (this.listAllDevicesRaw.has(id)) {
|
|
326
326
|
this.listAllDevicesRaw.delete(id);
|
|
327
327
|
}
|
|
328
|
+
// also remove all child devices if a parent/adapter object was deleted
|
|
329
|
+
const idPrefix = `${id }.`;
|
|
330
|
+
for (const key of this.listAllDevicesRaw.keys()) {
|
|
331
|
+
if (key.startsWith(idPrefix)) {
|
|
332
|
+
this.log.debug(`[onObjectChange] removing child device from map: ${key}`);
|
|
333
|
+
this.listAllDevicesRaw.delete(key);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
328
336
|
|
|
329
337
|
//unsubscribe of Objects and states
|
|
330
338
|
this.unsubscribeForeignObjects(id);
|
|
@@ -450,6 +458,9 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
450
458
|
}
|
|
451
459
|
}
|
|
452
460
|
break;
|
|
461
|
+
default:
|
|
462
|
+
this.log.warn(`[onMessage] Unknown command: ${obj.command}`);
|
|
463
|
+
break;
|
|
453
464
|
}
|
|
454
465
|
}
|
|
455
466
|
|
|
@@ -531,6 +542,7 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
531
542
|
// Get ID with short currDeviceString from objectjson
|
|
532
543
|
case 'hueExt':
|
|
533
544
|
case 'hmrpc':
|
|
545
|
+
case 'matter':
|
|
534
546
|
case 'nukiExt':
|
|
535
547
|
case 'wled':
|
|
536
548
|
case 'mqttNuki':
|
|
@@ -740,10 +752,16 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
740
752
|
default:
|
|
741
753
|
if (deviceBatteryState === undefined) {
|
|
742
754
|
if (deviceLowBatState !== undefined) {
|
|
743
|
-
|
|
755
|
+
// Explicit OK states: false, 'NORMAL', 0 (some adapters use 0=ok)
|
|
756
|
+
if (
|
|
757
|
+
deviceLowBatState === false ||
|
|
758
|
+
deviceLowBatState === 'NORMAL' ||
|
|
759
|
+
deviceLowBatState === 0
|
|
760
|
+
) {
|
|
744
761
|
batteryHealth = 'ok';
|
|
745
762
|
isBatteryDevice = true;
|
|
746
|
-
} else
|
|
763
|
+
} else {
|
|
764
|
+
// true, 1, any other string != 'NORMAL' → low
|
|
747
765
|
batteryHealth = 'low';
|
|
748
766
|
isBatteryDevice = true;
|
|
749
767
|
}
|
|
@@ -790,11 +808,14 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
790
808
|
}
|
|
791
809
|
break;
|
|
792
810
|
default:
|
|
793
|
-
if (typeof deviceLowBatState === '
|
|
811
|
+
if (typeof deviceLowBatState === 'boolean' && deviceLowBatState === true) {
|
|
812
|
+
// true = low bat
|
|
794
813
|
lowBatIndicator = true;
|
|
795
814
|
} else if (typeof deviceLowBatState === 'string' && deviceLowBatState !== 'NORMAL') {
|
|
815
|
+
// any string other than 'NORMAL' = low bat
|
|
796
816
|
lowBatIndicator = true;
|
|
797
|
-
} else if (typeof deviceLowBatState === '
|
|
817
|
+
} else if (typeof deviceLowBatState === 'number' && deviceLowBatState === 1) {
|
|
818
|
+
// 1 = low bat (0 = ok), consistent with getBatteryData
|
|
798
819
|
lowBatIndicator = true;
|
|
799
820
|
}
|
|
800
821
|
}
|
|
@@ -813,20 +834,16 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
813
834
|
* @param {object} selector - Selector
|
|
814
835
|
*/
|
|
815
836
|
async getLastContact(selector) {
|
|
816
|
-
const lastContact = tools.getTimestamp(selector);
|
|
817
|
-
|
|
818
|
-
let lastContactString = `${this.formatDate(new Date(selector), 'hh:mm:ss')}`;
|
|
837
|
+
const lastContact = tools.getTimestamp(selector);
|
|
819
838
|
|
|
820
|
-
|
|
821
|
-
if (Math.round(lastContact) >= 0) {
|
|
822
|
-
lastContactString = `${Math.round(lastContact)} ${translations.secs[this.config.userSelectedLanguage]}`;
|
|
823
|
-
}
|
|
839
|
+
let lastContactString;
|
|
824
840
|
|
|
825
|
-
// Optional: Wenn du ab einem bestimmten Wert lieber Minuten oder Stunden willst
|
|
826
841
|
if (lastContact >= 3600) {
|
|
827
842
|
lastContactString = `${(lastContact / 3600).toFixed(1)} ${translations.hours[this.config.userSelectedLanguage]}`;
|
|
828
|
-
} else {
|
|
843
|
+
} else if (lastContact >= 60) {
|
|
829
844
|
lastContactString = `${Math.round(lastContact / 60)} ${translations.minits[this.config.userSelectedLanguage]}`;
|
|
845
|
+
} else {
|
|
846
|
+
lastContactString = `${Math.round(lastContact)} ${translations.secs[this.config.userSelectedLanguage]}`;
|
|
830
847
|
}
|
|
831
848
|
|
|
832
849
|
return lastContactString;
|
|
@@ -842,7 +859,7 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
842
859
|
const deviceTimeSelector = await this.getForeignStateAsync(timeSelector);
|
|
843
860
|
const deviceUnreachSelector = await this.getForeignStateAsync(treeDP);
|
|
844
861
|
|
|
845
|
-
const lastDeviceUnreachStateChange = deviceUnreachSelector !=
|
|
862
|
+
const lastDeviceUnreachStateChange = deviceUnreachSelector?.lc != null ? tools.getTimestamp(deviceUnreachSelector.lc) : tools.getTimestamp(deviceTimeSelector?.ts ?? Date.now());
|
|
846
863
|
|
|
847
864
|
// ignore disabled device from zigbee2MQTT
|
|
848
865
|
if (adapterID === 'zigbee2MQTT') {
|
|
@@ -875,7 +892,15 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
875
892
|
}
|
|
876
893
|
|
|
877
894
|
const gefundenerAdapter = Object.values(adapterArray).find((adapter) => adapter.adapterID === adapterID);
|
|
895
|
+
if (!gefundenerAdapter) {
|
|
896
|
+
this.log.warn(`[getOnlineState] - adapter not found in adapterArray for adapterID: ${adapterID}`);
|
|
897
|
+
return [lastContactString ?? ' - ', deviceState, linkQualitySet];
|
|
898
|
+
}
|
|
878
899
|
const device = Object.values(this.config.tableDevices).find((adapter) => adapter.adapterKey === gefundenerAdapter.adapterKey);
|
|
900
|
+
if (!device) {
|
|
901
|
+
this.log.warn(`[getOnlineState] - device config not found for adapterKey: ${gefundenerAdapter.adapterKey}`);
|
|
902
|
+
return [lastContactString ?? ' - ', deviceState, linkQualitySet];
|
|
903
|
+
}
|
|
879
904
|
const maxSecondDevicesOffline = device.maxSecondDevicesOffline;
|
|
880
905
|
|
|
881
906
|
switch (adapterID) {
|
|
@@ -931,6 +956,7 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
931
956
|
case 'apcups':
|
|
932
957
|
case 'hue':
|
|
933
958
|
case 'hueExt':
|
|
959
|
+
case 'matter':
|
|
934
960
|
case 'ping':
|
|
935
961
|
case 'deconz':
|
|
936
962
|
case 'shelly':
|
|
@@ -1087,7 +1113,7 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
1087
1113
|
});
|
|
1088
1114
|
|
|
1089
1115
|
// LinkQuality lists
|
|
1090
|
-
if (device.SignalStrength
|
|
1116
|
+
if (device.SignalStrength !== ' - ') {
|
|
1091
1117
|
this.linkQualityDevices.push({
|
|
1092
1118
|
[translations.Device[this.config.userSelectedLanguage]]: device.Device,
|
|
1093
1119
|
[translations.Adapter[this.config.userSelectedLanguage]]: device.Adapter,
|
|
@@ -1138,7 +1164,6 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
1138
1164
|
* @param {ioBroker.State} state
|
|
1139
1165
|
*/
|
|
1140
1166
|
async renewDeviceData(id, state) {
|
|
1141
|
-
const regex = /^([^.]+\.\d+\.[^.]+)/;
|
|
1142
1167
|
let batteryData;
|
|
1143
1168
|
let signalData;
|
|
1144
1169
|
let oldLowBatState;
|
|
@@ -1153,7 +1178,15 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
1153
1178
|
|
|
1154
1179
|
if (deviceData) {
|
|
1155
1180
|
const gefundenerAdapter = Object.values(adapterArray).find((adapter) => adapter.adapterID === deviceData.adapterID);
|
|
1181
|
+
if (!gefundenerAdapter) {
|
|
1182
|
+
this.log.warn(`[renewDeviceData] - adapter not found for adapterID: ${deviceData.adapterID}`);
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1156
1185
|
const silentEnabled = Object.values(this.config.tableDevices).find((adapter) => adapter.adapterKey === gefundenerAdapter.adapterKey);
|
|
1186
|
+
if (!silentEnabled) {
|
|
1187
|
+
this.log.warn(`[renewDeviceData] - device config not found for adapterKey: ${gefundenerAdapter.adapterKey}`);
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1157
1190
|
|
|
1158
1191
|
// On statechange update available datapoint
|
|
1159
1192
|
switch (id) {
|
|
@@ -1188,7 +1221,8 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
1188
1221
|
if (deviceData.isBatteryDevice) {
|
|
1189
1222
|
oldLowBatState = deviceData.LowBat;
|
|
1190
1223
|
if (state.val === 0 && deviceData.BatteryRaw >= 5) {
|
|
1191
|
-
|
|
1224
|
+
// Glitch-Filter: ignore single 0-value if battery was above 5 before
|
|
1225
|
+
break;
|
|
1192
1226
|
}
|
|
1193
1227
|
batteryData = await this.getBatteryData(state.val, oldLowBatState, deviceData.faultReport, deviceData.adapterID);
|
|
1194
1228
|
|
|
@@ -1264,7 +1298,7 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
1264
1298
|
deviceData.rssiPeerSelectorHMRPC,
|
|
1265
1299
|
);
|
|
1266
1300
|
|
|
1267
|
-
if (contactData !== undefined
|
|
1301
|
+
if (contactData !== undefined && contactData !== null) {
|
|
1268
1302
|
deviceData.LastContact = contactData[0];
|
|
1269
1303
|
deviceData.Status = contactData[1];
|
|
1270
1304
|
deviceData.SignalStrength = contactData[2];
|
|
@@ -1323,7 +1357,7 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
1323
1357
|
const devicesState = await this.getForeignStateAsync(instanceConnectedDeviceDP);
|
|
1324
1358
|
|
|
1325
1359
|
let instanceConnectedDeviceVal;
|
|
1326
|
-
if (
|
|
1360
|
+
if (devicesState !== null && typeof devicesState.val === 'boolean') {
|
|
1327
1361
|
instanceConnectedDeviceVal = await tools.getInitValue(this, instanceConnectedDeviceDP);
|
|
1328
1362
|
} else {
|
|
1329
1363
|
instanceConnectedDeviceVal = 'N/A';
|
|
@@ -1549,13 +1583,19 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
1549
1583
|
await this.delay(instanceErrorTime);
|
|
1550
1584
|
const daemonIsAliveAfterSecondDelay = await this.checkDaemonIsHealthy(instanceID);
|
|
1551
1585
|
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1586
|
+
// nach allen Retries: Status übernehmen (egal ob erholt oder weiterhin fehlerhaft)
|
|
1587
|
+
isAlive = Boolean(daemonIsAliveAfterSecondDelay[0]);
|
|
1588
|
+
isHealthy = Boolean(daemonIsAliveAfterSecondDelay[1]);
|
|
1589
|
+
instanceStatusString = String(daemonIsAliveAfterSecondDelay[2]);
|
|
1590
|
+
connectedToHost = Boolean(daemonIsAliveAfterSecondDelay[3]);
|
|
1591
|
+
connectedToDevice = Boolean(daemonIsAliveAfterSecondDelay[4]);
|
|
1592
|
+
} else {
|
|
1593
|
+
// nach erstem Retry wieder gesund
|
|
1594
|
+
isAlive = Boolean(daemonIsAliveAfterDelay[0]);
|
|
1595
|
+
isHealthy = Boolean(daemonIsAliveAfterDelay[1]);
|
|
1596
|
+
instanceStatusString = String(daemonIsAliveAfterDelay[2]);
|
|
1597
|
+
connectedToHost = Boolean(daemonIsAliveAfterDelay[3]);
|
|
1598
|
+
connectedToDevice = Boolean(daemonIsAliveAfterDelay[4]);
|
|
1559
1599
|
}
|
|
1560
1600
|
} else {
|
|
1561
1601
|
daemonIsNotAlive = await this.checkDaemonIsAlive(instanceID, instanceDeactivationTime);
|
|
@@ -1580,14 +1620,17 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
1580
1620
|
async getAdapterUpdateData(adapterUpdateListDP) {
|
|
1581
1621
|
// Clear the existing adapter updates data
|
|
1582
1622
|
let adapterUpdatesJsonRaw = [];
|
|
1583
|
-
let adapterJsonList;
|
|
1623
|
+
let adapterJsonList = {};
|
|
1584
1624
|
|
|
1585
1625
|
// Fetch the adapter updates list
|
|
1586
1626
|
const adapterUpdatesListVal = await this.getForeignStatesAsync(adapterUpdateListDP);
|
|
1587
1627
|
|
|
1588
|
-
// Extract adapter data from the list
|
|
1589
|
-
for (const [
|
|
1590
|
-
|
|
1628
|
+
// Extract adapter data from the list - merge all admin instances
|
|
1629
|
+
for (const [_id, value] of Object.entries(adapterUpdatesListVal)) {
|
|
1630
|
+
const parsed = tools.parseData(value.val);
|
|
1631
|
+
if (parsed && typeof parsed === 'object') {
|
|
1632
|
+
Object.assign(adapterJsonList, parsed);
|
|
1633
|
+
}
|
|
1591
1634
|
}
|
|
1592
1635
|
|
|
1593
1636
|
// Populate the adapter updates data
|
|
@@ -1817,8 +1860,24 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
1817
1860
|
// send message when instance was deactivated
|
|
1818
1861
|
if (this.config.checkSendInstanceDeactivatedMsg && !instanceData.isAlive) {
|
|
1819
1862
|
if (this.blacklistInstancesNotify.includes(instanceID)) {
|
|
1820
|
-
|
|
1863
|
+
break;
|
|
1821
1864
|
}
|
|
1865
|
+
// Restart-Erkennung: Toleranzzeit abwarten und prüfen ob Instanz schon wieder läuft
|
|
1866
|
+
const restartTolerance = this.userTimeInstancesList.has(instanceID)
|
|
1867
|
+
? this.userTimeInstancesList.get(instanceID).deactivationTime * 1000
|
|
1868
|
+
: this.config.offlineTimeInstances * 1000;
|
|
1869
|
+
|
|
1870
|
+
this.log.debug(`[renewInstanceData] Instance ${instanceID} went offline - waiting ${restartTolerance}ms to check for restart...`);
|
|
1871
|
+
await this.delay(restartTolerance);
|
|
1872
|
+
|
|
1873
|
+
const aliveAfterWait = await tools.getInitValue(this, `system.adapter.${instanceID}.alive`);
|
|
1874
|
+
if (aliveAfterWait) {
|
|
1875
|
+
// Instanz ist bereits wieder online → war nur ein Neustart
|
|
1876
|
+
this.log.debug(`[renewInstanceData] Instance ${instanceID} is back online after restart. No deactivation notification sent.`);
|
|
1877
|
+
await checkInstance(instanceID, instanceData);
|
|
1878
|
+
break;
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1822
1881
|
await this.sendStateNotifications('Instances', 'deactivatedInstance', instanceID);
|
|
1823
1882
|
}
|
|
1824
1883
|
}
|
|
@@ -2291,18 +2350,24 @@ class DeviceWatcher extends utils.Adapter {
|
|
|
2291
2350
|
}
|
|
2292
2351
|
}
|
|
2293
2352
|
async getPreviousCronRun(lastCronRun) {
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2353
|
+
try {
|
|
2354
|
+
let interval;
|
|
2355
|
+
// cron-parser v4: parseExpression() – v5: CronExpressionParser.parse()
|
|
2356
|
+
if (typeof cronParserLib.parseExpression === 'function') {
|
|
2357
|
+
interval = cronParserLib.parseExpression(lastCronRun);
|
|
2358
|
+
} else if (cronParserLib.CronExpressionParser && typeof cronParserLib.CronExpressionParser.parse === 'function') {
|
|
2359
|
+
interval = cronParserLib.CronExpressionParser.parse(lastCronRun);
|
|
2360
|
+
} else {
|
|
2361
|
+
throw new Error('cron-parser: no compatible API found (parseExpression / CronExpressionParser.parse)');
|
|
2362
|
+
}
|
|
2363
|
+
const previous = interval.prev();
|
|
2364
|
+
|
|
2365
|
+
// Differenz in ms seit dem vorherigen Cron-Zeitpunkt
|
|
2366
|
+
return Date.now() - previous.getTime();
|
|
2367
|
+
} catch (error) {
|
|
2368
|
+
this.log.error(`[getPreviousCronRun] - ${error}`);
|
|
2369
|
+
return null;
|
|
2370
|
+
}
|
|
2306
2371
|
}
|
|
2307
2372
|
|
|
2308
2373
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.device-watcher",
|
|
3
|
-
"version": "2.15.
|
|
3
|
+
"version": "2.15.11",
|
|
4
4
|
"description": "Watchdog for devices",
|
|
5
5
|
"author": "Christian Behrends <mail@christian-behrends.de>",
|
|
6
6
|
"contributors": [
|
|
@@ -23,26 +23,24 @@
|
|
|
23
23
|
"url": "https://github.com/iobroker-community-adapters/ioBroker.device-watcher"
|
|
24
24
|
},
|
|
25
25
|
"engines": {
|
|
26
|
-
"node": ">=
|
|
26
|
+
"node": ">=22"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@iobroker/adapter-core": "^3.3.2",
|
|
30
30
|
"node-schedule": "^2.1.1",
|
|
31
|
-
"cron-parser": "^
|
|
31
|
+
"cron-parser": "^5.5.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@alcalzone/release-script": "^5.
|
|
35
|
-
"@alcalzone/release-script-plugin-iobroker": "^
|
|
36
|
-
"@alcalzone/release-script-plugin-license": "^
|
|
37
|
-
"@alcalzone/release-script-plugin-manual-review": "^
|
|
34
|
+
"@alcalzone/release-script": "^5.1.1",
|
|
35
|
+
"@alcalzone/release-script-plugin-iobroker": "^5.1.2",
|
|
36
|
+
"@alcalzone/release-script-plugin-license": "^5.1.1",
|
|
37
|
+
"@alcalzone/release-script-plugin-manual-review": "^5.1.1",
|
|
38
38
|
"@iobroker/adapter-dev": "^1.5.0",
|
|
39
39
|
"@iobroker/eslint-config": "^2.1.0",
|
|
40
40
|
"@iobroker/testing": "^5.2.2",
|
|
41
|
-
"@types/node": "^24.
|
|
41
|
+
"@types/node": "^24.12.0",
|
|
42
42
|
"@types/node-schedule": "^2.1.8",
|
|
43
|
-
"
|
|
44
|
-
"@typescript-eslint/parser": "^8.48.1",
|
|
45
|
-
"typescript": "~5.9.3"
|
|
43
|
+
"typescript": "~6.0.2"
|
|
46
44
|
},
|
|
47
45
|
"main": "main.js",
|
|
48
46
|
"files": [
|