matterbridge-zigbee2mqtt 2.4.2 → 2.4.4-dev.1
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 +22 -0
- package/README.md +0 -1
- package/dist/entity.js +119 -373
- package/dist/index.js +0 -35
- package/dist/payloadTypes.js +0 -23
- package/dist/platform.js +3 -49
- package/dist/zigbee2mqtt.js +22 -272
- package/dist/zigbee2mqttTypes.js +0 -23
- package/matterbridge-zigbee2mqtt.schema.json +2 -6
- package/npm-shrinkwrap.json +44 -14
- package/package.json +1 -2
- package/dist/entity.d.ts +0 -492
- package/dist/entity.d.ts.map +0 -1
- package/dist/entity.js.map +0 -1
- package/dist/index.d.ts +0 -40
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/payloadTypes.d.ts +0 -25
- package/dist/payloadTypes.d.ts.map +0 -1
- package/dist/payloadTypes.js.map +0 -1
- package/dist/platform.d.ts +0 -85
- package/dist/platform.d.ts.map +0 -1
- package/dist/platform.js.map +0 -1
- package/dist/zigbee2mqtt.d.ts +0 -182
- package/dist/zigbee2mqtt.d.ts.map +0 -1
- package/dist/zigbee2mqtt.js.map +0 -1
- package/dist/zigbee2mqttTypes.d.ts +0 -350
- package/dist/zigbee2mqttTypes.d.ts.map +0 -1
- package/dist/zigbee2mqttTypes.js.map +0 -1
package/dist/entity.js
CHANGED
|
@@ -1,37 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
* This file contains the classes ZigbeeEntity, ZigbeeDevice and ZigbeeGroup.
|
|
3
|
-
*
|
|
4
|
-
* @file entity.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @date 2023-12-29
|
|
7
|
-
* @version 3.1.0
|
|
8
|
-
*
|
|
9
|
-
* Copyright 2023, 2024, 2025 Luca Liguori.
|
|
10
|
-
*
|
|
11
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
-
* you may not use this file except in compliance with the License.
|
|
13
|
-
* You may obtain a copy of the License at
|
|
14
|
-
*
|
|
15
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
*
|
|
17
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
-
* See the License for the specific language governing permissions and
|
|
21
|
-
* limitations under the License. *
|
|
22
|
-
*/
|
|
23
|
-
import { MatterbridgeDevice, airQualitySensor, colorTemperatureSwitch, dimmableSwitch, onOffSwitch, OnOff, LevelControl, ColorControl, ColorControlCluster, TemperatureMeasurement, BooleanState, RelativeHumidityMeasurement, PressureMeasurement, OccupancySensing, IlluminanceMeasurement, PowerSource, ClusterId, WindowCovering, DoorLock, BridgedDeviceBasicInformation, ThermostatCluster, Thermostat, getClusterNameById, powerSource, bridgedNode, AirQuality, TotalVolatileOrganicCompoundsConcentrationMeasurement, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, FormaldehydeConcentrationMeasurement, Pm1ConcentrationMeasurement, Pm25ConcentrationMeasurement, Pm10ConcentrationMeasurement, electricalSensor, ElectricalEnergyMeasurement, ElectricalPowerMeasurement, onOffLight, dimmableLight, colorTemperatureLight, onOffOutlet, SwitchesTag, NumberTag, coverDevice, thermostatDevice, MatterbridgeEndpoint, dimmableOutlet, doorLockDevice, occupancySensor, lightSensor, contactSensor, temperatureSensor, humiditySensor, pressureSensor, genericSwitch, OnOffCluster, LevelControlCluster, WindowCoveringCluster, DoorLockCluster, } from 'matterbridge';
|
|
1
|
+
import { airQualitySensor, colorTemperatureSwitch, dimmableSwitch, onOffSwitch, OnOff, LevelControl, ColorControl, ColorControlCluster, TemperatureMeasurement, BooleanState, RelativeHumidityMeasurement, PressureMeasurement, OccupancySensing, IlluminanceMeasurement, PowerSource, ClusterId, WindowCovering, DoorLock, BridgedDeviceBasicInformation, ThermostatCluster, Thermostat, getClusterNameById, powerSource, bridgedNode, AirQuality, TotalVolatileOrganicCompoundsConcentrationMeasurement, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, FormaldehydeConcentrationMeasurement, Pm1ConcentrationMeasurement, Pm25ConcentrationMeasurement, Pm10ConcentrationMeasurement, electricalSensor, ElectricalEnergyMeasurement, ElectricalPowerMeasurement, onOffLight, dimmableLight, colorTemperatureLight, onOffOutlet, SwitchesTag, NumberTag, coverDevice, thermostatDevice, MatterbridgeEndpoint, dimmableOutlet, doorLockDevice, occupancySensor, lightSensor, contactSensor, temperatureSensor, humiditySensor, pressureSensor, genericSwitch, OnOffCluster, LevelControlCluster, WindowCoveringCluster, DoorLockCluster, } from 'matterbridge';
|
|
24
2
|
import { AnsiLogger, gn, dn, ign, idn, rs, db, debugStringify, hk, zb, or, nf, CYAN, er, YELLOW } from 'matterbridge/logger';
|
|
25
3
|
import { deepCopy, deepEqual, isValidNumber } from 'matterbridge/utils';
|
|
4
|
+
import { PowerSourceBehavior } from 'matterbridge/matter';
|
|
26
5
|
import * as color from 'matterbridge/utils';
|
|
27
6
|
import EventEmitter from 'events';
|
|
28
7
|
import { hostname } from 'os';
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
* @class
|
|
33
|
-
* @extends {EventEmitter}
|
|
34
|
-
*/
|
|
8
|
+
function require(type, options) {
|
|
9
|
+
return { clusterId: ClusterId(1), options };
|
|
10
|
+
}
|
|
35
11
|
export class ZigbeeEntity extends EventEmitter {
|
|
36
12
|
log;
|
|
37
13
|
serial = '';
|
|
@@ -51,7 +27,6 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
51
27
|
ignoreFeatures = [];
|
|
52
28
|
transition = false;
|
|
53
29
|
propertyMap = new Map();
|
|
54
|
-
// We save the tag list and device types and cluster servers and clients to avoid multiple lookups
|
|
55
30
|
mutableDevice = new Map();
|
|
56
31
|
colorTimeout = undefined;
|
|
57
32
|
thermostatTimeout = undefined;
|
|
@@ -59,12 +34,6 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
59
34
|
hasEndpoints = false;
|
|
60
35
|
isRouter = false;
|
|
61
36
|
noUpdate = false;
|
|
62
|
-
/**
|
|
63
|
-
* Creates an instance of ZigbeeEntity.
|
|
64
|
-
*
|
|
65
|
-
* @param {ZigbeePlatform} platform - The Zigbee platform instance.
|
|
66
|
-
* @param {BridgeDevice | BridgeGroup} entity - The bridge device or group instance received from zigbee2mqtt.
|
|
67
|
-
*/
|
|
68
37
|
constructor(platform, entity) {
|
|
69
38
|
super();
|
|
70
39
|
this.platform = platform;
|
|
@@ -82,26 +51,22 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
82
51
|
this.en = gn;
|
|
83
52
|
this.ien = ign;
|
|
84
53
|
}
|
|
85
|
-
this.log = new AnsiLogger({ logName: this.entityName, logTimestampFormat: 4
|
|
54
|
+
this.log = new AnsiLogger({ logName: this.entityName, logTimestampFormat: 4, logLevel: platform.debugEnabled ? "debug" : "info" });
|
|
86
55
|
this.log.debug(`Created MatterEntity: ${this.entityName}`);
|
|
87
56
|
this.platform.z2m.on('MESSAGE-' + this.entityName, (payload) => {
|
|
88
|
-
// Check if the message is a duplicate that can be ingored cause only linkquality and last_seen have changed (action is always passed)
|
|
89
57
|
const now = Date.now();
|
|
90
58
|
if (now - this.lastSeen < 1000 * 60 && deepEqual(this.lastPayload, payload, ['linkquality', 'last_seen', ...this.ignoreFeatures]) && !Object.prototype.hasOwnProperty.call(this.lastPayload, 'action')) {
|
|
91
59
|
this.log.debug(`Skipping not changed ${platform.z2mDevicesRegistered ? 'MQTT message' : 'State update'} for accessory ${this.entityName}`);
|
|
92
60
|
return;
|
|
93
61
|
}
|
|
94
62
|
this.lastSeen = Date.now();
|
|
95
|
-
// Check and deep copy the payload
|
|
96
63
|
if (deepEqual(this.lastPayload, payload, this.ignoreFeatures))
|
|
97
64
|
return;
|
|
98
65
|
this.lastPayload = deepCopy(payload);
|
|
99
66
|
if (Object.prototype.hasOwnProperty.call(this.lastPayload, 'action'))
|
|
100
67
|
delete this.lastPayload.action;
|
|
101
|
-
// Remove each key in ignoreFeatures from the payload copy
|
|
102
68
|
for (const key of this.ignoreFeatures) {
|
|
103
69
|
if (Object.prototype.hasOwnProperty.call(payload, key)) {
|
|
104
|
-
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
105
70
|
delete payload[key];
|
|
106
71
|
this.log.debug(`Removed key ${CYAN}${key}${db} from payload`);
|
|
107
72
|
}
|
|
@@ -115,17 +80,13 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
115
80
|
return;
|
|
116
81
|
}
|
|
117
82
|
this.log.info(`${db}${platform.z2mDevicesRegistered ? 'MQTT message' : 'State update'} for device ${this.ien}${this.entityName}${rs}${db} payload: ${debugStringify(payload)}`);
|
|
118
|
-
// Parse the payload and update the accessory
|
|
119
83
|
Object.entries(payload).forEach(([key, value]) => {
|
|
120
|
-
// Skip null and undefined values
|
|
121
84
|
if (value === undefined || value === null)
|
|
122
85
|
return;
|
|
123
86
|
if (this.bridgedDevice === undefined || this.noUpdate)
|
|
124
87
|
return;
|
|
125
|
-
// Modify voltage to battery_voltage
|
|
126
88
|
if (key === 'voltage' && this.isDevice && this.device?.power_source === 'Battery')
|
|
127
89
|
key = 'battery_voltage';
|
|
128
|
-
// Lookup the property in the propertyMap and ZigbeeToMatter table
|
|
129
90
|
const propertyMap = this.propertyMap.get(key);
|
|
130
91
|
if (propertyMap) {
|
|
131
92
|
this.log.debug(`Payload entry ${CYAN}${key}${db} => name: ${CYAN}${propertyMap.name}${db} type: ${CYAN}${propertyMap.type === '' ? 'generic' : propertyMap.type}${db} ` +
|
|
@@ -145,21 +106,16 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
145
106
|
}
|
|
146
107
|
else
|
|
147
108
|
this.log.debug(`*Payload entry ${CYAN}${key}${db} not found in propertyMap`);
|
|
148
|
-
// Switch actions on the endpoints
|
|
149
109
|
if (key === 'action' && value !== '') {
|
|
150
110
|
const propertyMap = this.propertyMap.get(('action_' + value));
|
|
151
111
|
if (propertyMap) {
|
|
152
|
-
// this.log.debug(`Payload entry ${CYAN}${value}${db} => name: ${CYAN}${propertyMap.name}${db} endpoint: ${CYAN}${propertyMap.endpoint}${db} action: ${CYAN}${propertyMap.action}${db}`);
|
|
153
112
|
const child = this.bridgedDevice.getChildEndpointByName(propertyMap.endpoint);
|
|
154
113
|
if (child && child.number)
|
|
155
|
-
|
|
114
|
+
child.triggerSwitchEvent(propertyMap.action, this.log);
|
|
156
115
|
}
|
|
157
116
|
else
|
|
158
117
|
this.log.debug(`*Payload entry ${CYAN}${('action_' + value)}${db} not found in propertyMap`);
|
|
159
118
|
}
|
|
160
|
-
// WindowCovering
|
|
161
|
-
// Zigbee2MQTT cover: 0 = open, 100 = closed
|
|
162
|
-
// Matter WindowCovering: 0 = open 10000 = closed
|
|
163
119
|
if (key === 'position' && this.isDevice && isValidNumber(value, 0, 100)) {
|
|
164
120
|
this.updateAttributeIfChanged(this.bridgedDevice, undefined, WindowCovering.Cluster.id, 'currentPositionLiftPercent100ths', value * 100);
|
|
165
121
|
}
|
|
@@ -180,26 +136,17 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
180
136
|
this.updateAttributeIfChanged(this.bridgedDevice, undefined, WindowCovering.Cluster.id, 'targetPositionLiftPercent100ths', position);
|
|
181
137
|
}
|
|
182
138
|
}
|
|
183
|
-
// ColorControl colorTemperatureMired and colorMode
|
|
184
139
|
if (key === 'color_temp' && 'color_mode' in payload && payload['color_mode'] === 'color_temp') {
|
|
185
140
|
this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'colorMode', ColorControl.ColorMode.ColorTemperatureMireds);
|
|
186
141
|
this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'colorTemperatureMireds', Math.max(147, Math.min(500, typeof value === 'number' ? value : 0)));
|
|
187
142
|
}
|
|
188
|
-
// ColorControl currentHue, currentSaturation and colorMode
|
|
189
143
|
if (key === 'color' && 'color_mode' in payload && payload['color_mode'] === 'hs') {
|
|
190
144
|
const { hue, saturation } = value;
|
|
191
145
|
this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'colorMode', ColorControl.ColorMode.CurrentHueAndCurrentSaturation);
|
|
192
146
|
this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'currentHue', Math.round((hue / 360) * 254));
|
|
193
147
|
this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'currentSaturation', Math.round((saturation / 100) * 254));
|
|
194
148
|
}
|
|
195
|
-
// ColorControl currentX, currentY and colorMode
|
|
196
149
|
if (key === 'color' && 'color_mode' in payload && payload['color_mode'] === 'xy') {
|
|
197
|
-
/* not supported by Apple Home so we convert xy to hue and saturation
|
|
198
|
-
const { x, y } = value as { x: number; y: number };
|
|
199
|
-
this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'colorMode', ColorControl.ColorMode.CurrentXAndCurrentY);
|
|
200
|
-
this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'currentX', Math.max(Math.min(Math.round(x * 65536), 65279), 0));
|
|
201
|
-
this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'currentY', Math.max(Math.min(Math.round(y * 65536), 65279), 0));
|
|
202
|
-
*/
|
|
203
150
|
const { x, y } = value;
|
|
204
151
|
const hsl = color.xyToHsl(x, y);
|
|
205
152
|
const rgb = color.xyColorToRgbColor(x, y);
|
|
@@ -226,13 +173,6 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
226
173
|
}
|
|
227
174
|
});
|
|
228
175
|
}
|
|
229
|
-
/**
|
|
230
|
-
* Destroys the ZigbeeEntity instance by clearing any active timeouts.
|
|
231
|
-
*
|
|
232
|
-
* @remarks
|
|
233
|
-
* This method is used to clean up the ZigbeeEntity instance by clearing any active timeouts for color and thermostat operations.
|
|
234
|
-
* It ensures that no further actions are taken on these timeouts after the entity is destroyed.
|
|
235
|
-
*/
|
|
236
176
|
destroy() {
|
|
237
177
|
if (this.colorTimeout)
|
|
238
178
|
clearTimeout(this.colorTimeout);
|
|
@@ -241,107 +181,58 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
241
181
|
clearTimeout(this.thermostatTimeout);
|
|
242
182
|
this.thermostatTimeout = undefined;
|
|
243
183
|
}
|
|
244
|
-
|
|
245
|
-
* Creates a mutable device with the specified definition and includes the specified server list.
|
|
246
|
-
*
|
|
247
|
-
* @param {DeviceTypeDefinition | AtLeastOne<DeviceTypeDefinition>} definition - The device type definition.
|
|
248
|
-
* @param {ClusterId[]} [includeServerList=[]] - The list of server clusters to include.
|
|
249
|
-
* @param {EndpointOptions} [options] - Optional endpoint options.
|
|
250
|
-
* @param {boolean} [debug] - Optional debug flag.
|
|
251
|
-
* @returns {MatterbridgeDevice} The created mutable device.
|
|
252
|
-
*
|
|
253
|
-
* @remarks
|
|
254
|
-
* This method creates a mutable device based on the provided definition. It adds the specified server clusters
|
|
255
|
-
* to the device and configures the device with basic information and power source clusters. If the device is a
|
|
256
|
-
* coordinator, it sets up the basic information cluster with coordinator-specific details. If the device is a
|
|
257
|
-
* group, it sets up the basic information cluster with group-specific details. The method also configures the
|
|
258
|
-
* power source cluster based on the device's power source.
|
|
259
|
-
*/
|
|
260
|
-
async createMutableDevice(definition, options, debug) {
|
|
261
|
-
if (this.platform.matterbridge.edge === true) {
|
|
262
|
-
this.bridgedDevice = (await MatterbridgeEndpoint.loadInstance(definition, options, debug));
|
|
263
|
-
}
|
|
264
|
-
else {
|
|
265
|
-
this.bridgedDevice = await MatterbridgeDevice.loadInstance(definition, undefined, debug);
|
|
266
|
-
}
|
|
267
|
-
return this.bridgedDevice;
|
|
268
|
-
}
|
|
269
|
-
getBridgedDeviceBasicInformation() {
|
|
184
|
+
addBridgedDeviceBasicInformation() {
|
|
270
185
|
if (!this.bridgedDevice)
|
|
271
186
|
throw new Error('No bridged device');
|
|
272
|
-
// Add BridgedDeviceBasicInformation cluster and device type
|
|
273
187
|
const softwareVersion = parseInt(this.platform.z2mBridgeInfo?.version || '1');
|
|
274
188
|
const softwareVersionString = `${this.platform.z2mBridgeInfo?.version} (commit ${this.platform.z2mBridgeInfo?.commit})`;
|
|
275
189
|
const hardwareVersion = parseInt(this.platform.matterbridge.matterbridgeVersion || '1');
|
|
276
190
|
const hardwareVersionString = this.platform.matterbridge.matterbridgeVersion || 'unknown';
|
|
277
191
|
if (this.isDevice && this.device && this.device.friendly_name === 'Coordinator') {
|
|
278
|
-
|
|
192
|
+
this.bridgedDevice.createDefaultBridgedDeviceBasicInformationClusterServer(this.device.friendly_name, this.serial, 0xfff1, 'zigbee2MQTT', 'Coordinator', softwareVersion, softwareVersionString, hardwareVersion, hardwareVersionString);
|
|
193
|
+
return this.bridgedDevice;
|
|
279
194
|
}
|
|
280
195
|
else if (this.isDevice && this.device) {
|
|
281
|
-
|
|
196
|
+
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);
|
|
197
|
+
return this.bridgedDevice;
|
|
282
198
|
}
|
|
283
199
|
if (!this.group)
|
|
284
200
|
throw new Error('No group found');
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
addBridgedDeviceBasicInformation() {
|
|
288
|
-
if (!this.bridgedDevice)
|
|
289
|
-
throw new Error('No bridged device');
|
|
290
|
-
// Add BridgedDeviceBasicInformation cluster and device type
|
|
291
|
-
this.bridgedDevice.addDeviceType(bridgedNode);
|
|
292
|
-
this.bridgedDevice.addClusterServer(this.getBridgedDeviceBasicInformation());
|
|
201
|
+
this.bridgedDevice.createDefaultBridgedDeviceBasicInformationClusterServer(this.group.friendly_name, this.serial, 0xfff1, 'zigbee2MQTT', 'Group', softwareVersion, softwareVersionString, hardwareVersion, hardwareVersionString);
|
|
293
202
|
return this.bridgedDevice;
|
|
294
203
|
}
|
|
295
|
-
|
|
204
|
+
addPowerSource() {
|
|
296
205
|
if (!this.bridgedDevice)
|
|
297
206
|
throw new Error('No bridged device');
|
|
298
207
|
if (this.isDevice) {
|
|
299
208
|
if (this.device?.power_source === 'Battery') {
|
|
300
|
-
|
|
209
|
+
this.bridgedDevice.createDefaultPowerSourceReplaceableBatteryClusterServer(100, PowerSource.BatChargeLevel.Ok);
|
|
301
210
|
}
|
|
302
211
|
else {
|
|
303
|
-
|
|
212
|
+
this.bridgedDevice.createDefaultPowerSourceWiredClusterServer();
|
|
304
213
|
}
|
|
305
214
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (!this.bridgedDevice)
|
|
310
|
-
throw new Error('No bridged device');
|
|
311
|
-
// Add PowerSource device type and cluster
|
|
312
|
-
this.bridgedDevice.addDeviceType(powerSource);
|
|
313
|
-
this.bridgedDevice.addClusterServer(this.getPowerSource());
|
|
215
|
+
if (this.isGroup) {
|
|
216
|
+
this.bridgedDevice.createDefaultPowerSourceWiredClusterServer();
|
|
217
|
+
}
|
|
314
218
|
return this.bridgedDevice;
|
|
315
219
|
}
|
|
316
|
-
/**
|
|
317
|
-
* Verifies that all required server clusters are present on the main endpoint and child endpoints.
|
|
318
|
-
*
|
|
319
|
-
* @param {MatterbridgeDevice} endpoint - The device endpoint to verify.
|
|
320
|
-
* @returns {boolean} True if all required server clusters are present, false otherwise.
|
|
321
|
-
*
|
|
322
|
-
* @remarks
|
|
323
|
-
* This method checks if all required server clusters are present on the main endpoint and its child endpoints.
|
|
324
|
-
* It logs an error message if any required server cluster is missing and returns false. If all required server
|
|
325
|
-
* clusters are present, it returns true.
|
|
326
|
-
*/
|
|
327
220
|
verifyMutableDevice(endpoint) {
|
|
328
221
|
if (!endpoint)
|
|
329
222
|
return false;
|
|
330
|
-
// Verify that all required server clusters are present in the main endpoint and in the child endpoints
|
|
331
223
|
for (const deviceType of endpoint.getDeviceTypes()) {
|
|
332
224
|
for (const clusterId of deviceType.requiredServerClusters) {
|
|
333
|
-
if (!endpoint.
|
|
334
|
-
endpoint.
|
|
225
|
+
if (!endpoint.hasClusterServer(clusterId)) {
|
|
226
|
+
endpoint.addClusterServers([clusterId]);
|
|
335
227
|
this.log.warn(`Endpoint with device type ${deviceType.name} (0x${deviceType.code.toString(16)}) requires cluster server ${getClusterNameById(clusterId)} (0x${clusterId.toString(16)}) but it is not present on endpoint`);
|
|
336
228
|
}
|
|
337
229
|
}
|
|
338
230
|
}
|
|
339
|
-
// Verify that all required server clusters are present in the child endpoints
|
|
340
231
|
for (const childEndpoint of endpoint.getChildEndpoints()) {
|
|
341
232
|
for (const deviceType of childEndpoint.getDeviceTypes()) {
|
|
342
233
|
for (const clusterId of deviceType.requiredServerClusters) {
|
|
343
|
-
if (!childEndpoint.
|
|
344
|
-
|
|
234
|
+
if (!childEndpoint.hasClusterServer(clusterId)) {
|
|
235
|
+
childEndpoint.addClusterServers([clusterId]);
|
|
345
236
|
this.log.warn(`Child endpoint with device type ${deviceType.name} (0x${deviceType.code.toString(16)}) requires cluster server ${getClusterNameById(clusterId)} (0x${clusterId.toString(16)}) but it is not present on child endpoint`);
|
|
346
237
|
}
|
|
347
238
|
}
|
|
@@ -349,23 +240,12 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
349
240
|
}
|
|
350
241
|
return true;
|
|
351
242
|
}
|
|
352
|
-
/**
|
|
353
|
-
* Configures the device by setting up the WindowCovering and DoorLock clusters if they are present.
|
|
354
|
-
*
|
|
355
|
-
* @returns {Promise<void>} A promise that resolves when the configuration is complete.
|
|
356
|
-
*
|
|
357
|
-
* @remarks
|
|
358
|
-
* This method configures the device by checking for the presence of the WindowCovering and DoorLock clusters.
|
|
359
|
-
* If the WindowCovering cluster is present, it sets the target as the current position and stops any ongoing
|
|
360
|
-
* movement. If the DoorLock cluster is present, it retrieves the lock state and triggers the appropriate lock
|
|
361
|
-
* operation event based on the current state.
|
|
362
|
-
*/
|
|
363
243
|
async configure() {
|
|
364
|
-
if (this.bridgedDevice?.
|
|
244
|
+
if (this.bridgedDevice?.hasClusterServer(WindowCovering.Cluster.id)) {
|
|
365
245
|
this.log.info(`Configuring ${this.bridgedDevice?.deviceName} WindowCovering cluster`);
|
|
366
246
|
await this.bridgedDevice?.setWindowCoveringTargetAsCurrentAndStopped();
|
|
367
247
|
}
|
|
368
|
-
if (this.bridgedDevice?.
|
|
248
|
+
if (this.bridgedDevice?.hasClusterServer(DoorLock.Cluster.id)) {
|
|
369
249
|
this.log.info(`Configuring ${this.bridgedDevice?.deviceName} DoorLock cluster`);
|
|
370
250
|
const state = this.bridgedDevice?.getAttribute(DoorLock.Cluster.id, 'lockState', this.log);
|
|
371
251
|
if (this.bridgedDevice.maybeNumber) {
|
|
@@ -376,32 +256,15 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
376
256
|
}
|
|
377
257
|
}
|
|
378
258
|
}
|
|
379
|
-
/**
|
|
380
|
-
* Updates the attribute of a cluster on a device endpoint if the value has changed.
|
|
381
|
-
*
|
|
382
|
-
* @param {Endpoint} deviceEndpoint - The device endpoint to update.
|
|
383
|
-
* @param {string | undefined} childEndpointName - The name of the child endpoint, if any.
|
|
384
|
-
* @param {number} clusterId - The ID of the cluster to update.
|
|
385
|
-
* @param {string} attributeName - The name of the attribute to update.
|
|
386
|
-
* @param {PayloadValue} value - The new value of the attribute.
|
|
387
|
-
* @param {string[]} [lookup] - Optional lookup array for converting string values to indices.
|
|
388
|
-
*
|
|
389
|
-
* @remarks
|
|
390
|
-
* This method checks if the specified attribute of a cluster on a device endpoint has changed. If the attribute
|
|
391
|
-
* has changed, it updates the attribute with the new value. If a lookup array is provided, it converts string
|
|
392
|
-
* values to their corresponding indices in the lookup array. The method logs the update process and handles any
|
|
393
|
-
* errors that occur during the update.
|
|
394
|
-
*/
|
|
395
259
|
updateAttributeIfChanged(deviceEndpoint, childEndpointName, clusterId, attributeName, value, lookup) {
|
|
396
260
|
if (childEndpointName && childEndpointName !== '') {
|
|
397
261
|
deviceEndpoint = this.bridgedDevice?.getChildEndpointByName(childEndpointName) ?? deviceEndpoint;
|
|
398
262
|
}
|
|
399
|
-
|
|
400
|
-
if (cluster === undefined) {
|
|
263
|
+
if (!deviceEndpoint.hasClusterServer(ClusterId(clusterId))) {
|
|
401
264
|
this.log.debug(`Update endpoint ${this.eidn}${deviceEndpoint.name}:${deviceEndpoint.number}${db}${childEndpointName ? ' (' + zb + childEndpointName + db + ')' : ''} cluster ${hk}${clusterId}${db}-${hk}${getClusterNameById(ClusterId(clusterId))}${db} not found: is z2m converter exposing all features?`);
|
|
402
265
|
return;
|
|
403
266
|
}
|
|
404
|
-
if (!
|
|
267
|
+
if (!deviceEndpoint.hasAttributeServer(ClusterId(clusterId), attributeName)) {
|
|
405
268
|
this.log.debug(`Update endpoint ${this.eidn}${deviceEndpoint.name}:${deviceEndpoint.number}${db}${childEndpointName ? ' (' + zb + childEndpointName + db + ')' : ''} error attribute ${hk}${clusterId}${db}-${hk}${getClusterNameById(ClusterId(clusterId))}${db}.${hk}${attributeName}${db} not found`);
|
|
406
269
|
return;
|
|
407
270
|
}
|
|
@@ -415,7 +278,7 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
415
278
|
return;
|
|
416
279
|
}
|
|
417
280
|
}
|
|
418
|
-
const localValue =
|
|
281
|
+
const localValue = deviceEndpoint.getAttribute(ClusterId(clusterId), attributeName, undefined);
|
|
419
282
|
if (typeof value === 'object' ? deepEqual(value, localValue) : value === localValue) {
|
|
420
283
|
this.log.debug(`Skip update endpoint ${deviceEndpoint.name}:${deviceEndpoint.number}${childEndpointName ? ' (' + childEndpointName + ')' : ''} ` +
|
|
421
284
|
`attribute ${getClusterNameById(ClusterId(clusterId))}.${attributeName} already ${typeof value === 'object' ? debugStringify(value) : value}`);
|
|
@@ -424,24 +287,12 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
424
287
|
this.log.info(`${db}Update endpoint ${this.eidn}${deviceEndpoint.name}:${deviceEndpoint.number}${db}${childEndpointName ? ' (' + zb + childEndpointName + db + ')' : ''} ` +
|
|
425
288
|
`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}`);
|
|
426
289
|
try {
|
|
427
|
-
|
|
290
|
+
deviceEndpoint.setAttribute(ClusterId(clusterId), attributeName, value, undefined);
|
|
428
291
|
}
|
|
429
292
|
catch (error) {
|
|
430
293
|
this.log.error(`Error setting attribute ${hk}${getClusterNameById(ClusterId(clusterId))}${er}.${hk}${attributeName}${er} to ${value}: ${error}`);
|
|
431
294
|
}
|
|
432
295
|
}
|
|
433
|
-
/**
|
|
434
|
-
* Publishes a command to the specified entity with the given payload.
|
|
435
|
-
*
|
|
436
|
-
* @param {string} command - The command to execute.
|
|
437
|
-
* @param {string} entityName - The name of the entity to publish the command to.
|
|
438
|
-
* @param {Payload} payload - The payload of the command.
|
|
439
|
-
*
|
|
440
|
-
* @remarks
|
|
441
|
-
* This method logs the execution of the command and publishes the command to the specified entity.
|
|
442
|
-
* If the entity name starts with 'bridge/request', it publishes the payload without a 'set' suffix.
|
|
443
|
-
* Otherwise, it publishes the payload with a 'set' suffix.
|
|
444
|
-
*/
|
|
445
296
|
publishCommand(command, entityName, payload) {
|
|
446
297
|
this.log.debug(`executeCommand ${command} called for ${this.ien}${entityName}${rs}${db} payload: ${debugStringify(payload)}`);
|
|
447
298
|
if (entityName.startsWith('bridge/request')) {
|
|
@@ -451,16 +302,7 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
451
302
|
this.platform.publish(entityName, 'set', JSON.stringify(payload));
|
|
452
303
|
}
|
|
453
304
|
}
|
|
454
|
-
/**
|
|
455
|
-
* Logs the property map of the Zigbee entity.
|
|
456
|
-
*
|
|
457
|
-
* @remarks
|
|
458
|
-
* This method iterates over the property map of the Zigbee entity and logs each property's details,
|
|
459
|
-
* including its name, type, values, minimum and maximum values, unit, and endpoint.
|
|
460
|
-
*/
|
|
461
|
-
// zigbeeDevice.propertyMap.set(property, { name, type, endpoint, category, description, label, unit, value_min, value_max, values: value });
|
|
462
305
|
logPropertyMap() {
|
|
463
|
-
// Log properties
|
|
464
306
|
this.propertyMap.forEach((value, key) => {
|
|
465
307
|
this.log.debug(`Property ${CYAN}${key}${db} name ${CYAN}${value.name}${db} type ${CYAN}${value.type === '' ? 'generic' : value.type}${db} endpoint ${CYAN}${value.endpoint === '' ? 'main' : value.endpoint}${db} ` +
|
|
466
308
|
`category ${CYAN}${value.category}${db} description ${CYAN}${value.description}${db} label ${CYAN}${value.label}${db} unit ${CYAN}${value.unit}${db} ` +
|
|
@@ -468,33 +310,10 @@ export class ZigbeeEntity extends EventEmitter {
|
|
|
468
310
|
});
|
|
469
311
|
}
|
|
470
312
|
}
|
|
471
|
-
/**
|
|
472
|
-
* Represents a Zigbee group entity.
|
|
473
|
-
*
|
|
474
|
-
* @class
|
|
475
|
-
* @extends {ZigbeeEntity}
|
|
476
|
-
*/
|
|
477
313
|
export class ZigbeeGroup extends ZigbeeEntity {
|
|
478
|
-
/**
|
|
479
|
-
* Creates an instance of ZigbeeGroup.
|
|
480
|
-
*
|
|
481
|
-
* @param {ZigbeePlatform} platform - The Zigbee platform instance.
|
|
482
|
-
* @param {BridgeGroup} group - The bridge group instance.
|
|
483
|
-
*/
|
|
484
314
|
constructor(platform, group) {
|
|
485
315
|
super(platform, group);
|
|
486
316
|
}
|
|
487
|
-
/**
|
|
488
|
-
* Creates a new ZigbeeGroup instance.
|
|
489
|
-
*
|
|
490
|
-
* @param {ZigbeePlatform} platform - The Zigbee platform instance.
|
|
491
|
-
* @param {BridgeGroup} group - The bridge group instance.
|
|
492
|
-
* @returns {Promise<ZigbeeGroup>} A promise that resolves to the created ZigbeeGroup instance.
|
|
493
|
-
*
|
|
494
|
-
* @remarks
|
|
495
|
-
* This method initializes a new ZigbeeGroup instance, sets up its properties, and configures the device
|
|
496
|
-
* based on the group members. It also adds command handlers for the group.
|
|
497
|
-
*/
|
|
498
317
|
static async create(platform, group) {
|
|
499
318
|
const zigbeeGroup = new ZigbeeGroup(platform, group);
|
|
500
319
|
if (zigbeeGroup.platform.postfixHostname) {
|
|
@@ -503,7 +322,6 @@ export class ZigbeeGroup extends ZigbeeEntity {
|
|
|
503
322
|
else {
|
|
504
323
|
zigbeeGroup.serial = `group-${group.id}`.slice(0, 32);
|
|
505
324
|
}
|
|
506
|
-
// Set the device entity select
|
|
507
325
|
zigbeeGroup.log.warn(`***Group ${zigbeeGroup.en}${group.friendly_name}${db} adds select device ${group.id} (${group.friendly_name})`);
|
|
508
326
|
if (!platform.selectDevice.get(`group-${group.id}`)) {
|
|
509
327
|
platform.selectDevice.set(`group-${group.id}`, { serial: `group-${group.id}`, name: group.friendly_name, icon: 'wifi', entities: [] });
|
|
@@ -519,23 +337,19 @@ export class ZigbeeGroup extends ZigbeeEntity {
|
|
|
519
337
|
let isCover = false;
|
|
520
338
|
let isThermostat = false;
|
|
521
339
|
if (group.members.length === 0) {
|
|
522
|
-
// Create a virtual device for the empty group to use in automations
|
|
523
340
|
zigbeeGroup.log.debug(`Group: ${gn}${group.friendly_name}${rs}${db} is a ${CYAN}virtual${db} group`);
|
|
524
|
-
zigbeeGroup.bridgedDevice =
|
|
341
|
+
zigbeeGroup.bridgedDevice = new MatterbridgeEndpoint([onOffSwitch, bridgedNode, powerSource], { uniqueStorageKey: group.friendly_name }, zigbeeGroup.log.logLevel === "debug");
|
|
525
342
|
isSwitch = true;
|
|
526
343
|
zigbeeGroup.propertyMap.set('state', { name: 'state', type: 'switch', endpoint: '' });
|
|
527
344
|
}
|
|
528
345
|
else {
|
|
529
|
-
// Create a switch or light or outlet device for the group
|
|
530
346
|
group.members.forEach((member) => {
|
|
531
|
-
// const device = zigbeeGroup.platform.z2m.getDevice(member.ieee_address);
|
|
532
347
|
const device = zigbeeGroup.platform.z2mBridgeDevices?.find((device) => device.ieee_address === member.ieee_address);
|
|
533
348
|
if (!device)
|
|
534
349
|
return;
|
|
535
350
|
zigbeeGroup.log.debug(`Group ${gn}${group.friendly_name}${db}: member device ${dn}${device.friendly_name}${db}`);
|
|
536
351
|
device.definition?.exposes.forEach((expose) => {
|
|
537
352
|
if (expose.features) {
|
|
538
|
-
// Specific features with type
|
|
539
353
|
expose.features?.forEach((feature) => {
|
|
540
354
|
if (expose.type === 'lock' && feature.name === 'state' && feature.property === 'child_lock') {
|
|
541
355
|
expose.type = 'child_lock';
|
|
@@ -565,7 +379,6 @@ export class ZigbeeGroup extends ZigbeeEntity {
|
|
|
565
379
|
});
|
|
566
380
|
}
|
|
567
381
|
else {
|
|
568
|
-
// Generic features without type
|
|
569
382
|
zigbeeGroup.log.debug(`- generic type ${CYAN}${expose.type}${db} expose name ${CYAN}${expose.name}${db} property ${CYAN}${expose.property}${db}`);
|
|
570
383
|
}
|
|
571
384
|
});
|
|
@@ -611,17 +424,14 @@ export class ZigbeeGroup extends ZigbeeEntity {
|
|
|
611
424
|
}
|
|
612
425
|
if (!deviceType)
|
|
613
426
|
return zigbeeGroup;
|
|
614
|
-
zigbeeGroup.bridgedDevice =
|
|
427
|
+
zigbeeGroup.bridgedDevice = new MatterbridgeEndpoint([deviceType, bridgedNode, powerSource], { uniqueStorageKey: group.friendly_name }, zigbeeGroup.log.logLevel === "debug");
|
|
615
428
|
}
|
|
616
429
|
zigbeeGroup.addBridgedDeviceBasicInformation();
|
|
617
430
|
zigbeeGroup.addPowerSource();
|
|
618
|
-
zigbeeGroup.bridgedDevice.addRequiredClusterServers(
|
|
619
|
-
// Verify the device
|
|
431
|
+
zigbeeGroup.bridgedDevice.addRequiredClusterServers();
|
|
620
432
|
if (!zigbeeGroup.bridgedDevice || !zigbeeGroup.verifyMutableDevice(zigbeeGroup.bridgedDevice))
|
|
621
433
|
return zigbeeGroup;
|
|
622
|
-
// Log properties
|
|
623
434
|
zigbeeGroup.logPropertyMap();
|
|
624
|
-
// Add command handlers
|
|
625
435
|
if (isSwitch || isLight) {
|
|
626
436
|
if (isSwitch && !isLight)
|
|
627
437
|
await zigbeeGroup.bridgedDevice.addFixedLabel('type', 'switch');
|
|
@@ -629,7 +439,6 @@ export class ZigbeeGroup extends ZigbeeEntity {
|
|
|
629
439
|
await zigbeeGroup.bridgedDevice.addFixedLabel('type', 'light');
|
|
630
440
|
zigbeeGroup.bridgedDevice.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
|
|
631
441
|
zigbeeGroup.log.warn(`Command identify called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} identifyTime:${identifyTime}`);
|
|
632
|
-
// logEndpoint(zigbeeGroup.bridgedDevice!);
|
|
633
442
|
});
|
|
634
443
|
zigbeeGroup.bridgedDevice.addCommandHandler('on', async () => {
|
|
635
444
|
zigbeeGroup.log.debug(`Command on called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db}`);
|
|
@@ -721,7 +530,6 @@ export class ZigbeeGroup extends ZigbeeEntity {
|
|
|
721
530
|
zigbeeGroup.bridgedDevice.subscribeAttribute(ThermostatCluster.id, 'systemMode', (newValue, oldValue) => {
|
|
722
531
|
zigbeeGroup.bridgedDevice?.log.info(`Thermostat systemMode changed from ${oldValue} to ${newValue}`);
|
|
723
532
|
if (oldValue !== newValue) {
|
|
724
|
-
// Thermostat.SystemMode.Heat && newValue === Thermostat.SystemMode.Off
|
|
725
533
|
zigbeeGroup.bridgedDevice?.log.info(`Setting thermostat systemMode to ${newValue}`);
|
|
726
534
|
if (newValue === Thermostat.SystemMode.Off) {
|
|
727
535
|
zigbeeGroup.publishCommand('SystemMode', group.friendly_name, { system_mode: 'off' });
|
|
@@ -762,7 +570,6 @@ export class ZigbeeGroup extends ZigbeeEntity {
|
|
|
762
570
|
return zigbeeGroup;
|
|
763
571
|
}
|
|
764
572
|
}
|
|
765
|
-
// prettier-ignore
|
|
766
573
|
export const z2ms = [
|
|
767
574
|
{ type: 'switch', name: 'state', property: 'state', deviceType: onOffSwitch, cluster: OnOff.Cluster.id, attribute: 'onOff', converter: (value) => { return value === 'ON' ? true : false; } },
|
|
768
575
|
{ type: 'switch', name: 'brightness', property: 'brightness', deviceType: dimmableSwitch, cluster: LevelControl.Cluster.id, attribute: 'currentLevel', converter: (value) => { return Math.max(1, Math.min(254, value)); } },
|
|
@@ -821,46 +628,22 @@ export const z2ms = [
|
|
|
821
628
|
{ type: '', name: 'voltage', property: 'voltage', deviceType: electricalSensor, cluster: ElectricalPowerMeasurement.Cluster.id, attribute: 'voltage', converter: (value) => { return value * 1000; } },
|
|
822
629
|
{ type: '', name: 'current', property: 'current', deviceType: electricalSensor, cluster: ElectricalPowerMeasurement.Cluster.id, attribute: 'activeCurrent', converter: (value) => { return value * 1000; } },
|
|
823
630
|
];
|
|
824
|
-
/**
|
|
825
|
-
* Represents a Zigbee device entity.
|
|
826
|
-
*
|
|
827
|
-
* @class
|
|
828
|
-
* @extends {ZigbeeEntity}
|
|
829
|
-
*/
|
|
830
631
|
export class ZigbeeDevice extends ZigbeeEntity {
|
|
831
|
-
/**
|
|
832
|
-
* Represents a Zigbee device entity.
|
|
833
|
-
*
|
|
834
|
-
* @class
|
|
835
|
-
* @extends {ZigbeeEntity}
|
|
836
|
-
*/
|
|
837
632
|
constructor(platform, device) {
|
|
838
633
|
super(platform, device);
|
|
839
634
|
}
|
|
840
|
-
/**
|
|
841
|
-
* Creates a new ZigbeeDevice instance.
|
|
842
|
-
*
|
|
843
|
-
* @param {ZigbeePlatform} platform - The Zigbee platform instance.
|
|
844
|
-
* @param {BridgeDevice} device - The bridge device instance.
|
|
845
|
-
* @returns {Promise<ZigbeeDevice>} A promise that resolves to the created ZigbeeDevice instance.
|
|
846
|
-
*
|
|
847
|
-
* @remarks
|
|
848
|
-
* This method initializes a new ZigbeeDevice instance, sets up its properties, and configures the device
|
|
849
|
-
* based on the device definition and options. It also adds command handlers for the device.
|
|
850
|
-
*/
|
|
851
635
|
static async create(platform, device) {
|
|
852
636
|
const zigbeeDevice = new ZigbeeDevice(platform, device);
|
|
853
637
|
zigbeeDevice.serial = `${device.ieee_address}`;
|
|
854
638
|
if (zigbeeDevice.platform.postfixHostname) {
|
|
855
639
|
zigbeeDevice.serial = `${zigbeeDevice.serial}_${hostname}`.slice(0, 32);
|
|
856
640
|
}
|
|
857
|
-
// Set Coordinator and dedicated routers
|
|
858
641
|
if (device.friendly_name === 'Coordinator' || (device.model_id === 'ti.router' && device.manufacturer === 'TexasInstruments') || (device.model_id.startsWith('SLZB-') && device.manufacturer === 'SMLIGHT')) {
|
|
859
642
|
zigbeeDevice.isRouter = true;
|
|
860
|
-
zigbeeDevice.bridgedDevice =
|
|
643
|
+
zigbeeDevice.bridgedDevice = new MatterbridgeEndpoint([doorLockDevice], { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
|
|
861
644
|
zigbeeDevice.addBridgedDeviceBasicInformation();
|
|
862
645
|
zigbeeDevice.addPowerSource();
|
|
863
|
-
zigbeeDevice.bridgedDevice.addRequiredClusterServers(
|
|
646
|
+
zigbeeDevice.bridgedDevice.addRequiredClusterServers();
|
|
864
647
|
await zigbeeDevice.bridgedDevice.addFixedLabel('type', 'lock');
|
|
865
648
|
zigbeeDevice.verifyMutableDevice(zigbeeDevice.bridgedDevice);
|
|
866
649
|
zigbeeDevice.bridgedDevice.addCommandHandler('lockDoor', async () => {
|
|
@@ -875,7 +658,6 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
875
658
|
});
|
|
876
659
|
return zigbeeDevice;
|
|
877
660
|
}
|
|
878
|
-
// Get types and properties
|
|
879
661
|
const types = [];
|
|
880
662
|
const endpoints = [];
|
|
881
663
|
const names = [];
|
|
@@ -889,7 +671,6 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
889
671
|
const values = [];
|
|
890
672
|
device.definition?.exposes.forEach((expose) => {
|
|
891
673
|
if (expose.features) {
|
|
892
|
-
// Specific features with type
|
|
893
674
|
expose.features?.forEach((feature) => {
|
|
894
675
|
if (expose.type === 'lock' && feature.name === 'state' && feature.property === 'child_lock')
|
|
895
676
|
feature.name = 'child_lock';
|
|
@@ -907,8 +688,6 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
907
688
|
});
|
|
908
689
|
}
|
|
909
690
|
else {
|
|
910
|
-
// Generic features without type
|
|
911
|
-
// Change voltage to battery_voltage for battery powered devices
|
|
912
691
|
if (device.power_source === 'Battery' && expose.name === 'voltage')
|
|
913
692
|
expose.name = 'battery_voltage';
|
|
914
693
|
if (device.power_source === 'Battery' && expose.property === 'voltage')
|
|
@@ -957,7 +736,6 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
957
736
|
types[index] = type === 'switch' || type === 'light' ? 'outlet' : type;
|
|
958
737
|
});
|
|
959
738
|
}
|
|
960
|
-
// Set the device entity select
|
|
961
739
|
platform.selectEntity.set('last_seen', { name: 'last_seen', description: 'Last seen', icon: 'hub' });
|
|
962
740
|
for (const [index, property] of properties.entries()) {
|
|
963
741
|
zigbeeDevice.log.debug(`Device ${zigbeeDevice.en}${device.friendly_name}${db} adds select device ${device.ieee_address} (${device.friendly_name})`);
|
|
@@ -969,21 +747,10 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
969
747
|
platform.selectEntity.set(property, { name: property, description: descriptions[index], icon: 'hub' });
|
|
970
748
|
platform.selectDevice.get(device.ieee_address)?.entities?.push({ name: property, description: descriptions[index], icon: 'hub' });
|
|
971
749
|
}
|
|
972
|
-
// Set the global and devic based feature blacklist
|
|
973
750
|
if (platform.featureBlackList)
|
|
974
751
|
zigbeeDevice.ignoreFeatures = [...zigbeeDevice.ignoreFeatures, ...platform.featureBlackList];
|
|
975
752
|
if (platform.deviceFeatureBlackList[device.friendly_name])
|
|
976
753
|
zigbeeDevice.ignoreFeatures = [...zigbeeDevice.ignoreFeatures, ...platform.deviceFeatureBlackList[device.friendly_name]];
|
|
977
|
-
/*
|
|
978
|
-
zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - types[${types.length}]: ${debugStringify(types)}`);
|
|
979
|
-
zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - endpoints[${endpoints.length}]: ${debugStringify(endpoints)}`);
|
|
980
|
-
zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - names[${names.length}]: ${debugStringify(names)}`);
|
|
981
|
-
zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - properties[${properties.length}]: ${debugStringify(properties)}`);
|
|
982
|
-
zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - categories[${categories.length}]: ${debugStringify(categories)}`);
|
|
983
|
-
zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - descriptions[${descriptions.length}]: ${debugStringify(descriptions)}`);
|
|
984
|
-
zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - labels[${labels.length}]: ${debugStringify(labels)}`);
|
|
985
|
-
zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - units[${units.length}]: ${debugStringify(units)}`);
|
|
986
|
-
*/
|
|
987
754
|
for (const [index, name] of names.entries()) {
|
|
988
755
|
if (platform.featureBlackList.includes(name) || platform.featureBlackList.includes(properties[index])) {
|
|
989
756
|
zigbeeDevice.log.debug(`Device ${zigbeeDevice.en}${device.friendly_name}${db} feature ${name} property ${properties[index]} is globally blacklisted`);
|
|
@@ -1012,9 +779,8 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1012
779
|
zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${zb}${endpoint}${db} type: ${zb}${type}${db} property: ${zb}${name}${db} => deviceType: ${z2m.deviceType?.name} cluster: ${z2m.cluster} attribute: ${z2m.attribute}`);
|
|
1013
780
|
zigbeeDevice.propertyMap.set(property, { name, type, endpoint, category, description, label, unit, value_min, value_max, values: value });
|
|
1014
781
|
if (endpoint === '') {
|
|
1015
|
-
/* prettier-ignore */
|
|
1016
782
|
if (!zigbeeDevice.mutableDevice.has(endpoint)) {
|
|
1017
|
-
zigbeeDevice.mutableDevice.set(endpoint, { tagList: [], deviceTypes: [z2m.deviceType], clusterServersIds: [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)],
|
|
783
|
+
zigbeeDevice.mutableDevice.set(endpoint, { tagList: [], deviceTypes: [z2m.deviceType], clusterServersIds: [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)], clusterServersOptions: [], clusterClientsIds: [], clusterClientsOptions: [] });
|
|
1018
784
|
}
|
|
1019
785
|
else {
|
|
1020
786
|
zigbeeDevice.mutableDevice.get(endpoint)?.deviceTypes.push(z2m.deviceType);
|
|
@@ -1036,9 +802,8 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1036
802
|
if (endpoint === 'l6')
|
|
1037
803
|
tagList.push({ mfgCode: null, namespaceId: NumberTag.Six.namespaceId, tag: NumberTag.Six.tag, label: 'endpoint ' + endpoint });
|
|
1038
804
|
tagList.push({ mfgCode: null, namespaceId: SwitchesTag.Custom.namespaceId, tag: SwitchesTag.Custom.tag, label: 'endpoint ' + endpoint });
|
|
1039
|
-
/* prettier-ignore */
|
|
1040
805
|
if (!zigbeeDevice.mutableDevice.has(endpoint)) {
|
|
1041
|
-
zigbeeDevice.mutableDevice.set(endpoint, { tagList, deviceTypes: [z2m.deviceType], clusterServersIds: [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)],
|
|
806
|
+
zigbeeDevice.mutableDevice.set(endpoint, { tagList, deviceTypes: [z2m.deviceType], clusterServersIds: [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)], clusterServersOptions: [], clusterClientsIds: [], clusterClientsOptions: [] });
|
|
1042
807
|
}
|
|
1043
808
|
else {
|
|
1044
809
|
zigbeeDevice.mutableDevice.get(endpoint)?.deviceTypes.push(z2m.deviceType);
|
|
@@ -1050,16 +815,13 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1050
815
|
}
|
|
1051
816
|
}
|
|
1052
817
|
else {
|
|
1053
|
-
// zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${zb}${endpoint}${db} type: ${zb}${type}${db} property: ${zb}${name}${db} => no mapping found`);
|
|
1054
818
|
}
|
|
1055
|
-
// Map actions to switches
|
|
1056
819
|
if (name === 'action' && zigbeeDevice.actions.length) {
|
|
1057
820
|
zigbeeDevice.log.info(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${nf} has actions mapped to these switches on sub endpoints:`);
|
|
1058
821
|
zigbeeDevice.log.info(' controller events <=> zigbee2mqtt actions');
|
|
1059
822
|
if (!zigbeeDevice.bridgedDevice)
|
|
1060
|
-
zigbeeDevice.bridgedDevice =
|
|
823
|
+
zigbeeDevice.bridgedDevice = new MatterbridgeEndpoint([bridgedNode], { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
|
|
1061
824
|
zigbeeDevice.hasEndpoints = true;
|
|
1062
|
-
// Mapping actions
|
|
1063
825
|
const switchMap = ['Single Press', 'Double Press', 'Long Press '];
|
|
1064
826
|
const triggerMap = ['Single', 'Double', 'Long'];
|
|
1065
827
|
let count = 1;
|
|
@@ -1072,7 +834,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1072
834
|
}
|
|
1073
835
|
const tagList = [];
|
|
1074
836
|
tagList.push({ mfgCode: null, namespaceId: SwitchesTag.Custom.namespaceId, tag: SwitchesTag.Custom.tag, label: 'switch_' + count });
|
|
1075
|
-
zigbeeDevice.mutableDevice.set('switch_' + count, { tagList, deviceTypes: [genericSwitch], clusterServersIds: [...genericSwitch.requiredServerClusters],
|
|
837
|
+
zigbeeDevice.mutableDevice.set('switch_' + count, { tagList, deviceTypes: [genericSwitch], clusterServersIds: [...genericSwitch.requiredServerClusters], clusterServersOptions: [], clusterClientsIds: [], clusterClientsOptions: [] });
|
|
1076
838
|
}
|
|
1077
839
|
else {
|
|
1078
840
|
for (let i = 0; i < zigbeeDevice.actions.length; i += 3) {
|
|
@@ -1084,7 +846,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1084
846
|
}
|
|
1085
847
|
const tagList = [];
|
|
1086
848
|
tagList.push({ mfgCode: null, namespaceId: SwitchesTag.Custom.namespaceId, tag: SwitchesTag.Custom.tag, label: 'switch_' + count });
|
|
1087
|
-
zigbeeDevice.mutableDevice.set('switch_' + count, { tagList, deviceTypes: [genericSwitch], clusterServersIds: [...genericSwitch.requiredServerClusters],
|
|
849
|
+
zigbeeDevice.mutableDevice.set('switch_' + count, { tagList, deviceTypes: [genericSwitch], clusterServersIds: [...genericSwitch.requiredServerClusters], clusterServersOptions: [], clusterClientsIds: [], clusterClientsOptions: [] });
|
|
1088
850
|
count++;
|
|
1089
851
|
}
|
|
1090
852
|
}
|
|
@@ -1092,19 +854,16 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1092
854
|
zigbeeDevice.composedType = 'button';
|
|
1093
855
|
}
|
|
1094
856
|
}
|
|
1095
|
-
// Add battery properties
|
|
1096
857
|
if (device.power_source === 'Battery') {
|
|
1097
858
|
zigbeeDevice.propertyMap.set('battery', { name: 'battery', type: '', endpoint: '' });
|
|
1098
859
|
zigbeeDevice.propertyMap.set('battery_low', { name: 'battery_low', type: '', endpoint: '' });
|
|
1099
860
|
zigbeeDevice.propertyMap.set('battery_voltage', { name: 'battery_voltage', type: '', endpoint: '' });
|
|
1100
861
|
}
|
|
1101
|
-
// Handle when the device has only child endpoints
|
|
1102
862
|
if (!zigbeeDevice.mutableDevice.has(''))
|
|
1103
|
-
zigbeeDevice.mutableDevice.set('', { tagList: [], deviceTypes: [bridgedNode, powerSource], clusterServersIds: [],
|
|
863
|
+
zigbeeDevice.mutableDevice.set('', { tagList: [], deviceTypes: [bridgedNode, powerSource], clusterServersIds: [], clusterServersOptions: [], clusterClientsIds: [], clusterClientsOptions: [] });
|
|
1104
864
|
const mainEndpoint = zigbeeDevice.mutableDevice.get('');
|
|
1105
865
|
if (!mainEndpoint)
|
|
1106
866
|
return zigbeeDevice;
|
|
1107
|
-
// Remove duplicates and superset device Types on all endpoints
|
|
1108
867
|
for (const device of zigbeeDevice.mutableDevice.values()) {
|
|
1109
868
|
const deviceTypesMap = new Map();
|
|
1110
869
|
device.deviceTypes.forEach((deviceType) => {
|
|
@@ -1120,22 +879,28 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1120
879
|
deviceTypesMap.delete(onOffLight.code);
|
|
1121
880
|
if (deviceTypesMap.has(dimmableLight.code) && deviceTypesMap.has(colorTemperatureLight.code))
|
|
1122
881
|
deviceTypesMap.delete(dimmableLight.code);
|
|
1123
|
-
|
|
882
|
+
deviceTypesMap.delete(powerSource.code);
|
|
883
|
+
device.deviceTypes = Array.from(deviceTypesMap.values());
|
|
1124
884
|
}
|
|
1125
|
-
|
|
1126
|
-
zigbeeDevice.
|
|
1127
|
-
|
|
1128
|
-
mainEndpoint.
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
885
|
+
zigbeeDevice.bridgedDevice = new MatterbridgeEndpoint([...mainEndpoint.deviceTypes, bridgedNode, powerSource], { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
|
|
886
|
+
zigbeeDevice.addBridgedDeviceBasicInformation();
|
|
887
|
+
zigbeeDevice.addPowerSource();
|
|
888
|
+
mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(PowerSource.Cluster.id), 1);
|
|
889
|
+
const options = require(PowerSourceBehavior.with(PowerSource.Feature.Wired), {
|
|
890
|
+
wiredCurrentType: PowerSource.WiredCurrentType.Ac,
|
|
891
|
+
description: 'AC Power',
|
|
892
|
+
status: PowerSource.PowerSourceStatus.Active,
|
|
893
|
+
order: 0,
|
|
894
|
+
endpointList: [],
|
|
895
|
+
});
|
|
896
|
+
mainEndpoint.clusterServersOptions.push(options);
|
|
1132
897
|
if (mainEndpoint.clusterServersIds.includes(ColorControl.Cluster.id)) {
|
|
1133
898
|
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')}`);
|
|
1134
899
|
if (!names.includes('color_hs') && !names.includes('color_xy')) {
|
|
1135
|
-
|
|
900
|
+
zigbeeDevice.bridgedDevice.createCtColorControlClusterServer();
|
|
901
|
+
mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(ColorControl.Cluster.id), 1);
|
|
1136
902
|
}
|
|
1137
903
|
}
|
|
1138
|
-
// Configure ThermostatCluster: Auto or Heating only or Cooling only. Set also min and max if available
|
|
1139
904
|
if (mainEndpoint.clusterServersIds.includes(Thermostat.Cluster.id)) {
|
|
1140
905
|
const heat = zigbeeDevice.propertyMap.get('occupied_heating_setpoint') || zigbeeDevice.propertyMap.get('current_heating_setpoint');
|
|
1141
906
|
const cool = zigbeeDevice.propertyMap.get('occupied_cooling_setpoint') || zigbeeDevice.propertyMap.get('current_cooling_setpoint');
|
|
@@ -1146,80 +911,65 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1146
911
|
zigbeeDevice.log.debug(`Configuring device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} Thermostat cluster with heating ${CYAN}${heat ? 'supported' : 'not supported'}${db} cooling ${CYAN}${cool ? 'supported' : 'not supported'}${db} ` +
|
|
1147
912
|
`minHeating ${CYAN}${minHeating}${db} maxHeating ${CYAN}${maxHeating}${db} minCooling ${CYAN}${minCooling}${db} maxCooling ${CYAN}${maxCooling}${db}`);
|
|
1148
913
|
if (heat && !cool) {
|
|
1149
|
-
zigbeeDevice.propertyMap.delete('running_state');
|
|
1150
|
-
|
|
914
|
+
zigbeeDevice.propertyMap.delete('running_state');
|
|
915
|
+
zigbeeDevice.bridgedDevice.createDefaultHeatingThermostatClusterServer(undefined, undefined, minHeating, maxHeating);
|
|
916
|
+
mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(Thermostat.Cluster.id), 1);
|
|
1151
917
|
}
|
|
1152
918
|
else if (!heat && cool) {
|
|
1153
|
-
zigbeeDevice.propertyMap.delete('running_state');
|
|
1154
|
-
|
|
919
|
+
zigbeeDevice.propertyMap.delete('running_state');
|
|
920
|
+
zigbeeDevice.bridgedDevice.createDefaultCoolingThermostatClusterServer(undefined, undefined, minCooling, maxCooling);
|
|
921
|
+
mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(Thermostat.Cluster.id), 1);
|
|
1155
922
|
}
|
|
1156
923
|
else if (heat && cool) {
|
|
1157
|
-
|
|
924
|
+
zigbeeDevice.bridgedDevice.createDefaultThermostatClusterServer(undefined, undefined, undefined, undefined, minHeating, maxHeating, minCooling, maxCooling);
|
|
925
|
+
mainEndpoint.clusterServersIds.splice(mainEndpoint.clusterServersIds.indexOf(Thermostat.Cluster.id), 1);
|
|
1158
926
|
}
|
|
1159
927
|
}
|
|
1160
|
-
// Filter out duplicate clusters and clusters objects
|
|
1161
928
|
for (const [endpoint, device] of zigbeeDevice.mutableDevice) {
|
|
1162
|
-
|
|
1163
|
-
const deviceClusterServersMap = new Map();
|
|
929
|
+
const deviceClusterServersIdMap = new Map();
|
|
1164
930
|
device.clusterServersIds.forEach((clusterServer) => {
|
|
1165
|
-
|
|
931
|
+
deviceClusterServersIdMap.set(clusterServer, clusterServer);
|
|
1166
932
|
});
|
|
1167
933
|
const deviceClusterServersObjMap = new Map();
|
|
1168
|
-
device.
|
|
1169
|
-
|
|
1170
|
-
deviceClusterServersObjMap.set(
|
|
934
|
+
device.clusterServersOptions.forEach((clusterServer) => {
|
|
935
|
+
deviceClusterServersIdMap.delete(clusterServer.clusterId);
|
|
936
|
+
deviceClusterServersObjMap.set(clusterServer.clusterId, clusterServer);
|
|
1171
937
|
});
|
|
1172
|
-
device.clusterServersIds = Array.from(
|
|
1173
|
-
device.
|
|
1174
|
-
|
|
1175
|
-
const deviceClusterClientsMap = new Map();
|
|
938
|
+
device.clusterServersIds = Array.from(deviceClusterServersIdMap.values());
|
|
939
|
+
device.clusterServersOptions = Array.from(deviceClusterServersObjMap.values());
|
|
940
|
+
const deviceClusterClientsIdMap = new Map();
|
|
1176
941
|
device.clusterClientsIds.forEach((clusterClient) => {
|
|
1177
|
-
|
|
942
|
+
deviceClusterClientsIdMap.set(clusterClient, clusterClient);
|
|
1178
943
|
});
|
|
1179
944
|
const deviceClusterClientsObjMap = new Map();
|
|
1180
|
-
device.
|
|
1181
|
-
|
|
1182
|
-
deviceClusterClientsObjMap.set(
|
|
945
|
+
device.clusterClientsOptions.forEach((clusterClient) => {
|
|
946
|
+
deviceClusterClientsIdMap.delete(clusterClient.clusterId);
|
|
947
|
+
deviceClusterClientsObjMap.set(clusterClient.clusterId, clusterClient);
|
|
1183
948
|
});
|
|
1184
|
-
device.clusterClientsIds = Array.from(
|
|
1185
|
-
device.
|
|
949
|
+
device.clusterClientsIds = Array.from(deviceClusterClientsIdMap.values());
|
|
950
|
+
device.clusterClientsOptions = Array.from(deviceClusterClientsObjMap.values());
|
|
1186
951
|
zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${zigbeeDevice.device?.friendly_name}${rs}${db} endpoint: ${ign}${endpoint === '' ? 'main' : endpoint}${rs}${db} => ` +
|
|
1187
952
|
`${nf}tagList: ${debugStringify(device.tagList)} deviceTypes: ${debugStringify(device.deviceTypes)} clusterServersIds: ${debugStringify(device.clusterServersIds)}`);
|
|
1188
953
|
}
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
zigbeeDevice.bridgedDevice?.addClusterServer(clusterServerObj);
|
|
1192
|
-
});
|
|
1193
|
-
// Add the cluster ids to the main endpoint
|
|
1194
|
-
zigbeeDevice.bridgedDevice.addClusterServerFromList(zigbeeDevice.bridgedDevice, mainEndpoint.clusterServersIds);
|
|
1195
|
-
zigbeeDevice.bridgedDevice.addRequiredClusterServers(zigbeeDevice.bridgedDevice);
|
|
1196
|
-
// Add the Fixed Label cluster to the main endpoint
|
|
954
|
+
zigbeeDevice.bridgedDevice.addClusterServers(mainEndpoint.clusterServersIds);
|
|
955
|
+
zigbeeDevice.bridgedDevice.addRequiredClusterServers();
|
|
1197
956
|
if (zigbeeDevice.composedType !== '')
|
|
1198
957
|
await zigbeeDevice.bridgedDevice.addFixedLabel('composed', zigbeeDevice.composedType);
|
|
1199
|
-
// Create the child endpoints
|
|
1200
958
|
for (const [endpoint, device] of zigbeeDevice.mutableDevice) {
|
|
1201
959
|
if (endpoint === '')
|
|
1202
960
|
continue;
|
|
1203
|
-
|
|
1204
|
-
device.clusterServersObjs.forEach((clusterServerObj) => {
|
|
1205
|
-
child.addClusterServer(clusterServerObj);
|
|
1206
|
-
});
|
|
961
|
+
zigbeeDevice.bridgedDevice?.addChildDeviceTypeWithClusterServer(endpoint, device.deviceTypes, device.clusterServersIds, { tagList: device.tagList }, zigbeeDevice.log.logLevel === "debug");
|
|
1207
962
|
}
|
|
1208
|
-
// Verify the device
|
|
1209
963
|
if (!zigbeeDevice.verifyMutableDevice(zigbeeDevice.bridgedDevice))
|
|
1210
964
|
return zigbeeDevice;
|
|
1211
|
-
// Clear the mutable device from memory
|
|
1212
965
|
zigbeeDevice.mutableDevice.clear();
|
|
1213
|
-
// Log properties
|
|
1214
966
|
zigbeeDevice.logPropertyMap();
|
|
1215
|
-
// Add command handlers
|
|
1216
967
|
zigbeeDevice.bridgedDevice.addCommandHandler('identify', async (data) => {
|
|
1217
968
|
zigbeeDevice.log.debug(`Command identify called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number} request identifyTime:${data.request.identifyTime} `);
|
|
1218
|
-
// logEndpoint(zigbeeDevice.bridgedDevice!);
|
|
1219
969
|
});
|
|
1220
|
-
if (zigbeeDevice.bridgedDevice.
|
|
970
|
+
if (zigbeeDevice.bridgedDevice.hasClusterServer(OnOffCluster.id) || zigbeeDevice.hasEndpoints) {
|
|
1221
971
|
for (const child of zigbeeDevice.bridgedDevice.getChildEndpoints()) {
|
|
1222
|
-
if (
|
|
972
|
+
if (child.hasClusterServer(OnOffCluster)) {
|
|
1223
973
|
child.addCommandHandler('on', async (data) => {
|
|
1224
974
|
zigbeeDevice.log.debug(`Command on called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number}`);
|
|
1225
975
|
const payload = {};
|
|
@@ -1271,9 +1021,9 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1271
1021
|
zigbeeDevice.publishCommand('toggle', device.friendly_name, payload);
|
|
1272
1022
|
});
|
|
1273
1023
|
}
|
|
1274
|
-
if (zigbeeDevice.bridgedDevice.
|
|
1024
|
+
if (zigbeeDevice.bridgedDevice.hasClusterServer(LevelControlCluster.id) || zigbeeDevice.hasEndpoints) {
|
|
1275
1025
|
for (const child of zigbeeDevice.bridgedDevice.getChildEndpoints()) {
|
|
1276
|
-
if (
|
|
1026
|
+
if (child.hasClusterServer(LevelControlCluster)) {
|
|
1277
1027
|
child.addCommandHandler('moveToLevel', async (data) => {
|
|
1278
1028
|
zigbeeDevice.log.debug(`Command moveToLevel called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number} request: ${data.request.level} transition: ${data.request.transitionTime}`);
|
|
1279
1029
|
const payload = {};
|
|
@@ -1317,7 +1067,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1317
1067
|
zigbeeDevice.publishCommand('moveToLevelWithOnOff', device.friendly_name, payload);
|
|
1318
1068
|
});
|
|
1319
1069
|
}
|
|
1320
|
-
if (zigbeeDevice.bridgedDevice.
|
|
1070
|
+
if (zigbeeDevice.bridgedDevice.hasAttributeServer(ColorControlCluster.id, 'colorTemperatureMireds')) {
|
|
1321
1071
|
zigbeeDevice.bridgedDevice.addCommandHandler('moveToColorTemperature', async ({ request }) => {
|
|
1322
1072
|
zigbeeDevice.log.debug(`Command moveToColorTemperature called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request: ${request.colorTemperatureMireds}`);
|
|
1323
1073
|
await zigbeeDevice.bridgedDevice?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.ColorTemperatureMireds, zigbeeDevice.log);
|
|
@@ -1327,7 +1077,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1327
1077
|
zigbeeDevice.publishCommand('moveToColorTemperature', device.friendly_name, payload);
|
|
1328
1078
|
});
|
|
1329
1079
|
}
|
|
1330
|
-
if (zigbeeDevice.bridgedDevice.
|
|
1080
|
+
if (zigbeeDevice.bridgedDevice.hasAttributeServer(ColorControlCluster.id, 'currentX')) {
|
|
1331
1081
|
zigbeeDevice.bridgedDevice.addCommandHandler('moveToColor', async ({ request }) => {
|
|
1332
1082
|
zigbeeDevice.log.debug(`Command moveToColor called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request: X: ${request.colorX} Y: ${request.colorY}`);
|
|
1333
1083
|
await zigbeeDevice.bridgedDevice?.setAttribute(ColorControlCluster.id, 'colorMode', ColorControl.ColorMode.CurrentXAndCurrentY, zigbeeDevice.log);
|
|
@@ -1337,7 +1087,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1337
1087
|
zigbeeDevice.publishCommand('moveToColor', device.friendly_name, payload);
|
|
1338
1088
|
});
|
|
1339
1089
|
}
|
|
1340
|
-
if (zigbeeDevice.bridgedDevice.
|
|
1090
|
+
if (zigbeeDevice.bridgedDevice.hasAttributeServer(ColorControlCluster.id, 'currentHue')) {
|
|
1341
1091
|
let lastRequestedHue = 0;
|
|
1342
1092
|
let lastRequestedSaturation = 0;
|
|
1343
1093
|
zigbeeDevice.bridgedDevice.addCommandHandler('moveToHue', async ({ request }) => {
|
|
@@ -1376,7 +1126,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1376
1126
|
zigbeeDevice.publishCommand('moveToHueAndSaturation', device.friendly_name, payload);
|
|
1377
1127
|
});
|
|
1378
1128
|
}
|
|
1379
|
-
if (zigbeeDevice.bridgedDevice.
|
|
1129
|
+
if (zigbeeDevice.bridgedDevice.hasClusterServer(WindowCoveringCluster.id)) {
|
|
1380
1130
|
zigbeeDevice.bridgedDevice.addCommandHandler('upOrOpen', async () => {
|
|
1381
1131
|
zigbeeDevice.log.debug(`Command upOrOpen called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db}`);
|
|
1382
1132
|
if (zigbeeDevice.isDevice && zigbeeDevice.propertyMap.has('position'))
|
|
@@ -1407,7 +1157,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1407
1157
|
zigbeeDevice.publishCommand('goToLiftPercentage', device.friendly_name, { position: liftPercent100thsValue / 100 });
|
|
1408
1158
|
});
|
|
1409
1159
|
}
|
|
1410
|
-
if (zigbeeDevice.bridgedDevice.
|
|
1160
|
+
if (zigbeeDevice.bridgedDevice.hasClusterServer(DoorLockCluster.id)) {
|
|
1411
1161
|
zigbeeDevice.bridgedDevice.addCommandHandler('lockDoor', async ({ request: request }) => {
|
|
1412
1162
|
zigbeeDevice.log.debug(`Command lockDoor called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db}`, request);
|
|
1413
1163
|
await zigbeeDevice.bridgedDevice?.setAttribute(DoorLockCluster.id, 'lockState', DoorLock.LockState.Locked, zigbeeDevice.log);
|
|
@@ -1419,7 +1169,7 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1419
1169
|
zigbeeDevice.publishCommand('unlockDoor', device.friendly_name, { state: 'UNLOCK' });
|
|
1420
1170
|
});
|
|
1421
1171
|
}
|
|
1422
|
-
if (zigbeeDevice.bridgedDevice.
|
|
1172
|
+
if (zigbeeDevice.bridgedDevice.hasClusterServer(ThermostatCluster.id)) {
|
|
1423
1173
|
zigbeeDevice.bridgedDevice.addCommandHandler('setpointRaiseLower', async ({ request: request }) => {
|
|
1424
1174
|
zigbeeDevice.log.debug(`Command setpointRaiseLower called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} request:`, request);
|
|
1425
1175
|
if (request.mode === Thermostat.SetpointRaiseLowerMode.Heat || request.mode === Thermostat.SetpointRaiseLowerMode.Both) {
|
|
@@ -1447,44 +1197,40 @@ export class ZigbeeDevice extends ZigbeeEntity {
|
|
|
1447
1197
|
}
|
|
1448
1198
|
}
|
|
1449
1199
|
});
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1200
|
+
zigbeeDevice.bridgedDevice.subscribeAttribute(ThermostatCluster.id, 'systemMode', async (value) => {
|
|
1201
|
+
zigbeeDevice.log.debug(`Subscribe systemMode called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} with:`, value);
|
|
1202
|
+
const system_mode = value === Thermostat.SystemMode.Off ? 'off' : value === Thermostat.SystemMode.Heat ? 'heat' : 'cool';
|
|
1203
|
+
zigbeeDevice.publishCommand('SystemMode', device.friendly_name, { system_mode });
|
|
1204
|
+
zigbeeDevice.noUpdate = true;
|
|
1205
|
+
zigbeeDevice.thermostatTimeout = setTimeout(() => {
|
|
1206
|
+
zigbeeDevice.noUpdate = false;
|
|
1207
|
+
}, 5 * 1000);
|
|
1208
|
+
}, zigbeeDevice.log);
|
|
1209
|
+
if (zigbeeDevice.bridgedDevice.hasAttributeServer(ThermostatCluster.id, 'occupiedHeatingSetpoint'))
|
|
1210
|
+
zigbeeDevice.bridgedDevice.subscribeAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', async (value) => {
|
|
1211
|
+
zigbeeDevice.log.debug(`Subscribe occupiedHeatingSetpoint called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} with:`, value);
|
|
1212
|
+
if (zigbeeDevice.propertyMap.has('current_heating_setpoint'))
|
|
1213
|
+
zigbeeDevice.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { current_heating_setpoint: Math.round(value / 100) });
|
|
1214
|
+
else if (zigbeeDevice.propertyMap.has('occupied_heating_setpoint'))
|
|
1215
|
+
zigbeeDevice.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { occupied_heating_setpoint: Math.round(value / 100) });
|
|
1216
|
+
zigbeeDevice.noUpdate = true;
|
|
1217
|
+
zigbeeDevice.thermostatTimeout = setTimeout(() => {
|
|
1218
|
+
zigbeeDevice.noUpdate = false;
|
|
1219
|
+
}, 5 * 1000);
|
|
1220
|
+
}, zigbeeDevice.log);
|
|
1221
|
+
if (zigbeeDevice.bridgedDevice.hasAttributeServer(ThermostatCluster.id, 'occupiedCoolingSetpoint'))
|
|
1222
|
+
zigbeeDevice.bridgedDevice.subscribeAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', async (value) => {
|
|
1223
|
+
zigbeeDevice.log.debug(`Subscribe occupiedCoolingSetpoint called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} with:`, value);
|
|
1224
|
+
if (zigbeeDevice.propertyMap.has('current_cooling_setpoint'))
|
|
1225
|
+
zigbeeDevice.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { current_cooling_setpoint: Math.round(value / 100) });
|
|
1226
|
+
else if (zigbeeDevice.propertyMap.has('occupied_cooling_setpoint'))
|
|
1227
|
+
zigbeeDevice.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { occupied_cooling_setpoint: Math.round(value / 100) });
|
|
1456
1228
|
zigbeeDevice.noUpdate = true;
|
|
1457
1229
|
zigbeeDevice.thermostatTimeout = setTimeout(() => {
|
|
1458
1230
|
zigbeeDevice.noUpdate = false;
|
|
1459
1231
|
}, 5 * 1000);
|
|
1460
1232
|
}, zigbeeDevice.log);
|
|
1461
|
-
if (thermostat.attributes.occupiedHeatingSetpoint)
|
|
1462
|
-
zigbeeDevice.bridgedDevice.subscribeAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', async (value) => {
|
|
1463
|
-
zigbeeDevice.log.debug(`Subscribe occupiedHeatingSetpoint called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} with:`, value);
|
|
1464
|
-
if (zigbeeDevice.propertyMap.has('current_heating_setpoint'))
|
|
1465
|
-
zigbeeDevice.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { current_heating_setpoint: Math.round(value / 100) });
|
|
1466
|
-
else if (zigbeeDevice.propertyMap.has('occupied_heating_setpoint'))
|
|
1467
|
-
zigbeeDevice.publishCommand('OccupiedHeatingSetpoint', device.friendly_name, { occupied_heating_setpoint: Math.round(value / 100) });
|
|
1468
|
-
zigbeeDevice.noUpdate = true;
|
|
1469
|
-
zigbeeDevice.thermostatTimeout = setTimeout(() => {
|
|
1470
|
-
zigbeeDevice.noUpdate = false;
|
|
1471
|
-
}, 5 * 1000);
|
|
1472
|
-
}, zigbeeDevice.log);
|
|
1473
|
-
if (thermostat.attributes.occupiedCoolingSetpoint)
|
|
1474
|
-
zigbeeDevice.bridgedDevice.subscribeAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', async (value) => {
|
|
1475
|
-
zigbeeDevice.log.debug(`Subscribe occupiedCoolingSetpoint called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} with:`, value);
|
|
1476
|
-
if (zigbeeDevice.propertyMap.has('current_cooling_setpoint'))
|
|
1477
|
-
zigbeeDevice.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { current_cooling_setpoint: Math.round(value / 100) });
|
|
1478
|
-
else if (zigbeeDevice.propertyMap.has('occupied_cooling_setpoint'))
|
|
1479
|
-
zigbeeDevice.publishCommand('OccupiedCoolingSetpoint', device.friendly_name, { occupied_cooling_setpoint: Math.round(value / 100) });
|
|
1480
|
-
zigbeeDevice.noUpdate = true;
|
|
1481
|
-
zigbeeDevice.thermostatTimeout = setTimeout(() => {
|
|
1482
|
-
zigbeeDevice.noUpdate = false;
|
|
1483
|
-
}, 5 * 1000);
|
|
1484
|
-
}, zigbeeDevice.log);
|
|
1485
|
-
}
|
|
1486
1233
|
}
|
|
1487
1234
|
return zigbeeDevice;
|
|
1488
1235
|
}
|
|
1489
1236
|
}
|
|
1490
|
-
//# sourceMappingURL=entity.js.map
|