matterbridge-example-dynamic-platform 1.0.17 → 1.0.19

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/platform.js CHANGED
@@ -1,18 +1,37 @@
1
- import { ColorControl, ColorControlCluster, DeviceTypes, DoorLock, DoorLockCluster, FlowMeasurement, LevelControlCluster, OnOffCluster, RelativeHumidityMeasurement, TemperatureMeasurement, Thermostat, ThermostatCluster, WindowCovering, WindowCoveringCluster, onOffSwitch, powerSource, } from 'matterbridge';
1
+ import { AirQuality, AirQualityCluster, BooleanStateCluster, BooleanStateConfiguration, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, ColorControl, ColorControlCluster, DeviceTypes, DoorLock, DoorLockCluster, FanControl, FanControlCluster, FlowMeasurement, FormaldehydeConcentrationMeasurement, LevelControlCluster, NitrogenDioxideConcentrationMeasurement, OnOffCluster, OzoneConcentrationMeasurement, Pm10ConcentrationMeasurement, Pm1ConcentrationMeasurement, Pm25ConcentrationMeasurement, RadonConcentrationMeasurement, RelativeHumidityMeasurement, SmokeCoAlarm, SmokeCoAlarmCluster, TemperatureMeasurement, Thermostat, ThermostatCluster, TotalVolatileOrganicCompoundsConcentrationMeasurement, WindowCovering, WindowCoveringCluster, airQualitySensor, bridgedNode, onOffSwitch, powerSource, rainSensor, smokeCoAlarm, waterFreezeDetector, waterLeakDetector, } from 'matterbridge';
2
2
  import { MatterbridgeDevice, MatterbridgeDynamicPlatform } from 'matterbridge';
3
+ import { isValidBoolean, isValidNumber } from 'matterbridge/utils';
4
+ import { db, er, hk, or } from 'matterbridge/logger';
3
5
  export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatform {
4
6
  switch;
7
+ lightOnOff;
8
+ dimmer;
5
9
  light;
10
+ lightXY;
11
+ lightHS;
12
+ lightCT;
6
13
  outlet;
7
14
  cover;
8
15
  lock;
9
16
  thermo;
17
+ fan;
18
+ waterLeak;
19
+ waterFreeze;
20
+ rain;
21
+ smoke;
22
+ airQuality;
10
23
  switchInterval;
11
24
  lightInterval;
12
25
  outletInterval;
13
26
  coverInterval;
14
27
  lockInterval;
15
28
  thermoInterval;
29
+ fanInterval;
30
+ waterLeakInterval;
31
+ waterFreezeInterval;
32
+ rainInterval;
33
+ smokeInterval;
34
+ airQualityInterval;
16
35
  constructor(matterbridge, log, config) {
17
36
  super(matterbridge, log, config);
18
37
  this.log.info('Initializing platform:', this.config.name);
@@ -20,11 +39,12 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
20
39
  async onStart(reason) {
21
40
  this.log.info('onStart called with reason:', reason ?? 'none');
22
41
  // Create a switch device
23
- this.switch = new MatterbridgeDevice(onOffSwitch);
42
+ this.switch = new MatterbridgeDevice(onOffSwitch, undefined, this.config.debug);
43
+ this.switch.log.logName = 'Switch';
24
44
  this.switch.createDefaultIdentifyClusterServer();
25
45
  this.switch.createDefaultGroupsClusterServer();
26
46
  this.switch.createDefaultScenesClusterServer();
27
- this.switch.createDefaultBridgedDeviceBasicInformationClusterServer('Bridged device 3', '0x23452164', 0xfff1, 'Luligu', 'Dynamic device 3');
47
+ this.switch.createDefaultBridgedDeviceBasicInformationClusterServer('Switch', '0x23452164', 0xfff1, 'Luligu', 'Matterbridge Switch');
28
48
  this.switch.createDefaultOnOffClusterServer();
29
49
  this.switch.addDeviceType(powerSource);
30
50
  this.switch.createDefaultPowerSourceRechargeableBatteryClusterServer(70);
@@ -33,128 +53,327 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
33
53
  this.log.info(`Command identify called identifyTime:${identifyTime}`);
34
54
  });
35
55
  this.switch.addCommandHandler('on', async () => {
36
- this.switch?.getClusterServer(OnOffCluster)?.setOnOffAttribute(true);
37
- this.log.info('Command on called');
56
+ this.switch?.setAttribute(OnOffCluster.id, 'onOff', true, this.switch.log, this.switch);
57
+ this.switch?.log.info('Command on called');
38
58
  });
39
59
  this.switch.addCommandHandler('off', async () => {
40
- this.switch?.getClusterServer(OnOffCluster)?.setOnOffAttribute(false);
41
- this.log.info('Command off called');
60
+ this.switch?.setAttribute(OnOffCluster.id, 'onOff', false, this.switch.log, this.switch);
61
+ this.switch?.log.info('Command off called');
62
+ });
63
+ addCommandHandler(OnOffCluster.id, 'on', async (data) => {
64
+ this.switch?.log.info(`Command on called with: ${data}`);
65
+ }, this.switch.log, this.switch);
66
+ addCommandHandler(OnOffCluster.id, 'off', async (data) => {
67
+ this.switch?.log.info(`Command off called with: ${data}`);
68
+ }, this.switch.log, this.switch);
69
+ // Create a on off light device
70
+ this.lightOnOff = new MatterbridgeDevice(DeviceTypes.ON_OFF_LIGHT, undefined, this.config.debug);
71
+ this.lightOnOff.log.logName = 'Light (on/off)';
72
+ this.lightOnOff.createDefaultIdentifyClusterServer();
73
+ this.lightOnOff.createDefaultGroupsClusterServer();
74
+ this.lightOnOff.createDefaultScenesClusterServer();
75
+ this.lightOnOff.createDefaultBridgedDeviceBasicInformationClusterServer('Light (on/off)', '0x2342375564', 0xfff1, 'Luligu', 'Matterbridge Light on/off');
76
+ this.lightOnOff.createDefaultOnOffClusterServer();
77
+ this.lightOnOff.addDeviceType(powerSource);
78
+ this.lightOnOff.createDefaultPowerSourceWiredClusterServer();
79
+ await this.registerDevice(this.lightOnOff);
80
+ this.lightOnOff.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
81
+ this.lightOnOff?.log.info(`Command identify called identifyTime:${identifyTime}`);
82
+ });
83
+ this.lightOnOff.addCommandHandler('on', async () => {
84
+ this.light?.setAttribute(OnOffCluster.id, 'onOff', true, this.lightOnOff?.log, this.lightOnOff);
85
+ this.lightOnOff?.log.info('Command on called');
86
+ });
87
+ this.lightOnOff.addCommandHandler('off', async () => {
88
+ this.light?.setAttribute(OnOffCluster.id, 'onOff', false, this.lightOnOff?.log, this.lightOnOff);
89
+ this.lightOnOff?.log.info('Command off called');
90
+ });
91
+ // Create a dimmer device
92
+ this.dimmer = new MatterbridgeDevice(DeviceTypes.DIMMABLE_LIGHT, undefined, this.config.debug);
93
+ this.dimmer.log.logName = 'Dimmer';
94
+ this.dimmer.createDefaultIdentifyClusterServer();
95
+ this.dimmer.createDefaultGroupsClusterServer();
96
+ this.dimmer.createDefaultScenesClusterServer();
97
+ this.dimmer.createDefaultBridgedDeviceBasicInformationClusterServer('Dimmer', '0x234554564', 0xfff1, 'Luligu', 'Matterbridge Dimmer');
98
+ this.dimmer.createDefaultOnOffClusterServer();
99
+ this.dimmer.createDefaultLevelControlClusterServer();
100
+ this.dimmer.addDeviceType(powerSource);
101
+ this.dimmer.createDefaultPowerSourceReplaceableBatteryClusterServer(70);
102
+ await this.registerDevice(this.dimmer);
103
+ this.dimmer.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
104
+ this.dimmer?.log.info(`Command identify called identifyTime:${identifyTime}`);
105
+ });
106
+ this.dimmer.addCommandHandler('on', async () => {
107
+ this.dimmer?.setAttribute(OnOffCluster.id, 'onOff', true, this.dimmer.log, this.dimmer);
108
+ this.dimmer?.log.info('Command on called');
109
+ });
110
+ this.dimmer.addCommandHandler('off', async () => {
111
+ this.dimmer?.setAttribute(OnOffCluster.id, 'onOff', true, this.dimmer.log, this.dimmer);
112
+ this.dimmer?.log.info('Command off called');
113
+ });
114
+ this.dimmer.addCommandHandler('moveToLevel', async ({ request: { level }, attributes: { currentLevel } }) => {
115
+ this.dimmer?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.dimmer.log, this.dimmer);
116
+ this.dimmer?.log.debug(`Command moveToLevel called request: ${level} attributes: ${currentLevel?.getLocal()}`);
117
+ });
118
+ this.dimmer.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level }, attributes: { currentLevel } }) => {
119
+ this.dimmer?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.dimmer.log, this.dimmer);
120
+ this.dimmer?.log.debug(`Command moveToLevelWithOnOff called request: ${level} attributes: ${currentLevel?.getLocal()}`);
42
121
  });
43
122
  // Create a light device
44
- this.light = new MatterbridgeDevice(DeviceTypes.ON_OFF_LIGHT);
123
+ this.light = new MatterbridgeDevice(DeviceTypes.COLOR_TEMPERATURE_LIGHT, undefined, this.config.debug);
124
+ this.light.log.logName = 'Light (XY, HS and CT)';
45
125
  this.light.createDefaultIdentifyClusterServer();
46
126
  this.light.createDefaultGroupsClusterServer();
47
127
  this.light.createDefaultScenesClusterServer();
48
- this.light.createDefaultBridgedDeviceBasicInformationClusterServer('Bridged device 2', '0x23480564', 0xfff1, 'Luligu', 'Dynamic device 2');
128
+ this.light.createDefaultBridgedDeviceBasicInformationClusterServer('Light (XY, HS and CT)', '0x23480564', 0xfff1, 'Luligu', 'Matterbridge Light');
49
129
  this.light.createDefaultOnOffClusterServer();
50
130
  this.light.createDefaultLevelControlClusterServer();
51
- this.light.createDefaultColorControlClusterServer();
131
+ this.light.createDefaultCompleteColorControlClusterServer();
52
132
  this.light.addDeviceType(powerSource);
53
133
  this.light.createDefaultPowerSourceReplaceableBatteryClusterServer(70);
54
134
  await this.registerDevice(this.light);
55
135
  this.light.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
56
- this.log.info(`Command identify called identifyTime:${identifyTime}`);
136
+ this.light?.log.info(`Command identify called identifyTime:${identifyTime}`);
57
137
  });
58
138
  this.light.addCommandHandler('on', async () => {
59
- this.light?.getClusterServer(OnOffCluster)?.setOnOffAttribute(true);
60
- this.log.info('Command on called');
139
+ this.light?.setAttribute(OnOffCluster.id, 'onOff', true, this.light?.log, this.light);
140
+ this.light?.log.info('Command on called');
61
141
  });
62
142
  this.light.addCommandHandler('off', async () => {
63
- this.light?.getClusterServer(OnOffCluster)?.setOnOffAttribute(false);
64
- this.log.info('Command off called');
143
+ this.light?.setAttribute(OnOffCluster.id, 'onOff', false, this.light?.log, this.light);
144
+ this.light?.log.info('Command off called');
65
145
  });
66
146
  this.light.addCommandHandler('moveToLevel', async ({ request: { level }, attributes: { currentLevel } }) => {
67
- this.light?.getClusterServer(LevelControlCluster)?.setCurrentLevelAttribute(level);
68
- this.log.debug(`Command moveToLevel called request: ${level} attributes: ${currentLevel?.getLocal()}`);
147
+ this.light?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.light?.log, this.light);
148
+ this.light?.log.debug(`Command moveToLevel called request: ${level} attributes: ${currentLevel?.getLocal()}`);
69
149
  });
70
150
  this.light.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level }, attributes: { currentLevel } }) => {
71
- this.light?.getClusterServer(LevelControlCluster)?.setCurrentLevelAttribute(level);
72
- this.log.debug(`Command moveToLevelWithOnOff called request: ${level} attributes: ${currentLevel?.getLocal()}`);
151
+ this.light?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.light?.log, this.light);
152
+ this.light?.log.debug(`Command moveToLevelWithOnOff called request: ${level} attributes: ${currentLevel?.getLocal()}`);
153
+ });
154
+ this.light.addCommandHandler('moveToColor', async ({ request: { colorX, colorY }, attributes: { currentX, currentY } }) => {
155
+ this.light?.setAttribute(ColorControlCluster.id, 'currentX', colorX, this.light?.log, this.light);
156
+ this.light?.setAttribute(ColorControlCluster.id, 'currentY', colorY, this.light?.log, this.light);
157
+ this.light?.log.debug(`Command moveToColor called request: X ${colorX / 65536} Y ${colorY / 65536} attributes: X ${(currentX?.getLocal() ?? 0) / 65536} Y ${(currentY?.getLocal() ?? 0) / 65536}`);
73
158
  });
74
159
  this.light.addCommandHandler('moveToHueAndSaturation', async ({ request: { hue, saturation }, attributes: { currentHue, currentSaturation } }) => {
75
- this.light?.getClusterServer(ColorControlCluster.with(ColorControl.Feature.HueSaturation))?.setCurrentHueAttribute(hue);
76
- this.light?.getClusterServer(ColorControlCluster.with(ColorControl.Feature.HueSaturation))?.setCurrentSaturationAttribute(saturation);
77
- this.log.debug(`Command moveToHueAndSaturation called request: hue ${hue} saturation ${saturation} attributes: hue ${currentHue?.getLocal()} saturation ${currentSaturation?.getLocal()}`);
160
+ this.light?.setAttribute(ColorControlCluster.id, 'currentHue', hue, this.light?.log, this.light);
161
+ this.light?.setAttribute(ColorControlCluster.id, 'currentSaturation', saturation, this.light?.log, this.light);
162
+ this.light?.log.debug(`Command moveToHueAndSaturation called request: hue ${hue} saturation ${saturation} attributes: hue ${currentHue?.getLocal()} saturation ${currentSaturation?.getLocal()}`);
163
+ });
164
+ this.light.addCommandHandler('moveToHue', async ({ request: { hue }, attributes: { currentHue, currentSaturation } }) => {
165
+ this.light?.setAttribute(ColorControlCluster.id, 'currentHue', hue, this.light?.log, this.light);
166
+ this.light?.log.debug(`Command moveToHue called request: hue ${hue} attributes: hue ${currentHue?.getLocal()} saturation ${currentSaturation?.getLocal()}`);
167
+ });
168
+ this.light.addCommandHandler('moveToSaturation', async ({ request: { saturation }, attributes: { currentHue, currentSaturation } }) => {
169
+ this.light?.setAttribute(ColorControlCluster.id, 'currentSaturation', saturation, this.light?.log, this.light);
170
+ this.light?.log.debug(`Command moveToSaturation called request: saturation ${saturation} attributes: hue ${currentHue?.getLocal()} saturation ${currentSaturation?.getLocal()}`);
78
171
  });
79
172
  this.light.addCommandHandler('moveToColorTemperature', async ({ request, attributes }) => {
80
- this.light?.getClusterServer(ColorControl.Complete)?.setColorTemperatureMiredsAttribute(request.colorTemperatureMireds);
81
- this.log.debug(`Command moveToColorTemperature called request: ${request.colorTemperatureMireds} attributes: ${attributes.colorTemperatureMireds?.getLocal()}`);
173
+ this.light?.setAttribute(ColorControlCluster.id, 'colorTemperatureMireds', request.colorTemperatureMireds, this.light?.log, this.light);
174
+ this.light?.log.debug(`Command moveToColorTemperature called request: ${request.colorTemperatureMireds} attributes: ${attributes.colorTemperatureMireds?.getLocal()}`);
175
+ });
176
+ // Create a light device with HS color control
177
+ this.lightHS = new MatterbridgeDevice(DeviceTypes.COLOR_TEMPERATURE_LIGHT, undefined, this.config.debug);
178
+ this.lightHS.log.logName = 'Light (HS)';
179
+ this.lightHS.createDefaultIdentifyClusterServer();
180
+ this.lightHS.createDefaultGroupsClusterServer();
181
+ this.lightHS.createDefaultScenesClusterServer();
182
+ this.lightHS.createDefaultBridgedDeviceBasicInformationClusterServer('Light (HS)', '0x25097564', 0xfff1, 'Luligu', 'Matterbridge Light');
183
+ this.lightHS.createDefaultOnOffClusterServer();
184
+ this.lightHS.createDefaultLevelControlClusterServer();
185
+ this.lightHS.createDefaultCompleteColorControlClusterServer();
186
+ this.lightHS.configureColorControlCluster(true, false, false, ColorControl.ColorMode.CurrentHueAndCurrentSaturation);
187
+ this.lightHS.addDeviceType(powerSource);
188
+ this.lightHS.createDefaultPowerSourceWiredClusterServer();
189
+ await this.registerDevice(this.lightHS);
190
+ this.lightHS.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
191
+ this.lightHS?.log.info(`Command identify called identifyTime:${identifyTime}`);
192
+ });
193
+ this.lightHS.addCommandHandler('on', async () => {
194
+ this.lightHS?.setAttribute(OnOffCluster.id, 'onOff', true, this.lightHS?.log, this.lightHS);
195
+ this.lightHS?.log.info('Command on called');
196
+ });
197
+ this.lightHS.addCommandHandler('off', async () => {
198
+ this.lightHS?.setAttribute(OnOffCluster.id, 'onOff', false, this.lightHS?.log, this.lightHS);
199
+ this.lightHS?.log.info('Command off called');
200
+ });
201
+ this.lightHS.addCommandHandler('moveToLevel', async ({ request: { level }, attributes: { currentLevel } }) => {
202
+ this.lightHS?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.lightHS?.log, this.lightHS);
203
+ this.lightHS?.log.debug(`Command moveToLevel called request: ${level} attributes: ${currentLevel?.getLocal()}`);
204
+ });
205
+ this.lightHS.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level }, attributes: { currentLevel } }) => {
206
+ this.lightHS?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.lightHS?.log, this.lightHS);
207
+ this.lightHS?.log.debug(`Command moveToLevelWithOnOff called request: ${level} attributes: ${currentLevel?.getLocal()}`);
208
+ });
209
+ this.lightHS.addCommandHandler('moveToHueAndSaturation', async ({ request: { hue, saturation }, attributes: { currentHue, currentSaturation } }) => {
210
+ this.lightHS?.setAttribute(ColorControlCluster.id, 'currentHue', hue, this.lightHS?.log, this.lightHS);
211
+ this.lightHS?.setAttribute(ColorControlCluster.id, 'currentSaturation', saturation, this.lightHS?.log, this.lightHS);
212
+ this.lightHS?.log.debug(`Command moveToHueAndSaturation called request: hue ${hue} saturation ${saturation} attributes: hue ${currentHue?.getLocal()} saturation ${currentSaturation?.getLocal()}`);
213
+ });
214
+ this.lightHS.addCommandHandler('moveToHue', async ({ request: { hue }, attributes: { currentHue, currentSaturation } }) => {
215
+ this.lightHS?.setAttribute(ColorControlCluster.id, 'currentHue', hue, this.lightHS?.log, this.lightHS);
216
+ this.lightHS?.log.debug(`Command moveToHue called request: hue ${hue} attributes: hue ${currentHue?.getLocal()} saturation ${currentSaturation?.getLocal()}`);
217
+ });
218
+ this.lightHS.addCommandHandler('moveToSaturation', async ({ request: { saturation }, attributes: { currentHue, currentSaturation } }) => {
219
+ this.lightHS?.setAttribute(ColorControlCluster.id, 'currentSaturation', saturation, this.lightHS?.log, this.lightHS);
220
+ this.lightHS?.log.debug(`Command moveToSaturation called request: saturation ${saturation} attributes: hue ${currentHue?.getLocal()} saturation ${currentSaturation?.getLocal()}`);
221
+ });
222
+ // Create a light device with XY color control
223
+ this.lightXY = new MatterbridgeDevice(DeviceTypes.COLOR_TEMPERATURE_LIGHT, undefined, this.config.debug);
224
+ this.lightXY.log.logName = 'Light (XY)';
225
+ this.lightXY.createDefaultIdentifyClusterServer();
226
+ this.lightXY.createDefaultGroupsClusterServer();
227
+ this.lightXY.createDefaultScenesClusterServer();
228
+ this.lightXY.createDefaultBridgedDeviceBasicInformationClusterServer('Light (XY)', '0x23497564', 0xfff1, 'Luligu', 'Matterbridge Light');
229
+ this.lightXY.createDefaultOnOffClusterServer();
230
+ this.lightXY.createDefaultLevelControlClusterServer();
231
+ this.lightXY.createDefaultCompleteColorControlClusterServer();
232
+ this.lightXY.configureColorControlCluster(false, true, false, ColorControl.ColorMode.CurrentXAndCurrentY);
233
+ this.lightXY.addDeviceType(powerSource);
234
+ this.lightXY.createDefaultPowerSourceWiredClusterServer();
235
+ await this.registerDevice(this.lightXY);
236
+ this.lightXY.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
237
+ this.lightXY?.log.info(`Command identify called identifyTime:${identifyTime}`);
238
+ });
239
+ this.lightXY.addCommandHandler('on', async () => {
240
+ this.lightXY?.setAttribute(OnOffCluster.id, 'onOff', true, this.lightXY?.log, this.lightXY);
241
+ this.lightXY?.log.info('Command on called');
242
+ });
243
+ this.lightXY.addCommandHandler('off', async () => {
244
+ this.lightXY?.setAttribute(OnOffCluster.id, 'onOff', false, this.lightXY?.log, this.lightXY);
245
+ this.lightXY?.log.info('Command off called');
246
+ });
247
+ this.lightXY.addCommandHandler('moveToLevel', async ({ request: { level }, attributes: { currentLevel } }) => {
248
+ this.lightXY?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.lightXY?.log, this.lightXY);
249
+ this.lightXY?.log.debug(`Command moveToLevel called request: ${level} attributes: ${currentLevel?.getLocal()}`);
250
+ });
251
+ this.lightXY.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level }, attributes: { currentLevel } }) => {
252
+ this.lightXY?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.lightXY?.log, this.lightXY);
253
+ this.lightXY?.log.debug(`Command moveToLevelWithOnOff called request: ${level} attributes: ${currentLevel?.getLocal()}`);
254
+ });
255
+ this.lightXY.addCommandHandler('moveToColor', async ({ request: { colorX, colorY }, attributes: { currentX, currentY } }) => {
256
+ this.lightXY?.setAttribute(ColorControlCluster.id, 'currentX', colorX, this.lightXY?.log, this.lightXY);
257
+ this.lightXY?.setAttribute(ColorControlCluster.id, 'currentY', colorY, this.lightXY?.log, this.lightXY);
258
+ this.lightXY?.log.debug(`Command moveToColor called request: X ${colorX / 65536} Y ${colorY / 65536} attributes: X ${(currentX?.getLocal() ?? 0) / 65536} Y ${(currentY?.getLocal() ?? 0) / 65536}`);
259
+ });
260
+ // Create a light device with CT color control
261
+ this.lightCT = new MatterbridgeDevice(DeviceTypes.COLOR_TEMPERATURE_LIGHT, undefined, this.config.debug);
262
+ this.lightCT.log.logName = 'Light (CT)';
263
+ this.lightCT.createDefaultIdentifyClusterServer();
264
+ this.lightCT.createDefaultGroupsClusterServer();
265
+ this.lightCT.createDefaultScenesClusterServer();
266
+ this.lightCT.createDefaultBridgedDeviceBasicInformationClusterServer('Light (CT)', '0x23480749', 0xfff1, 'Luligu', 'Matterbridge Light');
267
+ this.lightCT.createDefaultOnOffClusterServer();
268
+ this.lightCT.createDefaultLevelControlClusterServer();
269
+ this.lightCT.createDefaultCompleteColorControlClusterServer();
270
+ this.lightCT.configureColorControlCluster(false, false, true, ColorControl.ColorMode.ColorTemperatureMireds);
271
+ this.lightCT.addDeviceType(powerSource);
272
+ this.lightCT.createDefaultPowerSourceReplaceableBatteryClusterServer(70);
273
+ await this.registerDevice(this.lightCT);
274
+ this.lightCT.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
275
+ this.lightCT?.log.info(`Command identify called identifyTime:${identifyTime}`);
276
+ });
277
+ this.lightCT.addCommandHandler('on', async () => {
278
+ this.lightCT?.setAttribute(OnOffCluster.id, 'onOff', true, this.lightCT?.log, this.lightCT);
279
+ this.lightCT?.log.info('Command on called');
280
+ });
281
+ this.lightCT.addCommandHandler('off', async () => {
282
+ this.lightCT?.setAttribute(OnOffCluster.id, 'onOff', false, this.lightCT?.log, this.lightCT);
283
+ this.lightCT?.log.info('Command off called');
284
+ });
285
+ this.lightCT.addCommandHandler('moveToLevel', async ({ request: { level }, attributes: { currentLevel } }) => {
286
+ this.lightCT?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.lightCT?.log, this.lightCT);
287
+ this.lightCT?.log.debug(`Command moveToLevel called request: ${level} attributes: ${currentLevel?.getLocal()}`);
288
+ });
289
+ this.lightCT.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level }, attributes: { currentLevel } }) => {
290
+ this.lightCT?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.lightCT?.log, this.lightCT);
291
+ this.lightCT?.log.debug(`Command moveToLevelWithOnOff called request: ${level} attributes: ${currentLevel?.getLocal()}`);
292
+ });
293
+ this.lightCT.addCommandHandler('moveToColorTemperature', async ({ request, attributes }) => {
294
+ this.lightCT?.setAttribute(ColorControlCluster.id, 'colorTemperatureMireds', request.colorTemperatureMireds, this.lightCT?.log, this.lightCT);
295
+ this.lightCT?.log.debug(`Command moveToColorTemperature called request: ${request.colorTemperatureMireds} attributes: ${attributes.colorTemperatureMireds?.getLocal()}`);
82
296
  });
83
297
  // Create an outlet device
84
- this.outlet = new MatterbridgeDevice(DeviceTypes.ON_OFF_PLUGIN_UNIT);
298
+ this.outlet = new MatterbridgeDevice(DeviceTypes.ON_OFF_PLUGIN_UNIT, undefined, this.config.debug);
299
+ this.outlet.log.logName = 'Outlet';
85
300
  this.outlet.createDefaultIdentifyClusterServer();
86
301
  this.outlet.createDefaultGroupsClusterServer();
87
302
  this.outlet.createDefaultScenesClusterServer();
88
- this.outlet.createDefaultBridgedDeviceBasicInformationClusterServer('Bridged device 4', '0x29252164', 0xfff1, 'Luligu', 'Dynamic device 4');
303
+ this.outlet.createDefaultBridgedDeviceBasicInformationClusterServer('Outlet', '0x29252164', 0xfff1, 'Luligu', 'Matterbridge Outlet');
89
304
  this.outlet.createDefaultOnOffClusterServer();
90
305
  this.outlet.addDeviceType(powerSource);
91
306
  this.outlet.createDefaultPowerSourceWiredClusterServer();
92
307
  await this.registerDevice(this.outlet);
93
308
  this.outlet.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
94
- this.log.info(`Command identify called identifyTime:${identifyTime}`);
309
+ this.outlet?.log.info(`Command identify called identifyTime:${identifyTime}`);
95
310
  });
96
311
  this.outlet.addCommandHandler('on', async () => {
97
- this.outlet?.getClusterServer(OnOffCluster)?.setOnOffAttribute(true);
98
- this.log.info('Command on called');
312
+ this.outlet?.setAttribute(OnOffCluster.id, 'onOff', true, this.outlet?.log, this.outlet);
313
+ this.outlet?.log.info('Command on called');
99
314
  });
100
315
  this.outlet.addCommandHandler('off', async () => {
101
- this.outlet?.getClusterServer(OnOffCluster)?.setOnOffAttribute(false);
102
- this.log.info('Command off called');
316
+ this.outlet?.setAttribute(OnOffCluster.id, 'onOff', false, this.outlet?.log, this.outlet);
317
+ this.outlet?.log.info('Command off called');
103
318
  });
104
319
  // Create a window covering device
105
- this.cover = new MatterbridgeDevice(DeviceTypes.WINDOW_COVERING);
320
+ // Matter uses 10000 = fully closed 0 = fully opened
321
+ this.cover = new MatterbridgeDevice(DeviceTypes.WINDOW_COVERING, undefined, this.config.debug);
322
+ this.cover.log.logName = 'Cover';
106
323
  this.cover.createDefaultIdentifyClusterServer();
107
324
  this.cover.createDefaultGroupsClusterServer();
108
325
  this.cover.createDefaultScenesClusterServer();
109
- this.cover.createDefaultBridgedDeviceBasicInformationClusterServer('Bridged device 1', '0x01020564', 0xfff1, 'Luligu', 'Dynamic device 1');
326
+ this.cover.createDefaultBridgedDeviceBasicInformationClusterServer('Cover', '0x01020564', 0xfff1, 'Luligu', 'Matterbridge Cover');
110
327
  this.cover.createDefaultWindowCoveringClusterServer();
111
328
  this.cover.addDeviceType(powerSource);
112
329
  this.cover.createDefaultPowerSourceRechargeableBatteryClusterServer(86);
113
330
  await this.registerDevice(this.cover);
114
331
  this.cover.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
115
- this.log.info(`Command identify called identifyTime:${identifyTime}`);
332
+ this.cover?.log.info(`Command identify called identifyTime:${identifyTime}`);
116
333
  });
117
334
  this.cover.addCommandHandler('stopMotion', async ({ attributes: { currentPositionLiftPercent100ths, targetPositionLiftPercent100ths, operationalStatus } }) => {
118
335
  this.cover?.setWindowCoveringTargetAsCurrentAndStopped();
119
- this.log.info(`Command stopMotion called: current ${currentPositionLiftPercent100ths?.getLocal()} target ${targetPositionLiftPercent100ths?.getLocal()} status ${operationalStatus?.getLocal().lift}`);
336
+ this.cover?.log.info(`Command stopMotion called: current ${currentPositionLiftPercent100ths?.getLocal()} target ${targetPositionLiftPercent100ths?.getLocal()} status ${operationalStatus?.getLocal().lift}`);
120
337
  });
121
338
  this.cover.addCommandHandler('downOrClose', async ({ attributes: { currentPositionLiftPercent100ths, targetPositionLiftPercent100ths, operationalStatus } }) => {
122
- this.cover?.setWindowCoveringCurrentTargetStatus(0, 0, WindowCovering.MovementStatus.Stopped);
123
- this.log.info(`Command downOrClose called: current ${currentPositionLiftPercent100ths?.getLocal()} target ${targetPositionLiftPercent100ths?.getLocal()} status ${operationalStatus?.getLocal().lift}`);
339
+ this.cover?.setWindowCoveringCurrentTargetStatus(10000, 10000, WindowCovering.MovementStatus.Stopped);
340
+ this.cover?.log.info(`Command downOrClose called: current ${currentPositionLiftPercent100ths?.getLocal()} target ${targetPositionLiftPercent100ths?.getLocal()} status ${operationalStatus?.getLocal().lift}`);
124
341
  });
125
342
  this.cover.addCommandHandler('upOrOpen', async ({ attributes: { currentPositionLiftPercent100ths, targetPositionLiftPercent100ths, operationalStatus } }) => {
126
- this.cover?.setWindowCoveringCurrentTargetStatus(10000, 10000, WindowCovering.MovementStatus.Stopped);
127
- this.log.info(`Command upOrOpen called: current ${currentPositionLiftPercent100ths?.getLocal()} target ${targetPositionLiftPercent100ths?.getLocal()} status ${operationalStatus?.getLocal().lift}`);
343
+ this.cover?.setWindowCoveringCurrentTargetStatus(0, 0, WindowCovering.MovementStatus.Stopped);
344
+ this.cover?.log.info(`Command upOrOpen called: current ${currentPositionLiftPercent100ths?.getLocal()} target ${targetPositionLiftPercent100ths?.getLocal()} status ${operationalStatus?.getLocal().lift}`);
128
345
  });
129
346
  this.cover.addCommandHandler('goToLiftPercentage', async ({ request: { liftPercent100thsValue }, attributes: { currentPositionLiftPercent100ths, targetPositionLiftPercent100ths, operationalStatus } }) => {
130
347
  this.cover?.setWindowCoveringCurrentTargetStatus(liftPercent100thsValue, liftPercent100thsValue, WindowCovering.MovementStatus.Stopped);
131
- this.log.info(`Command goToLiftPercentage ${liftPercent100thsValue} called: current ${currentPositionLiftPercent100ths?.getLocal()} target ${targetPositionLiftPercent100ths?.getLocal()} status ${operationalStatus?.getLocal().lift}`);
348
+ this.cover?.log.info(`Command goToLiftPercentage ${liftPercent100thsValue} called: current ${currentPositionLiftPercent100ths?.getLocal()} target ${targetPositionLiftPercent100ths?.getLocal()} status ${operationalStatus?.getLocal().lift}`);
132
349
  });
133
350
  // Create a lock device
134
- this.lock = new MatterbridgeDevice(DeviceTypes.DOOR_LOCK);
351
+ this.lock = new MatterbridgeDevice(DeviceTypes.DOOR_LOCK, undefined, this.config.debug);
352
+ this.lock.log.logName = 'Lock';
135
353
  this.lock.createDefaultIdentifyClusterServer();
136
- this.lock.createDefaultBridgedDeviceBasicInformationClusterServer('Bridged device 5', '0x96352164', 0xfff1, 'Luligu', 'Dynamic device 5');
354
+ this.lock.createDefaultBridgedDeviceBasicInformationClusterServer('Lock', '0x96352164', 0xfff1, 'Luligu', 'Matterbridge Lock');
137
355
  this.lock.createDefaultDoorLockClusterServer();
138
356
  this.lock.addDeviceType(powerSource);
139
357
  this.lock.createDefaultPowerSourceRechargeableBatteryClusterServer(30);
140
358
  await this.registerDevice(this.lock);
141
359
  this.lock.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
142
- this.log.info(`Command identify called identifyTime:${identifyTime}`);
360
+ this.lock?.log.info(`Command identify called identifyTime:${identifyTime}`);
143
361
  });
144
362
  this.lock.addCommandHandler('lockDoor', async () => {
145
- this.lock?.getClusterServer(DoorLockCluster)?.setLockStateAttribute(DoorLock.LockState.Locked);
146
- this.log.info('Command lockDoor called');
363
+ this.lock?.setAttribute(DoorLockCluster.id, 'lockState', DoorLock.LockState.Locked, this.lock?.log, this.lock);
364
+ this.lock?.log.info('Command lockDoor called');
147
365
  });
148
366
  this.lock.addCommandHandler('unlockDoor', async () => {
149
- this.lock?.getClusterServer(DoorLockCluster)?.setLockStateAttribute(DoorLock.LockState.Unlocked);
150
- this.log.info('Command unlockDoor called');
367
+ this.lock?.setAttribute(DoorLockCluster.id, 'lockState', DoorLock.LockState.Unlocked, this.lock?.log, this.lock);
368
+ this.lock?.log.info('Command unlockDoor called');
151
369
  });
152
370
  // Create a thermostat device
153
- this.thermo = new MatterbridgeDevice(DeviceTypes.THERMOSTAT);
371
+ this.thermo = new MatterbridgeDevice(DeviceTypes.THERMOSTAT, undefined, this.config.debug);
372
+ this.thermo.log.logName = 'Thermostat';
154
373
  this.thermo.createDefaultIdentifyClusterServer();
155
374
  this.thermo.createDefaultGroupsClusterServer();
156
375
  this.thermo.createDefaultScenesClusterServer();
157
- this.thermo.createDefaultBridgedDeviceBasicInformationClusterServer('Bridged device 6', '0x96382164', 0xfff1, 'Luligu', 'Dynamic device 6');
376
+ this.thermo.createDefaultBridgedDeviceBasicInformationClusterServer('Thermostat', '0x96382164', 0xfff1, 'Luligu', 'Matterbridge Thermostat');
158
377
  this.thermo.createDefaultThermostatClusterServer(20, 18, 22);
159
378
  this.thermo.addDeviceType(powerSource);
160
379
  this.thermo.createDefaultPowerSourceRechargeableBatteryClusterServer(70);
@@ -166,134 +385,341 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
166
385
  humidityChild.getClusterServer(RelativeHumidityMeasurement.Cluster)?.setMeasuredValueAttribute(80 * 100);
167
386
  await this.registerDevice(this.thermo);
168
387
  this.thermo.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
169
- this.log.info(`Command identify called identifyTime:${identifyTime}`);
170
- // if (this.thermo) logEndpoint(this.thermo);
388
+ this.thermo?.log.info(`Command identify called identifyTime:${identifyTime}`);
171
389
  });
172
390
  this.thermo.addCommandHandler('setpointRaiseLower', async ({ request: { mode, amount }, attributes }) => {
173
391
  const lookupSetpointAdjustMode = ['Heat', 'Cool', 'Both'];
174
- this.log.info(`Command setpointRaiseLower called with mode: ${lookupSetpointAdjustMode[mode]} amount: ${amount / 10}`);
175
- if (mode === Thermostat.SetpointAdjustMode.Heat && attributes.occupiedHeatingSetpoint) {
392
+ this.thermo?.log.info(`Command setpointRaiseLower called with mode: ${lookupSetpointAdjustMode[mode]} amount: ${amount / 10}`);
393
+ if (mode === /* Thermostat.SetpointRaiseLowerMode.Heat*/ 0 && attributes.occupiedHeatingSetpoint) {
176
394
  const setpoint = attributes.occupiedHeatingSetpoint?.getLocal() / 100 + amount / 10;
177
395
  attributes.occupiedHeatingSetpoint.setLocal(setpoint * 100);
178
- this.log.info('Set occupiedHeatingSetpoint:', setpoint);
396
+ this.thermo?.log.info('Set occupiedHeatingSetpoint:', setpoint);
179
397
  }
180
- if (mode === Thermostat.SetpointAdjustMode.Cool && attributes.occupiedCoolingSetpoint) {
398
+ if (mode === /* Thermostat.SetpointRaiseLowerMode.Cool*/ 1 && attributes.occupiedCoolingSetpoint) {
181
399
  const setpoint = attributes.occupiedCoolingSetpoint.getLocal() / 100 + amount / 10;
182
400
  attributes.occupiedCoolingSetpoint.setLocal(setpoint * 100);
183
- this.log.info('Set occupiedCoolingSetpoint:', setpoint);
401
+ this.thermo?.log.info('Set occupiedCoolingSetpoint:', setpoint);
184
402
  }
185
403
  });
186
404
  const thermostat = this.thermo.getClusterServer(ThermostatCluster.with(Thermostat.Feature.Heating, Thermostat.Feature.Cooling, Thermostat.Feature.AutoMode));
187
405
  if (thermostat) {
188
- thermostat.subscribeSystemModeAttribute(async (value) => {
406
+ subscribeAttribute(ThermostatCluster.id, 'systemMode', async (value) => {
189
407
  const lookupSystemMode = ['Off', 'Auto', '', 'Cool', 'Heat', 'EmergencyHeat', 'Precooling', 'FanOnly', 'Dry', 'Sleep'];
190
- this.log.info('Subscribe systemMode called with:', lookupSystemMode[value]);
191
- });
192
- thermostat.subscribeOccupiedHeatingSetpointAttribute(async (value) => {
193
- this.log.info('Subscribe occupiedHeatingSetpoint called with:', value / 100);
194
- });
195
- thermostat.subscribeOccupiedCoolingSetpointAttribute(async (value) => {
196
- this.log.info('Subscribe occupiedCoolingSetpoint called with:', value / 100);
197
- });
408
+ this.thermo?.log.info('Subscribe systemMode called with:', lookupSystemMode[value]);
409
+ }, this.thermo.log, this.thermo);
410
+ subscribeAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', async (value) => {
411
+ this.thermo?.log.info('Subscribe occupiedHeatingSetpoint called with:', value / 100);
412
+ }, this.thermo.log, this.thermo);
413
+ subscribeAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', async (value) => {
414
+ this.thermo?.log.info('Subscribe occupiedCoolingSetpoint called with:', value / 100);
415
+ }, this.thermo.log, this.thermo);
198
416
  }
417
+ // Create a fan device
418
+ this.fan = new MatterbridgeDevice([DeviceTypes.FAN, bridgedNode], undefined, this.config.debug);
419
+ this.fan.log.logName = 'Fan';
420
+ this.fan.createDefaultBridgedDeviceBasicInformationClusterServer('Fan', 'serial_980545631228', 0xfff1, 'Luligu', 'Matterbridge Fan', 2, '2.1.1');
421
+ this.fan.addDeviceTypeWithClusterServer([DeviceTypes.FAN], []);
422
+ await this.registerDevice(this.fan);
423
+ const fanCluster = this.fan.getClusterServer(FanControlCluster.with(FanControl.Feature.MultiSpeed, FanControl.Feature.Auto));
424
+ if (fanCluster) {
425
+ const fanModeLookup = ['Off', 'Low', 'Medium', 'High', 'On', 'Auto', 'Smart'];
426
+ subscribeAttribute(FanControlCluster.id, 'fanMode', (newValue, oldValue) => {
427
+ this.fan?.log.info(`Fan mode changed from ${fanModeLookup[oldValue]} to ${fanModeLookup[newValue]}`);
428
+ if (newValue === FanControl.FanMode.Off) {
429
+ fanCluster.setPercentCurrentAttribute(0);
430
+ }
431
+ else if (newValue === FanControl.FanMode.Low) {
432
+ fanCluster.setPercentCurrentAttribute(33);
433
+ }
434
+ else if (newValue === FanControl.FanMode.Medium) {
435
+ fanCluster.setPercentCurrentAttribute(66);
436
+ }
437
+ else if (newValue === FanControl.FanMode.High) {
438
+ fanCluster.setPercentCurrentAttribute(100);
439
+ }
440
+ else if (newValue === FanControl.FanMode.On) {
441
+ fanCluster.setPercentCurrentAttribute(100);
442
+ }
443
+ else if (newValue === FanControl.FanMode.Auto) {
444
+ fanCluster.setPercentCurrentAttribute(50);
445
+ }
446
+ }, this.fan.log, this.fan);
447
+ subscribeAttribute(FanControlCluster.id, 'percentSetting', (newValue, oldValue) => {
448
+ this.fan?.log.info(`Percent setting changed from ${oldValue} to ${newValue}`);
449
+ if (newValue)
450
+ fanCluster.setPercentCurrentAttribute(newValue);
451
+ }, this.fan.log, this.fan);
452
+ subscribeAttribute(FanControlCluster.id, 'speedSetting', (newValue, oldValue) => {
453
+ this.fan?.log.info(`Speed setting changed from ${oldValue} to ${newValue}`);
454
+ if (newValue)
455
+ fanCluster.setSpeedCurrentAttribute(newValue);
456
+ }, this.fan.log, this.fan);
457
+ }
458
+ this.waterLeak = new MatterbridgeDevice([waterLeakDetector, bridgedNode], undefined, this.config.debug);
459
+ this.waterLeak.log.logName = 'Water leak detector';
460
+ this.waterLeak.createDefaultBridgedDeviceBasicInformationClusterServer('Water leak detector', 'serial_98745631222', 0xfff1, 'Luligu', 'Matterbridge WaterLeakDetector');
461
+ this.waterLeak.addDeviceTypeWithClusterServer([waterLeakDetector], [BooleanStateConfiguration.Cluster.id]);
462
+ this.waterLeak.setAttribute(BooleanStateCluster.id, 'stateValue', false, this.waterLeak.log);
463
+ await this.registerDevice(this.waterLeak);
464
+ this.waterFreeze = new MatterbridgeDevice([waterFreezeDetector, bridgedNode], undefined, this.config.debug);
465
+ this.waterFreeze.log.logName = 'Water freeze detector';
466
+ this.waterFreeze.createDefaultBridgedDeviceBasicInformationClusterServer('Water freeze detector', 'serial_98745631223', 0xfff1, 'Luligu', 'Matterbridge WaterFreezeDetector');
467
+ this.waterFreeze.addDeviceTypeWithClusterServer([waterFreezeDetector], [BooleanStateConfiguration.Cluster.id]);
468
+ this.waterFreeze.setAttribute(BooleanStateCluster.id, 'stateValue', false, this.waterFreeze.log);
469
+ await this.registerDevice(this.waterFreeze);
470
+ this.rain = new MatterbridgeDevice([rainSensor, bridgedNode], undefined, this.config.debug);
471
+ this.rain.log.logName = 'Rain sensor';
472
+ this.rain.createDefaultBridgedDeviceBasicInformationClusterServer('Rain sensor', 'serial_98745631224', 0xfff1, 'Luligu', 'Matterbridge RainSensor');
473
+ this.rain.addDeviceTypeWithClusterServer([rainSensor], [BooleanStateConfiguration.Cluster.id]);
474
+ this.rain.setAttribute(BooleanStateCluster.id, 'stateValue', false, this.rain.log);
475
+ await this.registerDevice(this.rain);
476
+ this.smoke = new MatterbridgeDevice([smokeCoAlarm, bridgedNode], undefined, this.config.debug);
477
+ this.smoke.log.logName = 'Smoke alarm sensor';
478
+ this.smoke.createDefaultBridgedDeviceBasicInformationClusterServer('Smoke alarm sensor', 'serial_94745631225', 0xfff1, 'Luligu', 'Matterbridge SmokeCoAlarm');
479
+ this.smoke.addDeviceTypeWithClusterServer([smokeCoAlarm], [CarbonMonoxideConcentrationMeasurement.Cluster.id]);
480
+ this.smoke.setAttribute(SmokeCoAlarmCluster.id, 'smokeState', SmokeCoAlarm.AlarmState.Normal, this.smoke.log);
481
+ this.smoke.setAttribute(SmokeCoAlarmCluster.id, 'coState', SmokeCoAlarm.AlarmState.Normal, this.smoke.log);
482
+ this.smoke.setAttribute(CarbonMonoxideConcentrationMeasurement.Cluster.id, 'measuredValue', 100, this.smoke.log);
483
+ await this.registerDevice(this.smoke);
484
+ // Create an airQuality device
485
+ this.airQuality = new MatterbridgeDevice([airQualitySensor, bridgedNode], undefined, this.config.debug);
486
+ this.airQuality.log.logName = 'Air quality Sensor';
487
+ this.airQuality.createDefaultBridgedDeviceBasicInformationClusterServer('Air quality sensor', 'serial_987484318322', 0xfff1, 'Luligu', 'Matterbridge Air Quality Sensor');
488
+ this.airQuality.addDeviceTypeWithClusterServer([airQualitySensor], [
489
+ TemperatureMeasurement.Cluster.id,
490
+ RelativeHumidityMeasurement.Cluster.id,
491
+ CarbonMonoxideConcentrationMeasurement.Cluster.id,
492
+ CarbonDioxideConcentrationMeasurement.Cluster.id,
493
+ NitrogenDioxideConcentrationMeasurement.Cluster.id,
494
+ OzoneConcentrationMeasurement.Cluster.id,
495
+ FormaldehydeConcentrationMeasurement.Cluster.id,
496
+ Pm1ConcentrationMeasurement.Cluster.id,
497
+ Pm25ConcentrationMeasurement.Cluster.id,
498
+ Pm10ConcentrationMeasurement.Cluster.id,
499
+ RadonConcentrationMeasurement.Cluster.id,
500
+ TotalVolatileOrganicCompoundsConcentrationMeasurement.Cluster.id,
501
+ ]);
502
+ this.airQuality.setAttribute(AirQuality.Cluster.id, 'airQuality', AirQuality.AirQualityEnum.Good, this.airQuality.log);
503
+ this.airQuality.setAttribute(TemperatureMeasurement.Cluster.id, 'measuredValue', 2150, this.airQuality.log);
504
+ this.airQuality.setAttribute(RelativeHumidityMeasurement.Cluster.id, 'measuredValue', 5500, this.airQuality.log);
505
+ this.airQuality.setAttribute(CarbonMonoxideConcentrationMeasurement.Cluster.id, 'measuredValue', 10, this.airQuality.log);
506
+ this.airQuality.setAttribute(CarbonDioxideConcentrationMeasurement.Cluster.id, 'measuredValue', 400, this.airQuality.log);
507
+ this.airQuality.setAttribute(NitrogenDioxideConcentrationMeasurement.Cluster.id, 'measuredValue', 1, this.airQuality.log);
508
+ this.airQuality.setAttribute(OzoneConcentrationMeasurement.Cluster.id, 'measuredValue', 1, this.airQuality.log);
509
+ this.airQuality.setAttribute(FormaldehydeConcentrationMeasurement.Cluster.id, 'measuredValue', 1, this.airQuality.log);
510
+ this.airQuality.setAttribute(Pm1ConcentrationMeasurement.Cluster.id, 'measuredValue', 100, this.airQuality.log);
511
+ this.airQuality.setAttribute(Pm25ConcentrationMeasurement.Cluster.id, 'measuredValue', 100, this.airQuality.log);
512
+ this.airQuality.setAttribute(Pm10ConcentrationMeasurement.Cluster.id, 'measuredValue', 100, this.airQuality.log);
513
+ this.airQuality.setAttribute(RadonConcentrationMeasurement.Cluster.id, 'measuredValue', 100, this.airQuality.log);
514
+ this.airQuality.setAttribute(TotalVolatileOrganicCompoundsConcentrationMeasurement.Cluster.id, 'measuredValue', 100, this.airQuality.log);
515
+ await this.registerDevice(this.airQuality);
199
516
  }
200
517
  async onConfigure() {
201
518
  this.log.info('onConfigure called');
202
519
  // Set switch to off
203
- this.switch?.getClusterServer(OnOffCluster)?.setOnOffAttribute(false);
204
- this.log.info('Set switch initial onOff to false');
520
+ this.switch?.setAttribute(OnOffCluster.id, 'onOff', false, this.switch.log);
521
+ this.switch?.log.info('Set switch initial onOff to false');
522
+ // Toggle switch onOff every minute
205
523
  this.switchInterval = setInterval(() => {
206
- if (!this.switch)
207
- return;
208
- const status = this.switch.getClusterServer(OnOffCluster)?.getOnOffAttribute();
209
- this.switch.getClusterServer(OnOffCluster)?.setOnOffAttribute(!status);
210
- this.log.info(`Set switch onOff to ${status}`);
524
+ const status = this.switch?.getAttribute(OnOffCluster.id, 'onOff', this.switch.log);
525
+ if (isValidBoolean(status)) {
526
+ this.switch?.setAttribute(OnOffCluster.id, 'onOff', !status, this.switch.log);
527
+ this.switch?.log.info(`Set switch onOff to ${!status}`);
528
+ }
211
529
  }, 60 * 1000 + 100);
530
+ // Set light on/off to off
531
+ this.lightOnOff?.setAttribute(OnOffCluster.id, 'onOff', false, this.lightOnOff.log);
532
+ this.lightOnOff?.log.info('Set light initial onOff to false.');
533
+ // Set light on/off to off
534
+ this.dimmer?.setAttribute(OnOffCluster.id, 'onOff', false, this.dimmer.log);
535
+ this.dimmer?.setAttribute(LevelControlCluster.id, 'currentLevel', 0, this.dimmer.log);
536
+ this.dimmer?.log.info('Set dimmer initial onOff to false, currentLevel to 0.');
212
537
  // Set light to off, level to 0 and hue to 0 and saturation to 50% (pink color)
213
- this.light?.getClusterServer(OnOffCluster)?.setOnOffAttribute(false);
214
- this.light?.getClusterServer(LevelControlCluster)?.setCurrentLevelAttribute(0);
215
- this.light?.getClusterServer(ColorControlCluster.with(ColorControl.Feature.HueSaturation))?.setCurrentHueAttribute(0);
216
- this.light?.getClusterServer(ColorControlCluster.with(ColorControl.Feature.HueSaturation))?.setCurrentSaturationAttribute(128);
217
- this.log.info('Set light initial onOff to false, currentLevel to 0, hue to 0 and saturation to 50%.');
538
+ this.light?.setAttribute(OnOffCluster.id, 'onOff', false, this.light.log);
539
+ this.light?.setAttribute(LevelControlCluster.id, 'currentLevel', 0, this.light.log);
540
+ this.light?.setAttribute(ColorControlCluster.id, 'currentHue', 0, this.light.log);
541
+ this.light?.setAttribute(ColorControlCluster.id, 'currentSaturation', 128, this.light.log);
542
+ this.light?.configureColorControlMode(ColorControl.ColorMode.CurrentHueAndCurrentSaturation);
543
+ this.light?.log.info('Set light initial onOff to false, currentLevel to 0, hue to 0 and saturation to 50%.');
544
+ // Set light XY to true, level to 100% and XY to red
545
+ this.lightXY?.setAttribute(OnOffCluster.id, 'onOff', true, this.lightXY.log);
546
+ this.lightXY?.setAttribute(LevelControlCluster.id, 'currentLevel', 254, this.lightXY.log);
547
+ this.lightXY?.setAttribute(ColorControlCluster.id, 'currentX', 0.7006 * 65536, this.lightXY.log);
548
+ this.lightXY?.setAttribute(ColorControlCluster.id, 'currentY', 0.2993 * 65536, this.lightXY.log);
549
+ this.lightXY?.configureColorControlMode(ColorControl.ColorMode.CurrentXAndCurrentY);
550
+ this.lightXY?.log.info('Set light XY initial onOff to true, currentLevel to 254, X to 0.7006 and Y to 0.2993.');
551
+ // Set light HS to off, level to 0 and hue to 0 and saturation to 50% (pink color)
552
+ this.lightHS?.setAttribute(OnOffCluster.id, 'onOff', false, this.lightHS.log);
553
+ this.lightHS?.setAttribute(LevelControlCluster.id, 'currentLevel', 0, this.lightHS.log);
554
+ this.lightHS?.setAttribute(ColorControlCluster.id, 'currentHue', 0, this.lightHS.log);
555
+ this.lightHS?.setAttribute(ColorControlCluster.id, 'currentSaturation', 128, this.lightHS.log);
556
+ this.lightHS?.configureColorControlMode(ColorControl.ColorMode.CurrentHueAndCurrentSaturation);
557
+ this.lightHS?.log.info('Set light HS initial onOff to false, currentLevel to 0, hue to 0 and saturation to 50%.');
558
+ // Set light CT to true, level to 50% and colorTemperatureMireds to 250
559
+ this.lightCT?.setAttribute(OnOffCluster.id, 'onOff', true, this.lightCT.log);
560
+ this.lightCT?.setAttribute(LevelControlCluster.id, 'currentLevel', 128, this.lightCT.log);
561
+ this.lightCT?.setAttribute(ColorControlCluster.id, 'colorTemperatureMireds', 250, this.lightCT.log);
562
+ this.lightCT?.configureColorControlMode(ColorControl.ColorMode.ColorTemperatureMireds);
563
+ this.lightCT?.log.info('Set light CT initial onOff to true, currentLevel to 128, colorTemperatureMireds to 250.');
218
564
  this.lightInterval = setInterval(() => {
219
- if (!this.light)
220
- return;
221
- const lightLevelControlCluster = this.light.getClusterServer(LevelControlCluster);
222
- if (lightLevelControlCluster) {
223
- let level = lightLevelControlCluster.getCurrentLevelAttribute();
224
- if (level === null)
225
- return;
565
+ const state = this.light?.getAttribute(OnOffCluster.id, 'onOff', this.light.log);
566
+ let level = this.light?.getAttribute(LevelControlCluster.id, 'currentLevel', this.light.log);
567
+ if (isValidBoolean(state) && isValidNumber(level, 0, 254)) {
226
568
  level += 10;
227
569
  if (level > 254) {
228
570
  level = 0;
229
- this.light.getClusterServer(OnOffCluster)?.setOnOffAttribute(false);
230
- this.log.info('Set light onOff to false');
231
- return;
571
+ this.lightOnOff?.setAttribute(OnOffCluster.id, 'onOff', false, this.lightOnOff.log);
572
+ this.dimmer?.setAttribute(OnOffCluster.id, 'onOff', false, this.dimmer.log);
573
+ this.light?.setAttribute(OnOffCluster.id, 'onOff', false, this.light.log);
574
+ this.lightXY?.setAttribute(OnOffCluster.id, 'onOff', false, this.lightXY.log);
575
+ this.lightHS?.setAttribute(OnOffCluster.id, 'onOff', false, this.lightHS.log);
576
+ this.lightCT?.setAttribute(OnOffCluster.id, 'onOff', false, this.lightCT.log);
577
+ this.log.info('Set lights onOff to false');
578
+ this.dimmer?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.dimmer.log);
579
+ this.light?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.light.log);
580
+ this.lightXY?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.lightXY.log);
581
+ this.lightHS?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.lightHS.log);
582
+ this.lightCT?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.lightCT.log);
583
+ this.log.info(`Set lights currentLevel to ${level}`);
232
584
  }
233
585
  else {
234
- this.light.getClusterServer(OnOffCluster)?.setOnOffAttribute(true);
235
- this.log.info('Set light onOff to true');
586
+ this.lightOnOff?.setAttribute(OnOffCluster.id, 'onOff', true, this.lightOnOff?.log);
587
+ this.dimmer?.setAttribute(OnOffCluster.id, 'onOff', true, this.dimmer.log);
588
+ this.light?.setAttribute(OnOffCluster.id, 'onOff', true, this.light.log);
589
+ this.lightXY?.setAttribute(OnOffCluster.id, 'onOff', true, this.lightXY.log);
590
+ this.lightHS?.setAttribute(OnOffCluster.id, 'onOff', true, this.lightHS.log);
591
+ this.lightCT?.setAttribute(OnOffCluster.id, 'onOff', true, this.lightCT.log);
592
+ this.log.info('Set lights onOff to true');
593
+ this.dimmer?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.dimmer.log);
594
+ this.light?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.light.log);
595
+ this.lightXY?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.lightXY.log);
596
+ this.lightHS?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.lightHS.log);
597
+ this.lightCT?.setAttribute(LevelControlCluster.id, 'currentLevel', level, this.lightCT.log);
598
+ this.log.info(`Set lights currentLevel to ${level}`);
236
599
  }
237
- lightLevelControlCluster.setCurrentLevelAttribute(level);
238
- this.log.info(`Set light currentLevel to ${level}`);
239
600
  }
240
601
  }, 60 * 1000 + 200);
241
602
  // Set outlet to off
242
- this.outlet?.getClusterServer(OnOffCluster)?.setOnOffAttribute(false);
243
- this.log.info('Set outlet initial onOff to false');
603
+ this.outlet?.setAttribute(OnOffCluster.id, 'onOff', false, this.outlet.log);
604
+ this.outlet?.log.info('Set outlet initial onOff to false');
605
+ // Toggle outlet onOff every minute
244
606
  this.outletInterval = setInterval(() => {
245
- if (!this.outlet)
246
- return;
247
- const status = this.outlet.getClusterServer(OnOffCluster)?.getOnOffAttribute();
248
- this.outlet.getClusterServer(OnOffCluster)?.setOnOffAttribute(!status);
249
- this.log.info(`Set outlet onOff to ${status}`);
607
+ const state = this.outlet?.getAttribute(OnOffCluster.id, 'onOff', this.outlet.log);
608
+ if (isValidBoolean(state)) {
609
+ this.outlet?.setAttribute(OnOffCluster.id, 'onOff', !state, this.outlet.log);
610
+ this.outlet?.log.info(`Set outlet onOff to ${!state}`);
611
+ }
250
612
  }, 60 * 1000 + 300);
251
613
  // Set cover to target = current position and status to stopped (current position is persisted in the cluster)
252
614
  this.cover?.setWindowCoveringTargetAsCurrentAndStopped();
253
- this.log.info('Set cover initial targetPositionLiftPercent100ths = currentPositionLiftPercent100ths and operationalStatus to Stopped.');
615
+ this.cover?.log.info('Set cover initial targetPositionLiftPercent100ths = currentPositionLiftPercent100ths and operationalStatus to Stopped.');
616
+ // Increment cover position every minute
254
617
  this.coverInterval = setInterval(() => {
255
- if (!this.cover)
256
- return;
257
- const coverCluster = this.cover.getClusterServer(WindowCoveringCluster.with(WindowCovering.Feature.Lift, WindowCovering.Feature.PositionAwareLift));
258
- if (coverCluster && coverCluster.getCurrentPositionLiftPercent100thsAttribute) {
259
- let position = coverCluster.getCurrentPositionLiftPercent100thsAttribute();
260
- if (position === null)
261
- return;
618
+ let position = this.cover?.getAttribute(WindowCoveringCluster.id, 'currentPositionLiftPercent100ths', this.cover.log);
619
+ if (isValidNumber(position, 0, 10000)) {
262
620
  position = position > 9000 ? 0 : position + 1000;
263
- coverCluster.setTargetPositionLiftPercent100thsAttribute(position);
264
- coverCluster.setCurrentPositionLiftPercent100thsAttribute(position);
265
- coverCluster.setOperationalStatusAttribute({
266
- global: WindowCovering.MovementStatus.Stopped,
267
- lift: WindowCovering.MovementStatus.Stopped,
268
- tilt: WindowCovering.MovementStatus.Stopped,
269
- });
270
- this.log.info(`Set cover current and target positionLiftPercent100ths to ${position} and operationalStatus to Stopped`);
621
+ this.cover?.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', position, this.cover.log);
622
+ this.cover?.setAttribute(WindowCoveringCluster.id, 'currentPositionLiftPercent100ths', position, this.cover.log);
623
+ this.cover?.setAttribute(WindowCoveringCluster.id, 'operationalStatus', { global: WindowCovering.MovementStatus.Stopped, lift: WindowCovering.MovementStatus.Stopped, tilt: WindowCovering.MovementStatus.Stopped }, this.cover.log);
624
+ this.cover?.log.info(`Set cover current and target positionLiftPercent100ths to ${position} and operationalStatus to Stopped`);
271
625
  }
272
626
  }, 60 * 1000 + 400);
273
627
  // Set lock to Locked
274
- this.lock?.getClusterServer(DoorLockCluster)?.setLockStateAttribute(DoorLock.LockState.Locked);
275
- this.log.info('Set lock initial lockState to Locked');
628
+ this.lock?.setAttribute(DoorLockCluster.id, 'lockState', DoorLock.LockState.Locked, this.lock.log);
629
+ this.lock?.log.info('Set lock initial lockState to Locked');
630
+ // Toggle lock every minute
276
631
  this.lockInterval = setInterval(() => {
277
- if (!this.lock)
278
- return;
279
- const status = this.lock.getClusterServer(DoorLockCluster)?.getLockStateAttribute();
280
- this.lock.getClusterServer(DoorLockCluster)?.setLockStateAttribute(status === DoorLock.LockState.Locked ? DoorLock.LockState.Unlocked : DoorLock.LockState.Locked);
281
- this.log.info(`Set lock lockState to ${status === DoorLock.LockState.Locked ? 'Locked' : 'Unlocked'}`);
282
- }, 60 * 1000 + 700);
632
+ const status = this.lock?.getAttribute(DoorLockCluster.id, 'lockState', this.lock.log);
633
+ if (isValidNumber(status, DoorLock.LockState.Locked, DoorLock.LockState.Unlocked)) {
634
+ this.lock?.setAttribute(DoorLockCluster.id, 'lockState', status === DoorLock.LockState.Locked ? DoorLock.LockState.Unlocked : DoorLock.LockState.Locked, this.lock.log);
635
+ this.lock?.log.info(`Set lock lockState to ${status === DoorLock.LockState.Locked ? 'Unlocked' : 'Locked'}`);
636
+ }
637
+ }, 60 * 1000 + 500);
283
638
  // Set local to 16°C
284
- this.thermo?.getClusterServer(ThermostatCluster.with(Thermostat.Feature.Heating, Thermostat.Feature.Cooling, Thermostat.Feature.AutoMode))?.setLocalTemperatureAttribute(1600);
285
- this.log.info('Set thermo initial localTemperature to 16°C');
639
+ this.thermo?.setAttribute(ThermostatCluster.id, 'localTemperature', 1600, this.thermo.log);
640
+ this.thermo?.setAttribute(ThermostatCluster.id, 'systemMode', Thermostat.SystemMode.Auto, this.thermo.log);
641
+ this.thermo?.log.info('Set thermostat initial localTemperature to 16°C and mode Auto');
642
+ // Increment localTemperature every minute
286
643
  this.thermoInterval = setInterval(() => {
287
- if (!this.thermo)
288
- return;
289
- const cluster = this.thermo.getClusterServer(ThermostatCluster.with(Thermostat.Feature.Heating, Thermostat.Feature.Cooling, Thermostat.Feature.AutoMode));
290
- if (!cluster)
291
- return;
292
- let local = cluster.getLocalTemperatureAttribute() ?? 1600;
293
- local = local >= 2300 ? 1600 : local + 100;
294
- cluster.setLocalTemperatureAttribute(local);
295
- this.log.info(`Set thermo localTemperature to ${local / 100}°C`);
644
+ let temperature = this.thermo?.getAttribute(ThermostatCluster.id, 'localTemperature', this.thermo.log);
645
+ if (isValidNumber(temperature, 1600, 2400)) {
646
+ temperature = temperature + 100 >= 2400 ? 1600 : temperature + 100;
647
+ this.thermo?.setAttribute(ThermostatCluster.id, 'localTemperature', temperature, this.thermo.log);
648
+ this.thermo?.log.info(`Set thermostat localTemperature to ${temperature / 100}°C`);
649
+ }
650
+ }, 60 * 1000 + 600);
651
+ // Set fan to auto
652
+ this.fan?.log.info('Set fan initial fanMode to Auto, percentCurrent to 50 and speedCurrent to 50');
653
+ this.fan?.setAttribute(FanControlCluster.id, 'fanMode', FanControl.FanMode.Auto, this.fan.log);
654
+ this.fan?.setAttribute(FanControlCluster.id, 'percentCurrent', 50, this.fan.log);
655
+ this.fan?.setAttribute(FanControlCluster.id, 'speedCurrent', 50, this.fan.log);
656
+ // Increment fan percentCurrent every minute
657
+ this.fanInterval = setInterval(() => {
658
+ const mode = this.fan?.getAttribute(FanControlCluster.id, 'fanMode', this.fan.log);
659
+ let value = this.fan?.getAttribute(FanControlCluster.id, 'percentCurrent', this.fan.log);
660
+ if (isValidNumber(mode, FanControl.FanMode.Off, FanControl.FanMode.Auto) && mode === FanControl.FanMode.Auto && isValidNumber(value, 0, 100)) {
661
+ value = value + 10 >= 100 ? 0 : value + 10;
662
+ this.fan?.setAttribute(FanControlCluster.id, 'percentCurrent', value, this.fan.log);
663
+ this.fan?.log.info(`Set fan percentCurrent to ${value}`);
664
+ }
296
665
  }, 60 * 1000 + 700);
666
+ // Set waterLeak to false
667
+ this.waterLeak?.setAttribute(BooleanStateCluster.id, 'stateValue', false, this.waterLeak.log);
668
+ // Toggle waterLeak every minute
669
+ this.waterLeakInterval = setInterval(() => {
670
+ let value = this.waterLeak?.getAttribute(BooleanStateCluster.id, 'stateValue', this.waterLeak.log);
671
+ if (isValidBoolean(value)) {
672
+ value = !value;
673
+ this.waterLeak?.setAttribute(BooleanStateCluster.id, 'stateValue', value, this.waterLeak.log);
674
+ this.waterLeak?.log.info(`Set waterLeak stateValue to ${value}`);
675
+ }
676
+ }, 60 * 1000 + 800);
677
+ // Set waterFreeze to false
678
+ this.waterFreeze?.setAttribute(BooleanStateCluster.id, 'stateValue', false, this.waterFreeze.log);
679
+ // Toggle waterFreeze every minute
680
+ this.waterFreezeInterval = setInterval(() => {
681
+ let value = this.waterFreeze?.getAttribute(BooleanStateCluster.id, 'stateValue', this.waterFreeze.log);
682
+ if (isValidBoolean(value)) {
683
+ value = !value;
684
+ this.waterFreeze?.setAttribute(BooleanStateCluster.id, 'stateValue', value, this.waterFreeze.log);
685
+ this.waterFreeze?.log.info(`Set waterFreeze stateValue to ${value}`);
686
+ }
687
+ }, 60 * 1000 + 900);
688
+ // Set rain to false
689
+ this.rain?.setAttribute(BooleanStateCluster.id, 'stateValue', false, this.rain.log);
690
+ // Toggle rain every minute
691
+ this.rainInterval = setInterval(() => {
692
+ let value = this.rain?.getAttribute(BooleanStateCluster.id, 'stateValue', this.rain.log);
693
+ if (isValidBoolean(value)) {
694
+ value = !value;
695
+ this.rain?.setAttribute(BooleanStateCluster.id, 'stateValue', value, this.rain.log);
696
+ this.rain?.log.info(`Set rain stateValue to ${value}`);
697
+ }
698
+ }, 60 * 1000 + 1000);
699
+ // Set smoke to Normal
700
+ this.smoke?.setAttribute(SmokeCoAlarmCluster.id, 'smokeState', SmokeCoAlarm.AlarmState.Normal, this.smoke.log);
701
+ this.smoke?.setAttribute(SmokeCoAlarmCluster.id, 'coState', SmokeCoAlarm.AlarmState.Normal, this.smoke.log);
702
+ // Toggle smoke every minute
703
+ this.smokeInterval = setInterval(() => {
704
+ let value = this.smoke?.getAttribute(SmokeCoAlarmCluster.id, 'smokeState', this.smoke.log);
705
+ if (isValidNumber(value, SmokeCoAlarm.AlarmState.Normal, SmokeCoAlarm.AlarmState.Critical)) {
706
+ value = value === SmokeCoAlarm.AlarmState.Normal ? SmokeCoAlarm.AlarmState.Critical : SmokeCoAlarm.AlarmState.Normal;
707
+ this.smoke?.setAttribute(SmokeCoAlarmCluster.id, 'smokeState', value, this.smoke.log);
708
+ this.smoke?.setAttribute(SmokeCoAlarmCluster.id, 'coState', value, this.smoke.log);
709
+ this.smoke?.log.info(`Set smoke smokeState and coState to ${value}`);
710
+ }
711
+ }, 60 * 1000 + 1100);
712
+ // Set air quality to Normal
713
+ this.airQuality?.setAttribute(AirQualityCluster.id, 'airQuality', AirQuality.AirQualityEnum.Good, this.airQuality.log);
714
+ // Toggle air quality every minute
715
+ this.airQualityInterval = setInterval(() => {
716
+ let value = this.airQuality?.getAttribute(AirQualityCluster.id, 'airQuality', this.airQuality?.log);
717
+ if (isValidNumber(value, AirQuality.AirQualityEnum.Good, AirQuality.AirQualityEnum.ExtremelyPoor)) {
718
+ value = value >= AirQuality.AirQualityEnum.ExtremelyPoor ? AirQuality.AirQualityEnum.Good : value + 1;
719
+ this.airQuality?.setAttribute(AirQualityCluster.id, 'airQuality', value, this.airQuality.log);
720
+ this.smoke?.log.info(`Set air quality to ${value}`);
721
+ }
722
+ }, 60 * 1000 + 1100);
297
723
  }
298
724
  async onShutdown(reason) {
299
725
  this.log.info('onShutdown called with reason:', reason ?? 'none');
@@ -303,8 +729,71 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
303
729
  clearInterval(this.coverInterval);
304
730
  clearInterval(this.lockInterval);
305
731
  clearInterval(this.thermoInterval);
732
+ clearInterval(this.fanInterval);
733
+ clearInterval(this.waterLeakInterval);
734
+ clearInterval(this.waterFreezeInterval);
735
+ clearInterval(this.rainInterval);
736
+ clearInterval(this.smokeInterval);
737
+ clearInterval(this.airQualityInterval);
306
738
  if (this.config.unregisterOnShutdown === true)
307
739
  await this.unregisterAllDevices();
308
740
  }
309
741
  }
742
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
743
+ function subscribeAttribute(clusterId, attribute, listener, log, endpoint) {
744
+ // if (!endpoint) endpoint = this as Endpoint;
745
+ if (!endpoint)
746
+ return false;
747
+ const clusterServer = endpoint.getClusterServerById(clusterId);
748
+ if (!clusterServer) {
749
+ log?.error(`subscribeAttribute error: Cluster ${clusterId} not found on endpoint ${endpoint.name}:${endpoint.number}`);
750
+ return false;
751
+ }
752
+ const capitalizedAttributeName = attribute.charAt(0).toUpperCase() + attribute.slice(1);
753
+ if (!clusterServer.isAttributeSupportedByName(attribute) && !clusterServer.isAttributeSupportedByName(capitalizedAttributeName)) {
754
+ if (log)
755
+ log.error(`subscribeAttribute error: Attribute ${attribute} not found on Cluster ${clusterServer.name} on endpoint ${endpoint.name}:${endpoint.number}`);
756
+ return false;
757
+ }
758
+ // Find the subscribe method
759
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
760
+ if (!clusterServer[`subscribe${capitalizedAttributeName}Attribute`]) {
761
+ log?.error(`subscribeAttribute error: subscribe${capitalizedAttributeName}Attribute not found on Cluster ${clusterServer.name} on endpoint ${endpoint.name}:${endpoint.number}`);
762
+ return false;
763
+ }
764
+ // Subscribe to the attribute
765
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-object-type
766
+ const subscribe = clusterServer[`subscribe${capitalizedAttributeName}Attribute`];
767
+ subscribe(listener);
768
+ log?.info(`${db}Subscribe endpoint ${or}${endpoint.name}:${endpoint.number}${db} attribute ${hk}${clusterServer.name}.${capitalizedAttributeName}${db}`);
769
+ return true;
770
+ }
771
+ function addCommandHandler(clusterId, command, handler, log, endpoint) {
772
+ // if (!endpoint) endpoint = this as Endpoint;
773
+ if (!endpoint)
774
+ return false;
775
+ const clusterServer = endpoint.getClusterServerById(clusterId);
776
+ if (!clusterServer) {
777
+ log?.error(`addCommandHandler error: Cluster ${clusterId} not found on endpoint ${endpoint.name}:${endpoint.number}`);
778
+ return false;
779
+ }
780
+ if (!clusterServer.isCommandSupportedByName(command)) {
781
+ if (log)
782
+ log.error(`addCommandHandler error: Command ${command} not found on Cluster ${clusterServer.name} on endpoint ${endpoint.name}:${endpoint.number}`);
783
+ return false;
784
+ }
785
+ // Find the command
786
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
787
+ const commands = clusterServer.commands;
788
+ for (const [key, value] of Object.entries(commands)) {
789
+ // console.log(`Key "${key}": ${debugStringify(value)}`);
790
+ if (key === command) {
791
+ value.handler = handler;
792
+ log?.info(`${db}Command handler added for endpoint ${or}${endpoint.name}:${endpoint.number}${db} ${hk}${clusterServer.name}.${command}${db}`);
793
+ return true;
794
+ }
795
+ }
796
+ log?.error(`Command handler not found for endpoint ${or}${endpoint.name}:${endpoint.number}${er} ${hk}${clusterServer.name}.${command}${er}`);
797
+ return false;
798
+ }
310
799
  //# sourceMappingURL=platform.js.map