matterbridge-zigbee2mqtt 2.5.0-dev.3 → 2.5.0-dev.5

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
@@ -17,7 +17,7 @@ New device types:
17
17
  - rainSensor
18
18
  - smokeSensor
19
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.
20
+ If your controller has issues detecting the new device type, blacklist these devices, restart, wait 5 minutes that the controller removes them, remove the blacklist and restart again. This will create a new endpoint on the controller and the controllers will likely remove and recreate all the devices so make a backup of configurations (i.e. room assignements) and automations on the controller.
21
21
 
22
22
  ## [2.5.0] - 2025-05-23
23
23
 
@@ -27,10 +27,11 @@ If your controller has issues detecting the new device type, blacklist these dev
27
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
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
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 rgb devices that don't support color temperature.
30
+ - [colorTemp]: Added conversion from color temperature to rgb for the rgb devices that don't support color_temp.
31
31
  - [battery]: Set batChargeLevel to warning if battery is less than 40% and the device doesn't expose battery_low.
32
32
  - [battery]: Set batChargeLevel to critical if battery is less than 20% and the device doesn't expose battery_low.
33
- - [retain]: Send retained mqtt states at startup if z2m has retain enabled.
33
+ - [retain]: Send retained mqtt states at startup if z2m has retain enabled. See the README.md for explanations.
34
+ - [logger]: Added onChangeLoggerLevel() to the platform.
34
35
 
35
36
  ### Changed
36
37
 
@@ -39,6 +40,12 @@ If your controller has issues detecting the new device type, blacklist these dev
39
40
  - [plugin]: Requires Matterbridge 3.0.3.
40
41
  - [config]: As anticipated in the previous release, the parameter postfixHostname has been removed. Use postfix if needed.
41
42
  - [colorRgb]: Changed the default device type from colorTemperatureLight to extendedColorLight to solve the SmartThings issue with colors.
43
+ - [colorTemp]: The min and max mired values for color_temp are now set in the cluster.
44
+
45
+ ### Fixed
46
+
47
+ - [logger]: Fixed logger not always taking the correct value from the frontend.
48
+ - [issue104]: Solved wrong mode AUTO in system_mode for HEAT only devices.
42
49
 
43
50
  <a href="https://www.buymeacoffee.com/luligugithub">
44
51
  <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
package/README.md CHANGED
@@ -151,7 +151,7 @@ These are the default vules:
151
151
  "featureBlackList": [],
152
152
  "deviceFeatureBlackList": {},
153
153
  "scenesType": "outlet",
154
- "scenesPrefix: true,
154
+ "scenesPrefix": true,
155
155
  "postfix": ""
156
156
  }
157
157
  ```
@@ -180,19 +180,7 @@ If you want to exclude "temperature" and "humidity" for the device "My motion se
180
180
  }
181
181
  ```
182
182
 
183
- The parameter postfixHostname is deprecated and will be removed in the next release. Use postfix instead if needed.
184
- By default matterbridge-zigbee2mqtt uses hostname in order to make entities unique, however in some cases
185
- you may not want this behavior. You can use "postfixHostname" boolean flag to disable this behavior:
186
-
187
- ```json
188
- {
189
- ...
190
- "postfixHostname": false
191
- ...
192
- }
193
- ```
194
-
195
- From the release 1.2.14 of Matterbridge you can edit the config file directly in the frontend.
183
+ From the release 1.2.14 of Matterbridge you can edit the config file directly in the frontend. I strongly suggest you use the integrated config editor.
196
184
 
197
185
  You can edit the config file manually if you prefer:
198
186
 
@@ -218,19 +206,46 @@ nano matterbridge-zigbee2mqtt.config.json
218
206
 
219
207
  ## What is supported?
220
208
 
221
- Out of the box, this plugin supports all possible conversion from zigbee2mqtt to Matter 1.1.
209
+ Out of the box, this plugin supports all possible conversion from zigbee2mqtt to Matter 1.4.
210
+
211
+ It also supports all clusters in the multi endpoints devices (e.g. DIY devices or the double channel switches/dimmers).
222
212
 
223
- The latest release also supports all clusters in the multi endpoints devices (e.g. DIY devices or the double channel switches/dimmers).
213
+ Since the Matter support in the available ecosystems (controllers) is sometimes limited and, when available, only covers Matter 1.2 specifications, some z2m devices cannot be exposed properly or cannot be exposed at all.
224
214
 
225
- Since the Matter support in the available ecosystems (controllers) is sometimes limited and, when available, only covers Matter 1.1 specifications, some z2m devices cannot be exposed properly or cannot be exposed at all.
215
+ ## Scenes in groups and devices
216
+
217
+ With release 2.5.0 has been added support for scenes in groups and devices.
218
+
219
+ In the config select what device type you want to use to expose the command that runs the scene: 'light' | 'outlet' | 'switch' | 'mounted_switch'.
220
+
221
+ Switch is not supported by Alexa. Mounted Switch is not supported by Apple Home.
222
+
223
+ 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. Consider also that each scene name must by unique if scenesPrefix is disabled.
224
+
225
+ The state of the virtual device is always reverted to off in a few seconds.
226
+
227
+ It is possibile to disable the feature globally with featureBlackList (add "scenes" to the list) and on a per device/group base with deviceFeatureBlackList (add "scenes" to the list).
226
228
 
227
229
  ## Availability
228
230
 
229
- If the availability is enabled in zigbee2mqtt settings, it is exposed.
231
+ If the availability is enabled in zigbee2mqtt settings, it is used to set the corresponding device/group reachable or not.
232
+
233
+ [Screenshot](https://github.com/user-attachments/assets/7e0d395f-19e4-4e7f-b263-0cae3df70be4)
230
234
 
231
235
  ## Retain
232
236
 
233
- If the retain option is enabled in zigbee2mqtt settings or device setting, at restart all retained states are updated.
237
+ If the retain option is enabled in zigbee2mqtt settings or device setting, at restart all retained states are updated. I suggest to use this option expecially for battery powered devices.
238
+
239
+ To enable retain globally, stop zigbee2mqtt, add retain: true to device_options and restart zigbee2mqtt.
240
+
241
+ ```
242
+ device_options:
243
+ retain: true
244
+ ```
245
+
246
+ To enable retain for a single device set it in the device settings.
247
+
248
+ [Screenshot](https://github.com/user-attachments/assets/5ae09f2a-6cff-4623-92f4-87f7721ee443)
234
249
 
235
250
  ## Unsupported devices
236
251
 
@@ -259,20 +274,6 @@ If one of your devices is not supported out of the box, open an issue and we wil
259
274
 
260
275
  ![See the screenshot here](https://github.com/Luligu/matterbridge-zigbee2mqtt/blob/main/screenshot/Smart%20button.png)
261
276
 
262
- ## Scenes in groups and devices
263
-
264
- With release 2.5.0 has been added support for scenes in groups and devices.
265
-
266
- In the config select what device type you want to use to expose the command that runs the scene: 'light' | 'outlet' | 'switch' | 'mounted_switch'.
267
-
268
- Switch is not supported by Alexa. Mounted Switch is not supported by Apple Home.
269
-
270
- 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.
271
-
272
- The state of the virtual device is always reverted to off in a few seconds.
273
-
274
- It is possibile to disable the feature globally with featureBlackList and on a per device/group base with deviceFeatureBlackList.
275
-
276
277
  # Known issues
277
278
 
278
279
  For general controller issues check the Matterbridge Known issues section
@@ -287,6 +288,6 @@ For general controller issues check the Matterbridge Known issues section
287
288
 
288
289
  ## Alexa
289
290
 
290
- In the plugin config add each switch device to the lightList or outletList. Matterbridge uses a modified switch device type without client cluster that Alexa doesn't recognize.
291
+ In the plugin config add each switch device to the lightList or outletList. Matterbridge uses a switch device type without client cluster that Alexa doesn't recognize.
291
292
 
292
293
  ## SmartThings
package/dist/entity.js CHANGED
@@ -50,7 +50,7 @@ export class ZigbeeEntity extends EventEmitter {
50
50
  this.en = gn;
51
51
  this.ien = ign;
52
52
  }
53
- this.log = new AnsiLogger({ logName: this.entityName, logTimestampFormat: 4, logLevel: platform.debugEnabled ? "debug" : "info" });
53
+ this.log = new AnsiLogger({ logName: this.entityName, logTimestampFormat: 4, logLevel: platform.debugEnabled ? "debug" : platform.log.logLevel });
54
54
  this.log.debug(`Created MatterEntity: ${this.entityName}`);
55
55
  this.platform.z2m.on('MESSAGE-' + this.entityName, (payload) => {
56
56
  const now = Date.now();
@@ -106,6 +106,10 @@ export class ZigbeeEntity extends EventEmitter {
106
106
  if (!z2m)
107
107
  z2m = z2ms.find((z2m) => z2m.property === propertyMap?.name);
108
108
  if (z2m) {
109
+ if (z2m.valueLookup && propertyMap.values && propertyMap.values !== '' && typeof value === 'string' && !propertyMap.values.includes(value)) {
110
+ this.log.debug(`*Payload entry ${CYAN}${key}${db} value ${CYAN}${value}${db} not found in propertyMap values ${CYAN}${propertyMap.values}${db}`);
111
+ return;
112
+ }
109
113
  if (z2m.converter || z2m.valueLookup) {
110
114
  this.updateAttributeIfChanged(this.bridgedDevice, propertyMap === undefined || propertyMap.endpoint === '' ? undefined : propertyMap.endpoint, z2m.cluster, z2m.attribute, z2m.converter ? z2m.converter(value) : value, z2m.valueLookup);
111
115
  return;
@@ -154,7 +158,8 @@ export class ZigbeeEntity extends EventEmitter {
154
158
  }
155
159
  if (key === 'color_temp' && 'color_mode' in payload && payload['color_mode'] === 'color_temp') {
156
160
  this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'colorMode', ColorControl.ColorMode.ColorTemperatureMireds);
157
- this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'colorTemperatureMireds', Math.max(147, Math.min(500, typeof value === 'number' ? value : 0)));
161
+ const colorTemp = this.propertyMap.get('color_temp');
162
+ this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'colorTemperatureMireds', Math.max(colorTemp?.value_min ?? 147, Math.min(colorTemp?.value_max ?? 500, typeof value === 'number' ? value : 0)));
158
163
  }
159
164
  if (key === 'color' && 'color_mode' in payload && payload['color_mode'] === 'hs') {
160
165
  const { hue, saturation } = value;
@@ -212,15 +217,13 @@ export class ZigbeeEntity extends EventEmitter {
212
217
  const hardwareVersionString = this.platform.matterbridge.matterbridgeVersion || 'unknown';
213
218
  if (this.isDevice && this.device && this.device.friendly_name === 'Coordinator') {
214
219
  this.bridgedDevice.createDefaultBridgedDeviceBasicInformationClusterServer(this.device.friendly_name, this.serial, 0xfff1, 'zigbee2MQTT', 'Coordinator', softwareVersion, softwareVersionString, hardwareVersion, hardwareVersionString);
215
- return this.bridgedDevice;
216
220
  }
217
221
  else if (this.isDevice && this.device) {
218
222
  this.bridgedDevice.createDefaultBridgedDeviceBasicInformationClusterServer(this.device.friendly_name, this.serial, 0xfff1, this.device.definition ? this.device.definition.vendor : this.device.manufacturer, this.device.definition ? this.device.definition.model : this.device.model_id, softwareVersion, softwareVersionString, hardwareVersion, hardwareVersionString);
219
- return this.bridgedDevice;
220
223
  }
221
- if (!this.group)
222
- throw new Error('No group found');
223
- this.bridgedDevice.createDefaultBridgedDeviceBasicInformationClusterServer(this.group.friendly_name, this.serial, 0xfff1, 'zigbee2MQTT', 'Group', softwareVersion, softwareVersionString, hardwareVersion, hardwareVersionString);
224
+ else if (this.isGroup && this.group) {
225
+ this.bridgedDevice.createDefaultBridgedDeviceBasicInformationClusterServer(this.group.friendly_name, this.serial, 0xfff1, 'zigbee2MQTT', 'Group', softwareVersion, softwareVersionString, hardwareVersion, hardwareVersionString);
226
+ }
224
227
  return this.bridgedDevice;
225
228
  }
226
229
  addPowerSource() {
@@ -277,6 +280,12 @@ export class ZigbeeEntity extends EventEmitter {
277
280
  this.bridgedDevice?.triggerEvent(DoorLock.Cluster.id, 'lockOperation', { lockOperationType: DoorLock.LockOperationType.Unlock, operationSource: DoorLock.OperationSource.Manual, userIndex: null, fabricIndex: null, sourceNode: null }, this.log);
278
281
  }
279
282
  }
283
+ if (this.bridgedDevice?.hasClusterServer(ColorControl.Cluster.id)) {
284
+ this.log.info(`Configuring ${this.bridgedDevice?.deviceName} ColorControl cluster`);
285
+ const colorTemp = this.propertyMap.get('color_temp');
286
+ this.bridgedDevice?.setAttribute(ColorControl.Cluster.id, 'colorTempPhysicalMinMireds', colorTemp?.value_min ?? 147, this.log);
287
+ this.bridgedDevice?.setAttribute(ColorControl.Cluster.id, 'colorTempPhysicalMaxMireds', colorTemp?.value_max ?? 500, this.log);
288
+ }
280
289
  }
281
290
  updateAttributeIfChanged(deviceEndpoint, childEndpointName, clusterId, attributeName, value, lookup) {
282
291
  if (value === undefined)
@@ -987,11 +996,11 @@ export class ZigbeeDevice extends ZigbeeEntity {
987
996
  if (mainEndpoint.clusterServersIds.includes(ColorControl.Cluster.id)) {
988
997
  if (!names.includes('color_hs') && !names.includes('color_xy')) {
989
998
  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}`);
990
- zigbeeDevice.bridgedDevice.createCtColorControlClusterServer(zigbeeDevice.propertyMap.get('color_temp')?.value_max, zigbeeDevice.propertyMap.get('color_temp')?.value_min, zigbeeDevice.propertyMap.get('color_temp')?.value_max);
999
+ zigbeeDevice.bridgedDevice.createCtColorControlClusterServer();
991
1000
  }
992
1001
  else {
993
1002
  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}`);
994
- 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);
1003
+ zigbeeDevice.bridgedDevice.createDefaultColorControlClusterServer();
995
1004
  }
996
1005
  mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(ColorControl.Cluster.id), 1);
997
1006
  }
@@ -1011,11 +1020,17 @@ export class ZigbeeDevice extends ZigbeeEntity {
1011
1020
  zigbeeDevice.propertyMap.delete('running_state');
1012
1021
  zigbeeDevice.bridgedDevice.createDefaultHeatingThermostatClusterServer(undefined, undefined, minHeating, maxHeating);
1013
1022
  mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(Thermostat.Cluster.id), 1);
1023
+ const sMode = zigbeeDevice.propertyMap.get('system_mode');
1024
+ if (sMode)
1025
+ sMode.values = 'off|heat';
1014
1026
  }
1015
1027
  else if ((!heat && cool) || (!system_mode_values?.includes('auto') && system_mode_values?.includes('cool'))) {
1016
1028
  zigbeeDevice.propertyMap.delete('running_state');
1017
1029
  zigbeeDevice.bridgedDevice.createDefaultCoolingThermostatClusterServer(undefined, undefined, minCooling, maxCooling);
1018
1030
  mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(Thermostat.Cluster.id), 1);
1031
+ const sMode = zigbeeDevice.propertyMap.get('system_mode');
1032
+ if (sMode)
1033
+ sMode.values = 'off|cool';
1019
1034
  }
1020
1035
  else {
1021
1036
  zigbeeDevice.bridgedDevice.createDefaultThermostatClusterServer(undefined, undefined, undefined, undefined, minHeating, maxHeating, minCooling, maxCooling);
package/dist/platform.js CHANGED
@@ -358,12 +358,14 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
358
358
  await this.requestGroupUpdate(bridgedEntity.group);
359
359
  }
360
360
  this.availabilityTimer = setTimeout(() => {
361
+ this.log.info(`Setting availability for ${this.z2mEntityAvailability.size} entities`);
361
362
  for (const [entity, available] of this.z2mEntityAvailability) {
362
363
  if (available)
363
364
  this.z2m.emit('ONLINE-' + entity);
364
365
  else
365
366
  this.z2m.emit('OFFLINE-' + entity);
366
367
  }
368
+ this.log.info(`Setting retained values for ${this.z2mEntityPayload.size} entities`);
367
369
  for (const [entity, payload] of this.z2mEntityPayload) {
368
370
  this.z2m.emit('MESSAGE-' + entity, payload);
369
371
  }
@@ -379,6 +381,18 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
379
381
  }
380
382
  this.log.info(`Configured zigbee2mqtt dynamic platform v${this.version}`);
381
383
  }
384
+ async onChangeLoggerLevel(logLevel) {
385
+ this.log.info(`Configuring zigbee2mqtt platform logger level to ${CYAN}${logLevel}${nf}`);
386
+ this.log.logLevel = logLevel;
387
+ this.z2m.setLogLevel(logLevel);
388
+ for (const bridgedDevice of this.bridgedDevices) {
389
+ bridgedDevice.log.logLevel = logLevel;
390
+ }
391
+ for (const entity of this.zigbeeEntities) {
392
+ entity.log.logLevel = logLevel;
393
+ }
394
+ this.log.debug('Changed logger level to ' + logLevel);
395
+ }
382
396
  async onShutdown(reason) {
383
397
  await super.onShutdown(reason);
384
398
  this.z2m.removeAllListeners();
@@ -65,13 +65,14 @@ export class Zigbee2MQTT extends EventEmitter {
65
65
  setLogDebug(logDebug) {
66
66
  this.log.logLevel = logDebug ? "debug" : "info";
67
67
  }
68
+ setLogLevel(logLevel) {
69
+ this.log.logLevel = logLevel;
70
+ }
68
71
  async setDataPath(dataPath) {
69
72
  try {
70
73
  await mkdir(dataPath, { recursive: true });
71
74
  this.mqttDataPath = dataPath;
72
75
  this.log.debug(`Data directory ${this.mqttDataPath} created successfully.`);
73
- const filePath = path.join(this.mqttDataPath, 'bridge-payloads.txt');
74
- fs.unlinkSync(filePath);
75
76
  }
76
77
  catch (e) {
77
78
  const error = e;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge-zigbee2mqtt",
3
- "version": "2.5.0-dev.3",
3
+ "version": "2.5.0-dev.5",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-zigbee2mqtt",
9
- "version": "2.5.0-dev.3",
9
+ "version": "2.5.0-dev.5",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "moment": "2.30.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-zigbee2mqtt",
3
- "version": "2.5.0-dev.3",
3
+ "version": "2.5.0-dev.5",
4
4
  "description": "Matterbridge zigbee2mqtt plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",