matterbridge-zigbee2mqtt 2.3.0-dev.1 → 2.3.0-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/entity.js CHANGED
@@ -1,37 +1,9 @@
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
1
  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';
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';
26
4
  import * as color from 'matterbridge/utils';
27
5
  import EventEmitter from 'events';
28
6
  import { hostname } from 'os';
29
- /**
30
- * Represents a Zigbee entity: a group or a device.
31
- *
32
- * @class
33
- * @extends {EventEmitter}
34
- */
35
7
  export class ZigbeeEntity extends EventEmitter {
36
8
  log;
37
9
  serial = '';
@@ -51,7 +23,6 @@ export class ZigbeeEntity extends EventEmitter {
51
23
  ignoreFeatures = [];
52
24
  transition = false;
53
25
  propertyMap = new Map();
54
- // We save the tag list and device types and cluster servers and clients to avoid multiple lookups
55
26
  mutableDevice = new Map();
56
27
  colorTimeout = undefined;
57
28
  thermostatTimeout = undefined;
@@ -59,12 +30,6 @@ export class ZigbeeEntity extends EventEmitter {
59
30
  hasEndpoints = false;
60
31
  isRouter = false;
61
32
  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
33
  constructor(platform, entity) {
69
34
  super();
70
35
  this.platform = platform;
@@ -82,26 +47,22 @@ export class ZigbeeEntity extends EventEmitter {
82
47
  this.en = gn;
83
48
  this.ien = ign;
84
49
  }
85
- this.log = new AnsiLogger({ logName: this.entityName, logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: platform.debugEnabled ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
50
+ this.log = new AnsiLogger({ logName: this.entityName, logTimestampFormat: 4, logLevel: platform.debugEnabled ? "debug" : "info" });
86
51
  this.log.debug(`Created MatterEntity: ${this.entityName}`);
87
52
  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
53
  const now = Date.now();
90
54
  if (now - this.lastSeen < 1000 * 60 && deepEqual(this.lastPayload, payload, ['linkquality', 'last_seen', ...this.ignoreFeatures]) && !Object.prototype.hasOwnProperty.call(this.lastPayload, 'action')) {
91
55
  this.log.debug(`Skipping not changed ${platform.z2mDevicesRegistered ? 'MQTT message' : 'State update'} for accessory ${this.entityName}`);
92
56
  return;
93
57
  }
94
58
  this.lastSeen = Date.now();
95
- // Check and deep copy the payload
96
59
  if (deepEqual(this.lastPayload, payload, this.ignoreFeatures))
97
60
  return;
98
61
  this.lastPayload = deepCopy(payload);
99
62
  if (Object.prototype.hasOwnProperty.call(this.lastPayload, 'action'))
100
63
  delete this.lastPayload.action;
101
- // Remove each key in ignoreFeatures from the payload copy
102
64
  for (const key of this.ignoreFeatures) {
103
65
  if (Object.prototype.hasOwnProperty.call(payload, key)) {
104
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
105
66
  delete payload[key];
106
67
  this.log.debug(`Removed key ${CYAN}${key}${db} from payload`);
107
68
  }
@@ -115,24 +76,19 @@ export class ZigbeeEntity extends EventEmitter {
115
76
  return;
116
77
  }
117
78
  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
79
  Object.entries(payload).forEach(([key, value]) => {
120
- // Skip null and undefined values
121
80
  if (value === undefined || value === null)
122
81
  return;
123
82
  if (this.bridgedDevice === undefined || this.noUpdate)
124
83
  return;
125
- // Modify voltage to battery_voltage
126
84
  if (key === 'voltage' && this.isDevice && this.device?.power_source === 'Battery')
127
85
  key = 'battery_voltage';
128
- // Modify illuminance and illuminance_lux
129
86
  if (key === 'illuminance' && this.isDevice && this.device && this.device.definition && ['RTCGQ14LM'].includes(this.device.definition.model)) {
130
87
  key = 'illuminance_lux';
131
88
  }
132
89
  if (key === 'illuminance' && typeof value === 'number' && this.isDevice && this.device && this.device.definition && ['ZG-204ZL', 'ZG-205Z/A'].includes(this.device.definition.model)) {
133
90
  value = value * 10;
134
91
  }
135
- // Lookup the property in the propertyMap and ZigbeeToMatter table
136
92
  const propertyMap = this.propertyMap.get(key);
137
93
  if (propertyMap) {
138
94
  this.log.debug(`Payload entry ${CYAN}${key}${db} => name: ${CYAN}${propertyMap.name}${db} type: ${CYAN}${propertyMap.type === '' ? 'generic' : propertyMap.type}${db} ` +
@@ -152,11 +108,9 @@ export class ZigbeeEntity extends EventEmitter {
152
108
  }
153
109
  else
154
110
  this.log.debug(`*Payload entry ${CYAN}${key}${db} not found in propertyMap`);
155
- // Switch actions on the endpoints
156
111
  if (key === 'action' && value !== '') {
157
112
  const propertyMap = this.propertyMap.get(('action_' + value));
158
113
  if (propertyMap) {
159
- // this.log.debug(`Payload entry ${CYAN}${value}${db} => name: ${CYAN}${propertyMap.name}${db} endpoint: ${CYAN}${propertyMap.endpoint}${db} action: ${CYAN}${propertyMap.action}${db}`);
160
114
  const child = this.bridgedDevice.getChildEndpointByName(propertyMap.endpoint);
161
115
  if (child && child.number)
162
116
  this.bridgedDevice.triggerSwitchEvent(propertyMap.action, this.log, child);
@@ -164,9 +118,6 @@ export class ZigbeeEntity extends EventEmitter {
164
118
  else
165
119
  this.log.debug(`*Payload entry ${CYAN}${('action_' + value)}${db} not found in propertyMap`);
166
120
  }
167
- // WindowCovering
168
- // Zigbee2MQTT cover: 0 = open, 100 = closed
169
- // Matter WindowCovering: 0 = open 10000 = closed
170
121
  if (key === 'position' && this.isDevice && isValidNumber(value, 0, 100)) {
171
122
  this.updateAttributeIfChanged(this.bridgedDevice, undefined, WindowCovering.Cluster.id, 'currentPositionLiftPercent100ths', value * 100);
172
123
  }
@@ -187,12 +138,10 @@ export class ZigbeeEntity extends EventEmitter {
187
138
  this.updateAttributeIfChanged(this.bridgedDevice, undefined, WindowCovering.Cluster.id, 'targetPositionLiftPercent100ths', position);
188
139
  }
189
140
  }
190
- // ColorControl colorTemperatureMired and colorMode
191
141
  if (key === 'color_temp' && 'color_mode' in payload && payload['color_mode'] === 'color_temp') {
192
142
  this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'colorTemperatureMireds', Math.max(147, Math.min(500, typeof value === 'number' ? value : 0)));
193
143
  this.updateAttributeIfChanged(this.bridgedDevice, undefined, ColorControl.Cluster.id, 'colorMode', ColorControl.ColorMode.ColorTemperatureMireds);
194
144
  }
195
- // ColorControl currentHue, currentSaturation and colorMode
196
145
  if (key === 'color' && 'color_mode' in payload && payload['color_mode'] === 'xy') {
197
146
  const { x, y } = value;
198
147
  const hsl = color.xyToHsl(x, y);
@@ -220,13 +169,6 @@ export class ZigbeeEntity extends EventEmitter {
220
169
  }
221
170
  });
222
171
  }
223
- /**
224
- * Destroys the ZigbeeEntity instance by clearing any active timeouts.
225
- *
226
- * @remarks
227
- * This method is used to clean up the ZigbeeEntity instance by clearing any active timeouts for color and thermostat operations.
228
- * It ensures that no further actions are taken on these timeouts after the entity is destroyed.
229
- */
230
172
  destroy() {
231
173
  if (this.colorTimeout)
232
174
  clearTimeout(this.colorTimeout);
@@ -235,22 +177,6 @@ export class ZigbeeEntity extends EventEmitter {
235
177
  clearTimeout(this.thermostatTimeout);
236
178
  this.thermostatTimeout = undefined;
237
179
  }
238
- /**
239
- * Creates a mutable device with the specified definition and includes the specified server list.
240
- *
241
- * @param {DeviceTypeDefinition | AtLeastOne<DeviceTypeDefinition>} definition - The device type definition.
242
- * @param {ClusterId[]} [includeServerList=[]] - The list of server clusters to include.
243
- * @param {EndpointOptions} [options] - Optional endpoint options.
244
- * @param {boolean} [debug] - Optional debug flag.
245
- * @returns {MatterbridgeDevice} The created mutable device.
246
- *
247
- * @remarks
248
- * This method creates a mutable device based on the provided definition. It adds the specified server clusters
249
- * to the device and configures the device with basic information and power source clusters. If the device is a
250
- * coordinator, it sets up the basic information cluster with coordinator-specific details. If the device is a
251
- * group, it sets up the basic information cluster with group-specific details. The method also configures the
252
- * power source cluster based on the device's power source.
253
- */
254
180
  async createMutableDevice(definition, options, debug) {
255
181
  if (this.platform.matterbridge.edge === true) {
256
182
  this.bridgedDevice = (await MatterbridgeEndpoint.loadInstance(definition, options, debug));
@@ -263,7 +189,6 @@ export class ZigbeeEntity extends EventEmitter {
263
189
  getBridgedDeviceBasicInformation() {
264
190
  if (!this.bridgedDevice)
265
191
  throw new Error('No bridged device');
266
- // Add BridgedDeviceBasicInformation cluster and device type
267
192
  const softwareVersion = parseInt(this.platform.z2mBridgeInfo?.version || '1');
268
193
  const softwareVersionString = `${this.platform.z2mBridgeInfo?.version} (commit ${this.platform.z2mBridgeInfo?.commit})`;
269
194
  const hardwareVersion = parseInt(this.platform.matterbridge.matterbridgeVersion || '1');
@@ -281,7 +206,6 @@ export class ZigbeeEntity extends EventEmitter {
281
206
  addBridgedDeviceBasicInformation() {
282
207
  if (!this.bridgedDevice)
283
208
  throw new Error('No bridged device');
284
- // Add BridgedDeviceBasicInformation cluster and device type
285
209
  this.bridgedDevice.addDeviceType(bridgedNode);
286
210
  this.bridgedDevice.addClusterServer(this.getBridgedDeviceBasicInformation());
287
211
  return this.bridgedDevice;
@@ -302,26 +226,13 @@ export class ZigbeeEntity extends EventEmitter {
302
226
  addPowerSource() {
303
227
  if (!this.bridgedDevice)
304
228
  throw new Error('No bridged device');
305
- // Add PowerSource device type and cluster
306
229
  this.bridgedDevice.addDeviceType(powerSource);
307
230
  this.bridgedDevice.addClusterServer(this.getPowerSource());
308
231
  return this.bridgedDevice;
309
232
  }
310
- /**
311
- * Verifies that all required server clusters are present on the main endpoint and child endpoints.
312
- *
313
- * @param {MatterbridgeDevice} endpoint - The device endpoint to verify.
314
- * @returns {boolean} True if all required server clusters are present, false otherwise.
315
- *
316
- * @remarks
317
- * This method checks if all required server clusters are present on the main endpoint and its child endpoints.
318
- * It logs an error message if any required server cluster is missing and returns false. If all required server
319
- * clusters are present, it returns true.
320
- */
321
233
  verifyMutableDevice(endpoint) {
322
234
  if (!endpoint)
323
235
  return false;
324
- // Verify that all required server clusters are present in the main endpoint and in the child endpoints
325
236
  for (const deviceType of endpoint.getDeviceTypes()) {
326
237
  for (const clusterId of deviceType.requiredServerClusters) {
327
238
  if (!endpoint.getClusterServerById(clusterId)) {
@@ -330,7 +241,6 @@ export class ZigbeeEntity extends EventEmitter {
330
241
  }
331
242
  }
332
243
  }
333
- // Verify that all required server clusters are present in the child endpoints
334
244
  for (const childEndpoint of endpoint.getChildEndpoints()) {
335
245
  for (const deviceType of childEndpoint.getDeviceTypes()) {
336
246
  for (const clusterId of deviceType.requiredServerClusters) {
@@ -343,17 +253,6 @@ export class ZigbeeEntity extends EventEmitter {
343
253
  }
344
254
  return true;
345
255
  }
346
- /**
347
- * Configures the device by setting up the WindowCovering and DoorLock clusters if they are present.
348
- *
349
- * @returns {Promise<void>} A promise that resolves when the configuration is complete.
350
- *
351
- * @remarks
352
- * This method configures the device by checking for the presence of the WindowCovering and DoorLock clusters.
353
- * If the WindowCovering cluster is present, it sets the target as the current position and stops any ongoing
354
- * movement. If the DoorLock cluster is present, it retrieves the lock state and triggers the appropriate lock
355
- * operation event based on the current state.
356
- */
357
256
  async configure() {
358
257
  if (this.bridgedDevice?.getClusterServerById(WindowCovering.Cluster.id)) {
359
258
  this.log.info(`Configuring ${this.bridgedDevice?.deviceName} WindowCovering cluster`);
@@ -370,22 +269,6 @@ export class ZigbeeEntity extends EventEmitter {
370
269
  }
371
270
  }
372
271
  }
373
- /**
374
- * Updates the attribute of a cluster on a device endpoint if the value has changed.
375
- *
376
- * @param {Endpoint} deviceEndpoint - The device endpoint to update.
377
- * @param {string | undefined} childEndpointName - The name of the child endpoint, if any.
378
- * @param {number} clusterId - The ID of the cluster to update.
379
- * @param {string} attributeName - The name of the attribute to update.
380
- * @param {PayloadValue} value - The new value of the attribute.
381
- * @param {string[]} [lookup] - Optional lookup array for converting string values to indices.
382
- *
383
- * @remarks
384
- * This method checks if the specified attribute of a cluster on a device endpoint has changed. If the attribute
385
- * has changed, it updates the attribute with the new value. If a lookup array is provided, it converts string
386
- * values to their corresponding indices in the lookup array. The method logs the update process and handles any
387
- * errors that occur during the update.
388
- */
389
272
  updateAttributeIfChanged(deviceEndpoint, childEndpointName, clusterId, attributeName, value, lookup) {
390
273
  if (childEndpointName && childEndpointName !== '') {
391
274
  deviceEndpoint = this.bridgedDevice?.getChildEndpointByName(childEndpointName) ?? deviceEndpoint;
@@ -424,18 +307,6 @@ export class ZigbeeEntity extends EventEmitter {
424
307
  this.log.error(`Error setting attribute ${hk}${getClusterNameById(ClusterId(clusterId))}${er}.${hk}${attributeName}${er} to ${value}: ${error}`);
425
308
  }
426
309
  }
427
- /**
428
- * Publishes a command to the specified entity with the given payload.
429
- *
430
- * @param {string} command - The command to execute.
431
- * @param {string} entityName - The name of the entity to publish the command to.
432
- * @param {Payload} payload - The payload of the command.
433
- *
434
- * @remarks
435
- * This method logs the execution of the command and publishes the command to the specified entity.
436
- * If the entity name starts with 'bridge/request', it publishes the payload without a 'set' suffix.
437
- * Otherwise, it publishes the payload with a 'set' suffix.
438
- */
439
310
  publishCommand(command, entityName, payload) {
440
311
  this.log.debug(`executeCommand ${command} called for ${this.ien}${entityName}${rs}${db} payload: ${debugStringify(payload)}`);
441
312
  if (entityName.startsWith('bridge/request')) {
@@ -445,16 +316,7 @@ export class ZigbeeEntity extends EventEmitter {
445
316
  this.platform.publish(entityName, 'set', JSON.stringify(payload));
446
317
  }
447
318
  }
448
- /**
449
- * Logs the property map of the Zigbee entity.
450
- *
451
- * @remarks
452
- * This method iterates over the property map of the Zigbee entity and logs each property's details,
453
- * including its name, type, values, minimum and maximum values, unit, and endpoint.
454
- */
455
- // zigbeeDevice.propertyMap.set(property, { name, type, endpoint, category, description, label, unit, value_min, value_max, values: value });
456
319
  logPropertyMap() {
457
- // Log properties
458
320
  this.propertyMap.forEach((value, key) => {
459
321
  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} ` +
460
322
  `category ${CYAN}${value.category}${db} description ${CYAN}${value.description}${db} label ${CYAN}${value.label}${db} unit ${CYAN}${value.unit}${db} ` +
@@ -462,33 +324,10 @@ export class ZigbeeEntity extends EventEmitter {
462
324
  });
463
325
  }
464
326
  }
465
- /**
466
- * Represents a Zigbee group entity.
467
- *
468
- * @class
469
- * @extends {ZigbeeEntity}
470
- */
471
327
  export class ZigbeeGroup extends ZigbeeEntity {
472
- /**
473
- * Creates an instance of ZigbeeGroup.
474
- *
475
- * @param {ZigbeePlatform} platform - The Zigbee platform instance.
476
- * @param {BridgeGroup} group - The bridge group instance.
477
- */
478
328
  constructor(platform, group) {
479
329
  super(platform, group);
480
330
  }
481
- /**
482
- * Creates a new ZigbeeGroup instance.
483
- *
484
- * @param {ZigbeePlatform} platform - The Zigbee platform instance.
485
- * @param {BridgeGroup} group - The bridge group instance.
486
- * @returns {Promise<ZigbeeGroup>} A promise that resolves to the created ZigbeeGroup instance.
487
- *
488
- * @remarks
489
- * This method initializes a new ZigbeeGroup instance, sets up its properties, and configures the device
490
- * based on the group members. It also adds command handlers for the group.
491
- */
492
331
  static async create(platform, group) {
493
332
  const zigbeeGroup = new ZigbeeGroup(platform, group);
494
333
  if (zigbeeGroup.platform.postfixHostname) {
@@ -508,23 +347,19 @@ export class ZigbeeGroup extends ZigbeeEntity {
508
347
  let isCover = false;
509
348
  let isThermostat = false;
510
349
  if (group.members.length === 0) {
511
- // Create a virtual device for the empty group to use in automations
512
350
  zigbeeGroup.log.debug(`Group: ${gn}${group.friendly_name}${rs}${db} is a ${CYAN}virtual${db} group`);
513
- zigbeeGroup.bridgedDevice = await zigbeeGroup.createMutableDevice([onOffSwitch], { uniqueStorageKey: group.friendly_name }, zigbeeGroup.log.logLevel === "debug" /* LogLevel.DEBUG */);
351
+ zigbeeGroup.bridgedDevice = await zigbeeGroup.createMutableDevice([onOffSwitch], { uniqueStorageKey: group.friendly_name }, zigbeeGroup.log.logLevel === "debug");
514
352
  isSwitch = true;
515
353
  zigbeeGroup.propertyMap.set('state', { name: 'state', type: 'switch', endpoint: '' });
516
354
  }
517
355
  else {
518
- // Create a switch or light or outlet device for the group
519
356
  group.members.forEach((member) => {
520
- // const device = zigbeeGroup.platform.z2m.getDevice(member.ieee_address);
521
357
  const device = zigbeeGroup.platform.z2mBridgeDevices?.find((device) => device.ieee_address === member.ieee_address);
522
358
  if (!device)
523
359
  return;
524
360
  zigbeeGroup.log.debug(`Group ${gn}${group.friendly_name}${db}: member device ${dn}${device.friendly_name}${db}`);
525
361
  device.definition?.exposes.forEach((expose) => {
526
362
  if (expose.features) {
527
- // Specific features with type
528
363
  expose.features?.forEach((feature) => {
529
364
  if (expose.type === 'lock' && feature.name === 'state' && feature.property === 'child_lock') {
530
365
  expose.type = 'child_lock';
@@ -554,7 +389,6 @@ export class ZigbeeGroup extends ZigbeeEntity {
554
389
  });
555
390
  }
556
391
  else {
557
- // Generic features without type
558
392
  zigbeeGroup.log.debug(`- generic type ${CYAN}${expose.type}${db} expose name ${CYAN}${expose.name}${db} property ${CYAN}${expose.property}${db}`);
559
393
  }
560
394
  });
@@ -600,17 +434,14 @@ export class ZigbeeGroup extends ZigbeeEntity {
600
434
  }
601
435
  if (!deviceType)
602
436
  return zigbeeGroup;
603
- zigbeeGroup.bridgedDevice = await zigbeeGroup.createMutableDevice([deviceType], { uniqueStorageKey: group.friendly_name }, zigbeeGroup.log.logLevel === "debug" /* LogLevel.DEBUG */);
437
+ zigbeeGroup.bridgedDevice = await zigbeeGroup.createMutableDevice([deviceType], { uniqueStorageKey: group.friendly_name }, zigbeeGroup.log.logLevel === "debug");
604
438
  }
605
439
  zigbeeGroup.addBridgedDeviceBasicInformation();
606
440
  zigbeeGroup.addPowerSource();
607
441
  zigbeeGroup.bridgedDevice.addRequiredClusterServers(zigbeeGroup.bridgedDevice);
608
- // Verify the device
609
442
  if (!zigbeeGroup.bridgedDevice || !zigbeeGroup.verifyMutableDevice(zigbeeGroup.bridgedDevice))
610
443
  return zigbeeGroup;
611
- // Log properties
612
444
  zigbeeGroup.logPropertyMap();
613
- // Add command handlers
614
445
  if (isSwitch || isLight) {
615
446
  if (isSwitch)
616
447
  await zigbeeGroup.bridgedDevice.addFixedLabel('type', 'switch');
@@ -618,7 +449,6 @@ export class ZigbeeGroup extends ZigbeeEntity {
618
449
  await zigbeeGroup.bridgedDevice.addFixedLabel('type', 'light');
619
450
  zigbeeGroup.bridgedDevice.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
620
451
  zigbeeGroup.log.warn(`Command identify called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db} identifyTime:${identifyTime}`);
621
- // logEndpoint(zigbeeGroup.bridgedDevice!);
622
452
  });
623
453
  zigbeeGroup.bridgedDevice.addCommandHandler('on', async () => {
624
454
  zigbeeGroup.log.debug(`Command on called for ${zigbeeGroup.ien}${group.friendly_name}${rs}${db}`);
@@ -710,7 +540,6 @@ export class ZigbeeGroup extends ZigbeeEntity {
710
540
  zigbeeGroup.bridgedDevice.subscribeAttribute(ThermostatCluster.id, 'systemMode', (newValue, oldValue) => {
711
541
  zigbeeGroup.bridgedDevice?.log.info(`Thermostat systemMode changed from ${oldValue} to ${newValue}`);
712
542
  if (oldValue !== newValue) {
713
- // Thermostat.SystemMode.Heat && newValue === Thermostat.SystemMode.Off
714
543
  zigbeeGroup.bridgedDevice?.log.info(`Setting thermostat systemMode to ${newValue}`);
715
544
  if (newValue === Thermostat.SystemMode.Off) {
716
545
  zigbeeGroup.publishCommand('SystemMode', group.friendly_name, { system_mode: 'off' });
@@ -751,7 +580,6 @@ export class ZigbeeGroup extends ZigbeeEntity {
751
580
  return zigbeeGroup;
752
581
  }
753
582
  }
754
- // prettier-ignore
755
583
  export const z2ms = [
756
584
  { type: 'switch', name: 'state', property: 'state', deviceType: onOffSwitch, cluster: OnOff.Cluster.id, attribute: 'onOff', converter: (value) => { return value === 'ON' ? true : false; } },
757
585
  { type: 'switch', name: 'brightness', property: 'brightness', deviceType: dimmableSwitch, cluster: LevelControl.Cluster.id, attribute: 'currentLevel', converter: (value) => { return Math.max(0, Math.min(254, value)); } },
@@ -811,43 +639,19 @@ export const z2ms = [
811
639
  { type: '', name: 'voltage', property: 'voltage', deviceType: electricalSensor, cluster: ElectricalPowerMeasurement.Cluster.id, attribute: 'voltage', converter: (value) => { return value * 1000; } },
812
640
  { type: '', name: 'current', property: 'current', deviceType: electricalSensor, cluster: ElectricalPowerMeasurement.Cluster.id, attribute: 'activeCurrent', converter: (value) => { return value * 1000; } },
813
641
  ];
814
- /**
815
- * Represents a Zigbee device entity.
816
- *
817
- * @class
818
- * @extends {ZigbeeEntity}
819
- */
820
642
  export class ZigbeeDevice extends ZigbeeEntity {
821
- /**
822
- * Represents a Zigbee device entity.
823
- *
824
- * @class
825
- * @extends {ZigbeeEntity}
826
- */
827
643
  constructor(platform, device) {
828
644
  super(platform, device);
829
645
  }
830
- /**
831
- * Creates a new ZigbeeDevice instance.
832
- *
833
- * @param {ZigbeePlatform} platform - The Zigbee platform instance.
834
- * @param {BridgeDevice} device - The bridge device instance.
835
- * @returns {Promise<ZigbeeDevice>} A promise that resolves to the created ZigbeeDevice instance.
836
- *
837
- * @remarks
838
- * This method initializes a new ZigbeeDevice instance, sets up its properties, and configures the device
839
- * based on the device definition and options. It also adds command handlers for the device.
840
- */
841
646
  static async create(platform, device) {
842
647
  const zigbeeDevice = new ZigbeeDevice(platform, device);
843
648
  zigbeeDevice.serial = `${device.ieee_address}`;
844
649
  if (zigbeeDevice.platform.postfixHostname) {
845
650
  zigbeeDevice.serial = `${zigbeeDevice.serial}_${hostname}`.slice(0, 32);
846
651
  }
847
- // Set Coordinator and dedicated routers
848
652
  if (device.friendly_name === 'Coordinator' || (device.model_id === 'ti.router' && device.manufacturer === 'TexasInstruments') || (device.model_id.startsWith('SLZB-') && device.manufacturer === 'SMLIGHT')) {
849
653
  zigbeeDevice.isRouter = true;
850
- zigbeeDevice.bridgedDevice = await zigbeeDevice.createMutableDevice([doorLockDevice], { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug" /* LogLevel.DEBUG */);
654
+ zigbeeDevice.bridgedDevice = await zigbeeDevice.createMutableDevice([doorLockDevice], { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
851
655
  zigbeeDevice.addBridgedDeviceBasicInformation();
852
656
  zigbeeDevice.addPowerSource();
853
657
  zigbeeDevice.bridgedDevice.addRequiredClusterServers(zigbeeDevice.bridgedDevice);
@@ -865,7 +669,6 @@ export class ZigbeeDevice extends ZigbeeEntity {
865
669
  });
866
670
  return zigbeeDevice;
867
671
  }
868
- // Get types and properties
869
672
  const types = [];
870
673
  const endpoints = [];
871
674
  const names = [];
@@ -879,7 +682,6 @@ export class ZigbeeDevice extends ZigbeeEntity {
879
682
  const values = [];
880
683
  device.definition?.exposes.forEach((expose) => {
881
684
  if (expose.features) {
882
- // Specific features with type
883
685
  expose.features?.forEach((feature) => {
884
686
  if (expose.type === 'lock' && feature.name === 'state' && feature.property === 'child_lock')
885
687
  feature.name = 'child_lock';
@@ -897,17 +699,10 @@ export class ZigbeeDevice extends ZigbeeEntity {
897
699
  });
898
700
  }
899
701
  else {
900
- // Generic features without type
901
- // Change voltage to battery_voltage for battery powered devices
902
702
  if (device.power_source === 'Battery' && expose.name === 'voltage')
903
703
  expose.name = 'battery_voltage';
904
704
  if (device.power_source === 'Battery' && expose.property === 'voltage')
905
705
  expose.property = 'battery_voltage';
906
- // Fix illuminance and illuminance_lux for light sensors:
907
- // illuminance is raw value (use like it is)
908
- // illuminance_lux is in lux (convert with log10)
909
- // illuminance has description "Raw measured illuminance"
910
- // illuminance_lux has description "Measured illuminance in lux"
911
706
  if (expose.description === 'Raw measured illuminance') {
912
707
  expose.name = 'illuminance';
913
708
  expose.property = 'illuminance';
@@ -968,16 +763,6 @@ export class ZigbeeDevice extends ZigbeeEntity {
968
763
  zigbeeDevice.ignoreFeatures = [...zigbeeDevice.ignoreFeatures, ...platform.featureBlackList];
969
764
  if (platform.deviceFeatureBlackList[device.friendly_name])
970
765
  zigbeeDevice.ignoreFeatures = [...zigbeeDevice.ignoreFeatures, ...platform.deviceFeatureBlackList[device.friendly_name]];
971
- /*
972
- zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - types[${types.length}]: ${debugStringify(types)}`);
973
- zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - endpoints[${endpoints.length}]: ${debugStringify(endpoints)}`);
974
- zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - names[${names.length}]: ${debugStringify(names)}`);
975
- zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - properties[${properties.length}]: ${debugStringify(properties)}`);
976
- zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - categories[${categories.length}]: ${debugStringify(categories)}`);
977
- zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - descriptions[${descriptions.length}]: ${debugStringify(descriptions)}`);
978
- zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - labels[${labels.length}]: ${debugStringify(labels)}`);
979
- zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} - units[${units.length}]: ${debugStringify(units)}`);
980
- */
981
766
  for (const [index, name] of names.entries()) {
982
767
  if (platform.featureBlackList.includes(name)) {
983
768
  zigbeeDevice.log.debug(`Device ${zigbeeDevice.en}${device.friendly_name}${db} feature ${name} is globally blacklisted`);
@@ -1006,7 +791,6 @@ export class ZigbeeDevice extends ZigbeeEntity {
1006
791
  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}`);
1007
792
  zigbeeDevice.propertyMap.set(property, { name, type, endpoint, category, description, label, unit, value_min, value_max, values: value });
1008
793
  if (endpoint === '') {
1009
- /* prettier-ignore */
1010
794
  if (!zigbeeDevice.mutableDevice.has(endpoint)) {
1011
795
  zigbeeDevice.mutableDevice.set(endpoint, { tagList: [], deviceTypes: [z2m.deviceType], clusterServersIds: [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)], clusterServersObjs: [], clusterClientsIds: [], clusterClientsObjs: [] });
1012
796
  }
@@ -1030,7 +814,6 @@ export class ZigbeeDevice extends ZigbeeEntity {
1030
814
  if (endpoint === 'l6')
1031
815
  tagList.push({ mfgCode: null, namespaceId: NumberTag.Six.namespaceId, tag: NumberTag.Six.tag, label: 'endpoint ' + endpoint });
1032
816
  tagList.push({ mfgCode: null, namespaceId: SwitchesTag.Custom.namespaceId, tag: SwitchesTag.Custom.tag, label: 'endpoint ' + endpoint });
1033
- /* prettier-ignore */
1034
817
  if (!zigbeeDevice.mutableDevice.has(endpoint)) {
1035
818
  zigbeeDevice.mutableDevice.set(endpoint, { tagList, deviceTypes: [z2m.deviceType], clusterServersIds: [...z2m.deviceType.requiredServerClusters, ClusterId(z2m.cluster)], clusterServersObjs: [], clusterClientsIds: [], clusterClientsObjs: [] });
1036
819
  }
@@ -1044,16 +827,13 @@ export class ZigbeeDevice extends ZigbeeEntity {
1044
827
  }
1045
828
  }
1046
829
  else {
1047
- // 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`);
1048
830
  }
1049
- // Map actions to switches
1050
831
  if (name === 'action' && zigbeeDevice.actions.length) {
1051
832
  zigbeeDevice.log.info(`Device ${zigbeeDevice.ien}${device.friendly_name}${rs}${nf} has actions mapped to these switches on sub endpoints:`);
1052
833
  zigbeeDevice.log.info(' controller events <=> zigbee2mqtt actions');
1053
834
  if (!zigbeeDevice.bridgedDevice)
1054
- zigbeeDevice.bridgedDevice = await zigbeeDevice.createMutableDevice([bridgedNode], { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug" /* LogLevel.DEBUG */);
835
+ zigbeeDevice.bridgedDevice = await zigbeeDevice.createMutableDevice([bridgedNode], { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
1055
836
  zigbeeDevice.hasEndpoints = true;
1056
- // Mapping actions
1057
837
  const switchMap = ['Single Press', 'Double Press', 'Long Press '];
1058
838
  const triggerMap = ['Single', 'Double', 'Long'];
1059
839
  let count = 1;
@@ -1086,7 +866,6 @@ export class ZigbeeDevice extends ZigbeeEntity {
1086
866
  zigbeeDevice.composedType = 'button';
1087
867
  }
1088
868
  }
1089
- // Add battery properties
1090
869
  if (device.power_source === 'Battery') {
1091
870
  zigbeeDevice.propertyMap.set('battery', { name: 'battery', type: '', endpoint: '' });
1092
871
  zigbeeDevice.propertyMap.set('battery_low', { name: 'battery_low', type: '', endpoint: '' });
@@ -1097,7 +876,6 @@ export class ZigbeeDevice extends ZigbeeEntity {
1097
876
  const mainEndpoint = zigbeeDevice.mutableDevice.get('');
1098
877
  if (!mainEndpoint)
1099
878
  return zigbeeDevice;
1100
- // Remove superset device Types and order them
1101
879
  for (const device of zigbeeDevice.mutableDevice.values()) {
1102
880
  const deviceTypesMap = new Map();
1103
881
  device.deviceTypes.forEach((deviceType) => {
@@ -1113,20 +891,15 @@ export class ZigbeeDevice extends ZigbeeEntity {
1113
891
  deviceTypesMap.delete(onOffLight.code);
1114
892
  if (deviceTypesMap.has(dimmableLight.code) && deviceTypesMap.has(colorTemperatureLight.code))
1115
893
  deviceTypesMap.delete(dimmableLight.code);
1116
- device.deviceTypes = Array.from(deviceTypesMap.values()); /* .sort((a, b) => b.code - a.code);*/
894
+ device.deviceTypes = Array.from(deviceTypesMap.values());
1117
895
  }
1118
- // Create the mutable device for the main endpoint
1119
- zigbeeDevice.bridgedDevice = await zigbeeDevice.createMutableDevice(mainEndpoint.deviceTypes, { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug" /* LogLevel.DEBUG */);
1120
- // Configure BridgedDeviceBasicInformation cluster
896
+ zigbeeDevice.bridgedDevice = await zigbeeDevice.createMutableDevice(mainEndpoint.deviceTypes, { uniqueStorageKey: device.friendly_name }, zigbeeDevice.log.logLevel === "debug");
1121
897
  mainEndpoint.clusterServersObjs.push(zigbeeDevice.getBridgedDeviceBasicInformation());
1122
- // Configure PowerSource cluster
1123
898
  mainEndpoint.clusterServersObjs.push(zigbeeDevice.getPowerSource());
1124
- // Configure ColorControlCluster
1125
899
  if (mainEndpoint.clusterServersIds.includes(ColorControl.Cluster.id)) {
1126
900
  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')}`);
1127
901
  zigbeeDevice.bridgedDevice.configureColorControlCluster(names.includes('color_hs') || names.includes('color_xy'), false, names.includes('color_temp'));
1128
902
  }
1129
- // Configure ThermostatCluster
1130
903
  if (mainEndpoint.clusterServersIds.includes(Thermostat.Cluster.id)) {
1131
904
  const heat = zigbeeDevice.propertyMap.get('occupied_heating_setpoint') || zigbeeDevice.propertyMap.get('current_heating_setpoint');
1132
905
  const cool = zigbeeDevice.propertyMap.get('occupied_cooling_setpoint') || zigbeeDevice.propertyMap.get('current_cooling_setpoint');
@@ -1137,20 +910,18 @@ export class ZigbeeDevice extends ZigbeeEntity {
1137
910
  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} ` +
1138
911
  `minHeating ${CYAN}${minHeating}${db} maxHeating ${CYAN}${maxHeating}${db} minCooling ${CYAN}${minCooling}${db} maxCooling ${CYAN}${maxCooling}${db}`);
1139
912
  if (heat && !cool) {
1140
- zigbeeDevice.propertyMap.delete('running_state'); // Remove running_state if only heating is supported cause it's not supported by the cluster without AutoMode
913
+ zigbeeDevice.propertyMap.delete('running_state');
1141
914
  mainEndpoint.clusterServersObjs.push(zigbeeDevice.bridgedDevice.getDefaultHeatingThermostatClusterServer(undefined, undefined, minHeating, maxHeating));
1142
915
  }
1143
916
  else if (!heat && cool) {
1144
- zigbeeDevice.propertyMap.delete('running_state'); // Remove running_state if only cooling is supported cause it's not supported by the cluster without AutoMode
917
+ zigbeeDevice.propertyMap.delete('running_state');
1145
918
  mainEndpoint.clusterServersObjs.push(zigbeeDevice.bridgedDevice.getDefaultCoolingThermostatClusterServer(undefined, undefined, minCooling, maxCooling));
1146
919
  }
1147
920
  else if (heat && cool) {
1148
921
  mainEndpoint.clusterServersObjs.push(zigbeeDevice.bridgedDevice.getDefaultThermostatClusterServer(undefined, undefined, undefined, undefined, minHeating, maxHeating, minCooling, maxCooling));
1149
922
  }
1150
923
  }
1151
- // Filter out duplicate server clusters and server clusters objects
1152
924
  for (const [endpoint, device] of zigbeeDevice.mutableDevice) {
1153
- // Filter out duplicate server clusters and server clusters objects
1154
925
  const deviceClusterServersMap = new Map();
1155
926
  device.clusterServersIds.forEach((clusterServer) => {
1156
927
  deviceClusterServersMap.set(clusterServer, clusterServer);
@@ -1162,7 +933,6 @@ export class ZigbeeDevice extends ZigbeeEntity {
1162
933
  });
1163
934
  device.clusterServersIds = Array.from(deviceClusterServersMap.values());
1164
935
  device.clusterServersObjs = Array.from(deviceClusterServersObjMap.values());
1165
- // Filter out duplicate client clusters and client clusters objects
1166
936
  const deviceClusterClientsMap = new Map();
1167
937
  device.clusterClientsIds.forEach((clusterClient) => {
1168
938
  deviceClusterClientsMap.set(clusterClient, clusterClient);
@@ -1177,7 +947,6 @@ export class ZigbeeDevice extends ZigbeeEntity {
1177
947
  zigbeeDevice.log.debug(`Device ${zigbeeDevice.ien}${zigbeeDevice.device?.friendly_name}${rs}${db} endpoint: ${ign}${endpoint === '' ? 'main' : endpoint}${rs}${db} => ` +
1178
948
  `${nf}tagList: ${debugStringify(device.tagList)} deviceTypes: ${debugStringify(device.deviceTypes)} clusterServersIds: ${debugStringify(device.clusterServersIds)}`);
1179
949
  }
1180
- // Add the clusters to the main endpoint
1181
950
  mainEndpoint.clusterServersObjs.forEach((clusterServerObj) => {
1182
951
  zigbeeDevice.bridgedDevice?.addClusterServer(clusterServerObj);
1183
952
  });
@@ -1185,24 +954,19 @@ export class ZigbeeDevice extends ZigbeeEntity {
1185
954
  zigbeeDevice.bridgedDevice.addRequiredClusterServers(zigbeeDevice.bridgedDevice);
1186
955
  if (zigbeeDevice.composedType !== '')
1187
956
  await zigbeeDevice.bridgedDevice.addFixedLabel('composed', zigbeeDevice.composedType);
1188
- // Create the child endpoints
1189
957
  for (const [endpoint, device] of zigbeeDevice.mutableDevice) {
1190
958
  if (endpoint === '')
1191
959
  continue;
1192
- const child = zigbeeDevice.bridgedDevice?.addChildDeviceTypeWithClusterServer(endpoint, device.deviceTypes, device.clusterServersIds, { tagList: device.tagList }, zigbeeDevice.log.logLevel === "debug" /* LogLevel.DEBUG */);
960
+ const child = zigbeeDevice.bridgedDevice?.addChildDeviceTypeWithClusterServer(endpoint, device.deviceTypes, device.clusterServersIds, { tagList: device.tagList }, zigbeeDevice.log.logLevel === "debug");
1193
961
  device.clusterServersObjs.forEach((clusterServerObj) => {
1194
962
  child.addClusterServer(clusterServerObj);
1195
963
  });
1196
964
  }
1197
- // Verify the device
1198
965
  if (!zigbeeDevice.verifyMutableDevice(zigbeeDevice.bridgedDevice))
1199
966
  return zigbeeDevice;
1200
- // Log properties
1201
967
  zigbeeDevice.logPropertyMap();
1202
- // Add command handlers
1203
968
  zigbeeDevice.bridgedDevice.addCommandHandler('identify', async (data) => {
1204
969
  zigbeeDevice.log.debug(`Command identify called for ${zigbeeDevice.ien}${device.friendly_name}${rs}${db} endpoint: ${data.endpoint.number} request identifyTime:${data.request.identifyTime} `);
1205
- // logEndpoint(zigbeeDevice.bridgedDevice!);
1206
970
  });
1207
971
  if (zigbeeDevice.bridgedDevice.getClusterServerById(OnOffCluster.id) || zigbeeDevice.hasEndpoints) {
1208
972
  for (const child of zigbeeDevice.bridgedDevice.getChildEndpoints()) {
@@ -1464,4 +1228,3 @@ export class ZigbeeDevice extends ZigbeeEntity {
1464
1228
  return zigbeeDevice;
1465
1229
  }
1466
1230
  }
1467
- //# sourceMappingURL=entity.js.map