matterbridge-zigbee2mqtt 3.1.0 → 3.2.0-dev-20260705-147850d

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
@@ -9,9 +9,11 @@
9
9
  ![Node.js CI](https://github.com/Luligu/matterbridge-zigbee2mqtt/actions/workflows/build.yml/badge.svg)
10
10
  ![CodeQL](https://github.com/Luligu/matterbridge-zigbee2mqtt/actions/workflows/codeql.yml/badge.svg)
11
11
  [![codecov](https://codecov.io/gh/Luligu/matterbridge-zigbee2mqtt/branch/main/graph/badge.svg)](https://codecov.io/gh/Luligu/matterbridge-zigbee2mqtt)
12
- [![styled with prettier](https://img.shields.io/badge/styled_with-Prettier-f8bc45.svg?logo=prettier)](https://prettier.io/)
13
- [![linted with eslint](https://img.shields.io/badge/linted_with-ES_Lint-4B32C3.svg?logo=eslint)](https://eslint.org/)
12
+ [![tested with Vitest](https://img.shields.io/badge/tested_with-Vitest-6E9F18.svg?logo=vitest&logoColor=white)](https://vitest.dev)
13
+ [![styled with Oxc](https://img.shields.io/badge/styled_with-Oxc-9BE4E0.svg?logo=oxc&logoColor=white)](https://oxc.rs/docs/guide/usage/formatter.html)
14
+ [![linted with Oxc](https://img.shields.io/badge/linted_with-Oxc-9BE4E0.svg?logo=oxc&logoColor=white)](https://oxc.rs/docs/guide/usage/linter.html)
14
15
  [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
16
+ [![TypeScript Native](https://img.shields.io/badge/TypeScript_Native-3178C6?logo=typescript&logoColor=white)](https://github.com/microsoft/typescript-go)
15
17
  [![ESM](https://img.shields.io/badge/ESM-Node.js-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
16
18
  [![matterbridge.io](https://img.shields.io/badge/matterbridge.io-online-brightgreen)](https://matterbridge.io)
17
19
 
@@ -28,6 +30,24 @@ If you like this project and find it useful, please consider giving it a star on
28
30
 
29
31
  <a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/assets/bmc-button.svg" alt="Buy me a coffee" width="120"></a>
30
32
 
33
+ ## [3.2.0] - Dev branch
34
+
35
+ ### Breaking changes
36
+
37
+ - [matterbridge]: Require matterbridge v.3.9.0.
38
+
39
+ ### Changed
40
+
41
+ - [toolchain]: Migrate to the native toolchain (tsgo + oxlint + oxfmt + vitest). Replace ESLint/Prettier/Jest with oxlint/oxfmt and Vitest.
42
+ - [vitest]: Move the tests from `src` to `vitest` and migrate them from Jest and `matterbridge/jestutils` to Vitest and `matterbridge/vitest-utils`.
43
+ - [vitest]: Update the test assertions for matterbridge v.3.9.x: endpoint names now use the device type name (i.e. `OnOffLightSwitch` instead of `MA-onoffswitch`) and `onOffLightSwitch` includes the `Binding` cluster.
44
+ - [package]: Update dependencies.
45
+ - [package]: Apply style.
46
+ - [package]: Bump `node-ansi-logger` to v.3.3.0.
47
+ - [package]: Bump `node-persist-manager` to v.2.1.0.
48
+
49
+ <a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/assets/bmc-button.svg" alt="Buy me a coffee" width="80"></a>
50
+
31
51
  ## [3.1.0] - 2026-06-07
32
52
 
33
53
  ### Breaking changes
package/README.md CHANGED
@@ -9,9 +9,11 @@
9
9
  ![Node.js CI](https://github.com/Luligu/matterbridge-zigbee2mqtt/actions/workflows/build.yml/badge.svg)
10
10
  ![CodeQL](https://github.com/Luligu/matterbridge-zigbee2mqtt/actions/workflows/codeql.yml/badge.svg)
11
11
  [![codecov](https://codecov.io/gh/Luligu/matterbridge-zigbee2mqtt/branch/main/graph/badge.svg)](https://codecov.io/gh/Luligu/matterbridge-zigbee2mqtt)
12
- [![styled with prettier](https://img.shields.io/badge/styled_with-Prettier-f8bc45.svg?logo=prettier)](https://prettier.io/)
13
- [![linted with eslint](https://img.shields.io/badge/linted_with-ES_Lint-4B32C3.svg?logo=eslint)](https://eslint.org/)
12
+ [![tested with Vitest](https://img.shields.io/badge/tested_with-Vitest-6E9F18.svg?logo=vitest&logoColor=white)](https://vitest.dev)
13
+ [![styled with Oxc](https://img.shields.io/badge/styled_with-Oxc-9BE4E0.svg?logo=oxc&logoColor=white)](https://oxc.rs/docs/guide/usage/formatter.html)
14
+ [![linted with Oxc](https://img.shields.io/badge/linted_with-Oxc-9BE4E0.svg?logo=oxc&logoColor=white)](https://oxc.rs/docs/guide/usage/linter.html)
14
15
  [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
16
+ [![TypeScript Native](https://img.shields.io/badge/TypeScript_Native-3178C6?logo=typescript&logoColor=white)](https://github.com/microsoft/typescript-go)
15
17
  [![ESM](https://img.shields.io/badge/ESM-Node.js-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
16
18
  [![matterbridge.io](https://img.shields.io/badge/matterbridge.io-online-brightgreen)](https://matterbridge.io)
17
19
 
package/dist/entity.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import EventEmitter from 'node:events';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
- import { airQualitySensor, bridgedNode, colorTemperatureLight, colorTemperatureSwitch, contactSensor, coverDevice, dimmableLight, dimmableOutlet, dimmableSwitch, doorLockDevice, electricalSensor, extendedColorLight, genericSwitch, humiditySensor, lightSensor, MatterbridgeEndpoint, occupancySensor, onOffLight, onOffOutlet, onOffSwitch, powerSource, pressureSensor, rainSensor, smokeCoAlarm, temperatureSensor, thermostatDevice, waterLeakDetector, } from 'matterbridge';
4
+ import { airQualitySensor, bridgedNode, colorDimmerSwitch, colorTemperatureLight, contactSensor, dimmableLight, dimmablePlugInUnit, dimmerSwitch, doorLock, electricalSensor, extendedColorLight, genericSwitch, humiditySensor, lightSensor, MatterbridgeEndpoint, occupancySensor, onOffLight, onOffLightSwitch, onOffPlugInUnit, powerSource, pressureSensor, rainSensor, smokeCoAlarm, temperatureSensor, thermostat, waterLeakDetector, windowCovering, } from 'matterbridge';
5
5
  import { AnsiLogger, CYAN, db, debugStringify, dn, gn, hk, idn, ign, nf, or, rs, YELLOW, zb } from 'matterbridge/logger';
6
6
  import { NumberTag, SwitchesTag } from 'matterbridge/matter';
7
7
  import { AirQuality, BooleanState, BridgedDeviceBasicInformation, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, ColorControl, DoorLock, ElectricalEnergyMeasurement, ElectricalPowerMeasurement, FormaldehydeConcentrationMeasurement, Identify, IlluminanceMeasurement, LevelControl, OccupancySensing, OnOff, Pm1ConcentrationMeasurement, Pm10ConcentrationMeasurement, Pm25ConcentrationMeasurement, PowerSource, PressureMeasurement, RelativeHumidityMeasurement, SmokeCoAlarm, TemperatureMeasurement, Thermostat, TotalVolatileOrganicCompoundsConcentrationMeasurement, WindowCovering, } from 'matterbridge/matter/clusters';
@@ -20,7 +20,7 @@ export class ZigbeeEntity extends EventEmitter {
20
20
  en = '';
21
21
  ien = '';
22
22
  bridgedDevice;
23
- eidn = `${or}`;
23
+ eidn = or;
24
24
  lastPayload = {};
25
25
  lastSeen = 0;
26
26
  ignoreFeatures = [];
@@ -42,14 +42,14 @@ export class ZigbeeEntity extends EventEmitter {
42
42
  constructor(platform, entity) {
43
43
  super();
44
44
  this.platform = platform;
45
- if (entity.ieee_address !== undefined) {
45
+ if (this.isValidDevice(entity)) {
46
46
  this.device = entity;
47
47
  this.entityName = entity.friendly_name;
48
48
  this.isDevice = true;
49
49
  this.en = dn;
50
50
  this.ien = idn;
51
51
  }
52
- if (entity.id !== undefined) {
52
+ if (this.isValidGroup(entity)) {
53
53
  this.group = entity;
54
54
  this.entityName = entity.friendly_name;
55
55
  this.isGroup = true;
@@ -122,8 +122,7 @@ export class ZigbeeEntity extends EventEmitter {
122
122
  `endpoint: ${CYAN}${propertyMap.endpoint === '' ? 'main' : propertyMap.endpoint}${db}`);
123
123
  let z2m;
124
124
  z2m = z2ms.find((z2m) => z2m.type === propertyMap?.type && z2m.property === propertyMap?.name);
125
- if (!z2m)
126
- z2m = z2ms.find((z2m) => z2m.property === propertyMap?.name);
125
+ z2m ??= z2ms.find((z2m) => z2m.property === propertyMap?.name);
127
126
  if (z2m) {
128
127
  if (z2m.valueLookup && propertyMap.values && propertyMap.values !== '' && typeof value === 'string' && !propertyMap.values.includes(value)) {
129
128
  this.log.debug(`*Payload entry ${CYAN}${key}${db} value ${CYAN}${value}${db} not found in propertyMap values ${CYAN}${propertyMap.values}${db}`);
@@ -139,15 +138,15 @@ export class ZigbeeEntity extends EventEmitter {
139
138
  }
140
139
  else
141
140
  this.log.debug(`*Payload entry ${CYAN}${key}${db} not found in propertyMap`);
142
- if (key === 'action' && value !== '') {
143
- const propertyMap = this.propertyMap.get(('action_' + value));
141
+ if (key === 'action' && typeof value === 'string' && value !== '') {
142
+ const propertyMap = this.propertyMap.get('action_' + value);
144
143
  if (propertyMap) {
145
144
  const child = this.bridgedDevice.getChildEndpointById(propertyMap.endpoint);
146
- if (child && child.maybeNumber)
145
+ if (child?.maybeNumber)
147
146
  fireAndForget(child.triggerSwitchEvent(propertyMap.action, this.log), this.log, `Error triggering switch event ${propertyMap.action} on ${propertyMap.endpoint}`);
148
147
  }
149
148
  else
150
- this.log.debug(`*Payload entry ${CYAN}${('action_' + value)}${db} not found in propertyMap`);
149
+ this.log.debug(`*Payload entry ${CYAN}${'action_' + value}${db} not found in propertyMap`);
151
150
  }
152
151
  if (key === 'position' && this.isDevice && isValidNumber(value, 0, 100)) {
153
152
  this.updateAttributeIfChanged(this.bridgedDevice, undefined, WindowCovering.id, 'currentPositionLiftPercent100ths', value * 100);
@@ -257,6 +256,12 @@ export class ZigbeeEntity extends EventEmitter {
257
256
  this.mutableDevice.clear();
258
257
  this.propertyMap.clear();
259
258
  }
259
+ isValidDevice(entity) {
260
+ return 'ieee_address' in entity;
261
+ }
262
+ isValidGroup(entity) {
263
+ return 'id' in entity;
264
+ }
260
265
  cachePublish(command = 'unknown', payload, transitionTime) {
261
266
  if (command === 'moveToColorTemperature') {
262
267
  delete this.cachePayload['color'];
@@ -286,17 +291,17 @@ export class ZigbeeEntity extends EventEmitter {
286
291
  }, this.noUpdateTimeoutTime).unref();
287
292
  }, this.cachePublishTimeoutTime).unref();
288
293
  }
289
- setCachePublishAttributes(endpoint, postfix = undefined) {
294
+ setCachePublishAttributes(endpoint, postfix) {
290
295
  const brightness = endpoint.hasAttributeServer(LevelControl.id, 'currentLevel') ? Math.round((endpoint.getAttribute(LevelControl.id, 'currentLevel') / 254) * 255) : undefined;
291
296
  if (isValidNumber(brightness, 1, 255))
292
- this.cachePayload['brightness' + (postfix || '')] = brightness;
297
+ this.cachePayload['brightness' + (postfix ?? '')] = brightness;
293
298
  const color_temp = endpoint.hasClusterServer(ColorControl.id) &&
294
299
  endpoint.hasAttributeServer(ColorControl.id, 'colorTemperatureMireds') &&
295
300
  endpoint.getAttribute(ColorControl.id, 'colorMode') === ColorControl.ColorMode.ColorTemperatureMireds
296
301
  ? endpoint.getAttribute(ColorControl.id, 'colorTemperatureMireds')
297
302
  : undefined;
298
303
  if (isValidNumber(color_temp))
299
- this.cachePayload['color_temp' + (postfix || '')] = color_temp;
304
+ this.cachePayload['color_temp' + (postfix ?? '')] = color_temp;
300
305
  const hs_color = endpoint.hasClusterServer(ColorControl.id) &&
301
306
  endpoint.hasAttributeServer(ColorControl.id, 'currentHue') &&
302
307
  endpoint.hasAttributeServer(ColorControl.id, 'currentSaturation') &&
@@ -304,7 +309,7 @@ export class ZigbeeEntity extends EventEmitter {
304
309
  ? [Math.round((endpoint.getAttribute(ColorControl.id, 'currentHue') / 254) * 360), Math.round((endpoint.getAttribute(ColorControl.id, 'currentSaturation') / 254) * 100)]
305
310
  : undefined;
306
311
  if (isValidArray(hs_color, 2)) {
307
- this.cachePayload['color' + (postfix || '')] = { h: hs_color[0], s: hs_color[1] };
312
+ this.cachePayload['color' + (postfix ?? '')] = { h: hs_color[0], s: hs_color[1] };
308
313
  }
309
314
  const xy_color = endpoint.hasClusterServer(ColorControl.id) &&
310
315
  endpoint.hasAttributeServer(ColorControl.id, 'currentX') &&
@@ -313,7 +318,7 @@ export class ZigbeeEntity extends EventEmitter {
313
318
  ? [endpoint.getAttribute(ColorControl.id, 'currentX') / 65535, endpoint.getAttribute(ColorControl.id, 'currentY') / 65535]
314
319
  : undefined;
315
320
  if (isValidArray(xy_color, 2))
316
- this.cachePayload['color' + (postfix || '')] = { x: xy_color[0], y: xy_color[1] };
321
+ this.cachePayload['color' + (postfix ?? '')] = { x: xy_color[0], y: xy_color[1] };
317
322
  const lookupColorMode = [
318
323
  'CurrentHueAndCurrentSaturation',
319
324
  'CurrentXAndCurrentY',
@@ -470,11 +475,11 @@ export class ZigbeeEntity extends EventEmitter {
470
475
  addBridgedDeviceBasicInformation() {
471
476
  if (!this.bridgedDevice)
472
477
  throw new Error('No bridged device');
473
- const softwareVersion = parseInt(this.platform.z2mBridgeInfo?.version || '1');
478
+ const softwareVersion = Number.parseInt(this.platform.z2mBridgeInfo?.version || '1');
474
479
  const softwareVersionString = `${this.platform.z2mBridgeInfo?.version} (commit ${this.platform.z2mBridgeInfo?.commit})`;
475
- const hardwareVersion = parseInt(this.platform.matterbridge.matterbridgeVersion || '1');
480
+ const hardwareVersion = Number.parseInt(this.platform.matterbridge.matterbridgeVersion || '1');
476
481
  const hardwareVersionString = this.platform.matterbridge.matterbridgeVersion || 'unknown';
477
- if (this.isDevice && this.device && this.device.friendly_name === 'Coordinator') {
482
+ if (this.isDevice && this.device?.friendly_name === 'Coordinator') {
478
483
  this.bridgedDevice.createDefaultBridgedDeviceBasicInformationClusterServer(this.device.friendly_name, this.serial, 0xfff1, 'zigbee2MQTT', 'Coordinator', softwareVersion, softwareVersionString, hardwareVersion, hardwareVersionString);
479
484
  }
480
485
  else if (this.isDevice && this.device) {
@@ -570,7 +575,7 @@ export class ZigbeeEntity extends EventEmitter {
570
575
  return;
571
576
  }
572
577
  }
573
- const localValue = deviceEndpoint.getAttribute(ClusterId(clusterId), attributeName, undefined);
578
+ const localValue = deviceEndpoint.getAttribute(ClusterId(clusterId), attributeName);
574
579
  if (typeof value === 'object' ? deepEqual(value, localValue) : value === localValue) {
575
580
  this.log.debug(`Skip update endpoint ${deviceEndpoint.name}:${deviceEndpoint.maybeNumber}${childEndpointName ? ' (' + childEndpointName + ')' : ''} ` +
576
581
  `attribute ${getClusterNameById(ClusterId(clusterId))}.${attributeName} already ${typeof value === 'object' ? debugStringify(value) : value}`);
@@ -603,11 +608,11 @@ export class ZigbeeGroup extends ZigbeeEntity {
603
608
  }
604
609
  static async create(platform, group) {
605
610
  const zigbeeGroup = new ZigbeeGroup(platform, group);
606
- if (zigbeeGroup.platform.postfix !== '') {
607
- zigbeeGroup.serial = `group-${group.id}-${zigbeeGroup.platform.postfix}`.slice(0, 32);
611
+ if (zigbeeGroup.platform.postfix === '') {
612
+ zigbeeGroup.serial = `group-${group.id}`.slice(0, 32);
608
613
  }
609
614
  else {
610
- zigbeeGroup.serial = `group-${group.id}`.slice(0, 32);
615
+ zigbeeGroup.serial = `group-${group.id}-${zigbeeGroup.platform.postfix}`.slice(0, 32);
611
616
  }
612
617
  platform.setSelectDevice(`group-${group.id}`, group.friendly_name, 'wifi');
613
618
  let useState = false;
@@ -623,7 +628,8 @@ export class ZigbeeGroup extends ZigbeeEntity {
623
628
  let isThermostat = false;
624
629
  if (group.members.length === 0) {
625
630
  zigbeeGroup.log.debug(`Group: ${gn}${group.friendly_name}${rs}${db} is a ${CYAN}virtual${db} group`);
626
- zigbeeGroup.bridgedDevice = new MatterbridgeEndpoint([onOffSwitch, bridgedNode, powerSource], { id: group.friendly_name }, zigbeeGroup.log.logLevel === "debug");
631
+ zigbeeGroup.bridgedDevice = new MatterbridgeEndpoint([onOffLightSwitch, bridgedNode, powerSource], { id: group.friendly_name }, zigbeeGroup.log.logLevel === "debug");
632
+ zigbeeGroup.bridgedDevice.createDefaultOnOffClusterServer();
627
633
  isSwitch = true;
628
634
  zigbeeGroup.propertyMap.set('state', { name: 'state', type: 'switch', endpoint: '' });
629
635
  }
@@ -646,10 +652,10 @@ export class ZigbeeGroup extends ZigbeeEntity {
646
652
  isSwitch = true;
647
653
  if (expose.type === 'light')
648
654
  isLight = true;
649
- useState = useState === true || feature.name === 'state' ? true : false;
650
- useBrightness = useBrightness === true || feature.name === 'brightness' ? true : false;
651
- useColor = useColor === true || feature.property === 'color' ? true : false;
652
- useColorTemperature = useColorTemperature === true || feature.name === 'color_temp' ? true : false;
655
+ useState = useState || feature.name === 'state' ? true : false;
656
+ useBrightness = useBrightness || feature.name === 'brightness' ? true : false;
657
+ useColor = useColor || feature.property === 'color' ? true : false;
658
+ useColorTemperature = useColorTemperature || feature.name === 'color_temp' ? true : false;
653
659
  if (feature.value_min)
654
660
  minColorTemperature = Math.min(minColorTemperature, feature.value_min);
655
661
  if (feature.value_max)
@@ -668,7 +674,7 @@ export class ZigbeeGroup extends ZigbeeEntity {
668
674
  }
669
675
  });
670
676
  device.definition?.options.forEach((option) => {
671
- useTransition = useTransition === true || option.name === 'transition' ? true : false;
677
+ useTransition = useTransition || option.name === 'transition' ? true : false;
672
678
  });
673
679
  });
674
680
  zigbeeGroup.log.debug(`Group ${gn}${group.friendly_name}${rs}${db} switch: ${CYAN}${isSwitch}${db} light: ${CYAN}${isLight}${db} cover: ${CYAN}${isCover}${db} thermostat: ${CYAN}${isThermostat}${db}`);
@@ -677,11 +683,11 @@ export class ZigbeeGroup extends ZigbeeEntity {
677
683
  if (useState) {
678
684
  deviceType = onOffLight;
679
685
  if (platform.switchList.includes(group.friendly_name))
680
- deviceType = onOffSwitch;
686
+ deviceType = onOffLightSwitch;
681
687
  else if (platform.lightList.includes(group.friendly_name))
682
688
  deviceType = onOffLight;
683
689
  else if (platform.outletList.includes(group.friendly_name))
684
- deviceType = onOffOutlet;
690
+ deviceType = onOffPlugInUnit;
685
691
  zigbeeGroup.propertyMap.set('state', { name: 'state', type: isLight ? 'light' : 'switch', endpoint: '' });
686
692
  }
687
693
  if (useBrightness) {
@@ -700,13 +706,13 @@ export class ZigbeeGroup extends ZigbeeEntity {
700
706
  zigbeeGroup.propertyMap.set('color', { name: 'color', type: 'light', endpoint: '' });
701
707
  }
702
708
  if (isCover) {
703
- deviceType = coverDevice;
709
+ deviceType = windowCovering;
704
710
  zigbeeGroup.propertyMap.set('state', { name: 'state', type: 'cover', endpoint: '' });
705
711
  zigbeeGroup.propertyMap.set('position', { name: 'position', type: 'cover', endpoint: '' });
706
712
  zigbeeGroup.propertyMap.set('moving', { name: 'moving', type: 'cover', endpoint: '' });
707
713
  }
708
714
  if (isThermostat) {
709
- deviceType = thermostatDevice;
715
+ deviceType = thermostat;
710
716
  zigbeeGroup.propertyMap.set('local_temperature', { name: 'local_temperature', type: 'climate', endpoint: '' });
711
717
  zigbeeGroup.propertyMap.set('current_heating_setpoint', { name: 'current_heating_setpoint', type: 'climate', endpoint: '' });
712
718
  zigbeeGroup.propertyMap.set('current_cooling_setpoint', { name: 'current_cooling_setpoint', type: 'climate', endpoint: '' });
@@ -716,6 +722,8 @@ export class ZigbeeGroup extends ZigbeeEntity {
716
722
  if (!deviceType)
717
723
  return zigbeeGroup;
718
724
  zigbeeGroup.bridgedDevice = new MatterbridgeEndpoint([deviceType, bridgedNode, powerSource], { id: group.friendly_name }, zigbeeGroup.log.logLevel === "debug");
725
+ if (deviceType.code === onOffLightSwitch.code)
726
+ zigbeeGroup.bridgedDevice.createDefaultOnOffClusterServer();
719
727
  }
720
728
  if (!platform.featureBlackList?.includes('scenes') && !platform.deviceFeatureBlackList[group.friendly_name]?.includes('scenes')) {
721
729
  for (const scene of group.scenes) {
@@ -729,7 +737,7 @@ export class ZigbeeGroup extends ZigbeeEntity {
729
737
  }
730
738
  zigbeeGroup.addBridgedDeviceBasicInformation();
731
739
  zigbeeGroup.addPowerSource();
732
- zigbeeGroup.bridgedDevice.addRequiredClusterServers();
740
+ zigbeeGroup.bridgedDevice.addRequiredClusters();
733
741
  if (!zigbeeGroup.bridgedDevice || !zigbeeGroup.verifyMutableDevice(zigbeeGroup.bridgedDevice))
734
742
  return zigbeeGroup;
735
743
  zigbeeGroup.mutableDevice.clear();
@@ -855,34 +863,34 @@ export class ZigbeeGroup extends ZigbeeEntity {
855
863
  }
856
864
  }
857
865
  const z2ms = [
858
- { type: 'switch', name: 'state', property: 'state', deviceType: onOffSwitch, cluster: OnOff.id, attribute: 'onOff', converter: (value) => { return value === 'ON' ? true : false; } },
859
- { type: 'switch', name: 'brightness', property: 'brightness', deviceType: dimmableSwitch, cluster: LevelControl.id, attribute: 'currentLevel', converter: (value) => { return Math.max(1, Math.min(254, value)); } },
860
- { type: 'switch', name: 'color_hs', property: 'color_hs', deviceType: colorTemperatureSwitch, cluster: ColorControl.id, attribute: 'colorMode' },
861
- { type: 'switch', name: 'color_xy', property: 'color_xy', deviceType: colorTemperatureSwitch, cluster: ColorControl.id, attribute: 'colorMode' },
862
- { type: 'switch', name: 'color_temp', property: 'color_temp', deviceType: colorTemperatureSwitch, cluster: ColorControl.id, attribute: 'colorMode' },
863
- { type: 'outlet', name: 'state', property: 'state', deviceType: onOffOutlet, cluster: OnOff.id, attribute: 'onOff', converter: (value) => { return value === 'ON' ? true : false; } },
864
- { type: 'outlet', name: 'brightness', property: 'brightness', deviceType: dimmableOutlet, cluster: LevelControl.id, attribute: 'currentLevel', converter: (value) => { return Math.max(1, Math.min(254, value)); } },
865
- { type: 'light', name: 'state', property: 'state', deviceType: onOffLight, cluster: OnOff.id, attribute: 'onOff', converter: (value) => { return value === 'ON' ? true : false; } },
866
+ { type: 'switch', name: 'state', property: 'state', deviceType: onOffLightSwitch, cluster: OnOff.id, attribute: 'onOff', converter: (value) => { return value === 'ON'; } },
867
+ { type: 'switch', name: 'brightness', property: 'brightness', deviceType: dimmerSwitch, cluster: LevelControl.id, attribute: 'currentLevel', converter: (value) => { return Math.max(1, Math.min(254, value)); } },
868
+ { type: 'switch', name: 'color_hs', property: 'color_hs', deviceType: colorDimmerSwitch, cluster: ColorControl.id, attribute: 'colorMode' },
869
+ { type: 'switch', name: 'color_xy', property: 'color_xy', deviceType: colorDimmerSwitch, cluster: ColorControl.id, attribute: 'colorMode' },
870
+ { type: 'switch', name: 'color_temp', property: 'color_temp', deviceType: colorDimmerSwitch, cluster: ColorControl.id, attribute: 'colorMode' },
871
+ { type: 'outlet', name: 'state', property: 'state', deviceType: onOffPlugInUnit, cluster: OnOff.id, attribute: 'onOff', converter: (value) => { return value === 'ON'; } },
872
+ { type: 'outlet', name: 'brightness', property: 'brightness', deviceType: dimmablePlugInUnit, cluster: LevelControl.id, attribute: 'currentLevel', converter: (value) => { return Math.max(1, Math.min(254, value)); } },
873
+ { type: 'light', name: 'state', property: 'state', deviceType: onOffLight, cluster: OnOff.id, attribute: 'onOff', converter: (value) => { return value === 'ON'; } },
866
874
  { type: 'light', name: 'brightness', property: 'brightness', deviceType: dimmableLight, cluster: LevelControl.id, attribute: 'currentLevel', converter: (value) => { return Math.max(1, Math.min(254, value)); } },
867
875
  { type: 'light', name: 'color_hs', property: 'color_hs', deviceType: extendedColorLight, cluster: ColorControl.id, attribute: 'colorMode' },
868
876
  { type: 'light', name: 'color_xy', property: 'color_xy', deviceType: extendedColorLight, cluster: ColorControl.id, attribute: 'colorMode' },
869
877
  { type: 'light', name: 'color_temp', property: 'color_temp', deviceType: colorTemperatureLight, cluster: ColorControl.id, attribute: 'colorMode' },
870
- { type: 'cover', name: 'state', property: 'state', deviceType: coverDevice, cluster: WindowCovering.id, attribute: 'targetPositionLiftPercent100ths' },
871
- { type: 'cover', name: 'moving', property: 'moving', deviceType: coverDevice, cluster: WindowCovering.id, attribute: 'operationalStatus' },
872
- { type: 'cover', name: 'position', property: 'position', deviceType: coverDevice, cluster: WindowCovering.id, attribute: 'currentPositionLiftPercent100ths' },
873
- { type: 'lock', name: 'state', property: 'state', deviceType: doorLockDevice, cluster: DoorLock.id, attribute: 'lockState', converter: (value) => { return value === 'LOCK' ? DoorLock.LockState.Locked : DoorLock.LockState.Unlocked; } },
874
- { type: 'climate', name: 'local_temperature', property: 'local_temperature', deviceType: thermostatDevice, cluster: Thermostat.id, attribute: 'localTemperature', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
875
- { type: 'climate', name: 'current_heating_setpoint', property: 'current_heating_setpoint', deviceType: thermostatDevice, cluster: Thermostat.id, attribute: 'occupiedHeatingSetpoint' },
876
- { type: 'climate', name: 'occupied_heating_setpoint', property: 'occupied_heating_setpoint', deviceType: thermostatDevice, cluster: Thermostat.id, attribute: 'occupiedHeatingSetpoint', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
877
- { type: 'climate', name: 'occupied_cooling_setpoint', property: 'occupied_cooling_setpoint', deviceType: thermostatDevice, cluster: Thermostat.id, attribute: 'occupiedCoolingSetpoint', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
878
- { type: 'climate', name: 'unoccupied_heating_setpoint', property: 'unoccupied_heating_setpoint', deviceType: thermostatDevice, cluster: Thermostat.id, attribute: 'occupiedHeatingSetpoint', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
879
- { type: 'climate', name: 'unoccupied_cooling_setpoint', property: 'unoccupied_cooling_setpoint', deviceType: thermostatDevice, cluster: Thermostat.id, attribute: 'occupiedCoolingSetpoint', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
880
- { type: 'climate', name: 'running_state', property: 'running_state', deviceType: thermostatDevice, cluster: Thermostat.id, attribute: 'thermostatRunningMode', valueLookup: ['idle', '', '', 'cool', 'heat'] },
881
- { type: 'climate', name: 'system_mode', property: 'system_mode', deviceType: thermostatDevice, cluster: Thermostat.id, attribute: 'systemMode', valueLookup: ['off', 'auto', '', 'cool', 'heat', '', '', 'fan_only'] },
882
- { type: '', name: 'min_temperature_limit', property: 'min_temperature_limit', deviceType: thermostatDevice, cluster: Thermostat.id, attribute: 'minHeatSetpointLimit', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
883
- { type: '', name: 'max_temperature_limit', property: 'max_temperature_limit', deviceType: thermostatDevice, cluster: Thermostat.id, attribute: 'maxHeatSetpointLimit', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
884
- { type: '', name: 'min_heat_setpoint_limit', property: 'min_heat_setpoint_limit', deviceType: thermostatDevice, cluster: Thermostat.id, attribute: 'minHeatSetpointLimit', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
885
- { type: '', name: 'max_heat_setpoint_limit', property: 'max_heat_setpoint_limit', deviceType: thermostatDevice, cluster: Thermostat.id, attribute: 'maxHeatSetpointLimit', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
878
+ { type: 'cover', name: 'state', property: 'state', deviceType: windowCovering, cluster: WindowCovering.id, attribute: 'targetPositionLiftPercent100ths' },
879
+ { type: 'cover', name: 'moving', property: 'moving', deviceType: windowCovering, cluster: WindowCovering.id, attribute: 'operationalStatus' },
880
+ { type: 'cover', name: 'position', property: 'position', deviceType: windowCovering, cluster: WindowCovering.id, attribute: 'currentPositionLiftPercent100ths' },
881
+ { type: 'lock', name: 'state', property: 'state', deviceType: doorLock, cluster: DoorLock.id, attribute: 'lockState', converter: (value) => { return value === 'LOCK' ? DoorLock.LockState.Locked : DoorLock.LockState.Unlocked; } },
882
+ { type: 'climate', name: 'local_temperature', property: 'local_temperature', deviceType: thermostat, cluster: Thermostat.id, attribute: 'localTemperature', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
883
+ { type: 'climate', name: 'current_heating_setpoint', property: 'current_heating_setpoint', deviceType: thermostat, cluster: Thermostat.id, attribute: 'occupiedHeatingSetpoint' },
884
+ { type: 'climate', name: 'occupied_heating_setpoint', property: 'occupied_heating_setpoint', deviceType: thermostat, cluster: Thermostat.id, attribute: 'occupiedHeatingSetpoint', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
885
+ { type: 'climate', name: 'occupied_cooling_setpoint', property: 'occupied_cooling_setpoint', deviceType: thermostat, cluster: Thermostat.id, attribute: 'occupiedCoolingSetpoint', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
886
+ { type: 'climate', name: 'unoccupied_heating_setpoint', property: 'unoccupied_heating_setpoint', deviceType: thermostat, cluster: Thermostat.id, attribute: 'occupiedHeatingSetpoint', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
887
+ { type: 'climate', name: 'unoccupied_cooling_setpoint', property: 'unoccupied_cooling_setpoint', deviceType: thermostat, cluster: Thermostat.id, attribute: 'occupiedCoolingSetpoint', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
888
+ { type: 'climate', name: 'running_state', property: 'running_state', deviceType: thermostat, cluster: Thermostat.id, attribute: 'thermostatRunningMode', valueLookup: ['idle', '', '', 'cool', 'heat'] },
889
+ { type: 'climate', name: 'system_mode', property: 'system_mode', deviceType: thermostat, cluster: Thermostat.id, attribute: 'systemMode', valueLookup: ['off', 'auto', '', 'cool', 'heat', '', '', 'fan_only'] },
890
+ { type: '', name: 'min_temperature_limit', property: 'min_temperature_limit', deviceType: thermostat, cluster: Thermostat.id, attribute: 'minHeatSetpointLimit', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
891
+ { type: '', name: 'max_temperature_limit', property: 'max_temperature_limit', deviceType: thermostat, cluster: Thermostat.id, attribute: 'maxHeatSetpointLimit', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
892
+ { type: '', name: 'min_heat_setpoint_limit', property: 'min_heat_setpoint_limit', deviceType: thermostat, cluster: Thermostat.id, attribute: 'minHeatSetpointLimit', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
893
+ { type: '', name: 'max_heat_setpoint_limit', property: 'max_heat_setpoint_limit', deviceType: thermostat, cluster: Thermostat.id, attribute: 'maxHeatSetpointLimit', converter: (value) => { return Math.max(-5000, Math.min(5000, value * 100)); } },
886
894
  { type: '', name: 'presence', property: 'presence', deviceType: occupancySensor, cluster: OccupancySensing.id, attribute: 'occupancy', converter: (value) => { return { occupied: value }; } },
887
895
  { type: '', name: 'occupancy', property: 'occupancy', deviceType: occupancySensor, cluster: OccupancySensing.id, attribute: 'occupancy', converter: (value) => { return { occupied: value }; } },
888
896
  { type: '', name: 'illuminance', property: 'illuminance', deviceType: lightSensor, cluster: IlluminanceMeasurement.id, attribute: 'measuredValue', converter: (value) => { return Math.round(Math.max(Math.min(10000 * Math.log10(value), 0xfffe), 0)); } },
@@ -920,7 +928,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
920
928
  }
921
929
  static async create(platform, device) {
922
930
  const zigbeeDevice = new ZigbeeDevice(platform, device);
923
- zigbeeDevice.serial = `${device.ieee_address}`;
931
+ zigbeeDevice.serial = device.ieee_address;
924
932
  if (zigbeeDevice.platform.postfix !== '') {
925
933
  zigbeeDevice.serial = `${zigbeeDevice.serial}-${zigbeeDevice.platform.postfix}`.slice(0, 32);
926
934
  }
@@ -929,7 +937,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
929
937
  (device.model_id.startsWith('SLZB-') && device.manufacturer === 'SMLIGHT')) {
930
938
  zigbeeDevice.isRouter = true;
931
939
  platform.setSelectDevice(device.ieee_address, device.friendly_name, 'wifi');
932
- zigbeeDevice.bridgedDevice = new MatterbridgeEndpoint([doorLockDevice, bridgedNode, powerSource], { id: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
940
+ zigbeeDevice.bridgedDevice = new MatterbridgeEndpoint([doorLock, bridgedNode, powerSource], { id: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
933
941
  zigbeeDevice.addBridgedDeviceBasicInformation();
934
942
  zigbeeDevice.addPowerSource();
935
943
  zigbeeDevice.bridgedDevice.addRequiredClusterServers();
@@ -984,8 +992,8 @@ export class ZigbeeDevice extends ZigbeeEntity {
984
992
  descriptions.push(feature.description ?? '');
985
993
  labels.push(feature.label ?? '');
986
994
  units.push(feature.unit ?? '');
987
- value_mins.push(feature.value_min ?? NaN);
988
- value_maxs.push(feature.value_max ?? NaN);
995
+ value_mins.push(feature.value_min ?? Number.NaN);
996
+ value_maxs.push(feature.value_max ?? Number.NaN);
989
997
  values.push(feature.values ? feature.values.join('|') : '');
990
998
  });
991
999
  }
@@ -1002,8 +1010,8 @@ export class ZigbeeDevice extends ZigbeeEntity {
1002
1010
  descriptions.push(expose.description ?? '');
1003
1011
  labels.push(expose.label ?? '');
1004
1012
  units.push(expose.unit ?? '');
1005
- value_mins.push(expose.value_min ?? NaN);
1006
- value_maxs.push(expose.value_max ?? NaN);
1013
+ value_mins.push(expose.value_min ?? Number.NaN);
1014
+ value_maxs.push(expose.value_max ?? Number.NaN);
1007
1015
  values.push(expose.values ? expose.values.join('|') : '');
1008
1016
  if (expose.name === 'action' && expose.values) {
1009
1017
  zigbeeDevice.actions.push(...expose.values);
@@ -1019,8 +1027,8 @@ export class ZigbeeDevice extends ZigbeeEntity {
1019
1027
  descriptions.push(option.description ?? '');
1020
1028
  labels.push(option.label ?? '');
1021
1029
  units.push(option.unit ?? '');
1022
- value_mins.push(option.value_min ?? NaN);
1023
- value_maxs.push(option.value_max ?? NaN);
1030
+ value_mins.push(option.value_min ?? Number.NaN);
1031
+ value_maxs.push(option.value_max ?? Number.NaN);
1024
1032
  values.push(option.values ? option.values.join('|') : '');
1025
1033
  });
1026
1034
  if (platform.switchList.includes(device.friendly_name)) {
@@ -1077,14 +1085,14 @@ export class ZigbeeDevice extends ZigbeeEntity {
1077
1085
  zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${zb}${endpoint}${db} type: ${zb}${type}${db} property: ${zb}${name}${db} => deviceType: ${z2m.deviceType?.name} cluster: ${z2m.cluster} attribute: ${z2m.attribute}`);
1078
1086
  zigbeeDevice.propertyMap.set(property, { name, type, endpoint, category, description, label, unit, value_min, value_max, values: value });
1079
1087
  if (endpoint === '') {
1080
- if (!zigbeeDevice.mutableDevice.has(endpoint)) {
1081
- zigbeeDevice.mutableDevice.set(endpoint, { tagList: [], deviceTypes: [z2m.deviceType], clusterServersIds: [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)], clusterServersOptions: [], clusterClientsIds: [], clusterClientsOptions: [] });
1082
- }
1083
- else {
1088
+ if (zigbeeDevice.mutableDevice.has(endpoint)) {
1084
1089
  zigbeeDevice.mutableDevice.get(endpoint)?.deviceTypes.push(z2m.deviceType);
1085
1090
  zigbeeDevice.mutableDevice.get(endpoint)?.clusterServersIds.push(...z2m.deviceType.requiredServerClusters);
1086
1091
  zigbeeDevice.mutableDevice.get(endpoint)?.clusterServersIds.push(ClusterId(z2m.cluster));
1087
1092
  }
1093
+ else {
1094
+ zigbeeDevice.mutableDevice.set(endpoint, { tagList: [], deviceTypes: [z2m.deviceType], clusterServersIds: [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)], clusterServersOptions: [], clusterClientsIds: [], clusterClientsOptions: [] });
1095
+ }
1088
1096
  }
1089
1097
  else {
1090
1098
  const tagList = [];
@@ -1101,13 +1109,13 @@ export class ZigbeeDevice extends ZigbeeEntity {
1101
1109
  if (endpoint === 'l6')
1102
1110
  tagList.push({ mfgCode: null, namespaceId: NumberTag.Six.namespaceId, tag: NumberTag.Six.tag, label: 'endpoint ' + endpoint });
1103
1111
  tagList.push({ mfgCode: null, namespaceId: SwitchesTag.Custom.namespaceId, tag: SwitchesTag.Custom.tag, label: 'endpoint ' + endpoint });
1104
- if (!zigbeeDevice.mutableDevice.has(endpoint)) {
1105
- zigbeeDevice.mutableDevice.set(endpoint, { tagList, deviceTypes: [z2m.deviceType], clusterServersIds: [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)], clusterServersOptions: [], clusterClientsIds: [], clusterClientsOptions: [] });
1106
- }
1107
- else {
1112
+ if (zigbeeDevice.mutableDevice.has(endpoint)) {
1108
1113
  zigbeeDevice.mutableDevice.get(endpoint)?.deviceTypes.push(z2m.deviceType);
1109
1114
  zigbeeDevice.mutableDevice.get(endpoint)?.clusterServersIds.push(...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster));
1110
1115
  }
1116
+ else {
1117
+ zigbeeDevice.mutableDevice.set(endpoint, { tagList, deviceTypes: [z2m.deviceType], clusterServersIds: [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)], clusterServersOptions: [], clusterClientsIds: [], clusterClientsOptions: [] });
1118
+ }
1111
1119
  if (zigbeeDevice.composedType === '')
1112
1120
  zigbeeDevice.composedType = type;
1113
1121
  zigbeeDevice.hasEndpoints = true;
@@ -1118,8 +1126,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
1118
1126
  if (name === 'action' && zigbeeDevice.actions.length) {
1119
1127
  zigbeeDevice.log.info(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${nf} has actions mapped to these switches on sub endpoints:`);
1120
1128
  zigbeeDevice.log.info(' controller events <=> zigbee2mqtt actions');
1121
- if (!zigbeeDevice.bridgedDevice)
1122
- zigbeeDevice.bridgedDevice = new MatterbridgeEndpoint([bridgedNode], { id: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
1129
+ zigbeeDevice.bridgedDevice ??= new MatterbridgeEndpoint([bridgedNode], { id: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
1123
1130
  zigbeeDevice.hasEndpoints = true;
1124
1131
  const switchMap = ['Single Press', 'Double Press', 'Long Press '];
1125
1132
  const triggerMap = ['Single', 'Double', 'Long'];
@@ -1189,12 +1196,12 @@ export class ZigbeeDevice extends ZigbeeEntity {
1189
1196
  device.deviceTypes.forEach((deviceType) => {
1190
1197
  deviceTypesMap.set(deviceType.code, deviceType);
1191
1198
  });
1192
- if (deviceTypesMap.has(onOffSwitch.code) && deviceTypesMap.has(dimmableSwitch.code))
1193
- deviceTypesMap.delete(onOffSwitch.code);
1194
- if (deviceTypesMap.has(dimmableSwitch.code) && deviceTypesMap.has(colorTemperatureSwitch.code))
1195
- deviceTypesMap.delete(dimmableSwitch.code);
1196
- if (deviceTypesMap.has(onOffOutlet.code) && deviceTypesMap.has(dimmableOutlet.code))
1197
- deviceTypesMap.delete(onOffOutlet.code);
1199
+ if (deviceTypesMap.has(onOffLightSwitch.code) && deviceTypesMap.has(dimmerSwitch.code))
1200
+ deviceTypesMap.delete(onOffLightSwitch.code);
1201
+ if (deviceTypesMap.has(dimmerSwitch.code) && deviceTypesMap.has(colorDimmerSwitch.code))
1202
+ deviceTypesMap.delete(dimmerSwitch.code);
1203
+ if (deviceTypesMap.has(onOffPlugInUnit.code) && deviceTypesMap.has(dimmablePlugInUnit.code))
1204
+ deviceTypesMap.delete(onOffPlugInUnit.code);
1198
1205
  if (deviceTypesMap.has(onOffLight.code) && deviceTypesMap.has(dimmableLight.code))
1199
1206
  deviceTypesMap.delete(onOffLight.code);
1200
1207
  if (deviceTypesMap.has(dimmableLight.code) && deviceTypesMap.has(colorTemperatureLight.code))
@@ -1264,12 +1271,12 @@ export class ZigbeeDevice extends ZigbeeEntity {
1264
1271
  if (mainEndpoint.clusterServersIds.includes(Thermostat.id)) {
1265
1272
  const system_mode = zigbeeDevice.propertyMap.get('system_mode');
1266
1273
  const system_mode_values = system_mode?.values;
1267
- const heat = zigbeeDevice.propertyMap.get('occupied_heating_setpoint') || zigbeeDevice.propertyMap.get('unoccupied_heating_setpoint');
1268
- const cool = zigbeeDevice.propertyMap.get('occupied_cooling_setpoint') || zigbeeDevice.propertyMap.get('unoccupied_cooling_setpoint');
1269
- const minHeating = heat && heat.value_min !== undefined && !isNaN(heat.value_min) ? heat.value_min : 0;
1270
- const maxHeating = heat && heat.value_max !== undefined && !isNaN(heat.value_max) ? heat.value_max : 50;
1271
- const minCooling = cool && cool.value_min !== undefined && !isNaN(cool.value_min) ? cool.value_min : 0;
1272
- const maxCooling = cool && cool.value_max !== undefined && !isNaN(cool.value_max) ? cool.value_max : 50;
1274
+ const heat = zigbeeDevice.propertyMap.get('occupied_heating_setpoint') ?? zigbeeDevice.propertyMap.get('unoccupied_heating_setpoint');
1275
+ const cool = zigbeeDevice.propertyMap.get('occupied_cooling_setpoint') ?? zigbeeDevice.propertyMap.get('unoccupied_cooling_setpoint');
1276
+ const minHeating = heat?.value_min !== undefined && !Number.isNaN(heat.value_min) ? heat.value_min : 0;
1277
+ const maxHeating = heat?.value_max !== undefined && !Number.isNaN(heat.value_max) ? heat.value_max : 50;
1278
+ const minCooling = cool?.value_min !== undefined && !Number.isNaN(cool.value_min) ? cool.value_min : 0;
1279
+ const maxCooling = cool?.value_max !== undefined && !Number.isNaN(cool.value_max) ? cool.value_max : 50;
1273
1280
  zigbeeDevice.log.debug(`Configuring device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} Thermostat cluster with heating ${CYAN}${heat ? 'supported' : '(un)occupied not supported'}${db} cooling ${CYAN}${cool ? 'supported' : '(un)occupied not supported'}${db} ` +
1274
1281
  `system_mode ${CYAN}${system_mode_values ?? 'not supported'}${db} ` +
1275
1282
  `minHeating ${CYAN}${minHeating}${db} maxHeating ${CYAN}${maxHeating}${db} minCooling ${CYAN}${minCooling}${db} maxCooling ${CYAN}${maxCooling}${db}`);
@@ -1414,7 +1421,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
1414
1421
  });
1415
1422
  }
1416
1423
  if (zigbeeDevice.bridgedDevice.hasClusterServer(Thermostat.id)) {
1417
- zigbeeDevice.bridgedDevice.addCommandHandler('setpointRaiseLower', ({ request: request }) => {
1424
+ zigbeeDevice.bridgedDevice.addCommandHandler('setpointRaiseLower', ({ request }) => {
1418
1425
  zigbeeDevice.log.debug(`Command setpointRaiseLower called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request:`, request);
1419
1426
  if (request.mode === Thermostat.SetpointRaiseLowerMode.Heat || request.mode === Thermostat.SetpointRaiseLowerMode.Both) {
1420
1427
  const t = zigbeeDevice.bridgedDevice?.getAttribute(Thermostat.id, 'occupiedHeatingSetpoint', zigbeeDevice.log);
package/dist/module.js CHANGED
@@ -2,7 +2,7 @@ import path from 'node:path';
2
2
  import { MatterbridgeDynamicPlatform } from 'matterbridge';
3
3
  import { CYAN, db, debugStringify, dn, er, gn, nf, payloadStringify, rs, wr, zb } from 'matterbridge/logger';
4
4
  import { BridgedDeviceBasicInformation, DoorLock } from 'matterbridge/matter/clusters';
5
- import { fireAndForget, isValidNumber, isValidString, waiter } from 'matterbridge/utils';
5
+ import { fireAndForget, getErrorMessage, isValidNumber, isValidString, waiter } from 'matterbridge/utils';
6
6
  import { ZigbeeDevice, ZigbeeGroup } from './entity.js';
7
7
  import { Zigbee2MQTT } from './zigbee2mqtt.js';
8
8
  export default function initializePlugin(matterbridge, log, config) {
@@ -41,8 +41,8 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
41
41
  constructor(matterbridge, log, config) {
42
42
  super(matterbridge, log, config);
43
43
  this.config = config;
44
- if (typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.8.0')) {
45
- throw new Error(`This plugin requires Matterbridge version >= "3.8.0". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend."`);
44
+ if (typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.9.0')) {
45
+ throw new Error(`This plugin requires Matterbridge version >= "3.9.0". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend."`);
46
46
  }
47
47
  this.shouldStart = false;
48
48
  this.shouldConfigure = false;
@@ -81,6 +81,8 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
81
81
  this.postfix = config.postfix;
82
82
  }
83
83
  this.postfix = this.postfix.trim().slice(0, 3);
84
+ this.config.debug ??= false;
85
+ this.config.unregisterOnShutdown ??= false;
84
86
  config.host = this.mqttHost;
85
87
  config.port = this.mqttPort;
86
88
  config.topic = this.mqttTopic;
@@ -93,10 +95,8 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
93
95
  delete config.deviceScenes;
94
96
  if (config.groupScenes !== undefined)
95
97
  delete config.groupScenes;
96
- if (config.scenesType === undefined)
97
- config.scenesType = 'outlet';
98
- if (config.scenesPrefix === undefined)
99
- config.scenesPrefix = true;
98
+ config.scenesType ??= 'outlet';
99
+ config.scenesPrefix ??= true;
100
100
  this.log.info(`Initializing platform: ${CYAN}${this.config.name}${nf} version: ${CYAN}${this.config.version}${rs}`);
101
101
  this.log.info(`Loaded zigbee2mqtt parameters from ${CYAN}${path.join(matterbridge.matterbridgeDirectory, 'matterbridge-zigbee2mqtt.config.json')}${rs}`);
102
102
  this.z2m = new Zigbee2MQTT(this.mqttHost, this.mqttPort, this.mqttTopic, this.mqttUsername, this.mqttPassword, this.config.clientId, this.config.protocolVersion, this.config.ca, this.config.rejectUnauthorized, this.config.cert, this.config.key, config.debug);
@@ -142,9 +142,9 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
142
142
  this.log.info(`zigbee2MQTT version ${this.z2mBridgeInfo.version} zh version ${this.z2mBridgeInfo.zigbee_herdsman.version} zhc version ${this.z2mBridgeInfo.zigbee_herdsman_converters.version}`);
143
143
  if (this.z2mBridgeInfo.config.advanced.output === 'attribute')
144
144
  this.log.error(`zigbee2MQTT advanced.output must be 'json' or 'attribute_and_json'. Now is ${this.z2mBridgeInfo.config.advanced.output}`);
145
- if (this.z2mBridgeInfo.config.advanced.legacy_api === true)
145
+ if (this.z2mBridgeInfo.config.advanced.legacy_api)
146
146
  this.log.info(`zigbee2MQTT advanced.legacy_api is ${this.z2mBridgeInfo.config.advanced.legacy_api}`);
147
- if (this.z2mBridgeInfo.config.advanced.legacy_availability_payload === true)
147
+ if (this.z2mBridgeInfo.config.advanced.legacy_availability_payload)
148
148
  this.log.info(`zigbee2MQTT advanced.legacy_availability_payload is ${this.z2mBridgeInfo.config.advanced.legacy_availability_payload}`);
149
149
  if (this.z2mBridgeInfo.config.frontend?.package)
150
150
  this.log.info(`zigbee2MQTT frontend.package is ${this.z2mBridgeInfo.config.frontend?.package}`);
@@ -419,7 +419,7 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
419
419
  if (this.availabilityTimer)
420
420
  clearInterval(this.availabilityTimer);
421
421
  this.availabilityTimer = undefined;
422
- if (this.config.unregisterOnShutdown === true)
422
+ if (this.config.unregisterOnShutdown)
423
423
  await this.unregisterAllDevices();
424
424
  this.bridgedDevices = [];
425
425
  this.zigbeeEntities = [];
@@ -437,7 +437,7 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
437
437
  requestDeviceUpdate(device) {
438
438
  this.log.debug(`Requesting update for ${device.friendly_name} model_id: ${device.model_id} manufacturer: ${device.manufacturer}`);
439
439
  const payload = {};
440
- if (device.power_source === 'Battery' || !device.definition || !device.definition.exposes)
440
+ if (device.power_source === 'Battery' || !device.definition?.exposes)
441
441
  return;
442
442
  for (const feature of device.definition.exposes) {
443
443
  if (feature.features) {
@@ -485,7 +485,7 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
485
485
  this.log.warn(`Device ${dn}${device.friendly_name}${wr} ID: ${device.ieee_address} not registered`);
486
486
  }
487
487
  catch (error) {
488
- this.log.error(`Error registering device ${dn}${device.friendly_name}${er} ID: ${device.ieee_address}: ${error}`);
488
+ this.log.error(`Error registering device ${dn}${device.friendly_name}${er} ID: ${device.ieee_address}: ${getErrorMessage(error)}`);
489
489
  }
490
490
  return matterDevice;
491
491
  }
@@ -509,13 +509,13 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
509
509
  this.log.warn(`Group ${gn}${group.friendly_name}${wr} ID: ${group.id} not registered`);
510
510
  }
511
511
  catch (error) {
512
- this.log.error(`Error registering group ${gn}${group.friendly_name}${er} ID: ${group.id}: ${error}`);
512
+ this.log.error(`Error registering group ${gn}${group.friendly_name}${er} ID: ${group.id}: ${getErrorMessage(error)}`);
513
513
  }
514
514
  return matterGroup;
515
515
  }
516
516
  async unregisterZigbeeEntity(friendly_name) {
517
517
  const entity = this.zigbeeEntities.find((entity) => entity.entityName === friendly_name);
518
- if (entity) {
518
+ if (entity?.bridgedDevice) {
519
519
  this.log.info(`Removing device: ${friendly_name}`);
520
520
  await this.unregisterDevice(entity.bridgedDevice);
521
521
  entity.destroy();
@@ -2,6 +2,7 @@ import crypto from 'node:crypto';
2
2
  import { EventEmitter } from 'node:events';
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
+ import { getErrorMessage, isErrnoException } from 'matterbridge/utils';
5
6
  import { connectAsync } from 'mqtt';
6
7
  import { AnsiLogger, db, dn, er, gn, hk, id, idn, ign, REVERSE, REVERSEOFF, rs, zb } from 'node-ansi-logger';
7
8
  export class Zigbee2MQTT extends EventEmitter {
@@ -55,10 +56,7 @@ export class Zigbee2MQTT extends EventEmitter {
55
56
  this.options.protocolVersion = protocolVersion;
56
57
  if (mqttHost.startsWith('mqtts://') || mqttHost.startsWith('wss://')) {
57
58
  this.log.debug('Using mqtts:// protocol for secure MQTT connection');
58
- if (!ca) {
59
- this.log.info('When using mqtts:// protocol, you must provide the ca certificate for SSL/TLS connections with self-signed certificates.');
60
- }
61
- else {
59
+ if (ca) {
62
60
  try {
63
61
  fs.accessSync(ca, fs.constants.R_OK);
64
62
  this.options.ca = fs.readFileSync(ca);
@@ -68,7 +66,10 @@ export class Zigbee2MQTT extends EventEmitter {
68
66
  this.log.error(`Error reading the CA certificate from ${ca}:`, error);
69
67
  }
70
68
  }
71
- this.options.rejectUnauthorized = rejectUnauthorized !== undefined ? rejectUnauthorized : true;
69
+ else {
70
+ this.log.info('When using mqtts:// protocol, you must provide the ca certificate for SSL/TLS connections with self-signed certificates.');
71
+ }
72
+ this.options.rejectUnauthorized = rejectUnauthorized;
72
73
  this.log.info(`TLS rejectUnauthorized is set to ${this.options.rejectUnauthorized}`);
73
74
  if (cert && key) {
74
75
  try {
@@ -121,7 +122,7 @@ export class Zigbee2MQTT extends EventEmitter {
121
122
  this.z2mPermitJoin = false;
122
123
  this.z2mPermitJoinTimeout = 0;
123
124
  this.z2mVersion = '';
124
- this.z2mBridge = {};
125
+ this.z2mBridge = undefined;
125
126
  this.z2mDevices = [];
126
127
  this.z2mGroups = [];
127
128
  this.log.debug(`Created new instance with host: ${mqttHost} port: ${mqttPort} protocol ${protocolVersion} topic: ${mqttTopic} username: ${mqttUsername !== undefined && mqttUsername !== '' ? mqttUsername : 'undefined'} password: ${mqttPassword !== undefined && mqttPassword !== '' ? '*****' : 'undefined'}`);
@@ -138,13 +139,12 @@ export class Zigbee2MQTT extends EventEmitter {
138
139
  this.mqttDataPath = dataPath;
139
140
  this.log.debug(`Data directory ${this.mqttDataPath} created successfully.`);
140
141
  }
141
- catch (e) {
142
- const error = e;
143
- if (error.code === 'EEXIST') {
142
+ catch (error) {
143
+ if (isErrnoException(error) && error.code === 'EEXIST') {
144
144
  this.log.debug('Data directory already exists');
145
145
  }
146
146
  else {
147
- this.log.error('Error creating data directory:', error);
147
+ this.log.error(`Error creating data directory: ${getErrorMessage(error)}`);
148
148
  }
149
149
  }
150
150
  try {
@@ -152,21 +152,21 @@ export class Zigbee2MQTT extends EventEmitter {
152
152
  fs.unlinkSync(filePath);
153
153
  }
154
154
  catch (error) {
155
- this.log.debug(`Error deleting bridge-payloads.txt: ${error}`);
155
+ this.log.debug(`Error deleting bridge-payloads.txt: ${getErrorMessage(error)}`);
156
156
  }
157
157
  try {
158
158
  const filePath = path.join(this.mqttDataPath, 'bridge-publish-payloads.txt');
159
159
  fs.unlinkSync(filePath);
160
160
  }
161
161
  catch (error) {
162
- this.log.debug(`Error deleting bridge-publish-payloads.txt: ${error}`);
162
+ this.log.debug(`Error deleting bridge-publish-payloads.txt: ${getErrorMessage(error)}`);
163
163
  }
164
164
  try {
165
165
  const filePath = path.join(this.mqttDataPath, 'matter-commands.txt');
166
166
  fs.unlinkSync(filePath);
167
167
  }
168
168
  catch (error) {
169
- this.log.debug(`Error deleting matter-commands.txt: ${error}`);
169
+ this.log.debug(`Error deleting matter-commands.txt: ${getErrorMessage(error)}`);
170
170
  }
171
171
  }
172
172
  getUrl() {
@@ -229,13 +229,13 @@ export class Zigbee2MQTT extends EventEmitter {
229
229
  this.mqttKeepaliveInterval = setInterval(() => {
230
230
  this.log.debug('Publishing keepalive MQTT message');
231
231
  this.mqttClient?.publishAsync(`clients/${this.options.clientId}/heartbeat`, 'alive', { qos: 2 }).catch((error) => {
232
- this.log.error('Error publishing keepalive MQTT message:', error);
232
+ this.log.error(`Error publishing keepalive MQTT message: ${getErrorMessage(error)}`);
233
233
  });
234
234
  }, (this.options.keepalive ?? 60) * 1000).unref();
235
235
  return;
236
236
  })
237
237
  .catch((error) => {
238
- this.log.error(`Error connecting to ${this.getUrl()}: ${error.message}`);
238
+ this.log.error(`Error connecting to ${this.getUrl()}: ${getErrorMessage(error)}`);
239
239
  this.emit('mqtt_error', error);
240
240
  });
241
241
  }
@@ -262,7 +262,7 @@ export class Zigbee2MQTT extends EventEmitter {
262
262
  return;
263
263
  })
264
264
  .catch((error) => {
265
- this.log.error(`Error closing connection: ${error.message}`);
265
+ this.log.error(`Error closing connection: ${getErrorMessage(error)}`);
266
266
  });
267
267
  }
268
268
  }
@@ -277,7 +277,7 @@ export class Zigbee2MQTT extends EventEmitter {
277
277
  return;
278
278
  })
279
279
  .catch((error) => {
280
- this.log.error(`Subscribe error: ${error} on topic: ${topic}`);
280
+ this.log.error(`Subscribe error: ${getErrorMessage(error)} on topic: ${topic}`);
281
281
  });
282
282
  }
283
283
  else {
@@ -303,7 +303,7 @@ export class Zigbee2MQTT extends EventEmitter {
303
303
  })
304
304
  .catch((error) => {
305
305
  this.mqttPublishInflights--;
306
- this.log.error(`****Publish ${REVERSE}[${this.mqttPublishQueue.length}-${this.mqttPublishInflights}]${REVERSEOFF} error: ${error} on topic: ${topic} message: ${message} inflights: ${this.mqttPublishInflights}`);
306
+ this.log.error(`****Publish ${REVERSE}[${this.mqttPublishQueue.length}-${this.mqttPublishInflights}]${REVERSEOFF} error: ${getErrorMessage(error)} on topic: ${topic} message: ${message} inflights: ${this.mqttPublishInflights}`);
307
307
  })
308
308
  .finally(() => {
309
309
  this.mqttPublishQueue.splice(0, 1);
@@ -342,7 +342,7 @@ export class Zigbee2MQTT extends EventEmitter {
342
342
  }
343
343
  })
344
344
  .catch((error) => {
345
- this.log.error(`****Publish ${REVERSE}[${this.mqttPublishInflights}]${REVERSEOFF} error: ${error} on topic: ${topic} message: ${message}`);
345
+ this.log.error(`****Publish ${REVERSE}[${this.mqttPublishInflights}]${REVERSEOFF} error: ${getErrorMessage(error)} on topic: ${topic} message: ${message}`);
346
346
  })
347
347
  .finally(() => {
348
348
  this.mqttPublishInflights--;
@@ -375,7 +375,7 @@ export class Zigbee2MQTT extends EventEmitter {
375
375
  writeFile(file, data) {
376
376
  const filePath = path.join(this.mqttDataPath, file);
377
377
  fs.promises
378
- .writeFile(`${filePath}`, data)
378
+ .writeFile(filePath, data)
379
379
  .then(() => {
380
380
  this.log.debug(`Successfully wrote to ${filePath}`);
381
381
  return;
@@ -428,9 +428,9 @@ export class Zigbee2MQTT extends EventEmitter {
428
428
  this.log.debug(`Message bridge/info advanced.legacy_availability_payload => ${this.z2mBridge.config.advanced.legacy_availability_payload}`);
429
429
  if (this.z2mBridge.config.advanced.output === 'attribute')
430
430
  this.log.error(`Message bridge/info advanced.output must be 'json' or 'attribute_and_json'. Now is ${this.z2mBridge.config.advanced.output}`);
431
- if (this.z2mBridge.config.advanced.legacy_api === true)
431
+ if (this.z2mBridge.config.advanced.legacy_api)
432
432
  this.log.info(`Message bridge/info advanced.legacy_api is ${this.z2mBridge.config.advanced.legacy_api}`);
433
- if (this.z2mBridge.config.advanced.legacy_availability_payload === true)
433
+ if (this.z2mBridge.config.advanced.legacy_availability_payload)
434
434
  this.log.info(`Message bridge/info advanced.legacy_availability_payload is ${this.z2mBridge.config.advanced.legacy_availability_payload}`);
435
435
  this.emit('bridge-info', this.z2mBridge);
436
436
  if (this.log.logLevel === "debug")
@@ -761,7 +761,7 @@ export class Zigbee2MQTT extends EventEmitter {
761
761
  handleResponseGroupAddMember(payload) {
762
762
  const json = this.tryJsonParse(payload.toString());
763
763
  this.log.debug(`handleResponseGroupAddMembers() add to group friendly_name ${json.data.group} device ieee_address ${json.data.device} status ${json.status}`);
764
- if (json.status === 'ok' && json.data.device && json.data.device.includes('/')) {
764
+ if (json.status === 'ok' && json.data.device?.includes('/')) {
765
765
  this.emit('group_add_member', json.data.group, json.data.device.split('/')[0], json.status);
766
766
  }
767
767
  }
@@ -801,6 +801,7 @@ export class Zigbee2MQTT extends EventEmitter {
801
801
  this.log.debug(`handleEvent() type: device_interview name: ${json.data.friendly_name} address: ${json.data.ieee_address} status: ${json.data.status} supported: ${json.data.supported}`);
802
802
  this.emit('device_interview', json.data.friendly_name, json.data.ieee_address, json.data.status, json.data.supported);
803
803
  break;
804
+ default:
804
805
  }
805
806
  }
806
807
  readConfig(filename) {
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "matterbridge-zigbee2mqtt",
3
- "version": "3.1.0",
3
+ "version": "3.2.0-dev-20260705-147850d",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-zigbee2mqtt",
9
- "version": "3.1.0",
9
+ "version": "3.2.0-dev-20260705-147850d",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "moment": "2.30.1",
13
13
  "mqtt": "5.15.1",
14
- "node-ansi-logger": "3.2.1",
15
- "node-persist-manager": "2.0.2"
14
+ "node-ansi-logger": "3.3.0",
15
+ "node-persist-manager": "2.1.0"
16
16
  },
17
17
  "engines": {
18
18
  "node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.0.0 <25.0.0 || >=26.0.0 <27.0.0"
@@ -32,18 +32,18 @@
32
32
  }
33
33
  },
34
34
  "node_modules/@types/node": {
35
- "version": "25.9.2",
36
- "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz",
37
- "integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==",
35
+ "version": "26.1.0",
36
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.0.tgz",
37
+ "integrity": "sha512-O0A1G3xPGy4w7AgQdAQYUlQ+BKk2Oovw8eRpofyp5KdBZULnbe+WqaOVNrm705SHphCiG4XHsACrSmPu1f+Kgw==",
38
38
  "license": "MIT",
39
39
  "dependencies": {
40
- "undici-types": ">=7.24.0 <7.24.7"
40
+ "undici-types": "~8.3.0"
41
41
  }
42
42
  },
43
43
  "node_modules/@types/readable-stream": {
44
- "version": "4.0.23",
45
- "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.23.tgz",
46
- "integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==",
44
+ "version": "4.0.24",
45
+ "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.24.tgz",
46
+ "integrity": "sha512-NRvUNC/JFGPJvqdAfEve8oginbM6V08u5NzLWpG8MwA2kTPOLnqk+wpwuPT+mp3aUsxyuT6m2gnrPuHYCruzEg==",
47
47
  "license": "MIT",
48
48
  "dependencies": {
49
49
  "@types/node": "*"
@@ -103,15 +103,15 @@
103
103
  }
104
104
  },
105
105
  "node_modules/broker-factory": {
106
- "version": "3.1.14",
107
- "resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.14.tgz",
108
- "integrity": "sha512-L45k5HMbPIrMid0nTOZ/UPXG/c0aRuQKVrSDFIb1zOkvfiyHgYmIjc3cSiN1KwQIvRDOtKE0tfb3I9EZ3CmpQQ==",
106
+ "version": "3.1.15",
107
+ "resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.15.tgz",
108
+ "integrity": "sha512-ko+aWvgNuP49meGrdjUu7rC+Y+Wai3cCPxP3xWwHsHfehFjOh5ZQM2yC4gEB2UddeZ/YXhm0K1eG/L6fxym2Og==",
109
109
  "license": "MIT",
110
110
  "dependencies": {
111
- "@babel/runtime": "^7.29.2",
111
+ "@babel/runtime": "^7.29.7",
112
112
  "fast-unique-numbers": "^9.0.27",
113
113
  "tslib": "^2.8.1",
114
- "worker-factory": "^7.0.49"
114
+ "worker-factory": "^7.0.50"
115
115
  }
116
116
  },
117
117
  "node_modules/buffer": {
@@ -352,13 +352,13 @@
352
352
  "license": "MIT"
353
353
  },
354
354
  "node_modules/node-ansi-logger": {
355
- "version": "3.2.1",
356
- "resolved": "https://registry.npmjs.org/node-ansi-logger/-/node-ansi-logger-3.2.1.tgz",
357
- "integrity": "sha512-R5YptDlGVfVqACSiOtfmAwV+N/ytj8crwXStrs9d9kdAXYT4xF1J1H5qfhGbXsot06OHSha76Y2Fq66sIzMq2A==",
355
+ "version": "3.3.0",
356
+ "resolved": "https://registry.npmjs.org/node-ansi-logger/-/node-ansi-logger-3.3.0.tgz",
357
+ "integrity": "sha512-ZW77lbZkA8h3r2HA2rcE5RjTIcwRDNB072RkZVyimGQpmEorJT8ajZ7UDhkHGoh0JRrCJL0iUKBE8q4oU8MHMg==",
358
358
  "hasShrinkwrap": true,
359
359
  "license": "Apache-2.0",
360
360
  "engines": {
361
- "node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.0.0 <25.0.0"
361
+ "node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.0.0 <25.0.0 || >=26.0.0 <27.0.0"
362
362
  },
363
363
  "funding": {
364
364
  "type": "buymeacoffee",
@@ -366,16 +366,16 @@
366
366
  }
367
367
  },
368
368
  "node_modules/node-persist-manager": {
369
- "version": "2.0.2",
370
- "resolved": "https://registry.npmjs.org/node-persist-manager/-/node-persist-manager-2.0.2.tgz",
371
- "integrity": "sha512-FGDdNOH4c6Xqb2LwTZ55GT0UaoqaOiWJ+hWt+O7w1+9QIUQh2NsaXPtGaTiibvCi6cX823JTFQPJK2NRseLTvw==",
369
+ "version": "2.1.0",
370
+ "resolved": "https://registry.npmjs.org/node-persist-manager/-/node-persist-manager-2.1.0.tgz",
371
+ "integrity": "sha512-LJze/Fcd8LijM4OMPnp0AQ+Z1e9mFMkjOEDw08xHA2HSRd2+N5s0uyH5ogYmXPC68f0sc4GrdKMpAcsHuO3PKA==",
372
372
  "hasShrinkwrap": true,
373
373
  "license": "Apache-2.0",
374
374
  "dependencies": {
375
375
  "node-persist": "4.0.4"
376
376
  },
377
377
  "engines": {
378
- "node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.0.0 <25.0.0"
378
+ "node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.0.0 <25.0.0 || >=26.0.0 <27.0.0"
379
379
  },
380
380
  "funding": {
381
381
  "type": "buymeacoffee",
@@ -543,9 +543,9 @@
543
543
  "license": "MIT"
544
544
  },
545
545
  "node_modules/undici-types": {
546
- "version": "7.24.6",
547
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
548
- "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
546
+ "version": "8.3.0",
547
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
548
+ "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
549
549
  "license": "MIT"
550
550
  },
551
551
  "node_modules/util-deprecate": {
@@ -555,50 +555,50 @@
555
555
  "license": "MIT"
556
556
  },
557
557
  "node_modules/worker-factory": {
558
- "version": "7.0.49",
559
- "resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.49.tgz",
560
- "integrity": "sha512-lW7tpgy6aUv2dFsQhv1yv+XFzdkCf/leoKRTGMPVK5/die6RrUjqgJHJf556qO+ZfytNG6wPXc17E8zzsOLUDw==",
558
+ "version": "7.0.50",
559
+ "resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.50.tgz",
560
+ "integrity": "sha512-hhwc0G+sFwM4qBuhJIUBn2p1Jf8v/FwmLUANBf/Q+Lt2uI8mfIZQhXaZQACodQD4R7Zp6cn/6702bIvNn2puJQ==",
561
561
  "license": "MIT",
562
562
  "dependencies": {
563
- "@babel/runtime": "^7.29.2",
563
+ "@babel/runtime": "^7.29.7",
564
564
  "fast-unique-numbers": "^9.0.27",
565
565
  "tslib": "^2.8.1"
566
566
  }
567
567
  },
568
568
  "node_modules/worker-timers": {
569
- "version": "8.0.31",
570
- "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-8.0.31.tgz",
571
- "integrity": "sha512-ngkq5S6JuZyztom8tDgBzorLo9byhBMko/sXfgiUD945AuzKGg1GCgDMCC3NaYkicLpGKXutONM36wEX8UbBCA==",
569
+ "version": "8.0.33",
570
+ "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-8.0.33.tgz",
571
+ "integrity": "sha512-RQVlKkek80v8M6SHvdMKiywaXFmX3XTpYTXTw0r62PdXB06t74XNxcTuG8wsQwnjorAQymNQyyQ0rzv7PykEMQ==",
572
572
  "license": "MIT",
573
573
  "dependencies": {
574
- "@babel/runtime": "^7.29.2",
574
+ "@babel/runtime": "^7.29.7",
575
575
  "tslib": "^2.8.1",
576
- "worker-timers-broker": "^8.0.16",
577
- "worker-timers-worker": "^9.0.14"
576
+ "worker-timers-broker": "^8.0.18",
577
+ "worker-timers-worker": "^9.0.15"
578
578
  }
579
579
  },
580
580
  "node_modules/worker-timers-broker": {
581
- "version": "8.0.16",
582
- "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-8.0.16.tgz",
583
- "integrity": "sha512-JyP3AvUGyPGbBGW7XiUewm2+0pN/aYo1QpVf5kdXAfkDZcN3p7NbWrG6XnyDEpDIvfHk/+LCnOW/NsuiU9riYA==",
581
+ "version": "8.0.18",
582
+ "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-8.0.18.tgz",
583
+ "integrity": "sha512-FrjzDVX1wKfZN0gRbCFqv8VHuTncG4sbI/WGEg4tSSQeIsnwqg4YBYWMAHYLJtUDEYYmiK65UKFrVMVk2irDSg==",
584
584
  "license": "MIT",
585
585
  "dependencies": {
586
- "@babel/runtime": "^7.29.2",
587
- "broker-factory": "^3.1.14",
586
+ "@babel/runtime": "^7.29.7",
587
+ "broker-factory": "^3.1.15",
588
588
  "fast-unique-numbers": "^9.0.27",
589
589
  "tslib": "^2.8.1",
590
- "worker-timers-worker": "^9.0.14"
590
+ "worker-timers-worker": "^9.0.15"
591
591
  }
592
592
  },
593
593
  "node_modules/worker-timers-worker": {
594
- "version": "9.0.14",
595
- "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-9.0.14.tgz",
596
- "integrity": "sha512-/qF06C60sXmSLfUl7WglvrDIbspmPOM8UrG63Dnn4bi2x4/DfqHS/+dxF5B+MdHnYO5tVuZYLHdAodrKdabTIg==",
594
+ "version": "9.0.15",
595
+ "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-9.0.15.tgz",
596
+ "integrity": "sha512-KKUe7lZ/Aignr51H6hOUik8LwTnIgojH/1lwhli8A8qIEIyewogZTpNpMW5B6BF7nmwBOkUoTYgf1H3QShcjSA==",
597
597
  "license": "MIT",
598
598
  "dependencies": {
599
- "@babel/runtime": "^7.29.2",
599
+ "@babel/runtime": "^7.29.7",
600
600
  "tslib": "^2.8.1",
601
- "worker-factory": "^7.0.49"
601
+ "worker-factory": "^7.0.50"
602
602
  }
603
603
  },
604
604
  "node_modules/ws": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-zigbee2mqtt",
3
- "version": "3.1.0",
3
+ "version": "3.2.0-dev-20260705-147850d",
4
4
  "description": "Matterbridge zigbee2mqtt plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",
@@ -68,7 +68,7 @@
68
68
  "dependencies": {
69
69
  "moment": "2.30.1",
70
70
  "mqtt": "5.15.1",
71
- "node-ansi-logger": "3.2.1",
72
- "node-persist-manager": "2.0.2"
71
+ "node-ansi-logger": "3.3.0",
72
+ "node-persist-manager": "2.1.0"
73
73
  }
74
74
  }