iobroker.zigbee2mqtt 3.1.1 → 3.1.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 +14 -0
- package/io-package.json +53 -27
- package/lib/check.js +6 -4
- package/lib/colors.js +9 -9
- package/lib/deviceController.js +92 -39
- package/lib/exposes.js +19 -7
- package/lib/imageController.js +56 -26
- package/lib/messages.js +15 -13
- package/lib/mqttServerController.js +14 -8
- package/lib/nonGenericDevicesExtension.js +2 -3
- package/lib/rgb.js +52 -63
- package/lib/states.js +138 -28
- package/lib/statesController.js +102 -44
- package/lib/utils.js +36 -47
- package/lib/websocketController.js +84 -52
- package/lib/z2mController.js +28 -16
- package/main.js +103 -33
- package/package.json +1 -1
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.
|
|
90
|
+
await this.adapter.setStateAsync(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.
|
|
125
|
+
await this.adapter.setStateAsync(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.
|
|
133
|
+
await this.adapter.setStateAsync(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,10 +69,9 @@ 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
|
-
this.
|
|
74
|
+
await this.setStateAsync('info.connection', false, true);
|
|
61
75
|
|
|
62
76
|
const debugDevicesState = await this.getStateAsync('info.debugmessages');
|
|
63
77
|
if (debugDevicesState && debugDevicesState.val) {
|
|
@@ -110,7 +124,8 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
110
124
|
// Internal MQTT-Server
|
|
111
125
|
this.mqttServerController = new MqttServerController(this);
|
|
112
126
|
await this.mqttServerController.createMQTTServer();
|
|
113
|
-
|
|
127
|
+
// Kurze Pause damit der OS-Socket tatsächlich bereit ist (createMQTTServer wartet bereits auf listen)
|
|
128
|
+
await this.delay(200);
|
|
114
129
|
this.mqttClient = mqtt.connect(`mqtt://${this.config.mqttServerIPBind}:${this.config.mqttServerPort}`, {
|
|
115
130
|
clientId: `ioBroker.zigbee2mqtt_${Math.random().toString(16).slice(2, 8)}`,
|
|
116
131
|
clean: true,
|
|
@@ -123,7 +138,13 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
123
138
|
this.log.info(
|
|
124
139
|
`Connect to Zigbee2MQTT over ${this.config.connectionType === 'exmqtt' ? 'external mqtt' : 'internal mqtt'} connection.`
|
|
125
140
|
);
|
|
126
|
-
this.
|
|
141
|
+
this.setStateChangedAsync('info.connection', true, true).catch((e) =>
|
|
142
|
+
this.log.error(`MQTT connect setStateChangedAsync error: ${e}`)
|
|
143
|
+
);
|
|
144
|
+
if (!this.config.baseTopic) {
|
|
145
|
+
this.log.error('baseTopic is not configured – cannot subscribe to MQTT topics!');
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
127
148
|
this.mqttClient.subscribe(`${this.config.baseTopic}/#`, (err) => {
|
|
128
149
|
if (err) {
|
|
129
150
|
this.log.error(`MQTT subscribe error: ${err && err.message ? err.message : String(err)}`);
|
|
@@ -135,16 +156,18 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
135
156
|
this.log.info('MQTT client reconnecting to Zigbee2MQTT...');
|
|
136
157
|
});
|
|
137
158
|
|
|
138
|
-
this.mqttClient.on('offline',
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
159
|
+
this.mqttClient.on('offline', () => {
|
|
160
|
+
(async () => {
|
|
161
|
+
this.log.warn('MQTT client offline – connection to Zigbee2MQTT lost.');
|
|
162
|
+
await this.setStateChangedAsync('info.connection', false, true);
|
|
163
|
+
try {
|
|
164
|
+
if (this.statesController) {
|
|
165
|
+
await this.statesController.setAllAvailableToFalse();
|
|
166
|
+
}
|
|
167
|
+
} catch (e) {
|
|
168
|
+
this.log.error(`MQTT offline setAllAvailableToFalse error: ${e}`);
|
|
144
169
|
}
|
|
145
|
-
}
|
|
146
|
-
this.log.error(`MQTT offline setAllAvailableToFalse error: ${e}`);
|
|
147
|
-
}
|
|
170
|
+
})().catch((e) => this.log.error(`MQTT offline handler error: ${e}`));
|
|
148
171
|
});
|
|
149
172
|
|
|
150
173
|
this.mqttClient.on('error', (err) => {
|
|
@@ -152,9 +175,9 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
152
175
|
});
|
|
153
176
|
|
|
154
177
|
this.mqttClient.on('message', (topic, payload) => {
|
|
155
|
-
// baseTopic
|
|
156
|
-
const
|
|
157
|
-
if (
|
|
178
|
+
// baseTopic-Prefix vollständig entfernen – funktioniert auch bei mehrstufigem baseTopic (z.B. home/zigbee2mqtt)
|
|
179
|
+
const basePrefix = this.config.baseTopic ? `${this.config.baseTopic}/` : null;
|
|
180
|
+
if (!basePrefix || !topic.startsWith(basePrefix)) {
|
|
158
181
|
this.log.debug(`MQTT message with unexpected topic format: ${topic}`);
|
|
159
182
|
return;
|
|
160
183
|
}
|
|
@@ -167,7 +190,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
167
190
|
}
|
|
168
191
|
const messageObj = {
|
|
169
192
|
payload: parsedPayload,
|
|
170
|
-
topic: topic.slice(
|
|
193
|
+
topic: topic.slice(basePrefix.length),
|
|
171
194
|
};
|
|
172
195
|
this.messageParse(messageObj).catch((err) => {
|
|
173
196
|
this.log.error(`messageParse error: ${err}`);
|
|
@@ -183,13 +206,17 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
183
206
|
if (this.config.dummyMqtt === true) {
|
|
184
207
|
this.mqttServerController = new MqttServerController(this);
|
|
185
208
|
await this.mqttServerController.createDummyMQTTServer();
|
|
186
|
-
await this.delay(
|
|
209
|
+
await this.delay(200);
|
|
187
210
|
}
|
|
188
211
|
|
|
189
212
|
this.startWebsocket();
|
|
190
213
|
}
|
|
191
214
|
}
|
|
192
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Erstellt (einmalig) den WebsocketController und initiiert die WS-Verbindung.
|
|
218
|
+
* Wird beim Start und nach einem Verbindungsabbruch aufgerufen.
|
|
219
|
+
*/
|
|
193
220
|
startWebsocket() {
|
|
194
221
|
// Controller nur einmal erstellen – bei Reconnect wird derselbe wiederverwendet
|
|
195
222
|
if (!this.websocketController) {
|
|
@@ -198,6 +225,13 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
198
225
|
this.websocketController.initWsClient();
|
|
199
226
|
}
|
|
200
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Parst eine eingehende MQTT- oder WebSocket-Nachricht von Zigbee2MQTT
|
|
230
|
+
* und leitet sie an den zuständigen Controller weiter.
|
|
231
|
+
* Alle Aufrufe werden serialisiert (Mutex), um Race-Conditions zu vermeiden.
|
|
232
|
+
*
|
|
233
|
+
* @param {{ topic: string, payload: any }} messageObj Die zu verarbeitende Nachricht
|
|
234
|
+
*/
|
|
201
235
|
async messageParse(messageObj) {
|
|
202
236
|
// Mutex: serialisiert alle messageParse-Aufrufe
|
|
203
237
|
// Wichtig: lock muss IMMER wieder freigegeben werden, auch bei early return / Fehler.
|
|
@@ -227,7 +261,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
227
261
|
const coordState = typeof messageObj.payload === 'object' && messageObj.payload !== null
|
|
228
262
|
? messageObj.payload.state
|
|
229
263
|
: messageObj.payload;
|
|
230
|
-
this.
|
|
264
|
+
await this.setStateChangedAsync('info.coordinator_status', coordState, true);
|
|
231
265
|
break;
|
|
232
266
|
}
|
|
233
267
|
case 'bridge/info':
|
|
@@ -247,7 +281,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
247
281
|
await this.statesController.setAllAvailableToFalse();
|
|
248
282
|
this.showInfo = true;
|
|
249
283
|
}
|
|
250
|
-
this.
|
|
284
|
+
await this.setStateChangedAsync('info.connection', bridgeState === 'online', true);
|
|
251
285
|
break;
|
|
252
286
|
}
|
|
253
287
|
case 'bridge/devices':
|
|
@@ -318,12 +352,16 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
318
352
|
if (messageObj.payload === null || messageObj.payload === undefined) {
|
|
319
353
|
break;
|
|
320
354
|
}
|
|
355
|
+
const deviceTopic = messageObj.topic.replace('/availability', '');
|
|
356
|
+
if (!deviceTopic) {
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
321
359
|
const availState = typeof messageObj.payload === 'object'
|
|
322
360
|
? messageObj.payload.state
|
|
323
361
|
: messageObj.payload;
|
|
324
362
|
const newMessage = {
|
|
325
363
|
payload: { available: availState === 'online' },
|
|
326
|
-
topic:
|
|
364
|
+
topic: deviceTopic,
|
|
327
365
|
};
|
|
328
366
|
await this.statesController.processDeviceMessage(newMessage);
|
|
329
367
|
} else {
|
|
@@ -344,6 +382,11 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
344
382
|
}
|
|
345
383
|
}
|
|
346
384
|
|
|
385
|
+
/**
|
|
386
|
+
* Verarbeitet interne ioBroker-Nachrichten (z.B. Admin-UI-Kommandos).
|
|
387
|
+
*
|
|
388
|
+
* @param {ioBroker.Message} obj Das eingehende Nachrichtenobjekt
|
|
389
|
+
*/
|
|
347
390
|
async onMessage(obj) {
|
|
348
391
|
if (!obj || !obj.command) {
|
|
349
392
|
return;
|
|
@@ -384,6 +427,14 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
384
427
|
|
|
385
428
|
// Alle vorhandenen Adapter-Objekte laden
|
|
386
429
|
const allObjects = await this.getAdapterObjectsAsync();
|
|
430
|
+
if (!allObjects) {
|
|
431
|
+
const warn = 'deleteOldStates: getAdapterObjectsAsync returned null – aborting.';
|
|
432
|
+
this.log.error(warn);
|
|
433
|
+
if (obj.callback) {
|
|
434
|
+
this.sendTo(obj.from, obj.command, { error: warn }, obj.callback);
|
|
435
|
+
}
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
387
438
|
for (const id of Object.keys(allObjects)) {
|
|
388
439
|
const iobObj = allObjects[id];
|
|
389
440
|
|
|
@@ -453,6 +504,12 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
453
504
|
}
|
|
454
505
|
}
|
|
455
506
|
|
|
507
|
+
/**
|
|
508
|
+
* Wird beim Stoppen des Adapters aufgerufen.
|
|
509
|
+
* Trennt alle Verbindungen, stoppt Timer und ruft abschließend callback() auf.
|
|
510
|
+
*
|
|
511
|
+
* @param {() => void} callback Muss am Ende zwingend aufgerufen werden
|
|
512
|
+
*/
|
|
456
513
|
async onUnload(callback) {
|
|
457
514
|
try {
|
|
458
515
|
if (['exmqtt', 'intmqtt'].includes(this.config.connectionType)) {
|
|
@@ -491,20 +548,20 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
491
548
|
}
|
|
492
549
|
try {
|
|
493
550
|
if (this.websocketController) {
|
|
494
|
-
|
|
551
|
+
this.websocketController.allTimerClear();
|
|
495
552
|
}
|
|
496
553
|
} catch (e) {
|
|
497
554
|
this.log.error(e);
|
|
498
555
|
}
|
|
499
556
|
try {
|
|
500
557
|
if (this.statesController) {
|
|
501
|
-
|
|
558
|
+
this.statesController.allTimerClear();
|
|
502
559
|
}
|
|
503
560
|
} catch (e) {
|
|
504
561
|
this.log.error(e);
|
|
505
562
|
}
|
|
506
563
|
|
|
507
|
-
this.
|
|
564
|
+
await this.setStateAsync('info.connection', false, true);
|
|
508
565
|
|
|
509
566
|
// Schedule-Job beenden
|
|
510
567
|
const job = schedule.scheduledJobs['coordinatorCheck'];
|
|
@@ -518,16 +575,25 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
518
575
|
}
|
|
519
576
|
}
|
|
520
577
|
|
|
578
|
+
/**
|
|
579
|
+
* Reagiert auf Änderungen von ioBroker-States.
|
|
580
|
+
* Wandelt den State-Change in eine Zigbee2MQTT-Nachricht um und sendet sie.
|
|
581
|
+
*
|
|
582
|
+
* @param {string} id Vollständige State-ID (z.B. zigbee2mqtt.0.0xAABB.state)
|
|
583
|
+
* @param {ioBroker.State | null | undefined} state Das neue State-Objekt
|
|
584
|
+
*/
|
|
521
585
|
async onStateChange(id, state) {
|
|
522
586
|
if (state && state.ack === false) {
|
|
523
587
|
if (id.endsWith('info.debugmessages')) {
|
|
524
|
-
this.logCustomizations.debugDevices = String(state.val
|
|
525
|
-
this.
|
|
588
|
+
this.logCustomizations.debugDevices = state.val != null ? String(state.val) : '';
|
|
589
|
+
await this.setStateAsync(id, state.val, true);
|
|
526
590
|
return;
|
|
527
591
|
}
|
|
528
592
|
if (id.endsWith('info.logfilter')) {
|
|
529
|
-
this.logCustomizations.logfilter =
|
|
530
|
-
|
|
593
|
+
this.logCustomizations.logfilter = state.val != null
|
|
594
|
+
? String(state.val).split(';').filter((x) => x)
|
|
595
|
+
: [];
|
|
596
|
+
await this.setStateAsync(id, state.val, true);
|
|
531
597
|
return;
|
|
532
598
|
}
|
|
533
599
|
|
|
@@ -546,6 +612,10 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
546
612
|
this.log.warn(`Cannot publish state, MQTT client not connected. (${id})`);
|
|
547
613
|
return;
|
|
548
614
|
}
|
|
615
|
+
if (!this.config.baseTopic) {
|
|
616
|
+
this.log.error('baseTopic is not configured – cannot publish state!');
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
549
619
|
try {
|
|
550
620
|
this.mqttClient.publish(
|
|
551
621
|
`${this.config.baseTopic}/${message.topic}`,
|
|
@@ -572,7 +642,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
572
642
|
|
|
573
643
|
if (require.main !== module) {
|
|
574
644
|
/**
|
|
575
|
-
* @param {
|
|
645
|
+
* @param {object} [options] Optionale Adapter-Konfiguration (AdapterOptions)
|
|
576
646
|
*/
|
|
577
647
|
module.exports = (options) => new Zigbee2mqtt(options);
|
|
578
648
|
} else {
|