iobroker.zigbee2mqtt 3.1.1 → 3.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 +10 -67
- package/io-package.json +87 -61
- package/lib/check.js +6 -4
- package/lib/colors.js +9 -9
- package/lib/deviceController.js +90 -37
- package/lib/exposes.js +1362 -1350
- package/lib/imageController.js +56 -26
- package/lib/messages.js +15 -13
- package/lib/mqttServerController.js +36 -30
- package/lib/nonGenericDevicesExtension.js +2 -3
- package/lib/rgb.js +52 -63
- package/lib/states.js +142 -28
- package/lib/statesController.js +103 -45
- package/lib/utils.js +36 -47
- package/lib/websocketController.js +84 -52
- package/lib/z2mController.js +28 -16
- package/main.js +102 -23
- package/package.json +2 -2
package/lib/z2mController.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Übersetzt ioBroker-State-Änderungen in Zigbee2MQTT-MQTT-Nachrichten
|
|
3
|
+
* und leitet Z2M-Log-Nachrichten in den ioBroker-Logger weiter.
|
|
3
4
|
*/
|
|
4
5
|
class Z2mController {
|
|
5
6
|
/**
|
|
7
|
+
* Erstellt eine neue Z2mController-Instanz.
|
|
6
8
|
*
|
|
7
|
-
* @param adapter
|
|
8
|
-
* @param deviceCache
|
|
9
|
-
* @param groupCache
|
|
10
|
-
* @param logCustomizations
|
|
9
|
+
* @param {object} adapter Die ioBroker-Adapter-Instanz
|
|
10
|
+
* @param {Array} deviceCache Gemeinsamer Cache aller bekannten Geräte
|
|
11
|
+
* @param {Array} groupCache Gemeinsamer Cache aller bekannten Gruppen
|
|
12
|
+
* @param {object} logCustomizations Debug/Filter-Einstellungen (debugDevices, logfilter)
|
|
11
13
|
*/
|
|
12
14
|
constructor(adapter, deviceCache, groupCache, logCustomizations) {
|
|
13
15
|
this.adapter = adapter;
|
|
@@ -17,9 +19,12 @@ class Z2mController {
|
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
/**
|
|
22
|
+
* Erzeugt aus einer ioBroker-State-Änderung eine Zigbee2MQTT-Nachricht.
|
|
23
|
+
* Gibt null zurück wenn kein Gerät/State gefunden wurde oder der Setter undefined liefert.
|
|
20
24
|
*
|
|
21
|
-
* @param id
|
|
22
|
-
* @param state
|
|
25
|
+
* @param {string} id Vollständige State-ID (z.B. "zigbee2mqtt.0.0xAABB.state")
|
|
26
|
+
* @param {ioBroker.State} state Das neue State-Objekt mit val und ack
|
|
27
|
+
* @returns {Promise<{topic: string, payload: object}|undefined>} Die Z2M-Nachricht oder undefined
|
|
23
28
|
*/
|
|
24
29
|
async createZ2MMessage(id, state) {
|
|
25
30
|
const splitedID = id.split('.');
|
|
@@ -82,7 +87,7 @@ class Z2mController {
|
|
|
82
87
|
if (stateID === 'send_payload') {
|
|
83
88
|
try {
|
|
84
89
|
controlObj.payload = JSON.parse(stateVal);
|
|
85
|
-
this.adapter.setState(id, state, true);
|
|
90
|
+
this.adapter.setState(id, state.val, true);
|
|
86
91
|
} catch (error) {
|
|
87
92
|
// Fix 9: rawStr als String direkt als payload – wird in main.js via
|
|
88
93
|
// JSON.stringify() nochmals gewrappt → würde doppelt quoten.
|
|
@@ -98,9 +103,8 @@ class Z2mController {
|
|
|
98
103
|
// if available read option and set payload
|
|
99
104
|
if (deviceState.options) {
|
|
100
105
|
for (const option of deviceState.options) {
|
|
101
|
-
// Fix
|
|
102
|
-
|
|
103
|
-
if (device.optionsValues[option] === undefined) {
|
|
106
|
+
// Fix: "in"-Prüfung statt === undefined, damit null als gültiger gecachter Wert behandelt wird
|
|
107
|
+
if (!(option in device.optionsValues)) {
|
|
104
108
|
const optState = await this.adapter.getStateAsync(`${splitedID[0]}.${splitedID[1]}.${splitedID[2]}.${option}`);
|
|
105
109
|
device.optionsValues[option] = optState ? optState.val : null;
|
|
106
110
|
}
|
|
@@ -118,7 +122,7 @@ class Z2mController {
|
|
|
118
122
|
if (deviceState.isOption) {
|
|
119
123
|
// set optionsValues 'Cache'
|
|
120
124
|
device.optionsValues[stateName] = state.val;
|
|
121
|
-
this.adapter.setState(id, state, true);
|
|
125
|
+
this.adapter.setState(id, state.val, true);
|
|
122
126
|
return;
|
|
123
127
|
}
|
|
124
128
|
|
|
@@ -126,18 +130,23 @@ class Z2mController {
|
|
|
126
130
|
const immediateAckRole = ['button'].includes(deviceState.role);
|
|
127
131
|
const immediateAckId = ['brightness_move', 'colortemp_move', 'brightness_step', 'effect'].includes(deviceState.id);
|
|
128
132
|
if (immediateAckRole || immediateAckId) {
|
|
129
|
-
this.adapter.setState(id, state, true);
|
|
133
|
+
this.adapter.setState(id, state.val, true);
|
|
130
134
|
}
|
|
131
135
|
|
|
132
|
-
if (this.logCustomizations.debugDevices
|
|
133
|
-
this.
|
|
136
|
+
if (this.logCustomizations.debugDevices) {
|
|
137
|
+
const debugList = String(this.logCustomizations.debugDevices).split(',').map((s) => s.trim());
|
|
138
|
+
if (debugList.includes(device.ieee_address)) {
|
|
139
|
+
this.adapter.log.warn(`<<<--- toZ2M -> ${device.ieee_address} states: ${JSON.stringify(controlObj)}`);
|
|
140
|
+
}
|
|
134
141
|
}
|
|
135
142
|
return controlObj;
|
|
136
143
|
}
|
|
137
144
|
|
|
138
145
|
/**
|
|
146
|
+
* Leitet eine Z2M-Log-Nachricht (bridge/logging) an den ioBroker-Logger weiter.
|
|
147
|
+
* Nachrichten die dem konfigurierten Logfilter entsprechen werden unterdrückt.
|
|
139
148
|
*
|
|
140
|
-
* @param messageObj
|
|
149
|
+
* @param {{ payload: { message: string, level: string } }} messageObj Die Log-Nachricht
|
|
141
150
|
*/
|
|
142
151
|
async proxyZ2MLogs(messageObj) {
|
|
143
152
|
const logMessage = messageObj.payload && messageObj.payload.message;
|
|
@@ -158,6 +167,9 @@ class Z2mController {
|
|
|
158
167
|
case 'warning':
|
|
159
168
|
this.adapter.log.warn(logMessage);
|
|
160
169
|
break;
|
|
170
|
+
default:
|
|
171
|
+
this.adapter.log.debug(`Z2M [${logLevel}]: ${logMessage}`);
|
|
172
|
+
break;
|
|
161
173
|
}
|
|
162
174
|
}
|
|
163
175
|
}
|
package/main.js
CHANGED
|
@@ -16,6 +16,11 @@ const WebsocketController = require('./lib/websocketController').WebsocketContro
|
|
|
16
16
|
const MqttServerController = require('./lib/mqttServerController').MqttServerController;
|
|
17
17
|
|
|
18
18
|
class Zigbee2mqtt extends core.Adapter {
|
|
19
|
+
/**
|
|
20
|
+
* Erstellt eine neue Instanz des Zigbee2MQTT-Adapters.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} [options] Optionale Adapter-Konfiguration (AdapterOptions)
|
|
23
|
+
*/
|
|
19
24
|
constructor(options) {
|
|
20
25
|
super({
|
|
21
26
|
...options,
|
|
@@ -36,12 +41,22 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
36
41
|
this.mqttServerController = null;
|
|
37
42
|
this.messageParseMutex = Promise.resolve();
|
|
38
43
|
|
|
39
|
-
this.on('ready',
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
this.on('ready', () => {
|
|
45
|
+
this.onReady().catch((e) => this.log.error(`onReady error: ${e}`));
|
|
46
|
+
});
|
|
47
|
+
this.on('stateChange', (id, state) => {
|
|
48
|
+
this.onStateChange(id, state).catch((e) => this.log.error(`onStateChange error: ${e}`));
|
|
49
|
+
});
|
|
50
|
+
this.on('message', (obj) => {
|
|
51
|
+
this.onMessage(obj).catch((e) => this.log.error(`onMessage error: ${e}`));
|
|
52
|
+
});
|
|
42
53
|
this.on('unload', this.onUnload.bind(this));
|
|
43
54
|
}
|
|
44
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Wird aufgerufen sobald der Adapter bereit ist (alle Objekte initialisiert).
|
|
58
|
+
* Initialisiert alle Controller und baut die Verbindung zu Zigbee2MQTT auf.
|
|
59
|
+
*/
|
|
45
60
|
async onReady() {
|
|
46
61
|
this.statesController = new StatesController(this, this.deviceCache, this.groupCache, this.logCustomizations, this.createCache);
|
|
47
62
|
this.deviceController = new DeviceController(
|
|
@@ -54,8 +69,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
54
69
|
);
|
|
55
70
|
this.z2mController = new Z2mController(this, this.deviceCache, this.groupCache, this.logCustomizations);
|
|
56
71
|
|
|
57
|
-
|
|
58
|
-
await adapterInfo(this.config, this.log);
|
|
72
|
+
adapterInfo(this.config, this.log);
|
|
59
73
|
|
|
60
74
|
this.setState('info.connection', false, true);
|
|
61
75
|
|
|
@@ -95,6 +109,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
95
109
|
clientId: `ioBroker.zigbee2mqtt_${Math.random().toString(16).slice(2, 8)}`,
|
|
96
110
|
clean: true,
|
|
97
111
|
reconnectPeriod: 500,
|
|
112
|
+
connectTimeout: 10000,
|
|
98
113
|
};
|
|
99
114
|
|
|
100
115
|
if (this.config.externalMqttServerCredentials === true) {
|
|
@@ -110,11 +125,13 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
110
125
|
// Internal MQTT-Server
|
|
111
126
|
this.mqttServerController = new MqttServerController(this);
|
|
112
127
|
await this.mqttServerController.createMQTTServer();
|
|
113
|
-
|
|
128
|
+
// Kurze Pause damit der OS-Socket tatsächlich bereit ist (createMQTTServer wartet bereits auf listen)
|
|
129
|
+
await this.delay(200);
|
|
114
130
|
this.mqttClient = mqtt.connect(`mqtt://${this.config.mqttServerIPBind}:${this.config.mqttServerPort}`, {
|
|
115
131
|
clientId: `ioBroker.zigbee2mqtt_${Math.random().toString(16).slice(2, 8)}`,
|
|
116
132
|
clean: true,
|
|
117
133
|
reconnectPeriod: 500,
|
|
134
|
+
connectTimeout: 10000,
|
|
118
135
|
});
|
|
119
136
|
}
|
|
120
137
|
|
|
@@ -123,7 +140,13 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
123
140
|
this.log.info(
|
|
124
141
|
`Connect to Zigbee2MQTT over ${this.config.connectionType === 'exmqtt' ? 'external mqtt' : 'internal mqtt'} connection.`
|
|
125
142
|
);
|
|
126
|
-
this.
|
|
143
|
+
this.setStateChangedAsync('info.connection', true, true).catch((e) =>
|
|
144
|
+
this.log.error(`MQTT connect setStateChangedAsync error: ${e}`)
|
|
145
|
+
);
|
|
146
|
+
if (!this.config.baseTopic) {
|
|
147
|
+
this.log.error('baseTopic is not configured – cannot subscribe to MQTT topics!');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
127
150
|
this.mqttClient.subscribe(`${this.config.baseTopic}/#`, (err) => {
|
|
128
151
|
if (err) {
|
|
129
152
|
this.log.error(`MQTT subscribe error: ${err && err.message ? err.message : String(err)}`);
|
|
@@ -137,7 +160,9 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
137
160
|
|
|
138
161
|
this.mqttClient.on('offline', async () => {
|
|
139
162
|
this.log.warn('MQTT client offline – connection to Zigbee2MQTT lost.');
|
|
140
|
-
this.
|
|
163
|
+
await this.setStateChangedAsync('info.connection', false, true).catch((e) =>
|
|
164
|
+
this.log.error(`MQTT offline setStateChangedAsync error: ${e}`)
|
|
165
|
+
);
|
|
141
166
|
try {
|
|
142
167
|
if (this.statesController) {
|
|
143
168
|
await this.statesController.setAllAvailableToFalse();
|
|
@@ -147,14 +172,21 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
147
172
|
}
|
|
148
173
|
});
|
|
149
174
|
|
|
175
|
+
this.mqttClient.on('close', async () => {
|
|
176
|
+
this.log.debug('MQTT client connection closed.');
|
|
177
|
+
await this.setStateChangedAsync('info.connection', false, true).catch((e) =>
|
|
178
|
+
this.log.error(`MQTT close setStateChangedAsync error: ${e}`)
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
150
182
|
this.mqttClient.on('error', (err) => {
|
|
151
183
|
this.log.error(`MQTT client error: ${err && err.message ? err.message : String(err)}`);
|
|
152
184
|
});
|
|
153
185
|
|
|
154
186
|
this.mqttClient.on('message', (topic, payload) => {
|
|
155
|
-
// baseTopic
|
|
156
|
-
const
|
|
157
|
-
if (
|
|
187
|
+
// baseTopic-Prefix vollständig entfernen – funktioniert auch bei mehrstufigem baseTopic (z.B. home/zigbee2mqtt)
|
|
188
|
+
const basePrefix = this.config.baseTopic ? `${this.config.baseTopic}/` : null;
|
|
189
|
+
if (!basePrefix || !topic.startsWith(basePrefix)) {
|
|
158
190
|
this.log.debug(`MQTT message with unexpected topic format: ${topic}`);
|
|
159
191
|
return;
|
|
160
192
|
}
|
|
@@ -167,7 +199,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
167
199
|
}
|
|
168
200
|
const messageObj = {
|
|
169
201
|
payload: parsedPayload,
|
|
170
|
-
topic: topic.slice(
|
|
202
|
+
topic: topic.slice(basePrefix.length),
|
|
171
203
|
};
|
|
172
204
|
this.messageParse(messageObj).catch((err) => {
|
|
173
205
|
this.log.error(`messageParse error: ${err}`);
|
|
@@ -183,13 +215,17 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
183
215
|
if (this.config.dummyMqtt === true) {
|
|
184
216
|
this.mqttServerController = new MqttServerController(this);
|
|
185
217
|
await this.mqttServerController.createDummyMQTTServer();
|
|
186
|
-
await this.delay(
|
|
218
|
+
await this.delay(200);
|
|
187
219
|
}
|
|
188
220
|
|
|
189
221
|
this.startWebsocket();
|
|
190
222
|
}
|
|
191
223
|
}
|
|
192
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Erstellt (einmalig) den WebsocketController und initiiert die WS-Verbindung.
|
|
227
|
+
* Wird beim Start und nach einem Verbindungsabbruch aufgerufen.
|
|
228
|
+
*/
|
|
193
229
|
startWebsocket() {
|
|
194
230
|
// Controller nur einmal erstellen – bei Reconnect wird derselbe wiederverwendet
|
|
195
231
|
if (!this.websocketController) {
|
|
@@ -198,6 +234,13 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
198
234
|
this.websocketController.initWsClient();
|
|
199
235
|
}
|
|
200
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Parst eine eingehende MQTT- oder WebSocket-Nachricht von Zigbee2MQTT
|
|
239
|
+
* und leitet sie an den zuständigen Controller weiter.
|
|
240
|
+
* Alle Aufrufe werden serialisiert (Mutex), um Race-Conditions zu vermeiden.
|
|
241
|
+
*
|
|
242
|
+
* @param {{ topic: string, payload: any }} messageObj Die zu verarbeitende Nachricht
|
|
243
|
+
*/
|
|
201
244
|
async messageParse(messageObj) {
|
|
202
245
|
// Mutex: serialisiert alle messageParse-Aufrufe
|
|
203
246
|
// Wichtig: lock muss IMMER wieder freigegeben werden, auch bei early return / Fehler.
|
|
@@ -227,7 +270,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
227
270
|
const coordState = typeof messageObj.payload === 'object' && messageObj.payload !== null
|
|
228
271
|
? messageObj.payload.state
|
|
229
272
|
: messageObj.payload;
|
|
230
|
-
this.
|
|
273
|
+
await this.setStateChangedAsync('info.coordinator_status', coordState, true);
|
|
231
274
|
break;
|
|
232
275
|
}
|
|
233
276
|
case 'bridge/info':
|
|
@@ -247,7 +290,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
247
290
|
await this.statesController.setAllAvailableToFalse();
|
|
248
291
|
this.showInfo = true;
|
|
249
292
|
}
|
|
250
|
-
this.
|
|
293
|
+
await this.setStateChangedAsync('info.connection', bridgeState === 'online', true);
|
|
251
294
|
break;
|
|
252
295
|
}
|
|
253
296
|
case 'bridge/devices':
|
|
@@ -318,12 +361,16 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
318
361
|
if (messageObj.payload === null || messageObj.payload === undefined) {
|
|
319
362
|
break;
|
|
320
363
|
}
|
|
364
|
+
const deviceTopic = messageObj.topic.replace('/availability', '');
|
|
365
|
+
if (!deviceTopic) {
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
321
368
|
const availState = typeof messageObj.payload === 'object'
|
|
322
369
|
? messageObj.payload.state
|
|
323
370
|
: messageObj.payload;
|
|
324
371
|
const newMessage = {
|
|
325
372
|
payload: { available: availState === 'online' },
|
|
326
|
-
topic:
|
|
373
|
+
topic: deviceTopic,
|
|
327
374
|
};
|
|
328
375
|
await this.statesController.processDeviceMessage(newMessage);
|
|
329
376
|
} else {
|
|
@@ -344,6 +391,11 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
344
391
|
}
|
|
345
392
|
}
|
|
346
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Verarbeitet interne ioBroker-Nachrichten (z.B. Admin-UI-Kommandos).
|
|
396
|
+
*
|
|
397
|
+
* @param {ioBroker.Message} obj Das eingehende Nachrichtenobjekt
|
|
398
|
+
*/
|
|
347
399
|
async onMessage(obj) {
|
|
348
400
|
if (!obj || !obj.command) {
|
|
349
401
|
return;
|
|
@@ -384,6 +436,14 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
384
436
|
|
|
385
437
|
// Alle vorhandenen Adapter-Objekte laden
|
|
386
438
|
const allObjects = await this.getAdapterObjectsAsync();
|
|
439
|
+
if (!allObjects) {
|
|
440
|
+
const warn = 'deleteOldStates: getAdapterObjectsAsync returned null – aborting.';
|
|
441
|
+
this.log.error(warn);
|
|
442
|
+
if (obj.callback) {
|
|
443
|
+
this.sendTo(obj.from, obj.command, { error: warn }, obj.callback);
|
|
444
|
+
}
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
387
447
|
for (const id of Object.keys(allObjects)) {
|
|
388
448
|
const iobObj = allObjects[id];
|
|
389
449
|
|
|
@@ -453,10 +513,16 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
453
513
|
}
|
|
454
514
|
}
|
|
455
515
|
|
|
516
|
+
/**
|
|
517
|
+
* Wird beim Stoppen des Adapters aufgerufen.
|
|
518
|
+
* Trennt alle Verbindungen, stoppt Timer und ruft abschließend callback() auf.
|
|
519
|
+
*
|
|
520
|
+
* @param {() => void} callback Muss am Ende zwingend aufgerufen werden
|
|
521
|
+
*/
|
|
456
522
|
async onUnload(callback) {
|
|
457
523
|
try {
|
|
458
524
|
if (['exmqtt', 'intmqtt'].includes(this.config.connectionType)) {
|
|
459
|
-
if (this.mqttClient &&
|
|
525
|
+
if (this.mqttClient && this.mqttClient.connected) {
|
|
460
526
|
try {
|
|
461
527
|
this.mqttClient.removeAllListeners();
|
|
462
528
|
this.mqttClient.end(true);
|
|
@@ -491,14 +557,14 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
491
557
|
}
|
|
492
558
|
try {
|
|
493
559
|
if (this.websocketController) {
|
|
494
|
-
|
|
560
|
+
this.websocketController.allTimerClear();
|
|
495
561
|
}
|
|
496
562
|
} catch (e) {
|
|
497
563
|
this.log.error(e);
|
|
498
564
|
}
|
|
499
565
|
try {
|
|
500
566
|
if (this.statesController) {
|
|
501
|
-
|
|
567
|
+
this.statesController.allTimerClear();
|
|
502
568
|
}
|
|
503
569
|
} catch (e) {
|
|
504
570
|
this.log.error(e);
|
|
@@ -518,15 +584,24 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
518
584
|
}
|
|
519
585
|
}
|
|
520
586
|
|
|
587
|
+
/**
|
|
588
|
+
* Reagiert auf Änderungen von ioBroker-States.
|
|
589
|
+
* Wandelt den State-Change in eine Zigbee2MQTT-Nachricht um und sendet sie.
|
|
590
|
+
*
|
|
591
|
+
* @param {string} id Vollständige State-ID (z.B. zigbee2mqtt.0.0xAABB.state)
|
|
592
|
+
* @param {ioBroker.State | null | undefined} state Das neue State-Objekt
|
|
593
|
+
*/
|
|
521
594
|
async onStateChange(id, state) {
|
|
522
595
|
if (state && state.ack === false) {
|
|
523
596
|
if (id.endsWith('info.debugmessages')) {
|
|
524
|
-
this.logCustomizations.debugDevices = String(state.val
|
|
597
|
+
this.logCustomizations.debugDevices = state.val != null ? String(state.val) : '';
|
|
525
598
|
this.setState(id, state.val, true);
|
|
526
599
|
return;
|
|
527
600
|
}
|
|
528
601
|
if (id.endsWith('info.logfilter')) {
|
|
529
|
-
this.logCustomizations.logfilter =
|
|
602
|
+
this.logCustomizations.logfilter = state.val != null
|
|
603
|
+
? String(state.val).split(';').filter((x) => x)
|
|
604
|
+
: [];
|
|
530
605
|
this.setState(id, state.val, true);
|
|
531
606
|
return;
|
|
532
607
|
}
|
|
@@ -542,10 +617,14 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
542
617
|
}
|
|
543
618
|
|
|
544
619
|
if (['exmqtt', 'intmqtt'].includes(this.config.connectionType)) {
|
|
545
|
-
if (!this.mqttClient || this.mqttClient.
|
|
620
|
+
if (!this.mqttClient || !this.mqttClient.connected) {
|
|
546
621
|
this.log.warn(`Cannot publish state, MQTT client not connected. (${id})`);
|
|
547
622
|
return;
|
|
548
623
|
}
|
|
624
|
+
if (!this.config.baseTopic) {
|
|
625
|
+
this.log.error('baseTopic is not configured – cannot publish state!');
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
549
628
|
try {
|
|
550
629
|
this.mqttClient.publish(
|
|
551
630
|
`${this.config.baseTopic}/${message.topic}`,
|
|
@@ -572,7 +651,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
572
651
|
|
|
573
652
|
if (require.main !== module) {
|
|
574
653
|
/**
|
|
575
|
-
* @param {
|
|
654
|
+
* @param {object} [options] Optionale Adapter-Konfiguration (AdapterOptions)
|
|
576
655
|
*/
|
|
577
656
|
module.exports = (options) => new Zigbee2mqtt(options);
|
|
578
657
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.zigbee2mqtt",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Zigbee2MQTT adapter for ioBroker",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Dennis Rathjen and Arthur Rupp",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@iobroker/adapter-core": "^3.3.2",
|
|
28
28
|
"@iobroker/dm-utils": "^3.0.17",
|
|
29
|
-
"aedes": "^
|
|
29
|
+
"aedes": "^0.51.3",
|
|
30
30
|
"aedes-persistence-nedb": "^2.0.3",
|
|
31
31
|
"mqtt": "^5.15.1",
|
|
32
32
|
"axios": "^1.14.0",
|