matterbridge-zigbee2mqtt 2.4.7 → 2.5.0-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,39 @@ If you like this project and find it useful, please consider giving it a star on
8
8
  <img src="bmc-button.svg" alt="Buy me a coffee" width="120">
9
9
  </a>
10
10
 
11
+ ### Breaking Changes
12
+
13
+ New device types:
14
+
15
+ - extendedColorLight
16
+ - waterLeakDetector
17
+ - rainSensor
18
+ - smokeSensor
19
+
20
+ If your controller has issues detecting the new device type, blacklist these devices, restart, wait 5 minutes, remove the blacklist and restart again. This will create a new endpoint on the controller.
21
+
22
+ ## [2.5.0] - 2025-05-21
23
+
24
+ ### Added
25
+
26
+ - [scenes]: Added scenes support for groups and devices. See the README.md for explanations.
27
+ - [waterLeak]: Added waterLeakDetector device type for zigbee property "water_leak". Default to false (i.e. no alarm) since is not possible to get the property.
28
+ - [rainSensor]: Added rainSensor device type for zigbee property "rain". Default to false (i.e. no alarm) since is not possible to get the property.
29
+ - [smokeSensor]: Added smokeSensor device type for zigbee property "smoke". Default to false (i.e. no alarm) since is not possible to get the property.
30
+ - [colorTemp]: Added conversion from color temperature to rgb for the devices that doesn't support color temperature.
31
+
32
+ ### Changed
33
+
34
+ - [package]: Updated package.
35
+ - [package]: Updated dependencies.
36
+ - [plugin]: Requires Matterbridge 3.0.3.
37
+ - [config]: As anticipated in the previous release, the parameter postfixHostname has been removed. Use postfix if needed.
38
+ - [colorRgb]: Changed the default device type from colorTemperatureLight to extendedColorLight to solve the SmartThings issue with colors.
39
+
40
+ <a href="https://www.buymeacoffee.com/luligugithub">
41
+ <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
42
+ </a>
43
+
11
44
  ## [2.4.7] - 2025-03-19
12
45
 
13
46
  ### Added
package/README.md CHANGED
@@ -17,7 +17,7 @@ Matterbridge zigbee2mqtt is a matterbridge production-level plugin that expose a
17
17
 
18
18
  No hub or dedicated hardware needed.
19
19
 
20
- Interested in super fast and autonomous automations for zigbee2mqtt? Try this: https://github.com/Luligu/zigbee2mqtt-automations
20
+ Interested in super fast and autonomous **[automations for zigbee2mqtt](https://github.com/Luligu/zigbee2mqtt-automations)**? Try this: https://github.com/Luligu/zigbee2mqtt-automations.
21
21
 
22
22
  If you like this project and find it useful, please consider giving it a star on GitHub at https://github.com/Luligu/matterbridge-zigbee2mqtt and sponsoring it.
23
23
 
@@ -150,8 +150,9 @@ These are the default vules:
150
150
  "outletList": [],
151
151
  "featureBlackList": [],
152
152
  "deviceFeatureBlackList": {},
153
- "postfix": "",
154
- "postfixHostname": true
153
+ "scenesType": "outlet",
154
+ "scenesPrefix: true,
155
+ "postfix": ""
155
156
  }
156
157
  ```
157
158
 
@@ -250,6 +251,20 @@ If one of your devices is not supported out of the box, open an issue and we wil
250
251
 
251
252
  ![See the screenshot here](https://github.com/Luligu/matterbridge-zigbee2mqtt/blob/main/screenshot/Smart%20button.png)
252
253
 
254
+ ## Scenes in groups and devices
255
+
256
+ With release 2.5.0 has been added support for scenes in groups and devices.
257
+
258
+ In the config select what device type you want to use to expose the command that runs the scene: 'light' | 'outlet' | 'switch' | 'mounted_switch'.
259
+
260
+ Switch is not supported by Alexa. Mounted Switch is not supported by Apple Home.
261
+
262
+ The virtual device takes the name of the group or device it belongs to, with added the name of scene. If scenesPrefix is disabled, it takes only the name of the scene. Consider that in Matter the node name is 32 characters long.
263
+
264
+ The state of the virtual device is always reverted to off in a few seconds.
265
+
266
+ It is possibile to disable the feature globally with featureBlackList and on a per device/group base with deviceFeatureBlackList.
267
+
253
268
  # Known issues
254
269
 
255
270
  For general controller issues check the Matterbridge Known issues section
package/dist/entity.js CHANGED
@@ -1,12 +1,11 @@
1
- import { airQualitySensor, colorTemperatureSwitch, dimmableSwitch, onOffSwitch, powerSource, bridgedNode, electricalSensor, onOffLight, dimmableLight, colorTemperatureLight, onOffOutlet, coverDevice, thermostatDevice, MatterbridgeEndpoint, dimmableOutlet, doorLockDevice, occupancySensor, lightSensor, contactSensor, temperatureSensor, humiditySensor, pressureSensor, genericSwitch, } from 'matterbridge';
1
+ 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
2
  import { AnsiLogger, gn, dn, ign, idn, rs, db, debugStringify, hk, zb, or, nf, CYAN, er, YELLOW } from 'matterbridge/logger';
3
- import { deepCopy, deepEqual, isValidNumber } from 'matterbridge/utils';
3
+ import { deepCopy, deepEqual, isValidNumber, kelvinToRGB, miredToKelvin } from 'matterbridge/utils';
4
4
  import * as color from 'matterbridge/utils';
5
5
  import { SwitchesTag, NumberTag } from 'matterbridge/matter';
6
6
  import { getClusterNameById, ClusterId } from 'matterbridge/matter/types';
7
- 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, } from 'matterbridge/matter/clusters';
7
+ 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
8
  import EventEmitter from 'node:events';
9
- import { hostname } from 'node:os';
10
9
  export class ZigbeeEntity extends EventEmitter {
11
10
  log;
12
11
  serial = '';
@@ -110,7 +109,7 @@ export class ZigbeeEntity extends EventEmitter {
110
109
  const propertyMap = this.propertyMap.get(('action_' + value));
111
110
  if (propertyMap) {
112
111
  const child = this.bridgedDevice.getChildEndpointByName(propertyMap.endpoint);
113
- if (child && child.number)
112
+ if (child && child.maybeNumber)
114
113
  child.triggerSwitchEvent(propertyMap.action, this.log);
115
114
  }
116
115
  else
@@ -294,11 +293,11 @@ export class ZigbeeEntity extends EventEmitter {
294
293
  }
295
294
  const localValue = deviceEndpoint.getAttribute(ClusterId(clusterId), attributeName, undefined);
296
295
  if (typeof value === 'object' ? deepEqual(value, localValue) : value === localValue) {
297
- this.log.debug(`Skip update endpoint ${deviceEndpoint.name}:${deviceEndpoint.number}${childEndpointName ? ' (' + childEndpointName + ')' : ''} ` +
296
+ this.log.debug(`Skip update endpoint ${deviceEndpoint.name}:${deviceEndpoint.maybeNumber}${childEndpointName ? ' (' + childEndpointName + ')' : ''} ` +
298
297
  `attribute ${getClusterNameById(ClusterId(clusterId))}.${attributeName} already ${typeof value === 'object' ? debugStringify(value) : value}`);
299
298
  return;
300
299
  }
301
- this.log.info(`${db}Update endpoint ${this.eidn}${deviceEndpoint.name}:${deviceEndpoint.number}${db}${childEndpointName ? ' (' + zb + childEndpointName + db + ')' : ''} ` +
300
+ this.log.info(`${db}Update endpoint ${this.eidn}${deviceEndpoint.name}:${deviceEndpoint.maybeNumber}${db}${childEndpointName ? ' (' + zb + childEndpointName + db + ')' : ''} ` +
302
301
  `attribute ${hk}${getClusterNameById(ClusterId(clusterId))}${db}.${hk}${attributeName}${db} from ${YELLOW}${typeof localValue === 'object' ? debugStringify(localValue) : localValue}${db} to ${YELLOW}${typeof value === 'object' ? debugStringify(value) : value}${db}`);
303
302
  try {
304
303
  deviceEndpoint.setAttribute(ClusterId(clusterId), attributeName, value);
@@ -330,10 +329,7 @@ export class ZigbeeGroup extends ZigbeeEntity {
330
329
  }
331
330
  static async create(platform, group) {
332
331
  const zigbeeGroup = new ZigbeeGroup(platform, group);
333
- if (zigbeeGroup.platform.postfixHostname) {
334
- zigbeeGroup.serial = `group-${group.id}_${hostname}`.slice(0, 32);
335
- }
336
- else if (zigbeeGroup.platform.postfix !== '') {
332
+ if (zigbeeGroup.platform.postfix !== '') {
337
333
  zigbeeGroup.serial = `group-${group.id}-${zigbeeGroup.platform.postfix}`.slice(0, 32);
338
334
  }
339
335
  else {
@@ -419,7 +415,7 @@ export class ZigbeeGroup extends ZigbeeEntity {
419
415
  zigbeeGroup.propertyMap.set('color_temp', { name: 'color_temp', type: 'light', endpoint: '' });
420
416
  }
421
417
  if (useColor) {
422
- deviceType = colorTemperatureLight;
418
+ deviceType = extendedColorLight;
423
419
  zigbeeGroup.propertyMap.set('color', { name: 'color', type: 'light', endpoint: '' });
424
420
  }
425
421
  if (isCover) {
@@ -440,9 +436,14 @@ export class ZigbeeGroup extends ZigbeeEntity {
440
436
  return zigbeeGroup;
441
437
  zigbeeGroup.bridgedDevice = new MatterbridgeEndpoint([deviceType, bridgedNode, powerSource], { uniqueStorageKey: group.friendly_name }, zigbeeGroup.log.logLevel === "debug");
442
438
  }
443
- if (platform.config.groupScenes === true) {
439
+ if (!platform.featureBlackList?.includes('scenes') && !platform.deviceFeatureBlackList[group.friendly_name]?.includes('scenes')) {
444
440
  group.scenes.forEach((scene) => {
445
441
  zigbeeGroup.log.debug(`***Group ${gn}${group.friendly_name}${rs}${db} scene ${CYAN}${scene.name}${db} id ${CYAN}${scene.id}${db}`);
442
+ platform.setSelectDeviceEntity(`group-${group.id}`, 'scenes', 'Scenes', 'component');
443
+ platform.registerVirtualDevice(`${platform.config.scenesPrefix ? group.friendly_name + ' ' : ''}${scene.name}`, async () => {
444
+ zigbeeGroup.log.info(`Triggered scene "${scene.name}" id ${scene.id} from group ${group.friendly_name}`);
445
+ zigbeeGroup.publishCommand('scene_recall', group.friendly_name, { 'scene_recall': scene.id });
446
+ });
446
447
  });
447
448
  }
448
449
  zigbeeGroup.addBridgedDeviceBasicInformation();
@@ -458,7 +459,7 @@ export class ZigbeeGroup extends ZigbeeEntity {
458
459
  if (isLight)
459
460
  await zigbeeGroup.bridgedDevice.addFixedLabel('type', 'light');
460
461
  zigbeeGroup.bridgedDevice.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
461
- zigbeeGroup.log.warn(`Command identify called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} identifyTime:${identifyTime}`);
462
+ zigbeeGroup.log.debug(`Command identify called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} identifyTime:${identifyTime}`);
462
463
  });
463
464
  zigbeeGroup.bridgedDevice.addCommandHandler('on', async () => {
464
465
  zigbeeGroup.log.debug(`Command on called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db}`);
@@ -520,6 +521,14 @@ export class ZigbeeGroup extends ZigbeeEntity {
520
521
  const rgb = color.hslColorToRgbColor((request.hue / 254) * 360, (request.saturation / 254) * 100, 50);
521
522
  zigbeeGroup.publishCommand('moveToHueAndSaturation', group.friendly_name, { color: { r: rgb.r, g: rgb.g, b: rgb.b } });
522
523
  });
524
+ zigbeeGroup.bridgedDevice.addCommandHandler('moveToColor', async ({ request }) => {
525
+ zigbeeGroup.log.debug(`Command moveToColor called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: X: ${request.colorX} Y: ${request.colorY}`);
526
+ await zigbeeGroup.bridgedDevice?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentXAndCurrentY, zigbeeGroup.log);
527
+ const payload = { color: { x: request.colorX / 65536, y: request.colorY / 65536 } };
528
+ if (zigbeeGroup.transition && request.transitionTime && request.transitionTime / 10 >= 1)
529
+ payload['transition'] = Math.round(request.transitionTime / 10);
530
+ zigbeeGroup.publishCommand('moveToColor', group.friendly_name, payload);
531
+ });
523
532
  }
524
533
  }
525
534
  if (isCover) {
@@ -600,8 +609,8 @@ export const z2ms = [
600
609
  { type: 'outlet', name: 'brightness', property: 'brightness', deviceType: dimmableOutlet, cluster: LevelControl.Cluster.id, attribute: 'currentLevel', converter: (value) => { return Math.max(1, Math.min(254, value)); } },
601
610
  { type: 'light', name: 'state', property: 'state', deviceType: onOffLight, cluster: OnOff.Cluster.id, attribute: 'onOff', converter: (value) => { return value === 'ON' ? true : false; } },
602
611
  { type: 'light', name: 'brightness', property: 'brightness', deviceType: dimmableLight, cluster: LevelControl.Cluster.id, attribute: 'currentLevel', converter: (value) => { return Math.max(1, Math.min(254, value)); } },
603
- { type: 'light', name: 'color_hs', property: 'color_hs', deviceType: colorTemperatureLight, cluster: ColorControl.Cluster.id, attribute: 'colorMode' },
604
- { type: 'light', name: 'color_xy', property: 'color_xy', deviceType: colorTemperatureLight, cluster: ColorControl.Cluster.id, attribute: 'colorMode' },
612
+ { type: 'light', name: 'color_hs', property: 'color_hs', deviceType: extendedColorLight, cluster: ColorControl.Cluster.id, attribute: 'colorMode' },
613
+ { type: 'light', name: 'color_xy', property: 'color_xy', deviceType: extendedColorLight, cluster: ColorControl.Cluster.id, attribute: 'colorMode' },
605
614
  { type: 'light', name: 'color_temp', property: 'color_temp', deviceType: colorTemperatureLight, cluster: ColorControl.Cluster.id, attribute: 'colorMode' },
606
615
  { type: 'cover', name: 'state', property: 'state', deviceType: coverDevice, cluster: WindowCovering.Cluster.id, attribute: 'targetPositionLiftPercent100ths' },
607
616
  { type: 'cover', name: 'moving', property: 'moving', deviceType: coverDevice, cluster: WindowCovering.Cluster.id, attribute: 'operationalStatus' },
@@ -623,9 +632,10 @@ export const z2ms = [
623
632
  { type: '', name: 'occupancy', property: 'occupancy', deviceType: occupancySensor, cluster: OccupancySensing.Cluster.id, attribute: 'occupancy', converter: (value) => { return { occupied: value }; } },
624
633
  { type: '', name: 'illuminance', property: 'illuminance', deviceType: lightSensor, cluster: IlluminanceMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value) => { return Math.round(Math.max(Math.min(10000 * Math.log10(value), 0xfffe), 0)); } },
625
634
  { type: '', name: 'contact', property: 'contact', deviceType: contactSensor, cluster: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => { return value; } },
626
- { type: '', name: 'water_leak', property: 'water_leak', deviceType: contactSensor, cluster: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => { return !value; } },
635
+ { type: '', name: 'water_leak', property: 'water_leak', deviceType: waterLeakDetector, cluster: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => { return value; } },
636
+ { type: '', name: 'rain', property: 'rain', deviceType: rainSensor, cluster: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => { return value; } },
627
637
  { type: '', name: 'vibration', property: 'vibration', deviceType: contactSensor, cluster: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => { return !value; } },
628
- { type: '', name: 'smoke', property: 'smoke', deviceType: contactSensor, cluster: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => { return !value; } },
638
+ { type: '', name: 'smoke', property: 'smoke', deviceType: smokeCoAlarm, cluster: SmokeCoAlarm.Cluster.id, attribute: 'smokeState', converter: (value) => { return value ? SmokeCoAlarm.AlarmState.Critical : SmokeCoAlarm.AlarmState.Normal; } },
629
639
  { type: '', name: 'carbon_monoxide', property: 'carbon_monoxide', deviceType: contactSensor, cluster: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => { return !value; } },
630
640
  { type: '', name: 'temperature', property: 'temperature', deviceType: temperatureSensor, cluster: TemperatureMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value) => { return Math.round(value * 100); } },
631
641
  { type: '', name: 'humidity', property: 'humidity', deviceType: humiditySensor, cluster: RelativeHumidityMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value) => { return Math.round(value * 100); } },
@@ -644,10 +654,10 @@ export const z2ms = [
644
654
  { type: '', name: '', property: 'battery', deviceType: powerSource, cluster: PowerSource.Cluster.id, attribute: 'batPercentRemaining', converter: (value) => { return Math.round(value * 2); } },
645
655
  { type: '', name: '', property: 'battery_low', deviceType: powerSource, cluster: PowerSource.Cluster.id, attribute: 'batChargeLevel', converter: (value) => { return value === true ? PowerSource.BatChargeLevel.Critical : PowerSource.BatChargeLevel.Ok; } },
646
656
  { type: '', name: '', property: 'battery_voltage', deviceType: powerSource, cluster: PowerSource.Cluster.id, attribute: 'batVoltage', converter: (value) => { return value; } },
647
- { type: '', name: 'energy', property: 'energy', deviceType: electricalSensor, cluster: ElectricalEnergyMeasurement.Cluster.id, attribute: 'cumulativeEnergyImported', converter: (value) => { return { energy: value * 1000000 }; } },
648
- { type: '', name: 'power', property: 'power', deviceType: electricalSensor, cluster: ElectricalPowerMeasurement.Cluster.id, attribute: 'activePower', converter: (value) => { return value * 1000; } },
649
- { type: '', name: 'voltage', property: 'voltage', deviceType: electricalSensor, cluster: ElectricalPowerMeasurement.Cluster.id, attribute: 'voltage', converter: (value) => { return value * 1000; } },
650
- { type: '', name: 'current', property: 'current', deviceType: electricalSensor, cluster: ElectricalPowerMeasurement.Cluster.id, attribute: 'activeCurrent', converter: (value) => { return value * 1000; } },
657
+ { type: '', name: 'energy', property: 'energy', deviceType: electricalSensor, cluster: ElectricalEnergyMeasurement.Cluster.id, attribute: 'cumulativeEnergyImported', converter: (value) => { return { energy: Math.round(value * 1000000) }; } },
658
+ { type: '', name: 'power', property: 'power', deviceType: electricalSensor, cluster: ElectricalPowerMeasurement.Cluster.id, attribute: 'activePower', converter: (value) => { return Math.round(value * 1000); } },
659
+ { type: '', name: 'voltage', property: 'voltage', deviceType: electricalSensor, cluster: ElectricalPowerMeasurement.Cluster.id, attribute: 'voltage', converter: (value) => { return Math.round(value * 1000); } },
660
+ { type: '', name: 'current', property: 'current', deviceType: electricalSensor, cluster: ElectricalPowerMeasurement.Cluster.id, attribute: 'activeCurrent', converter: (value) => { return Math.round(value * 1000); } },
651
661
  ];
652
662
  export class ZigbeeDevice extends ZigbeeEntity {
653
663
  constructor(platform, device) {
@@ -656,37 +666,42 @@ export class ZigbeeDevice extends ZigbeeEntity {
656
666
  static async create(platform, device) {
657
667
  const zigbeeDevice = new ZigbeeDevice(platform, device);
658
668
  zigbeeDevice.serial = `${device.ieee_address}`;
659
- if (zigbeeDevice.platform.postfixHostname) {
660
- zigbeeDevice.serial = `${zigbeeDevice.serial}_${hostname}`.slice(0, 32);
661
- }
662
- else if (zigbeeDevice.platform.postfix !== '') {
669
+ if (zigbeeDevice.platform.postfix !== '') {
663
670
  zigbeeDevice.serial = `${zigbeeDevice.serial}-${zigbeeDevice.platform.postfix}`.slice(0, 32);
664
671
  }
665
672
  if (device.friendly_name === 'Coordinator' || (device.model_id === 'ti.router' && device.manufacturer === 'TexasInstruments') || (device.model_id.startsWith('SLZB-') && device.manufacturer === 'SMLIGHT')) {
666
673
  zigbeeDevice.isRouter = true;
667
674
  platform.setSelectDevice(device.ieee_address, device.friendly_name, 'wifi');
668
- zigbeeDevice.bridgedDevice = new MatterbridgeEndpoint([doorLockDevice], { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
675
+ zigbeeDevice.bridgedDevice = new MatterbridgeEndpoint([doorLockDevice, bridgedNode, powerSource], { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
669
676
  zigbeeDevice.addBridgedDeviceBasicInformation();
670
677
  zigbeeDevice.addPowerSource();
671
678
  zigbeeDevice.bridgedDevice.addRequiredClusterServers();
672
679
  await zigbeeDevice.bridgedDevice.addFixedLabel('type', 'lock');
673
680
  zigbeeDevice.verifyMutableDevice(zigbeeDevice.bridgedDevice);
681
+ zigbeeDevice.bridgedDevice.addCommandHandler('identify', async (data) => {
682
+ zigbeeDevice.log.debug(`Command identify called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request identifyTime:${data.request.identifyTime} `);
683
+ });
674
684
  zigbeeDevice.bridgedDevice.addCommandHandler('lockDoor', async () => {
675
- zigbeeDevice.log.debug(`Command permit_join: false called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db}`);
685
+ zigbeeDevice.log.debug(`Command permit_join false called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db}`);
676
686
  await zigbeeDevice.bridgedDevice?.setAttribute(DoorLockCluster.id, 'lockState', DoorLock.LockState.Locked, zigbeeDevice.log);
677
- zigbeeDevice.publishCommand('permit_join: false', 'bridge/request/permit_join', { value: false });
687
+ zigbeeDevice.publishCommand('permit_join false', 'bridge/request/permit_join', { value: false });
678
688
  });
679
689
  zigbeeDevice.bridgedDevice.addCommandHandler('unlockDoor', async () => {
680
- zigbeeDevice.log.debug(`Command permit_join: true called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db}`);
690
+ zigbeeDevice.log.debug(`Command permit_join true called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db}`);
681
691
  await zigbeeDevice.bridgedDevice?.setAttribute(DoorLockCluster.id, 'lockState', DoorLock.LockState.Unlocked, zigbeeDevice.log);
682
- zigbeeDevice.publishCommand('permit_join: true', 'bridge/request/permit_join', { value: true });
692
+ zigbeeDevice.publishCommand('permit_join true', 'bridge/request/permit_join', { value: true });
683
693
  });
684
694
  return zigbeeDevice;
685
695
  }
686
- if (platform.config.deviceScenes === true) {
696
+ if (!platform.featureBlackList?.includes('scenes') && !platform.deviceFeatureBlackList[device.friendly_name]?.includes('scenes')) {
687
697
  Object.entries(device.endpoints).forEach(([key, endpoint]) => {
688
698
  Object.values(endpoint.scenes).forEach((scene) => {
689
699
  zigbeeDevice.log.debug(`***Device ${dn}${device.friendly_name}${rs}${db} endpoint ${CYAN}${key}${db} scene ${CYAN}${scene.name}${db} id ${CYAN}${scene.id}${db}`);
700
+ platform.setSelectDeviceEntity(device.ieee_address, 'scenes', 'Scenes', 'component');
701
+ platform.registerVirtualDevice(`${platform.config.scenesPrefix ? device.friendly_name + ' ' : ''}${scene.name}`, async () => {
702
+ zigbeeDevice.log.info(`Triggered scene "${scene.name}" id ${scene.id} from device ${device.friendly_name}`);
703
+ zigbeeDevice.publishCommand('scene_recall', device.friendly_name, { 'scene_recall': scene.id });
704
+ });
690
705
  });
691
706
  });
692
707
  }
@@ -908,6 +923,11 @@ export class ZigbeeDevice extends ZigbeeEntity {
908
923
  deviceTypesMap.delete(onOffLight.code);
909
924
  if (deviceTypesMap.has(dimmableLight.code) && deviceTypesMap.has(colorTemperatureLight.code))
910
925
  deviceTypesMap.delete(dimmableLight.code);
926
+ if (deviceTypesMap.has(dimmableLight.code) && deviceTypesMap.has(extendedColorLight.code))
927
+ deviceTypesMap.delete(dimmableLight.code);
928
+ if (deviceTypesMap.has(colorTemperatureLight.code) && deviceTypesMap.has(extendedColorLight.code))
929
+ deviceTypesMap.delete(colorTemperatureLight.code);
930
+ deviceTypesMap.delete(bridgedNode.code);
911
931
  deviceTypesMap.delete(powerSource.code);
912
932
  device.deviceTypes = Array.from(deviceTypesMap.values());
913
933
  }
@@ -943,12 +963,26 @@ export class ZigbeeDevice extends ZigbeeEntity {
943
963
  zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${zigbeeDevice.device?.friendly_name}${rs}${db} endpoint: ${ign}${endpoint === '' ? 'main' : endpoint}${rs}${db} => ` +
944
964
  `${nf}tagList: ${debugStringify(device.tagList)} deviceTypes: ${debugStringify(device.deviceTypes)} clusterServersIds: ${debugStringify(device.clusterServersIds)}`);
945
965
  }
966
+ if ((mainEndpoint.deviceTypes.find((dt) => dt.code === waterLeakDetector.code) || mainEndpoint.deviceTypes.find((dt) => dt.code === rainSensor.code)) && mainEndpoint.clusterServersIds.includes(BooleanState.Cluster.id)) {
967
+ zigbeeDevice.log.debug(`Configuring device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} BooleanStateCluster cluster with`);
968
+ zigbeeDevice.bridgedDevice.createDefaultBooleanStateClusterServer(false);
969
+ mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(BooleanState.Cluster.id), 1);
970
+ }
971
+ if (mainEndpoint.deviceTypes.find((dt) => dt.code === smokeCoAlarm.code) && mainEndpoint.clusterServersIds.includes(SmokeCoAlarm.Cluster.id)) {
972
+ zigbeeDevice.log.debug(`Configuring device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} SmokeCoAlarmCluster cluster with`);
973
+ zigbeeDevice.bridgedDevice.createSmokeOnlySmokeCOAlarmClusterServer(SmokeCoAlarm.AlarmState.Normal);
974
+ mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(SmokeCoAlarm.Cluster.id), 1);
975
+ }
946
976
  if (mainEndpoint.clusterServersIds.includes(ColorControl.Cluster.id)) {
947
- zigbeeDevice.log.debug(`Configuring device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} ColorControlCluster cluster with HS: ${names.includes('color_hs')} XY: ${names.includes('color_xy')} CT: ${names.includes('color_temp')}`);
948
977
  if (!names.includes('color_hs') && !names.includes('color_xy')) {
949
- zigbeeDevice.bridgedDevice.createCtColorControlClusterServer();
950
- mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(ColorControl.Cluster.id), 1);
978
+ zigbeeDevice.log.debug(`Configuring device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} ColorControlCluster cluster with CT: ${names.includes('color_temp')} min: ${zigbeeDevice.propertyMap.get('color_temp')?.value_min} max: ${zigbeeDevice.propertyMap.get('color_temp')?.value_max}`);
979
+ zigbeeDevice.bridgedDevice.createCtColorControlClusterServer(zigbeeDevice.propertyMap.get('color_temp')?.value_max, zigbeeDevice.propertyMap.get('color_temp')?.value_min, zigbeeDevice.propertyMap.get('color_temp')?.value_max);
951
980
  }
981
+ else {
982
+ zigbeeDevice.log.debug(`Configuring device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} ColorControlCluster cluster with HS: ${names.includes('color_hs')} XY: ${names.includes('color_xy')} CT: ${names.includes('color_temp')} min: ${zigbeeDevice.propertyMap.get('color_temp')?.value_min} max: ${zigbeeDevice.propertyMap.get('color_temp')?.value_max}`);
983
+ zigbeeDevice.bridgedDevice.createDefaultColorControlClusterServer(undefined, undefined, undefined, undefined, zigbeeDevice.propertyMap.get('color_temp')?.value_max, zigbeeDevice.propertyMap.get('color_temp')?.value_min, zigbeeDevice.propertyMap.get('color_temp')?.value_max);
984
+ }
985
+ mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(ColorControl.Cluster.id), 1);
952
986
  }
953
987
  if (mainEndpoint.clusterServersIds.includes(Thermostat.Cluster.id)) {
954
988
  const system_mode = zigbeeDevice.propertyMap.get('system_mode');
@@ -991,25 +1025,25 @@ export class ZigbeeDevice extends ZigbeeEntity {
991
1025
  zigbeeDevice.mutableDevice.clear();
992
1026
  zigbeeDevice.logPropertyMap();
993
1027
  zigbeeDevice.bridgedDevice.addCommandHandler('identify', async (data) => {
994
- zigbeeDevice.log.debug(`Command identify called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number} request identifyTime:${data.request.identifyTime} `);
1028
+ zigbeeDevice.log.debug(`Command identify called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber} request identifyTime:${data.request.identifyTime} `);
995
1029
  });
996
1030
  if (zigbeeDevice.bridgedDevice.hasClusterServer(OnOffCluster.id) || zigbeeDevice.hasEndpoints) {
997
1031
  for (const child of zigbeeDevice.bridgedDevice.getChildEndpoints()) {
998
1032
  if (child.hasClusterServer(OnOffCluster)) {
999
1033
  child.addCommandHandler('on', async (data) => {
1000
- zigbeeDevice.log.debug(`Command on called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number}`);
1034
+ zigbeeDevice.log.debug(`Command on called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
1001
1035
  const payload = {};
1002
1036
  payload['state_' + data.endpoint.uniqueStorageKey] = 'ON';
1003
1037
  zigbeeDevice.publishCommand('on', device.friendly_name, payload);
1004
1038
  });
1005
1039
  child.addCommandHandler('off', async (data) => {
1006
- zigbeeDevice.log.debug(`Command off called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number}`);
1040
+ zigbeeDevice.log.debug(`Command off called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
1007
1041
  const payload = {};
1008
1042
  payload['state_' + data.endpoint.uniqueStorageKey] = 'OFF';
1009
1043
  zigbeeDevice.publishCommand('off', device.friendly_name, payload);
1010
1044
  });
1011
1045
  child.addCommandHandler('toggle', async (data) => {
1012
- zigbeeDevice.log.debug(`Command toggle called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number}`);
1046
+ zigbeeDevice.log.debug(`Command toggle called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
1013
1047
  const payload = {};
1014
1048
  payload['state_' + data.endpoint.uniqueStorageKey] = 'TOGGLE';
1015
1049
  zigbeeDevice.publishCommand('toggle', device.friendly_name, payload);
@@ -1017,41 +1051,23 @@ export class ZigbeeDevice extends ZigbeeEntity {
1017
1051
  }
1018
1052
  }
1019
1053
  zigbeeDevice.bridgedDevice.addCommandHandler('on', async (data) => {
1020
- zigbeeDevice.log.debug(`Command on called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number}`);
1021
- const payload = {};
1022
- const label = zigbeeDevice.platform.matterbridge.edge ? undefined : data.endpoint.uniqueStorageKey;
1023
- if (label === undefined)
1024
- payload['state'] = 'ON';
1025
- else
1026
- payload['state_' + label] = 'ON';
1027
- zigbeeDevice.publishCommand('on', device.friendly_name, payload);
1054
+ zigbeeDevice.log.debug(`Command on called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
1055
+ zigbeeDevice.publishCommand('on', device.friendly_name, { state: 'ON' });
1028
1056
  });
1029
1057
  zigbeeDevice.bridgedDevice.addCommandHandler('off', async (data) => {
1030
- zigbeeDevice.log.debug(`Command off called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number}`);
1031
- const payload = {};
1032
- const label = zigbeeDevice.platform.matterbridge.edge ? undefined : data.endpoint.uniqueStorageKey;
1033
- if (label === undefined)
1034
- payload['state'] = 'OFF';
1035
- else
1036
- payload['state_' + label] = 'OFF';
1037
- zigbeeDevice.publishCommand('off', device.friendly_name, payload);
1058
+ zigbeeDevice.log.debug(`Command off called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
1059
+ zigbeeDevice.publishCommand('off', device.friendly_name, { state: 'OFF' });
1038
1060
  });
1039
1061
  zigbeeDevice.bridgedDevice.addCommandHandler('toggle', async (data) => {
1040
- zigbeeDevice.log.debug(`Command toggle called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number}`);
1041
- const payload = {};
1042
- const label = zigbeeDevice.platform.matterbridge.edge ? undefined : data.endpoint.uniqueStorageKey;
1043
- if (label === undefined)
1044
- payload['state'] = 'TOGGLE';
1045
- else
1046
- payload['state_' + label] = 'TOGGLE';
1047
- zigbeeDevice.publishCommand('toggle', device.friendly_name, payload);
1062
+ zigbeeDevice.log.debug(`Command toggle called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
1063
+ zigbeeDevice.publishCommand('toggle', device.friendly_name, { state: 'TOGGLE' });
1048
1064
  });
1049
1065
  }
1050
1066
  if (zigbeeDevice.bridgedDevice.hasClusterServer(LevelControlCluster.id) || zigbeeDevice.hasEndpoints) {
1051
1067
  for (const child of zigbeeDevice.bridgedDevice.getChildEndpoints()) {
1052
1068
  if (child.hasClusterServer(LevelControlCluster)) {
1053
1069
  child.addCommandHandler('moveToLevel', async (data) => {
1054
- zigbeeDevice.log.debug(`Command moveToLevel called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number} request: ${data.request.level} transition: ${data.request.transitionTime}`);
1070
+ 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}`);
1055
1071
  const payload = {};
1056
1072
  payload['brightness_' + data.endpoint.uniqueStorageKey] = data.request.level;
1057
1073
  if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
@@ -1059,7 +1075,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
1059
1075
  zigbeeDevice.publishCommand('moveToLevel', device.friendly_name, payload);
1060
1076
  });
1061
1077
  child.addCommandHandler('moveToLevelWithOnOff', async (data) => {
1062
- zigbeeDevice.log.debug(`Command moveToLevelWithOnOff called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number} request: ${data.request.level} transition: ${data.request.transitionTime}`);
1078
+ 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}`);
1063
1079
  const payload = {};
1064
1080
  payload['brightness_' + data.endpoint.uniqueStorageKey] = data.request.level;
1065
1081
  if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
@@ -1069,25 +1085,15 @@ export class ZigbeeDevice extends ZigbeeEntity {
1069
1085
  }
1070
1086
  }
1071
1087
  zigbeeDevice.bridgedDevice.addCommandHandler('moveToLevel', async (data) => {
1072
- zigbeeDevice.log.debug(`Command moveToLevel called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number} request: ${data.request.level} transition: ${data.request.transitionTime}`);
1073
- const payload = {};
1074
- const label = zigbeeDevice.platform.matterbridge.edge ? undefined : data.endpoint.uniqueStorageKey;
1075
- if (label === undefined)
1076
- payload['brightness'] = data.request.level;
1077
- else
1078
- payload['brightness_' + label] = data.request.level;
1088
+ 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}`);
1089
+ const payload = { brightness: data.request.level };
1079
1090
  if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1080
1091
  payload['transition'] = Math.round(data.request.transitionTime / 10);
1081
1092
  zigbeeDevice.publishCommand('moveToLevel', device.friendly_name, payload);
1082
1093
  });
1083
1094
  zigbeeDevice.bridgedDevice.addCommandHandler('moveToLevelWithOnOff', async (data) => {
1084
- zigbeeDevice.log.debug(`Command moveToLevelWithOnOff called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number} request: ${data.request.level} transition: ${data.request.transitionTime}`);
1085
- const payload = {};
1086
- const label = zigbeeDevice.platform.matterbridge.edge ? undefined : data.endpoint.uniqueStorageKey;
1087
- if (label === undefined)
1088
- payload['brightness'] = data.request.level;
1089
- else
1090
- payload['brightness_' + label] = data.request.level;
1095
+ 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}`);
1096
+ const payload = { brightness: data.request.level };
1091
1097
  if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
1092
1098
  payload['transition'] = Math.round(data.request.transitionTime / 10);
1093
1099
  zigbeeDevice.publishCommand('moveToLevelWithOnOff', device.friendly_name, payload);
@@ -1097,7 +1103,15 @@ export class ZigbeeDevice extends ZigbeeEntity {
1097
1103
  zigbeeDevice.bridgedDevice.addCommandHandler('moveToColorTemperature', async ({ request }) => {
1098
1104
  zigbeeDevice.log.debug(`Command moveToColorTemperature called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request: ${request.colorTemperatureMireds}`);
1099
1105
  await zigbeeDevice.bridgedDevice?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.ColorTemperatureMireds, zigbeeDevice.log);
1100
- const payload = { color_temp: request.colorTemperatureMireds };
1106
+ const payload = {};
1107
+ if (zigbeeDevice.propertyMap.get('color_temp')) {
1108
+ payload['color_temp'] = request.colorTemperatureMireds;
1109
+ }
1110
+ else {
1111
+ const rgb = kelvinToRGB(miredToKelvin(request.colorTemperatureMireds));
1112
+ payload['color'] = { r: rgb.r, g: rgb.g, b: rgb.b };
1113
+ 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'])}.`);
1114
+ }
1101
1115
  if (zigbeeDevice.transition && request.transitionTime && request.transitionTime / 10 >= 1)
1102
1116
  payload['transition'] = Math.round(request.transitionTime / 10);
1103
1117
  zigbeeDevice.publishCommand('moveToColorTemperature', device.friendly_name, payload);
@@ -1156,7 +1170,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
1156
1170
  zigbeeDevice.bridgedDevice.addCommandHandler('upOrOpen', async () => {
1157
1171
  zigbeeDevice.log.debug(`Command upOrOpen called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db}`);
1158
1172
  if (zigbeeDevice.isDevice && zigbeeDevice.propertyMap.has('position'))
1159
- zigbeeDevice.bridgedDevice?.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', 0, zigbeeDevice.log);
1173
+ await zigbeeDevice.bridgedDevice?.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', 0, zigbeeDevice.log);
1160
1174
  else
1161
1175
  await zigbeeDevice.bridgedDevice?.setWindowCoveringTargetAndCurrentPosition(0);
1162
1176
  zigbeeDevice.publishCommand('upOrOpen', device.friendly_name, { state: 'OPEN' });
@@ -1164,7 +1178,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
1164
1178
  zigbeeDevice.bridgedDevice.addCommandHandler('downOrClose', async () => {
1165
1179
  zigbeeDevice.log.debug(`Command downOrClose called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db}`);
1166
1180
  if (zigbeeDevice.isDevice && zigbeeDevice.propertyMap.has('position'))
1167
- zigbeeDevice.bridgedDevice?.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', 10000, zigbeeDevice.log);
1181
+ await zigbeeDevice.bridgedDevice?.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', 10000, zigbeeDevice.log);
1168
1182
  else
1169
1183
  await zigbeeDevice.bridgedDevice?.setWindowCoveringTargetAndCurrentPosition(10000);
1170
1184
  zigbeeDevice.publishCommand('downOrClose', device.friendly_name, { state: 'CLOSE' });
@@ -1177,7 +1191,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
1177
1191
  zigbeeDevice.bridgedDevice.addCommandHandler('goToLiftPercentage', async ({ request: { liftPercent100thsValue } }) => {
1178
1192
  zigbeeDevice.log.debug(`Command goToLiftPercentage called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request liftPercent100thsValue: ${liftPercent100thsValue}`);
1179
1193
  if (zigbeeDevice.isDevice && zigbeeDevice.propertyMap.has('position'))
1180
- zigbeeDevice.bridgedDevice?.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', liftPercent100thsValue, zigbeeDevice.log);
1194
+ await zigbeeDevice.bridgedDevice?.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', liftPercent100thsValue, zigbeeDevice.log);
1181
1195
  else
1182
1196
  await zigbeeDevice.bridgedDevice?.setWindowCoveringTargetAndCurrentPosition(liftPercent100thsValue);
1183
1197
  zigbeeDevice.publishCommand('goToLiftPercentage', device.friendly_name, { position: liftPercent100thsValue / 100 });
@@ -1202,7 +1216,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
1202
1216
  const t = zigbeeDevice.bridgedDevice?.getAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', zigbeeDevice.log);
1203
1217
  const setpoint = Math.round(t / 100 + request.amount / 10);
1204
1218
  if (zigbeeDevice.propertyMap.has('current_heating_setpoint')) {
1205
- zigbeeDevice.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { current_heating_setpoint: setpoint });
1219
+ zigbeeDevice.publishCommand('CurrentHeatingSetpoint', device.friendly_name, { current_heating_setpoint: setpoint });
1206
1220
  }
1207
1221
  else if (zigbeeDevice.propertyMap.has('occupied_heating_setpoint')) {
1208
1222
  zigbeeDevice.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { occupied_heating_setpoint: setpoint });
@@ -1212,7 +1226,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
1212
1226
  const t = zigbeeDevice.bridgedDevice?.getAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', zigbeeDevice.log);
1213
1227
  const setpoint = Math.round(t / 100 + request.amount / 10);
1214
1228
  if (zigbeeDevice.propertyMap.has('current_heating_setpoint')) {
1215
- zigbeeDevice.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { current_heating_setpoint: setpoint });
1229
+ zigbeeDevice.publishCommand('CurrentCoolingSetpoint', device.friendly_name, { current_heating_setpoint: setpoint });
1216
1230
  }
1217
1231
  else if (zigbeeDevice.propertyMap.has('occupied_cooling_setpoint')) {
1218
1232
  zigbeeDevice.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { occupied_cooling_setpoint: setpoint });
package/dist/platform.js CHANGED
@@ -1,4 +1,4 @@
1
- import { MatterbridgeDynamicPlatform } from 'matterbridge';
1
+ import { addVirtualDevice, MatterbridgeDynamicPlatform } from 'matterbridge';
2
2
  import { dn, gn, db, wr, zb, payloadStringify, rs, debugStringify, CYAN, er, nf } from 'matterbridge/logger';
3
3
  import { isValidNumber, isValidString, waiter } from 'matterbridge/utils';
4
4
  import { BridgedDeviceBasicInformation, DoorLock } from 'matterbridge/matter/clusters';
@@ -11,6 +11,7 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
11
11
  bridgedDevices = [];
12
12
  zigbeeEntities = [];
13
13
  injectTimer;
14
+ namePostfix = 1;
14
15
  mqttHost = 'localhost';
15
16
  mqttPort = 1883;
16
17
  mqttTopic = 'zigbee2mqtt';
@@ -23,7 +24,6 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
23
24
  featureBlackList = [];
24
25
  deviceFeatureBlackList = {};
25
26
  postfix = '';
26
- postfixHostname = true;
27
27
  debugEnabled;
28
28
  shouldStart;
29
29
  shouldConfigure;
@@ -38,8 +38,8 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
38
38
  availabilityTimer;
39
39
  constructor(matterbridge, log, config) {
40
40
  super(matterbridge, log, config);
41
- if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('2.2.5')) {
42
- throw new Error(`This plugin requires Matterbridge version >= "2.2.5". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend."`);
41
+ if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.0.3')) {
42
+ throw new Error(`This plugin requires Matterbridge version >= "3.0.3". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend."`);
43
43
  }
44
44
  this.debugEnabled = config.debug;
45
45
  this.shouldStart = false;
@@ -69,7 +69,6 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
69
69
  if (config.postfix)
70
70
  this.postfix = config.postfix;
71
71
  this.postfix = this.postfix.trim().slice(0, 3);
72
- this.postfixHostname = config.postfixHostname ?? true;
73
72
  config.host = this.mqttHost;
74
73
  config.port = this.mqttPort;
75
74
  config.protocolVersion = this.mqttProtocol;
@@ -77,7 +76,16 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
77
76
  config.username = this.mqttUsername;
78
77
  config.password = this.mqttPassword;
79
78
  config.postfix = this.postfix;
80
- config.postfixHostname = this.postfixHostname;
79
+ if (config.postfixHostname !== undefined)
80
+ delete config.postfixHostname;
81
+ if (config.deviceScenes !== undefined)
82
+ delete config.deviceScenes;
83
+ if (config.groupScenes !== undefined)
84
+ delete config.groupScenes;
85
+ if (config.scenesType === undefined)
86
+ config.scenesType = 'outlet';
87
+ if (config.scenesPrefix === undefined)
88
+ config.scenesPrefix = true;
81
89
  if (config.type === 'MatterbridgeExtension') {
82
90
  this.z2m = new Zigbee2MQTT(this.mqttHost, this.mqttPort, this.mqttTopic, this.mqttUsername, this.mqttPassword, this.mqttProtocol, this.debugEnabled);
83
91
  this.z2m.setLogDebug(this.debugEnabled);
@@ -86,7 +94,6 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
86
94
  }
87
95
  this.log.info(`Initializing platform: ${CYAN}${this.config.name}${nf} version: ${CYAN}${this.config.version}${rs}`);
88
96
  this.log.info(`Loaded zigbee2mqtt parameters from ${CYAN}${path.join(matterbridge.matterbridgeDirectory, 'matterbridge-zigbee2mqtt.config.json')}${rs}`);
89
- this.clearSelect();
90
97
  this.z2m = new Zigbee2MQTT(this.mqttHost, this.mqttPort, this.mqttTopic, this.mqttUsername, this.mqttPassword, this.mqttProtocol, this.debugEnabled);
91
98
  this.z2m.setLogDebug(this.debugEnabled);
92
99
  this.z2m.setDataPath(path.join(matterbridge.matterbridgePluginDirectory, 'matterbridge-zigbee2mqtt'));
@@ -292,6 +299,9 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
292
299
  }
293
300
  async onStart(reason) {
294
301
  this.log.info(`Starting zigbee2mqtt dynamic platform v${this.version}: ` + reason);
302
+ await this.ready;
303
+ await this.clearSelect();
304
+ this.setSelectEntity('scenes', 'Scenes', 'component');
295
305
  const hasOnline = await waiter('z2mBridgeOnline', () => this.z2mBridgeOnline !== undefined);
296
306
  const hasInfo = await waiter('z2mBridgeInfo', () => this.z2mBridgeInfo !== undefined);
297
307
  const hasDevices = await waiter('z2mBridgeDevices & z2mBridgeGroups', () => this.z2mBridgeDevices !== undefined || this.z2mBridgeGroups !== undefined);
@@ -333,12 +343,12 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
333
343
  this.log.info(`Configuring router ${bridgedEntity.bridgedDevice?.deviceName}.`);
334
344
  if (this.z2mBridgeInfo?.permit_join) {
335
345
  bridgedEntity.bridgedDevice?.setAttribute(DoorLock.Cluster.id, 'lockState', DoorLock.LockState.Unlocked, this.log);
336
- if (bridgedEntity.bridgedDevice.number)
346
+ if (bridgedEntity.bridgedDevice.maybeNumber)
337
347
  bridgedEntity.bridgedDevice?.triggerEvent(DoorLock.Cluster.id, 'lockOperation', { lockOperationType: DoorLock.LockOperationType.Unlock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }, this.log);
338
348
  }
339
349
  else {
340
350
  bridgedEntity.bridgedDevice?.setAttribute(DoorLock.Cluster.id, 'lockState', DoorLock.LockState.Locked, this.log);
341
- if (bridgedEntity.bridgedDevice.number)
351
+ if (bridgedEntity.bridgedDevice.maybeNumber)
342
352
  bridgedEntity.bridgedDevice?.triggerEvent(DoorLock.Cluster.id, 'lockOperation', { lockOperationType: DoorLock.LockOperationType.Lock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }, this.log);
343
353
  }
344
354
  }
@@ -481,6 +491,22 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
481
491
  }
482
492
  return matterGroup;
483
493
  }
494
+ registerVirtualDevice(name, callback) {
495
+ let aggregator;
496
+ if (this.matterbridge.bridgeMode === 'bridge') {
497
+ aggregator = this.matterbridge.aggregatorNode;
498
+ }
499
+ else if (this.matterbridge.bridgeMode === 'childbridge') {
500
+ aggregator = this.matterbridge.plugins.get(this.name)?.aggregatorNode;
501
+ }
502
+ if (aggregator) {
503
+ if (aggregator.parts.has(name.replaceAll(' ', '') + ':' + this.config.scenesType)) {
504
+ this.log.warn(`Scene name ${name} already registered. Please use a different name. Changed to ${name + ' ' + this.namePostfix}`);
505
+ name = name + ' ' + this.namePostfix++;
506
+ }
507
+ addVirtualDevice(aggregator, name.slice(0, 32), this.config.scenesType, callback);
508
+ }
509
+ }
484
510
  async unregisterZigbeeEntity(friendly_name) {
485
511
  const entity = this.zigbeeEntities.find((entity) => entity.entityName === friendly_name);
486
512
  if (entity) {
@@ -119,28 +119,39 @@
119
119
  "selectDeviceEntityFrom": "name"
120
120
  }
121
121
  },
122
- "deviceScenes": {
123
- "description": "Enable the devices scenes",
124
- "type": "boolean",
125
- "default": false,
126
- "ui:widget": "hidden"
127
- },
128
- "groupScenes": {
129
- "description": "Enable the groups scenes",
122
+ "scenesType": {
123
+ "description": "Device type to use to expose scenes",
124
+ "type": "string",
125
+ "oneOf": [
126
+ {
127
+ "title": "Light",
128
+ "enum": ["light"]
129
+ },
130
+ {
131
+ "title": "Outlet",
132
+ "enum": ["outlet"]
133
+ },
134
+ {
135
+ "title": "Switch",
136
+ "enum": ["switch"]
137
+ },
138
+ {
139
+ "title": "Mounted Switch",
140
+ "enum": ["mounted_switch"]
141
+ }
142
+ ],
143
+ "default": "outlet"
144
+ },
145
+ "scenesPrefix": {
146
+ "description": "Add the device/group friendly name before the scene name.",
130
147
  "type": "boolean",
131
- "default": false,
132
- "ui:widget": "hidden"
148
+ "default": true
133
149
  },
134
150
  "postfix": {
135
151
  "description": "Add this unique postfix (3 characters max) to each device serial to avoid collision with other instances (you may loose the configuration of the devices in your controller when changing this value).",
136
152
  "type": "string",
137
153
  "default": ""
138
154
  },
139
- "postfixHostname": {
140
- "description": "DEPRECATED: unique postfix added to each device identifier to avoid collision with other instances (you may loose the configuration of the devices in your controller when changing this value). Use postfix instead.",
141
- "type": "boolean",
142
- "default": true
143
- },
144
155
  "debug": {
145
156
  "description": "Enable the debug for the plugin (development only)",
146
157
  "type": "boolean",
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "matterbridge-zigbee2mqtt",
3
- "version": "2.4.7",
3
+ "version": "2.5.0-dev.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-zigbee2mqtt",
9
- "version": "2.4.7",
9
+ "version": "2.5.0-dev.2",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "moment": "2.30.1",
13
- "mqtt": "5.10.4",
13
+ "mqtt": "5.13.0",
14
14
  "node-ansi-logger": "3.0.1",
15
15
  "node-persist-manager": "1.0.8"
16
16
  },
@@ -23,40 +23,27 @@
23
23
  }
24
24
  },
25
25
  "node_modules/@babel/runtime": {
26
- "version": "7.26.10",
27
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz",
28
- "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==",
26
+ "version": "7.27.1",
27
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz",
28
+ "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
29
29
  "license": "MIT",
30
- "dependencies": {
31
- "regenerator-runtime": "^0.14.0"
32
- },
33
30
  "engines": {
34
31
  "node": ">=6.9.0"
35
32
  }
36
33
  },
37
34
  "node_modules/@types/node": {
38
- "version": "22.13.10",
39
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
40
- "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
35
+ "version": "22.15.21",
36
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
37
+ "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
41
38
  "license": "MIT",
42
39
  "dependencies": {
43
- "undici-types": "~6.20.0"
40
+ "undici-types": "~6.21.0"
44
41
  }
45
42
  },
46
43
  "node_modules/@types/readable-stream": {
47
- "version": "4.0.18",
48
- "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.18.tgz",
49
- "integrity": "sha512-21jK/1j+Wg+7jVw1xnSwy/2Q1VgVjWuFssbYGTREPUBeZ+rqVFl2udq0IkxzPC0ZhOzVceUbyIACFZKLqKEBlA==",
50
- "license": "MIT",
51
- "dependencies": {
52
- "@types/node": "*",
53
- "safe-buffer": "~5.1.1"
54
- }
55
- },
56
- "node_modules/@types/ws": {
57
- "version": "8.18.0",
58
- "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.0.tgz",
59
- "integrity": "sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==",
44
+ "version": "4.0.19",
45
+ "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.19.tgz",
46
+ "integrity": "sha512-6Tgd3lMocKwOul/kwAAgSebkhdMCLhRvcJ6CKHA6wdql2qNIwK6hw3Y4PZQxn9HcJogoC/1ZOmkFM7OZKH/VrA==",
60
47
  "license": "MIT",
61
48
  "dependencies": {
62
49
  "@types/node": "*"
@@ -172,9 +159,9 @@
172
159
  }
173
160
  },
174
161
  "node_modules/debug": {
175
- "version": "4.4.0",
176
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
177
- "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
162
+ "version": "4.4.1",
163
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
164
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
178
165
  "license": "MIT",
179
166
  "dependencies": {
180
167
  "ms": "^2.1.3"
@@ -251,6 +238,19 @@
251
238
  "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
252
239
  "license": "ISC"
253
240
  },
241
+ "node_modules/ip-address": {
242
+ "version": "9.0.5",
243
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
244
+ "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
245
+ "license": "MIT",
246
+ "dependencies": {
247
+ "jsbn": "1.1.0",
248
+ "sprintf-js": "^1.1.3"
249
+ },
250
+ "engines": {
251
+ "node": ">= 12"
252
+ }
253
+ },
254
254
  "node_modules/js-sdsl": {
255
255
  "version": "4.3.0",
256
256
  "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz",
@@ -261,6 +261,12 @@
261
261
  "url": "https://opencollective.com/js-sdsl"
262
262
  }
263
263
  },
264
+ "node_modules/jsbn": {
265
+ "version": "1.1.0",
266
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
267
+ "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
268
+ "license": "MIT"
269
+ },
264
270
  "node_modules/minimist": {
265
271
  "version": "1.2.8",
266
272
  "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
@@ -280,24 +286,22 @@
280
286
  }
281
287
  },
282
288
  "node_modules/mqtt": {
283
- "version": "5.10.4",
284
- "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.4.tgz",
285
- "integrity": "sha512-wN+SuhT2/ZaG6NPxca0N6YtRivnMxk6VflxQUEeqDH4erKdj+wPAGhHmcTLzvqfE4sJRxrEJ+XJxUc0No0E7eQ==",
289
+ "version": "5.13.0",
290
+ "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.13.0.tgz",
291
+ "integrity": "sha512-pR+z+ChxFl3n8AKLQbTONVOOg/jl4KiKQRBAi78tjd6PksOWvl1nl9L8ZHOZ3MiavZfrUOjok2ddwc1VymGWRg==",
286
292
  "license": "MIT",
287
293
  "dependencies": {
288
- "@types/readable-stream": "^4.0.18",
289
- "@types/ws": "^8.5.14",
290
294
  "commist": "^3.2.0",
291
295
  "concat-stream": "^2.0.0",
292
296
  "debug": "^4.4.0",
293
297
  "help-me": "^5.0.0",
294
298
  "lru-cache": "^10.4.3",
295
299
  "minimist": "^1.2.8",
296
- "mqtt-packet": "^9.0.1",
300
+ "mqtt-packet": "^9.0.2",
297
301
  "number-allocator": "^1.0.14",
298
302
  "readable-stream": "^4.7.0",
299
- "reinterval": "^1.1.0",
300
303
  "rfdc": "^1.4.1",
304
+ "socks": "^2.8.3",
301
305
  "split2": "^4.2.0",
302
306
  "worker-timers": "^7.1.8",
303
307
  "ws": "^8.18.0"
@@ -431,18 +435,6 @@
431
435
  "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
432
436
  }
433
437
  },
434
- "node_modules/regenerator-runtime": {
435
- "version": "0.14.1",
436
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
437
- "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
438
- "license": "MIT"
439
- },
440
- "node_modules/reinterval": {
441
- "version": "1.1.0",
442
- "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz",
443
- "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==",
444
- "license": "MIT"
445
- },
446
438
  "node_modules/rfdc": {
447
439
  "version": "1.4.1",
448
440
  "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
@@ -450,11 +442,49 @@
450
442
  "license": "MIT"
451
443
  },
452
444
  "node_modules/safe-buffer": {
453
- "version": "5.1.2",
454
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
455
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
445
+ "version": "5.2.1",
446
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
447
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
448
+ "funding": [
449
+ {
450
+ "type": "github",
451
+ "url": "https://github.com/sponsors/feross"
452
+ },
453
+ {
454
+ "type": "patreon",
455
+ "url": "https://www.patreon.com/feross"
456
+ },
457
+ {
458
+ "type": "consulting",
459
+ "url": "https://feross.org/support"
460
+ }
461
+ ],
456
462
  "license": "MIT"
457
463
  },
464
+ "node_modules/smart-buffer": {
465
+ "version": "4.2.0",
466
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
467
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
468
+ "license": "MIT",
469
+ "engines": {
470
+ "node": ">= 6.0.0",
471
+ "npm": ">= 3.0.0"
472
+ }
473
+ },
474
+ "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==",
478
+ "license": "MIT",
479
+ "dependencies": {
480
+ "ip-address": "^9.0.5",
481
+ "smart-buffer": "^4.2.0"
482
+ },
483
+ "engines": {
484
+ "node": ">= 10.0.0",
485
+ "npm": ">= 3.0.0"
486
+ }
487
+ },
458
488
  "node_modules/split2": {
459
489
  "version": "4.2.0",
460
490
  "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
@@ -464,6 +494,12 @@
464
494
  "node": ">= 10.x"
465
495
  }
466
496
  },
497
+ "node_modules/sprintf-js": {
498
+ "version": "1.1.3",
499
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
500
+ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
501
+ "license": "BSD-3-Clause"
502
+ },
467
503
  "node_modules/string_decoder": {
468
504
  "version": "1.3.0",
469
505
  "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@@ -473,26 +509,6 @@
473
509
  "safe-buffer": "~5.2.0"
474
510
  }
475
511
  },
476
- "node_modules/string_decoder/node_modules/safe-buffer": {
477
- "version": "5.2.1",
478
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
479
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
480
- "funding": [
481
- {
482
- "type": "github",
483
- "url": "https://github.com/sponsors/feross"
484
- },
485
- {
486
- "type": "patreon",
487
- "url": "https://www.patreon.com/feross"
488
- },
489
- {
490
- "type": "consulting",
491
- "url": "https://feross.org/support"
492
- }
493
- ],
494
- "license": "MIT"
495
- },
496
512
  "node_modules/tslib": {
497
513
  "version": "2.8.1",
498
514
  "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -506,9 +522,9 @@
506
522
  "license": "MIT"
507
523
  },
508
524
  "node_modules/undici-types": {
509
- "version": "6.20.0",
510
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
511
- "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
525
+ "version": "6.21.0",
526
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
527
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
512
528
  "license": "MIT"
513
529
  },
514
530
  "node_modules/util-deprecate": {
@@ -552,9 +568,9 @@
552
568
  }
553
569
  },
554
570
  "node_modules/ws": {
555
- "version": "8.18.1",
556
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
557
- "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
571
+ "version": "8.18.2",
572
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
573
+ "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
558
574
  "license": "MIT",
559
575
  "engines": {
560
576
  "node": ">=10.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-zigbee2mqtt",
3
- "version": "2.4.7",
3
+ "version": "2.5.0-dev.2",
4
4
  "description": "Matterbridge zigbee2mqtt plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "moment": "2.30.1",
45
- "mqtt": "5.10.4",
45
+ "mqtt": "5.13.0",
46
46
  "node-ansi-logger": "3.0.1",
47
47
  "node-persist-manager": "1.0.8"
48
48
  }
@@ -0,0 +1,8 @@
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
+ }