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