matterbridge 2.1.0-dev.1 → 2.1.0-dev.10

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.
@@ -1,18 +1,14 @@
1
- import { createHash } from 'crypto';
2
1
  import { AnsiLogger, BLUE, CYAN, YELLOW, db, debugStringify, er, hk, or, zb } from './logger/export.js';
3
2
  import { bridgedNode } from './matterbridgeDeviceTypes.js';
4
- import { deepCopy, isValidNumber } from './utils/utils.js';
5
- import { MatterbridgeBehavior, MatterbridgeBehaviorDevice, MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, } from './matterbridgeBehaviors.js';
3
+ import { isValidNumber, isValidObject } from './utils/utils.js';
4
+ import { MatterbridgeBehavior, MatterbridgeBehaviorDevice, MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, MatterbridgeSwitchServer, } from './matterbridgeBehaviors.js';
5
+ import { addClusterServers, addFixedLabel, addOptionalClusterServers, addRequiredClusterServers, addUserLabel, capitalizeFirstLetter, createUniqueId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, getDefaultOccupancySensingClusterServer, lowercaseFirstLetter, updateAttribute, getClusterId, getAttributeId, setAttribute, getAttribute, } from './matterbridgeEndpointHelpers.js';
6
6
  import { Endpoint, Lifecycle, MutableEndpoint, NamedHandler, SupportedBehaviors, VendorId } from '@matter/main';
7
7
  import { getClusterNameById, MeasurementType } from '@matter/main/types';
8
8
  import { Descriptor } from '@matter/main/clusters/descriptor';
9
9
  import { PowerSource } from '@matter/main/clusters/power-source';
10
- import { UserLabel } from '@matter/main/clusters/user-label';
11
- import { FixedLabel } from '@matter/main/clusters/fixed-label';
12
- import { BasicInformation } from '@matter/main/clusters/basic-information';
13
10
  import { BridgedDeviceBasicInformation } from '@matter/main/clusters/bridged-device-basic-information';
14
11
  import { Identify } from '@matter/main/clusters/identify';
15
- import { Groups } from '@matter/main/clusters/groups';
16
12
  import { OnOff } from '@matter/main/clusters/on-off';
17
13
  import { LevelControl } from '@matter/main/clusters/level-control';
18
14
  import { ColorControl } from '@matter/main/clusters/color-control';
@@ -20,39 +16,18 @@ import { WindowCovering } from '@matter/main/clusters/window-covering';
20
16
  import { Thermostat } from '@matter/main/clusters/thermostat';
21
17
  import { FanControl } from '@matter/main/clusters/fan-control';
22
18
  import { DoorLock } from '@matter/main/clusters/door-lock';
23
- import { ModeSelect } from '@matter/main/clusters/mode-select';
24
19
  import { ValveConfigurationAndControl } from '@matter/main/clusters/valve-configuration-and-control';
25
20
  import { PumpConfigurationAndControl } from '@matter/main/clusters/pump-configuration-and-control';
26
21
  import { SmokeCoAlarm } from '@matter/main/clusters/smoke-co-alarm';
27
22
  import { Switch } from '@matter/main/clusters/switch';
28
- import { BooleanState } from '@matter/main/clusters/boolean-state';
29
23
  import { BooleanStateConfiguration } from '@matter/main/clusters/boolean-state-configuration';
30
24
  import { PowerTopology } from '@matter/main/clusters/power-topology';
31
25
  import { ElectricalPowerMeasurement } from '@matter/main/clusters/electrical-power-measurement';
32
26
  import { ElectricalEnergyMeasurement } from '@matter/main/clusters/electrical-energy-measurement';
33
- import { TemperatureMeasurement } from '@matter/main/clusters/temperature-measurement';
34
- import { RelativeHumidityMeasurement } from '@matter/main/clusters/relative-humidity-measurement';
35
- import { PressureMeasurement } from '@matter/main/clusters/pressure-measurement';
36
- import { FlowMeasurement } from '@matter/main/clusters/flow-measurement';
37
- import { IlluminanceMeasurement } from '@matter/main/clusters/illuminance-measurement';
38
- import { OccupancySensing } from '@matter/main/clusters/occupancy-sensing';
39
27
  import { AirQuality } from '@matter/main/clusters/air-quality';
40
- import { CarbonMonoxideConcentrationMeasurement } from '@matter/main/clusters/carbon-monoxide-concentration-measurement';
41
- import { CarbonDioxideConcentrationMeasurement } from '@matter/main/clusters/carbon-dioxide-concentration-measurement';
42
- import { NitrogenDioxideConcentrationMeasurement } from '@matter/main/clusters/nitrogen-dioxide-concentration-measurement';
43
- import { OzoneConcentrationMeasurement } from '@matter/main/clusters/ozone-concentration-measurement';
44
- import { FormaldehydeConcentrationMeasurement } from '@matter/main/clusters/formaldehyde-concentration-measurement';
45
- import { Pm1ConcentrationMeasurement } from '@matter/main/clusters/pm1-concentration-measurement';
46
- import { Pm25ConcentrationMeasurement } from '@matter/main/clusters/pm25-concentration-measurement';
47
- import { Pm10ConcentrationMeasurement } from '@matter/main/clusters/pm10-concentration-measurement';
48
- import { RadonConcentrationMeasurement } from '@matter/main/clusters/radon-concentration-measurement';
49
- import { TotalVolatileOrganicCompoundsConcentrationMeasurement } from '@matter/main/clusters/total-volatile-organic-compounds-concentration-measurement';
50
28
  import { ConcentrationMeasurement } from '@matter/main/clusters/concentration-measurement';
51
29
  import { DescriptorServer } from '@matter/main/behaviors/descriptor';
52
30
  import { PowerSourceServer } from '@matter/main/behaviors/power-source';
53
- import { UserLabelServer } from '@matter/main/behaviors/user-label';
54
- import { FixedLabelServer } from '@matter/main/behaviors/fixed-label';
55
- import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
56
31
  import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
57
32
  import { GroupsServer } from '@matter/main/behaviors/groups';
58
33
  import { ScenesManagementServer } from '@matter/main/behaviors/scenes-management';
@@ -79,242 +54,6 @@ import { Pm25ConcentrationMeasurementServer } from '@matter/main/behaviors/pm25-
79
54
  import { Pm10ConcentrationMeasurementServer } from '@matter/main/behaviors/pm10-concentration-measurement';
80
55
  import { RadonConcentrationMeasurementServer } from '@matter/main/behaviors/radon-concentration-measurement';
81
56
  import { TotalVolatileOrganicCompoundsConcentrationMeasurementServer } from '@matter/main/behaviors/total-volatile-organic-compounds-concentration-measurement';
82
- function capitalizeFirstLetter(name) {
83
- if (!name)
84
- return name;
85
- return name.charAt(0).toUpperCase() + name.slice(1);
86
- }
87
- function lowercaseFirstLetter(name) {
88
- if (!name)
89
- return name;
90
- return name.charAt(0).toLowerCase() + name.slice(1);
91
- }
92
- function createUniqueId(param1, param2, param3, param4) {
93
- const hash = createHash('md5');
94
- hash.update(param1 + param2 + param3 + param4);
95
- return hash.digest('hex');
96
- }
97
- function getBehaviourTypesFromClusterServerIds(clusterServerList) {
98
- const behaviorTypes = [];
99
- clusterServerList.forEach((clusterId) => {
100
- behaviorTypes.push(getBehaviourTypeFromClusterServerId(clusterId));
101
- });
102
- return behaviorTypes;
103
- }
104
- function getBehaviourTypesFromClusterClientIds(clusterClientList) {
105
- const behaviorTypes = [];
106
- clusterClientList.forEach((clusterId) => {
107
- });
108
- return behaviorTypes;
109
- }
110
- function getBehaviourTypeFromClusterServerId(clusterId) {
111
- if (clusterId === PowerSource.Cluster.id)
112
- return PowerSourceServer.with(PowerSource.Feature.Wired);
113
- if (clusterId === UserLabel.Cluster.id)
114
- return UserLabelServer;
115
- if (clusterId === FixedLabel.Cluster.id)
116
- return FixedLabelServer;
117
- if (clusterId === BasicInformation.Cluster.id)
118
- return BasicInformationServer;
119
- if (clusterId === BridgedDeviceBasicInformation.Cluster.id)
120
- return BridgedDeviceBasicInformationServer;
121
- if (clusterId === Identify.Cluster.id)
122
- return MatterbridgeIdentifyServer;
123
- if (clusterId === Groups.Cluster.id)
124
- return GroupsServer;
125
- if (clusterId === OnOff.Cluster.id)
126
- return MatterbridgeOnOffServer.with('Lighting');
127
- if (clusterId === LevelControl.Cluster.id)
128
- return MatterbridgeLevelControlServer.with('OnOff', 'Lighting');
129
- if (clusterId === ColorControl.Cluster.id)
130
- return MatterbridgeColorControlServer;
131
- if (clusterId === WindowCovering.Cluster.id)
132
- return MatterbridgeWindowCoveringServer.with('Lift', 'PositionAwareLift');
133
- if (clusterId === Thermostat.Cluster.id)
134
- return MatterbridgeThermostatServer.with('AutoMode', 'Heating', 'Cooling');
135
- if (clusterId === FanControl.Cluster.id)
136
- return MatterbridgeFanControlServer;
137
- if (clusterId === DoorLock.Cluster.id)
138
- return MatterbridgeDoorLockServer;
139
- if (clusterId === ModeSelect.Cluster.id)
140
- return MatterbridgeModeSelectServer;
141
- if (clusterId === ValveConfigurationAndControl.Cluster.id)
142
- return MatterbridgeValveConfigurationAndControlServer.with('Level');
143
- if (clusterId === PumpConfigurationAndControl.Cluster.id)
144
- return PumpConfigurationAndControlServer.with('ConstantSpeed');
145
- if (clusterId === SmokeCoAlarm.Cluster.id)
146
- return MatterbridgeSmokeCoAlarmServer.with('SmokeAlarm', 'CoAlarm');
147
- if (clusterId === Switch.Cluster.id)
148
- return SwitchServer.with('MomentarySwitch', 'MomentarySwitchRelease', 'MomentarySwitchLongPress', 'MomentarySwitchMultiPress');
149
- if (clusterId === BooleanState.Cluster.id)
150
- return BooleanStateServer.enable({ events: { stateChange: true } });
151
- if (clusterId === BooleanStateConfiguration.Cluster.id)
152
- return MatterbridgeBooleanStateConfigurationServer;
153
- if (clusterId === PowerTopology.Cluster.id)
154
- return PowerTopologyServer.with('TreeTopology');
155
- if (clusterId === ElectricalPowerMeasurement.Cluster.id)
156
- return ElectricalPowerMeasurementServer.with('AlternatingCurrent');
157
- if (clusterId === ElectricalEnergyMeasurement.Cluster.id)
158
- return ElectricalEnergyMeasurementServer.with('ImportedEnergy', 'ExportedEnergy', 'CumulativeEnergy');
159
- if (clusterId === TemperatureMeasurement.Cluster.id)
160
- return TemperatureMeasurementServer;
161
- if (clusterId === RelativeHumidityMeasurement.Cluster.id)
162
- return RelativeHumidityMeasurementServer;
163
- if (clusterId === PressureMeasurement.Cluster.id)
164
- return PressureMeasurementServer;
165
- if (clusterId === FlowMeasurement.Cluster.id)
166
- return FlowMeasurementServer;
167
- if (clusterId === IlluminanceMeasurement.Cluster.id)
168
- return IlluminanceMeasurementServer;
169
- if (clusterId === OccupancySensing.Cluster.id)
170
- return OccupancySensingServer;
171
- if (clusterId === AirQuality.Cluster.id)
172
- return AirQualityServer.with('Fair', 'Moderate', 'VeryPoor', 'ExtremelyPoor');
173
- if (clusterId === CarbonMonoxideConcentrationMeasurement.Cluster.id)
174
- return CarbonMonoxideConcentrationMeasurementServer.with('NumericMeasurement');
175
- if (clusterId === CarbonDioxideConcentrationMeasurement.Cluster.id)
176
- return CarbonDioxideConcentrationMeasurementServer.with('NumericMeasurement');
177
- if (clusterId === NitrogenDioxideConcentrationMeasurement.Cluster.id)
178
- return NitrogenDioxideConcentrationMeasurementServer.with('NumericMeasurement');
179
- if (clusterId === OzoneConcentrationMeasurement.Cluster.id)
180
- return OzoneConcentrationMeasurementServer.with('NumericMeasurement');
181
- if (clusterId === FormaldehydeConcentrationMeasurement.Cluster.id)
182
- return FormaldehydeConcentrationMeasurementServer.with('NumericMeasurement');
183
- if (clusterId === Pm1ConcentrationMeasurement.Cluster.id)
184
- return Pm1ConcentrationMeasurementServer.with('NumericMeasurement');
185
- if (clusterId === Pm25ConcentrationMeasurement.Cluster.id)
186
- return Pm25ConcentrationMeasurementServer.with('NumericMeasurement');
187
- if (clusterId === Pm10ConcentrationMeasurement.Cluster.id)
188
- return Pm10ConcentrationMeasurementServer.with('NumericMeasurement');
189
- if (clusterId === RadonConcentrationMeasurement.Cluster.id)
190
- return RadonConcentrationMeasurementServer.with('NumericMeasurement');
191
- if (clusterId === TotalVolatileOrganicCompoundsConcentrationMeasurement.Cluster.id)
192
- return TotalVolatileOrganicCompoundsConcentrationMeasurementServer.with('NumericMeasurement');
193
- return MatterbridgeIdentifyServer;
194
- }
195
- function getBehaviourTypeFromClusterClientId(clusterId) {
196
- }
197
- function getBehavior(endpoint, cluster) {
198
- let behavior;
199
- if (typeof cluster === 'string') {
200
- behavior = endpoint.behaviors.supported[lowercaseFirstLetter(cluster)];
201
- }
202
- else if (typeof cluster === 'number') {
203
- behavior = endpoint.behaviors.supported[lowercaseFirstLetter(getClusterNameById(cluster))];
204
- }
205
- else if (typeof cluster === 'object') {
206
- behavior = endpoint.behaviors.supported[lowercaseFirstLetter(cluster.name)];
207
- }
208
- else if (typeof cluster === 'function') {
209
- behavior = cluster;
210
- }
211
- return behavior;
212
- }
213
- function addRequiredClusterServers(endpoint) {
214
- const requiredServerList = [];
215
- endpoint.log.debug(`addRequiredClusterServers for ${CYAN}${endpoint.maybeId}${db}`);
216
- endpoint.deviceTypes.values().forEach((deviceType) => {
217
- endpoint.log.debug(`- for deviceType: ${zb}${'0x' + deviceType.code.toString(16).padStart(4, '0')}${db}-${zb}${deviceType.name}${db}`);
218
- deviceType.requiredServerClusters.forEach((clusterId) => {
219
- if (!requiredServerList.includes(clusterId) && !endpoint.hasClusterServer(clusterId)) {
220
- requiredServerList.push(clusterId);
221
- endpoint.log.debug(`- cluster: ${hk}${'0x' + clusterId.toString(16).padStart(4, '0')}${db}-${hk}${getClusterNameById(clusterId)}${db}`);
222
- }
223
- });
224
- });
225
- addClusterServers(endpoint, requiredServerList);
226
- }
227
- function addOptionalClusterServers(endpoint) {
228
- const optionalServerList = [];
229
- endpoint.log.debug(`addOptionalClusterServers for ${CYAN}${endpoint.maybeId}${db}`);
230
- endpoint.deviceTypes.values().forEach((deviceType) => {
231
- endpoint.log.debug(`- for deviceType: ${zb}${'0x' + deviceType.code.toString(16).padStart(4, '0')}${db}-${zb}${deviceType.name}${db}`);
232
- deviceType.optionalServerClusters.forEach((clusterId) => {
233
- if (!optionalServerList.includes(clusterId) && !endpoint.hasClusterServer(clusterId)) {
234
- optionalServerList.push(clusterId);
235
- endpoint.log.debug(`- cluster: ${hk}${'0x' + clusterId.toString(16).padStart(4, '0')}${db}-${hk}${getClusterNameById(clusterId)}${db}`);
236
- }
237
- });
238
- });
239
- addClusterServers(endpoint, optionalServerList);
240
- }
241
- function addClusterServers(endpoint, serverList) {
242
- if (serverList.includes(PowerSource.Cluster.id))
243
- endpoint.createDefaultPowerSourceWiredClusterServer();
244
- if (serverList.includes(Identify.Cluster.id))
245
- endpoint.createDefaultIdentifyClusterServer();
246
- if (serverList.includes(Groups.Cluster.id))
247
- endpoint.createDefaultGroupsClusterServer();
248
- if (serverList.includes(OnOff.Cluster.id))
249
- endpoint.createDefaultOnOffClusterServer();
250
- if (serverList.includes(LevelControl.Cluster.id))
251
- endpoint.createDefaultLevelControlClusterServer();
252
- if (serverList.includes(ColorControl.Cluster.id))
253
- endpoint.createDefaultColorControlClusterServer();
254
- if (serverList.includes(WindowCovering.Cluster.id))
255
- endpoint.createDefaultWindowCoveringClusterServer();
256
- if (serverList.includes(Thermostat.Cluster.id))
257
- endpoint.createDefaultThermostatClusterServer();
258
- if (serverList.includes(FanControl.Cluster.id))
259
- endpoint.createDefaultFanControlClusterServer();
260
- if (serverList.includes(DoorLock.Cluster.id))
261
- endpoint.createDefaultDoorLockClusterServer();
262
- if (serverList.includes(ValveConfigurationAndControl.Cluster.id))
263
- endpoint.createDefaultValveConfigurationAndControlClusterServer();
264
- if (serverList.includes(PumpConfigurationAndControl.Cluster.id))
265
- endpoint.createDefaultPumpConfigurationAndControlClusterServer();
266
- if (serverList.includes(SmokeCoAlarm.Cluster.id))
267
- endpoint.createDefaultSmokeCOAlarmClusterServer();
268
- if (serverList.includes(Switch.Cluster.id))
269
- endpoint.createDefaultSwitchClusterServer();
270
- if (serverList.includes(BooleanState.Cluster.id))
271
- endpoint.createDefaultBooleanStateClusterServer();
272
- if (serverList.includes(BooleanStateConfiguration.Cluster.id))
273
- endpoint.createDefaultBooleanStateConfigurationClusterServer();
274
- if (serverList.includes(PowerTopology.Cluster.id))
275
- endpoint.createDefaultPowerTopologyClusterServer();
276
- if (serverList.includes(ElectricalPowerMeasurement.Cluster.id))
277
- endpoint.createDefaultElectricalPowerMeasurementClusterServer();
278
- if (serverList.includes(ElectricalEnergyMeasurement.Cluster.id))
279
- endpoint.createDefaultElectricalEnergyMeasurementClusterServer();
280
- if (serverList.includes(TemperatureMeasurement.Cluster.id))
281
- endpoint.createDefaultTemperatureMeasurementClusterServer();
282
- if (serverList.includes(RelativeHumidityMeasurement.Cluster.id))
283
- endpoint.createDefaultRelativeHumidityMeasurementClusterServer();
284
- if (serverList.includes(PressureMeasurement.Cluster.id))
285
- endpoint.createDefaultPressureMeasurementClusterServer();
286
- if (serverList.includes(FlowMeasurement.Cluster.id))
287
- endpoint.createDefaultFlowMeasurementClusterServer();
288
- if (serverList.includes(IlluminanceMeasurement.Cluster.id))
289
- endpoint.createDefaultIlluminanceMeasurementClusterServer();
290
- if (serverList.includes(OccupancySensing.Cluster.id))
291
- endpoint.createDefaultOccupancySensingClusterServer();
292
- if (serverList.includes(AirQuality.Cluster.id))
293
- endpoint.createDefaultAirQualityClusterServer();
294
- if (serverList.includes(CarbonMonoxideConcentrationMeasurement.Cluster.id))
295
- endpoint.createDefaultCarbonMonoxideConcentrationMeasurementClusterServer();
296
- if (serverList.includes(CarbonDioxideConcentrationMeasurement.Cluster.id))
297
- endpoint.createDefaultCarbonDioxideConcentrationMeasurementClusterServer();
298
- if (serverList.includes(NitrogenDioxideConcentrationMeasurement.Cluster.id))
299
- endpoint.createDefaultNitrogenDioxideConcentrationMeasurementClusterServer();
300
- if (serverList.includes(OzoneConcentrationMeasurement.Cluster.id))
301
- endpoint.createDefaultOzoneConcentrationMeasurementClusterServer();
302
- if (serverList.includes(FormaldehydeConcentrationMeasurement.Cluster.id))
303
- endpoint.createDefaultFormaldehydeConcentrationMeasurementClusterServer();
304
- if (serverList.includes(Pm1ConcentrationMeasurement.Cluster.id))
305
- endpoint.createDefaultPm1ConcentrationMeasurementClusterServer();
306
- if (serverList.includes(Pm25ConcentrationMeasurement.Cluster.id))
307
- endpoint.createDefaultPm25ConcentrationMeasurementClusterServer();
308
- if (serverList.includes(Pm10ConcentrationMeasurement.Cluster.id))
309
- endpoint.createDefaultPm10ConcentrationMeasurementClusterServer();
310
- if (serverList.includes(RadonConcentrationMeasurement.Cluster.id))
311
- endpoint.createDefaultRadonConcentrationMeasurementClusterServer();
312
- if (serverList.includes(TotalVolatileOrganicCompoundsConcentrationMeasurement.Cluster.id))
313
- endpoint.createDefaultTvocMeasurementClusterServer();
314
- }
315
- export function optionsFor(type, options) {
316
- return options;
317
- }
318
57
  export class MatterbridgeEndpoint extends Endpoint {
319
58
  static bridgeMode = '';
320
59
  static logLevel = "info";
@@ -337,7 +76,6 @@ export class MatterbridgeEndpoint extends Endpoint {
337
76
  deviceType;
338
77
  uniqueStorageKey = undefined;
339
78
  tagList = undefined;
340
- subType = '';
341
79
  deviceTypes = new Map();
342
80
  commandHandler = new NamedHandler();
343
81
  constructor(definition, options = {}, debug = false) {
@@ -412,7 +150,8 @@ export class MatterbridgeEndpoint extends Endpoint {
412
150
  if (!behavior || !this.behaviors.supported[behavior.id])
413
151
  return false;
414
152
  const options = this.behaviors.optionsFor(behavior);
415
- return lowercaseFirstLetter(attribute) in options;
153
+ const defaults = this.behaviors.defaultsFor(behavior);
154
+ return lowercaseFirstLetter(attribute) in options || lowercaseFirstLetter(attribute) in defaults;
416
155
  }
417
156
  getClusterServerOptions(cluster) {
418
157
  const behavior = getBehavior(this, cluster);
@@ -420,50 +159,14 @@ export class MatterbridgeEndpoint extends Endpoint {
420
159
  return undefined;
421
160
  return this.behaviors.optionsFor(behavior);
422
161
  }
423
- getAttribute(clusterId, attribute, log) {
424
- const clusterName = lowercaseFirstLetter(getClusterNameById(clusterId));
425
- if (this.construction.status !== Lifecycle.Status.Active) {
426
- this.log.error(`getAttribute ${hk}${clusterName}.${attribute}${er} error: Endpoint ${or}${this.maybeId}${er}:${or}${this.maybeNumber}${er} is in the ${BLUE}${this.construction.status}${er} state`);
427
- return undefined;
428
- }
429
- const state = this.state;
430
- if (!(clusterName in state)) {
431
- this.log.error(`getAttribute error: Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} not found on endpoint ${or}${this.id}${er}:${or}${this.number}${er}`);
432
- return undefined;
433
- }
434
- attribute = lowercaseFirstLetter(attribute);
435
- if (!(attribute in state[clusterName])) {
436
- this.log.error(`getAttribute error: Attribute ${hk}${attribute}${er} not found on Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} on endpoint ${or}${this.id}${er}:${or}${this.number}${er}`);
437
- return undefined;
438
- }
439
- const value = state[clusterName][attribute];
440
- log?.info(`${db}Get endpoint ${or}${this.id}${db}:${or}${this.number}${db} attribute ${hk}${capitalizeFirstLetter(clusterName)}${db}.${hk}${attribute}${db} value ${YELLOW}${value !== null && typeof value === 'object' ? debugStringify(value) : value}${db}`);
441
- return value;
162
+ getAttribute(cluster, attribute, log) {
163
+ return getAttribute(this, cluster, attribute, log);
442
164
  }
443
165
  async setAttribute(clusterId, attribute, value, log) {
444
- const clusterName = lowercaseFirstLetter(getClusterNameById(clusterId));
445
- if (this.construction.status !== Lifecycle.Status.Active) {
446
- this.log.error(`setAttribute ${hk}${clusterName}.${attribute}${er} error: Endpoint ${or}${this.maybeId}${er}:${or}${this.maybeNumber}${er} is in the ${BLUE}${this.construction.status}${er} state`);
447
- return false;
448
- }
449
- const state = this.state;
450
- if (!(clusterName in state)) {
451
- this.log.error(`setAttribute ${hk}${attribute}${er} error: Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} not found on endpoint ${or}${this.id}${er}:${or}${this.number}${er}`);
452
- return false;
453
- }
454
- attribute = lowercaseFirstLetter(attribute);
455
- if (!(attribute in state[clusterName])) {
456
- this.log.error(`setAttribute error: Attribute ${hk}${attribute}${er} not found on Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} on endpoint ${or}${this.id}${er}:${or}${this.number}${er}`);
457
- return false;
458
- }
459
- let oldValue = state[clusterName][attribute];
460
- if (typeof oldValue === 'object')
461
- oldValue = deepCopy(oldValue);
462
- await this.setStateOf(this.behaviors.supported[clusterName], { [attribute]: value });
463
- log?.info(`${db}Set endpoint ${or}${this.id}${db}:${or}${this.number}${db} attribute ${hk}${capitalizeFirstLetter(clusterName)}${db}.${hk}${attribute}${db} ` +
464
- `from ${YELLOW}${oldValue !== null && typeof oldValue === 'object' ? debugStringify(oldValue) : oldValue}${db} ` +
465
- `to ${YELLOW}${value !== null && typeof value === 'object' ? debugStringify(value) : value}${db}`);
466
- return true;
166
+ return await setAttribute(this, clusterId, attribute, value, log);
167
+ }
168
+ async updateAttribute(cluster, attribute, value, log) {
169
+ return await updateAttribute(this, cluster, attribute, value, log);
467
170
  }
468
171
  async subscribeAttribute(clusterId, attribute, listener, log) {
469
172
  const clusterName = lowercaseFirstLetter(getClusterNameById(clusterId));
@@ -484,52 +187,30 @@ export class MatterbridgeEndpoint extends Endpoint {
484
187
  log?.info(`${db}Subscribed endpoint ${or}${this.id}${db}:${or}${this.number}${db} attribute ${hk}${capitalizeFirstLetter(clusterName)}${db}.${hk}${attribute}${db}`);
485
188
  return true;
486
189
  }
487
- async triggerEvent(clusterId, event, payload, log, endpoint) {
488
- if (!endpoint)
489
- endpoint = this;
190
+ async triggerEvent(clusterId, event, payload, log) {
490
191
  const clusterName = lowercaseFirstLetter(getClusterNameById(clusterId));
491
- if (endpoint.construction.status !== Lifecycle.Status.Active) {
492
- this.log.error(`triggerEvent ${hk}${clusterName}.${event}${er} error: Endpoint ${or}${endpoint.maybeId}${er}:${or}${endpoint.maybeNumber}${er} is in the ${BLUE}${endpoint.construction.status}${er} state`);
192
+ if (this.construction.status !== Lifecycle.Status.Active) {
193
+ this.log.error(`triggerEvent ${hk}${clusterName}.${event}${er} error: Endpoint ${or}${this.maybeId}${er}:${or}${this.maybeNumber}${er} is in the ${BLUE}${this.construction.status}${er} state`);
493
194
  return false;
494
195
  }
495
- const events = endpoint.events;
196
+ const events = this.events;
496
197
  if (!(clusterName in events) || !(event in events[clusterName])) {
497
- this.log.error(`triggerEvent ${hk}${event}${er} error: Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} not found on endpoint ${or}${endpoint.id}${er}:${or}${endpoint.number}${er}`);
198
+ this.log.error(`triggerEvent ${hk}${event}${er} error: Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} not found on endpoint ${or}${this.id}${er}:${or}${this.number}${er}`);
498
199
  return false;
499
200
  }
500
- await endpoint.act((agent) => agent[clusterName].events[event].emit(payload, agent.context));
501
- log?.info(`${db}Trigger event ${hk}${capitalizeFirstLetter(clusterName)}${db}.${hk}${event}${db} with ${debugStringify(payload)}${db} on endpoint ${or}${endpoint.id}${db}:${or}${endpoint.number}${db} `);
201
+ await this.act((agent) => agent[clusterName].events[event].emit(payload, agent.context));
202
+ log?.info(`${db}Trigger event ${hk}${capitalizeFirstLetter(clusterName)}${db}.${hk}${event}${db} with ${debugStringify(payload)}${db} on endpoint ${or}${this.id}${db}:${or}${this.number}${db} `);
502
203
  return true;
503
204
  }
504
205
  addClusterServers(serverList) {
505
206
  addClusterServers(this, serverList);
506
207
  }
507
208
  async addFixedLabel(label, value) {
508
- if (!this.hasClusterServer(FixedLabel.Cluster.id)) {
509
- this.log.debug(`addFixedLabel: add cluster ${hk}FixedLabel${db}:${hk}fixedLabel${db} with label ${CYAN}${label}${db} value ${CYAN}${value}${db}`);
510
- this.behaviors.require(FixedLabelServer, {
511
- labelList: [{ label, value }],
512
- });
513
- return this;
514
- }
515
- this.log.debug(`addFixedLabel: add label ${CYAN}${label}${db} value ${CYAN}${value}${db}`);
516
- const labelList = (this.getAttribute(FixedLabel.Cluster.id, 'labelList', this.log) ?? []).filter((entryLabel) => entryLabel.label !== label);
517
- labelList.push({ label, value });
518
- await this.setAttribute(FixedLabel.Cluster.id, 'labelList', labelList, this.log);
209
+ await addFixedLabel(this, label, value);
519
210
  return this;
520
211
  }
521
212
  async addUserLabel(label, value) {
522
- if (!this.hasClusterServer(UserLabel.Cluster.id)) {
523
- this.log.debug(`addUserLabel: add cluster ${hk}UserLabel${db}:${hk}userLabel${db} with label ${CYAN}${label}${db} value ${CYAN}${value}${db}`);
524
- this.behaviors.require(UserLabelServer, {
525
- labelList: [{ label, value }],
526
- });
527
- return this;
528
- }
529
- this.log.debug(`addUserLabel: add label ${CYAN}${label}${db} value ${CYAN}${value}${db}`);
530
- const labelList = (this.getAttribute(UserLabel.Cluster.id, 'labelList', this.log) ?? []).filter((entryLabel) => entryLabel.label !== label);
531
- labelList.push({ label, value });
532
- await this.setAttribute(UserLabel.Cluster.id, 'labelList', labelList, this.log);
213
+ await addUserLabel(this, label, value);
533
214
  return this;
534
215
  }
535
216
  addCommandHandler(command, handler) {
@@ -547,6 +228,27 @@ export class MatterbridgeEndpoint extends Endpoint {
547
228
  addOptionalClusterServers(this);
548
229
  return this;
549
230
  }
231
+ getAllClusterServers() {
232
+ return Object.values(this.behaviors.supported);
233
+ }
234
+ getAllClusterServerNames() {
235
+ return Object.keys(this.behaviors.supported);
236
+ }
237
+ forEachAttribute(callback) {
238
+ for (const [clusterName, clusterAttributes] of Object.entries(this.state)) {
239
+ for (const [attributeName, attributeValue] of Object.entries(clusterAttributes)) {
240
+ const clusterId = getClusterId(this, clusterName);
241
+ if (clusterId === undefined) {
242
+ continue;
243
+ }
244
+ const attributeId = getAttributeId(this, clusterName, attributeName);
245
+ if (attributeId === undefined) {
246
+ continue;
247
+ }
248
+ callback(clusterName, clusterId, attributeName, attributeId, attributeValue);
249
+ }
250
+ }
251
+ }
550
252
  addChildDeviceType(endpointName, definition, options = {}, debug = false) {
551
253
  this.log.debug(`addChildDeviceType: ${CYAN}${endpointName}${db}`);
552
254
  let alreadyAdded = false;
@@ -937,7 +639,6 @@ export class MatterbridgeEndpoint extends Endpoint {
937
639
  await this.setAttribute(ColorControl.Cluster.id, 'colorMode', colorMode, this.log);
938
640
  await this.setAttribute(ColorControl.Cluster.id, 'enhancedColorMode', colorMode, this.log);
939
641
  }
940
- return this;
941
642
  }
942
643
  createDefaultWindowCoveringClusterServer(positionPercent100ths) {
943
644
  this.behaviors.require(MatterbridgeWindowCoveringServer.with(WindowCovering.Feature.Lift, WindowCovering.Feature.PositionAwareLift), {
@@ -961,7 +662,7 @@ export class MatterbridgeEndpoint extends Endpoint {
961
662
  }
962
663
  async setWindowCoveringTargetAsCurrentAndStopped() {
963
664
  const position = this.getAttribute(WindowCovering.Cluster.id, 'currentPositionLiftPercent100ths', this.log);
964
- if (position !== null) {
665
+ if (isValidNumber(position, 0, 10000)) {
965
666
  await this.setAttribute(WindowCovering.Cluster.id, 'targetPositionLiftPercent100ths', position, this.log);
966
667
  await this.setAttribute(WindowCovering.Cluster.id, 'operationalStatus', {
967
668
  global: WindowCovering.MovementStatus.Stopped,
@@ -991,8 +692,10 @@ export class MatterbridgeEndpoint extends Endpoint {
991
692
  }
992
693
  getWindowCoveringStatus() {
993
694
  const status = this.getAttribute(WindowCovering.Cluster.id, 'operationalStatus', this.log);
994
- this.log.debug(`Get WindowCovering operationalStatus: ${status.global}`);
995
- return status.global;
695
+ if (isValidObject(status, 3) && 'global' in status && typeof status.global === 'number') {
696
+ this.log.debug(`Get WindowCovering operationalStatus: ${status.global}`);
697
+ return status.global;
698
+ }
996
699
  }
997
700
  async setWindowCoveringTargetAndCurrentPosition(position) {
998
701
  await this.setAttribute(WindowCovering.Cluster.id, 'currentPositionLiftPercent100ths', position, this.log);
@@ -1121,7 +824,7 @@ export class MatterbridgeEndpoint extends Endpoint {
1121
824
  return this;
1122
825
  }
1123
826
  createDefaultSwitchClusterServer() {
1124
- this.behaviors.require(SwitchServer.with(Switch.Feature.MomentarySwitch, Switch.Feature.MomentarySwitchRelease, Switch.Feature.MomentarySwitchLongPress, Switch.Feature.MomentarySwitchMultiPress).enable({
827
+ this.behaviors.require(MatterbridgeSwitchServer.with(Switch.Feature.MomentarySwitch, Switch.Feature.MomentarySwitchRelease, Switch.Feature.MomentarySwitchLongPress, Switch.Feature.MomentarySwitchMultiPress).enable({
1125
828
  events: { initialPress: true, longPress: true, shortRelease: true, longRelease: true, multiPressOngoing: true, multiPressComplete: true },
1126
829
  }), {
1127
830
  numberOfPositions: 2,
@@ -1280,76 +983,28 @@ export class MatterbridgeEndpoint extends Endpoint {
1280
983
  });
1281
984
  return this;
1282
985
  }
1283
- getDefaultTemperatureMeasurementClusterServer(measuredValue = 0) {
1284
- return optionsFor(TemperatureMeasurementServer, {
1285
- measuredValue,
1286
- minMeasuredValue: null,
1287
- maxMeasuredValue: null,
1288
- tolerance: 0,
1289
- });
1290
- }
1291
986
  createDefaultTemperatureMeasurementClusterServer(measuredValue = 0) {
1292
- this.behaviors.require(TemperatureMeasurementServer, this.getDefaultTemperatureMeasurementClusterServer(measuredValue));
987
+ this.behaviors.require(TemperatureMeasurementServer, getDefaultTemperatureMeasurementClusterServer(measuredValue));
1293
988
  return this;
1294
989
  }
1295
- getDefaultRelativeHumidityMeasurementClusterServer(measuredValue = 0) {
1296
- return optionsFor(RelativeHumidityMeasurementServer, {
1297
- measuredValue,
1298
- minMeasuredValue: null,
1299
- maxMeasuredValue: null,
1300
- tolerance: 0,
1301
- });
1302
- }
1303
990
  createDefaultRelativeHumidityMeasurementClusterServer(measuredValue = 0) {
1304
- this.behaviors.require(RelativeHumidityMeasurementServer, this.getDefaultRelativeHumidityMeasurementClusterServer(measuredValue));
991
+ this.behaviors.require(RelativeHumidityMeasurementServer, getDefaultRelativeHumidityMeasurementClusterServer(measuredValue));
1305
992
  return this;
1306
993
  }
1307
- getDefaultPressureMeasurementClusterServer(measuredValue = 1000) {
1308
- return optionsFor(PressureMeasurementServer, {
1309
- measuredValue,
1310
- minMeasuredValue: null,
1311
- maxMeasuredValue: null,
1312
- tolerance: 0,
1313
- });
1314
- }
1315
994
  createDefaultPressureMeasurementClusterServer(measuredValue = 1000) {
1316
- this.behaviors.require(PressureMeasurementServer, this.getDefaultPressureMeasurementClusterServer(measuredValue));
995
+ this.behaviors.require(PressureMeasurementServer, getDefaultPressureMeasurementClusterServer(measuredValue));
1317
996
  return this;
1318
997
  }
1319
- getDefaultIlluminanceMeasurementClusterServer(measuredValue = 0) {
1320
- return optionsFor(IlluminanceMeasurementServer, {
1321
- measuredValue,
1322
- minMeasuredValue: null,
1323
- maxMeasuredValue: null,
1324
- tolerance: 0,
1325
- });
1326
- }
1327
998
  createDefaultIlluminanceMeasurementClusterServer(measuredValue = 0) {
1328
- this.behaviors.require(IlluminanceMeasurementServer, this.getDefaultIlluminanceMeasurementClusterServer(measuredValue));
999
+ this.behaviors.require(IlluminanceMeasurementServer, getDefaultIlluminanceMeasurementClusterServer(measuredValue));
1329
1000
  return this;
1330
1001
  }
1331
- getDefaultFlowMeasurementClusterServer(measuredValue = 0) {
1332
- return optionsFor(FlowMeasurementServer, {
1333
- measuredValue,
1334
- minMeasuredValue: null,
1335
- maxMeasuredValue: null,
1336
- tolerance: 0,
1337
- });
1338
- }
1339
1002
  createDefaultFlowMeasurementClusterServer(measuredValue = 0) {
1340
- this.behaviors.require(FlowMeasurementServer, this.getDefaultFlowMeasurementClusterServer(measuredValue));
1003
+ this.behaviors.require(FlowMeasurementServer, getDefaultFlowMeasurementClusterServer(measuredValue));
1341
1004
  return this;
1342
1005
  }
1343
- getDefaultOccupancySensingClusterServer(occupied = false) {
1344
- return optionsFor(OccupancySensingServer, {
1345
- occupancy: { occupied },
1346
- occupancySensorType: OccupancySensing.OccupancySensorType.Pir,
1347
- occupancySensorTypeBitmap: { pir: true, ultrasonic: false, physicalContact: false },
1348
- pirOccupiedToUnoccupiedDelay: 30,
1349
- });
1350
- }
1351
1006
  createDefaultOccupancySensingClusterServer(occupied = false) {
1352
- this.behaviors.require(OccupancySensingServer, this.getDefaultOccupancySensingClusterServer(occupied));
1007
+ this.behaviors.require(OccupancySensingServer, getDefaultOccupancySensingClusterServer(occupied));
1353
1008
  return this;
1354
1009
  }
1355
1010
  createDefaultAirQualityClusterServer(airQuality = AirQuality.AirQualityEnum.Unknown) {