matterbridge-zigbee2mqtt 2.4.7 → 2.5.0-dev.3
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 +36 -0
- package/README.md +26 -3
- package/dist/entity.js +110 -85
- package/dist/platform.js +54 -21
- package/dist/zigbee2mqtt.js +10 -0
- package/matterbridge-zigbee2mqtt.schema.json +26 -15
- package/npm-shrinkwrap.json +93 -77
- package/package.json +2 -2
- package/tsconfig.jest.json +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,42 @@ 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-23
|
|
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 rgb devices that don't support color temperature.
|
|
31
|
+
- [battery]: Set batChargeLevel to warning if battery is less than 40% and the device doesn't expose battery_low.
|
|
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.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- [package]: Updated package.
|
|
38
|
+
- [package]: Updated dependencies.
|
|
39
|
+
- [plugin]: Requires Matterbridge 3.0.3.
|
|
40
|
+
- [config]: As anticipated in the previous release, the parameter postfixHostname has been removed. Use postfix if needed.
|
|
41
|
+
- [colorRgb]: Changed the default device type from colorTemperatureLight to extendedColorLight to solve the SmartThings issue with colors.
|
|
42
|
+
|
|
43
|
+
<a href="https://www.buymeacoffee.com/luligugithub">
|
|
44
|
+
<img src="bmc-button.svg" alt="Buy me a coffee" width="80">
|
|
45
|
+
</a>
|
|
46
|
+
|
|
11
47
|
## [2.4.7] - 2025-03-19
|
|
12
48
|
|
|
13
49
|
### 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
|
|
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
|
-
"
|
|
154
|
-
"
|
|
153
|
+
"scenesType": "outlet",
|
|
154
|
+
"scenesPrefix: true,
|
|
155
|
+
"postfix": ""
|
|
155
156
|
}
|
|
156
157
|
```
|
|
157
158
|
|
|
@@ -223,6 +224,14 @@ The latest release also supports all clusters in the multi endpoints devices (e.
|
|
|
223
224
|
|
|
224
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.
|
|
225
226
|
|
|
227
|
+
## Availability
|
|
228
|
+
|
|
229
|
+
If the availability is enabled in zigbee2mqtt settings, it is exposed.
|
|
230
|
+
|
|
231
|
+
## Retain
|
|
232
|
+
|
|
233
|
+
If the retain option is enabled in zigbee2mqtt settings or device setting, at restart all retained states are updated.
|
|
234
|
+
|
|
226
235
|
## Unsupported devices
|
|
227
236
|
|
|
228
237
|
If one of your devices is not supported out of the box, open an issue and we will try to support it if possible.
|
|
@@ -250,6 +259,20 @@ If one of your devices is not supported out of the box, open an issue and we wil
|
|
|
250
259
|
|
|
251
260
|

|
|
252
261
|
|
|
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
|
+
|
|
253
276
|
# Known issues
|
|
254
277
|
|
|
255
278
|
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 = '';
|
|
@@ -87,6 +86,17 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
87
86
|
return;
|
|
88
87
|
if (key === 'voltage' && this.isDevice && this.device?.power_source === 'Battery')
|
|
89
88
|
key = 'battery_voltage';
|
|
89
|
+
if (key === 'battery' && !('battery_low' in payload) && isValidNumber(value, 0, 100) && this.isDevice && this.device?.power_source === 'Battery') {
|
|
90
|
+
if (value < 20) {
|
|
91
|
+
this.updateAttributeIfChanged(this.bridgedDevice, undefined, PowerSource.Cluster.id, 'batChargeLevel', PowerSource.BatChargeLevel.Critical);
|
|
92
|
+
}
|
|
93
|
+
else if (value < 40) {
|
|
94
|
+
this.updateAttributeIfChanged(this.bridgedDevice, undefined, PowerSource.Cluster.id, 'batChargeLevel', PowerSource.BatChargeLevel.Warning);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this.updateAttributeIfChanged(this.bridgedDevice, undefined, PowerSource.Cluster.id, 'batChargeLevel', PowerSource.BatChargeLevel.Ok);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
90
100
|
const propertyMap = this.propertyMap.get(key);
|
|
91
101
|
if (propertyMap) {
|
|
92
102
|
this.log.debug(`Payload entry ${CYAN}${key}${db} => name: ${CYAN}${propertyMap.name}${db} type: ${CYAN}${propertyMap.type === '' ? 'generic' : propertyMap.type}${db} ` +
|
|
@@ -110,7 +120,7 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
110
120
|
const propertyMap = this.propertyMap.get(('action_' + value));
|
|
111
121
|
if (propertyMap) {
|
|
112
122
|
const child = this.bridgedDevice.getChildEndpointByName(propertyMap.endpoint);
|
|
113
|
-
if (child && child.
|
|
123
|
+
if (child && child.maybeNumber)
|
|
114
124
|
child.triggerSwitchEvent(propertyMap.action, this.log);
|
|
115
125
|
}
|
|
116
126
|
else
|
|
@@ -294,11 +304,11 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
294
304
|
}
|
|
295
305
|
const localValue = deviceEndpoint.getAttribute(ClusterId(clusterId), attributeName, undefined);
|
|
296
306
|
if (typeof value === 'object' ? deepEqual(value, localValue) : value === localValue) {
|
|
297
|
-
this.log.debug(`Skip update endpoint ${deviceEndpoint.name}:${deviceEndpoint.
|
|
307
|
+
this.log.debug(`Skip update endpoint ${deviceEndpoint.name}:${deviceEndpoint.maybeNumber}${childEndpointName ? ' (' + childEndpointName + ')' : ''} ` +
|
|
298
308
|
`attribute ${getClusterNameById(ClusterId(clusterId))}.${attributeName} already ${typeof value === 'object' ? debugStringify(value) : value}`);
|
|
299
309
|
return;
|
|
300
310
|
}
|
|
301
|
-
this.log.info(`${db}Update endpoint ${this.eidn}${deviceEndpoint.name}:${deviceEndpoint.
|
|
311
|
+
this.log.info(`${db}Update endpoint ${this.eidn}${deviceEndpoint.name}:${deviceEndpoint.maybeNumber}${db}${childEndpointName ? ' (' + zb + childEndpointName + db + ')' : ''} ` +
|
|
302
312
|
`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
313
|
try {
|
|
304
314
|
deviceEndpoint.setAttribute(ClusterId(clusterId), attributeName, value);
|
|
@@ -330,10 +340,7 @@ export class ZigbeeGroup extends ZigbeeEntity {
|
|
|
330
340
|
}
|
|
331
341
|
static async create(platform, group) {
|
|
332
342
|
const zigbeeGroup = new ZigbeeGroup(platform, group);
|
|
333
|
-
if (zigbeeGroup.platform.
|
|
334
|
-
zigbeeGroup.serial = `group-${group.id}_${hostname}`.slice(0, 32);
|
|
335
|
-
}
|
|
336
|
-
else if (zigbeeGroup.platform.postfix !== '') {
|
|
343
|
+
if (zigbeeGroup.platform.postfix !== '') {
|
|
337
344
|
zigbeeGroup.serial = `group-${group.id}-${zigbeeGroup.platform.postfix}`.slice(0, 32);
|
|
338
345
|
}
|
|
339
346
|
else {
|
|
@@ -419,7 +426,7 @@ export class ZigbeeGroup extends ZigbeeEntity {
|
|
|
419
426
|
zigbeeGroup.propertyMap.set('color_temp', { name: 'color_temp', type: 'light', endpoint: '' });
|
|
420
427
|
}
|
|
421
428
|
if (useColor) {
|
|
422
|
-
deviceType =
|
|
429
|
+
deviceType = extendedColorLight;
|
|
423
430
|
zigbeeGroup.propertyMap.set('color', { name: 'color', type: 'light', endpoint: '' });
|
|
424
431
|
}
|
|
425
432
|
if (isCover) {
|
|
@@ -440,9 +447,14 @@ export class ZigbeeGroup extends ZigbeeEntity {
|
|
|
440
447
|
return zigbeeGroup;
|
|
441
448
|
zigbeeGroup.bridgedDevice = new MatterbridgeEndpoint([deviceType, bridgedNode, powerSource], { uniqueStorageKey: group.friendly_name }, zigbeeGroup.log.logLevel === "debug");
|
|
442
449
|
}
|
|
443
|
-
if (platform.
|
|
450
|
+
if (!platform.featureBlackList?.includes('scenes') && !platform.deviceFeatureBlackList[group.friendly_name]?.includes('scenes')) {
|
|
444
451
|
group.scenes.forEach((scene) => {
|
|
445
452
|
zigbeeGroup.log.debug(`***Group ${gn}${group.friendly_name}${rs}${db} scene ${CYAN}${scene.name}${db} id ${CYAN}${scene.id}${db}`);
|
|
453
|
+
platform.setSelectDeviceEntity(`group-${group.id}`, 'scenes', 'Scenes', 'component');
|
|
454
|
+
platform.registerVirtualDevice(`${platform.config.scenesPrefix ? group.friendly_name + ' ' : ''}${scene.name}`, async () => {
|
|
455
|
+
zigbeeGroup.log.info(`Triggered scene "${scene.name}" id ${scene.id} from group ${group.friendly_name}`);
|
|
456
|
+
zigbeeGroup.publishCommand('scene_recall', group.friendly_name, { 'scene_recall': scene.id });
|
|
457
|
+
});
|
|
446
458
|
});
|
|
447
459
|
}
|
|
448
460
|
zigbeeGroup.addBridgedDeviceBasicInformation();
|
|
@@ -458,7 +470,7 @@ export class ZigbeeGroup extends ZigbeeEntity {
|
|
|
458
470
|
if (isLight)
|
|
459
471
|
await zigbeeGroup.bridgedDevice.addFixedLabel('type', 'light');
|
|
460
472
|
zigbeeGroup.bridgedDevice.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
|
|
461
|
-
zigbeeGroup.log.
|
|
473
|
+
zigbeeGroup.log.debug(`Command identify called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} identifyTime:${identifyTime}`);
|
|
462
474
|
});
|
|
463
475
|
zigbeeGroup.bridgedDevice.addCommandHandler('on', async () => {
|
|
464
476
|
zigbeeGroup.log.debug(`Command on called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db}`);
|
|
@@ -520,6 +532,14 @@ export class ZigbeeGroup extends ZigbeeEntity {
|
|
|
520
532
|
const rgb = color.hslColorToRgbColor((request.hue / 254) * 360, (request.saturation / 254) * 100, 50);
|
|
521
533
|
zigbeeGroup.publishCommand('moveToHueAndSaturation', group.friendly_name, { color: { r: rgb.r, g: rgb.g, b: rgb.b } });
|
|
522
534
|
});
|
|
535
|
+
zigbeeGroup.bridgedDevice.addCommandHandler('moveToColor', async ({ request }) => {
|
|
536
|
+
zigbeeGroup.log.debug(`Command moveToColor called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} request: X: ${request.colorX} Y: ${request.colorY}`);
|
|
537
|
+
await zigbeeGroup.bridgedDevice?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentXAndCurrentY, zigbeeGroup.log);
|
|
538
|
+
const payload = { color: { x: request.colorX / 65536, y: request.colorY / 65536 } };
|
|
539
|
+
if (zigbeeGroup.transition && request.transitionTime && request.transitionTime / 10 >= 1)
|
|
540
|
+
payload['transition'] = Math.round(request.transitionTime / 10);
|
|
541
|
+
zigbeeGroup.publishCommand('moveToColor', group.friendly_name, payload);
|
|
542
|
+
});
|
|
523
543
|
}
|
|
524
544
|
}
|
|
525
545
|
if (isCover) {
|
|
@@ -600,8 +620,8 @@ export const z2ms = [
|
|
|
600
620
|
{ 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
621
|
{ type: 'light', name: 'state', property: 'state', deviceType: onOffLight, cluster: OnOff.Cluster.id, attribute: 'onOff', converter: (value) => { return value === 'ON' ? true : false; } },
|
|
602
622
|
{ 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:
|
|
604
|
-
{ type: 'light', name: 'color_xy', property: 'color_xy', deviceType:
|
|
623
|
+
{ type: 'light', name: 'color_hs', property: 'color_hs', deviceType: extendedColorLight, cluster: ColorControl.Cluster.id, attribute: 'colorMode' },
|
|
624
|
+
{ type: 'light', name: 'color_xy', property: 'color_xy', deviceType: extendedColorLight, cluster: ColorControl.Cluster.id, attribute: 'colorMode' },
|
|
605
625
|
{ type: 'light', name: 'color_temp', property: 'color_temp', deviceType: colorTemperatureLight, cluster: ColorControl.Cluster.id, attribute: 'colorMode' },
|
|
606
626
|
{ type: 'cover', name: 'state', property: 'state', deviceType: coverDevice, cluster: WindowCovering.Cluster.id, attribute: 'targetPositionLiftPercent100ths' },
|
|
607
627
|
{ type: 'cover', name: 'moving', property: 'moving', deviceType: coverDevice, cluster: WindowCovering.Cluster.id, attribute: 'operationalStatus' },
|
|
@@ -623,9 +643,10 @@ export const z2ms = [
|
|
|
623
643
|
{ type: '', name: 'occupancy', property: 'occupancy', deviceType: occupancySensor, cluster: OccupancySensing.Cluster.id, attribute: 'occupancy', converter: (value) => { return { occupied: value }; } },
|
|
624
644
|
{ 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
645
|
{ 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:
|
|
646
|
+
{ type: '', name: 'water_leak', property: 'water_leak', deviceType: waterLeakDetector, cluster: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => { return value; } },
|
|
647
|
+
{ type: '', name: 'rain', property: 'rain', deviceType: rainSensor, cluster: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => { return value; } },
|
|
627
648
|
{ type: '', name: 'vibration', property: 'vibration', deviceType: contactSensor, cluster: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => { return !value; } },
|
|
628
|
-
{ type: '', name: 'smoke', property: 'smoke', deviceType:
|
|
649
|
+
{ type: '', name: 'smoke', property: 'smoke', deviceType: smokeCoAlarm, cluster: SmokeCoAlarm.Cluster.id, attribute: 'smokeState', converter: (value) => { return value ? SmokeCoAlarm.AlarmState.Critical : SmokeCoAlarm.AlarmState.Normal; } },
|
|
629
650
|
{ type: '', name: 'carbon_monoxide', property: 'carbon_monoxide', deviceType: contactSensor, cluster: BooleanState.Cluster.id, attribute: 'stateValue', converter: (value) => { return !value; } },
|
|
630
651
|
{ type: '', name: 'temperature', property: 'temperature', deviceType: temperatureSensor, cluster: TemperatureMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value) => { return Math.round(value * 100); } },
|
|
631
652
|
{ type: '', name: 'humidity', property: 'humidity', deviceType: humiditySensor, cluster: RelativeHumidityMeasurement.Cluster.id, attribute: 'measuredValue', converter: (value) => { return Math.round(value * 100); } },
|
|
@@ -644,10 +665,10 @@ export const z2ms = [
|
|
|
644
665
|
{ type: '', name: '', property: 'battery', deviceType: powerSource, cluster: PowerSource.Cluster.id, attribute: 'batPercentRemaining', converter: (value) => { return Math.round(value * 2); } },
|
|
645
666
|
{ 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
667
|
{ 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; } },
|
|
668
|
+
{ type: '', name: 'energy', property: 'energy', deviceType: electricalSensor, cluster: ElectricalEnergyMeasurement.Cluster.id, attribute: 'cumulativeEnergyImported', converter: (value) => { return { energy: Math.round(value * 1000000) }; } },
|
|
669
|
+
{ type: '', name: 'power', property: 'power', deviceType: electricalSensor, cluster: ElectricalPowerMeasurement.Cluster.id, attribute: 'activePower', converter: (value) => { return Math.round(value * 1000); } },
|
|
670
|
+
{ type: '', name: 'voltage', property: 'voltage', deviceType: electricalSensor, cluster: ElectricalPowerMeasurement.Cluster.id, attribute: 'voltage', converter: (value) => { return Math.round(value * 1000); } },
|
|
671
|
+
{ type: '', name: 'current', property: 'current', deviceType: electricalSensor, cluster: ElectricalPowerMeasurement.Cluster.id, attribute: 'activeCurrent', converter: (value) => { return Math.round(value * 1000); } },
|
|
651
672
|
];
|
|
652
673
|
export class ZigbeeDevice extends ZigbeeEntity {
|
|
653
674
|
constructor(platform, device) {
|
|
@@ -656,37 +677,42 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
656
677
|
static async create(platform, device) {
|
|
657
678
|
const zigbeeDevice = new ZigbeeDevice(platform, device);
|
|
658
679
|
zigbeeDevice.serial = `${device.ieee_address}`;
|
|
659
|
-
if (zigbeeDevice.platform.
|
|
660
|
-
zigbeeDevice.serial = `${zigbeeDevice.serial}_${hostname}`.slice(0, 32);
|
|
661
|
-
}
|
|
662
|
-
else if (zigbeeDevice.platform.postfix !== '') {
|
|
680
|
+
if (zigbeeDevice.platform.postfix !== '') {
|
|
663
681
|
zigbeeDevice.serial = `${zigbeeDevice.serial}-${zigbeeDevice.platform.postfix}`.slice(0, 32);
|
|
664
682
|
}
|
|
665
683
|
if (device.friendly_name === 'Coordinator' || (device.model_id === 'ti.router' && device.manufacturer === 'TexasInstruments') || (device.model_id.startsWith('SLZB-') && device.manufacturer === 'SMLIGHT')) {
|
|
666
684
|
zigbeeDevice.isRouter = true;
|
|
667
685
|
platform.setSelectDevice(device.ieee_address, device.friendly_name, 'wifi');
|
|
668
|
-
zigbeeDevice.bridgedDevice = new MatterbridgeEndpoint([doorLockDevice], { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
|
|
686
|
+
zigbeeDevice.bridgedDevice = new MatterbridgeEndpoint([doorLockDevice, bridgedNode, powerSource], { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
|
|
669
687
|
zigbeeDevice.addBridgedDeviceBasicInformation();
|
|
670
688
|
zigbeeDevice.addPowerSource();
|
|
671
689
|
zigbeeDevice.bridgedDevice.addRequiredClusterServers();
|
|
672
690
|
await zigbeeDevice.bridgedDevice.addFixedLabel('type', 'lock');
|
|
673
691
|
zigbeeDevice.verifyMutableDevice(zigbeeDevice.bridgedDevice);
|
|
692
|
+
zigbeeDevice.bridgedDevice.addCommandHandler('identify', async (data) => {
|
|
693
|
+
zigbeeDevice.log.debug(`Command identify called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request identifyTime:${data.request.identifyTime} `);
|
|
694
|
+
});
|
|
674
695
|
zigbeeDevice.bridgedDevice.addCommandHandler('lockDoor', async () => {
|
|
675
|
-
zigbeeDevice.log.debug(`Command permit_join
|
|
696
|
+
zigbeeDevice.log.debug(`Command permit_join false called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db}`);
|
|
676
697
|
await zigbeeDevice.bridgedDevice?.setAttribute(DoorLockCluster.id, 'lockState', DoorLock.LockState.Locked, zigbeeDevice.log);
|
|
677
|
-
zigbeeDevice.publishCommand('permit_join
|
|
698
|
+
zigbeeDevice.publishCommand('permit_join false', 'bridge/request/permit_join', { value: false });
|
|
678
699
|
});
|
|
679
700
|
zigbeeDevice.bridgedDevice.addCommandHandler('unlockDoor', async () => {
|
|
680
|
-
zigbeeDevice.log.debug(`Command permit_join
|
|
701
|
+
zigbeeDevice.log.debug(`Command permit_join true called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db}`);
|
|
681
702
|
await zigbeeDevice.bridgedDevice?.setAttribute(DoorLockCluster.id, 'lockState', DoorLock.LockState.Unlocked, zigbeeDevice.log);
|
|
682
|
-
zigbeeDevice.publishCommand('permit_join
|
|
703
|
+
zigbeeDevice.publishCommand('permit_join true', 'bridge/request/permit_join', { value: true });
|
|
683
704
|
});
|
|
684
705
|
return zigbeeDevice;
|
|
685
706
|
}
|
|
686
|
-
if (platform.
|
|
707
|
+
if (!platform.featureBlackList?.includes('scenes') && !platform.deviceFeatureBlackList[device.friendly_name]?.includes('scenes')) {
|
|
687
708
|
Object.entries(device.endpoints).forEach(([key, endpoint]) => {
|
|
688
709
|
Object.values(endpoint.scenes).forEach((scene) => {
|
|
689
710
|
zigbeeDevice.log.debug(`***Device ${dn}${device.friendly_name}${rs}${db} endpoint ${CYAN}${key}${db} scene ${CYAN}${scene.name}${db} id ${CYAN}${scene.id}${db}`);
|
|
711
|
+
platform.setSelectDeviceEntity(device.ieee_address, 'scenes', 'Scenes', 'component');
|
|
712
|
+
platform.registerVirtualDevice(`${platform.config.scenesPrefix ? device.friendly_name + ' ' : ''}${scene.name}`, async () => {
|
|
713
|
+
zigbeeDevice.log.info(`Triggered scene "${scene.name}" id ${scene.id} from device ${device.friendly_name}`);
|
|
714
|
+
zigbeeDevice.publishCommand('scene_recall', device.friendly_name, { 'scene_recall': scene.id });
|
|
715
|
+
});
|
|
690
716
|
});
|
|
691
717
|
});
|
|
692
718
|
}
|
|
@@ -908,6 +934,11 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
908
934
|
deviceTypesMap.delete(onOffLight.code);
|
|
909
935
|
if (deviceTypesMap.has(dimmableLight.code) && deviceTypesMap.has(colorTemperatureLight.code))
|
|
910
936
|
deviceTypesMap.delete(dimmableLight.code);
|
|
937
|
+
if (deviceTypesMap.has(dimmableLight.code) && deviceTypesMap.has(extendedColorLight.code))
|
|
938
|
+
deviceTypesMap.delete(dimmableLight.code);
|
|
939
|
+
if (deviceTypesMap.has(colorTemperatureLight.code) && deviceTypesMap.has(extendedColorLight.code))
|
|
940
|
+
deviceTypesMap.delete(colorTemperatureLight.code);
|
|
941
|
+
deviceTypesMap.delete(bridgedNode.code);
|
|
911
942
|
deviceTypesMap.delete(powerSource.code);
|
|
912
943
|
device.deviceTypes = Array.from(deviceTypesMap.values());
|
|
913
944
|
}
|
|
@@ -943,12 +974,26 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
943
974
|
zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${zigbeeDevice.device?.friendly_name}${rs}${db} endpoint: ${ign}${endpoint === '' ? 'main' : endpoint}${rs}${db} => ` +
|
|
944
975
|
`${nf}tagList: ${debugStringify(device.tagList)} deviceTypes: ${debugStringify(device.deviceTypes)} clusterServersIds: ${debugStringify(device.clusterServersIds)}`);
|
|
945
976
|
}
|
|
977
|
+
if ((mainEndpoint.deviceTypes.find((dt) => dt.code === waterLeakDetector.code) || mainEndpoint.deviceTypes.find((dt) => dt.code === rainSensor.code)) && mainEndpoint.clusterServersIds.includes(BooleanState.Cluster.id)) {
|
|
978
|
+
zigbeeDevice.log.debug(`Configuring device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} BooleanStateCluster cluster with`);
|
|
979
|
+
zigbeeDevice.bridgedDevice.createDefaultBooleanStateClusterServer(false);
|
|
980
|
+
mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(BooleanState.Cluster.id), 1);
|
|
981
|
+
}
|
|
982
|
+
if (mainEndpoint.deviceTypes.find((dt) => dt.code === smokeCoAlarm.code) && mainEndpoint.clusterServersIds.includes(SmokeCoAlarm.Cluster.id)) {
|
|
983
|
+
zigbeeDevice.log.debug(`Configuring device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} SmokeCoAlarmCluster cluster with`);
|
|
984
|
+
zigbeeDevice.bridgedDevice.createSmokeOnlySmokeCOAlarmClusterServer(SmokeCoAlarm.AlarmState.Normal);
|
|
985
|
+
mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(SmokeCoAlarm.Cluster.id), 1);
|
|
986
|
+
}
|
|
946
987
|
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
988
|
if (!names.includes('color_hs') && !names.includes('color_xy')) {
|
|
949
|
-
zigbeeDevice.
|
|
950
|
-
|
|
989
|
+
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);
|
|
951
991
|
}
|
|
992
|
+
else {
|
|
993
|
+
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);
|
|
995
|
+
}
|
|
996
|
+
mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(ColorControl.Cluster.id), 1);
|
|
952
997
|
}
|
|
953
998
|
if (mainEndpoint.clusterServersIds.includes(Thermostat.Cluster.id)) {
|
|
954
999
|
const system_mode = zigbeeDevice.propertyMap.get('system_mode');
|
|
@@ -991,25 +1036,25 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
991
1036
|
zigbeeDevice.mutableDevice.clear();
|
|
992
1037
|
zigbeeDevice.logPropertyMap();
|
|
993
1038
|
zigbeeDevice.bridgedDevice.addCommandHandler('identify', async (data) => {
|
|
994
|
-
zigbeeDevice.log.debug(`Command identify called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint
|
|
1039
|
+
zigbeeDevice.log.debug(`Command identify called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber} request identifyTime:${data.request.identifyTime} `);
|
|
995
1040
|
});
|
|
996
1041
|
if (zigbeeDevice.bridgedDevice.hasClusterServer(OnOffCluster.id) || zigbeeDevice.hasEndpoints) {
|
|
997
1042
|
for (const child of zigbeeDevice.bridgedDevice.getChildEndpoints()) {
|
|
998
1043
|
if (child.hasClusterServer(OnOffCluster)) {
|
|
999
1044
|
child.addCommandHandler('on', async (data) => {
|
|
1000
|
-
zigbeeDevice.log.debug(`Command on called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint
|
|
1045
|
+
zigbeeDevice.log.debug(`Command on called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
|
|
1001
1046
|
const payload = {};
|
|
1002
1047
|
payload['state_' + data.endpoint.uniqueStorageKey] = 'ON';
|
|
1003
1048
|
zigbeeDevice.publishCommand('on', device.friendly_name, payload);
|
|
1004
1049
|
});
|
|
1005
1050
|
child.addCommandHandler('off', async (data) => {
|
|
1006
|
-
zigbeeDevice.log.debug(`Command off called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint
|
|
1051
|
+
zigbeeDevice.log.debug(`Command off called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
|
|
1007
1052
|
const payload = {};
|
|
1008
1053
|
payload['state_' + data.endpoint.uniqueStorageKey] = 'OFF';
|
|
1009
1054
|
zigbeeDevice.publishCommand('off', device.friendly_name, payload);
|
|
1010
1055
|
});
|
|
1011
1056
|
child.addCommandHandler('toggle', async (data) => {
|
|
1012
|
-
zigbeeDevice.log.debug(`Command toggle called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint
|
|
1057
|
+
zigbeeDevice.log.debug(`Command toggle called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
|
|
1013
1058
|
const payload = {};
|
|
1014
1059
|
payload['state_' + data.endpoint.uniqueStorageKey] = 'TOGGLE';
|
|
1015
1060
|
zigbeeDevice.publishCommand('toggle', device.friendly_name, payload);
|
|
@@ -1017,41 +1062,23 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1017
1062
|
}
|
|
1018
1063
|
}
|
|
1019
1064
|
zigbeeDevice.bridgedDevice.addCommandHandler('on', async (data) => {
|
|
1020
|
-
zigbeeDevice.log.debug(`Command on called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint
|
|
1021
|
-
|
|
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);
|
|
1065
|
+
zigbeeDevice.log.debug(`Command on called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
|
|
1066
|
+
zigbeeDevice.publishCommand('on', device.friendly_name, { state: 'ON' });
|
|
1028
1067
|
});
|
|
1029
1068
|
zigbeeDevice.bridgedDevice.addCommandHandler('off', async (data) => {
|
|
1030
|
-
zigbeeDevice.log.debug(`Command off called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint
|
|
1031
|
-
|
|
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);
|
|
1069
|
+
zigbeeDevice.log.debug(`Command off called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
|
|
1070
|
+
zigbeeDevice.publishCommand('off', device.friendly_name, { state: 'OFF' });
|
|
1038
1071
|
});
|
|
1039
1072
|
zigbeeDevice.bridgedDevice.addCommandHandler('toggle', async (data) => {
|
|
1040
|
-
zigbeeDevice.log.debug(`Command toggle called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint
|
|
1041
|
-
|
|
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);
|
|
1073
|
+
zigbeeDevice.log.debug(`Command toggle called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint?.maybeNumber}`);
|
|
1074
|
+
zigbeeDevice.publishCommand('toggle', device.friendly_name, { state: 'TOGGLE' });
|
|
1048
1075
|
});
|
|
1049
1076
|
}
|
|
1050
1077
|
if (zigbeeDevice.bridgedDevice.hasClusterServer(LevelControlCluster.id) || zigbeeDevice.hasEndpoints) {
|
|
1051
1078
|
for (const child of zigbeeDevice.bridgedDevice.getChildEndpoints()) {
|
|
1052
1079
|
if (child.hasClusterServer(LevelControlCluster)) {
|
|
1053
1080
|
child.addCommandHandler('moveToLevel', async (data) => {
|
|
1054
|
-
zigbeeDevice.log.debug(`Command moveToLevel called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint
|
|
1081
|
+
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
1082
|
const payload = {};
|
|
1056
1083
|
payload['brightness_' + data.endpoint.uniqueStorageKey] = data.request.level;
|
|
1057
1084
|
if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
|
|
@@ -1059,7 +1086,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1059
1086
|
zigbeeDevice.publishCommand('moveToLevel', device.friendly_name, payload);
|
|
1060
1087
|
});
|
|
1061
1088
|
child.addCommandHandler('moveToLevelWithOnOff', async (data) => {
|
|
1062
|
-
zigbeeDevice.log.debug(`Command moveToLevelWithOnOff called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint
|
|
1089
|
+
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
1090
|
const payload = {};
|
|
1064
1091
|
payload['brightness_' + data.endpoint.uniqueStorageKey] = data.request.level;
|
|
1065
1092
|
if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
|
|
@@ -1069,25 +1096,15 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1069
1096
|
}
|
|
1070
1097
|
}
|
|
1071
1098
|
zigbeeDevice.bridgedDevice.addCommandHandler('moveToLevel', async (data) => {
|
|
1072
|
-
zigbeeDevice.log.debug(`Command moveToLevel called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint
|
|
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;
|
|
1099
|
+
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}`);
|
|
1100
|
+
const payload = { brightness: data.request.level };
|
|
1079
1101
|
if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
|
|
1080
1102
|
payload['transition'] = Math.round(data.request.transitionTime / 10);
|
|
1081
1103
|
zigbeeDevice.publishCommand('moveToLevel', device.friendly_name, payload);
|
|
1082
1104
|
});
|
|
1083
1105
|
zigbeeDevice.bridgedDevice.addCommandHandler('moveToLevelWithOnOff', async (data) => {
|
|
1084
|
-
zigbeeDevice.log.debug(`Command moveToLevelWithOnOff called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint
|
|
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;
|
|
1106
|
+
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}`);
|
|
1107
|
+
const payload = { brightness: data.request.level };
|
|
1091
1108
|
if (zigbeeDevice.transition && data.request.transitionTime && data.request.transitionTime / 10 >= 1)
|
|
1092
1109
|
payload['transition'] = Math.round(data.request.transitionTime / 10);
|
|
1093
1110
|
zigbeeDevice.publishCommand('moveToLevelWithOnOff', device.friendly_name, payload);
|
|
@@ -1097,7 +1114,15 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1097
1114
|
zigbeeDevice.bridgedDevice.addCommandHandler('moveToColorTemperature', async ({ request }) => {
|
|
1098
1115
|
zigbeeDevice.log.debug(`Command moveToColorTemperature called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request: ${request.colorTemperatureMireds}`);
|
|
1099
1116
|
await zigbeeDevice.bridgedDevice?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.ColorTemperatureMireds, zigbeeDevice.log);
|
|
1100
|
-
const payload = {
|
|
1117
|
+
const payload = {};
|
|
1118
|
+
if (zigbeeDevice.propertyMap.get('color_temp')) {
|
|
1119
|
+
payload['color_temp'] = request.colorTemperatureMireds;
|
|
1120
|
+
}
|
|
1121
|
+
else {
|
|
1122
|
+
const rgb = kelvinToRGB(miredToKelvin(request.colorTemperatureMireds));
|
|
1123
|
+
payload['color'] = { r: rgb.r, g: rgb.g, b: rgb.b };
|
|
1124
|
+
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'])}.`);
|
|
1125
|
+
}
|
|
1101
1126
|
if (zigbeeDevice.transition && request.transitionTime && request.transitionTime / 10 >= 1)
|
|
1102
1127
|
payload['transition'] = Math.round(request.transitionTime / 10);
|
|
1103
1128
|
zigbeeDevice.publishCommand('moveToColorTemperature', device.friendly_name, payload);
|
|
@@ -1156,7 +1181,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1156
1181
|
zigbeeDevice.bridgedDevice.addCommandHandler('upOrOpen', async () => {
|
|
1157
1182
|
zigbeeDevice.log.debug(`Command upOrOpen called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db}`);
|
|
1158
1183
|
if (zigbeeDevice.isDevice && zigbeeDevice.propertyMap.has('position'))
|
|
1159
|
-
zigbeeDevice.bridgedDevice?.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', 0, zigbeeDevice.log);
|
|
1184
|
+
await zigbeeDevice.bridgedDevice?.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', 0, zigbeeDevice.log);
|
|
1160
1185
|
else
|
|
1161
1186
|
await zigbeeDevice.bridgedDevice?.setWindowCoveringTargetAndCurrentPosition(0);
|
|
1162
1187
|
zigbeeDevice.publishCommand('upOrOpen', device.friendly_name, { state: 'OPEN' });
|
|
@@ -1164,7 +1189,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1164
1189
|
zigbeeDevice.bridgedDevice.addCommandHandler('downOrClose', async () => {
|
|
1165
1190
|
zigbeeDevice.log.debug(`Command downOrClose called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db}`);
|
|
1166
1191
|
if (zigbeeDevice.isDevice && zigbeeDevice.propertyMap.has('position'))
|
|
1167
|
-
zigbeeDevice.bridgedDevice?.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', 10000, zigbeeDevice.log);
|
|
1192
|
+
await zigbeeDevice.bridgedDevice?.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', 10000, zigbeeDevice.log);
|
|
1168
1193
|
else
|
|
1169
1194
|
await zigbeeDevice.bridgedDevice?.setWindowCoveringTargetAndCurrentPosition(10000);
|
|
1170
1195
|
zigbeeDevice.publishCommand('downOrClose', device.friendly_name, { state: 'CLOSE' });
|
|
@@ -1177,7 +1202,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1177
1202
|
zigbeeDevice.bridgedDevice.addCommandHandler('goToLiftPercentage', async ({ request: { liftPercent100thsValue } }) => {
|
|
1178
1203
|
zigbeeDevice.log.debug(`Command goToLiftPercentage called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request liftPercent100thsValue: ${liftPercent100thsValue}`);
|
|
1179
1204
|
if (zigbeeDevice.isDevice && zigbeeDevice.propertyMap.has('position'))
|
|
1180
|
-
zigbeeDevice.bridgedDevice?.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', liftPercent100thsValue, zigbeeDevice.log);
|
|
1205
|
+
await zigbeeDevice.bridgedDevice?.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', liftPercent100thsValue, zigbeeDevice.log);
|
|
1181
1206
|
else
|
|
1182
1207
|
await zigbeeDevice.bridgedDevice?.setWindowCoveringTargetAndCurrentPosition(liftPercent100thsValue);
|
|
1183
1208
|
zigbeeDevice.publishCommand('goToLiftPercentage', device.friendly_name, { position: liftPercent100thsValue / 100 });
|
|
@@ -1202,7 +1227,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1202
1227
|
const t = zigbeeDevice.bridgedDevice?.getAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', zigbeeDevice.log);
|
|
1203
1228
|
const setpoint = Math.round(t / 100 + request.amount / 10);
|
|
1204
1229
|
if (zigbeeDevice.propertyMap.has('current_heating_setpoint')) {
|
|
1205
|
-
zigbeeDevice.publishCommand('
|
|
1230
|
+
zigbeeDevice.publishCommand('CurrentHeatingSetpoint', device.friendly_name, { current_heating_setpoint: setpoint });
|
|
1206
1231
|
}
|
|
1207
1232
|
else if (zigbeeDevice.propertyMap.has('occupied_heating_setpoint')) {
|
|
1208
1233
|
zigbeeDevice.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { occupied_heating_setpoint: setpoint });
|
|
@@ -1212,7 +1237,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1212
1237
|
const t = zigbeeDevice.bridgedDevice?.getAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', zigbeeDevice.log);
|
|
1213
1238
|
const setpoint = Math.round(t / 100 + request.amount / 10);
|
|
1214
1239
|
if (zigbeeDevice.propertyMap.has('current_heating_setpoint')) {
|
|
1215
|
-
zigbeeDevice.publishCommand('
|
|
1240
|
+
zigbeeDevice.publishCommand('CurrentCoolingSetpoint', device.friendly_name, { current_heating_setpoint: setpoint });
|
|
1216
1241
|
}
|
|
1217
1242
|
else if (zigbeeDevice.propertyMap.has('occupied_cooling_setpoint')) {
|
|
1218
1243
|
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;
|
|
@@ -34,12 +34,13 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
|
|
|
34
34
|
z2mBridgeInfo;
|
|
35
35
|
z2mBridgeDevices;
|
|
36
36
|
z2mBridgeGroups;
|
|
37
|
-
|
|
37
|
+
z2mEntityAvailability = new Map();
|
|
38
|
+
z2mEntityPayload = new Map();
|
|
38
39
|
availabilityTimer;
|
|
39
40
|
constructor(matterbridge, log, config) {
|
|
40
41
|
super(matterbridge, log, config);
|
|
41
|
-
if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('
|
|
42
|
-
throw new Error(`This plugin requires Matterbridge version >= "
|
|
42
|
+
if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.0.3')) {
|
|
43
|
+
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
44
|
}
|
|
44
45
|
this.debugEnabled = config.debug;
|
|
45
46
|
this.shouldStart = false;
|
|
@@ -69,7 +70,6 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
|
|
|
69
70
|
if (config.postfix)
|
|
70
71
|
this.postfix = config.postfix;
|
|
71
72
|
this.postfix = this.postfix.trim().slice(0, 3);
|
|
72
|
-
this.postfixHostname = config.postfixHostname ?? true;
|
|
73
73
|
config.host = this.mqttHost;
|
|
74
74
|
config.port = this.mqttPort;
|
|
75
75
|
config.protocolVersion = this.mqttProtocol;
|
|
@@ -77,7 +77,16 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
|
|
|
77
77
|
config.username = this.mqttUsername;
|
|
78
78
|
config.password = this.mqttPassword;
|
|
79
79
|
config.postfix = this.postfix;
|
|
80
|
-
config.postfixHostname
|
|
80
|
+
if (config.postfixHostname !== undefined)
|
|
81
|
+
delete config.postfixHostname;
|
|
82
|
+
if (config.deviceScenes !== undefined)
|
|
83
|
+
delete config.deviceScenes;
|
|
84
|
+
if (config.groupScenes !== undefined)
|
|
85
|
+
delete config.groupScenes;
|
|
86
|
+
if (config.scenesType === undefined)
|
|
87
|
+
config.scenesType = 'outlet';
|
|
88
|
+
if (config.scenesPrefix === undefined)
|
|
89
|
+
config.scenesPrefix = true;
|
|
81
90
|
if (config.type === 'MatterbridgeExtension') {
|
|
82
91
|
this.z2m = new Zigbee2MQTT(this.mqttHost, this.mqttPort, this.mqttTopic, this.mqttUsername, this.mqttPassword, this.mqttProtocol, this.debugEnabled);
|
|
83
92
|
this.z2m.setLogDebug(this.debugEnabled);
|
|
@@ -86,7 +95,6 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
|
|
|
86
95
|
}
|
|
87
96
|
this.log.info(`Initializing platform: ${CYAN}${this.config.name}${nf} version: ${CYAN}${this.config.version}${rs}`);
|
|
88
97
|
this.log.info(`Loaded zigbee2mqtt parameters from ${CYAN}${path.join(matterbridge.matterbridgeDirectory, 'matterbridge-zigbee2mqtt.config.json')}${rs}`);
|
|
89
|
-
this.clearSelect();
|
|
90
98
|
this.z2m = new Zigbee2MQTT(this.mqttHost, this.mqttPort, this.mqttTopic, this.mqttUsername, this.mqttPassword, this.mqttProtocol, this.debugEnabled);
|
|
91
99
|
this.z2m.setLogDebug(this.debugEnabled);
|
|
92
100
|
this.z2m.setDataPath(path.join(matterbridge.matterbridgePluginDirectory, 'matterbridge-zigbee2mqtt'));
|
|
@@ -187,11 +195,14 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
|
|
|
187
195
|
}
|
|
188
196
|
});
|
|
189
197
|
this.z2m.on('availability', (device, available) => {
|
|
190
|
-
this.
|
|
198
|
+
this.z2mEntityAvailability.set(device, available);
|
|
191
199
|
if (available)
|
|
192
|
-
this.log.info(`zigbee2MQTT
|
|
200
|
+
this.log.info(`zigbee2MQTT entity ${device} is ${available ? 'online' : 'offline'}`);
|
|
193
201
|
else
|
|
194
|
-
this.log.warn(`zigbee2MQTT
|
|
202
|
+
this.log.warn(`zigbee2MQTT entity ${device} is ${available ? 'online' : 'offline'}`);
|
|
203
|
+
});
|
|
204
|
+
this.z2m.on('message', (device, payload) => {
|
|
205
|
+
this.z2mEntityPayload.set(device, payload);
|
|
195
206
|
});
|
|
196
207
|
this.z2m.on('permit_join', async (device, time, status) => {
|
|
197
208
|
this.log.info(`zigbee2MQTT sent permit_join device: ${device} time: ${time} status: ${status}`);
|
|
@@ -292,6 +303,9 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
|
|
|
292
303
|
}
|
|
293
304
|
async onStart(reason) {
|
|
294
305
|
this.log.info(`Starting zigbee2mqtt dynamic platform v${this.version}: ` + reason);
|
|
306
|
+
await this.ready;
|
|
307
|
+
await this.clearSelect();
|
|
308
|
+
this.setSelectEntity('scenes', 'Scenes', 'component');
|
|
295
309
|
const hasOnline = await waiter('z2mBridgeOnline', () => this.z2mBridgeOnline !== undefined);
|
|
296
310
|
const hasInfo = await waiter('z2mBridgeInfo', () => this.z2mBridgeInfo !== undefined);
|
|
297
311
|
const hasDevices = await waiter('z2mBridgeDevices & z2mBridgeGroups', () => this.z2mBridgeDevices !== undefined || this.z2mBridgeGroups !== undefined);
|
|
@@ -324,33 +338,36 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
|
|
|
324
338
|
await super.onConfigure();
|
|
325
339
|
this.log.info(`Requesting update for ${this.zigbeeEntities.length} zigbee entities.`);
|
|
326
340
|
for (const bridgedEntity of this.zigbeeEntities) {
|
|
327
|
-
if (bridgedEntity.isDevice && bridgedEntity.device)
|
|
328
|
-
await this.requestDeviceUpdate(bridgedEntity.device);
|
|
329
|
-
if (bridgedEntity.isGroup && bridgedEntity.group)
|
|
330
|
-
await this.requestGroupUpdate(bridgedEntity.group);
|
|
331
341
|
await bridgedEntity.configure();
|
|
332
342
|
if (bridgedEntity.isRouter && bridgedEntity.bridgedDevice) {
|
|
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.
|
|
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.
|
|
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
|
}
|
|
355
|
+
if (bridgedEntity.isDevice && bridgedEntity.device)
|
|
356
|
+
await this.requestDeviceUpdate(bridgedEntity.device);
|
|
357
|
+
if (bridgedEntity.isGroup && bridgedEntity.group)
|
|
358
|
+
await this.requestGroupUpdate(bridgedEntity.group);
|
|
345
359
|
}
|
|
346
360
|
this.availabilityTimer = setTimeout(() => {
|
|
347
|
-
for (const [
|
|
361
|
+
for (const [entity, available] of this.z2mEntityAvailability) {
|
|
348
362
|
if (available)
|
|
349
|
-
this.z2m.emit('ONLINE-' +
|
|
363
|
+
this.z2m.emit('ONLINE-' + entity);
|
|
350
364
|
else
|
|
351
|
-
this.z2m.emit('OFFLINE-' +
|
|
365
|
+
this.z2m.emit('OFFLINE-' + entity);
|
|
366
|
+
}
|
|
367
|
+
for (const [entity, payload] of this.z2mEntityPayload) {
|
|
368
|
+
this.z2m.emit('MESSAGE-' + entity, payload);
|
|
352
369
|
}
|
|
353
|
-
},
|
|
370
|
+
}, 10 * 1000).unref();
|
|
354
371
|
if (this.config.injectPayloads) {
|
|
355
372
|
this.injectTimer = setInterval(() => {
|
|
356
373
|
const data = this.z2m.readConfig(path.join(this.matterbridge.matterbridgeDirectory, this.config.injectPayloads));
|
|
@@ -481,6 +498,22 @@ export class ZigbeePlatform extends MatterbridgeDynamicPlatform {
|
|
|
481
498
|
}
|
|
482
499
|
return matterGroup;
|
|
483
500
|
}
|
|
501
|
+
registerVirtualDevice(name, callback) {
|
|
502
|
+
let aggregator;
|
|
503
|
+
if (this.matterbridge.bridgeMode === 'bridge') {
|
|
504
|
+
aggregator = this.matterbridge.aggregatorNode;
|
|
505
|
+
}
|
|
506
|
+
else if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
507
|
+
aggregator = this.matterbridge.plugins.get(this.name)?.aggregatorNode;
|
|
508
|
+
}
|
|
509
|
+
if (aggregator) {
|
|
510
|
+
if (aggregator.parts.has(name.replaceAll(' ', '') + ':' + this.config.scenesType)) {
|
|
511
|
+
this.log.warn(`Scene name ${name} already registered. Please use a different name. Changed to ${name + ' ' + this.namePostfix}`);
|
|
512
|
+
name = name + ' ' + this.namePostfix++;
|
|
513
|
+
}
|
|
514
|
+
addVirtualDevice(aggregator, name.slice(0, 32), this.config.scenesType, callback);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
484
517
|
async unregisterZigbeeEntity(friendly_name) {
|
|
485
518
|
const entity = this.zigbeeEntities.find((entity) => entity.entityName === friendly_name);
|
|
486
519
|
if (entity) {
|
package/dist/zigbee2mqtt.js
CHANGED
|
@@ -70,6 +70,8 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
70
70
|
await mkdir(dataPath, { recursive: true });
|
|
71
71
|
this.mqttDataPath = dataPath;
|
|
72
72
|
this.log.debug(`Data directory ${this.mqttDataPath} created successfully.`);
|
|
73
|
+
const filePath = path.join(this.mqttDataPath, 'bridge-payloads.txt');
|
|
74
|
+
fs.unlinkSync(filePath);
|
|
73
75
|
}
|
|
74
76
|
catch (e) {
|
|
75
77
|
const error = e;
|
|
@@ -80,6 +82,13 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
80
82
|
this.log.error('Error creating data directory:', error);
|
|
81
83
|
}
|
|
82
84
|
}
|
|
85
|
+
try {
|
|
86
|
+
const filePath = path.join(this.mqttDataPath, 'bridge-payloads.txt');
|
|
87
|
+
fs.unlinkSync(filePath);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
this.log.debug(`Error deleting bridge-payloads.txt: ${error}`);
|
|
91
|
+
}
|
|
83
92
|
}
|
|
84
93
|
getUrl() {
|
|
85
94
|
return 'mqtt://' + this.mqttHost + ':' + this.mqttPort.toString();
|
|
@@ -613,6 +622,7 @@ export class Zigbee2MQTT extends EventEmitter {
|
|
|
613
622
|
else if (service === 'set') {
|
|
614
623
|
}
|
|
615
624
|
else if (service === undefined) {
|
|
625
|
+
this.emit('message', entity, data);
|
|
616
626
|
this.emit('MESSAGE-' + entity, data);
|
|
617
627
|
}
|
|
618
628
|
else {
|
|
@@ -119,28 +119,39 @@
|
|
|
119
119
|
"selectDeviceEntityFrom": "name"
|
|
120
120
|
}
|
|
121
121
|
},
|
|
122
|
-
"
|
|
123
|
-
"description": "
|
|
124
|
-
"type": "
|
|
125
|
-
"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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":
|
|
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",
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge-zigbee2mqtt",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0-dev.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "matterbridge-zigbee2mqtt",
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.5.0-dev.3",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"moment": "2.30.1",
|
|
13
|
-
"mqtt": "5.
|
|
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.
|
|
27
|
-
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.
|
|
28
|
-
"integrity": "sha512-
|
|
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.
|
|
39
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.
|
|
40
|
-
"integrity": "sha512-
|
|
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.
|
|
40
|
+
"undici-types": "~6.21.0"
|
|
44
41
|
}
|
|
45
42
|
},
|
|
46
43
|
"node_modules/@types/readable-stream": {
|
|
47
|
-
"version": "4.0.
|
|
48
|
-
"resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.
|
|
49
|
-
"integrity": "sha512-
|
|
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.
|
|
176
|
-
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.
|
|
177
|
-
"integrity": "sha512-
|
|
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.
|
|
284
|
-
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.
|
|
285
|
-
"integrity": "sha512-
|
|
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.
|
|
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
|
|
454
|
-
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.
|
|
455
|
-
"integrity": "sha512-
|
|
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.
|
|
510
|
-
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.
|
|
511
|
-
"integrity": "sha512-
|
|
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.
|
|
556
|
-
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.
|
|
557
|
-
"integrity": "sha512-
|
|
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.
|
|
3
|
+
"version": "2.5.0-dev.3",
|
|
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.
|
|
45
|
+
"mqtt": "5.13.0",
|
|
46
46
|
"node-ansi-logger": "3.0.1",
|
|
47
47
|
"node-persist-manager": "1.0.8"
|
|
48
48
|
}
|