matterbridge-zigbee2mqtt 2.2.3-dev.5 → 2.3.0-dev.2
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/CHANGELOG.md +5 -2
- package/dist/entity.js +16 -264
- package/dist/index.js +0 -35
- package/dist/payloadTypes.js +0 -23
- package/dist/platform.js +6 -53
- package/dist/zigbee2mqtt.js +22 -272
- package/dist/zigbee2mqttTypes.js +0 -23
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/tsconfig.production.json +19 -0
- package/dist/entity.d.ts +0 -244
- package/dist/entity.d.ts.map +0 -1
- package/dist/entity.js.map +0 -1
- package/dist/index.d.ts +0 -40
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/payloadTypes.d.ts +0 -25
- package/dist/payloadTypes.d.ts.map +0 -1
- package/dist/payloadTypes.js.map +0 -1
- package/dist/platform.d.ts +0 -88
- package/dist/platform.d.ts.map +0 -1
- package/dist/platform.js.map +0 -1
- package/dist/zigbee2mqtt.d.ts +0 -182
- package/dist/zigbee2mqtt.d.ts.map +0 -1
- package/dist/zigbee2mqtt.js.map +0 -1
- package/dist/zigbee2mqttTypes.d.ts +0 -350
- package/dist/zigbee2mqttTypes.d.ts.map +0 -1
- package/dist/zigbee2mqttTypes.js.map +0 -1
package/dist/zigbee2mqtt.js
CHANGED
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Zigbee2MQTT and all the interfaces to communicate with zigbee2MQTT.
|
|
3
|
-
*
|
|
4
|
-
* @file zigbee2mqtt.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @date 2023-06-30
|
|
7
|
-
* @version 2.3.3
|
|
8
|
-
*
|
|
9
|
-
* Copyright 2023, 2024, 2025 Luca Liguori.
|
|
10
|
-
*
|
|
11
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
-
* you may not use this file except in compliance with the License.
|
|
13
|
-
* You may obtain a copy of the License at
|
|
14
|
-
*
|
|
15
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
*
|
|
17
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
-
* See the License for the specific language governing permissions and
|
|
21
|
-
* limitations under the License. *
|
|
22
|
-
*/
|
|
23
1
|
import fs from 'fs';
|
|
24
2
|
import path from 'path';
|
|
25
3
|
import * as util from 'util';
|
|
@@ -30,9 +8,7 @@ import { AnsiLogger, rs, db, dn, gn, er, zb, hk, id, idn, ign, REVERSE, REVERSEO
|
|
|
30
8
|
import { mkdir } from 'fs/promises';
|
|
31
9
|
const writeFile = util.promisify(fs.writeFile);
|
|
32
10
|
export class Zigbee2MQTT extends EventEmitter {
|
|
33
|
-
// Logger
|
|
34
11
|
log;
|
|
35
|
-
// Instance properties
|
|
36
12
|
mqttHost;
|
|
37
13
|
mqttPort;
|
|
38
14
|
mqttTopic;
|
|
@@ -55,19 +31,17 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
55
31
|
z2mDevices;
|
|
56
32
|
z2mGroups;
|
|
57
33
|
loggedEntries = 0;
|
|
58
|
-
// Define our MQTT options
|
|
59
34
|
options = {
|
|
60
35
|
clientId: 'classZigbee2MQTT_' + crypto.randomBytes(8).toString('hex'),
|
|
61
36
|
keepalive: 60,
|
|
62
37
|
protocolId: 'MQTT',
|
|
63
38
|
protocolVersion: 5,
|
|
64
|
-
reconnectPeriod: 5000,
|
|
65
|
-
connectTimeout: 60 * 1000,
|
|
39
|
+
reconnectPeriod: 5000,
|
|
40
|
+
connectTimeout: 60 * 1000,
|
|
66
41
|
username: undefined,
|
|
67
42
|
password: undefined,
|
|
68
43
|
clean: true,
|
|
69
44
|
};
|
|
70
|
-
// Constructor
|
|
71
45
|
constructor(mqttHost, mqttPort, mqttTopic, mqttUsername = undefined, mqttPassword = undefined, protocolVersion = 5, debug = false) {
|
|
72
46
|
super();
|
|
73
47
|
this.mqttHost = mqttHost;
|
|
@@ -85,11 +59,11 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
85
59
|
this.z2mVersion = '';
|
|
86
60
|
this.z2mDevices = [];
|
|
87
61
|
this.z2mGroups = [];
|
|
88
|
-
this.log = new AnsiLogger({ logName: 'Zigbee2MQTT', logTimestampFormat: 4
|
|
62
|
+
this.log = new AnsiLogger({ logName: 'Zigbee2MQTT', logTimestampFormat: 4, logLevel: debug ? "debug" : "info" });
|
|
89
63
|
this.log.debug(`Created new instance with host: ${mqttHost} port: ${mqttPort} protocol ${protocolVersion} topic: ${mqttTopic} username: ${mqttUsername !== undefined && mqttUsername !== '' ? mqttUsername : 'undefined'} password: ${mqttPassword !== undefined && mqttPassword !== '' ? '*****' : 'undefined'}`);
|
|
90
64
|
}
|
|
91
65
|
setLogDebug(logDebug) {
|
|
92
|
-
this.log.logLevel = logDebug ? "debug"
|
|
66
|
+
this.log.logLevel = logDebug ? "debug" : "info";
|
|
93
67
|
}
|
|
94
68
|
async setDataPath(dataPath) {
|
|
95
69
|
try {
|
|
@@ -107,7 +81,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
107
81
|
}
|
|
108
82
|
}
|
|
109
83
|
}
|
|
110
|
-
// Get the URL for connect
|
|
111
84
|
getUrl() {
|
|
112
85
|
return 'mqtt://' + this.mqttHost + ':' + this.mqttPort.toString();
|
|
113
86
|
}
|
|
@@ -117,13 +90,12 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
117
90
|
.then((client) => {
|
|
118
91
|
this.log.debug('Connection established');
|
|
119
92
|
this.mqttClient = client;
|
|
120
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
121
93
|
this.mqttClient.on('connect', (packet) => {
|
|
122
|
-
this.log.debug(`MQTT client connect to ${this.getUrl()}${rs}`
|
|
94
|
+
this.log.debug(`MQTT client connect to ${this.getUrl()}${rs}`);
|
|
123
95
|
this.mqttIsConnected = true;
|
|
124
96
|
this.mqttIsReconnecting = false;
|
|
125
97
|
this.mqttIsEnding = false;
|
|
126
|
-
this.emit('mqtt_connect');
|
|
98
|
+
this.emit('mqtt_connect');
|
|
127
99
|
});
|
|
128
100
|
this.mqttClient.on('reconnect', () => {
|
|
129
101
|
this.log.debug(`MQTT client reconnect to ${this.getUrl()}${rs}`);
|
|
@@ -154,17 +126,11 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
154
126
|
this.log.debug('MQTT client error', error);
|
|
155
127
|
this.emit('mqtt_error', error);
|
|
156
128
|
});
|
|
157
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
158
129
|
this.mqttClient.on('packetsend', (packet) => {
|
|
159
|
-
// this.log.debug('classZigbee2MQTT=>Event packetsend');
|
|
160
130
|
});
|
|
161
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
162
131
|
this.mqttClient.on('packetreceive', (packet) => {
|
|
163
|
-
// this.log.debug('classZigbee2MQTT=>Event packetreceive');
|
|
164
132
|
});
|
|
165
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
166
133
|
this.mqttClient.on('message', (topic, payload, packet) => {
|
|
167
|
-
// this.log.debug(`classZigbee2MQTT=>Event message topic: ${topic} payload: ${payload.toString()} packet: ${debugStringify(packet)}`);
|
|
168
134
|
this.messageHandler(topic, payload);
|
|
169
135
|
});
|
|
170
136
|
this.log.debug('Started');
|
|
@@ -172,7 +138,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
172
138
|
this.mqttIsReconnecting = false;
|
|
173
139
|
this.mqttIsEnding = false;
|
|
174
140
|
this.emit('mqtt_connect');
|
|
175
|
-
// Send a heartbeat every 60 seconds
|
|
176
141
|
this.mqttKeepaliveInterval = setInterval(async () => {
|
|
177
142
|
this.log.debug('Publishing keepalive MQTT message');
|
|
178
143
|
try {
|
|
@@ -217,7 +182,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
217
182
|
async subscribe(topic) {
|
|
218
183
|
if (this.mqttClient && this.mqttIsConnected) {
|
|
219
184
|
this.log.debug(`Subscribing topic: ${topic}`);
|
|
220
|
-
// Use subscribeAsync for promise-based handling
|
|
221
185
|
this.mqttClient
|
|
222
186
|
.subscribeAsync(topic, { qos: 2 })
|
|
223
187
|
.then(() => {
|
|
@@ -241,7 +205,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
241
205
|
this.mqttPublishQueueTimeout = setInterval(async () => {
|
|
242
206
|
if (this.mqttClient && this.mqttPublishQueue.length > 0) {
|
|
243
207
|
this.log.debug(`**Publish ${REVERSE}[${this.mqttPublishQueue.length}-${this.mqttPublishInflights}]${REVERSEOFF} topic: ${this.mqttPublishQueue[0].topic} message: ${this.mqttPublishQueue[0].message}${rs}`);
|
|
244
|
-
// this.publish(this.mqttPublishQueue[0].topic, this.mqttPublishQueue[0].message);
|
|
245
208
|
try {
|
|
246
209
|
this.mqttPublishInflights++;
|
|
247
210
|
await this.mqttClient.publishAsync(this.mqttPublishQueue[0].topic, this.mqttPublishQueue[0].message, { qos: 2 });
|
|
@@ -294,15 +257,13 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
294
257
|
async writeBufferJSON(file, buffer) {
|
|
295
258
|
const filePath = path.join(this.mqttDataPath, file);
|
|
296
259
|
let jsonData;
|
|
297
|
-
// Parse the buffer to JSON
|
|
298
260
|
try {
|
|
299
261
|
jsonData = this.tryJsonParse(buffer.toString());
|
|
300
262
|
}
|
|
301
263
|
catch (error) {
|
|
302
264
|
this.log.error('writeBufferJSON: parsing error:', error);
|
|
303
|
-
return;
|
|
265
|
+
return;
|
|
304
266
|
}
|
|
305
|
-
// Write the JSON data to a file
|
|
306
267
|
writeFile(`${filePath}.json`, JSON.stringify(jsonData, null, 2))
|
|
307
268
|
.then(() => {
|
|
308
269
|
this.log.debug(`Successfully wrote to ${filePath}.json`);
|
|
@@ -313,7 +274,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
313
274
|
}
|
|
314
275
|
async writeFile(file, data) {
|
|
315
276
|
const filePath = path.join(this.mqttDataPath, file);
|
|
316
|
-
// Write the data to a file
|
|
317
277
|
writeFile(`${filePath}`, data)
|
|
318
278
|
.then(() => {
|
|
319
279
|
this.log.debug(`Successfully wrote to ${filePath}`);
|
|
@@ -322,7 +282,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
322
282
|
this.log.error(`Error writing to ${filePath}:`, error);
|
|
323
283
|
});
|
|
324
284
|
}
|
|
325
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
326
285
|
tryJsonParse(text) {
|
|
327
286
|
try {
|
|
328
287
|
return JSON.parse(text);
|
|
@@ -343,7 +302,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
343
302
|
else {
|
|
344
303
|
data = { state: payloadString };
|
|
345
304
|
}
|
|
346
|
-
// this.log.debug('classZigbee2MQTT=>Message bridge/state', data);
|
|
347
305
|
if (data.state === 'online') {
|
|
348
306
|
this.z2mIsOnline = true;
|
|
349
307
|
this.emit('online');
|
|
@@ -356,7 +314,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
356
314
|
}
|
|
357
315
|
else if (topic.startsWith(this.mqttTopic + '/bridge/info')) {
|
|
358
316
|
const data = this.tryJsonParse(payload.toString());
|
|
359
|
-
// this.log.info('classZigbee2MQTT=>Message bridge/info', data);
|
|
360
317
|
this.z2mPermitJoin = data.permit_join ? data.permit_join : false;
|
|
361
318
|
this.z2mPermitJoinTimeout = data.permit_join_timeout ? data.permit_join_timeout : 0;
|
|
362
319
|
this.z2mVersion = data.version ? data.version : '';
|
|
@@ -375,14 +332,14 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
375
332
|
this.log.info(`Message bridge/info advanced.legacy_availability_payload is ${data.config.advanced.legacy_availability_payload}`);
|
|
376
333
|
this.emit('info', this.z2mVersion, this.z2mIsAvailabilityEnabled, this.z2mPermitJoin, this.z2mPermitJoinTimeout);
|
|
377
334
|
this.emit('bridge-info', data);
|
|
378
|
-
if (this.log.logLevel === "debug"
|
|
335
|
+
if (this.log.logLevel === "debug")
|
|
379
336
|
this.writeBufferJSON('bridge-info', payload);
|
|
380
337
|
}
|
|
381
338
|
else if (topic.startsWith(this.mqttTopic + '/bridge/devices')) {
|
|
382
339
|
this.z2mDevices.splice(0, this.z2mDevices.length);
|
|
383
340
|
const devices = this.tryJsonParse(payload.toString());
|
|
384
341
|
const data = this.tryJsonParse(payload.toString());
|
|
385
|
-
if (this.log.logLevel === "debug"
|
|
342
|
+
if (this.log.logLevel === "debug")
|
|
386
343
|
this.writeBufferJSON('bridge-devices', payload);
|
|
387
344
|
this.emit('bridge-devices', data);
|
|
388
345
|
let index = 1;
|
|
@@ -437,13 +394,10 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
437
394
|
};
|
|
438
395
|
for (const expose of device.definition.exposes) {
|
|
439
396
|
if (!expose.property && !expose.name && expose.features && expose.type) {
|
|
440
|
-
// Specific expose https://www.zigbee2mqtt.io/guide/usage/exposes.html
|
|
441
397
|
if (z2m.category === '') {
|
|
442
|
-
// Only the first type: light, switch ...
|
|
443
398
|
z2m.category = expose.type;
|
|
444
399
|
}
|
|
445
400
|
for (const feature of expose.features) {
|
|
446
|
-
// Exposes nested inside features
|
|
447
401
|
feature.category = expose.type;
|
|
448
402
|
z2m.exposes.push(feature);
|
|
449
403
|
if (feature.endpoint) {
|
|
@@ -452,7 +406,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
452
406
|
}
|
|
453
407
|
}
|
|
454
408
|
else {
|
|
455
|
-
// Generic expose https://www.zigbee2mqtt.io/guide/usage/exposes.html
|
|
456
409
|
expose.category = '';
|
|
457
410
|
z2m.exposes.push(expose);
|
|
458
411
|
}
|
|
@@ -468,20 +421,18 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
468
421
|
endpoint: key,
|
|
469
422
|
};
|
|
470
423
|
z2m.endpoints.push(endpointWithKey);
|
|
471
|
-
// this.log.debug('classZigbee2MQTT=>Message bridge/devices endpoints=>', device.friendly_name, key, endpoint);
|
|
472
424
|
}
|
|
473
425
|
this.z2mDevices.push(z2m);
|
|
474
426
|
}
|
|
475
427
|
}
|
|
476
428
|
this.log.debug(`Received ${this.z2mDevices.length} devices`);
|
|
477
429
|
this.emit('devices');
|
|
478
|
-
// this.printDevices();
|
|
479
430
|
}
|
|
480
431
|
else if (topic.startsWith(this.mqttTopic + '/bridge/groups')) {
|
|
481
432
|
this.z2mGroups.splice(0, this.z2mGroups.length);
|
|
482
433
|
const groups = this.tryJsonParse(payload.toString());
|
|
483
434
|
const data = this.tryJsonParse(payload.toString());
|
|
484
|
-
if (this.log.logLevel === "debug"
|
|
435
|
+
if (this.log.logLevel === "debug")
|
|
485
436
|
this.writeBufferJSON('bridge-groups', payload);
|
|
486
437
|
this.emit('bridge-groups', data);
|
|
487
438
|
let index = 1;
|
|
@@ -507,7 +458,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
507
458
|
}
|
|
508
459
|
this.log.debug(`Received ${this.z2mGroups.length} groups`);
|
|
509
460
|
this.emit('groups');
|
|
510
|
-
// this.printGroups();
|
|
511
461
|
}
|
|
512
462
|
else if (topic.startsWith(this.mqttTopic + '/bridge/extensions')) {
|
|
513
463
|
const extensions = this.tryJsonParse(payload.toString());
|
|
@@ -565,49 +515,28 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
565
515
|
}
|
|
566
516
|
const data = this.tryJsonParse(payload.toString());
|
|
567
517
|
this.log.debug(`Message topic: ${topic} payload:${rs}`, data);
|
|
568
|
-
/*
|
|
569
|
-
[05/09/2023, 20:35:26] [z2m] classZigbee2MQTT=>Message bridge/response zigbee2mqtt/bridge/response/group/add {
|
|
570
|
-
data: { friendly_name: 'Guest', id: 1 },
|
|
571
|
-
status: 'ok',
|
|
572
|
-
transaction: '1nqux-2'
|
|
573
|
-
}
|
|
574
|
-
[11/09/2023, 15:13:54] [z2m] classZigbee2MQTT=>Message bridge/response zigbee2mqtt/bridge/response/group/members/add {
|
|
575
|
-
data: { device: '0x84fd27fffe83066f/1', group: 'Master Guest room' },
|
|
576
|
-
status: 'ok',
|
|
577
|
-
transaction: '2ww7l-5'
|
|
578
|
-
}
|
|
579
|
-
*/
|
|
580
518
|
}
|
|
581
519
|
else if (topic.startsWith(this.mqttTopic + '/bridge/logging')) {
|
|
582
|
-
// const data = JSON.parse(payload.toString());
|
|
583
|
-
// this.log.debug('classZigbee2MQTT=>Message bridge/logging', data);
|
|
584
520
|
}
|
|
585
521
|
else if (topic.startsWith(this.mqttTopic + '/bridge/config')) {
|
|
586
522
|
this.log.debug(`Message topic: ${topic}`);
|
|
587
|
-
// const data = JSON.parse(payload.toString());
|
|
588
|
-
// this.log.debug('classZigbee2MQTT=>Message bridge/config', data);
|
|
589
523
|
}
|
|
590
524
|
else if (topic.startsWith(this.mqttTopic + '/bridge/definitions')) {
|
|
591
525
|
this.log.debug(`Message topic: ${topic}`);
|
|
592
|
-
// const data = JSON.parse(payload.toString());
|
|
593
|
-
// this.log.debug('classZigbee2MQTT=>Message bridge/definitions', data);
|
|
594
526
|
}
|
|
595
527
|
else if (topic.startsWith(this.mqttTopic + '/bridge')) {
|
|
596
528
|
this.log.debug(`Message topic: ${topic}`);
|
|
597
|
-
// const data = JSON.parse(payload.toString());
|
|
598
|
-
// this.log.debug('classZigbee2MQTT=>Message bridge/definitions', data);
|
|
599
529
|
}
|
|
600
530
|
else {
|
|
601
531
|
let entity = topic.replace(this.mqttTopic + '/', '');
|
|
602
532
|
let service = '';
|
|
603
533
|
if (entity.search('/')) {
|
|
604
|
-
// set get availability or unknown TODO
|
|
605
534
|
const parts = entity.split('/');
|
|
606
535
|
entity = parts[0];
|
|
607
536
|
service = parts[1];
|
|
608
537
|
}
|
|
609
538
|
if (entity === 'Coordinator') {
|
|
610
|
-
const data = this.tryJsonParse(payload.toString());
|
|
539
|
+
const data = this.tryJsonParse(payload.toString());
|
|
611
540
|
if (service === 'availability') {
|
|
612
541
|
if (data.state === 'online') {
|
|
613
542
|
this.log.debug(`Received ONLINE for ${id}Coordinator${rs}`, data);
|
|
@@ -618,8 +547,7 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
618
547
|
}
|
|
619
548
|
return;
|
|
620
549
|
}
|
|
621
|
-
|
|
622
|
-
if (this.log.logLevel === "debug" /* LogLevel.DEBUG */ && this.loggedEntries < 1000) {
|
|
550
|
+
if (this.log.logLevel === "debug" && this.loggedEntries < 1000) {
|
|
623
551
|
const logEntry = {
|
|
624
552
|
entity,
|
|
625
553
|
service,
|
|
@@ -656,9 +584,7 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
656
584
|
return this.z2mGroups.find((group) => group.friendly_name === name);
|
|
657
585
|
}
|
|
658
586
|
handleDeviceMessage(deviceIndex, entity, service, payload) {
|
|
659
|
-
// this.log.info(`classZigbee2MQTT=>handleDeviceMessage ${id}#${deviceIndex + 1}${rs} entity ${dn}${entity}${rs} service ${zb}${service}${rs} payload ${pl}${payload}${rs}`);
|
|
660
587
|
if (payload.length === 0 || payload === null) {
|
|
661
|
-
// this.log.warn(`handleDeviceMessage ${id}#${deviceIndex + 1}${rs} entity ${dn}${entity}${rs} service ${zb}${service}${rs} payload null`);
|
|
662
588
|
return;
|
|
663
589
|
}
|
|
664
590
|
const payloadString = payload.toString();
|
|
@@ -667,41 +593,33 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
667
593
|
data = this.tryJsonParse(payload.toString());
|
|
668
594
|
}
|
|
669
595
|
else {
|
|
670
|
-
data = { state: payloadString };
|
|
596
|
+
data = { state: payloadString };
|
|
671
597
|
}
|
|
672
598
|
if (service === 'availability') {
|
|
673
599
|
if (data.state === 'online') {
|
|
674
600
|
this.z2mDevices[deviceIndex].isAvailabilityEnabled = true;
|
|
675
601
|
this.z2mDevices[deviceIndex].isOnline = true;
|
|
676
|
-
// this.log.warn('handleDeviceMessage availability payload: ', data);
|
|
677
602
|
this.emit('availability', entity, true);
|
|
678
603
|
this.emit('ONLINE-' + entity);
|
|
679
604
|
}
|
|
680
605
|
else if (data.state === 'offline') {
|
|
681
606
|
this.z2mDevices[deviceIndex].isOnline = false;
|
|
682
|
-
// this.log.warn('handleDeviceMessage availability payload: ', data);
|
|
683
607
|
this.emit('availability', entity, false);
|
|
684
608
|
this.emit('OFFLINE-' + entity);
|
|
685
609
|
}
|
|
686
610
|
}
|
|
687
611
|
else if (service === 'get') {
|
|
688
|
-
// Do nothing
|
|
689
612
|
}
|
|
690
613
|
else if (service === 'set') {
|
|
691
|
-
// Do nothing
|
|
692
614
|
}
|
|
693
615
|
else if (service === undefined) {
|
|
694
|
-
// this.log.debug(`classZigbee2MQTT=>emitting message for device ${dn}${entity}${rs} payload ${pl}${payload}${rs}`);
|
|
695
616
|
this.emit('MESSAGE-' + entity, data);
|
|
696
617
|
}
|
|
697
618
|
else {
|
|
698
|
-
// MQTT output attribute type
|
|
699
619
|
}
|
|
700
620
|
}
|
|
701
621
|
handleGroupMessage(groupIndex, entity, service, payload) {
|
|
702
|
-
// this.log.info(`classZigbee2MQTT=>handleGroupMessage ${id}#${groupIndex + 1}${rs} entity ${gn}${entity}${rs} service ${zb}${service}${rs} payload ${pl}${payload}${rs}`);
|
|
703
622
|
if (payload.length === 0 || payload === null) {
|
|
704
|
-
// this.log.warn(`handleGroupMessage ${id}#${groupIndex + 1}${rs} entity ${gn}${entity}${rs} service ${zb}${service}${rs} payload null`);
|
|
705
623
|
return;
|
|
706
624
|
}
|
|
707
625
|
const payloadString = payload.toString();
|
|
@@ -710,7 +628,7 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
710
628
|
data = this.tryJsonParse(payload.toString());
|
|
711
629
|
}
|
|
712
630
|
else {
|
|
713
|
-
data = { state: payloadString };
|
|
631
|
+
data = { state: payloadString };
|
|
714
632
|
}
|
|
715
633
|
data['last_seen'] = new Date().toISOString();
|
|
716
634
|
if (service === 'availability') {
|
|
@@ -727,29 +645,16 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
727
645
|
}
|
|
728
646
|
}
|
|
729
647
|
else if (service === 'get') {
|
|
730
|
-
// Do nothing
|
|
731
648
|
}
|
|
732
649
|
else if (service === 'set') {
|
|
733
|
-
// Do nothing
|
|
734
650
|
}
|
|
735
651
|
else if (service === undefined) {
|
|
736
|
-
// this.log.debug(`classZigbee2MQTT=>emitting message for group ${gn}${entity}${rs} payload ${pl}${payload}${rs}`);
|
|
737
652
|
this.emit('MESSAGE-' + entity, data);
|
|
738
653
|
}
|
|
739
654
|
else {
|
|
740
|
-
// MQTT output attribute type
|
|
741
655
|
}
|
|
742
656
|
}
|
|
743
657
|
handleResponseNetworkmap(payload) {
|
|
744
|
-
/*
|
|
745
|
-
"routes": [
|
|
746
|
-
{
|
|
747
|
-
"destinationAddress": 31833,
|
|
748
|
-
"nextHop": 31833,
|
|
749
|
-
"status": "ACTIVE"
|
|
750
|
-
}
|
|
751
|
-
],
|
|
752
|
-
*/
|
|
753
658
|
const data = this.tryJsonParse(payload.toString());
|
|
754
659
|
const topology = data.data.value;
|
|
755
660
|
const lqi = (lqi) => {
|
|
@@ -799,7 +704,7 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
799
704
|
};
|
|
800
705
|
const timePassedSince = (lastSeen) => {
|
|
801
706
|
const now = Date.now();
|
|
802
|
-
const difference = now - lastSeen;
|
|
707
|
+
const difference = now - lastSeen;
|
|
803
708
|
const days = Math.floor(difference / (1000 * 60 * 60 * 24));
|
|
804
709
|
if (days > 0) {
|
|
805
710
|
return `${days} days ago`;
|
|
@@ -815,116 +720,51 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
815
720
|
const seconds = Math.floor((difference % (1000 * 60)) / 1000);
|
|
816
721
|
return `${seconds} seconds ago`;
|
|
817
722
|
};
|
|
818
|
-
if (this.log.logLevel === "debug"
|
|
723
|
+
if (this.log.logLevel === "debug")
|
|
819
724
|
this.writeBufferJSON('networkmap_' + data.data.type, payload);
|
|
820
725
|
if (data.data.type === 'graphviz') {
|
|
821
|
-
if (this.log.logLevel === "debug"
|
|
726
|
+
if (this.log.logLevel === "debug")
|
|
822
727
|
this.writeFile('networkmap_' + data.data.type + '.txt', data.data.value);
|
|
823
728
|
}
|
|
824
729
|
if (data.data.type === 'plantuml') {
|
|
825
|
-
if (this.log.logLevel === "debug"
|
|
730
|
+
if (this.log.logLevel === "debug")
|
|
826
731
|
this.writeFile('networkmap_' + data.data.type + '.txt', data.data.value);
|
|
827
732
|
}
|
|
828
733
|
if (data.data.type === 'raw') {
|
|
829
|
-
// Log nodes with links
|
|
830
734
|
this.log.warn('Network map nodes:');
|
|
831
735
|
topology.nodes.sort((a, b) => a.friendlyName.localeCompare(b.friendlyName));
|
|
832
736
|
topology.nodes.forEach((node, index) => {
|
|
833
737
|
this.log.debug(`Node [${index.toString().padStart(3, ' ')}] ${node.type === 'EndDevice' ? ign : node.type === 'Router' ? idn : '\x1b[48;5;1m\x1b[38;5;255m'}${node.friendlyName}${rs}${db} addr: ${node.ieeeAddr}-0x${node.networkAddress.toString(16)} type: ${node.type} lastseen: ${timePassedSince(node.lastSeen)}`);
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
sourceLinks.sort((a, b) => a.lqi - b.lqi); // Sort by lqi
|
|
738
|
+
const sourceLinks = topology.links.filter((link) => link.sourceIeeeAddr === node.ieeeAddr);
|
|
739
|
+
sourceLinks.sort((a, b) => a.lqi - b.lqi);
|
|
837
740
|
sourceLinks.forEach((link, index) => {
|
|
838
|
-
// const targetNode = topology.nodes.find((node) => node.ieeeAddr === link.target.ieeeAddr);
|
|
839
741
|
this.log.debug(` link [${index.toString().padStart(4, ' ')}] lqi: ${lqi(link.lqi)} depth: ${depth(link.depth)} relation: ${relationship(link.relationship)} > > > ${friendlyName(link.target.ieeeAddr)}`);
|
|
840
742
|
});
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
targetLinks.sort((a, b) => a.lqi - b.lqi); // Sort by lqi
|
|
743
|
+
const targetLinks = topology.links.filter((link) => link.targetIeeeAddr === node.ieeeAddr);
|
|
744
|
+
targetLinks.sort((a, b) => a.lqi - b.lqi);
|
|
844
745
|
targetLinks.forEach((link, index) => {
|
|
845
|
-
// const sourceNode = topology.nodes.find((node) => node.ieeeAddr === link.source.ieeeAddr);
|
|
846
746
|
this.log.debug(` link [${index.toString().padStart(4, ' ')}] lqi: ${lqi(link.lqi)} depth: ${depth(link.depth)} relation: ${relationship(link.relationship)} < < < ${friendlyName(link.source.ieeeAddr)}`);
|
|
847
747
|
});
|
|
848
748
|
});
|
|
849
|
-
// Log links
|
|
850
|
-
/*
|
|
851
|
-
this.log.warn('Network map links:');
|
|
852
|
-
map.links.sort((a, b) => a.sourceIeeeAddr.localeCompare(b.sourceIeeeAddr));
|
|
853
|
-
map.links.forEach( (link, index) => {
|
|
854
|
-
const sourceNode = map.nodes.find(node => node.ieeeAddr === link.source.ieeeAddr);
|
|
855
|
-
assert(sourceNode, `${wr}NwkAddr error node ${link.sourceIeeeAddr} not found${db}`);
|
|
856
|
-
const targetNode = map.nodes.find(node => node.ieeeAddr === link.target.ieeeAddr);
|
|
857
|
-
assert(targetNode, `${wr}NwkAddr error node ${link.targetIeeeAddr} not found${db}`);
|
|
858
|
-
this.log.debug(`- link[${index}]: ${link.source.ieeeAddr}-${link.source.networkAddress.toString(16)} (${sourceNode?.friendlyName})
|
|
859
|
-
Lqi: ${link.lqi} Depth: ${link.depth} Relation: ${link.relationship} => ${link.target.ieeeAddr}-${link.target.networkAddress.toString(16)} (${targetNode?.friendlyName})`);
|
|
860
|
-
} );
|
|
861
|
-
*/
|
|
862
749
|
}
|
|
863
750
|
}
|
|
864
751
|
handleResponseDeviceRename(payload) {
|
|
865
|
-
/*
|
|
866
|
-
{
|
|
867
|
-
data: {
|
|
868
|
-
from: '0xcc86ecfffe4e9d25',
|
|
869
|
-
homeassistant_rename: false,
|
|
870
|
-
to: 'Double switch'
|
|
871
|
-
},
|
|
872
|
-
status: 'ok',
|
|
873
|
-
transaction: 'smeo0-8'
|
|
874
|
-
}
|
|
875
|
-
*/
|
|
876
752
|
const json = this.tryJsonParse(payload.toString());
|
|
877
753
|
this.log.debug(`handleResponseDeviceRename from ${json.data.from} to ${json.data.to} status ${json.status}`);
|
|
878
754
|
const device = this.z2mDevices.find((device) => device.friendly_name === json.data.to);
|
|
879
755
|
this.emit('device_rename', device?.ieee_address, json.data.from, json.data.to);
|
|
880
756
|
}
|
|
881
757
|
handleResponseDeviceRemove(payload) {
|
|
882
|
-
/*
|
|
883
|
-
{
|
|
884
|
-
data: { block: false, force: false, id: 'Presence sensor' },
|
|
885
|
-
status: 'ok',
|
|
886
|
-
transaction: 'bet01-20'
|
|
887
|
-
}
|
|
888
|
-
*/
|
|
889
758
|
const json = this.tryJsonParse(payload.toString());
|
|
890
759
|
this.log.debug(`handleResponseDeviceRemove name ${json.data.id} status ${json.status} block ${json.data.block} force ${json.data.force}`);
|
|
891
760
|
this.emit('device_remove', json.data.id, json.status, json.data.block, json.data.force);
|
|
892
761
|
}
|
|
893
762
|
handleResponseDeviceOptions(payload) {
|
|
894
|
-
/*
|
|
895
|
-
{
|
|
896
|
-
data: {
|
|
897
|
-
from: {
|
|
898
|
-
color_sync: false,
|
|
899
|
-
legacy: false,
|
|
900
|
-
state_action: false,
|
|
901
|
-
transition: 0
|
|
902
|
-
},
|
|
903
|
-
id: '0xa4c1388ad0ebb0a6',
|
|
904
|
-
restart_required: false,
|
|
905
|
-
to: {
|
|
906
|
-
color_sync: false,
|
|
907
|
-
legacy: false,
|
|
908
|
-
state_action: false,
|
|
909
|
-
transition: 0
|
|
910
|
-
}
|
|
911
|
-
},
|
|
912
|
-
status: 'ok',
|
|
913
|
-
transaction: '8j6s7-3'
|
|
914
|
-
}
|
|
915
|
-
*/
|
|
916
763
|
const json = this.tryJsonParse(payload.toString());
|
|
917
764
|
this.log.debug(`handleResponseDeviceOptions ieee_address ${json.data.id} status ${json.status} from ${json.data.from} to ${json.data.to}`);
|
|
918
765
|
this.emit('device_options', json.data.id, json.status, json.data.from, json.data.to);
|
|
919
766
|
}
|
|
920
767
|
handleResponseGroupAdd(payload) {
|
|
921
|
-
/*
|
|
922
|
-
{
|
|
923
|
-
data: { friendly_name: 'Test', id: 7 },
|
|
924
|
-
status: 'ok',
|
|
925
|
-
transaction: '8j6s7-9'
|
|
926
|
-
}
|
|
927
|
-
*/
|
|
928
768
|
const json = this.tryJsonParse(payload.toString());
|
|
929
769
|
this.log.debug(`handleResponseGroupAdd() friendly_name ${json.data.friendly_name} id ${json.data.id} status ${json.status}`);
|
|
930
770
|
if (json.status === 'ok') {
|
|
@@ -932,13 +772,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
932
772
|
}
|
|
933
773
|
}
|
|
934
774
|
handleResponseGroupRemove(payload) {
|
|
935
|
-
/*
|
|
936
|
-
{
|
|
937
|
-
data: { force: false, id: 'Test' },
|
|
938
|
-
status: 'ok',
|
|
939
|
-
transaction: '8j6s7-10'
|
|
940
|
-
}
|
|
941
|
-
*/
|
|
942
775
|
const json = this.tryJsonParse(payload.toString());
|
|
943
776
|
this.log.debug(`handleResponseGroupRemove() friendly_name ${json.data.id} status ${json.status}`);
|
|
944
777
|
if (json.status === 'ok') {
|
|
@@ -946,13 +779,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
946
779
|
}
|
|
947
780
|
}
|
|
948
781
|
handleResponseGroupRename(payload) {
|
|
949
|
-
/*
|
|
950
|
-
{
|
|
951
|
-
data: { from: 'Test2', homeassistant_rename: false, to: 'Test' },
|
|
952
|
-
status: 'ok',
|
|
953
|
-
transaction: '0r51l-15'
|
|
954
|
-
}
|
|
955
|
-
*/
|
|
956
782
|
const json = this.tryJsonParse(payload.toString());
|
|
957
783
|
this.log.debug(`handleResponseGroupRename() from ${json.data.from} to ${json.data.to} status ${json.status}`);
|
|
958
784
|
if (json.status === 'ok') {
|
|
@@ -960,13 +786,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
960
786
|
}
|
|
961
787
|
}
|
|
962
788
|
handleResponseGroupAddMember(payload) {
|
|
963
|
-
/*
|
|
964
|
-
{
|
|
965
|
-
data: { device: '0xa4c1388ad0ebb0a6/1', group: 'Test2' },
|
|
966
|
-
status: 'ok',
|
|
967
|
-
transaction: '0r51l-7'
|
|
968
|
-
}
|
|
969
|
-
*/
|
|
970
789
|
const json = this.tryJsonParse(payload.toString());
|
|
971
790
|
this.log.debug(`handleResponseGroupAddMembers() add to group friendly_name ${json.data.group} device ieee_address ${json.data.device} status ${json.status}`);
|
|
972
791
|
if (json.status === 'ok' && json.data.device && json.data.device.includes('/')) {
|
|
@@ -974,13 +793,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
974
793
|
}
|
|
975
794
|
}
|
|
976
795
|
handleResponseGroupRemoveMember(payload) {
|
|
977
|
-
/*
|
|
978
|
-
{
|
|
979
|
-
data: { device: 'Gledopto RGBCTT light', group: 'Test2' },
|
|
980
|
-
status: 'ok',
|
|
981
|
-
transaction: '0r51l-10'
|
|
982
|
-
}
|
|
983
|
-
*/
|
|
984
796
|
const json = this.tryJsonParse(payload.toString());
|
|
985
797
|
this.log.debug(`handleResponseGroupRemoveMember() remove from group friendly_name ${json.data.group} device friendly_name ${json.data.device} status ${json.status}`);
|
|
986
798
|
if (json.status === 'ok') {
|
|
@@ -988,13 +800,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
988
800
|
}
|
|
989
801
|
}
|
|
990
802
|
handleResponsePermitJoin(payload) {
|
|
991
|
-
/*
|
|
992
|
-
{
|
|
993
|
-
data: { device?: 'Coordinator', time: 254, value: true },
|
|
994
|
-
status: 'ok',
|
|
995
|
-
transaction: 'adeis-5'
|
|
996
|
-
}
|
|
997
|
-
*/
|
|
998
803
|
const json = this.tryJsonParse(payload.toString());
|
|
999
804
|
this.log.debug(`handleResponsePermitJoin() device: ${json.data.device ? json.data.device : 'All'} time: ${json.data.time} value: ${json.data.value} status: ${json.status}`);
|
|
1000
805
|
if (json.status === 'ok') {
|
|
@@ -1008,75 +813,23 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
1008
813
|
this.log.error('handleEvent() undefined type', json);
|
|
1009
814
|
break;
|
|
1010
815
|
case 'device_leave':
|
|
1011
|
-
/*
|
|
1012
|
-
{
|
|
1013
|
-
data: { friendly_name: 'Light sensor', ieee_address: '0x54ef44100085c321' },
|
|
1014
|
-
type: 'device_leave'
|
|
1015
|
-
}
|
|
1016
|
-
*/
|
|
1017
816
|
this.log.debug(`handleEvent() type: device_leave name: ${json.data.friendly_name} address: ${json.data.ieee_address}`);
|
|
1018
817
|
this.emit('device_leave', json.data.friendly_name, json.data.ieee_address);
|
|
1019
818
|
break;
|
|
1020
819
|
case 'device_joined':
|
|
1021
|
-
/*
|
|
1022
|
-
{
|
|
1023
|
-
data: {
|
|
1024
|
-
friendly_name: 'Kitchen Dishwasher water leak sensor',
|
|
1025
|
-
ieee_address: '0x00158d0007c2b057'
|
|
1026
|
-
},
|
|
1027
|
-
type: 'device_joined'
|
|
1028
|
-
}
|
|
1029
|
-
*/
|
|
1030
820
|
this.log.debug(`handleEvent() type: device_joined name: ${json.data.friendly_name} address: ${json.data.ieee_address}`);
|
|
1031
821
|
this.emit('device_joined', json.data.friendly_name, json.data.ieee_address);
|
|
1032
822
|
break;
|
|
1033
823
|
case 'device_announce':
|
|
1034
|
-
/*
|
|
1035
|
-
{
|
|
1036
|
-
data: {
|
|
1037
|
-
friendly_name: 'Kitchen Sink water leak sensor',
|
|
1038
|
-
ieee_address: '0x00158d0008f1099b'
|
|
1039
|
-
},
|
|
1040
|
-
type: 'device_announce'
|
|
1041
|
-
}
|
|
1042
|
-
*/
|
|
1043
824
|
this.log.debug(`handleEvent() type: device_announce name: ${json.data.friendly_name} address: ${json.data.ieee_address}`);
|
|
1044
825
|
this.emit('device_announce', json.data.friendly_name, json.data.ieee_address);
|
|
1045
826
|
break;
|
|
1046
827
|
case 'device_interview':
|
|
1047
|
-
/*
|
|
1048
|
-
{
|
|
1049
|
-
data: {
|
|
1050
|
-
friendly_name: 'Kitchen Sink water leak sensor',
|
|
1051
|
-
ieee_address: '0x00158d0008f1099b',
|
|
1052
|
-
status: 'started'
|
|
1053
|
-
},
|
|
1054
|
-
type: 'device_interview'
|
|
1055
|
-
}
|
|
1056
|
-
{
|
|
1057
|
-
data: {
|
|
1058
|
-
definition: {
|
|
1059
|
-
description: 'Aqara water leak sensor',
|
|
1060
|
-
exposes: [Array],
|
|
1061
|
-
model: 'SJCGQ11LM',
|
|
1062
|
-
options: [Array],
|
|
1063
|
-
supports_ota: false,
|
|
1064
|
-
vendor: 'Xiaomi'
|
|
1065
|
-
},
|
|
1066
|
-
friendly_name: 'Kitchen Sink water leak sensor',
|
|
1067
|
-
ieee_address: '0x00158d0008f1099b',
|
|
1068
|
-
status: 'successful',
|
|
1069
|
-
supported: true
|
|
1070
|
-
},
|
|
1071
|
-
type: 'device_interview'
|
|
1072
|
-
}
|
|
1073
|
-
*/
|
|
1074
828
|
this.log.debug(`handleEvent() type: device_interview name: ${json.data.friendly_name} address: ${json.data.ieee_address} status: ${json.data.status} supported: ${json.data.supported}`);
|
|
1075
829
|
this.emit('device_interview', json.data.friendly_name, json.data.ieee_address, json.data.status, json.data.supported);
|
|
1076
830
|
break;
|
|
1077
831
|
}
|
|
1078
832
|
}
|
|
1079
|
-
// Function to read JSON config from a file
|
|
1080
833
|
readConfig(filename) {
|
|
1081
834
|
this.log.debug(`Reading config from ${filename}`);
|
|
1082
835
|
try {
|
|
@@ -1089,8 +842,6 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
1089
842
|
return null;
|
|
1090
843
|
}
|
|
1091
844
|
}
|
|
1092
|
-
// Function to write JSON config to a file
|
|
1093
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1094
845
|
writeConfig(filename, data) {
|
|
1095
846
|
this.log.debug(`Writing config to ${filename}`);
|
|
1096
847
|
try {
|
|
@@ -1218,4 +969,3 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
1218
969
|
});
|
|
1219
970
|
}
|
|
1220
971
|
}
|
|
1221
|
-
//# sourceMappingURL=zigbee2mqtt.js.map
|