iobroker.zwavews 0.1.2 → 0.1.4
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 +8 -0
- package/io-package.json +29 -29
- package/lib/devicemgmt.js +29 -26
- package/lib/helper.js +78 -54
- package/lib/mqttServerController.js +1 -1
- package/lib/statesController.js +13 -9
- package/lib/utils.js +45 -19
- package/lib/websocketController.js +12 -8
- package/main.js +9 -5
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -35,6 +35,14 @@ Activate WS Server Settings in `zwave-js-ui` we use the Home Assistant Settings
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
## Changelog
|
|
38
|
+
### 0.1.4 (2026-04-16)
|
|
39
|
+
* (arteck) Dependencies have been updated
|
|
40
|
+
* (arteck) add vscode folder
|
|
41
|
+
|
|
42
|
+
### 0.1.3 (2026-04-03)
|
|
43
|
+
* (arteck) del last dot from DP
|
|
44
|
+
* (arteck) fix scene
|
|
45
|
+
|
|
38
46
|
### 0.1.2 (2026-03-15)
|
|
39
47
|
* (arteck) typo
|
|
40
48
|
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "zwavews",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.4",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.1.4": {
|
|
7
|
+
"en": "Dependencies have been updated\nadd vscode folder",
|
|
8
|
+
"de": "Abhängigkeiten wurden aktualisiert\nvscode ordner hinzufügen",
|
|
9
|
+
"ru": "Зависимости были обновлены\nдобавить vscode папку",
|
|
10
|
+
"pt": "As dependências foram atualizadas\nadicionar pasta vscode",
|
|
11
|
+
"nl": "Afhankelijkheden zijn bijgewerkt\nvscode map toevoegen",
|
|
12
|
+
"fr": "Les dépendances ont été actualisées\najouter un dossier vscode",
|
|
13
|
+
"it": "Le dipendenze sono state aggiornate\naggiungere cartella vscode",
|
|
14
|
+
"es": "Se han actualizado las dependencias\nañadir carpeta vscode",
|
|
15
|
+
"pl": "Zaktualizowano zależności\ndodaj folder vscode",
|
|
16
|
+
"uk": "Залежність було оновлено\nдодати папку проти коду",
|
|
17
|
+
"zh-cn": "依赖关系已更新\n添加 vscode 文件夹"
|
|
18
|
+
},
|
|
19
|
+
"0.1.3": {
|
|
20
|
+
"en": "fix unknown state from scene\ndel last dot from DP\nfix scene",
|
|
21
|
+
"de": "unbekannter zustand von szene\nder letzte Punkt von DP\nfixe szene",
|
|
22
|
+
"ru": "неизвестное состояние с места происшествия\nпоследняя точка от DP\nисправить",
|
|
23
|
+
"pt": "corrigir o estado desconhecido da cena\ndo último ponto do DP\ncorrigir cena",
|
|
24
|
+
"nl": "fix onbekende toestand vanaf locatie\nde laatste stip van DP\nscène herstellen",
|
|
25
|
+
"fr": "réparer l'état inconnu de la scène\ndel dernier point de DP\nréparer la scène",
|
|
26
|
+
"it": "fissare stato sconosciuto dalla scena\ndel ultimo punto da DP\ncorrere la scena",
|
|
27
|
+
"es": "arreglar estado desconocido de la escena\ndel último punto de DP\nescena arreglada",
|
|
28
|
+
"pl": "naprawić nieznany stan z miejsca zbrodni\ndel ostatnia kropka z DP\nmiejsce",
|
|
29
|
+
"uk": "виправити невідомого стану з сцени\ndel last dot від DP\nфіксувати сцена",
|
|
30
|
+
"zh-cn": "从现场修复未知状态\ndP 的最后一个点\n修补场景"
|
|
31
|
+
},
|
|
6
32
|
"0.1.2": {
|
|
7
33
|
"en": "typo",
|
|
8
34
|
"de": "typo",
|
|
@@ -67,32 +93,6 @@
|
|
|
67
93
|
"pl": "uruchomić adapter\nZaktualizowano zależności",
|
|
68
94
|
"uk": "запуск адаптера\nЗалежність було оновлено",
|
|
69
95
|
"zh-cn": "固定适配器启动\n依赖关系已更新"
|
|
70
|
-
},
|
|
71
|
-
"0.0.16": {
|
|
72
|
-
"en": "fix warning message",
|
|
73
|
-
"de": "warnmeldung aktivieren",
|
|
74
|
-
"ru": "исправить предупреждающее сообщение",
|
|
75
|
-
"pt": "corrigir a mensagem de aviso",
|
|
76
|
-
"nl": "waarschuwingsbericht herstellen",
|
|
77
|
-
"fr": "corriger le message d'avertissement",
|
|
78
|
-
"it": "correzione del messaggio di avviso",
|
|
79
|
-
"es": "mensaje de advertencia",
|
|
80
|
-
"pl": "naprawić komunikat ostrzegawczy",
|
|
81
|
-
"uk": "фіксувати повідомлення про попередження",
|
|
82
|
-
"zh-cn": "修补警告消息"
|
|
83
|
-
},
|
|
84
|
-
"0.0.15": {
|
|
85
|
-
"en": "typo\nfix ready status if status is dead",
|
|
86
|
-
"de": "typo\nbereitstellen des status, wenn der status tot ist",
|
|
87
|
-
"ru": "опечатка\nготовый статус, если статус мертв",
|
|
88
|
-
"pt": "erro de digitação\ncorrigir o estado pronto se o estado estiver morto",
|
|
89
|
-
"nl": "type\nfix ready status als status dood is",
|
|
90
|
-
"fr": "typo\nfixer le statut prêt si le statut est mort",
|
|
91
|
-
"it": "tipo\nfissare lo stato pronto se lo stato è morto",
|
|
92
|
-
"es": "typo\nfijar estado listo si el estado está muerto",
|
|
93
|
-
"pl": "typo\nustaw stan gotowy, jeśli stan jest martwy",
|
|
94
|
-
"uk": "типи\nвиправити готовий статус, якщо статус мертвий",
|
|
95
|
-
"zh-cn": "类型\n如果状态已死亡, 则固定状态"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|
|
@@ -180,13 +180,13 @@
|
|
|
180
180
|
],
|
|
181
181
|
"globalDependencies": [
|
|
182
182
|
{
|
|
183
|
-
"admin": ">=7.6.
|
|
183
|
+
"admin": ">=7.6.20"
|
|
184
184
|
}
|
|
185
185
|
],
|
|
186
186
|
"plugins": {
|
|
187
187
|
"docker": {
|
|
188
188
|
"iobDockerComposeFiles": [
|
|
189
|
-
"
|
|
189
|
+
"docker-compose.yaml"
|
|
190
190
|
]
|
|
191
191
|
}
|
|
192
192
|
},
|
package/lib/devicemgmt.js
CHANGED
|
@@ -3,12 +3,13 @@ const dmUtils = require('@iobroker/dm-utils');
|
|
|
3
3
|
const humanizeDuration = require('humanize-duration');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Device management class for the ZWave adapter.
|
|
7
7
|
*/
|
|
8
8
|
class dmZwave extends dmUtils.DeviceManagement {
|
|
9
9
|
/**
|
|
10
|
+
* Creates a new dmZwave instance.
|
|
10
11
|
*
|
|
11
|
-
* @param adapter
|
|
12
|
+
* @param {object} adapter - The ioBroker adapter instance.
|
|
12
13
|
*/
|
|
13
14
|
constructor(adapter) {
|
|
14
15
|
super(adapter);
|
|
@@ -28,9 +29,7 @@ class dmZwave extends dmUtils.DeviceManagement {
|
|
|
28
29
|
|
|
29
30
|
const device = this.adapter.nodeCache[nodeId].nodeData;
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
status.connection = device.ready ? 'connected' : 'disconnected';
|
|
33
|
-
}
|
|
32
|
+
status.connection = device.ready ? 'connected' : 'disconnected';
|
|
34
33
|
|
|
35
34
|
//const link_quality = await this.adapter.getStateAsync(`${theDevice._id}.status`);
|
|
36
35
|
//status.rssi = link_quality.val == 'alive' ? '100' : '0';
|
|
@@ -91,9 +90,10 @@ class dmZwave extends dmUtils.DeviceManagement {
|
|
|
91
90
|
}
|
|
92
91
|
|
|
93
92
|
/**
|
|
93
|
+
* Opens the device documentation PDF or link in a form dialog.
|
|
94
94
|
*
|
|
95
|
-
* @param context
|
|
96
|
-
* @param device
|
|
95
|
+
* @param {object} context - The device management context used to show the form.
|
|
96
|
+
* @param {object} device - The ZWave device object containing device config and metadata.
|
|
97
97
|
*/
|
|
98
98
|
async openPDF(context, device) {
|
|
99
99
|
const manual = device?.deviceConfig?.metadata?.manual;
|
|
@@ -140,12 +140,13 @@ class dmZwave extends dmUtils.DeviceManagement {
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
|
+
* Returns the detail schema and data for a specific device.
|
|
143
144
|
*
|
|
144
|
-
* @param id
|
|
145
|
-
* @param action
|
|
146
|
-
* @param context
|
|
145
|
+
* @param {string} id - The node ID of the device.
|
|
146
|
+
* @param {object} _action - The action object passed by the device management framework.
|
|
147
|
+
* @param {object} _context - The device management context.
|
|
147
148
|
*/
|
|
148
|
-
async getDeviceDetails(id,
|
|
149
|
+
async getDeviceDetails(id, _action, _context) {
|
|
149
150
|
this.adapter.log.debug('getDeviceDetails');
|
|
150
151
|
|
|
151
152
|
const device = this.adapter.nodeCache[id]?.nodeData;
|
|
@@ -362,27 +363,27 @@ class dmZwave extends dmUtils.DeviceManagement {
|
|
|
362
363
|
|
|
363
364
|
|
|
364
365
|
/**
|
|
366
|
+
* Formats a timestamp according to the given format type.
|
|
365
367
|
*
|
|
366
|
-
* @param time
|
|
367
|
-
* @param type
|
|
368
|
+
* @param {number} time - The timestamp in milliseconds (epoch).
|
|
369
|
+
* @param {'ISO_8601'|'ISO_8601_local'|'epoch'|'relative'} type - The desired output format.
|
|
368
370
|
*/
|
|
369
|
-
|
|
371
|
+
formatDate(time, type) { //'ISO_8601' | 'ISO_8601_local' | 'epoch' | 'relative'
|
|
370
372
|
if (type === 'ISO_8601') {
|
|
371
|
-
return new Date(time).toISOString();
|
|
372
|
-
} else if (type === 'ISO_8601_local') {
|
|
373
|
-
return this.toLocalISOString(new Date(time));
|
|
374
|
-
} else if (type === 'epoch') {
|
|
375
|
-
return time;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
return ago;
|
|
380
|
-
|
|
373
|
+
return new Date(time).toISOString();
|
|
374
|
+
} else if (type === 'ISO_8601_local') {
|
|
375
|
+
return this.toLocalISOString(new Date(time));
|
|
376
|
+
} else if (type === 'epoch') {
|
|
377
|
+
return time;
|
|
378
|
+
}
|
|
379
|
+
// relative
|
|
380
|
+
return `${humanizeDuration(Date.now() - time, { language: 'en', largest: 2, round: true })} ago`;
|
|
381
381
|
}
|
|
382
382
|
|
|
383
383
|
/**
|
|
384
|
+
* Converts a Date object to a local ISO 8601 string.
|
|
384
385
|
*
|
|
385
|
-
* @param d
|
|
386
|
+
* @param {Date} d - The Date object to convert.
|
|
386
387
|
*/
|
|
387
388
|
toLocalISOString(d) {
|
|
388
389
|
const off = d.getTimezoneOffset();
|
|
@@ -391,8 +392,10 @@ return time;
|
|
|
391
392
|
// Entfernt den ioBroker-Prefix am Anfang, z.B.
|
|
392
393
|
// "zwavews.0.nodeID_1.info.name" -> "nodeID_1.info.name"
|
|
393
394
|
/**
|
|
395
|
+
* Strips the ioBroker adapter prefix from an object ID.
|
|
394
396
|
*
|
|
395
|
-
* @param id
|
|
397
|
+
* @param {string} id - The full ioBroker object ID (e.g. "zwavews.0.nodeID_1.info.name").
|
|
398
|
+
* @returns {string} The ID without the adapter prefix (e.g. "nodeID_1.info.name").
|
|
396
399
|
*/
|
|
397
400
|
stripIobPrefix(id) {
|
|
398
401
|
const s = String(id ?? '');
|
package/lib/helper.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const utils = require("./utils");
|
|
2
2
|
const constant = require("./constants");
|
|
3
|
-
const {isObject} = require("./utils");
|
|
4
3
|
|
|
5
4
|
/*
|
|
6
5
|
options:
|
|
@@ -12,13 +11,14 @@ autoCast (true false) // make JSON.parse to parse numbers correctly
|
|
|
12
11
|
descriptions: Object of names for state keys
|
|
13
12
|
*/
|
|
14
13
|
/**
|
|
15
|
-
*
|
|
14
|
+
* Helper class for creating and managing ioBroker objects and states from ZWave data.
|
|
16
15
|
*/
|
|
17
16
|
class Helper {
|
|
18
17
|
/**
|
|
18
|
+
* Creates a new Helper instance.
|
|
19
19
|
*
|
|
20
|
-
* @param adapter
|
|
21
|
-
* @param alreadyCreatedObjects
|
|
20
|
+
* @param {object} adapter - The ioBroker adapter instance.
|
|
21
|
+
* @param {object} [alreadyCreatedObjects] - Cache of already created object paths.
|
|
22
22
|
*/
|
|
23
23
|
constructor(adapter, alreadyCreatedObjects = {}) {
|
|
24
24
|
this.adapter = adapter;
|
|
@@ -27,17 +27,10 @@ class Helper {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
+
* Creates a ZWave node device and all its value states in ioBroker.
|
|
30
31
|
*
|
|
31
|
-
* @param
|
|
32
|
-
* @param element
|
|
33
|
-
* @param options
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
*
|
|
39
|
-
* @param nodeIdOriginal
|
|
40
|
-
* @param element
|
|
32
|
+
* @param {string|number} nodeIdOriginal - The original node ID as received from the ZWave driver.
|
|
33
|
+
* @param {object} element - The node data object containing values, name and device config.
|
|
41
34
|
*/
|
|
42
35
|
async createNode(nodeIdOriginal, element) {
|
|
43
36
|
try {
|
|
@@ -68,13 +61,10 @@ class Helper {
|
|
|
68
61
|
|
|
69
62
|
if (valuesOnly != null && typeof valuesOnly === "object" && valuesOnly.length > 0) {
|
|
70
63
|
for (const v of valuesOnly) {
|
|
71
|
-
let parsePath = utils.formatObject(`${nodeId}.${v.commandClassName}`);
|
|
64
|
+
let parsePath = utils.deleteLastDot(utils.formatObject(`${nodeId}.${v.commandClassName}`));
|
|
72
65
|
let metadata = v.metadata || {};
|
|
73
66
|
|
|
74
|
-
if (constant.noInfoDP.includes(v.commandClassName)) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
if (constant.noInfoDP.includes(v.propertyName)) {
|
|
67
|
+
if (constant.noInfoDP.includes(v.commandClassName) || constant.noInfoDP.includes(v.propertyName)) {
|
|
78
68
|
continue;
|
|
79
69
|
}
|
|
80
70
|
|
|
@@ -100,10 +90,10 @@ class Helper {
|
|
|
100
90
|
.replace(/[^\p{L}\p{N}\s]/gu, "")
|
|
101
91
|
.replace(/\s+/g, " ")
|
|
102
92
|
.trim()}`;
|
|
93
|
+
}
|
|
103
94
|
|
|
104
|
-
|
|
95
|
+
if (constant.RGB.includes(v.propertyKeyName)) {
|
|
105
96
|
parsePath = utils.replaceLastDot(parsePath);
|
|
106
|
-
}
|
|
107
97
|
}
|
|
108
98
|
|
|
109
99
|
if (this.isObject(v.value)) { // da gibts ein object mit value
|
|
@@ -115,7 +105,7 @@ class Helper {
|
|
|
115
105
|
parsePath = `${parsePath}_${v.endpoint}`;
|
|
116
106
|
}
|
|
117
107
|
|
|
118
|
-
parsePath = utils.formatObject(parsePath); // entferne sonderzeichen und
|
|
108
|
+
parsePath = utils.deleteLastDot(utils.formatObject(parsePath)); // entferne sonderzeichen und blank aus dem namen und letzten dot
|
|
119
109
|
|
|
120
110
|
const nam_id = v.label ?? v.propertyName;
|
|
121
111
|
|
|
@@ -127,7 +117,7 @@ class Helper {
|
|
|
127
117
|
if (constant.mixedType.includes(nam_id)) {
|
|
128
118
|
typeDp = "mixed";
|
|
129
119
|
}
|
|
130
|
-
|
|
120
|
+
|
|
131
121
|
const common = {
|
|
132
122
|
id: nam_id,
|
|
133
123
|
name: nam_id,
|
|
@@ -166,7 +156,7 @@ class Helper {
|
|
|
166
156
|
this.adapter.subscribeStates(parsePath);
|
|
167
157
|
}
|
|
168
158
|
|
|
169
|
-
this.
|
|
159
|
+
await this.changeState(parsePath, valDp);
|
|
170
160
|
|
|
171
161
|
this.alreadyCreatedObjects[parsePath] = {};
|
|
172
162
|
}
|
|
@@ -177,16 +167,18 @@ class Helper {
|
|
|
177
167
|
}
|
|
178
168
|
|
|
179
169
|
/**
|
|
170
|
+
* Recursively parses an element and creates the corresponding ioBroker objects and states.
|
|
180
171
|
*
|
|
181
|
-
* @param path
|
|
182
|
-
* @param element
|
|
183
|
-
* @param options
|
|
172
|
+
* @param {string} path - The ioBroker object path to write to.
|
|
173
|
+
* @param {*} element - The value or object to parse and persist.
|
|
174
|
+
* @param {object} [options] - Parsing options (e.g. write, channelName, descriptions).
|
|
175
|
+
* @param {boolean} [change] - If true, forces setState instead of setStateChanged.
|
|
184
176
|
*/
|
|
185
|
-
async parse(path, element, options = { write: false }) {
|
|
186
|
-
let parsePath = utils.formatObject(path);
|
|
177
|
+
async parse(path, element, options = { write: false },change = false) {
|
|
178
|
+
let parsePath = utils.deleteLastDot(utils.formatObject(path));
|
|
187
179
|
|
|
188
|
-
if (element
|
|
189
|
-
this.adapter.log.
|
|
180
|
+
if (element === undefined || element === null) {
|
|
181
|
+
this.adapter.log.error(`Skip undefined value for ${parsePath}`);
|
|
190
182
|
return;
|
|
191
183
|
}
|
|
192
184
|
|
|
@@ -220,13 +212,16 @@ class Helper {
|
|
|
220
212
|
|
|
221
213
|
this.alreadyCreatedObjects[parsePath] = {};
|
|
222
214
|
} catch (error) {
|
|
215
|
+
this.adapter.log.error(`parse error ${ parsePath}`);
|
|
223
216
|
this.adapter.log.error(error);
|
|
224
217
|
}
|
|
225
218
|
}
|
|
226
219
|
|
|
227
|
-
this.
|
|
220
|
+
await this.changeState(parsePath, valDp, change);
|
|
221
|
+
|
|
228
222
|
return;
|
|
229
223
|
}
|
|
224
|
+
|
|
230
225
|
options.channelName = utils.getLastSegment(parsePath);
|
|
231
226
|
|
|
232
227
|
if (!this.alreadyCreatedObjects[parsePath]) {
|
|
@@ -242,6 +237,7 @@ class Helper {
|
|
|
242
237
|
this.alreadyCreatedObjects[parsePath] = { };
|
|
243
238
|
delete options.channelName;
|
|
244
239
|
} catch (error) {
|
|
240
|
+
this.adapter.log.error(`parse error ${ parsePath}`);
|
|
245
241
|
this.adapter.log.error(error);
|
|
246
242
|
}
|
|
247
243
|
}
|
|
@@ -310,6 +306,8 @@ class Helper {
|
|
|
310
306
|
typeDp = "mixed";
|
|
311
307
|
}
|
|
312
308
|
|
|
309
|
+
fullPath = utils.deleteLastDot(fullPath);
|
|
310
|
+
|
|
313
311
|
const common = {
|
|
314
312
|
id: objectName,
|
|
315
313
|
name: objectName,
|
|
@@ -333,14 +331,15 @@ class Helper {
|
|
|
333
331
|
}
|
|
334
332
|
|
|
335
333
|
try {
|
|
336
|
-
if (valDP !== undefined) {
|
|
337
|
-
this.adapter.setStateChanged(fullPath, valDP, true);
|
|
338
334
|
|
|
335
|
+
await this.changeState(fullPath, valDP, change);
|
|
336
|
+
|
|
337
|
+
if (valDP !== undefined) {
|
|
339
338
|
if (fullPath.endsWith('ready') ) {
|
|
340
339
|
valDP = element['status'];
|
|
341
|
-
if (utils.isNumeric(valDP) && valDP
|
|
340
|
+
if (utils.isNumeric(valDP) && valDP === 3) {
|
|
342
341
|
fullPath = fullPath.replace(".status", ".ready");
|
|
343
|
-
this.
|
|
342
|
+
await this.changeState(fullPath, false);
|
|
344
343
|
}
|
|
345
344
|
}
|
|
346
345
|
}
|
|
@@ -353,19 +352,22 @@ class Helper {
|
|
|
353
352
|
|
|
354
353
|
|
|
355
354
|
/**
|
|
355
|
+
* Checks whether a value is a non-null object.
|
|
356
356
|
*
|
|
357
|
-
* @param value
|
|
357
|
+
* @param {*} value - The value to check.
|
|
358
|
+
* @returns {boolean}
|
|
358
359
|
*/
|
|
359
360
|
isObject(value) {
|
|
360
361
|
return value !== null && typeof value === "object";
|
|
361
362
|
}
|
|
362
363
|
|
|
363
364
|
/**
|
|
365
|
+
* Extracts and processes an array from an element, creating ioBroker objects for each entry.
|
|
364
366
|
*
|
|
365
|
-
* @param element
|
|
366
|
-
* @param key
|
|
367
|
-
* @param path
|
|
368
|
-
* @param options
|
|
367
|
+
* @param {object|Array} element - The element containing the array, or the array itself.
|
|
368
|
+
* @param {string} key - The key of the array within the element, or empty string if element is the array.
|
|
369
|
+
* @param {string} path - The ioBroker base path to write to.
|
|
370
|
+
* @param {object} options - Parsing options forwarded to the parse method.
|
|
369
371
|
*/
|
|
370
372
|
async extractArray(element, key, path, options) {
|
|
371
373
|
try {
|
|
@@ -396,13 +398,15 @@ class Helper {
|
|
|
396
398
|
}
|
|
397
399
|
|
|
398
400
|
/**
|
|
401
|
+
* Determines the ioBroker role string for a datapoint based on its value and metadata.
|
|
399
402
|
*
|
|
400
|
-
* @param element
|
|
401
|
-
* @param options
|
|
402
|
-
* @param dpName
|
|
403
|
+
* @param {*} element - The value or metadata object to derive the role from.
|
|
404
|
+
* @param {object|boolean} options - Parsing options or write flag.
|
|
405
|
+
* @param {string} [dpName] - The datapoint name used to detect time-based roles.
|
|
406
|
+
* @returns {string} The ioBroker role string (e.g. "state", "switch", "text").
|
|
403
407
|
*/
|
|
404
408
|
getRole(element, options, dpName) {
|
|
405
|
-
const write = options.write;
|
|
409
|
+
// const write = options.write;
|
|
406
410
|
const hasStates = element && typeof element === "object" && element.states !== undefined;
|
|
407
411
|
|
|
408
412
|
|
|
@@ -412,7 +416,7 @@ class Helper {
|
|
|
412
416
|
}
|
|
413
417
|
|
|
414
418
|
if (hasStates) {
|
|
415
|
-
if (element.type
|
|
419
|
+
if (element.type === "boolean") {
|
|
416
420
|
delete element.states;
|
|
417
421
|
return "button";
|
|
418
422
|
}
|
|
@@ -431,8 +435,10 @@ class Helper {
|
|
|
431
435
|
return "state";
|
|
432
436
|
}
|
|
433
437
|
/**
|
|
438
|
+
* Resolves and normalises the value from a ZWave command class metadata object.
|
|
434
439
|
*
|
|
435
|
-
* @param element
|
|
440
|
+
* @param {object} element - The metadata object containing type, value, min, writeable and readable fields.
|
|
441
|
+
* @returns {*} The resolved and normalised value ready for use as an ioBroker state value.
|
|
436
442
|
*/
|
|
437
443
|
resolveCommandClassValue(element) {
|
|
438
444
|
const type = element.type;
|
|
@@ -494,8 +500,9 @@ class Helper {
|
|
|
494
500
|
|
|
495
501
|
|
|
496
502
|
/**
|
|
503
|
+
* Creates the ready and status state objects directly on the node device.
|
|
497
504
|
*
|
|
498
|
-
* @param nodeId
|
|
505
|
+
* @param {string} nodeId - The formatted node ID used as the ioBroker object path prefix.
|
|
499
506
|
*/
|
|
500
507
|
async createReadyStatus(nodeId) {
|
|
501
508
|
// leg die status direkt auch an
|
|
@@ -529,12 +536,13 @@ class Helper {
|
|
|
529
536
|
native: {},
|
|
530
537
|
});
|
|
531
538
|
}
|
|
532
|
-
/**
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
539
|
+
/**
|
|
540
|
+
* Updates the name or description of an existing ioBroker device object.
|
|
541
|
+
*
|
|
542
|
+
* @param {string} nodeId - The ioBroker object ID of the device to update.
|
|
543
|
+
* @param {object} element - The element containing the new name, productLabel, manufacturer or desc.
|
|
544
|
+
* @param {boolean} [nameChange] - If true, updates the common name; otherwise updates the description.
|
|
545
|
+
*/
|
|
538
546
|
async updateDevice(nodeId, element, nameChange = true) {
|
|
539
547
|
const obj = await this.adapter.getObjectAsync(nodeId);
|
|
540
548
|
if (obj) {
|
|
@@ -553,6 +561,22 @@ class Helper {
|
|
|
553
561
|
}
|
|
554
562
|
}
|
|
555
563
|
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Sets or conditionally updates an ioBroker state value.
|
|
567
|
+
*
|
|
568
|
+
* @param {string} path - The ioBroker state ID to set.
|
|
569
|
+
* @param {*} value - The value to write to the state.
|
|
570
|
+
* @param {boolean} [change] - If true, uses setState (unconditional); otherwise uses setStateChanged.
|
|
571
|
+
*/
|
|
572
|
+
async changeState(path, value, change = false) {
|
|
573
|
+
if (change) {
|
|
574
|
+
this.adapter.setState(path, value, true);
|
|
575
|
+
} else {
|
|
576
|
+
this.adapter.setStateChanged(path, value, true);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
556
580
|
}
|
|
557
581
|
|
|
558
582
|
module.exports = {
|
package/lib/statesController.js
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Controls reading and writing of ioBroker states for the ZWave adapter.
|
|
3
3
|
*/
|
|
4
4
|
class StatesController {
|
|
5
5
|
/**
|
|
6
|
+
* Creates a new StatesController instance.
|
|
6
7
|
*
|
|
7
|
-
* @param adapter
|
|
8
|
-
* @param deviceCache
|
|
8
|
+
* @param {object} adapter - The ioBroker adapter instance.
|
|
9
9
|
*/
|
|
10
10
|
constructor(adapter) {
|
|
11
11
|
this.adapter = adapter;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
+
* Sets a state value unconditionally, skipping null/undefined values.
|
|
15
16
|
*
|
|
16
|
-
* @param stateName
|
|
17
|
-
* @param value
|
|
17
|
+
* @param {string} stateName - The ioBroker state ID to set.
|
|
18
|
+
* @param {*} value - The value to write to the state.
|
|
18
19
|
*/
|
|
19
20
|
async setStateSafelyAsync(stateName, value) {
|
|
20
21
|
if (value === undefined || value === null) {
|
|
@@ -24,9 +25,10 @@ class StatesController {
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
/**
|
|
28
|
+
* Sets a state value only if it has changed, skipping null/undefined values.
|
|
27
29
|
*
|
|
28
|
-
* @param stateName
|
|
29
|
-
* @param value
|
|
30
|
+
* @param {string} stateName - The ioBroker state ID to set.
|
|
31
|
+
* @param {*} value - The value to write to the state.
|
|
30
32
|
*/
|
|
31
33
|
async setStateChangedSafelyAsync(stateName, value) {
|
|
32
34
|
if (value === undefined || value === null) {
|
|
@@ -36,8 +38,9 @@ class StatesController {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
/**
|
|
41
|
+
* Reads all writable ZWave states from ioBroker and returns them as a map keyed by object ID.
|
|
39
42
|
*
|
|
40
|
-
* @
|
|
43
|
+
* @returns {Promise<object>} A map of writable state IDs to their MQTT path and write flag.
|
|
41
44
|
*/
|
|
42
45
|
async subscribeAllWritableExistsStates() {
|
|
43
46
|
const writableStates = {};
|
|
@@ -62,7 +65,8 @@ class StatesController {
|
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
/**
|
|
65
|
-
*
|
|
68
|
+
* Sets all node ready-states to false, all status-states to "unknown"
|
|
69
|
+
* and the gateway status to "offline".
|
|
66
70
|
*/
|
|
67
71
|
async setAllAvailableToFalse() {
|
|
68
72
|
const readyStates = await this.adapter.getStatesAsync("*.ready");
|
package/lib/utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Converts a byte array to a word array.
|
|
2
3
|
*
|
|
3
|
-
* @param ba
|
|
4
|
+
* @param {number[]} ba - The byte array to convert.
|
|
4
5
|
*/
|
|
5
6
|
function bytesArrayToWordArray(ba) {
|
|
6
7
|
const wa = [];
|
|
@@ -13,8 +14,9 @@ function bytesArrayToWordArray(ba) {
|
|
|
13
14
|
// If the value is greater than 1000, kelvin is assumed.
|
|
14
15
|
// If smaller, it is assumed to be mired.
|
|
15
16
|
/**
|
|
17
|
+
* Converts a temperature value to mired.
|
|
16
18
|
*
|
|
17
|
-
* @param t
|
|
19
|
+
* @param {number} t - Temperature value in Kelvin or mired.
|
|
18
20
|
*/
|
|
19
21
|
function toMired(t) {
|
|
20
22
|
let miredValue = t;
|
|
@@ -25,8 +27,9 @@ function toMired(t) {
|
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/**
|
|
30
|
+
* Converts between mired and Kelvin.
|
|
28
31
|
*
|
|
29
|
-
* @param t
|
|
32
|
+
* @param {number} t - Temperature value to convert.
|
|
30
33
|
*/
|
|
31
34
|
function miredKelvinConversion(t) {
|
|
32
35
|
return Math.round(1000000 / t);
|
|
@@ -35,8 +38,8 @@ function miredKelvinConversion(t) {
|
|
|
35
38
|
/**
|
|
36
39
|
* Converts a decimal number to a hex string with zero-padding
|
|
37
40
|
*
|
|
38
|
-
* @param decimal
|
|
39
|
-
* @param padding
|
|
41
|
+
* @param {number} decimal - The decimal number to convert.
|
|
42
|
+
* @param {number} padding - The minimum length of the resulting hex string.
|
|
40
43
|
*/
|
|
41
44
|
function decimalToHex(decimal, padding) {
|
|
42
45
|
let hex = Number(decimal).toString(16);
|
|
@@ -53,8 +56,9 @@ function decimalToHex(decimal, padding) {
|
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
/**
|
|
59
|
+
* Removes all elements from an array in place.
|
|
56
60
|
*
|
|
57
|
-
* @param array
|
|
61
|
+
* @param {any[]} array - The array to clear.
|
|
58
62
|
*/
|
|
59
63
|
function clearArray(array) {
|
|
60
64
|
while (array.length > 0) {
|
|
@@ -63,9 +67,10 @@ function clearArray(array) {
|
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
/**
|
|
70
|
+
* Moves all elements from source array into target array.
|
|
66
71
|
*
|
|
67
|
-
* @param source
|
|
68
|
-
* @param target
|
|
72
|
+
* @param {any[]} source - The source array to move elements from.
|
|
73
|
+
* @param {any[]} target - The target array to move elements into.
|
|
69
74
|
*/
|
|
70
75
|
function moveArray(source, target) {
|
|
71
76
|
while (source.length > 0) {
|
|
@@ -74,16 +79,18 @@ function moveArray(source, target) {
|
|
|
74
79
|
}
|
|
75
80
|
|
|
76
81
|
/**
|
|
82
|
+
* Checks whether a value is a plain object.
|
|
77
83
|
*
|
|
78
|
-
* @param item
|
|
84
|
+
* @param {any} item - The value to check.
|
|
79
85
|
*/
|
|
80
86
|
function isObject(item) {
|
|
81
87
|
return typeof item === "object" && !Array.isArray(item) && item !== null;
|
|
82
88
|
}
|
|
83
89
|
|
|
84
90
|
/**
|
|
91
|
+
* Checks whether a value is valid JSON.
|
|
85
92
|
*
|
|
86
|
-
* @param item
|
|
93
|
+
* @param {any} item - The value to check.
|
|
87
94
|
*/
|
|
88
95
|
function isJson(item) {
|
|
89
96
|
let value = typeof item !== "string" ? JSON.stringify(item) : item;
|
|
@@ -97,8 +104,9 @@ function isJson(item) {
|
|
|
97
104
|
}
|
|
98
105
|
|
|
99
106
|
/**
|
|
107
|
+
* Returns the last segment of a dot- or slash-separated string.
|
|
100
108
|
*
|
|
101
|
-
* @param input
|
|
109
|
+
* @param {string} input - The input string to parse.
|
|
102
110
|
*/
|
|
103
111
|
function getLastSegment(input) {
|
|
104
112
|
if (typeof input !== "string") {
|
|
@@ -109,7 +117,9 @@ function getLastSegment(input) {
|
|
|
109
117
|
}
|
|
110
118
|
|
|
111
119
|
/**
|
|
112
|
-
*
|
|
120
|
+
* Checks whether a value is numeric (finite number or numeric string).
|
|
121
|
+
*
|
|
122
|
+
* @param {any} value - The value to check.
|
|
113
123
|
* @returns {boolean}
|
|
114
124
|
*/
|
|
115
125
|
function isNumeric(value) {
|
|
@@ -130,8 +140,9 @@ function isNumeric(value) {
|
|
|
130
140
|
}
|
|
131
141
|
|
|
132
142
|
/**
|
|
143
|
+
* Replaces the last dot in a string with an underscore.
|
|
133
144
|
*
|
|
134
|
-
* @param str
|
|
145
|
+
* @param {string} str - The string to process.
|
|
135
146
|
*/
|
|
136
147
|
function replaceLastDot(str) {
|
|
137
148
|
const idx = str.lastIndexOf(".");
|
|
@@ -139,8 +150,18 @@ function replaceLastDot(str) {
|
|
|
139
150
|
}
|
|
140
151
|
|
|
141
152
|
/**
|
|
153
|
+
* Removes a trailing dot from a string if present.
|
|
154
|
+
*
|
|
155
|
+
* @param {string|undefined} str - The string to process.
|
|
156
|
+
*/
|
|
157
|
+
function deleteLastDot(str) {
|
|
158
|
+
return str.endsWith(".") ? str.slice(0, -1) : str;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Trims and normalises an object name string.
|
|
142
163
|
*
|
|
143
|
-
* @param str
|
|
164
|
+
* @param {string} str - The string to format.
|
|
144
165
|
*/
|
|
145
166
|
function formatObject(str) {
|
|
146
167
|
if (typeof str !== "string") {
|
|
@@ -150,8 +171,9 @@ function formatObject(str) {
|
|
|
150
171
|
}
|
|
151
172
|
|
|
152
173
|
/**
|
|
174
|
+
* Replaces all dots in an MQTT topic with slashes.
|
|
153
175
|
*
|
|
154
|
-
* @param input
|
|
176
|
+
* @param {string} input - The MQTT topic string to format.
|
|
155
177
|
*/
|
|
156
178
|
function formatMQTT(input) {
|
|
157
179
|
if (typeof input !== "string") {
|
|
@@ -161,17 +183,19 @@ function formatMQTT(input) {
|
|
|
161
183
|
}
|
|
162
184
|
|
|
163
185
|
/**
|
|
186
|
+
* Zero-pads the numeric suffix of a node ID string.
|
|
164
187
|
*
|
|
165
|
-
* @param nodeId
|
|
166
|
-
* @param width
|
|
188
|
+
* @param {string} nodeId - The node ID string to pad.
|
|
189
|
+
* @param {number} [width] - The desired minimum width of the numeric part.
|
|
167
190
|
*/
|
|
168
191
|
function padNodeId(nodeId, width = 3) {
|
|
169
192
|
return nodeId.replace(/(\d+)$/, (m) => m.padStart(width, "0"));
|
|
170
193
|
}
|
|
171
194
|
|
|
172
195
|
/**
|
|
196
|
+
* Returns a human-readable status text for a given node status code.
|
|
173
197
|
*
|
|
174
|
-
* @param status
|
|
198
|
+
* @param {number} status - The numeric status code.
|
|
175
199
|
*/
|
|
176
200
|
function getStatusText(status) {
|
|
177
201
|
const nodeStatus = {
|
|
@@ -186,8 +210,9 @@ function getStatusText(status) {
|
|
|
186
210
|
}
|
|
187
211
|
|
|
188
212
|
/**
|
|
213
|
+
* Formats a node ID, padding numeric IDs with a prefix.
|
|
189
214
|
*
|
|
190
|
-
* @param nodeIdOriginal
|
|
215
|
+
* @param {string|number} nodeIdOriginal - The original node ID to format.
|
|
191
216
|
*/
|
|
192
217
|
function formatNodeId(nodeIdOriginal) {
|
|
193
218
|
let nodeId = nodeIdOriginal;
|
|
@@ -215,4 +240,5 @@ module.exports = {
|
|
|
215
240
|
padNodeId,
|
|
216
241
|
getStatusText,
|
|
217
242
|
formatObject,
|
|
243
|
+
deleteLastDot,
|
|
218
244
|
};
|
|
@@ -7,19 +7,22 @@ let pingTimeout;
|
|
|
7
7
|
let autoRestartTimeout;
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Manages the WebSocket connection to the zwave-js-ui server.
|
|
11
11
|
*/
|
|
12
12
|
class WebsocketController {
|
|
13
13
|
/**
|
|
14
|
+
* Creates a new WebsocketController instance.
|
|
14
15
|
*
|
|
15
|
-
* @param adapter
|
|
16
|
+
* @param {object} adapter - The ioBroker adapter instance.
|
|
16
17
|
*/
|
|
17
18
|
constructor(adapter) {
|
|
18
19
|
this.adapter = adapter;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
/**
|
|
23
|
+
* Initialises and connects the WebSocket client to the zwave-js-ui server.
|
|
22
24
|
*
|
|
25
|
+
* @returns {WebSocket} The created WebSocket client instance.
|
|
23
26
|
*/
|
|
24
27
|
initWsClient() {
|
|
25
28
|
try {
|
|
@@ -64,8 +67,9 @@ class WebsocketController {
|
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
/**
|
|
70
|
+
* Sends a message to the zwave-js-ui server via the WebSocket connection.
|
|
67
71
|
*
|
|
68
|
-
* @param message
|
|
72
|
+
* @param {string} message - The message payload to send.
|
|
69
73
|
*/
|
|
70
74
|
send(message) {
|
|
71
75
|
if (wsClient.readyState !== WebSocket.OPEN) {
|
|
@@ -76,7 +80,7 @@ class WebsocketController {
|
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
/**
|
|
79
|
-
*
|
|
83
|
+
* Sends a WebSocket ping to the server and schedules the next ping.
|
|
80
84
|
*/
|
|
81
85
|
sendPingToServer() {
|
|
82
86
|
//this.logDebug('Send ping to server');
|
|
@@ -87,7 +91,7 @@ class WebsocketController {
|
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
/**
|
|
90
|
-
*
|
|
94
|
+
* Resets the heartbeat timeout; terminates the connection if no pong is received in time.
|
|
91
95
|
*/
|
|
92
96
|
wsHeartbeat() {
|
|
93
97
|
clearTimeout(pingTimeout);
|
|
@@ -98,7 +102,7 @@ class WebsocketController {
|
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
/**
|
|
101
|
-
*
|
|
105
|
+
* Schedules an automatic reconnect attempt after the configured restart timeout.
|
|
102
106
|
*/
|
|
103
107
|
async autoRestart() {
|
|
104
108
|
this.adapter.log.warn(`Start try again in ${restartTimeout / 1000} seconds...`);
|
|
@@ -108,7 +112,7 @@ class WebsocketController {
|
|
|
108
112
|
}
|
|
109
113
|
|
|
110
114
|
/**
|
|
111
|
-
*
|
|
115
|
+
* Closes the WebSocket connection if it is currently open.
|
|
112
116
|
*/
|
|
113
117
|
closeConnection() {
|
|
114
118
|
if (wsClient && wsClient.readyState !== WebSocket.CLOSED) {
|
|
@@ -117,7 +121,7 @@ class WebsocketController {
|
|
|
117
121
|
}
|
|
118
122
|
|
|
119
123
|
/**
|
|
120
|
-
*
|
|
124
|
+
* Clears all active timers (ping, pingTimeout, autoRestartTimeout).
|
|
121
125
|
*/
|
|
122
126
|
async allTimerClear() {
|
|
123
127
|
clearTimeout(pingTimeout);
|
package/main.js
CHANGED
|
@@ -160,6 +160,7 @@ class zwavews extends core.Adapter {
|
|
|
160
160
|
this.setStateChanged('info.connection', false, true);
|
|
161
161
|
await statesController.setAllAvailableToFalse();
|
|
162
162
|
startListening = false;
|
|
163
|
+
allNodesCreated = false;
|
|
163
164
|
deviceCache = [];
|
|
164
165
|
this.nodeCache = [];
|
|
165
166
|
this.log.info('Websocket connection closed. Attempting to reconnect...');
|
|
@@ -263,7 +264,7 @@ class zwavews extends core.Adapter {
|
|
|
263
264
|
}
|
|
264
265
|
}
|
|
265
266
|
|
|
266
|
-
parsePath = utils.formatObject(parsePath);
|
|
267
|
+
parsePath = utils.deleteLastDot(utils.formatObject(parsePath));
|
|
267
268
|
|
|
268
269
|
if (nodeArg.commandClass === 119) { // sonderlocke für node naming
|
|
269
270
|
switch (nodeArg.property) {
|
|
@@ -291,8 +292,13 @@ class zwavews extends core.Adapter {
|
|
|
291
292
|
parsePath = `${parsePath}_${nodeArg.endpoint}`;
|
|
292
293
|
}
|
|
293
294
|
|
|
294
|
-
|
|
295
|
+
parsePath = utils.deleteLastDot(parsePath); // check again
|
|
295
296
|
|
|
297
|
+
if (eventTyp.event === 'value notification') {
|
|
298
|
+
await helper.parse(`${parsePath}`, nodeArg.newValue, options, true);
|
|
299
|
+
} else {
|
|
300
|
+
await helper.parse(`${parsePath}`, nodeArg.newValue, options, false);
|
|
301
|
+
}
|
|
296
302
|
break;
|
|
297
303
|
}
|
|
298
304
|
|
|
@@ -381,11 +387,9 @@ class zwavews extends core.Adapter {
|
|
|
381
387
|
if (["exmqtt", "intmqtt"].includes(this.config.connectionType)) {
|
|
382
388
|
if (mqttClient && !mqttClient.closed) {
|
|
383
389
|
try {
|
|
384
|
-
if (mqttClient) {
|
|
385
390
|
mqttClient.end();
|
|
386
|
-
}
|
|
387
391
|
} catch (e) {
|
|
388
|
-
|
|
392
|
+
this.log.error(e);
|
|
389
393
|
}
|
|
390
394
|
}
|
|
391
395
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.zwavews",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "zwavews adapter for ioBroker",
|
|
5
5
|
"author": {
|
|
6
|
-
"name": "
|
|
6
|
+
"name": "Arthur Rupp",
|
|
7
7
|
"email": "arteck@outlook.com"
|
|
8
8
|
},
|
|
9
9
|
"homepage": "https://github.com/arteck/ioBroker.zwavews",
|
|
@@ -26,28 +26,28 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@iobroker/adapter-core": "^3.3.2",
|
|
29
|
-
"@iobroker/dm-utils": "^
|
|
29
|
+
"@iobroker/dm-utils": "^3.0.3",
|
|
30
30
|
"humanize-duration": "^3.33.2",
|
|
31
31
|
"aedes": "^0.51.3",
|
|
32
32
|
"aedes-persistence-nedb": "^2.0.3",
|
|
33
|
-
"mqtt": "^5.15.
|
|
33
|
+
"mqtt": "^5.15.1",
|
|
34
34
|
"net": "^1.0.2",
|
|
35
35
|
"node-schedule": "^2.1.1",
|
|
36
36
|
"sharp": "^0.34.5",
|
|
37
|
-
"ws": "^8.
|
|
37
|
+
"ws": "^8.20.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@alcalzone/release-script": "^5.1.1",
|
|
41
|
-
"@alcalzone/release-script-plugin-iobroker": "^
|
|
41
|
+
"@alcalzone/release-script-plugin-iobroker": "^5.1.2",
|
|
42
42
|
"@alcalzone/release-script-plugin-license": "^5.1.1",
|
|
43
|
-
"@alcalzone/release-script-plugin-manual-review": "^
|
|
43
|
+
"@alcalzone/release-script-plugin-manual-review": "^5.1.1",
|
|
44
44
|
"@iobroker/adapter-dev": "^1.5.0",
|
|
45
45
|
"@iobroker/testing": "^5.2.2",
|
|
46
46
|
"@iobroker/eslint-config": "^2.2.0",
|
|
47
47
|
"@tsconfig/node14": "^14.1.8",
|
|
48
|
-
"@types/node": "^25.
|
|
48
|
+
"@types/node": "^25.5.0",
|
|
49
49
|
"@types/node-schedule": "^2.1.8",
|
|
50
|
-
"typescript": "~
|
|
50
|
+
"typescript": "~6.0.2"
|
|
51
51
|
},
|
|
52
52
|
"main": "main.js",
|
|
53
53
|
"files": [
|