iobroker.zwavews 0.1.5 → 0.2.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/README.md CHANGED
@@ -17,6 +17,13 @@
17
17
 
18
18
  The `zwavews` adapter connects a [`zwave-js-ui`](https://zwave-js.github.io/zwave-js-ui/#/) to ioBroker and creates corresponding data points for devices, values, and statuses. This allows Z-Wave devices to be conveniently used in visualizations, logic, and automations.
19
19
 
20
+ ### Features
21
+ * **Real-time communication**: Instantly receives updates of device values and statuses via WebSocket or MQTT.
22
+ * **Auto-Discovery**: Automatically creates and updates the device and state structure in ioBroker from the `zwave-js-ui` nodes.
23
+ * **Device Management**: View battery levels, connection status, and detailed device metrics right from the ioBroker interface.
24
+ * **Firmware Updates**: Observe firmware update progress directly via the adapter's logs and states.
25
+ * **State Control**: Send commands and update values natively through the ioBroker object tree.
26
+ * **Support for multiple protocols**: You can connect to `zwave-js-ui` using WebSocket, External MQTT, or an Internal Dummy MQTT server.
20
27
 
21
28
  ## Adapter Documentation
22
29
 
@@ -35,6 +42,12 @@ Activate WS Server Settings in `zwave-js-ui` we use the Home Assistant Settings
35
42
 
36
43
 
37
44
  ## Changelog
45
+ ### 0.2.0 (2026-04-26)
46
+ * (arteck) del deprectated setStateAsync
47
+
48
+ ### 0.1.6 (2026-04-23)
49
+ * (arteck) add test
50
+
38
51
  ### 0.1.5 (2026-04-21)
39
52
  * (arteck) upd devicemanager
40
53
 
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "zwavews",
4
- "version": "0.1.5",
4
+ "version": "0.2.0",
5
5
  "news": {
6
+ "0.2.0": {
7
+ "en": "del deprectated setStateAsync",
8
+ "de": "del deprected setStateAsync",
9
+ "ru": "обсуждение StateAsync",
10
+ "pt": "del deprectated setStateAsync",
11
+ "nl": "del deprectated setStateAsync",
12
+ "fr": "set dépréciéStateAsync",
13
+ "it": "del set deprettatoStateAsync",
14
+ "es": "del deprectated setStateAsync",
15
+ "pl": "del deprectated setStateAsync",
16
+ "uk": "deprectated setStateAsync",
17
+ "zh-cn": "已贬值的设置"
18
+ },
19
+ "0.1.6": {
20
+ "en": "add test",
21
+ "de": "test hinzufügen",
22
+ "ru": "добавить тест",
23
+ "pt": "adicionar teste",
24
+ "nl": "test toevoegen",
25
+ "fr": "ajouter un essai",
26
+ "it": "aggiungere test",
27
+ "es": "agregar la prueba",
28
+ "pl": "dodać test",
29
+ "uk": "додати тест",
30
+ "zh-cn": "添加测试"
31
+ },
6
32
  "0.1.5": {
7
33
  "en": "upd devicemanager",
8
34
  "de": "gerätemanager und -manager",
@@ -67,32 +93,6 @@
67
93
  "pl": "dodaj informacje o debugowaniu",
68
94
  "uk": "додати інформацію debug",
69
95
  "zh-cn": "添加调试信息"
70
- },
71
- "0.1.0": {
72
- "en": "BREAKING CHANGE - dp name is now with underline\nadd deviceManager\nfix dp's with a space\nfix dp's with special chars",
73
- "de": "BREAKING CHANGE - dp name ist jetzt mit unterstreichen\nadd-Gerät Manager\ndp's mit einem raum reparieren\nfix dp's with special chars",
74
- "ru": "BREAKING CHANGE - название dp теперь с подчеркиванием\nдобавить устройство Менеджер\nустановить dp в пространстве\nобсуждение dp's with special chars",
75
- "pt": "MUDANÇA DE INÍCIO - nome dp está agora com sublinhado\nadicionar dispositivo Gestor\ncorrigir dp's com um espaço\ncorrigir dp's com caracteres especiais",
76
- "nl": "BREAKING VERANDERING - dp naam is nu met benadrukken\napparaat toevoegen Beheerder\nfix dp's met een spatie\nfix dp's met speciale tekens",
77
- "fr": "CHANGEMENT DE BREAKING - DP nom est maintenant avec le soulignement\najouter un périphérique Gestionnaire\nréparer dp's avec un espace\nfixer dp's avec des caractères spéciaux",
78
- "it": "BREAKING CHANGE - il nome dp è ora in linea\naggiungere dispositivo Manager\nfix dp's con uno spazio\nsistemare dp's con beneficenza speciali",
79
- "es": "CAMBIO DE BREAKING - nombre dp ahora es con subrayado\nañadir dispositivo Manager\narreglar dp con un espacio\narreglar dp con chars especiales",
80
- "pl": "ZMIANA BREAKING - nazwa dp jest teraz z podkreśleniem\ndodaj urządzenie Kierownik\nfix dp 's z przestrzenią\nfix dp 's ze specjalnymi znakami",
81
- "uk": "BREAKING CHANGE - ім'я dp тепер з онлайн\nдодати пристрій Менеджер\nзакріпити dp's з космосом\nфіксувати dp з особливими chars",
82
- "zh-cn": "断裂变换 - dp 名称现在是下划线\n添加设备 经理\n修补 dp 的空格\n修复 dp 与特殊字符"
83
- },
84
- "0.0.18": {
85
- "en": "add info.sendMessageAllowed object to allow sending the message to zwave-ui-js\nadd new checkbox to set info.sendMessageAllowed immediately after starting the adapter",
86
- "de": "add info.sendMessageAllowed object to let send the message to zwave-ui-js\nneue Checkbox hinzufügen, um info.sendMessage einzustellen Sofort nach dem Start des Adapters zugelassen",
87
- "ru": "добавить info.sendMessageРазрешенный объект для отправки сообщения на zwave-ui-js\nдобавить новый флажок для установки info.sendMessage Допускается сразу после запуска адаптера",
88
- "pt": "add info.sendMessagePermitido objeto para permitir o envio da mensagem para zwave-ui-js\nadicionar nova caixa de seleção para definir info.sendMessage Permitido imediatamente após iniciar o adaptador",
89
- "nl": "info.sendMessageToegewezen object om het bericht naar zwave-ui-js te versturen\nnieuwe selectievakje toevoegen om info.sendMessage in te stellen Onmiddellijk na het starten van de adapter toegestaan",
90
- "fr": "ajouter info.sendMessageObjet autorisé pour permettre l'envoi du message à zwave-ui-js\najouter une nouvelle case à cocher pour définir info.sendMessage Autorisé immédiatement après le démarrage de l'adaptateur",
91
- "it": "aggiungere info.sendMessageAllowed oggetto per consentire l'invio del messaggio a zwave-ui-js\naggiungere nuova casella di controllo per impostare info.sendMessage Consentito subito dopo l'avvio dell'adattatore",
92
- "es": "añadir info.sendMessagePropósito permitido enviar el mensaje a zwave-ui-js\nañadir nueva casilla para configurar información.sendMessage Permitido inmediatamente después de iniciar el adaptador",
93
- "pl": "dodaj info.sendMessageDopuszczalny obiekt, aby umożliwić wysyłanie wiadomości do zwave- ui- js\ndodaj nową opcję do ustawienia info.sendMessage Dozwolone bezpośrednio po uruchomieniu adaptera",
94
- "uk": "додайте інформацію.sendMessageВсього об'єкту, щоб дозволити надсилати повідомлення на zwave-ui-js\nдодати нову прапорець, щоб встановити інформацію.sendMessage Допускається відразу після запуску адаптера",
95
- "zh-cn": "添加信息. sendMessage Allowed 对象允许将消息发送到 zwave- ui- js\n添加新复选框以设置信息. sendMessage 启动适配器后立即允许"
96
96
  }
97
97
  },
98
98
  "titleLang": {
package/lib/helper.js CHANGED
@@ -316,8 +316,8 @@ return "boolean";
316
316
 
317
317
  if (isObj) {
318
318
  if (Object.keys(valDP).length > 0) {
319
- options.write = false;
320
- await this.parse(fullPath, valDP, options);
319
+ // FIX: options-Objekt nicht mutieren – Spread-Kopie verwenden
320
+ await this.parse(fullPath, valDP, { ...options, write: false });
321
321
  }
322
322
  continue;
323
323
  }
@@ -612,7 +612,7 @@ return;
612
612
  */
613
613
  async changeState(path, value, change = false) {
614
614
  if (change) {
615
- await this.adapter.setStateAsync(path, value, true);
615
+ this.adapter.setState(path, value, true);
616
616
  } else {
617
617
  await this.adapter.setStateChangedAsync(path, value, true);
618
618
  }
@@ -33,7 +33,7 @@ class MqttServerController {
33
33
  this.adapter.config.mqttServerIPBind,
34
34
  () => {
35
35
  this.adapter.log.info(
36
- `Statring MQTT-Server on IP ${this.adapter.config.mqttServerIPBind} and Port ${this.adapter.config.mqttServerPort}`,
36
+ `Starting MQTT-Server on IP ${this.adapter.config.mqttServerIPBind} and Port ${this.adapter.config.mqttServerPort}`,
37
37
  );
38
38
  },
39
39
  );
@@ -54,7 +54,7 @@ class MqttServerController {
54
54
  this.adapter.config.mqttServerIPBind,
55
55
  () => {
56
56
  this.adapter.log.info(
57
- `Statring DummyMQTT-Server on IP ${this.adapter.config.mqttServerIPBind} and Port ${this.adapter.config.mqttServerPort}`,
57
+ `Starting DummyMQTT-Server on IP ${this.adapter.config.mqttServerIPBind} and Port ${this.adapter.config.mqttServerPort}`,
58
58
  );
59
59
  },
60
60
  );
@@ -21,7 +21,7 @@ class StatesController {
21
21
  if (value === undefined || value === null) {
22
22
  return;
23
23
  }
24
- await this.adapter.setStateAsync(stateName, value, true);
24
+ this.adapter.setState(stateName, value, true);
25
25
  }
26
26
 
27
27
  /**
@@ -1,10 +1,9 @@
1
+ 'use strict';
2
+
1
3
  const WebSocket = require('ws');
2
- let wsClient;
3
- const wsHeartbeatIntervall = 5000;
4
- const restartTimeout = 1000;
5
- let ping;
6
- let pingTimeout;
7
- let autoRestartTimeout;
4
+
5
+ const WS_HEARTBEAT_INTERVAL = 5000;
6
+ const WS_RESTART_TIMEOUT = 1000;
8
7
 
9
8
  /**
10
9
  * Manages the WebSocket connection to the zwave-js-ui server.
@@ -17,12 +16,18 @@ class WebsocketController {
17
16
  */
18
17
  constructor(adapter) {
19
18
  this.adapter = adapter;
19
+
20
+ // FIX: Instanz-Properties statt Modul-globaler Variablen
21
+ this.wsClient = null;
22
+ this.ping = null;
23
+ this.pingTimeout = null;
24
+ this.autoRestartTimeout = null;
20
25
  }
21
26
 
22
27
  /**
23
28
  * Initialises and connects the WebSocket client to the zwave-js-ui server.
24
29
  *
25
- * @returns {WebSocket} The created WebSocket client instance.
30
+ * @returns {WebSocket|null} The created WebSocket client instance, or null on error.
26
31
  */
27
32
  initWsClient() {
28
33
  try {
@@ -32,37 +37,35 @@ class WebsocketController {
32
37
  wsURL += `?token=${this.adapter.config.wsToken}`;
33
38
  }
34
39
 
35
- wsClient = new WebSocket(wsURL, { rejectUnauthorized: false });
40
+ this.wsClient = new WebSocket(wsURL, { rejectUnauthorized: false });
36
41
 
37
- wsClient.on('open', () => {
38
- // Send ping to server
42
+ this.wsClient.on('open', () => {
39
43
  this.sendPingToServer();
40
- // Start Heartbeat
41
44
  this.wsHeartbeat();
42
45
  });
43
46
 
44
- wsClient.on('pong', () => {
47
+ this.wsClient.on('pong', () => {
45
48
  this.wsHeartbeat();
46
49
  });
47
50
 
48
- wsClient.on('close', async () => {
49
- clearTimeout(pingTimeout);
50
- clearTimeout(ping);
51
+ this.wsClient.on('close', () => {
52
+ clearTimeout(this.pingTimeout);
53
+ clearTimeout(this.ping);
51
54
 
52
- if (wsClient.readyState === WebSocket.CLOSED) {
55
+ if (this.wsClient.readyState === WebSocket.CLOSED) {
53
56
  this.autoRestart();
54
57
  }
55
58
  });
56
59
 
57
- wsClient.on('message', () => {});
58
-
59
- wsClient.on('error', (err) => {
60
- this.adapter.log.debug(err);
60
+ this.wsClient.on('error', (err) => {
61
+ // FIX: err.message statt komplettes Objekt loggen
62
+ this.adapter.log.warn(`<zwavews> WebSocket error: ${err.message}`);
61
63
  });
62
64
 
63
- return wsClient;
65
+ return this.wsClient;
64
66
  } catch (err) {
65
- this.adapter.log.error(err);
67
+ this.adapter.log.error(`<zwavews> initWsClient failed: ${err.message}`);
68
+ return null;
66
69
  }
67
70
  }
68
71
 
@@ -72,51 +75,56 @@ class WebsocketController {
72
75
  * @param {string} message - The message payload to send.
73
76
  */
74
77
  send(message) {
75
- if (wsClient.readyState !== WebSocket.OPEN) {
76
- this.adapter.log.warn('Cannot set State, no websocket connection to zwave-js-ui');
78
+ // FIX: Null-Check für wsClient
79
+ if (!this.wsClient || this.wsClient.readyState !== WebSocket.OPEN) {
80
+ this.adapter.log.warn('<zwavews> Cannot send message, no open websocket connection to zwave-js-ui.');
77
81
  return;
78
82
  }
79
- wsClient.send(message);
83
+ this.wsClient.send(message);
80
84
  }
81
85
 
82
86
  /**
83
87
  * Sends a WebSocket ping to the server and schedules the next ping.
84
88
  */
85
89
  sendPingToServer() {
86
- //this.logDebug('Send ping to server');
87
- wsClient.ping();
88
- ping = setTimeout(() => {
90
+ if (!this.wsClient || this.wsClient.readyState !== WebSocket.OPEN) {
91
+ return;
92
+ }
93
+ this.wsClient.ping();
94
+ this.ping = setTimeout(() => {
89
95
  this.sendPingToServer();
90
- }, wsHeartbeatIntervall);
96
+ }, WS_HEARTBEAT_INTERVAL);
91
97
  }
92
98
 
93
99
  /**
94
100
  * Resets the heartbeat timeout; terminates the connection if no pong is received in time.
95
101
  */
96
102
  wsHeartbeat() {
97
- clearTimeout(pingTimeout);
98
- pingTimeout = setTimeout(() => {
99
- this.adapter.log.warn('Websocked connection timed out');
100
- wsClient.terminate();
101
- }, wsHeartbeatIntervall + 3000);
103
+ clearTimeout(this.pingTimeout);
104
+ this.pingTimeout = setTimeout(() => {
105
+ this.adapter.log.warn('<zwavews> WebSocket connection timed out, terminating.');
106
+ if (this.wsClient) {
107
+ this.wsClient.terminate();
108
+ }
109
+ }, WS_HEARTBEAT_INTERVAL + 3000);
102
110
  }
103
111
 
104
112
  /**
105
113
  * Schedules an automatic reconnect attempt after the configured restart timeout.
106
114
  */
107
115
  autoRestart() {
108
- this.adapter.log.warn(`Start try again in ${restartTimeout / 1000} seconds...`);
109
- autoRestartTimeout = setTimeout(() => {
116
+ this.adapter.log.warn(`<zwavews> WebSocket closed, reconnecting in ${WS_RESTART_TIMEOUT / 1000}s...`);
117
+ this.autoRestartTimeout = setTimeout(() => {
110
118
  this.adapter.startWebsocket();
111
- }, restartTimeout);
119
+ }, WS_RESTART_TIMEOUT);
112
120
  }
113
121
 
114
122
  /**
115
123
  * Closes the WebSocket connection if it is currently open.
116
124
  */
117
125
  closeConnection() {
118
- if (wsClient && wsClient.readyState !== WebSocket.CLOSED) {
119
- wsClient.close();
126
+ if (this.wsClient && this.wsClient.readyState !== WebSocket.CLOSED) {
127
+ this.wsClient.close();
120
128
  }
121
129
  }
122
130
 
@@ -124,9 +132,9 @@ class WebsocketController {
124
132
  * Clears all active timers (ping, pingTimeout, autoRestartTimeout).
125
133
  */
126
134
  allTimerClear() {
127
- clearTimeout(pingTimeout);
128
- clearTimeout(ping);
129
- clearTimeout(autoRestartTimeout);
135
+ clearTimeout(this.pingTimeout);
136
+ clearTimeout(this.ping);
137
+ clearTimeout(this.autoRestartTimeout);
130
138
  }
131
139
  }
132
140
 
package/main.js CHANGED
@@ -1,509 +1,492 @@
1
1
  'use strict';
2
2
 
3
- const core = require("@iobroker/adapter-core");
4
- const mqtt = require("mqtt");
5
- const utils = require("./lib/utils");
6
- const constant = require("./lib/constants");
7
- const dmZwave = require('./lib/devicemgmt.js');
8
-
9
- const adapterInfo = require("./lib/messages").adapterInfo;
10
- const StatesController = require("./lib/statesController").StatesController;
11
- const WebsocketController = require('./lib/websocketController').WebsocketController;
12
- const Helper = require("./lib/helper").Helper;
13
-
14
- const MqttServerController = require("./lib/mqttServerController").MqttServerController;
15
-
16
- let mqttClient;
17
- let deviceCache = {};
18
- let websocketController;
19
- let mqttServerController;
20
- let statesController;
21
- let helper;
22
- let messageParseMutex = Promise.resolve();
23
- let options = {};
24
- let startListening = false;
25
- let allNodesCreated = false;
26
-
27
- let driver;
28
- let controller;
29
- let allNodes;
30
- let eventTyp;
31
-
3
+ const core = require('@iobroker/adapter-core');
4
+ const mqtt = require('mqtt');
5
+ const utils = require('./lib/utils');
6
+ const constant = require('./lib/constants');
7
+ const dmZwave = require('./lib/devicemgmt.js');
8
+
9
+ const { adapterInfo } = require('./lib/messages');
10
+ const { StatesController } = require('./lib/statesController');
11
+ const { WebsocketController } = require('./lib/websocketController');
12
+ const { Helper } = require('./lib/helper');
13
+ const { MqttServerController } = require('./lib/mqttServerController');
32
14
 
33
15
  class zwavews extends core.Adapter {
34
- constructor(options) {
35
- super({
36
- ...options,
37
- name: "zwavews",
38
- });
39
- this.on("ready", this.onReady.bind(this));
40
- this.on("stateChange", this.onStateChange.bind(this));
41
- this.on("unload", this.onUnload.bind(this));
42
- }
16
+ constructor(options) {
17
+ super({
18
+ ...options,
19
+ name: 'zwavews',
20
+ });
43
21
 
44
- async onReady() {
45
- statesController = new StatesController(this);
22
+ // Instanz-State statt Modul-globale Variablen
23
+ this.mqttClient = null;
24
+ this.deviceCache = {};
25
+ this.websocketController = null;
26
+ this.mqttServerController = null;
27
+ this.statesController = null;
28
+ this.helper = null;
29
+ this.messageParseMutex = Promise.resolve();
30
+ this.parseOptions = {};
31
+ this.startListening = false;
32
+ this.allNodesCreated = false;
33
+ this.nodeCache = {};
34
+
35
+ this.on('ready', this.onReady.bind(this));
36
+ this.on('stateChange', this.onStateChange.bind(this));
37
+ this.on('unload', this.onUnload.bind(this));
38
+ }
46
39
 
47
- // Initialize your adapter here
48
- adapterInfo(this.config, this.log);
40
+ async onReady() {
41
+ this.statesController = new StatesController(this);
49
42
 
50
- this.setStateChanged("info.connection", false, true);
51
- await statesController.setAllAvailableToFalse();
43
+ adapterInfo(this.config, this.log);
52
44
 
53
- helper = new Helper(this, deviceCache);
45
+ this.setStateChanged('info.connection', false, true);
46
+ await this.statesController.setAllAvailableToFalse();
54
47
 
55
- this.deviceManagement = new dmZwave(this);
48
+ this.helper = new Helper(this, this.deviceCache);
49
+ this.deviceManagement = new dmZwave(this);
56
50
 
57
- if (this.config.wsOnStart) {
58
- this.setStateChanged("info.sendMessageAllowed", true, true);
59
- }
51
+ if (this.config.wsOnStart) {
52
+ this.setStateChanged('info.sendMessageAllowed', true, true);
53
+ }
60
54
 
61
- this.nodeCache = {};
62
- this.setStateChanged("info.debugmessages", "", true);
55
+ this.setStateChanged('info.debugmessages', '', true);
63
56
 
57
+ // MQTT-Verbindungstypen
58
+ if (['exmqtt', 'intmqtt'].includes(this.config.connectionType)) {
59
+ if (this.config.connectionType === 'exmqtt') {
60
+ if (!this.config.externalMqttServerIP) {
61
+ this.log.warn('Please configure the External MQTT-Server connection!');
62
+ return;
63
+ }
64
64
 
65
- // MQTT
66
- if (["exmqtt", "intmqtt"].includes(this.config.connectionType)) {
67
- // External MQTT-Server
68
- if (this.config.connectionType === "exmqtt") {
69
- if (this.config.externalMqttServerIP === "") {
70
- this.log.warn(
71
- "Please configure the External MQTT-Server connection!",
72
- );
73
- return;
74
- }
65
+ const mqttClientOptions = {
66
+ clientId: `ioBroker.zwavews_${Math.random().toString(16).slice(2, 8)}`,
67
+ clean: false,
68
+ protocolVersion: 4,
69
+ reconnectPeriod: 5000,
70
+ connectTimeout: 30000,
71
+ keepalive: 30,
72
+ resubscribe: true,
73
+ };
74
+
75
+ if (this.config.externalMqttServerCredentials === true) {
76
+ mqttClientOptions.username = this.config.externalMqttServerUsername;
77
+ mqttClientOptions.password = this.config.externalMqttServerPassword;
78
+ }
75
79
 
76
- // MQTT connection settings
77
- const mqttClientOptions = {
78
- clientId: `ioBroker.zwavews_${Math.random().toString(16).slice(2, 8)}`,
79
- clean: false,
80
- protocolVersion: 4,
81
- reconnectPeriod: 5000,
82
- connectTimeout: 30000, // 30s
83
- keepalive: 30,
84
- resubscribe: true,
85
- };
86
-
87
- // Set external mqtt credentials
88
- if (this.config.externalMqttServerCredentials === true) {
89
- mqttClientOptions.username = this.config.externalMqttServerUsername;
90
- mqttClientOptions.password = this.config.externalMqttServerPassword;
91
- }
80
+ this.mqttClient = mqtt.connect(
81
+ `mqtt://${this.config.externalMqttServerIP}:${this.config.externalMqttServerPort}`,
82
+ mqttClientOptions,
83
+ );
84
+ } else {
85
+ // Interner MQTT-Server
86
+ this.mqttServerController = new MqttServerController(this);
87
+ await this.mqttServerController.createMQTTServer();
88
+ await this.delay(1500);
89
+ this.mqttClient = mqtt.connect(
90
+ `mqtt://${this.config.mqttServerIPBind}:${this.config.mqttServerPort}`,
91
+ {
92
+ clientId: `ioBroker.zwavews_${Math.random().toString(16).slice(2, 8)}`,
93
+ clean: true,
94
+ reconnectPeriod: 500,
95
+ },
96
+ );
97
+ }
92
98
 
93
- // Init connection
94
- mqttClient = mqtt.connect(
95
- `mqtt://${this.config.externalMqttServerIP}:${this.config.externalMqttServerPort}`,
96
- mqttClientOptions,
97
- );
98
- } else {
99
- // Internal MQTT-Server
100
- mqttServerController = new MqttServerController(this);
101
- await mqttServerController.createMQTTServer();
102
- await this.delay(1500);
103
- mqttClient = mqtt.connect(
104
- `mqtt://${this.config.mqttServerIPBind}:${this.config.mqttServerPort}`,
105
- {
106
- clientId: `ioBroker.zwavews_${Math.random().toString(16).slice(2, 8)}`,
107
- clean: true,
108
- reconnectPeriod: 500,
109
- },
110
- );
111
- }
112
-
113
- // MQTT Client
114
- mqttClient.on("connect", () => {
115
- this.log.info(`Connect to zwavews over ${this.config.connectionType === "exmqtt" ? "external mqtt" : "internal mqtt"} connection.`);
116
- this.setStateChanged("info.connection", true, true);
117
- });
118
-
119
- mqttClient.subscribe(`${this.config.baseTopic}/#`, (err) => {
120
- if (err) {
121
- this.log.error(`<zwavews> MQTT subscribe error: ${err.message}`);
122
- }
123
- });
99
+ // FIX: subscribe innerhalb des connect-Events, nicht außerhalb
100
+ this.mqttClient.on('connect', () => {
101
+ const connType = this.config.connectionType === 'exmqtt' ? 'external mqtt' : 'internal mqtt';
102
+ this.log.info(`Connect to zwavews over ${connType} connection.`);
103
+ this.setStateChanged('info.connection', true, true);
124
104
 
125
- mqttClient.on("message", (topic, payload) => {
126
- const rawPayload = payload.toString();
127
- let parsedPayload;
128
- try {
129
- parsedPayload = rawPayload === "" ? null : JSON.parse(rawPayload);
130
- } catch {
131
- parsedPayload = rawPayload;
132
- }
133
- const newMessage = JSON.stringify({
134
- payload: parsedPayload,
135
- topic: topic.slice(topic.indexOf("/") + 1),
136
- });
137
- this.messageParse(newMessage);
138
- });
139
- } else if (this.config.connectionType === 'ws') {
140
- // Websocket
141
- if (this.config.wsServerIP === '') {
142
- this.log.warn('Please configure the Websoket connection!');
105
+ this.mqttClient.subscribe(`${this.config.baseTopic}/#`, (err) => {
106
+ if (err) {
107
+ this.log.error(`<zwavews> MQTT subscribe error: ${err.message}`);
108
+ }
109
+ });
110
+ });
111
+
112
+ this.mqttClient.on('error', (err) => {
113
+ this.log.error(`<zwavews> MQTT client error: ${err.message}`);
114
+ });
115
+
116
+ this.mqttClient.on('offline', () => {
117
+ this.log.warn('<zwavews> MQTT client offline.');
118
+ this.setStateChanged('info.connection', false, true);
119
+ });
120
+
121
+ this.mqttClient.on('message', (topic, payload) => {
122
+ const rawPayload = payload.toString();
123
+ let parsedPayload;
124
+ try {
125
+ parsedPayload = rawPayload === '' ? null : JSON.parse(rawPayload);
126
+ } catch {
127
+ parsedPayload = rawPayload;
128
+ }
129
+ const newMessage = JSON.stringify({
130
+ payload: parsedPayload,
131
+ topic: topic.slice(topic.indexOf('/') + 1),
132
+ });
133
+ this.messageParse(newMessage);
134
+ });
135
+ } else if (this.config.connectionType === 'ws') {
136
+ if (!this.config.wsServerIP) {
137
+ this.log.warn('Please configure the Websocket connection!');
143
138
  return;
144
139
  }
145
140
 
146
- // Dummy MQTT-Server
147
141
  if (this.config.dummyMqtt === true) {
148
- mqttServerController = new MqttServerController(this);
149
- await mqttServerController.createDummyMQTTServer();
150
- this.setStateChanged("info.connection", true, true);
142
+ this.mqttServerController = new MqttServerController(this);
143
+ await this.mqttServerController.createDummyMQTTServer();
144
+ this.setStateChanged('info.connection', true, true);
151
145
  await this.delay(1500);
152
146
  }
153
147
 
154
148
  this.startWebsocket();
155
149
  }
156
- }
157
-
158
- startWebsocket() {
159
- websocketController = new WebsocketController(this);
160
- const wsClient = websocketController.initWsClient();
161
-
162
- if (!wsClient) {
163
- this.log.error('<zwavews> initWsClient returned null — websocket not started.');
164
- return;
165
- }
166
-
167
- wsClient.on('open', () => {
168
- this.log.info('Connect to zwave-js-ui over websocket connection.');
169
- startListening = true;
170
- websocketController.send(JSON.stringify({command: "start_listening"}));
171
- });
172
-
173
- wsClient.on('message', (message) => {
174
- this.messageParse(message);
175
- });
176
-
177
- wsClient.on('close', async () => {
178
- this.setStateChanged('info.connection', false, true);
179
- await statesController.setAllAvailableToFalse();
180
- startListening = false;
181
- allNodesCreated = false;
182
- deviceCache = {};
183
- this.nodeCache = {};
184
- this.log.info('Websocket connection closed. Attempting to reconnect...');
185
- });
186
- }
187
-
188
- async messageParse(message) {
189
- // Mutex lock: queue up calls to messageParse
190
- let release;
191
- const lock = new Promise((resolve) => (release = resolve));
192
- const prev = messageParseMutex;
193
- messageParseMutex = lock;
194
- await prev;
195
-
196
- try {
197
- const messageObj = JSON.parse(message);
198
-
199
- const debugDevicesState = await this.getStateAsync("info.debugId");
200
-
201
- this.log.debug(`--->>> fromZ2W_RAW1 -> ${JSON.stringify(messageObj)}`);
202
-
203
- const type = messageObj?.type;
204
-
205
- if (this.config.connectionType === 'ws') {
206
- switch (type) {
207
- case 'version': { // say hello
208
- this.setStateChanged('info.connection', true, true);
209
- this.setStateChanged('info.zwave_gateway_version', messageObj.driverVersion, true);
210
- this.setStateChanged('info.zwave_gateway_status', 'online', true);
211
- break;
212
- }
213
- case 'result': {
214
- if (messageObj.result?.success === true) {
215
- this.setStateChanged('info.debugmessages', JSON.stringify(messageObj), true);
216
- break;
217
- }
150
+ }
218
151
 
219
- if (allNodesCreated) { // wird manchmal doppelt geschickt
220
- break;
221
- }
152
+ startWebsocket() {
153
+ this.websocketController = new WebsocketController(this);
154
+ const wsClient = this.websocketController.initWsClient();
222
155
 
223
- if (!messageObj.result?.state || !Array.isArray(messageObj.result.state.nodes)) {
224
- this.log.warn('<zwavews> Invalid result.state structure received, skipping.');
225
- break;
226
- }
156
+ if (!wsClient) {
157
+ this.log.error('<zwavews> initWsClient returned null websocket not started.');
158
+ return;
159
+ }
227
160
 
228
- driver = messageObj.result.state.driver;
229
- controller = messageObj.result.state.controller;
230
- allNodes = messageObj.result.state.nodes;
161
+ wsClient.on('open', () => {
162
+ this.log.info('Connect to zwave-js-ui over websocket connection.');
163
+ this.startListening = true;
164
+ this.websocketController.send(JSON.stringify({ command: 'start_listening' }));
165
+ });
231
166
 
232
- for (const nodeData of allNodes) {
233
- const nodeId = utils.formatNodeId(nodeData.nodeId);
167
+ wsClient.on('message', (message) => {
168
+ this.messageParse(message);
169
+ });
234
170
 
235
- if (debugDevicesState && debugDevicesState.val && String(debugDevicesState.val).includes(nodeId)) {
236
- this.log.warn(`--->>> fromZ2W_RAW2-> ${JSON.stringify(nodeData)}` );
171
+ wsClient.on('close', async () => {
172
+ this.setStateChanged('info.connection', false, true);
173
+ await this.statesController.setAllAvailableToFalse();
174
+ this.startListening = false;
175
+ this.allNodesCreated = false;
176
+ this.deviceCache = {};
177
+ this.nodeCache = {};
178
+ this.log.info('Websocket connection closed. Attempting to reconnect...');
179
+ });
180
+ }
181
+
182
+ async messageParse(message) {
183
+ // FIX: release mit let deklarieren (war implizite globale Variable)
184
+ let release;
185
+ const lock = new Promise((resolve) => (release = resolve));
186
+ const prev = this.messageParseMutex;
187
+ this.messageParseMutex = lock;
188
+ await prev;
189
+
190
+ try {
191
+ const messageObj = JSON.parse(message);
192
+ const debugDevicesState = await this.getStateAsync('info.debugId');
193
+
194
+ this.log.debug(`--->>> fromZ2W_RAW1 -> ${JSON.stringify(messageObj)}`);
195
+
196
+ const type = messageObj?.type;
197
+
198
+ if (this.config.connectionType === 'ws') {
199
+ switch (type) {
200
+ case 'version': {
201
+ this.setStateChanged('info.connection', true, true);
202
+ this.setStateChanged('info.zwave_gateway_version', messageObj.driverVersion, true);
203
+ this.setStateChanged('info.zwave_gateway_status', 'online', true);
204
+ break;
237
205
  }
206
+ case 'result': {
207
+ if (messageObj.result?.success === true) {
208
+ this.setStateChanged('info.debugmessages', JSON.stringify(messageObj), true);
209
+ break;
210
+ }
211
+
212
+ if (this.allNodesCreated) {
213
+ break;
214
+ }
215
+
216
+ if (!messageObj.result?.state || !Array.isArray(messageObj.result.state.nodes)) {
217
+ this.log.warn('<zwavews> Invalid result.state structure received, skipping.');
218
+ break;
219
+ }
220
+
221
+ const { nodes: allNodes } = messageObj.result.state;
222
+
223
+ for (const nodeData of allNodes) {
224
+ const nodeId = utils.formatNodeId(nodeData.nodeId);
225
+
226
+ if (debugDevicesState?.val && String(debugDevicesState.val).includes(nodeId)) {
227
+ this.log.warn(`--->>> fromZ2W_RAW2-> ${JSON.stringify(nodeData)}`);
228
+ }
229
+
230
+ if (!this.nodeCache[nodeId]) {
231
+ if (this.config.showNodeInfoMessage) {
232
+ this.log.info(`Node Info Update for ${nodeId}`);
233
+ }
234
+ this.nodeCache[nodeId] = { nodeData };
235
+ }
236
+ await this.helper.createNode(nodeId, nodeData, this.parseOptions);
237
+ }
238
+
239
+ this.allNodesCreated = true;
238
240
 
239
- if (!this.nodeCache[nodeId]) {
240
241
  if (this.config.showNodeInfoMessage) {
241
- this.log.info(`Node Info Update for ${nodeId}`);
242
+ this.log.info('all Nodes are ready');
243
+ }
244
+ if (this.startListening) {
245
+ this.websocketController.send(JSON.stringify({ command: 'start_listening' }));
246
+ this.startListening = false;
242
247
  }
243
- this.nodeCache[nodeId] = {nodeData};
248
+ break;
244
249
  }
245
- await helper.createNode(nodeId, nodeData, options);
250
+ case 'event': {
251
+ const eventTyp = messageObj.event;
252
+
253
+ switch (eventTyp.event) {
254
+ case 'value updated':
255
+ case 'value added':
256
+ case 'value notification': {
257
+ const nodeArg = eventTyp.args;
258
+ const nodeId = utils.formatNodeId(eventTyp.nodeId);
259
+
260
+ if (debugDevicesState?.val && String(debugDevicesState.val).includes(nodeId)) {
261
+ this.log.warn(`--->>> fromZ2W_RAW2-> ${JSON.stringify(eventTyp)}`);
262
+ }
263
+
264
+ let parsePath = `${nodeId}.${nodeArg.commandClassName}.${nodeArg.propertyName
265
+ .replace(/[^\p{L}\p{N}\s]/gu, '')
266
+ .replace(/\s+/g, ' ')
267
+ .trim()}`;
268
+
269
+ if (nodeArg?.propertyKeyName) {
270
+ parsePath = `${parsePath}.${nodeArg.propertyKeyName
271
+ .replace(/[^\p{L}\p{N}\s]/gu, '')
272
+ .replace(/\s+/g, ' ')
273
+ .trim()}`;
274
+
275
+ if (constant.RGB.includes(nodeArg.propertyKeyName)) {
276
+ parsePath = utils.replaceLastDot(parsePath);
277
+ }
278
+ }
279
+
280
+ parsePath = utils.deleteLastDot(utils.formatObject(parsePath));
281
+
282
+ if (nodeArg.commandClass === 119) {
283
+ switch (nodeArg.property) {
284
+ case 'name':
285
+ await this.helper.updateDevice(nodeId, nodeArg);
286
+ parsePath = `${nodeId}.info.${nodeArg.property}`;
287
+ break;
288
+ case 'location':
289
+ break;
290
+ default:
291
+ parsePath = `${nodeId}.info.${nodeArg.property}`;
292
+ break;
293
+ }
294
+ }
295
+
296
+ this.log.debug(`${parsePath} ->> ${nodeArg.newValue}`);
297
+
298
+ if (parsePath.includes('firmwareVersions')) {
299
+ parsePath = `${parsePath}_value`;
300
+ }
301
+
302
+ if (nodeArg.endpoint != null && nodeArg.endpoint > 0) {
303
+ parsePath = `${parsePath}_${nodeArg.endpoint}`;
304
+ }
305
+
306
+ parsePath = utils.deleteLastDot(parsePath);
307
+
308
+ if (eventTyp.event === 'value notification') {
309
+ await this.helper.parse(parsePath, nodeArg.newValue, this.parseOptions, true);
310
+ } else {
311
+ await this.helper.parse(parsePath, nodeArg.newValue, this.parseOptions, false);
312
+ }
313
+ break;
314
+ }
315
+
316
+ case 'firmware update progress': {
317
+ const total = Number(eventTyp.totalFragments) || 0;
318
+ const sent = Number(eventTyp.sentFragments) || 0;
319
+ const progress = total > 0 ? Math.min(100, Math.max(0, (sent / total) * 100)) : 0;
320
+ this.log.info(
321
+ `Firmware update progress for ${utils.formatNodeId(eventTyp.nodeId)} ->> ` +
322
+ `send Fragments ${sent} -- total ${total} (${progress.toFixed(1)}%)`,
323
+ );
324
+ break;
325
+ }
326
+
327
+ case 'firmware update finished': {
328
+ this.log.info(`${utils.formatNodeId(eventTyp.nodeId)} --> ${eventTyp.event}`);
329
+ break;
330
+ }
331
+
332
+ case 'ready':
333
+ case 'sleep':
334
+ case 'wake up':
335
+ case 'alive':
336
+ case 'dead': {
337
+ const nodeId = utils.formatNodeId(eventTyp.nodeId);
338
+ await this.helper.parse(`${nodeId}.status`, eventTyp.event.toLowerCase(), this.parseOptions);
339
+
340
+ if (eventTyp.event === 'dead') {
341
+ await this.helper.parse(`${nodeId}.ready`, false, this.parseOptions);
342
+ } else {
343
+ await this.helper.parse(`${nodeId}.ready`, true, this.parseOptions);
344
+ }
345
+
346
+ if (this.config.wakeUpInfo) {
347
+ this.log.info(`${utils.formatNodeId(eventTyp.nodeId)} --> ${eventTyp.event}`);
348
+ }
349
+ break;
350
+ }
351
+
352
+ case 'node removed': {
353
+ const nodeId = utils.formatNodeId(eventTyp.nodeId);
354
+ if (this.config.useEventInDesc) {
355
+ await this.helper.updateDevice(nodeId, { desc: 'Node is Deleted' }, false);
356
+ } else {
357
+ await this.helper.updateDevice(nodeId, { name: 'Node is Deleted' }, true);
358
+ }
359
+ this.log.error(`Delete ${utils.formatNodeId(eventTyp.nodeId)}`);
360
+ break;
361
+ }
362
+
363
+ case 'interview started':
364
+ case 'interview stage completed':
365
+ case 'interview failed':
366
+ case 'interview completed':
367
+ this.log.info(`${utils.formatNodeId(eventTyp.nodeId)} --> ${eventTyp.event}`);
368
+ break;
369
+
370
+ case 'statistics updated':
371
+ case 'metadata updated':
372
+ case 'node info received':
373
+ break;
374
+
375
+ default:
376
+ if (this.config.newTypeEvent) {
377
+ this.log.warn(`New type event ->> ${eventTyp.event}`);
378
+ this.log.warn(JSON.stringify(messageObj));
379
+ }
380
+ break;
381
+ }
382
+ break;
383
+ }
384
+ default:
385
+ break;
246
386
  }
247
-
248
- allNodesCreated = true;
387
+ }
388
+ } catch (err) {
389
+ this.log.error(err);
390
+ this.log.error(`<zwavews> error message -->> ${message}`);
391
+ } finally {
392
+ release();
393
+ }
394
+ }
249
395
 
250
- if (this.config.showNodeInfoMessage) {
251
- this.log.info(`all Nodes are ready`);
252
- }
253
- if (startListening) {
254
- websocketController.send(JSON.stringify({command: "start_listening"}));
255
- startListening = false;
396
+ async onUnload(callback) {
397
+ try {
398
+ if (['exmqtt', 'intmqtt'].includes(this.config.connectionType)) {
399
+ if (this.mqttClient && !this.mqttClient.closed) {
400
+ try {
401
+ this.mqttClient.end();
402
+ } catch (e) {
403
+ this.log.error(e);
404
+ }
256
405
  }
257
- break;
258
406
  }
259
- case 'event':
260
- eventTyp = messageObj.event;
261
-
262
- switch (eventTyp.event) {
263
- case 'value updated':
264
- case 'value added':
265
- case 'value notification': {
266
- const nodeArg = eventTyp.args;
267
- const nodeId = utils.formatNodeId(eventTyp.nodeId);
268
-
269
- if (debugDevicesState && debugDevicesState.val && String(debugDevicesState.val).includes(nodeId)) {
270
- this.log.warn(`--->>> fromZ2W_RAW2-> ${JSON.stringify(eventTyp)}` );
271
- }
272
-
273
- let parsePath = `${nodeId}.${nodeArg.commandClassName}.${nodeArg.propertyName
274
- .replace(/[^\p{L}\p{N}\s]/gu, "")
275
- .replace(/\s+/g, " ")
276
- .trim()}`;
277
-
278
- if (nodeArg?.propertyKeyName) {
279
- parsePath = `${parsePath}.${nodeArg.propertyKeyName
280
- .replace(/[^\p{L}\p{N}\s]/gu, "")
281
- .replace(/\s+/g, " ")
282
- .trim()}`;
283
-
284
- if (constant.RGB.includes(nodeArg.propertyKeyName)) {
285
- parsePath = utils.replaceLastDot(parsePath);
286
- }
287
- }
288
-
289
- parsePath = utils.deleteLastDot(utils.formatObject(parsePath));
290
-
291
- if (nodeArg.commandClass === 119) { // sonderlocke für node naming
292
- switch (nodeArg.property) {
293
- case 'name':
294
- await helper.updateDevice(nodeId, nodeArg);
295
- parsePath = `${nodeId}.info.${nodeArg.property}`;
296
- break;
297
- case 'location':
298
- // intentionally ignored
299
- break;
300
- default:
301
- parsePath = `${nodeId}.info.${nodeArg.property}`;
302
- break;
303
- }
304
- }
305
-
306
- this.log.debug(`${parsePath} ->> ${nodeArg.newValue}`);
307
-
308
- if (parsePath.includes('firmwareVersions')) { // damit array werte gespeichert werden
309
- parsePath = `${parsePath}_value`;
310
- }
311
-
312
- // mehr als 1 endpoint behandeln
313
- if (nodeArg.endpoint != null && nodeArg.endpoint > 0) {
314
- parsePath = `${parsePath}_${nodeArg.endpoint}`;
315
- }
316
-
317
- parsePath = utils.deleteLastDot(parsePath); // check again
318
-
319
- if (eventTyp.event === 'value notification') {
320
- await helper.parse(`${parsePath}`, nodeArg.newValue, options, true);
321
- } else {
322
- await helper.parse(`${parsePath}`, nodeArg.newValue, options, false);
323
- }
324
- break;
325
- }
326
-
327
- case 'firmware update progress': {
328
- const total = Number(eventTyp.totalFragments) || 0;
329
- const sent = Number(eventTyp.sentFragments) || 0;
330
-
331
- const progress = total > 0 ? Math.min(100, Math.max(0, (sent / total) * 100)) : 0;
332
-
333
- this.log.info(
334
- `Firmware update progress for ${utils.formatNodeId(eventTyp.nodeId)} ->> ` + `send Fragments ${sent} -- total ${total}
335
- (${progress.toFixed(1)}%)`);
336
- break;
337
- }
338
- case 'firmware update finished': {
339
- this.log.info(`${utils.formatNodeId(eventTyp.nodeId)} --> ${eventTyp.event}`);
340
- break;
341
- }
342
- case 'ready':
343
- case 'sleep':
344
- case 'wake up':
345
- case 'alive':
346
- case 'dead': {
347
- const nodeId = utils.formatNodeId(eventTyp.nodeId);
348
- await helper.parse(`${nodeId}.status`, eventTyp.event.toLowerCase(), options);
349
-
350
- if (eventTyp.event === 'dead') {
351
- await helper.parse(`${nodeId}.ready`, false, options);
352
- } else {
353
- await helper.parse(`${nodeId}.ready`, true, options);
354
- }
355
-
356
- if (this.config.wakeUpInfo) {
357
- this.log.info(`${utils.formatNodeId(eventTyp.nodeId)} --> ${eventTyp.event}`);
358
- }
359
- break;
360
- }
361
-
362
- case 'node removed': {
363
- const nodeId = utils.formatNodeId(eventTyp.nodeId);
364
-
365
- if (this.config.useEventInDesc) {
366
- const nodeArg = {desc: "Node is Deleted"};
367
- await helper.updateDevice(nodeId, nodeArg, false);
368
- } else {
369
- const nodeArg = {name : 'Node is Deleted'};
370
- await helper.updateDevice(nodeId, nodeArg, true);
371
- }
372
- this.log.error(`Delete ${utils.formatNodeId(eventTyp.nodeId)}`);
373
- break;
374
- }
375
- case 'interview started':
376
- case 'interview stage completed':
377
- case 'interview failed':
378
- case 'interview completed':
379
- this.log.info(`${utils.formatNodeId(eventTyp.nodeId)} --> ${eventTyp.event}`);
380
- break;
381
-
382
- case 'statistics updated':
383
- case 'metadata updated':
384
- case 'node info received':
385
- break;
386
- default:
387
- if (this.config.newTypeEvent) {
388
- this.log.warn(`New type event ->> ${eventTyp.event}`);
389
- this.log.warn(JSON.stringify(messageObj));
407
+
408
+ if (this.config.connectionType === 'intmqtt' || this.config.dummyMqtt === true) {
409
+ try {
410
+ if (this.mqttServerController) {
411
+ this.mqttServerController.closeServer();
390
412
  }
391
- break;
413
+ } catch (e) {
414
+ this.log.error(e);
415
+ }
392
416
  }
393
417
 
394
- break;
395
- default:
396
- break;
418
+ if (this.websocketController) {
419
+ try {
420
+ await this.websocketController.allTimerClear();
421
+ this.websocketController.closeConnection();
422
+ } catch (e) {
423
+ this.log.error(e);
424
+ }
425
+ }
426
+
427
+ try {
428
+ if (this.statesController) {
429
+ await this.statesController.setAllAvailableToFalse();
430
+ }
431
+ } catch (e) {
432
+ this.log.error(e);
433
+ }
434
+
435
+ this.setStateChanged('info.connection', false, true);
436
+ } finally {
437
+ callback();
397
438
  }
398
- }
399
- } catch (err) {
400
- this.log.error(err);
401
- this.log.error(`<zwavews> error message -->> ${message}`);
402
- } finally {
403
- release();
404
439
  }
405
- }
406
-
407
- async onUnload(callback) {
408
- try {
409
- // Close MQTT connections
410
- if (["exmqtt", "intmqtt"].includes(this.config.connectionType)) {
411
- if (mqttClient && !mqttClient.closed) {
412
- try {
413
- mqttClient.end();
414
- } catch (e) {
415
- this.log.error(e);
416
- }
417
- }
418
- }
419
- // Internal or Dummy MQTT-Server
420
- if (this.config.connectionType === "intmqtt" || this.config.dummyMqtt === true) {
421
- try {
422
- if (mqttServerController) {
423
- mqttServerController.closeServer();
424
- }
425
- } catch (e) {
426
- this.log.error(e);
427
- }
428
- }
429
- // WebSocket cleanup
430
- if (websocketController) {
431
- try {
432
- await websocketController.allTimerClear();
433
- websocketController.closeConnection();
434
- } catch (e) {
435
- this.log.error(e);
436
- }
437
- }
438
- // Set all device available states to false
439
- try {
440
- if (statesController) {
441
- await statesController.setAllAvailableToFalse();
440
+
441
+ async onStateChange(id, state) {
442
+ if (!this.allNodesCreated) {
443
+ return;
442
444
  }
443
- } catch (e) {
444
- this.log.error(e);
445
- }
446
445
 
447
- this.setStateChanged("info.connection", false, true);
448
- } finally {
449
- callback();
450
- }
451
- }
446
+ if (state && state.ack === false) {
447
+ if (id.endsWith('info.debugId')) {
448
+ this.setStateChanged(id, state.val, true);
449
+ return;
450
+ }
452
451
 
453
- async onStateChange(id, state) {
454
- if (!allNodesCreated) { // wenn alle nodes angelegt sind, erst dann horchen auf target
455
- return;
456
- }
452
+ const obj = await this.getObjectAsync(id);
453
+ if (obj) {
454
+ const nativeObj = obj.native || {};
455
+
456
+ const m = id.match(/nodeID_0*(\d+)/i);
457
+ if (!m) {
458
+ this.log.warn(`<zwavews> Could not extract nodeId from state id: ${id}`);
459
+ return;
460
+ }
461
+ const nodeId = Number(m[1]);
462
+
463
+ const message = {
464
+ messageId: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
465
+ command: 'node.set_value',
466
+ nodeId,
467
+ valueId: nativeObj.valueId,
468
+ value: state.val,
469
+ };
470
+
471
+ const sendMessageAllowed = await this.getStateAsync('info.sendMessageAllowed');
472
+
473
+ if (sendMessageAllowed && sendMessageAllowed.val === true) {
474
+ if (this.websocketController) {
475
+ this.websocketController.send(JSON.stringify(message));
476
+ } else {
477
+ this.log.warn('<zwavews> websocketController not initialised, cannot send message.');
478
+ }
479
+ }
457
480
 
458
- if (state && state.ack === false) {
459
- if (id.endsWith("info.debugId")) {
460
- this.setStateChanged(id, state.val, true);
461
- return;
462
- }
463
-
464
- const obj = await this.getObjectAsync(id);
465
- if (obj) {
466
- const nativeObj = obj.native || {};
467
-
468
- const m = id.match(/nodeID_0*(\d+)/i);
469
- if (!m) {
470
- this.log.warn(`<zwavews> Could not extract nodeId from state id: ${id}`);
471
- return;
472
- }
473
- const nodeId = Number(m[1]);
474
-
475
- const message = {
476
- messageId: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
477
- command: "node.set_value",
478
- nodeId: nodeId,
479
- valueId: nativeObj.valueId,
480
- value: state.val
481
- };
482
-
483
- const sendMessageAllowed = await this.getStateAsync("info.sendMessageAllowed");
484
-
485
- if (sendMessageAllowed && sendMessageAllowed.val === true) {
486
- if (websocketController) {
487
- websocketController.send(JSON.stringify(message));
488
- } else {
489
- this.log.warn('<zwavews> websocketController not initialised, cannot send message.');
490
- }
491
- }
492
-
493
- this.setStateChanged('info.debugmessages', JSON.stringify(message), true);
494
- this.log.debug(`<zwavews> message onStateChange ${JSON.stringify(message)}`);
495
- }
481
+ this.setStateChanged('info.debugmessages', JSON.stringify(message), true);
482
+ this.log.debug(`<zwavews> message onStateChange ${JSON.stringify(message)}`);
483
+ }
484
+ }
496
485
  }
497
- }
498
486
  }
499
487
 
500
488
  if (require.main !== module) {
501
- // Export the constructor in compact mode
502
- /**
503
- * @param {Partial<core.AdapterOptions>} [options]
504
- */
505
- module.exports = (options) => new zwavews(options);
489
+ module.exports = (options) => new zwavews(options);
506
490
  } else {
507
- // otherwise start the instance directly
508
- new zwavews();
491
+ new zwavews();
509
492
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.zwavews",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "description": "zwavews adapter for ioBroker",
5
5
  "author": {
6
6
  "name": "Arthur Rupp",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@iobroker/adapter-core": "^3.3.2",
29
- "@iobroker/dm-utils": "^3.0.3",
29
+ "@iobroker/dm-utils": "^3.0.19",
30
30
  "humanize-duration": "^3.33.2",
31
31
  "aedes": "^0.51.3",
32
32
  "aedes-persistence-nedb": "^2.0.3",
@@ -45,7 +45,7 @@
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.5.0",
48
+ "@types/node": "^25.6.0",
49
49
  "@types/node-schedule": "^2.1.8",
50
50
  "typescript": "~6.0.2"
51
51
  },