matterbridge-zigbee2mqtt 2.6.0 → 2.7.0-dev-20250711-6299fe0

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 CHANGED
@@ -21,6 +21,31 @@ New device types:
21
21
 
22
22
  If your controller has issues detecting the new device type, blacklist these devices, restart, wait 5 minutes that the controller removes them, remove the blacklist and restart again. This will create a new endpoint on the controller and the controllers will likely remove and recreate all the devices so make a backup of configurations (i.e. room assignements) and automations on the controller.
23
23
 
24
+ ## [2.7.0] - 2025-07-10
25
+
26
+ ### Added
27
+
28
+ - [entity]: Added the ability to cache commands on a single light device or group. They will be executed in once. This helps to execute global controller scenes in large setups.
29
+ - [composed]: Added the ability to send commands on subenpoint also for ColorControl cluster.
30
+
31
+ ### Changed
32
+
33
+ - [package]: Updated dependencies.
34
+ - [package]: Updated package to Automator v. 2.0.2.
35
+ - [DevContainer]: Added support for the [**Matterbridge Plugin Dev Container**](https://github.com/Luligu/matterbridge/blob/dev/README-DEV.md#matterbridge-plugin-dev-container) with optimized named volumes for `matterbridge` and `node_modules`.
36
+ - [GitHub]: Added GitHub issue templates for bug reports and feature requests.
37
+ - [ESLint]: Refactored the flat config.
38
+ - [ESLint]: Added the plugins `eslint-plugin-promise`, `eslint-plugin-jsdoc`, and `@vitest/eslint-plugin`.
39
+ - [Jest]: Refactored the flat config.
40
+ - [Vitest]: Added Vitest for TypeScript project testing. It will replace Jest, which does not work correctly with ESM module mocks.
41
+ - [JSDoc]: Added missing JSDoc comments, including `@param` and `@returns` tags.
42
+ - [CodeQL]: Added CodeQL badge in the readme.
43
+ - [Codecov]: Added Codecov badge in the readme.
44
+
45
+ <a href="https://www.buymeacoffee.com/luligugithub">
46
+ <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
47
+ </a>
48
+
24
49
  ## [2.6.0] - 2025-06-07
25
50
 
26
51
  ### Added
package/README.md CHANGED
@@ -5,6 +5,8 @@
5
5
  [![Docker Version](https://img.shields.io/docker/v/luligu/matterbridge?label=docker%20version&sort=semver)](https://hub.docker.com/r/luligu/matterbridge)
6
6
  [![Docker Pulls](https://img.shields.io/docker/pulls/luligu/matterbridge.svg)](https://hub.docker.com/r/luligu/matterbridge)
7
7
  ![Node.js CI](https://github.com/Luligu/matterbridge-zigbee2mqtt/actions/workflows/build-matterbridge-plugin.yml/badge.svg)
8
+ ![CodeQL](https://github.com/Luligu/matterbridge-zigbee2mqtt/actions/workflows/codeql.yml/badge.svg)
9
+ [![codecov](https://codecov.io/gh/Luligu/matterbridge-zigbee2mqtt/branch/main/graph/badge.svg)](https://codecov.io/gh/Luligu/matterbridge-zigbee2mqtt)
8
10
 
9
11
  [![powered by](https://img.shields.io/badge/powered%20by-matterbridge-blue)](https://www.npmjs.com/package/matterbridge)
10
12
  [![powered by](https://img.shields.io/badge/powered%20by-matter--history-blue)](https://www.npmjs.com/package/matter-history)
@@ -13,7 +15,7 @@
13
15
 
14
16
  ---
15
17
 
16
- Matterbridge zigbee2mqtt is a matterbridge production-level plugin that expose all zigbee2mqtt devices and groups to Matter.
18
+ Matterbridge zigbee2mqtt is a matterbridge production-level plugin that expose all zigbee2mqtt devices and groups to Matter. Scenes are supported too.
17
19
 
18
20
  No hub or dedicated hardware needed.
19
21
 
@@ -37,7 +39,7 @@ on Windows:
37
39
  npm install -g matterbridge --omit=dev
38
40
  ```
39
41
 
40
- on Linux (you need the necessary permissions):
42
+ on Linux and macOS (you need the necessary permissions):
41
43
 
42
44
  ```
43
45
  sudo npm install -g matterbridge --omit=dev
@@ -67,7 +69,7 @@ npm install -g matterbridge-zigbee2mqtt --omit=dev
67
69
  matterbridge -add matterbridge-zigbee2mqtt
68
70
  ```
69
71
 
70
- On linux:
72
+ On linux and macOS:
71
73
 
72
74
  ```
73
75
  cd ~/Matterbridge
@@ -92,10 +94,10 @@ cd matterbridge-zigbee2mqtt
92
94
  npm ci
93
95
  npm run dev:link
94
96
  npm run build
95
- matterbridge -add .\
97
+ matterbridge -add .
96
98
  ```
97
99
 
98
- On linux:
100
+ On linux and macOS:
99
101
 
100
102
  ```
101
103
  cd ~/Matterbridge
@@ -104,7 +106,7 @@ cd matterbridge-zigbee2mqtt
104
106
  npm ci
105
107
  npm run dev:link
106
108
  npm run build
107
- matterbridge -add ./
109
+ matterbridge -add .
108
110
  ```
109
111
 
110
112
  Then start Matterbridge
package/dist/entity.js CHANGED
@@ -1,11 +1,11 @@
1
+ import EventEmitter from 'node:events';
1
2
  import { airQualitySensor, colorTemperatureSwitch, dimmableSwitch, onOffSwitch, powerSource, bridgedNode, electricalSensor, onOffLight, dimmableLight, colorTemperatureLight, onOffOutlet, coverDevice, thermostatDevice, MatterbridgeEndpoint, dimmableOutlet, doorLockDevice, occupancySensor, lightSensor, contactSensor, temperatureSensor, humiditySensor, pressureSensor, genericSwitch, waterLeakDetector, rainSensor, smokeCoAlarm, extendedColorLight, } from 'matterbridge';
2
3
  import { AnsiLogger, gn, dn, ign, idn, rs, db, debugStringify, hk, zb, or, nf, CYAN, er, YELLOW } from 'matterbridge/logger';
3
- import { deepCopy, deepEqual, isValidNumber, kelvinToRGB, miredToKelvin } from 'matterbridge/utils';
4
+ import { deepCopy, deepEqual, isValidNumber, isValidObject, kelvinToRGB, miredToKelvin } from 'matterbridge/utils';
4
5
  import * as color from 'matterbridge/utils';
5
6
  import { SwitchesTag, NumberTag } from 'matterbridge/matter';
6
7
  import { getClusterNameById, ClusterId } from 'matterbridge/matter/types';
7
8
  import { ElectricalEnergyMeasurement, ElectricalPowerMeasurement, OnOffCluster, LevelControlCluster, WindowCoveringCluster, DoorLockCluster, BridgedDeviceBasicInformation, OnOff, LevelControl, ColorControl, ColorControlCluster, TemperatureMeasurement, BooleanState, RelativeHumidityMeasurement, PressureMeasurement, OccupancySensing, IlluminanceMeasurement, PowerSource, WindowCovering, DoorLock, ThermostatCluster, Thermostat, AirQuality, TotalVolatileOrganicCompoundsConcentrationMeasurement, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, FormaldehydeConcentrationMeasurement, Pm1ConcentrationMeasurement, Pm25ConcentrationMeasurement, Pm10ConcentrationMeasurement, SmokeCoAlarm, } from 'matterbridge/matter/clusters';
8
- import EventEmitter from 'node:events';
9
9
  export class ZigbeeEntity extends EventEmitter {
10
10
  log;
11
11
  serial = '';
@@ -26,7 +26,7 @@ export class ZigbeeEntity extends EventEmitter {
26
26
  transition = false;
27
27
  propertyMap = new Map();
28
28
  mutableDevice = new Map();
29
- colorTimeout = undefined;
29
+ lightTimeout = undefined;
30
30
  thermostatTimeout = undefined;
31
31
  thermostatSystemModeLookup = ['off', 'auto', '', 'cool', 'heat', '', '', 'fan_only'];
32
32
  composedType = '';
@@ -50,11 +50,17 @@ export class ZigbeeEntity extends EventEmitter {
50
50
  this.en = gn;
51
51
  this.ien = ign;
52
52
  }
53
- this.log = new AnsiLogger({ logName: this.entityName, logTimestampFormat: 4, logLevel: platform.debugEnabled ? "debug" : platform.log.logLevel });
53
+ this.log = new AnsiLogger({
54
+ logName: this.entityName,
55
+ logTimestampFormat: 4,
56
+ logLevel: platform.debugEnabled ? "debug" : platform.log.logLevel,
57
+ });
54
58
  this.log.debug(`Created MatterEntity: ${this.entityName}`);
55
59
  this.platform.z2m.on('MESSAGE-' + this.entityName, (payload) => {
56
60
  const now = Date.now();
57
- if (now - this.lastSeen < 1000 * 60 && deepEqual(this.lastPayload, payload, ['linkquality', 'last_seen', ...this.ignoreFeatures]) && !Object.prototype.hasOwnProperty.call(this.lastPayload, 'action')) {
61
+ if (now - this.lastSeen < 1000 * 60 &&
62
+ deepEqual(this.lastPayload, payload, ['linkquality', 'last_seen', ...this.ignoreFeatures]) &&
63
+ !Object.prototype.hasOwnProperty.call(this.lastPayload, 'action')) {
58
64
  this.log.debug(`Skipping not changed ${platform.z2mDevicesRegistered ? 'MQTT message' : 'State update'} for accessory ${this.entityName}`);
59
65
  return;
60
66
  }
@@ -200,9 +206,9 @@ export class ZigbeeEntity extends EventEmitter {
200
206
  }
201
207
  destroy() {
202
208
  this.removeAllListeners();
203
- if (this.colorTimeout)
204
- clearTimeout(this.colorTimeout);
205
- this.colorTimeout = undefined;
209
+ if (this.lightTimeout)
210
+ clearTimeout(this.lightTimeout);
211
+ this.lightTimeout = undefined;
206
212
  if (this.thermostatTimeout)
207
213
  clearTimeout(this.thermostatTimeout);
208
214
  this.thermostatTimeout = undefined;
@@ -466,7 +472,7 @@ export class ZigbeeGroup extends ZigbeeEntity {
466
472
  platform.setSelectDeviceEntity(`group-${group.id}`, 'scenes', 'Scenes', 'component');
467
473
  platform.registerVirtualDevice(`${platform.config.scenesPrefix ? group.friendly_name + ' ' : ''}${scene.name}`, async () => {
468
474
  zigbeeGroup.log.info(`Triggered scene "${scene.name}" id ${scene.id} from group ${group.friendly_name}`);
469
- zigbeeGroup.publishCommand('scene_recall', group.friendly_name, { 'scene_recall': scene.id });
475
+ zigbeeGroup.publishCommand('scene_recall', group.friendly_name, { scene_recall: scene.id });
470
476
  });
471
477
  });
472
478
  }
@@ -477,6 +483,25 @@ export class ZigbeeGroup extends ZigbeeEntity {
477
483
  return zigbeeGroup;
478
484
  zigbeeGroup.mutableDevice.clear();
479
485
  zigbeeGroup.logPropertyMap();
486
+ let lastRequestedHue = -1;
487
+ let lastRequestedSaturation = -1;
488
+ let nextPayload = {};
489
+ function cachePublishLight(command = 'cachedPublishLight') {
490
+ clearTimeout(zigbeeGroup.lightTimeout);
491
+ zigbeeGroup.lightTimeout = setTimeout(() => {
492
+ clearTimeout(zigbeeGroup.lightTimeout);
493
+ zigbeeGroup.lightTimeout = undefined;
494
+ if (lastRequestedHue >= 0 && lastRequestedSaturation >= 0) {
495
+ const rgb = color.hslColorToRgbColor((lastRequestedHue / 254) * 360, (lastRequestedSaturation / 254) * 100, 50);
496
+ nextPayload['color'] = { r: rgb.r, g: rgb.g, b: rgb.b };
497
+ }
498
+ if (isValidObject(nextPayload, 1))
499
+ zigbeeGroup.publishCommand(command, group.friendly_name, nextPayload);
500
+ nextPayload = {};
501
+ lastRequestedHue = -1;
502
+ lastRequestedSaturation = -1;
503
+ }, 100);
504
+ }
480
505
  if (isSwitch || isLight) {
481
506
  if (isSwitch && !isLight)
482
507
  await zigbeeGroup.bridgedDevice.addFixedLabel('type', 'switch');
@@ -487,71 +512,87 @@ export class ZigbeeGroup extends ZigbeeEntity {
487
512
  });
488
513
  zigbeeGroup.bridgedDevice.addCommandHandler('on', async () => {
489
514
  zigbeeGroup.log.debug(`Command on called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db}`);
490
- zigbeeGroup.publishCommand('on', group.friendly_name, { state: 'ON' });
515
+ nextPayload['state'] = 'ON';
516
+ cachePublishLight();
491
517
  });
492
518
  zigbeeGroup.bridgedDevice.addCommandHandler('off', async () => {
493
519
  zigbeeGroup.log.debug(`Command off called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db}`);
494
- zigbeeGroup.publishCommand('off', group.friendly_name, { state: 'OFF' });
520
+ nextPayload['state'] = 'OFF';
521
+ cachePublishLight();
495
522
  });
496
523
  zigbeeGroup.bridgedDevice.addCommandHandler('toggle', async () => {
497
524
  zigbeeGroup.log.debug(`Command toggle called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db}`);
498
- zigbeeGroup.publishCommand('toggle', group.friendly_name, { state: 'TOGGLE' });
525
+ nextPayload['state'] = 'TOGGLE';
526
+ cachePublishLight();
499
527
  });
500
528
  }
501
529
  if (isLight) {
502
530
  if (useBrightness) {
503
- zigbeeGroup.bridgedDevice.addCommandHandler('moveToLevel', async ({ request: { level } }) => {
504
- zigbeeGroup.log.debug(`Command moveToLevel called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: ${level}`);
505
- zigbeeGroup.publishCommand('moveToLevel', group.friendly_name, { brightness: level });
531
+ zigbeeGroup.bridgedDevice.addCommandHandler('moveToLevel', async (data) => {
532
+ zigbeeGroup.log.debug(`Command moveToLevel called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: ${data.request.level}`);
533
+ nextPayload['state'] = 'ON';
534
+ nextPayload['brightness'] = data.request.level;
535
+ if (zigbeeGroup.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
536
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
537
+ cachePublishLight();
506
538
  });
507
- zigbeeGroup.bridgedDevice.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => {
508
- zigbeeGroup.log.debug(`Command moveToLevelWithOnOff called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: ${level}`);
509
- zigbeeGroup.publishCommand('moveToLevelWithOnOff', group.friendly_name, { brightness: level });
539
+ zigbeeGroup.bridgedDevice.addCommandHandler('moveToLevelWithOnOff', async (data) => {
540
+ zigbeeGroup.log.debug(`Command moveToLevelWithOnOff called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: ${data.request.level}`);
541
+ nextPayload['state'] = 'ON';
542
+ nextPayload['brightness'] = data.request.level;
543
+ if (zigbeeGroup.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
544
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
545
+ cachePublishLight();
510
546
  });
511
547
  }
512
548
  if (useColorTemperature) {
513
- zigbeeGroup.bridgedDevice.addCommandHandler('moveToColorTemperature', async ({ request: request }) => {
514
- zigbeeGroup.log.debug(`Command moveToColorTemperature called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: ${request.colorTemperatureMireds}`);
549
+ zigbeeGroup.bridgedDevice.addCommandHandler('moveToColorTemperature', async (data) => {
550
+ zigbeeGroup.log.debug(`Command moveToColorTemperature called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: ${data.request.colorTemperatureMireds}`);
515
551
  await zigbeeGroup.bridgedDevice?.setAttribute(ColorControl.Cluster.id, 'colorMode', ColorControl.ColorMode.ColorTemperatureMireds);
516
- zigbeeGroup.publishCommand('moveToColorTemperature', group.friendly_name, { color_temp: request.colorTemperatureMireds });
552
+ nextPayload['state'] = 'ON';
553
+ nextPayload['color_temp'] = data.request.colorTemperatureMireds;
554
+ if (zigbeeGroup.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
555
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
556
+ cachePublishLight();
517
557
  });
518
558
  }
519
559
  if (useColor) {
520
- let lastRequestedHue = 0;
521
- let lastRequestedSaturation = 0;
522
- zigbeeGroup.bridgedDevice.addCommandHandler('moveToHue', async ({ request: request }) => {
523
- zigbeeGroup.log.debug(`Command moveToHue called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: ${request.hue}`);
560
+ zigbeeGroup.bridgedDevice.addCommandHandler('moveToHue', async (data) => {
561
+ zigbeeGroup.log.debug(`Command moveToHue called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: ${data.request.hue}`);
524
562
  await zigbeeGroup.bridgedDevice?.setAttribute(ColorControl.Cluster.id, 'colorMode', ColorControl.ColorMode.CurrentHueAndCurrentSaturation);
525
- lastRequestedHue = request.hue;
526
- zigbeeGroup.colorTimeout = setTimeout(() => {
527
- clearTimeout(zigbeeGroup.colorTimeout);
528
- const rgb = color.hslColorToRgbColor((request.hue / 254) * 360, (lastRequestedSaturation / 254) * 100, 50);
529
- zigbeeGroup.publishCommand('moveToHue', group.friendly_name, { color: { r: rgb.r, g: rgb.g, b: rgb.b } });
530
- }, 500);
563
+ nextPayload['state'] = 'ON';
564
+ lastRequestedHue = data.request.hue;
565
+ if (zigbeeGroup.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
566
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
567
+ cachePublishLight();
531
568
  });
532
- zigbeeGroup.bridgedDevice.addCommandHandler('moveToSaturation', async ({ request: request }) => {
533
- zigbeeGroup.log.debug(`Command moveToSaturation called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: ${request.saturation}`);
569
+ zigbeeGroup.bridgedDevice.addCommandHandler('moveToSaturation', async (data) => {
570
+ zigbeeGroup.log.debug(`Command moveToSaturation called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: ${data.request.saturation}`);
534
571
  await zigbeeGroup.bridgedDevice?.setAttribute(ColorControl.Cluster.id, 'colorMode', ColorControl.ColorMode.CurrentHueAndCurrentSaturation);
535
- lastRequestedSaturation = request.saturation;
536
- zigbeeGroup.colorTimeout = setTimeout(() => {
537
- clearTimeout(zigbeeGroup.colorTimeout);
538
- const rgb = color.hslColorToRgbColor((lastRequestedHue / 254) * 360, (request.saturation / 254) * 100, 50);
539
- zigbeeGroup.publishCommand('moveToSaturation', group.friendly_name, { color: { r: rgb.r, g: rgb.g, b: rgb.b } });
540
- }, 500);
572
+ nextPayload['state'] = 'ON';
573
+ lastRequestedSaturation = data.request.saturation;
574
+ if (zigbeeGroup.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
575
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
576
+ cachePublishLight();
541
577
  });
542
- zigbeeGroup.bridgedDevice.addCommandHandler('moveToHueAndSaturation', async ({ request: request }) => {
543
- zigbeeGroup.log.debug(`Command moveToHueAndSaturation called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: ${request.hue}-${request.saturation}`);
578
+ zigbeeGroup.bridgedDevice.addCommandHandler('moveToHueAndSaturation', async (data) => {
579
+ zigbeeGroup.log.debug(`Command moveToHueAndSaturation called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: ${data.request.hue}-${data.request.saturation}`);
544
580
  await zigbeeGroup.bridgedDevice?.setAttribute(ColorControl.Cluster.id, 'colorMode', ColorControl.ColorMode.CurrentHueAndCurrentSaturation);
545
- const rgb = color.hslColorToRgbColor((request.hue / 254) * 360, (request.saturation / 254) * 100, 50);
546
- zigbeeGroup.publishCommand('moveToHueAndSaturation', group.friendly_name, { color: { r: rgb.r, g: rgb.g, b: rgb.b } });
581
+ nextPayload['state'] = 'ON';
582
+ const rgb = color.hslColorToRgbColor((data.request.hue / 254) * 360, (data.request.saturation / 254) * 100, 50);
583
+ nextPayload['color'] = { r: rgb.r, g: rgb.g, b: rgb.b };
584
+ if (zigbeeGroup.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
585
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
586
+ cachePublishLight();
547
587
  });
548
- zigbeeGroup.bridgedDevice.addCommandHandler('moveToColor', async ({ request }) => {
549
- zigbeeGroup.log.debug(`Command moveToColor called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: X: ${request.colorX} Y: ${request.colorY}`);
588
+ zigbeeGroup.bridgedDevice.addCommandHandler('moveToColor', async (data) => {
589
+ zigbeeGroup.log.debug(`Command moveToColor called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: X: ${data.request.colorX} Y: ${data.request.colorY}`);
550
590
  await zigbeeGroup.bridgedDevice?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentXAndCurrentY, zigbeeGroup.log);
551
- const payload = { color: { x: request.colorX / 65536, y: request.colorY / 65536 } };
552
- if (zigbeeGroup.transition && request.transitionTime && request.transitionTime / 10 >= 1)
553
- payload['transition'] = Math.round(request.transitionTime / 10);
554
- zigbeeGroup.publishCommand('moveToColor', group.friendly_name, payload);
591
+ nextPayload['state'] = 'ON';
592
+ nextPayload['color'] = { x: data.request.colorX / 65536, y: data.request.colorY / 65536 };
593
+ if (zigbeeGroup.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
594
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
595
+ cachePublishLight();
555
596
  });
556
597
  }
557
598
  }
@@ -693,7 +734,9 @@ export class ZigbeeDevice extends ZigbeeEntity {
693
734
  if (zigbeeDevice.platform.postfix !== '') {
694
735
  zigbeeDevice.serial = `${zigbeeDevice.serial}-${zigbeeDevice.platform.postfix}`.slice(0, 32);
695
736
  }
696
- if (device.friendly_name === 'Coordinator' || (device.model_id === 'ti.router' && device.manufacturer === 'TexasInstruments') || (device.model_id.startsWith('SLZB-') && device.manufacturer === 'SMLIGHT')) {
737
+ if (device.friendly_name === 'Coordinator' ||
738
+ (device.model_id === 'ti.router' && device.manufacturer === 'TexasInstruments') ||
739
+ (device.model_id.startsWith('SLZB-') && device.manufacturer === 'SMLIGHT')) {
697
740
  zigbeeDevice.isRouter = true;
698
741
  platform.setSelectDevice(device.ieee_address, device.friendly_name, 'wifi');
699
742
  zigbeeDevice.bridgedDevice = new MatterbridgeEndpoint([doorLockDevice, bridgedNode, powerSource], { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
@@ -724,7 +767,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
724
767
  platform.setSelectDeviceEntity(device.ieee_address, 'scenes', 'Scenes', 'component');
725
768
  platform.registerVirtualDevice(`${platform.config.scenesPrefix ? device.friendly_name + ' ' : ''}${scene.name}`, async () => {
726
769
  zigbeeDevice.log.info(`Triggered scene "${scene.name}" id ${scene.id} from device ${device.friendly_name}`);
727
- zigbeeDevice.publishCommand('scene_recall', device.friendly_name, { 'scene_recall': scene.id });
770
+ zigbeeDevice.publishCommand('scene_recall', device.friendly_name, { scene_recall: scene.id });
728
771
  });
729
772
  });
730
773
  });
@@ -902,7 +945,14 @@ export class ZigbeeDevice extends ZigbeeEntity {
902
945
  }
903
946
  const tagList = [];
904
947
  tagList.push({ mfgCode: null, namespaceId: SwitchesTag.Custom.namespaceId, tag: SwitchesTag.Custom.tag, label: 'switch_' + count });
905
- zigbeeDevice.mutableDevice.set('switch_' + count, { tagList, deviceTypes: [genericSwitch], clusterServersIds: [...genericSwitch.requiredServerClusters], clusterServersOptions: [], clusterClientsIds: [], clusterClientsOptions: [] });
948
+ zigbeeDevice.mutableDevice.set('switch_' + count, {
949
+ tagList,
950
+ deviceTypes: [genericSwitch],
951
+ clusterServersIds: [...genericSwitch.requiredServerClusters],
952
+ clusterServersOptions: [],
953
+ clusterClientsIds: [],
954
+ clusterClientsOptions: [],
955
+ });
906
956
  }
907
957
  else {
908
958
  for (let i = 0; i < zigbeeDevice.actions.length; i += 3) {
@@ -914,7 +964,14 @@ export class ZigbeeDevice extends ZigbeeEntity {
914
964
  }
915
965
  const tagList = [];
916
966
  tagList.push({ mfgCode: null, namespaceId: SwitchesTag.Custom.namespaceId, tag: SwitchesTag.Custom.tag, label: 'switch_' + count });
917
- zigbeeDevice.mutableDevice.set('switch_' + count, { tagList, deviceTypes: [genericSwitch], clusterServersIds: [...genericSwitch.requiredServerClusters], clusterServersOptions: [], clusterClientsIds: [], clusterClientsOptions: [] });
967
+ zigbeeDevice.mutableDevice.set('switch_' + count, {
968
+ tagList,
969
+ deviceTypes: [genericSwitch],
970
+ clusterServersIds: [...genericSwitch.requiredServerClusters],
971
+ clusterServersOptions: [],
972
+ clusterClientsIds: [],
973
+ clusterClientsOptions: [],
974
+ });
918
975
  count++;
919
976
  }
920
977
  }
@@ -928,7 +985,14 @@ export class ZigbeeDevice extends ZigbeeEntity {
928
985
  zigbeeDevice.propertyMap.set('battery_voltage', { name: 'battery_voltage', type: '', endpoint: '' });
929
986
  }
930
987
  if (!zigbeeDevice.mutableDevice.has(''))
931
- zigbeeDevice.mutableDevice.set('', { tagList: [], deviceTypes: [bridgedNode, powerSource], clusterServersIds: [], clusterServersOptions: [], clusterClientsIds: [], clusterClientsOptions: [] });
988
+ zigbeeDevice.mutableDevice.set('', {
989
+ tagList: [],
990
+ deviceTypes: [bridgedNode, powerSource],
991
+ clusterServersIds: [],
992
+ clusterServersOptions: [],
993
+ clusterClientsIds: [],
994
+ clusterClientsOptions: [],
995
+ });
932
996
  const mainEndpoint = zigbeeDevice.mutableDevice.get('');
933
997
  if (!mainEndpoint)
934
998
  return zigbeeDevice;
@@ -987,7 +1051,8 @@ export class ZigbeeDevice extends ZigbeeEntity {
987
1051
  zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${zigbeeDevice.device?.friendly_name}${rs}${db} endpoint: ${ign}${endpoint === '' ? 'main' : endpoint}${rs}${db} => ` +
988
1052
  `${nf}tagList: ${debugStringify(device.tagList)} deviceTypes: ${debugStringify(device.deviceTypes)} clusterServersIds: ${debugStringify(device.clusterServersIds)}`);
989
1053
  }
990
- if ((mainEndpoint.deviceTypes.find((dt) => dt.code === waterLeakDetector.code) || mainEndpoint.deviceTypes.find((dt) => dt.code === rainSensor.code)) && mainEndpoint.clusterServersIds.includes(BooleanState.Cluster.id)) {
1054
+ if ((mainEndpoint.deviceTypes.find((dt) => dt.code === waterLeakDetector.code) || mainEndpoint.deviceTypes.find((dt) => dt.code === rainSensor.code)) &&
1055
+ mainEndpoint.clusterServersIds.includes(BooleanState.Cluster.id)) {
991
1056
  zigbeeDevice.log.debug(`Configuring device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} BooleanStateCluster cluster with`);
992
1057
  zigbeeDevice.bridgedDevice.createDefaultBooleanStateClusterServer(false);
993
1058
  mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(BooleanState.Cluster.id), 1);
@@ -1054,146 +1119,226 @@ export class ZigbeeDevice extends ZigbeeEntity {
1054
1119
  return zigbeeDevice;
1055
1120
  zigbeeDevice.mutableDevice.clear();
1056
1121
  zigbeeDevice.logPropertyMap();
1122
+ let lastRequestedHue = -1;
1123
+ let lastRequestedSaturation = -1;
1124
+ let nextPayload = {};
1125
+ function cachePublishLight(command = 'cachedPublishLight') {
1126
+ clearTimeout(zigbeeDevice.lightTimeout);
1127
+ zigbeeDevice.lightTimeout = setTimeout(() => {
1128
+ clearTimeout(zigbeeDevice.lightTimeout);
1129
+ zigbeeDevice.lightTimeout = undefined;
1130
+ if (lastRequestedHue >= 0 && lastRequestedSaturation >= 0) {
1131
+ const rgb = color.hslColorToRgbColor((lastRequestedHue / 254) * 360, (lastRequestedSaturation / 254) * 100, 50);
1132
+ nextPayload['color'] = { r: rgb.r, g: rgb.g, b: rgb.b };
1133
+ }
1134
+ if (isValidObject(nextPayload, 1))
1135
+ zigbeeDevice.publishCommand(command, device.friendly_name, nextPayload);
1136
+ nextPayload = {};
1137
+ lastRequestedHue = -1;
1138
+ lastRequestedSaturation = -1;
1139
+ }, 100);
1140
+ }
1057
1141
  zigbeeDevice.bridgedDevice.addCommandHandler('identify', async (data) => {
1058
- zigbeeDevice.log.debug(`Command identify called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber} request identifyTime:${data.request.identifyTime} `);
1142
+ zigbeeDevice.log.debug(`Command identify called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request identifyTime:${data.request.identifyTime} `);
1059
1143
  });
1060
1144
  if (zigbeeDevice.bridgedDevice.hasClusterServer(OnOffCluster.id) || zigbeeDevice.hasEndpoints) {
1061
1145
  for (const child of zigbeeDevice.bridgedDevice.getChildEndpoints()) {
1062
1146
  if (child.hasClusterServer(OnOffCluster)) {
1063
1147
  child.addCommandHandler('on', async (data) => {
1064
- zigbeeDevice.log.debug(`Command on called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
1065
- const payload = {};
1066
- payload['state_' + data.endpoint.uniqueStorageKey] = 'ON';
1067
- zigbeeDevice.publishCommand('on', device.friendly_name, payload);
1148
+ zigbeeDevice.log.debug(`Command on called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber}`);
1149
+ nextPayload['state_' + data.endpoint.uniqueStorageKey] = 'ON';
1150
+ cachePublishLight();
1068
1151
  });
1069
1152
  child.addCommandHandler('off', async (data) => {
1070
- zigbeeDevice.log.debug(`Command off called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
1071
- const payload = {};
1072
- payload['state_' + data.endpoint.uniqueStorageKey] = 'OFF';
1073
- zigbeeDevice.publishCommand('off', device.friendly_name, payload);
1153
+ zigbeeDevice.log.debug(`Command off called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber}`);
1154
+ nextPayload['state_' + data.endpoint.uniqueStorageKey] = 'OFF';
1155
+ cachePublishLight();
1074
1156
  });
1075
1157
  child.addCommandHandler('toggle', async (data) => {
1076
- zigbeeDevice.log.debug(`Command toggle called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
1077
- const payload = {};
1078
- payload['state_' + data.endpoint.uniqueStorageKey] = 'TOGGLE';
1079
- zigbeeDevice.publishCommand('toggle', device.friendly_name, payload);
1158
+ zigbeeDevice.log.debug(`Command toggle called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber}`);
1159
+ nextPayload['state_' + data.endpoint.uniqueStorageKey] = 'TOGGLE';
1160
+ cachePublishLight();
1080
1161
  });
1081
1162
  }
1082
1163
  }
1083
1164
  zigbeeDevice.bridgedDevice.addCommandHandler('on', async (data) => {
1084
- zigbeeDevice.log.debug(`Command on called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
1085
- zigbeeDevice.publishCommand('on', device.friendly_name, { state: 'ON' });
1165
+ zigbeeDevice.log.debug(`Command on called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber}`);
1166
+ nextPayload['state'] = 'ON';
1167
+ cachePublishLight();
1086
1168
  });
1087
1169
  zigbeeDevice.bridgedDevice.addCommandHandler('off', async (data) => {
1088
- zigbeeDevice.log.debug(`Command off called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
1089
- zigbeeDevice.publishCommand('off', device.friendly_name, { state: 'OFF' });
1170
+ zigbeeDevice.log.debug(`Command off called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber}`);
1171
+ nextPayload['state'] = 'OFF';
1172
+ cachePublishLight();
1090
1173
  });
1091
1174
  zigbeeDevice.bridgedDevice.addCommandHandler('toggle', async (data) => {
1092
- zigbeeDevice.log.debug(`Command toggle called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
1093
- zigbeeDevice.publishCommand('toggle', device.friendly_name, { state: 'TOGGLE' });
1175
+ zigbeeDevice.log.debug(`Command toggle called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber}`);
1176
+ nextPayload['state'] = 'TOGGLE';
1177
+ cachePublishLight();
1094
1178
  });
1095
1179
  }
1096
1180
  if (zigbeeDevice.bridgedDevice.hasClusterServer(LevelControlCluster.id) || zigbeeDevice.hasEndpoints) {
1097
1181
  for (const child of zigbeeDevice.bridgedDevice.getChildEndpoints()) {
1098
1182
  if (child.hasClusterServer(LevelControlCluster)) {
1099
1183
  child.addCommandHandler('moveToLevel', async (data) => {
1100
- zigbeeDevice.log.debug(`Command moveToLevel called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber} request: ${data.request.level} transition: ${data.request.transitionTime}`);
1101
- const payload = {};
1102
- payload['brightness_' + data.endpoint.uniqueStorageKey] = data.request.level;
1184
+ zigbeeDevice.log.debug(`Command moveToLevel called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: ${data.request.level} transition: ${data.request.transitionTime}`);
1185
+ nextPayload['state_' + data.endpoint.uniqueStorageKey] = 'ON';
1186
+ nextPayload['brightness_' + data.endpoint.uniqueStorageKey] = data.request.level;
1103
1187
  if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1104
- payload['transition'] = Math.round(data.request.transitionTime / 10);
1105
- zigbeeDevice.publishCommand('moveToLevel', device.friendly_name, payload);
1188
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1189
+ cachePublishLight();
1106
1190
  });
1107
1191
  child.addCommandHandler('moveToLevelWithOnOff', async (data) => {
1108
- zigbeeDevice.log.debug(`Command moveToLevelWithOnOff called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber} request: ${data.request.level} transition: ${data.request.transitionTime}`);
1109
- const payload = {};
1110
- payload['brightness_' + data.endpoint.uniqueStorageKey] = data.request.level;
1192
+ zigbeeDevice.log.debug(`Command moveToLevelWithOnOff called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: ${data.request.level} transition: ${data.request.transitionTime}`);
1193
+ nextPayload['state_' + data.endpoint.uniqueStorageKey] = 'ON';
1194
+ nextPayload['brightness_' + data.endpoint.uniqueStorageKey] = data.request.level;
1111
1195
  if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1112
- payload['transition'] = Math.round(data.request.transitionTime / 10);
1113
- zigbeeDevice.publishCommand('moveToLevelWithOnOff', device.friendly_name, payload);
1196
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1197
+ cachePublishLight();
1114
1198
  });
1115
1199
  }
1116
1200
  }
1117
1201
  zigbeeDevice.bridgedDevice.addCommandHandler('moveToLevel', async (data) => {
1118
- zigbeeDevice.log.debug(`Command moveToLevel called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber} request: ${data.request.level} transition: ${data.request.transitionTime}`);
1119
- const payload = { brightness: data.request.level };
1202
+ zigbeeDevice.log.debug(`Command moveToLevel called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: ${data.request.level} transition: ${data.request.transitionTime}`);
1203
+ nextPayload['state'] = 'ON';
1204
+ nextPayload['brightness'] = data.request.level;
1120
1205
  if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1121
- payload['transition'] = Math.round(data.request.transitionTime / 10);
1122
- zigbeeDevice.publishCommand('moveToLevel', device.friendly_name, payload);
1206
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1207
+ cachePublishLight();
1123
1208
  });
1124
1209
  zigbeeDevice.bridgedDevice.addCommandHandler('moveToLevelWithOnOff', async (data) => {
1125
- zigbeeDevice.log.debug(`Command moveToLevelWithOnOff called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber} request: ${data.request.level} transition: ${data.request.transitionTime}`);
1126
- const payload = { brightness: data.request.level };
1210
+ zigbeeDevice.log.debug(`Command moveToLevelWithOnOff called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: ${data.request.level} transition: ${data.request.transitionTime}`);
1211
+ nextPayload['state'] = 'ON';
1212
+ nextPayload['brightness'] = data.request.level;
1127
1213
  if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1128
- payload['transition'] = Math.round(data.request.transitionTime / 10);
1129
- zigbeeDevice.publishCommand('moveToLevelWithOnOff', device.friendly_name, payload);
1214
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1215
+ cachePublishLight();
1130
1216
  });
1131
1217
  }
1132
- if (zigbeeDevice.bridgedDevice.hasAttributeServer(ColorControlCluster.id, 'colorTemperatureMireds')) {
1133
- zigbeeDevice.bridgedDevice.addCommandHandler('moveToColorTemperature', async ({ request }) => {
1134
- zigbeeDevice.log.debug(`Command moveToColorTemperature called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request: ${request.colorTemperatureMireds}`);
1135
- await zigbeeDevice.bridgedDevice?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.ColorTemperatureMireds, zigbeeDevice.log);
1136
- const payload = {};
1218
+ if (zigbeeDevice.bridgedDevice.hasAttributeServer(ColorControlCluster.id, 'colorTemperatureMireds') || zigbeeDevice.hasEndpoints) {
1219
+ for (const child of zigbeeDevice.bridgedDevice.getChildEndpoints()) {
1220
+ if (child.hasAttributeServer(ColorControlCluster.id, 'colorTemperatureMireds')) {
1221
+ child.addCommandHandler('moveToColorTemperature', async (data) => {
1222
+ zigbeeDevice.log.debug(`Command moveToColorTemperature called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: ${data.request.colorTemperatureMireds} transition: ${data.request.transitionTime}`);
1223
+ await child.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.ColorTemperatureMireds, zigbeeDevice.log);
1224
+ nextPayload['state_' + data.endpoint.uniqueStorageKey] = 'ON';
1225
+ if (zigbeeDevice.propertyMap.get('color_temp')) {
1226
+ nextPayload['color_temp_' + data.endpoint.uniqueStorageKey] = data.request.colorTemperatureMireds;
1227
+ if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1228
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1229
+ cachePublishLight();
1230
+ }
1231
+ else {
1232
+ const rgb = kelvinToRGB(miredToKelvin(data.request.colorTemperatureMireds));
1233
+ nextPayload['color_' + data.endpoint.uniqueStorageKey] = { r: rgb.r, g: rgb.g, b: rgb.b };
1234
+ if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1235
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1236
+ cachePublishLight();
1237
+ zigbeeDevice.log.info(`Command moveToColorTemperature called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${nf} but color_temp property is not available. Converting ${data.request.colorTemperatureMireds} to RGB ${debugStringify(rgb)}.`);
1238
+ }
1239
+ });
1240
+ }
1241
+ }
1242
+ zigbeeDevice.bridgedDevice.addCommandHandler('moveToColorTemperature', async (data) => {
1243
+ zigbeeDevice.log.debug(`Command moveToColorTemperature called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: ${data.request.colorTemperatureMireds} transition: ${data.request.transitionTime}`);
1244
+ await data.endpoint?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.ColorTemperatureMireds, zigbeeDevice.log);
1245
+ nextPayload['state'] = 'ON';
1137
1246
  if (zigbeeDevice.propertyMap.get('color_temp')) {
1138
- payload['color_temp'] = request.colorTemperatureMireds;
1247
+ nextPayload['color_temp'] = data.request.colorTemperatureMireds;
1248
+ if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1249
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1250
+ cachePublishLight();
1139
1251
  }
1140
1252
  else {
1141
- const rgb = kelvinToRGB(miredToKelvin(request.colorTemperatureMireds));
1142
- payload['color'] = { r: rgb.r, g: rgb.g, b: rgb.b };
1143
- zigbeeDevice.log.info(`Command moveToColorTemperature called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${nf} but color_temp property is not available. Converting ${request.colorTemperatureMireds} to RGB ${debugStringify(payload['color'])}.`);
1253
+ const rgb = kelvinToRGB(miredToKelvin(data.request.colorTemperatureMireds));
1254
+ nextPayload['color'] = { r: rgb.r, g: rgb.g, b: rgb.b };
1255
+ if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1256
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1257
+ cachePublishLight();
1258
+ zigbeeDevice.log.info(`Command moveToColorTemperature called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${nf} but color_temp property is not available. Converting ${data.request.colorTemperatureMireds} to RGB ${debugStringify(rgb)}.`);
1144
1259
  }
1145
- if (zigbeeDevice.transition && request.transitionTime && request.transitionTime / 10 >= 1)
1146
- payload['transition'] = Math.round(request.transitionTime / 10);
1147
- zigbeeDevice.publishCommand('moveToColorTemperature', device.friendly_name, payload);
1148
1260
  });
1149
1261
  }
1150
- if (zigbeeDevice.bridgedDevice.hasAttributeServer(ColorControlCluster.id, 'currentX')) {
1151
- zigbeeDevice.bridgedDevice.addCommandHandler('moveToColor', async ({ request }) => {
1152
- zigbeeDevice.log.debug(`Command moveToColor called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request: X: ${request.colorX} Y: ${request.colorY}`);
1153
- await zigbeeDevice.bridgedDevice?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentXAndCurrentY, zigbeeDevice.log);
1154
- const payload = { color: { x: request.colorX / 65536, y: request.colorY / 65536 } };
1155
- if (zigbeeDevice.transition && request.transitionTime && request.transitionTime / 10 >= 1)
1156
- payload['transition'] = Math.round(request.transitionTime / 10);
1157
- zigbeeDevice.publishCommand('moveToColor', device.friendly_name, payload);
1262
+ if (zigbeeDevice.bridgedDevice.hasAttributeServer(ColorControlCluster.id, 'currentX') || zigbeeDevice.hasEndpoints) {
1263
+ for (const child of zigbeeDevice.bridgedDevice.getChildEndpoints()) {
1264
+ if (child.hasAttributeServer(ColorControlCluster.id, 'currentX')) {
1265
+ child.addCommandHandler('moveToColor', async (data) => {
1266
+ zigbeeDevice.log.debug(`Command moveToColor called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: X: ${data.request.colorX} Y: ${data.request.colorY} transition: ${data.request.transitionTime}`);
1267
+ await child.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentXAndCurrentY, zigbeeDevice.log);
1268
+ nextPayload['state_' + data.endpoint.uniqueStorageKey] = 'ON';
1269
+ nextPayload['color_' + data.endpoint.uniqueStorageKey] = { x: data.request.colorX / 65536, y: data.request.colorY / 65536 };
1270
+ if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1271
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1272
+ cachePublishLight();
1273
+ });
1274
+ }
1275
+ }
1276
+ zigbeeDevice.bridgedDevice.addCommandHandler('moveToColor', async (data) => {
1277
+ zigbeeDevice.log.debug(`Command moveToColor called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: X: ${data.request.colorX} Y: ${data.request.colorY} transition: ${data.request.transitionTime}`);
1278
+ await data.endpoint?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentXAndCurrentY, zigbeeDevice.log);
1279
+ nextPayload['state'] = 'ON';
1280
+ nextPayload['color'] = { x: data.request.colorX / 65536, y: data.request.colorY / 65536 };
1281
+ if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1282
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1283
+ cachePublishLight();
1158
1284
  });
1159
1285
  }
1160
- if (zigbeeDevice.bridgedDevice.hasAttributeServer(ColorControlCluster.id, 'currentHue')) {
1161
- let lastRequestedHue = 0;
1162
- let lastRequestedSaturation = 0;
1163
- zigbeeDevice.bridgedDevice.addCommandHandler('moveToHue', async ({ request }) => {
1164
- zigbeeDevice.log.debug(`Command moveToHue called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request: ${request.hue}`);
1286
+ if (zigbeeDevice.bridgedDevice.hasAttributeServer(ColorControlCluster.id, 'currentHue') || zigbeeDevice.hasEndpoints) {
1287
+ for (const child of zigbeeDevice.bridgedDevice.getChildEndpoints()) {
1288
+ if (child.hasAttributeServer(ColorControlCluster.id, 'currentHue')) {
1289
+ child.addCommandHandler('moveToHue', async (data) => {
1290
+ zigbeeDevice.log.debug(`Command moveToHue called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: ${data.request.hue} transition: ${data.request.transitionTime}`);
1291
+ await child.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentHueAndCurrentSaturation, zigbeeDevice.log);
1292
+ nextPayload['state_' + data.endpoint.uniqueStorageKey] = 'ON';
1293
+ lastRequestedHue = data.request.hue;
1294
+ if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1295
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1296
+ cachePublishLight();
1297
+ });
1298
+ child.addCommandHandler('moveToSaturation', async (data) => {
1299
+ zigbeeDevice.log.debug(`Command moveToSaturation called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: ${data.request.saturation} transition: ${data.request.transitionTime}`);
1300
+ await child.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentHueAndCurrentSaturation, zigbeeDevice.log);
1301
+ nextPayload['state_' + data.endpoint.uniqueStorageKey] = 'ON';
1302
+ lastRequestedSaturation = data.request.saturation;
1303
+ if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1304
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1305
+ cachePublishLight();
1306
+ });
1307
+ child.addCommandHandler('moveToHueAndSaturation', async (data) => {
1308
+ zigbeeDevice.log.debug(`Command moveToHueAndSaturation called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: ${data.request.hue}-${data.request.saturation} transition: ${data.request.transitionTime}`);
1309
+ await child.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentHueAndCurrentSaturation, zigbeeDevice.log);
1310
+ nextPayload['state_' + data.endpoint.uniqueStorageKey] = 'ON';
1311
+ const rgb = color.hslColorToRgbColor((data.request.hue / 254) * 360, (data.request.saturation / 254) * 100, 50);
1312
+ nextPayload['color_' + data.endpoint.uniqueStorageKey] = { r: rgb.r, g: rgb.g, b: rgb.b };
1313
+ cachePublishLight();
1314
+ });
1315
+ }
1316
+ }
1317
+ zigbeeDevice.bridgedDevice.addCommandHandler('moveToHue', async (data) => {
1318
+ zigbeeDevice.log.debug(`Command moveToHue called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: ${data.request.hue} transition: ${data.request.transitionTime}`);
1165
1319
  await zigbeeDevice.bridgedDevice?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentHueAndCurrentSaturation, zigbeeDevice.log);
1166
- lastRequestedHue = request.hue;
1167
- zigbeeDevice.colorTimeout = setTimeout(() => {
1168
- clearTimeout(zigbeeDevice.colorTimeout);
1169
- const rgb = color.hslColorToRgbColor((request.hue / 254) * 360, (lastRequestedSaturation / 254) * 100, 50);
1170
- const payload = { color: { r: rgb.r, g: rgb.g, b: rgb.b } };
1171
- if (zigbeeDevice.transition && request.transitionTime && request.transitionTime / 10 >= 1)
1172
- payload['transition'] = Math.round(request.transitionTime / 10);
1173
- zigbeeDevice.publishCommand('moveToHue', device.friendly_name, payload);
1174
- }, 500);
1320
+ nextPayload['state'] = 'ON';
1321
+ lastRequestedHue = data.request.hue;
1322
+ if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1323
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1324
+ cachePublishLight();
1175
1325
  });
1176
- zigbeeDevice.bridgedDevice.addCommandHandler('moveToSaturation', async ({ request }) => {
1177
- zigbeeDevice.log.debug(`Command moveToSaturation called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request: ${request.saturation}`);
1326
+ zigbeeDevice.bridgedDevice.addCommandHandler('moveToSaturation', async (data) => {
1327
+ zigbeeDevice.log.debug(`Command moveToSaturation called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: ${data.request.saturation} transition: ${data.request.transitionTime}`);
1178
1328
  await zigbeeDevice.bridgedDevice?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentHueAndCurrentSaturation, zigbeeDevice.log);
1179
- lastRequestedSaturation = request.saturation;
1180
- zigbeeDevice.colorTimeout = setTimeout(() => {
1181
- clearTimeout(zigbeeDevice.colorTimeout);
1182
- const rgb = color.hslColorToRgbColor((lastRequestedHue / 254) * 360, (request.saturation / 254) * 100, 50);
1183
- const payload = { color: { r: rgb.r, g: rgb.g, b: rgb.b } };
1184
- if (zigbeeDevice.transition && request.transitionTime && request.transitionTime / 10 >= 1)
1185
- payload['transition'] = Math.round(request.transitionTime / 10);
1186
- zigbeeDevice.publishCommand('moveToSaturation', device.friendly_name, payload);
1187
- }, 500);
1329
+ nextPayload['state'] = 'ON';
1330
+ lastRequestedSaturation = data.request.saturation;
1331
+ if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1332
+ nextPayload['transition'] = Math.round(data.request.transitionTime / 10);
1333
+ cachePublishLight();
1188
1334
  });
1189
- zigbeeDevice.bridgedDevice.addCommandHandler('moveToHueAndSaturation', async ({ request }) => {
1190
- zigbeeDevice.log.debug(`Command moveToHueAndSaturation called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request: ${request.hue}-${request.saturation}`);
1335
+ zigbeeDevice.bridgedDevice.addCommandHandler('moveToHueAndSaturation', async (data) => {
1336
+ zigbeeDevice.log.debug(`Command moveToHueAndSaturation called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeId}:${data.endpoint?.maybeNumber} request: ${data.request.hue}-${data.request.saturation} transition: ${data.request.transitionTime}`);
1191
1337
  await zigbeeDevice.bridgedDevice?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentHueAndCurrentSaturation, zigbeeDevice.log);
1192
- const rgb = color.hslColorToRgbColor((request.hue / 254) * 360, (request.saturation / 254) * 100, 50);
1193
- const payload = { color: { r: rgb.r, g: rgb.g, b: rgb.b } };
1194
- if (zigbeeDevice.transition && request.transitionTime && request.transitionTime / 10 >= 1)
1195
- payload['transition'] = Math.round(request.transitionTime / 10);
1196
- zigbeeDevice.publishCommand('moveToHueAndSaturation', device.friendly_name, payload);
1338
+ nextPayload['state'] = 'ON';
1339
+ const rgb = color.hslColorToRgbColor((data.request.hue / 254) * 360, (data.request.saturation / 254) * 100, 50);
1340
+ nextPayload['color'] = { r: rgb.r, g: rgb.g, b: rgb.b };
1341
+ cachePublishLight();
1197
1342
  });
1198
1343
  }
1199
1344
  if (zigbeeDevice.bridgedDevice.hasClusterServer(WindowCoveringCluster.id)) {
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { ZigbeePlatform } from './platform.js';
2
- export * from './entity.js';
3
2
  export default function initializePlugin(matterbridge, log, config) {
4
3
  return new ZigbeePlatform(matterbridge, log, config);
5
4
  }
package/dist/platform.js CHANGED
@@ -1,17 +1,15 @@
1
+ import path from 'node:path';
1
2
  import { addVirtualDevice, MatterbridgeDynamicPlatform } from 'matterbridge';
2
3
  import { dn, gn, db, wr, zb, payloadStringify, rs, debugStringify, CYAN, er, nf } from 'matterbridge/logger';
3
4
  import { isValidNumber, isValidString, waiter } from 'matterbridge/utils';
4
5
  import { BridgedDeviceBasicInformation, DoorLock } from 'matterbridge/matter/clusters';
5
- import path from 'node:path';
6
6
  import { ZigbeeDevice, ZigbeeGroup } from './entity.js';
7
7
  import { Zigbee2MQTT } from './zigbee2mqtt.js';
8
8
  export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
9
- publishCallBack = undefined;
10
- permitJoinCallBack = undefined;
11
9
  bridgedDevices = [];
12
10
  zigbeeEntities = [];
13
- injectTimer;
14
11
  namePostfix = 1;
12
+ injectTimer;
15
13
  mqttHost = 'mqtt://localhost';
16
14
  mqttPort = 1883;
17
15
  mqttTopic = 'zigbee2mqtt';
@@ -337,20 +335,20 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
337
335
  }
338
336
  async onConfigure() {
339
337
  await super.onConfigure();
340
- this.log.info(`Requesting update for ${this.zigbeeEntities.length} zigbee entities.`);
338
+ this.log.info(`Configuring ${this.zigbeeEntities.length} zigbee entities.`);
341
339
  for (const bridgedEntity of this.zigbeeEntities) {
342
340
  await bridgedEntity.configure();
343
341
  if (bridgedEntity.isRouter && bridgedEntity.bridgedDevice) {
344
- this.log.info(`Configuring router ${bridgedEntity.bridgedDevice?.deviceName}.`);
342
+ this.log.info(`Configuring router ${bridgedEntity.bridgedDevice.deviceName}.`);
345
343
  if (this.z2mBridgeInfo?.permit_join) {
346
- bridgedEntity.bridgedDevice?.setAttribute(DoorLock.Cluster.id, 'lockState', DoorLock.LockState.Unlocked, this.log);
344
+ bridgedEntity.bridgedDevice.setAttribute(DoorLock.Cluster.id, 'lockState', DoorLock.LockState.Unlocked, this.log);
347
345
  if (bridgedEntity.bridgedDevice.maybeNumber)
348
- bridgedEntity.bridgedDevice?.triggerEvent(DoorLock.Cluster.id, 'lockOperation', { lockOperationType: DoorLock.LockOperationType.Unlock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }, this.log);
346
+ bridgedEntity.bridgedDevice.triggerEvent(DoorLock.Cluster.id, 'lockOperation', { lockOperationType: DoorLock.LockOperationType.Unlock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }, this.log);
349
347
  }
350
348
  else {
351
- bridgedEntity.bridgedDevice?.setAttribute(DoorLock.Cluster.id, 'lockState', DoorLock.LockState.Locked, this.log);
349
+ bridgedEntity.bridgedDevice.setAttribute(DoorLock.Cluster.id, 'lockState', DoorLock.LockState.Locked, this.log);
352
350
  if (bridgedEntity.bridgedDevice.maybeNumber)
353
- bridgedEntity.bridgedDevice?.triggerEvent(DoorLock.Cluster.id, 'lockOperation', { lockOperationType: DoorLock.LockOperationType.Lock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }, this.log);
351
+ bridgedEntity.bridgedDevice.triggerEvent(DoorLock.Cluster.id, 'lockOperation', { lockOperationType: DoorLock.LockOperationType.Lock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }, this.log);
354
352
  }
355
353
  }
356
354
  if (bridgedEntity.isDevice && bridgedEntity.device)
@@ -398,7 +396,6 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
398
396
  await super.onShutdown(reason);
399
397
  this.z2m.removeAllListeners();
400
398
  this.z2m.stop();
401
- this.publishCallBack = undefined;
402
399
  this.log.debug('Shutting down zigbee2mqtt platform: ' + reason);
403
400
  for (const entity of this.zigbeeEntities) {
404
401
  entity.destroy();
@@ -413,26 +410,9 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
413
410
  await this.unregisterAllDevices();
414
411
  this.log.info(`Shutdown zigbee2mqtt dynamic platform v${this.version}`);
415
412
  }
416
- setPublishCallBack(onPublish) {
417
- this.publishCallBack = onPublish;
418
- }
419
- setPermitJoinCallBack(onPermitJoin) {
420
- this.permitJoinCallBack = onPermitJoin;
421
- }
422
413
  async publish(topic, subTopic, message) {
423
- if (this.config.type === 'MatterbridgeExtension') {
424
- if (this.publishCallBack && !topic.startsWith('bridge/request'))
425
- await this.publishCallBack(topic, subTopic, message);
426
- if (this.permitJoinCallBack && topic.startsWith('bridge/request'))
427
- await this.permitJoinCallBack('', message === '{"value":true}');
428
- }
429
- else {
430
- await this.z2m.publish(this.z2m.mqttTopic + '/' + topic + (subTopic === '' ? '' : '/' + subTopic), message);
431
- this.log.info(`MQTT publish topic: ${CYAN}${this.z2m.mqttTopic + '/' + topic + (subTopic === '' ? '' : '/' + subTopic)}${nf} payload: ${CYAN}${message}${nf}`);
432
- }
433
- }
434
- emit(eventName, data) {
435
- this.z2m.emit(eventName, data);
414
+ this.log.info(`MQTT publish topic: ${CYAN}${this.z2m.mqttTopic + '/' + topic + (subTopic === '' ? '' : '/' + subTopic)}${nf} payload: ${CYAN}${message}${nf}`);
415
+ await this.z2m.publish(this.z2m.mqttTopic + '/' + topic + (subTopic === '' ? '' : '/' + subTopic), message);
436
416
  }
437
417
  async requestDeviceUpdate(device) {
438
418
  this.log.debug(`Requesting update for ${device.friendly_name} model_id: ${device.model_id} manufacturer: ${device.manufacturer}`);
@@ -205,6 +205,7 @@ export class Zigbee2MQTT extends EventEmitter {
205
205
  this.log.error('Error publishing keepalive MQTT message:', error);
206
206
  }
207
207
  }, (this.options.keepalive ?? 60) * 1000).unref();
208
+ return;
208
209
  })
209
210
  .catch((error) => {
210
211
  this.log.error(`Error connecting to ${this.getUrl()}: ${error.message}`);
@@ -231,6 +232,7 @@ export class Zigbee2MQTT extends EventEmitter {
231
232
  this.mqttIsEnding = false;
232
233
  this.mqttClient = undefined;
233
234
  this.log.debug('Connection closed');
235
+ return;
234
236
  })
235
237
  .catch((error) => {
236
238
  this.log.error(`Error closing connection: ${error.message}`);
@@ -245,6 +247,7 @@ export class Zigbee2MQTT extends EventEmitter {
245
247
  .then(() => {
246
248
  this.log.debug(`Subscribe success on topic: ${topic}`);
247
249
  this.emit('mqtt_subscribed');
250
+ return;
248
251
  })
249
252
  .catch((error) => {
250
253
  this.log.error(`Subscribe error: ${error} on topic: ${topic}`);
@@ -325,6 +328,7 @@ export class Zigbee2MQTT extends EventEmitter {
325
328
  writeFile(`${filePath}.json`, JSON.stringify(jsonData, null, 2))
326
329
  .then(() => {
327
330
  this.log.debug(`Successfully wrote to ${filePath}.json`);
331
+ return;
328
332
  })
329
333
  .catch((error) => {
330
334
  this.log.error(`Error writing to ${filePath}.json:`, error);
@@ -335,6 +339,7 @@ export class Zigbee2MQTT extends EventEmitter {
335
339
  writeFile(`${filePath}`, data)
336
340
  .then(() => {
337
341
  this.log.debug(`Successfully wrote to ${filePath}`);
342
+ return;
338
343
  })
339
344
  .catch((error) => {
340
345
  this.log.error(`Error writing to ${filePath}:`, error);
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "name": "matterbridge-zigbee2mqtt",
3
- "version": "2.6.0",
3
+ "version": "2.7.0-dev-20250711-6299fe0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-zigbee2mqtt",
9
- "version": "2.6.0",
9
+ "version": "2.7.0-dev-20250711-6299fe0",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "moment": "2.30.1",
13
- "mqtt": "5.13.1",
14
- "node-ansi-logger": "3.0.1",
15
- "node-persist-manager": "1.0.8"
13
+ "mqtt": "5.13.2",
14
+ "node-ansi-logger": "3.1.1",
15
+ "node-persist-manager": "2.0.0"
16
16
  },
17
17
  "engines": {
18
- "node": ">=18.0.0 <19.0.0 || >=20.0.0 <21.0.0 || >=22.0.0"
18
+ "node": ">=18.0.0 <19.0.0 || >=20.0.0 <21.0.0 || >=22.0.0 <23.0.0 || >=24.0.0 <25.0.0"
19
19
  },
20
20
  "funding": {
21
21
  "type": "buymeacoffee",
@@ -32,12 +32,12 @@
32
32
  }
33
33
  },
34
34
  "node_modules/@types/node": {
35
- "version": "22.15.30",
36
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz",
37
- "integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==",
35
+ "version": "24.0.12",
36
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.12.tgz",
37
+ "integrity": "sha512-LtOrbvDf5ndC9Xi+4QZjVL0woFymF/xSTKZKPgrrl7H7XoeDvnD+E2IclKVDyaK9UM756W/3BXqSU+JEHopA9g==",
38
38
  "license": "MIT",
39
39
  "dependencies": {
40
- "undici-types": "~6.21.0"
40
+ "undici-types": "~7.8.0"
41
41
  }
42
42
  },
43
43
  "node_modules/@types/readable-stream": {
@@ -49,6 +49,15 @@
49
49
  "@types/node": "*"
50
50
  }
51
51
  },
52
+ "node_modules/@types/ws": {
53
+ "version": "8.18.1",
54
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
55
+ "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
56
+ "license": "MIT",
57
+ "dependencies": {
58
+ "@types/node": "*"
59
+ }
60
+ },
52
61
  "node_modules/abort-controller": {
53
62
  "version": "3.0.0",
54
63
  "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
@@ -286,11 +295,13 @@
286
295
  }
287
296
  },
288
297
  "node_modules/mqtt": {
289
- "version": "5.13.1",
290
- "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.13.1.tgz",
291
- "integrity": "sha512-g+4G+ma0UeL3Pgu1y1si2NHb4VLIEUCtF789WrG99lLG0XZyoab2EJoy58YgGSg/1yFdthDBH0+4llsZZD/vug==",
298
+ "version": "5.13.2",
299
+ "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.13.2.tgz",
300
+ "integrity": "sha512-21E15qjFMNEddowGtLiMw4wXfzZxd3Iv+q+zC+tJSmY0flEreNgov+8SKrYcapA+pjClaqj+IgJW0jsJsHkmVQ==",
292
301
  "license": "MIT",
293
302
  "dependencies": {
303
+ "@types/readable-stream": "^4.0.18",
304
+ "@types/ws": "^8.18.1",
294
305
  "commist": "^3.2.0",
295
306
  "concat-stream": "^2.0.0",
296
307
  "debug": "^4.4.0",
@@ -339,12 +350,13 @@
339
350
  "license": "MIT"
340
351
  },
341
352
  "node_modules/node-ansi-logger": {
342
- "version": "3.0.1",
343
- "resolved": "https://registry.npmjs.org/node-ansi-logger/-/node-ansi-logger-3.0.1.tgz",
344
- "integrity": "sha512-Nx7nkO6Sby8Eti1UFFdff3gImEC35izuvf+aRFh3mrDZ8hgXM/cZdflkpDMnDdxnabYl91LVi4RhB/dm5Yk9iA==",
345
- "license": "MIT",
353
+ "version": "3.1.1",
354
+ "resolved": "https://registry.npmjs.org/node-ansi-logger/-/node-ansi-logger-3.1.1.tgz",
355
+ "integrity": "sha512-tFeCSxwiRg5XaNda5nC27alzraZP76nLtUk1JDZqb9byhW4WYaSGL7/lmxFHEI16gypQDMEYljuF+wcG+cPPHw==",
356
+ "hasShrinkwrap": true,
357
+ "license": "Apache-2.0",
346
358
  "engines": {
347
- "node": ">=18.0.0"
359
+ "node": ">=18.0.0 <19.0.0 || >=20.0.0 <21.0.0 || >=22.0.0 <23.0.0 || >=24.0.0 <25.0.0"
348
360
  },
349
361
  "funding": {
350
362
  "type": "buymeacoffee",
@@ -364,15 +376,15 @@
364
376
  }
365
377
  },
366
378
  "node_modules/node-persist-manager": {
367
- "version": "1.0.8",
368
- "resolved": "https://registry.npmjs.org/node-persist-manager/-/node-persist-manager-1.0.8.tgz",
369
- "integrity": "sha512-J0wjdxOloD6aw4s2kljbV3nvHfguWph8LnfmTL+6MZiBsh46wjaIaZd/cHU0HWonrNfktCv7Zuvn0sEH0f2s+A==",
379
+ "version": "2.0.0",
380
+ "resolved": "https://registry.npmjs.org/node-persist-manager/-/node-persist-manager-2.0.0.tgz",
381
+ "integrity": "sha512-jpgOqCCn4ZEnIr4WcvqkyyGmmLJarV6aUyBlDQdG1G84X9UUmOmI1W2OZtc9K0z375CYWhjtGEXaY7GXNiEOfA==",
370
382
  "license": "MIT",
371
383
  "dependencies": {
372
- "node-persist": "^4.0.2"
384
+ "node-persist": "4.0.4"
373
385
  },
374
386
  "engines": {
375
- "node": ">=18.0.0"
387
+ "node": ">=18.0.0 <19.0.0 || >=20.0.0 <21.0.0 || >=22.0.0 <23.0.0 || >=24.0.0 <25.0.0"
376
388
  },
377
389
  "funding": {
378
390
  "type": "buymeacoffee",
@@ -472,9 +484,9 @@
472
484
  }
473
485
  },
474
486
  "node_modules/socks": {
475
- "version": "2.8.4",
476
- "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz",
477
- "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==",
487
+ "version": "2.8.5",
488
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz",
489
+ "integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==",
478
490
  "license": "MIT",
479
491
  "dependencies": {
480
492
  "ip-address": "^9.0.5",
@@ -522,9 +534,9 @@
522
534
  "license": "MIT"
523
535
  },
524
536
  "node_modules/undici-types": {
525
- "version": "6.21.0",
526
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
527
- "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
537
+ "version": "7.8.0",
538
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
539
+ "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
528
540
  "license": "MIT"
529
541
  },
530
542
  "node_modules/util-deprecate": {
@@ -568,9 +580,9 @@
568
580
  }
569
581
  },
570
582
  "node_modules/ws": {
571
- "version": "8.18.2",
572
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
573
- "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
583
+ "version": "8.18.3",
584
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
585
+ "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
574
586
  "license": "MIT",
575
587
  "engines": {
576
588
  "node": ">=10.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-zigbee2mqtt",
3
- "version": "2.6.0",
3
+ "version": "2.7.0-dev-20250711-6299fe0",
4
4
  "description": "Matterbridge zigbee2mqtt plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",
@@ -38,12 +38,12 @@
38
38
  "zigbee2mqtt"
39
39
  ],
40
40
  "engines": {
41
- "node": ">=18.0.0 <19.0.0 || >=20.0.0 <21.0.0 || >=22.0.0"
41
+ "node": ">=18.0.0 <19.0.0 || >=20.0.0 <21.0.0 || >=22.0.0 <23.0.0 || >=24.0.0 <25.0.0"
42
42
  },
43
43
  "dependencies": {
44
44
  "moment": "2.30.1",
45
- "mqtt": "5.13.1",
46
- "node-ansi-logger": "3.0.1",
47
- "node-persist-manager": "1.0.8"
45
+ "mqtt": "5.13.2",
46
+ "node-ansi-logger": "3.1.1",
47
+ "node-persist-manager": "2.0.0"
48
48
  }
49
49
  }
package/CODEOWNERS DELETED
@@ -1 +0,0 @@
1
- @Luligu
@@ -1,8 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- // enable isolatedModules just for ts-jest
5
- "isolatedModules": true
6
- },
7
- "include": ["**/*.spec.ts", "**/*.test.ts", "**/__test__/*"]
8
- }