iobroker.zigbee2mqtt 3.2.2 → 3.2.5
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 +20 -9
- package/admin/i18n/de/translations.json +16 -2
- package/admin/i18n/en/translations.json +21 -6
- package/admin/i18n/es/translations.json +19 -1
- package/admin/i18n/fr/translations.json +18 -1
- package/admin/i18n/it/translations.json +18 -1
- package/admin/i18n/nl/translations.json +18 -1
- package/admin/i18n/pl/translations.json +18 -1
- package/admin/i18n/pt/translations.json +18 -1
- package/admin/i18n/ru/translations.json +18 -1
- package/admin/i18n/uk/translations.json +18 -1
- package/admin/i18n/zh-cn/translations.json +16 -1
- package/admin/jsonConfig.json +38 -48
- package/io-package.json +44 -4
- package/lib/deviceController.js +51 -21
- package/lib/exposes.js +13 -14
- package/lib/imageController.js +1 -1
- package/lib/mqttServerController.js +25 -5
- package/lib/rgb.js +5 -5
- package/lib/states.js +23 -1
- package/lib/statesController.js +196 -28
- package/lib/websocketController.js +5 -0
- package/lib/z2mController.js +9 -1
- package/main.js +38 -12
- package/package.json +10 -10
package/lib/statesController.js
CHANGED
|
@@ -28,6 +28,76 @@ class StatesController {
|
|
|
28
28
|
this._debugDeviceList = logCustomizations.debugDevices
|
|
29
29
|
? String(logCustomizations.debugDevices).split(',').map((s) => s.trim()).filter(Boolean)
|
|
30
30
|
: [];
|
|
31
|
+
|
|
32
|
+
// PERFORMANCE: Map-Cache für Topic->Device Lookup (vermeidet concat().find() pro Nachricht)
|
|
33
|
+
this.deviceMap = new Map();
|
|
34
|
+
|
|
35
|
+
// PERFORMANCE: Session-Cache für bereits per setObjectNotExistsAsync erstellte IDs
|
|
36
|
+
// Vermeidet wiederholte js-controller-Roundtrips
|
|
37
|
+
this.ensuredObjects = new Set();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Invalidiert den Topic->Device Map-Cache sowie den Session-Cache der
|
|
42
|
+
* bereits sichergestellten Objekte. Muss aufgerufen werden, wenn
|
|
43
|
+
* deviceCache/groupCache zur Laufzeit neu aufgebaut werden.
|
|
44
|
+
*/
|
|
45
|
+
clearDeviceMap() {
|
|
46
|
+
this.deviceMap.clear();
|
|
47
|
+
this.ensuredObjects.clear();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Findet ein Gerät oder eine Gruppe anhand des Topics.
|
|
52
|
+
* Nutzt einen Map-Cache und füllt diesen bei Bedarf über eine einmalige lineare Suche.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} topic Das Topic (entspricht device.id)
|
|
55
|
+
* @returns {object|null} Das gefundene Geräte-/Gruppenobjekt oder null
|
|
56
|
+
*/
|
|
57
|
+
findDeviceByTopic(topic) { if (!topic) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const t = String(topic);
|
|
61
|
+
if (this.deviceMap.has(t)) {
|
|
62
|
+
return this.deviceMap.get(t);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Fallback: lineare Suche (einmalig) und Cache-Eintrag
|
|
66
|
+
for (const grp of this.groupCache || []) {
|
|
67
|
+
if (grp && grp.id === t) {
|
|
68
|
+
this.deviceMap.set(t, grp);
|
|
69
|
+
return grp;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const dev of this.deviceCache || []) {
|
|
73
|
+
if (dev && dev.id === t) {
|
|
74
|
+
this.deviceMap.set(t, dev);
|
|
75
|
+
return dev;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Legt ein ioBroker-Objekt nur einmal pro Session an (reduziert setObjectNotExistsAsync-Roundtrips).
|
|
83
|
+
*
|
|
84
|
+
* @param {string} id Vollständige Objekt-ID
|
|
85
|
+
* @param {object} obj Objekt-Definition für setObjectNotExistsAsync
|
|
86
|
+
*/
|
|
87
|
+
async ensureObjectOnce(id, obj) {
|
|
88
|
+
if (!id) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (this.ensuredObjects.has(id)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
await this.adapter.setObjectNotExistsAsync(id, obj);
|
|
96
|
+
this.ensuredObjects.add(id);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
// Loggen, aber nicht wiederholend blockieren
|
|
99
|
+
this.adapter.log.warn(`ensureObjectOnce: setObjectNotExistsAsync failed for ${id}: ${e}`);
|
|
100
|
+
}
|
|
31
101
|
}
|
|
32
102
|
|
|
33
103
|
/**
|
|
@@ -45,7 +115,8 @@ class StatesController {
|
|
|
45
115
|
return;
|
|
46
116
|
}
|
|
47
117
|
|
|
48
|
-
|
|
118
|
+
// PERFORMANCE: Map-basiertes Lookup statt concat().find()
|
|
119
|
+
const device = this.findDeviceByTopic(messageObj.topic);
|
|
49
120
|
if (device) {
|
|
50
121
|
try {
|
|
51
122
|
await this.setDeviceStateSafely(messageObj, device);
|
|
@@ -116,6 +187,35 @@ class StatesController {
|
|
|
116
187
|
return;
|
|
117
188
|
}
|
|
118
189
|
|
|
190
|
+
// Fix 3: Vorab prüfen, ob überhaupt ein State des Devices existiert.
|
|
191
|
+
// Nur wenn KEIN EINZIGER State existiert → requeue.
|
|
192
|
+
// Wenn mindestens einer existiert → verarbeite vorhandene, ignoriere fehlende.
|
|
193
|
+
let hasAnyExistingState = false;
|
|
194
|
+
for (const key of Object.keys(messageObj.payload)) {
|
|
195
|
+
if (hasAnyExistingState) { break; }
|
|
196
|
+
let states = device.states.filter(state => state.prop && state.prop === key);
|
|
197
|
+
if (states.length === 0) { states = device.states.filter(x => x.id === key); }
|
|
198
|
+
for (const state of states) {
|
|
199
|
+
if (this.createCache[device.ieee_address]
|
|
200
|
+
&& this.createCache[device.ieee_address][state.id]
|
|
201
|
+
&& this.createCache[device.ieee_address][state.id].created === true) {
|
|
202
|
+
hasAnyExistingState = true;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!hasAnyExistingState) {
|
|
209
|
+
// Kein einziger State existiert → Nachricht in Queue stellen
|
|
210
|
+
pushToQueue(messageObj);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// COLLECTION: Alle non-action state write-Promises sammeln und dann parallel ausführen
|
|
215
|
+
const stateWrites = [];
|
|
216
|
+
// Flag für available-Update (wenn last_seen vorhanden und config aktiv)
|
|
217
|
+
let needSetAvailableTrue = false;
|
|
218
|
+
|
|
119
219
|
for (let [key, value] of Object.entries(messageObj.payload)) {
|
|
120
220
|
if (value === undefined || value === null) {
|
|
121
221
|
continue;
|
|
@@ -136,14 +236,15 @@ class StatesController {
|
|
|
136
236
|
// some devices has addition information in payload
|
|
137
237
|
const fullPath = `${device.ieee_address}.additional`;
|
|
138
238
|
|
|
139
|
-
|
|
239
|
+
// PERFORMANCE: ensure object only once per session
|
|
240
|
+
await this.ensureObjectOnce(fullPath, {
|
|
140
241
|
type: 'channel',
|
|
141
242
|
common: {
|
|
142
243
|
name: 'hidden channelstate',
|
|
143
244
|
},
|
|
144
245
|
native: {},
|
|
145
246
|
});
|
|
146
|
-
await this.
|
|
247
|
+
await this.ensureObjectOnce(`${fullPath}.${key}`, {
|
|
147
248
|
type: 'state',
|
|
148
249
|
common: {
|
|
149
250
|
name: key,
|
|
@@ -157,7 +258,14 @@ class StatesController {
|
|
|
157
258
|
if (typeof value === 'object') {
|
|
158
259
|
value = JSON.stringify(value);
|
|
159
260
|
}
|
|
160
|
-
|
|
261
|
+
// write additional state without awaiting
|
|
262
|
+
stateWrites.push((async () => {
|
|
263
|
+
try {
|
|
264
|
+
await this.adapter.setStateChangedAsync(`${fullPath}.${key}`, value, true);
|
|
265
|
+
} catch (e) {
|
|
266
|
+
this.adapter.log.warn(`Cannot set additional state ${fullPath}.${key}: ${e}`);
|
|
267
|
+
}
|
|
268
|
+
})());
|
|
161
269
|
}
|
|
162
270
|
continue;
|
|
163
271
|
}
|
|
@@ -166,15 +274,17 @@ class StatesController {
|
|
|
166
274
|
const stateName = `${device.ieee_address}.${state.id}`;
|
|
167
275
|
|
|
168
276
|
// set available status if last_seen is set
|
|
169
|
-
if (state.id === 'last_seen' && this.adapter.config.
|
|
170
|
-
|
|
277
|
+
if (state.id === 'last_seen' && this.adapter.config.alwaysUpdateAvailableState === true) {
|
|
278
|
+
// mark flag to set available before other writes
|
|
279
|
+
needSetAvailableTrue = true;
|
|
171
280
|
}
|
|
172
281
|
|
|
173
|
-
// State noch nicht erstellt? →
|
|
282
|
+
// State noch nicht erstellt? → überspringen (Requeue erfolgt nur,
|
|
283
|
+
// wenn GAR KEIN State des Devices existiert – siehe Pre-Check oben)
|
|
174
284
|
if (!this.createCache[device.ieee_address]
|
|
175
285
|
|| !this.createCache[device.ieee_address][state.id]
|
|
176
286
|
|| this.createCache[device.ieee_address][state.id].created !== true) {
|
|
177
|
-
|
|
287
|
+
// If none created we already pushed to queue earlier
|
|
178
288
|
continue;
|
|
179
289
|
}
|
|
180
290
|
|
|
@@ -183,14 +293,34 @@ class StatesController {
|
|
|
183
293
|
if (state.prop && state.prop === 'action') {
|
|
184
294
|
actionStates.push(state);
|
|
185
295
|
}
|
|
186
|
-
else if (this.adapter.config.
|
|
187
|
-
|
|
296
|
+
else if (this.adapter.config.alwaysUpdateOccupancyState === true && state.id === 'occupancy' && value === true) {
|
|
297
|
+
// occupancy true: schedule write
|
|
298
|
+
stateWrites.push((async () => {
|
|
299
|
+
try {
|
|
300
|
+
await this.setStateSafelyAsync(stateName, value);
|
|
301
|
+
} catch (e) {
|
|
302
|
+
this.adapter.log.warn(`Cannot set occupancy ${stateName}: ${e}`);
|
|
303
|
+
}
|
|
304
|
+
})());
|
|
188
305
|
}
|
|
189
306
|
else {
|
|
190
307
|
if (state.getter) {
|
|
191
|
-
|
|
308
|
+
// schedule changed-safe write
|
|
309
|
+
stateWrites.push((async () => {
|
|
310
|
+
try {
|
|
311
|
+
await this.setStateChangedSafelyAsync(stateName, state.getter(messageObj.payload));
|
|
312
|
+
} catch (e) {
|
|
313
|
+
this.adapter.log.warn(`Cannot set state ${stateName}: ${e}`);
|
|
314
|
+
}
|
|
315
|
+
})());
|
|
192
316
|
} else {
|
|
193
|
-
|
|
317
|
+
stateWrites.push((async () => {
|
|
318
|
+
try {
|
|
319
|
+
await this.setStateChangedSafelyAsync(stateName, value);
|
|
320
|
+
} catch (e) {
|
|
321
|
+
this.adapter.log.warn(`Cannot set state ${stateName}: ${e}`);
|
|
322
|
+
}
|
|
323
|
+
})());
|
|
194
324
|
}
|
|
195
325
|
}
|
|
196
326
|
} catch (err) {
|
|
@@ -199,25 +329,52 @@ class StatesController {
|
|
|
199
329
|
}
|
|
200
330
|
}
|
|
201
331
|
|
|
332
|
+
// Wenn last_seen vorhanden: setze available zuerst (nicht parallelisiert)
|
|
333
|
+
if (needSetAvailableTrue) {
|
|
334
|
+
try {
|
|
335
|
+
await this.setStateSafelyAsync(`${device.ieee_address}.available`, true);
|
|
336
|
+
} catch (e) {
|
|
337
|
+
this.adapter.log.warn(`Cannot set available for ${device.ieee_address}: ${e}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
202
340
|
|
|
341
|
+
// Parallel ausführen: non-action states
|
|
342
|
+
try {
|
|
343
|
+
await Promise.all(stateWrites);
|
|
344
|
+
} catch (e) {
|
|
345
|
+
this.adapter.log.warn(`processDeviceMessage: parallel writes encountered error: ${e}`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ACTION states: auch parallel ausführen, berücksichtige setStateWithTimeoutAsync
|
|
349
|
+
const actionPromises = [];
|
|
203
350
|
for (const state of actionStates) {
|
|
204
351
|
const stateName = `${device.ieee_address}.${state.id}`;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
352
|
+
actionPromises.push((async () => {
|
|
353
|
+
try {
|
|
354
|
+
const getterPayload = state.getter(messageObj.payload);
|
|
355
|
+
if (getterPayload !== undefined) {
|
|
356
|
+
if (state.isEvent && state.isEvent === true) {
|
|
357
|
+
if (state.type === 'boolean') {
|
|
358
|
+
// setStateWithTimeoutAsync kümmert sich intern um Timer/Optimierung
|
|
359
|
+
await this.setStateWithTimeoutAsync(stateName, getterPayload, 250);
|
|
360
|
+
} else {
|
|
361
|
+
await this.setStateSafelyAsync(stateName, getterPayload);
|
|
362
|
+
}
|
|
212
363
|
} else {
|
|
213
|
-
await this.
|
|
364
|
+
await this.setStateChangedSafelyAsync(stateName, getterPayload);
|
|
214
365
|
}
|
|
215
|
-
} else {
|
|
216
|
-
await this.setStateChangedSafelyAsync(stateName, getterPayload);
|
|
217
366
|
}
|
|
367
|
+
} catch (err) {
|
|
368
|
+
this.adapter.log.warn(`Cannot set action state ${stateName}: ${err}`);
|
|
218
369
|
}
|
|
219
|
-
}
|
|
220
|
-
|
|
370
|
+
})());
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (actionPromises.length > 0) {
|
|
374
|
+
try {
|
|
375
|
+
await Promise.all(actionPromises);
|
|
376
|
+
} catch (e) {
|
|
377
|
+
this.adapter.log.warn(`processDeviceMessage: action state parallel writes error: ${e}`);
|
|
221
378
|
}
|
|
222
379
|
}
|
|
223
380
|
}
|
|
@@ -264,22 +421,33 @@ class StatesController {
|
|
|
264
421
|
return;
|
|
265
422
|
}
|
|
266
423
|
|
|
267
|
-
|
|
424
|
+
// Prüfen, ob bereits ein Timer läuft (State ist dann schon auf true)
|
|
425
|
+
const wasAlreadyTrue = !!this.timeOutCache[stateName];
|
|
268
426
|
|
|
269
|
-
//
|
|
270
|
-
// Wenn value=false (z.B. Stop-Aktion im simpleMoveStopState-Modus),
|
|
271
|
-
// soll der State dauerhaft false bleiben und NICHT nach timeout auf true springen.
|
|
427
|
+
// Timer immer clearen, wenn vorhanden
|
|
272
428
|
if (this.timeOutCache[stateName]) {
|
|
273
429
|
this.adapter.clearTimeout(this.timeOutCache[stateName]);
|
|
274
430
|
delete this.timeOutCache[stateName];
|
|
275
431
|
}
|
|
432
|
+
|
|
276
433
|
if (value === true) {
|
|
434
|
+
// Nur setState aufrufen, wenn der State NICHT bereits auf true war.
|
|
435
|
+
// Bei Dauerfeuer (value=true→true schneller als timeout) spart das
|
|
436
|
+
// den unnötigen setState-Call und verhindert, dass der Timer
|
|
437
|
+
// nie ausgelöst wird (Timer-Leak).
|
|
438
|
+
if (!wasAlreadyTrue) {
|
|
439
|
+
await this.setStateSafelyAsync(stateName, true);
|
|
440
|
+
}
|
|
441
|
+
// Neuen Timer für Auto-Reset setzen
|
|
277
442
|
this.timeOutCache[stateName] = this.adapter.setTimeout(() => {
|
|
278
443
|
delete this.timeOutCache[stateName];
|
|
279
444
|
this.adapter.setState(stateName, false, true).catch((err) => {
|
|
280
445
|
this.adapter.log.debug(`setStateWithTimeout reset error for ${stateName}: ${err}`);
|
|
281
446
|
});
|
|
282
447
|
}, timeout);
|
|
448
|
+
} else {
|
|
449
|
+
// value === false: State setzen, Timer bereits gelöscht
|
|
450
|
+
await this.setStateSafelyAsync(stateName, false);
|
|
283
451
|
}
|
|
284
452
|
}
|
|
285
453
|
|
|
@@ -147,6 +147,11 @@ class WebsocketController {
|
|
|
147
147
|
for (const key of Object.keys(this.adapter.createCache)) {
|
|
148
148
|
delete this.adapter.createCache[key];
|
|
149
149
|
}
|
|
150
|
+
// Topic->Device Map-Cache des StatesController invalidieren, da deviceCache/groupCache
|
|
151
|
+
// neu aufgebaut werden (verhindert veraltete Referenzen auf alte Geräteobjekte)
|
|
152
|
+
if (this.adapter.statesController && typeof this.adapter.statesController.clearDeviceMap === 'function') {
|
|
153
|
+
this.adapter.statesController.clearDeviceMap();
|
|
154
|
+
}
|
|
150
155
|
|
|
151
156
|
// Nur reconnecten wenn kein intentionales Shutdown
|
|
152
157
|
if (!this._intentionalClose) {
|
package/lib/z2mController.js
CHANGED
|
@@ -105,7 +105,15 @@ class Z2mController {
|
|
|
105
105
|
for (const option of deviceState.options) {
|
|
106
106
|
// Fix: "in"-Prüfung statt === undefined, damit null als gültiger gecachter Wert behandelt wird
|
|
107
107
|
if (!(option in device.optionsValues)) {
|
|
108
|
-
|
|
108
|
+
let optState;
|
|
109
|
+
try {
|
|
110
|
+
optState = await this.adapter.getStateAsync(`${splitedID[0]}.${splitedID[1]}.${splitedID[2]}.${option}`);
|
|
111
|
+
} catch (e) {
|
|
112
|
+
this.adapter.log.warn(
|
|
113
|
+
`Cannot read state for option "${option}": ${e.message}`
|
|
114
|
+
);
|
|
115
|
+
optState = null;
|
|
116
|
+
}
|
|
109
117
|
device.optionsValues[option] = optState ? optState.val : null;
|
|
110
118
|
}
|
|
111
119
|
|
package/main.js
CHANGED
|
@@ -40,6 +40,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
40
40
|
this.websocketController = null;
|
|
41
41
|
this.mqttServerController = null;
|
|
42
42
|
this.messageParseMutex = Promise.resolve();
|
|
43
|
+
this.mqttReconnectAttempts = 0;
|
|
43
44
|
|
|
44
45
|
this.on('ready', () => {
|
|
45
46
|
this.onReady().catch((e) => this.log.error(`onReady error: ${e}`));
|
|
@@ -124,7 +125,12 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
124
125
|
} else {
|
|
125
126
|
// Internal MQTT-Server
|
|
126
127
|
this.mqttServerController = new MqttServerController(this);
|
|
127
|
-
await this.mqttServerController.createMQTTServer();
|
|
128
|
+
const serverStarted = await this.mqttServerController.createMQTTServer();
|
|
129
|
+
if (!serverStarted) {
|
|
130
|
+
this.log.error('Internal MQTT server could not be started. The adapter cannot connect to Zigbee2MQTT.');
|
|
131
|
+
this.setState('info.connection', false, true);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
128
134
|
// Kurze Pause damit der OS-Socket tatsächlich bereit ist (createMQTTServer wartet bereits auf listen)
|
|
129
135
|
await this.delay(200);
|
|
130
136
|
this.mqttClient = mqtt.connect(`mqtt://${this.config.mqttServerIPBind}:${this.config.mqttServerPort}`, {
|
|
@@ -137,6 +143,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
137
143
|
|
|
138
144
|
// MQTT Client Events
|
|
139
145
|
this.mqttClient.on('connect', () => {
|
|
146
|
+
this.mqttReconnectAttempts = 0;
|
|
140
147
|
this.log.info(
|
|
141
148
|
`Connect to Zigbee2MQTT over ${this.config.connectionType === 'exmqtt' ? 'external mqtt' : 'internal mqtt'} connection.`
|
|
142
149
|
);
|
|
@@ -155,7 +162,20 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
155
162
|
});
|
|
156
163
|
|
|
157
164
|
this.mqttClient.on('reconnect', () => {
|
|
158
|
-
this.
|
|
165
|
+
this.mqttReconnectAttempts++;
|
|
166
|
+
if (this.mqttReconnectAttempts > 20) {
|
|
167
|
+
this.log.error(
|
|
168
|
+
`MQTT client gave up after ${this.mqttReconnectAttempts} reconnect attempts (10 seconds). ` +
|
|
169
|
+
`Please check whether Zigbee2MQTT is running and the MQTT connection settings are correct.`
|
|
170
|
+
);
|
|
171
|
+
if (this.mqttClient) {
|
|
172
|
+
this.mqttClient.end(true);
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
this.log.info(
|
|
177
|
+
`MQTT client reconnecting to Zigbee2MQTT... (attempt ${this.mqttReconnectAttempts}/20)`
|
|
178
|
+
);
|
|
159
179
|
});
|
|
160
180
|
|
|
161
181
|
this.mqttClient.on('offline', async () => {
|
|
@@ -214,7 +234,12 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
214
234
|
|
|
215
235
|
if (this.config.dummyMqtt === true) {
|
|
216
236
|
this.mqttServerController = new MqttServerController(this);
|
|
217
|
-
await this.mqttServerController.createDummyMQTTServer();
|
|
237
|
+
const serverStarted = await this.mqttServerController.createDummyMQTTServer();
|
|
238
|
+
if (!serverStarted) {
|
|
239
|
+
this.log.error('Dummy MQTT server could not be started – WebSocket mode cannot continue.');
|
|
240
|
+
this.setState('info.connection', false, true);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
218
243
|
await this.delay(200);
|
|
219
244
|
}
|
|
220
245
|
|
|
@@ -527,7 +552,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
527
552
|
this.mqttClient.removeAllListeners();
|
|
528
553
|
this.mqttClient.end(true);
|
|
529
554
|
} catch (e) {
|
|
530
|
-
this.log.error(e);
|
|
555
|
+
this.log.error(`[onUnload] Fehler beim Schließen des MQTT-Clients: ${e.message}`);
|
|
531
556
|
}
|
|
532
557
|
}
|
|
533
558
|
}
|
|
@@ -537,15 +562,16 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
537
562
|
this.mqttServerController.closeServer();
|
|
538
563
|
}
|
|
539
564
|
} catch (e) {
|
|
540
|
-
this.log.error(e);
|
|
565
|
+
this.log.error(`[onUnload] Fehler beim Schließen des MQTT-Servers: ${e.message}`);
|
|
541
566
|
}
|
|
542
|
-
}
|
|
567
|
+
}
|
|
568
|
+
if (this.config.connectionType === 'ws') {
|
|
543
569
|
try {
|
|
544
570
|
if (this.websocketController) {
|
|
545
571
|
this.websocketController.closeConnection();
|
|
546
572
|
}
|
|
547
573
|
} catch (e) {
|
|
548
|
-
this.log.error(e);
|
|
574
|
+
this.log.error(`[onUnload] Fehler beim Schließen der WebSocket-Verbindung: ${e.message}`);
|
|
549
575
|
}
|
|
550
576
|
}
|
|
551
577
|
try {
|
|
@@ -553,21 +579,21 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
553
579
|
await this.statesController.setAllAvailableToFalse();
|
|
554
580
|
}
|
|
555
581
|
} catch (e) {
|
|
556
|
-
this.log.error(e);
|
|
582
|
+
this.log.error(`[onUnload] Fehler beim Setzen aller States auf false: ${e.message}`);
|
|
557
583
|
}
|
|
558
584
|
try {
|
|
559
585
|
if (this.websocketController) {
|
|
560
586
|
this.websocketController.allTimerClear();
|
|
561
587
|
}
|
|
562
588
|
} catch (e) {
|
|
563
|
-
this.log.error(e);
|
|
589
|
+
this.log.error(`[onUnload] Fehler beim Zurücksetzen der WebSocket-Timer: ${e.message}`);
|
|
564
590
|
}
|
|
565
591
|
try {
|
|
566
592
|
if (this.statesController) {
|
|
567
593
|
this.statesController.allTimerClear();
|
|
568
594
|
}
|
|
569
595
|
} catch (e) {
|
|
570
|
-
this.log.error(e);
|
|
596
|
+
this.log.error(`[onUnload] Fehler beim Zurücksetzen der States-Timer: ${e.message}`);
|
|
571
597
|
}
|
|
572
598
|
|
|
573
599
|
this.setState('info.connection', false, true);
|
|
@@ -595,14 +621,14 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
595
621
|
if (state && state.ack === false) {
|
|
596
622
|
if (id.endsWith('info.debugmessages')) {
|
|
597
623
|
this.logCustomizations.debugDevices = state.val != null ? String(state.val) : '';
|
|
598
|
-
this.setState(id, state.val, true);
|
|
624
|
+
await this.setState(id, state.val, true).catch(e => this.log.error(`[stateChange] Fehler beim Setzen von ${id}: ${e.message}`));
|
|
599
625
|
return;
|
|
600
626
|
}
|
|
601
627
|
if (id.endsWith('info.logfilter')) {
|
|
602
628
|
this.logCustomizations.logfilter = state.val != null
|
|
603
629
|
? String(state.val).split(';').filter((x) => x)
|
|
604
630
|
: [];
|
|
605
|
-
this.setState(id, state.val, true);
|
|
631
|
+
await this.setState(id, state.val, true).catch(e => this.log.error(`[stateChange] Fehler beim Setzen von ${id}: ${e.message}`));
|
|
606
632
|
return;
|
|
607
633
|
}
|
|
608
634
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.zigbee2mqtt",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.5",
|
|
4
4
|
"description": "Zigbee2MQTT adapter for ioBroker",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Dennis Rathjen and Arthur Rupp",
|
|
@@ -24,27 +24,27 @@
|
|
|
24
24
|
"node": ">= 22"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@iobroker/adapter-core": "^3.
|
|
28
|
-
"@iobroker/dm-utils": "^3.
|
|
27
|
+
"@iobroker/adapter-core": "^3.4.1",
|
|
28
|
+
"@iobroker/dm-utils": "^3.1.3",
|
|
29
29
|
"aedes": "^0.51.3",
|
|
30
30
|
"aedes-persistence": "^9.1.2",
|
|
31
|
-
"axios": "^1.
|
|
32
|
-
"mqtt": "^5.15.
|
|
31
|
+
"axios": "^1.18.1",
|
|
32
|
+
"mqtt": "^5.15.2",
|
|
33
33
|
"net": "^1.0.2",
|
|
34
34
|
"node-schedule": "^2.1.1",
|
|
35
35
|
"sharp": "^0.34.5",
|
|
36
|
-
"ws": "^8.
|
|
36
|
+
"ws": "^8.21.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@alcalzone/release-script": "^5.2.
|
|
39
|
+
"@alcalzone/release-script": "^5.2.1",
|
|
40
40
|
"@alcalzone/release-script-plugin-iobroker": "^5.2.0",
|
|
41
41
|
"@alcalzone/release-script-plugin-license": "^5.2.0",
|
|
42
42
|
"@alcalzone/release-script-plugin-manual-review": "^5.2.0",
|
|
43
43
|
"@iobroker/adapter-dev": "^1.5.0",
|
|
44
44
|
"@iobroker/eslint-config": "^2.3.4",
|
|
45
|
-
"@iobroker/testing": "^5.
|
|
46
|
-
"@tsconfig/
|
|
47
|
-
"@types/node": "^25.
|
|
45
|
+
"@iobroker/testing": "^5.2.2",
|
|
46
|
+
"@tsconfig/node22": "^22.0.5",
|
|
47
|
+
"@types/node": "^25.9.5",
|
|
48
48
|
"@types/node-schedule": "^2.1.8",
|
|
49
49
|
"typescript": "~6.0.3"
|
|
50
50
|
},
|