matterbridge-example-dynamic-platform 1.1.9 → 1.2.0-edge.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.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,30 @@ If you like this project and find it useful, please consider giving it a star on
8
8
  <img src="bmc-button.svg" alt="Buy me a coffee" width="120">
9
9
  </a>
10
10
 
11
+ ## [1.2.0] - 2025-04-??
12
+
13
+ ### Added
14
+
15
+ - [platform]: Added OnOff Mounted Switch device (supported by SmartThings, Alexa, Home Assistant).
16
+ - [platform]: Added Dimmer Mounted Switch device (supported by SmartThings, Alexa, Home Assistant).
17
+ - [platform]: Added Robot Vacuum Cleaner device (supported by SmartThings, Alexa, Home Assistant and Apple Home).
18
+ - [platform]: Added Laundry Washer device (supported by SmartThings, Alexa and Home Assistant).
19
+ - [platform]: Added Laundry Dryer device (supported by SmartThings, Alexa and Home Assistant).
20
+ - [platform]: Added Dishwasher device (supported by SmartThings, Alexa and Home Assistant).
21
+ - [platform]: Added Refrigerator device (supported by SmartThings, Alexa and Home Assistant).
22
+ - [platform]: Added Oven device (supported by SmartThings, Alexa and Home Assistant).
23
+ - [platform]: Added Microwave Oven device (supported by SmartThings, Alexa and Home Assistant).
24
+ - [platform]: Added Extractor Hood device (supported by SmartThings, Alexa and Home Assistant).
25
+ - [platform]: Added Cooktop device (supported by SmartThings, Alexa and Home Assistant).
26
+
27
+ ### Changed
28
+
29
+ - [package]: Updated dependencies.
30
+
31
+ <a href="https://www.buymeacoffee.com/luligugithub">
32
+ <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
33
+ </a>
34
+
11
35
  ## [1.1.9] - 2025-04-07
12
36
 
13
37
  ### Added
@@ -0,0 +1,530 @@
1
+ import { MatterbridgeEndpoint, MatterbridgeServer, MatterbridgeOnOffServer, RefrigeratorTag, PositionTag, laundryWasher, laundryDryer, dishwasher, refrigerator, temperatureControlledCabinetCooler, oven, temperatureControlledCabinetHeater, microwaveOven, extractorHood, cooktop, cookSurface, } from 'matterbridge';
2
+ import { ClusterBehavior } from 'matterbridge/matter';
3
+ import { OperationalState, TemperatureControl, DishwasherMode, LaundryWasherControls, LaundryWasherMode, LaundryDryerControls, OvenMode, ModeBase, RefrigeratorAndTemperatureControlledCabinetMode, MicrowaveOvenMode, MicrowaveOvenControl, OvenCavityOperationalState, } from 'matterbridge/matter/clusters';
4
+ import { DishwasherAlarmServer, LaundryDryerControlsServer, LaundryWasherControlsServer, MicrowaveOvenControlBehavior, MicrowaveOvenModeServer, OperationalStateBehavior, TemperatureControlBehavior, } from 'matterbridge/matter/behaviors';
5
+ export class Appliances extends MatterbridgeEndpoint {
6
+ constructor(deviceType, name, serial) {
7
+ super(deviceType, { uniqueStorageKey: `${name}-${serial}` }, true);
8
+ if (deviceType.code === laundryWasher.code) {
9
+ this.createDefaultIdentifyClusterServer();
10
+ this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Laundry Washer');
11
+ this.createDeadFrontOnOffClusterServer();
12
+ this.createLevelTemperatureControlClusterServer(3, ['Cold', '30°', '40°', '60°', '80°']);
13
+ this.createDefaultLaundryWasherModeClusterServer();
14
+ this.createSpinLaundryWasherControlsClusterServer(3, ['400', '800', '1200', '1600']);
15
+ this.createDefaultOperationalStateClusterServer(OperationalState.OperationalStateEnum.Stopped);
16
+ }
17
+ else if (deviceType.code === laundryDryer.code) {
18
+ this.createDefaultIdentifyClusterServer();
19
+ this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Laundry Dryer');
20
+ this.createDeadFrontOnOffClusterServer();
21
+ this.createLevelTemperatureControlClusterServer(3, ['Cold', '30°', '40°', '60°', '80°']);
22
+ this.createDefaultLaundryWasherModeClusterServer();
23
+ this.createDefaultLaundryDryerControlsClusterServer(1);
24
+ this.createDefaultOperationalStateClusterServer(OperationalState.OperationalStateEnum.Stopped);
25
+ }
26
+ else if (deviceType.code === dishwasher.code) {
27
+ this.createDefaultIdentifyClusterServer();
28
+ this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Dishwasher');
29
+ this.createDeadFrontOnOffClusterServer();
30
+ this.createLevelTemperatureControlClusterServer(1, ['Cold', '30°', '40°', '60°']);
31
+ this.createDefaultDishwasherModeClusterServer();
32
+ this.createDefaultDishwasherAlarmClusterServer();
33
+ this.createDefaultOperationalStateClusterServer(OperationalState.OperationalStateEnum.Stopped);
34
+ }
35
+ else if (deviceType.name === refrigerator.name) {
36
+ this.createDefaultIdentifyClusterServer();
37
+ this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Refrigerator');
38
+ const refrigerator = this.addChildDeviceType('Refrigerator', temperatureControlledCabinetCooler, { tagList: [{ mfgCode: null, namespaceId: RefrigeratorTag.Refrigerator.namespaceId, tag: RefrigeratorTag.Refrigerator.tag, label: RefrigeratorTag.Refrigerator.label }] }, true);
39
+ refrigerator.log.logName = `Refrigerator (cabinet Refrigerator)`;
40
+ refrigerator.createDefaultIdentifyClusterServer();
41
+ Appliances.createLevelTemperatureControlClusterServer(refrigerator, 2, ['Level 1', 'Level 2', 'Level 3', 'Level 4', 'Level 5']);
42
+ refrigerator.createDefaultTemperatureMeasurementClusterServer(1000);
43
+ Appliances.createDefaultRefrigeratorAndTemperatureControlledCabinetModeClusterServer(refrigerator, 1);
44
+ const freezer = this.addChildDeviceType('Freezer', temperatureControlledCabinetCooler, { tagList: [{ mfgCode: null, namespaceId: RefrigeratorTag.Freezer.namespaceId, tag: RefrigeratorTag.Freezer.tag, label: RefrigeratorTag.Freezer.label }] }, true);
45
+ freezer.log.logName = `Refrigerator (cabinet Freezer)`;
46
+ freezer.createDefaultIdentifyClusterServer();
47
+ Appliances.createLevelTemperatureControlClusterServer(freezer, 2, ['Level 1', 'Level 2', 'Level 3', 'Level 4', 'Level 5']);
48
+ freezer.createDefaultTemperatureMeasurementClusterServer(-2000);
49
+ Appliances.createDefaultRefrigeratorAndTemperatureControlledCabinetModeClusterServer(freezer, 1);
50
+ }
51
+ else if (deviceType.name === oven.name) {
52
+ this.createDefaultIdentifyClusterServer();
53
+ this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Oven');
54
+ const cabinettop = this.addChildDeviceType('Oven (top)', temperatureControlledCabinetHeater, { tagList: [{ mfgCode: null, namespaceId: PositionTag.Top.namespaceId, tag: PositionTag.Top.tag, label: PositionTag.Top.label }] }, true);
55
+ cabinettop.log.logName = `Oven (top)`;
56
+ cabinettop.createDefaultIdentifyClusterServer();
57
+ Appliances.createLevelTemperatureControlClusterServer(cabinettop, 2, ['Defrost', '180°', '200°', '250°', '300°']);
58
+ cabinettop.createDefaultTemperatureMeasurementClusterServer(20000);
59
+ Appliances.createDefaultOvenModeClusterServer(cabinettop, 1);
60
+ const cabinetbottom = this.addChildDeviceType('Oven (bottom)', temperatureControlledCabinetHeater, { tagList: [{ mfgCode: null, namespaceId: PositionTag.Bottom.namespaceId, tag: PositionTag.Bottom.tag, label: PositionTag.Bottom.label }] }, true);
61
+ cabinetbottom.log.logName = `Oven (bottom)`;
62
+ cabinetbottom.createDefaultIdentifyClusterServer();
63
+ Appliances.createLevelTemperatureControlClusterServer(cabinetbottom, 2, ['Defrost', '180°', '200°', '250°', '300°']);
64
+ cabinetbottom.createDefaultTemperatureMeasurementClusterServer(30000);
65
+ Appliances.createDefaultOvenModeClusterServer(cabinetbottom, 1);
66
+ }
67
+ else if (deviceType.name === microwaveOven.name) {
68
+ this.createDefaultIdentifyClusterServer();
69
+ this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Microwave Oven');
70
+ this.createDefaultOperationalStateClusterServer(OperationalState.OperationalStateEnum.Stopped);
71
+ this.createDefaultMicrowaveOvenModeClusterServer();
72
+ this.createDefaultMicrowaveOvenControlClusterServer();
73
+ }
74
+ else if (deviceType.name === extractorHood.name) {
75
+ this.createDefaultIdentifyClusterServer();
76
+ this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Extractor Hood');
77
+ this.createBaseFanControlClusterServer();
78
+ this.createDefaultHepaFilterMonitoringClusterServer();
79
+ this.createDefaultActivatedCarbonFilterMonitoringClusterServer();
80
+ }
81
+ else if (deviceType.name === cooktop.name) {
82
+ this.createDefaultIdentifyClusterServer();
83
+ this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Cooktop');
84
+ this.createOffOnlyOnOffClusterServer(true);
85
+ const cookSurface1 = this.addChildDeviceType('Surface 1', cookSurface, { tagList: [{ mfgCode: null, namespaceId: PositionTag.Left.namespaceId, tag: PositionTag.Left.tag, label: PositionTag.Left.label }] }, true);
86
+ cookSurface1.log.logName = `Cook surface (right)`;
87
+ cookSurface1.createDefaultIdentifyClusterServer();
88
+ Appliances.createLevelTemperatureControlClusterServer(cookSurface1, 2, ['Level 1', 'Level 2', 'Level 3', 'Level 4', 'Level 5']);
89
+ cookSurface1.createDefaultTemperatureMeasurementClusterServer(10000);
90
+ cookSurface1.createOffOnlyOnOffClusterServer(true);
91
+ const cookSurface2 = this.addChildDeviceType('Surface 2', cookSurface, { tagList: [{ mfgCode: null, namespaceId: PositionTag.Right.namespaceId, tag: PositionTag.Right.tag, label: PositionTag.Right.label }] }, true);
92
+ cookSurface2.log.logName = `Cook surface (left)`;
93
+ cookSurface2.createDefaultIdentifyClusterServer();
94
+ Appliances.createLevelTemperatureControlClusterServer(cookSurface2, 3, ['Level 1', 'Level 2', 'Level 3', 'Level 4', 'Level 5']);
95
+ cookSurface2.createDefaultTemperatureMeasurementClusterServer(12000);
96
+ cookSurface2.createOffOnlyOnOffClusterServer(true);
97
+ this.eventsOf(MatterbridgeOnOffServer).onOff$Changed.on(async (value) => {
98
+ if (!value) {
99
+ this.log.notice('Turning off all cook surfaces');
100
+ await cookSurface1.setStateOf(MatterbridgeOnOffServer, { onOff: false });
101
+ await cookSurface2.setStateOf(MatterbridgeOnOffServer, { onOff: false });
102
+ }
103
+ });
104
+ }
105
+ }
106
+ createDefaultOperationalStateClusterServer(operationalState = OperationalState.OperationalStateEnum.Stopped) {
107
+ this.behaviors.require(MatterbridgeOperationalStateServer, {
108
+ phaseList: [],
109
+ currentPhase: null,
110
+ operationalStateList: [
111
+ { operationalStateId: OperationalState.OperationalStateEnum.Stopped, operationalStateLabel: 'Stopped' },
112
+ { operationalStateId: OperationalState.OperationalStateEnum.Running, operationalStateLabel: 'Running' },
113
+ { operationalStateId: OperationalState.OperationalStateEnum.Paused, operationalStateLabel: 'Paused' },
114
+ { operationalStateId: OperationalState.OperationalStateEnum.Error, operationalStateLabel: 'Error' },
115
+ ],
116
+ operationalState,
117
+ operationalError: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
118
+ });
119
+ return this;
120
+ }
121
+ createDefaultOvenCavityOperationalStateClusterServer(operationalState = OperationalState.OperationalStateEnum.Stopped) {
122
+ this.behaviors.require(OvenCavityOperationalStateServer, {
123
+ phaseList: [],
124
+ currentPhase: null,
125
+ operationalStateList: [
126
+ { operationalStateId: OperationalState.OperationalStateEnum.Stopped, operationalStateLabel: 'Stopped' },
127
+ { operationalStateId: OperationalState.OperationalStateEnum.Running, operationalStateLabel: 'Running' },
128
+ { operationalStateId: OperationalState.OperationalStateEnum.Error, operationalStateLabel: 'Error' },
129
+ ],
130
+ operationalState,
131
+ operationalError: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
132
+ });
133
+ return this;
134
+ }
135
+ static createDefaultRefrigeratorAndTemperatureControlledCabinetModeClusterServer(endpoint, currentMode) {
136
+ endpoint.behaviors.require(RefrigeratorAndTemperatureControlledCabinetModeServer, {
137
+ supportedModes: [
138
+ { label: 'Auto', mode: 0, modeTags: [{ value: RefrigeratorAndTemperatureControlledCabinetMode.ModeTag.Auto }] },
139
+ { label: 'RapidCool', mode: 1, modeTags: [{ value: RefrigeratorAndTemperatureControlledCabinetMode.ModeTag.RapidCool }] },
140
+ { label: 'RapidFreeze', mode: 2, modeTags: [{ value: RefrigeratorAndTemperatureControlledCabinetMode.ModeTag.RapidFreeze }] },
141
+ ],
142
+ currentMode,
143
+ });
144
+ return endpoint;
145
+ }
146
+ static createDefaultOvenModeClusterServer(endpoint, currentMode) {
147
+ endpoint.behaviors.require(OvenModeServer, {
148
+ supportedModes: [
149
+ { label: 'Bake', mode: 1, modeTags: [{ value: OvenMode.ModeTag.Bake }] },
150
+ { label: 'Convection', mode: 2, modeTags: [{ value: OvenMode.ModeTag.Convection }] },
151
+ { label: 'Grill', mode: 3, modeTags: [{ value: OvenMode.ModeTag.Grill }] },
152
+ { label: 'Roast', mode: 4, modeTags: [{ value: OvenMode.ModeTag.Roast }] },
153
+ { label: 'Clean', mode: 5, modeTags: [{ value: OvenMode.ModeTag.Clean }] },
154
+ { label: 'Convection Bake', mode: 6, modeTags: [{ value: OvenMode.ModeTag.ConvectionBake }] },
155
+ { label: 'Convection Roast', mode: 7, modeTags: [{ value: OvenMode.ModeTag.ConvectionRoast }] },
156
+ { label: 'Warming', mode: 8, modeTags: [{ value: OvenMode.ModeTag.Warming }] },
157
+ { label: 'Proofing', mode: 9, modeTags: [{ value: OvenMode.ModeTag.Proofing }] },
158
+ { label: 'Steam', mode: 10, modeTags: [{ value: OvenMode.ModeTag.Steam }] },
159
+ ],
160
+ currentMode,
161
+ });
162
+ return endpoint;
163
+ }
164
+ createDefaultDishwasherModeClusterServer(currentMode) {
165
+ this.behaviors.require(DishwasherModeServer, {
166
+ supportedModes: [
167
+ { label: 'Light', mode: 1, modeTags: [{ value: DishwasherMode.ModeTag.Light }] },
168
+ { label: 'Normal', mode: 2, modeTags: [{ value: DishwasherMode.ModeTag.Normal }] },
169
+ { label: 'Heavy', mode: 3, modeTags: [{ value: DishwasherMode.ModeTag.Heavy }] },
170
+ ],
171
+ currentMode,
172
+ });
173
+ return this;
174
+ }
175
+ createDefaultLaundryWasherModeClusterServer(currentMode) {
176
+ this.behaviors.require(LaundryWasherModeServer, {
177
+ supportedModes: [
178
+ { label: 'Delicate', mode: 1, modeTags: [{ value: LaundryWasherMode.ModeTag.Delicate }] },
179
+ { label: 'Normal', mode: 2, modeTags: [{ value: LaundryWasherMode.ModeTag.Normal }] },
180
+ { label: 'Heavy', mode: 3, modeTags: [{ value: LaundryWasherMode.ModeTag.Heavy }] },
181
+ { label: 'Whites', mode: 4, modeTags: [{ value: LaundryWasherMode.ModeTag.Whites }] },
182
+ ],
183
+ currentMode,
184
+ });
185
+ return this;
186
+ }
187
+ createDefaultMicrowaveOvenModeClusterServer(currentMode, supportedModes) {
188
+ this.behaviors.require(MicrowaveOvenModeServer, {
189
+ supportedModes: supportedModes ?? [
190
+ { label: 'Auto', mode: 1, modeTags: [{ value: MicrowaveOvenMode.ModeTag.Auto }] },
191
+ { label: 'Quick', mode: 2, modeTags: [{ value: MicrowaveOvenMode.ModeTag.Quick }] },
192
+ { label: 'Quiet', mode: 3, modeTags: [{ value: MicrowaveOvenMode.ModeTag.Quiet }] },
193
+ { label: 'Min', mode: 4, modeTags: [{ value: MicrowaveOvenMode.ModeTag.Min }] },
194
+ { label: 'Max', mode: 5, modeTags: [{ value: MicrowaveOvenMode.ModeTag.Max }] },
195
+ { label: 'Normal', mode: 6, modeTags: [{ value: MicrowaveOvenMode.ModeTag.Normal }] },
196
+ { label: 'Defrost', mode: 7, modeTags: [{ value: MicrowaveOvenMode.ModeTag.Defrost }] },
197
+ ],
198
+ currentMode: currentMode ?? 1,
199
+ });
200
+ return this;
201
+ }
202
+ createDefaultMicrowaveOvenControlClusterServer(selectedWattIndex = 5, supportedWatts = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000], cookTime = 60, maxCookTime = 3600) {
203
+ this.behaviors.require(MatterbridgeMicrowaveOvenControlServer.with(MicrowaveOvenControl.Feature.PowerInWatts), {
204
+ supportedWatts,
205
+ selectedWattIndex,
206
+ cookTime,
207
+ maxCookTime,
208
+ });
209
+ return this;
210
+ }
211
+ createSpinLaundryWasherControlsClusterServer(spinSpeedCurrent, spinSpeeds) {
212
+ this.behaviors.require(LaundryWasherControlsServer.with(LaundryWasherControls.Feature.Spin), {
213
+ spinSpeeds: spinSpeeds ?? ['400', '800', '1200', '1600'],
214
+ spinSpeedCurrent,
215
+ });
216
+ return this;
217
+ }
218
+ createRinseLaundryWasherControlsClusterServer(numberOfRinses, supportedRinses) {
219
+ this.behaviors.require(LaundryWasherControlsServer.with(LaundryWasherControls.Feature.Rinse), {
220
+ supportedRinses: supportedRinses ?? [
221
+ LaundryWasherControls.NumberOfRinses.None,
222
+ LaundryWasherControls.NumberOfRinses.Normal,
223
+ LaundryWasherControls.NumberOfRinses.Extra,
224
+ LaundryWasherControls.NumberOfRinses.Max,
225
+ ],
226
+ numberOfRinses,
227
+ });
228
+ return this;
229
+ }
230
+ createDefaultLaundryDryerControlsClusterServer(selectedDrynessLevel, supportedDrynessLevels) {
231
+ this.behaviors.require(LaundryDryerControlsServer, {
232
+ supportedDrynessLevels: supportedDrynessLevels ?? [
233
+ LaundryDryerControls.DrynessLevel.Low,
234
+ LaundryDryerControls.DrynessLevel.Normal,
235
+ LaundryDryerControls.DrynessLevel.Extra,
236
+ LaundryDryerControls.DrynessLevel.Max,
237
+ ],
238
+ selectedDrynessLevel,
239
+ });
240
+ return this;
241
+ }
242
+ createDefaultDishwasherAlarmClusterServer() {
243
+ this.behaviors.require(DishwasherAlarmServer, {
244
+ mask: { inflowError: true, drainError: true, doorError: true, tempTooLow: true, tempTooHigh: true, waterLevelError: true },
245
+ state: { inflowError: false, drainError: false, doorError: false, tempTooLow: false, tempTooHigh: false, waterLevelError: false },
246
+ supported: { inflowError: true, drainError: true, doorError: true, tempTooLow: true, tempTooHigh: true, waterLevelError: true },
247
+ });
248
+ return this;
249
+ }
250
+ createLevelTemperatureControlClusterServer(selectedTemperatureLevel = 1, supportedTemperatureLevels = ['Cold', 'Warm', 'Hot']) {
251
+ this.behaviors.require(MatterbridgeLevelTemperatureControlServer.with(TemperatureControl.Feature.TemperatureLevel), {
252
+ selectedTemperatureLevel,
253
+ supportedTemperatureLevels,
254
+ });
255
+ return this;
256
+ }
257
+ static createLevelTemperatureControlClusterServer(endpoint, selectedTemperatureLevel = 1, supportedTemperatureLevels = ['Cold', 'Warm', 'Hot']) {
258
+ endpoint.behaviors.require(MatterbridgeLevelTemperatureControlServer.with(TemperatureControl.Feature.TemperatureLevel), {
259
+ selectedTemperatureLevel,
260
+ supportedTemperatureLevels,
261
+ });
262
+ return endpoint;
263
+ }
264
+ createNumberTemperatureControlClusterServer(temperatureSetpoint, minTemperature, maxTemperature, step = 1) {
265
+ this.behaviors.require(MatterbridgeNumberTemperatureControlServer.with(TemperatureControl.Feature.TemperatureNumber, TemperatureControl.Feature.TemperatureStep), {
266
+ temperatureSetpoint,
267
+ minTemperature,
268
+ maxTemperature,
269
+ step,
270
+ });
271
+ return this;
272
+ }
273
+ static createNumberTemperatureControlClusterServer(endpoint, temperatureSetpoint, minTemperature, maxTemperature, step = 1) {
274
+ endpoint.behaviors.require(MatterbridgeNumberTemperatureControlServer.with(TemperatureControl.Feature.TemperatureNumber, TemperatureControl.Feature.TemperatureStep), {
275
+ temperatureSetpoint,
276
+ minTemperature,
277
+ maxTemperature,
278
+ step,
279
+ });
280
+ return endpoint;
281
+ }
282
+ }
283
+ class MatterbridgeOperationalStateServer extends OperationalStateBehavior {
284
+ initialize() {
285
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
286
+ device.log.info('MatterbridgeOperationalStateServer initialized: setting operational state to Stopped');
287
+ this.state.operationalState = OperationalState.OperationalStateEnum.Stopped;
288
+ this.state.operationalError = { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' };
289
+ }
290
+ pause() {
291
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
292
+ device.log.info('MatterbridgeOperationalStateServer: pause called setting operational state to Paused');
293
+ this.state.operationalState = OperationalState.OperationalStateEnum.Paused;
294
+ this.state.operationalError = { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' };
295
+ return {
296
+ commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
297
+ };
298
+ }
299
+ stop() {
300
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
301
+ device.log.info('MatterbridgeOperationalStateServer: stop called setting operational state to Stopped');
302
+ this.state.operationalState = OperationalState.OperationalStateEnum.Stopped;
303
+ this.state.operationalError = { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' };
304
+ return {
305
+ commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
306
+ };
307
+ }
308
+ start() {
309
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
310
+ device.log.info('MatterbridgeOperationalStateServer: start called setting operational state to Running');
311
+ this.state.operationalState = OperationalState.OperationalStateEnum.Running;
312
+ this.state.operationalError = { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' };
313
+ return {
314
+ commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
315
+ };
316
+ }
317
+ resume() {
318
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
319
+ device.log.info('MatterbridgeOperationalStateServer: resume called setting operational state to Running');
320
+ this.state.operationalState = OperationalState.OperationalStateEnum.Running;
321
+ this.state.operationalError = { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' };
322
+ return {
323
+ commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
324
+ };
325
+ }
326
+ }
327
+ class MatterbridgeLevelTemperatureControlServer extends TemperatureControlBehavior.with(TemperatureControl.Feature.TemperatureLevel) {
328
+ initialize() {
329
+ if (this.state.supportedTemperatureLevels.length >= 2) {
330
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
331
+ device.log.info('MatterbridgeLevelTemperatureControlServer initialized');
332
+ }
333
+ }
334
+ setTemperature(request) {
335
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
336
+ if (request.targetTemperatureLevel !== undefined && request.targetTemperatureLevel >= 0 && request.targetTemperatureLevel < this.state.supportedTemperatureLevels.length) {
337
+ device.log.info(`MatterbridgeLevelTemperatureControlServer: setTemperature called setting selectedTemperatureLevel to ${request.targetTemperatureLevel}: ${this.state.supportedTemperatureLevels[request.targetTemperatureLevel]}`);
338
+ this.state.selectedTemperatureLevel = request.targetTemperatureLevel;
339
+ }
340
+ else {
341
+ device.log.error(`MatterbridgeLevelTemperatureControlServer: setTemperature called with invalid targetTemperatureLevel ${request.targetTemperatureLevel}`);
342
+ }
343
+ }
344
+ }
345
+ class MatterbridgeNumberTemperatureControlServer extends TemperatureControlBehavior.with(TemperatureControl.Feature.TemperatureNumber) {
346
+ initialize() {
347
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
348
+ device.log.info('MatterbridgeNumberTemperatureControlServer initialized');
349
+ }
350
+ setTemperature(request) {
351
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
352
+ if (request.targetTemperature !== undefined && request.targetTemperature >= this.state.minTemperature && request.targetTemperature <= this.state.maxTemperature) {
353
+ device.log.info(`MatterbridgeNumberTemperatureControlServer: setTemperature called setting temperatureSetpoint to ${request.targetTemperature}`);
354
+ this.state.temperatureSetpoint = request.targetTemperature;
355
+ }
356
+ else {
357
+ device.log.error(`MatterbridgeNumberTemperatureControlServer: setTemperature called with invalid targetTemperature ${request.targetTemperature}`);
358
+ }
359
+ }
360
+ }
361
+ class MatterbridgeMicrowaveOvenControlServer extends MicrowaveOvenControlBehavior.with(MicrowaveOvenControl.Feature.PowerInWatts) {
362
+ initialize() {
363
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
364
+ device.log.info('MatterbridgeMicrowaveOvenControlServer initialized');
365
+ }
366
+ setCookingParameters(request) {
367
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
368
+ if (request.cookMode !== undefined) {
369
+ device.log.info(`MatterbridgeMicrowaveOvenControlServer: setCookingParameters called setting cookMode to ${request.cookMode}`);
370
+ this.endpoint.setStateOf(MicrowaveOvenModeServer, { currentMode: request.cookMode });
371
+ }
372
+ else {
373
+ device.log.info(`MatterbridgeMicrowaveOvenControlServer: setCookingParameters called with no cookMode so set to Normal`);
374
+ this.endpoint.setStateOf(MicrowaveOvenModeServer, { currentMode: 6 });
375
+ }
376
+ if (request.cookTime !== undefined && request.cookTime >= 0 && request.cookTime <= this.state.maxCookTime) {
377
+ device.log.info(`MatterbridgeMicrowaveOvenControlServer: setCookingParameters called setting cookTime to ${request.cookTime}`);
378
+ this.state.cookTime = request.cookTime;
379
+ }
380
+ else {
381
+ device.log.info(`MatterbridgeMicrowaveOvenControlServer: setCookingParameters called with no cookTime so set to 30sec.`);
382
+ this.state.cookTime = 30;
383
+ }
384
+ if (request.wattSettingIndex !== undefined && request.wattSettingIndex >= 0 && request.wattSettingIndex < this.state.supportedWatts.length) {
385
+ device.log.info(`MatterbridgeMicrowaveOvenControlServer: setCookingParameters called setting selectedWattIndex to ${request.wattSettingIndex}`);
386
+ this.state.selectedWattIndex = request.wattSettingIndex;
387
+ }
388
+ else {
389
+ device.log.info(`MatterbridgeMicrowaveOvenControlServer: setCookingParameters called with no wattSettingIndex so set to the highest Watt setting for the selected CookMode`);
390
+ this.state.selectedWattIndex = this.state.supportedWatts.length - 1;
391
+ }
392
+ }
393
+ addMoreTime(request) {
394
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
395
+ if (request.timeToAdd !== undefined && request.timeToAdd >= 0) {
396
+ device.log.info(`MatterbridgeMicrowaveOvenControlServer: addMoreTime called setting cookTime to ${this.state.cookTime + request.timeToAdd}`);
397
+ this.state.cookTime += request.timeToAdd;
398
+ }
399
+ else {
400
+ device.log.error(`MatterbridgeMicrowaveOvenControlServer: addMoreTime called with invalid cookTime ${request.timeToAdd}`);
401
+ }
402
+ }
403
+ }
404
+ export const OvenCavityOperationalStateBehavior = ClusterBehavior.withInterface().for(OvenCavityOperationalState.Cluster);
405
+ export class OvenCavityOperationalStateServer extends OvenCavityOperationalStateBehavior {
406
+ initialize() {
407
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
408
+ device.log.info('OvenCavityOperationalStateServer initialized: setting operational state to Stopped and operational error to No error');
409
+ this.state.operationalState = OperationalState.OperationalStateEnum.Stopped;
410
+ this.state.operationalError = { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' };
411
+ }
412
+ stop() {
413
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
414
+ device.log.info('OvenCavityOperationalStateServer: stop called setting operational state to Stopped and operational error to No error');
415
+ this.state.operationalState = OperationalState.OperationalStateEnum.Stopped;
416
+ this.state.operationalError = { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' };
417
+ return {
418
+ commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
419
+ };
420
+ }
421
+ start() {
422
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
423
+ device.log.info('OvenCavityOperationalStateServer: start called setting operational state to Running and operational error to No error');
424
+ this.state.operationalState = OperationalState.OperationalStateEnum.Running;
425
+ this.state.operationalError = { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' };
426
+ return {
427
+ commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
428
+ };
429
+ }
430
+ }
431
+ export const RefrigeratorAndTemperatureControlledCabinetModeBehavior = ClusterBehavior.withInterface().for(RefrigeratorAndTemperatureControlledCabinetMode.Cluster);
432
+ class RefrigeratorAndTemperatureControlledCabinetModeServer extends RefrigeratorAndTemperatureControlledCabinetModeBehavior {
433
+ initialize() {
434
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
435
+ device.log.info('MatterbridgeRefrigeratorAndTemperatureControlledCabinetModeServer initialized: setting currentMode to 1');
436
+ this.state.currentMode = 1;
437
+ }
438
+ changeToMode(request) {
439
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
440
+ const supportedMode = this.state.supportedModes.find((supportedMode) => supportedMode.mode === request.newMode);
441
+ if (supportedMode) {
442
+ device.log.info(`MatterbridgeRefrigeratorAndTemperatureControlledCabinetModeServer: changeToMode called with mode ${supportedMode.mode} = ${supportedMode.label}`);
443
+ this.state.currentMode = request.newMode;
444
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
445
+ }
446
+ else {
447
+ device.log.info(`MatterbridgeRefrigeratorAndTemperatureControlledCabinetModeServer: changeToMode called with invalid mode ${request.newMode}`);
448
+ return { status: ModeBase.ModeChangeStatus.InvalidInMode, statusText: 'Invalid mode' };
449
+ }
450
+ }
451
+ }
452
+ export const OvenModeBehavior = ClusterBehavior.withInterface().for(OvenMode.Cluster);
453
+ class OvenModeServer extends OvenModeBehavior {
454
+ initialize() {
455
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
456
+ device.log.info('OvenModeServer initialized: setting currentMode to 3');
457
+ this.state.currentMode = 3;
458
+ }
459
+ changeToMode(request) {
460
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
461
+ const supportedMode = this.state.supportedModes.find((supportedMode) => supportedMode.mode === request.newMode);
462
+ if (supportedMode) {
463
+ device.log.info(`OvenModeServer: changeToMode called with mode ${supportedMode.mode} = ${supportedMode.label}`);
464
+ this.state.currentMode = request.newMode;
465
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
466
+ }
467
+ else {
468
+ device.log.info(`OvenModeServer: changeToMode called with invalid mode ${request.newMode}`);
469
+ return { status: ModeBase.ModeChangeStatus.InvalidInMode, statusText: 'Invalid mode' };
470
+ }
471
+ }
472
+ }
473
+ export const DishwasherModeBehavior = ClusterBehavior.withInterface().for(DishwasherMode.Cluster);
474
+ class DishwasherModeServer extends DishwasherModeBehavior {
475
+ initialize() {
476
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
477
+ device.log.info('DishwasherModeServer initialized: setting currentMode to 3');
478
+ this.state.currentMode = 2;
479
+ this.reactTo(this.agent.get(MatterbridgeOnOffServer).events.onOff$Changed, this.handleOnOffChange);
480
+ }
481
+ handleOnOffChange(onOff) {
482
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
483
+ if (onOff === false) {
484
+ device.log.info('***OnOffServer changed to OFF: setting Dead Front state to Manufacturer Specific');
485
+ this.state.currentMode = 2;
486
+ }
487
+ }
488
+ changeToMode(request) {
489
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
490
+ const supportedMode = this.state.supportedModes.find((supportedMode) => supportedMode.mode === request.newMode);
491
+ if (supportedMode) {
492
+ device.log.info(`DishwasherModeServer: changeToMode called with mode ${supportedMode.mode} = ${supportedMode.label}`);
493
+ this.state.currentMode = request.newMode;
494
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
495
+ }
496
+ else {
497
+ device.log.error(`DishwasherModeServer: changeToMode called with invalid mode ${request.newMode}`);
498
+ return { status: ModeBase.ModeChangeStatus.InvalidInMode, statusText: 'Invalid mode' };
499
+ }
500
+ }
501
+ }
502
+ export const LaundryWasherModeBehavior = ClusterBehavior.withInterface().for(LaundryWasherMode.Cluster);
503
+ class LaundryWasherModeServer extends LaundryWasherModeBehavior {
504
+ initialize() {
505
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
506
+ device.log.info('LaundryWasherModeServer initialized: setting currentMode to 3');
507
+ this.state.currentMode = 2;
508
+ this.reactTo(this.agent.get(MatterbridgeOnOffServer).events.onOff$Changed, this.handleOnOffChange);
509
+ }
510
+ handleOnOffChange(onOff) {
511
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
512
+ if (onOff === false) {
513
+ device.log.notice('OnOffServer changed to OFF: setting Dead Front state to Manufacturer Specific');
514
+ this.state.currentMode = 2;
515
+ }
516
+ }
517
+ changeToMode(request) {
518
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
519
+ const supportedMode = this.state.supportedModes.find((supportedMode) => supportedMode.mode === request.newMode);
520
+ if (supportedMode) {
521
+ device.log.info(`LaundryWasherModeServer: changeToMode called with mode ${supportedMode.mode} = ${supportedMode.label}`);
522
+ this.state.currentMode = request.newMode;
523
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
524
+ }
525
+ else {
526
+ device.log.error(`LaundryWasherModeServer: changeToMode called with invalid mode ${request.newMode}`);
527
+ return { status: ModeBase.ModeChangeStatus.InvalidInMode, statusText: 'Invalid mode' };
528
+ }
529
+ }
530
+ }
package/dist/platform.js CHANGED
@@ -1,7 +1,9 @@
1
- import { MatterbridgeEndpoint, MatterbridgeDynamicPlatform, airConditioner, airQualitySensor, bridgedNode, colorTemperatureLight, coverDevice, dimmableLight, doorLockDevice, fanDevice, flowSensor, humiditySensor, onOffLight, onOffOutlet, onOffSwitch, powerSource, rainSensor, smokeCoAlarm, temperatureSensor, thermostatDevice, waterFreezeDetector, waterLeakDetector, airPurifier, pumpDevice, waterValve, genericSwitch, } from 'matterbridge';
1
+ import { MatterbridgeEndpoint, MatterbridgeDynamicPlatform, airQualitySensor, bridgedNode, colorTemperatureLight, coverDevice, dimmableLight, doorLockDevice, fanDevice, flowSensor, humiditySensor, onOffLight, onOffOutlet, onOffSwitch, powerSource, rainSensor, smokeCoAlarm, temperatureSensor, thermostatDevice, waterFreezeDetector, waterLeakDetector, airPurifier, pumpDevice, waterValve, genericSwitch, airConditioner, laundryWasher, cooktop, extractorHood, microwaveOven, oven, refrigerator, dishwasher, laundryDryer, onOffMountedSwitch, dimmableMountedSwitch, extendedColorLight, } from 'matterbridge';
2
2
  import { isValidBoolean, isValidNumber } from 'matterbridge/utils';
3
3
  import { LocationTag } from 'matterbridge/matter';
4
4
  import { PowerSource, BooleanState, OnOff, LevelControl, AirQuality, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, FlowMeasurement, ColorControl, DoorLock, FanControl, FormaldehydeConcentrationMeasurement, NitrogenDioxideConcentrationMeasurement, OzoneConcentrationMeasurement, Pm10ConcentrationMeasurement, Pm1ConcentrationMeasurement, Pm25ConcentrationMeasurement, RadonConcentrationMeasurement, RelativeHumidityMeasurement, RelativeHumidityMeasurementCluster, SmokeCoAlarm, TemperatureMeasurement, Thermostat, ThermostatCluster, TotalVolatileOrganicCompoundsConcentrationMeasurement, WindowCovering, } from 'matterbridge/matter/clusters';
5
+ import { Appliances } from './appliances.js';
6
+ import { Robot } from './robot.js';
5
7
  export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatform {
6
8
  switch;
7
9
  mountedOnOffSwitch;
@@ -48,6 +50,9 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
48
50
  airConditionerInterval;
49
51
  genericSwitchInterval;
50
52
  genericSwitchLastEvent = 'Release';
53
+ intervalOnOff = false;
54
+ intervalLevel = 0;
55
+ intervalColorTemperature = 147;
51
56
  bridgedDevices = new Map();
52
57
  fanModeLookup = ['Off', 'Low', 'Medium', 'High', 'On', 'Auto', 'Smart'];
53
58
  constructor(matterbridge, log, config) {
@@ -90,6 +95,65 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
90
95
  await this.switch?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.switch.log);
91
96
  this.switch?.log.info('Command off called');
92
97
  });
98
+ this.mountedOnOffSwitch = new MatterbridgeEndpoint([onOffMountedSwitch, bridgedNode, powerSource], { uniqueStorageKey: 'OnOffMountedSwitch' }, this.config.debug)
99
+ .createDefaultIdentifyClusterServer()
100
+ .createDefaultGroupsClusterServer()
101
+ .createDefaultBridgedDeviceBasicInformationClusterServer('OnOff Mounted Switch', '0x298242164', 0xfff1, 'Matterbridge', 'Matterbridge OnOff Mounted Switch', parseInt(this.version.replace(/\D/g, '')), this.version === '' ? 'Unknown' : this.version, parseInt(this.matterbridge.matterbridgeVersion.replace(/\D/g, '')), this.matterbridge.matterbridgeVersion)
102
+ .createDefaultOnOffClusterServer()
103
+ .createDefaultPowerSourceRechargeableBatteryClusterServer(70);
104
+ this.setSelectDevice(this.mountedOnOffSwitch.serialNumber ?? '', this.mountedOnOffSwitch.deviceName ?? '', undefined, 'hub');
105
+ if (this.validateDevice(this.mountedOnOffSwitch.deviceName ?? '')) {
106
+ await this.registerDevice(this.mountedOnOffSwitch);
107
+ this.bridgedDevices.set(this.mountedOnOffSwitch.deviceName ?? '', this.mountedOnOffSwitch);
108
+ }
109
+ else {
110
+ this.mountedOnOffSwitch = undefined;
111
+ }
112
+ this.mountedOnOffSwitch?.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
113
+ this.mountedOnOffSwitch?.log.info(`Command identify called identifyTime:${identifyTime}`);
114
+ });
115
+ this.mountedOnOffSwitch?.addCommandHandler('on', async () => {
116
+ await this.mountedOnOffSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.mountedOnOffSwitch.log);
117
+ this.mountedOnOffSwitch?.log.info('Command on called');
118
+ });
119
+ this.mountedOnOffSwitch?.addCommandHandler('off', async () => {
120
+ await this.mountedOnOffSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.mountedOnOffSwitch.log);
121
+ this.mountedOnOffSwitch?.log.info('Command off called');
122
+ });
123
+ this.mountedDimmerSwitch = new MatterbridgeEndpoint([dimmableMountedSwitch, bridgedNode, powerSource], { uniqueStorageKey: 'DimmerMountedSwitch' }, this.config.debug)
124
+ .createDefaultIdentifyClusterServer()
125
+ .createDefaultGroupsClusterServer()
126
+ .createDefaultBridgedDeviceBasicInformationClusterServer('Dimmer Mounted Switch', '0x22145578864', 0xfff1, 'Matterbridge', 'Matterbridge Dimmer Mounted Switch', parseInt(this.version.replace(/\D/g, '')), this.version === '' ? 'Unknown' : this.version, parseInt(this.matterbridge.matterbridgeVersion.replace(/\D/g, '')), this.matterbridge.matterbridgeVersion)
127
+ .createDefaultOnOffClusterServer()
128
+ .createDefaultLevelControlClusterServer()
129
+ .createDefaultPowerSourceRechargeableBatteryClusterServer(70);
130
+ this.setSelectDevice(this.mountedDimmerSwitch.serialNumber ?? '', this.mountedDimmerSwitch.deviceName ?? '', undefined, 'hub');
131
+ if (this.validateDevice(this.mountedDimmerSwitch.deviceName ?? '')) {
132
+ await this.registerDevice(this.mountedDimmerSwitch);
133
+ this.bridgedDevices.set(this.mountedDimmerSwitch.deviceName ?? '', this.mountedDimmerSwitch);
134
+ }
135
+ else {
136
+ this.mountedDimmerSwitch = undefined;
137
+ }
138
+ this.mountedDimmerSwitch?.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
139
+ this.mountedDimmerSwitch?.log.info(`Command identify called identifyTime:${identifyTime}`);
140
+ });
141
+ this.mountedDimmerSwitch?.addCommandHandler('on', async () => {
142
+ await this.mountedDimmerSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.mountedDimmerSwitch.log);
143
+ this.mountedDimmerSwitch?.log.info('Command on called');
144
+ });
145
+ this.mountedDimmerSwitch?.addCommandHandler('off', async () => {
146
+ await this.mountedDimmerSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.mountedDimmerSwitch.log);
147
+ this.mountedDimmerSwitch?.log.info('Command off called');
148
+ });
149
+ this.mountedDimmerSwitch?.addCommandHandler('moveToLevel', async ({ request: { level } }) => {
150
+ await this.mountedDimmerSwitch?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.mountedDimmerSwitch.log);
151
+ this.mountedDimmerSwitch?.log.debug(`Command moveToLevel called request: ${level}`);
152
+ });
153
+ this.mountedDimmerSwitch?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => {
154
+ await this.mountedDimmerSwitch?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.mountedDimmerSwitch.log);
155
+ this.mountedDimmerSwitch?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`);
156
+ });
93
157
  this.lightOnOff = new MatterbridgeEndpoint([onOffLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (on/off)' }, this.config.debug)
94
158
  .createDefaultIdentifyClusterServer()
95
159
  .createDefaultGroupsClusterServer()
@@ -121,7 +185,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
121
185
  .createDefaultBridgedDeviceBasicInformationClusterServer('Dimmer', '0x234554564', 0xfff1, 'Matterbridge', 'Matterbridge Dimmer', parseInt(this.version.replace(/\D/g, '')), this.version === '' ? 'Unknown' : this.version, parseInt(this.matterbridge.matterbridgeVersion.replace(/\D/g, '')), this.matterbridge.matterbridgeVersion)
122
186
  .createDefaultOnOffClusterServer()
123
187
  .createDefaultLevelControlClusterServer()
124
- .createDefaultPowerSourceReplaceableBatteryClusterServer(70);
188
+ .createDefaultPowerSourceReplaceableBatteryClusterServer(70, PowerSource.BatChargeLevel.Ok, 2990, '2 x AA', 2);
125
189
  this.setSelectDevice(this.dimmer.serialNumber ?? '', this.dimmer.deviceName ?? '', undefined, 'hub');
126
190
  if (this.validateDevice(this.dimmer.deviceName ?? '')) {
127
191
  await this.registerDevice(this.dimmer);
@@ -149,7 +213,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
149
213
  await this.dimmer?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.dimmer.log);
150
214
  this.dimmer?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`);
151
215
  });
152
- this.light = new MatterbridgeEndpoint([colorTemperatureLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (XY, HS and CT)' }, this.config.debug)
216
+ this.light = new MatterbridgeEndpoint([extendedColorLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (XY, HS and CT)' }, this.config.debug)
153
217
  .createDefaultIdentifyClusterServer()
154
218
  .createDefaultGroupsClusterServer()
155
219
  .createDefaultBridgedDeviceBasicInformationClusterServer('Light (XY, HS and CT)', '0x23480564', 0xfff1, 'Matterbridge', 'Matterbridge Light', parseInt(this.version.replace(/\D/g, '')), this.version === '' ? 'Unknown' : this.version, parseInt(this.matterbridge.matterbridgeVersion.replace(/\D/g, '')), this.matterbridge.matterbridgeVersion)
@@ -258,7 +322,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
258
322
  await this.lightHS?.setAttribute(ColorControl.Cluster.id, 'colorTemperatureMireds', colorTemperatureMireds, this.lightHS?.log);
259
323
  this.lightHS?.log.debug(`Command moveToColorTemperature called request: ${colorTemperatureMireds}`);
260
324
  });
261
- this.lightXY = new MatterbridgeEndpoint([colorTemperatureLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (XY, CT)' }, this.config.debug)
325
+ this.lightXY = new MatterbridgeEndpoint([extendedColorLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (XY, CT)' }, this.config.debug)
262
326
  .createDefaultIdentifyClusterServer()
263
327
  .createDefaultGroupsClusterServer()
264
328
  .createDefaultBridgedDeviceBasicInformationClusterServer('Light (XY, CT)', '0x23497564', 0xfff1, 'Matterbridge', 'Matterbridge Light', parseInt(this.version.replace(/\D/g, '')), this.version === '' ? 'Unknown' : this.version, parseInt(this.matterbridge.matterbridgeVersion.replace(/\D/g, '')), this.matterbridge.matterbridgeVersion)
@@ -554,7 +618,9 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
554
618
  .createDefaultFanControlClusterServer()
555
619
  .createDefaultTemperatureMeasurementClusterServer(20 * 100)
556
620
  .createDefaultRelativeHumidityMeasurementClusterServer(50 * 100)
557
- .createDefaultPowerSourceWiredClusterServer();
621
+ .createDefaultPowerSourceWiredClusterServer()
622
+ .createDefaultActivatedCarbonFilterMonitoringClusterServer()
623
+ .createDefaultHepaFilterMonitoringClusterServer();
558
624
  this.setSelectDevice(this.airPurifier.serialNumber ?? '', this.airPurifier.deviceName ?? '', undefined, 'hub');
559
625
  if (this.validateDevice(this.airPurifier.deviceName ?? '')) {
560
626
  await this.registerDevice(this.airPurifier);
@@ -592,11 +658,12 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
592
658
  if (isValidNumber(newValue, 0, 100))
593
659
  await this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', newValue, this.airPurifier?.log);
594
660
  }, this.airPurifier.log);
595
- this.airConditioner = new MatterbridgeEndpoint([airConditioner, bridgedNode, powerSource], { uniqueStorageKey: 'Air conditioner' }, this.config.debug)
596
- .createDefaultBridgedDeviceBasicInformationClusterServer('Air conditioner', '0x96382864AC', 0xfff1, 'Matterbridge', 'Matterbridge Air conditioner', parseInt(this.version.replace(/\D/g, '')), this.version === '' ? 'Unknown' : this.version, parseInt(this.matterbridge.matterbridgeVersion.replace(/\D/g, '')), this.matterbridge.matterbridgeVersion)
661
+ this.airConditioner = new MatterbridgeEndpoint([airConditioner, bridgedNode, powerSource], { uniqueStorageKey: 'Air Conditioner' }, this.config.debug)
662
+ .createDefaultBridgedDeviceBasicInformationClusterServer('Air Conditioner', '0x96382864AC', 0xfff1, 'Matterbridge', 'Matterbridge Air Conditioner', parseInt(this.version.replace(/\D/g, '')), this.version === '' ? 'Unknown' : this.version, parseInt(this.matterbridge.matterbridgeVersion.replace(/\D/g, '')), this.matterbridge.matterbridgeVersion)
597
663
  .createDefaultIdentifyClusterServer()
598
664
  .createDeadFrontOnOffClusterServer(true)
599
665
  .createDefaultThermostatClusterServer(20, 18, 22)
666
+ .createDefaultThermostatUserInterfaceConfigurationClusterServer()
600
667
  .createDefaultFanControlClusterServer(FanControl.FanMode.Auto)
601
668
  .createDefaultTemperatureMeasurementClusterServer(20 * 100)
602
669
  .createDefaultRelativeHumidityMeasurementClusterServer(50 * 100)
@@ -615,8 +682,6 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
615
682
  });
616
683
  this.airConditioner?.addCommandHandler('on', async () => {
617
684
  this.airConditioner?.log.info('Command on called');
618
- await this.airConditioner?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.airConditioner?.log);
619
- await this.airConditioner?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.airConditioner?.log);
620
685
  await this.airConditioner?.setAttribute(ThermostatCluster.id, 'localTemperature', 20 * 100, this.airConditioner?.log);
621
686
  await this.airConditioner?.setAttribute(TemperatureMeasurement.Cluster.id, 'measuredValue', 20 * 100, this.airConditioner?.log);
622
687
  await this.airConditioner?.setAttribute(RelativeHumidityMeasurementCluster.id, 'measuredValue', 50 * 100, this.airConditioner?.log);
@@ -625,7 +690,6 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
625
690
  });
626
691
  this.airConditioner?.addCommandHandler('off', async () => {
627
692
  this.airConditioner?.log.info('Command off called');
628
- await this.airConditioner?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.airConditioner?.log);
629
693
  await this.airConditioner?.setAttribute(ThermostatCluster.id, 'localTemperature', null, this.airConditioner?.log);
630
694
  await this.airConditioner?.setAttribute(TemperatureMeasurement.Cluster.id, 'measuredValue', null, this.airConditioner?.log);
631
695
  await this.airConditioner?.setAttribute(RelativeHumidityMeasurementCluster.id, 'measuredValue', null, this.airConditioner?.log);
@@ -635,7 +699,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
635
699
  this.pump = new MatterbridgeEndpoint([pumpDevice, bridgedNode, powerSource], { uniqueStorageKey: 'Pump' }, this.config.debug)
636
700
  .createDefaultBridgedDeviceBasicInformationClusterServer('Pump', '0x96382864PUMP', 0xfff1, 'Matterbridge', 'Matterbridge Pump', parseInt(this.version.replace(/\D/g, '')), this.version === '' ? 'Unknown' : this.version, parseInt(this.matterbridge.matterbridgeVersion.replace(/\D/g, '')), this.matterbridge.matterbridgeVersion)
637
701
  .createDefaultIdentifyClusterServer()
638
- .createDefaultOnOffClusterServer(true)
702
+ .createOnOffClusterServer()
703
+ .createLevelControlClusterServer()
639
704
  .createDefaultPumpConfigurationAndControlClusterServer()
640
705
  .createDefaultPowerSourceWiredClusterServer();
641
706
  this.setSelectDevice(this.pump.serialNumber ?? '', this.pump.deviceName ?? '', undefined, 'hub');
@@ -850,25 +915,82 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
850
915
  else {
851
916
  this.latchingSwitch = undefined;
852
917
  }
918
+ const robot = new Robot('Robot Vacuum', '1238777820');
919
+ this.setSelectDevice(robot.serialNumber ?? '', robot.deviceName ?? '', undefined, 'hub');
920
+ if (this.validateDevice(robot.deviceName ?? '')) {
921
+ await this.registerDevice(robot);
922
+ this.bridgedDevices.set(robot.deviceName ?? '', robot);
923
+ }
924
+ const laundryWasherDevice = new Appliances(laundryWasher, 'Laundry Washer', '1234567890');
925
+ this.setSelectDevice(laundryWasherDevice.serialNumber ?? '', laundryWasherDevice.deviceName ?? '', undefined, 'hub');
926
+ if (this.validateDevice(laundryWasherDevice.deviceName ?? '')) {
927
+ await this.registerDevice(laundryWasherDevice);
928
+ this.bridgedDevices.set(laundryWasherDevice.deviceName ?? '', laundryWasherDevice);
929
+ }
930
+ const laundryDryerDevice = new Appliances(laundryDryer, 'Laundry Dryer', '1235227890');
931
+ this.setSelectDevice(laundryDryerDevice.serialNumber ?? '', laundryDryerDevice.deviceName ?? '', undefined, 'hub');
932
+ if (this.validateDevice(laundryDryerDevice.deviceName ?? '')) {
933
+ await this.registerDevice(laundryDryerDevice);
934
+ this.bridgedDevices.set(laundryDryerDevice.deviceName ?? '', laundryDryerDevice);
935
+ }
936
+ const dishwasherDevice = new Appliances(dishwasher, 'Dishwasher', '0987654321');
937
+ this.setSelectDevice(dishwasherDevice.serialNumber ?? '', dishwasherDevice.deviceName ?? '', undefined, 'hub');
938
+ if (this.validateDevice(dishwasherDevice.deviceName ?? '')) {
939
+ await this.registerDevice(dishwasherDevice);
940
+ this.bridgedDevices.set(dishwasherDevice.deviceName ?? '', dishwasherDevice);
941
+ }
942
+ const refrigeratorDevice = new Appliances(refrigerator, 'Refrigerator', '9987654322');
943
+ this.setSelectDevice(refrigeratorDevice.serialNumber ?? '', refrigeratorDevice.deviceName ?? '', undefined, 'hub');
944
+ if (this.validateDevice(refrigeratorDevice.deviceName ?? '')) {
945
+ await this.registerDevice(refrigeratorDevice);
946
+ this.bridgedDevices.set(refrigeratorDevice.deviceName ?? '', refrigeratorDevice);
947
+ }
948
+ const ovenDevice = new Appliances(oven, 'Oven', '1298867891');
949
+ this.setSelectDevice(ovenDevice.serialNumber ?? '', ovenDevice.deviceName ?? '', undefined, 'hub');
950
+ if (this.validateDevice(ovenDevice.deviceName ?? '')) {
951
+ await this.registerDevice(ovenDevice);
952
+ this.bridgedDevices.set(ovenDevice.deviceName ?? '', ovenDevice);
953
+ }
954
+ const microwaveOvenDevice = new Appliances(microwaveOven, 'Microwave Oven', '1234567892');
955
+ this.setSelectDevice(microwaveOvenDevice.serialNumber ?? '', microwaveOvenDevice.deviceName ?? '', undefined, 'hub');
956
+ if (this.validateDevice(microwaveOvenDevice.deviceName ?? '')) {
957
+ await this.registerDevice(microwaveOvenDevice);
958
+ this.bridgedDevices.set(microwaveOvenDevice.deviceName ?? '', microwaveOvenDevice);
959
+ }
960
+ const extractorHoodDevice = new Appliances(extractorHood, 'Extractor Hood', '1234567893');
961
+ this.setSelectDevice(extractorHoodDevice.serialNumber ?? '', extractorHoodDevice.deviceName ?? '', undefined, 'hub');
962
+ if (this.validateDevice(extractorHoodDevice.deviceName ?? '')) {
963
+ await this.registerDevice(extractorHoodDevice);
964
+ this.bridgedDevices.set(extractorHoodDevice.deviceName ?? '', extractorHoodDevice);
965
+ }
966
+ const cooktopDevice = new Appliances(cooktop, 'Cooktop', '1255887894');
967
+ this.setSelectDevice(cooktopDevice.serialNumber ?? '', cooktopDevice.deviceName ?? '', undefined, 'hub');
968
+ if (this.validateDevice(cooktopDevice.deviceName ?? '')) {
969
+ await this.registerDevice(cooktopDevice);
970
+ this.bridgedDevices.set(cooktopDevice.deviceName ?? '', cooktopDevice);
971
+ }
853
972
  }
854
973
  async onConfigure() {
855
974
  await super.onConfigure();
856
975
  this.log.info('onConfigure called');
857
- await this.switch?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.switch.log);
858
- this.switch?.log.info('Set switch initial onOff to false');
976
+ this.log.notice('<iframe width="500" height="500" src="https://www.youtube.com/embed/DLzxrzFCyOs?hl=en_US&version=3&rel=0&autoplay=1" frameborder="1" allowfullscreen></iframe>');
977
+ await this.switch?.setAttribute(OnOff.Cluster.id, 'onOff', this.intervalOnOff, this.switch.log);
978
+ await this.mountedOnOffSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', this.intervalOnOff, this.mountedOnOffSwitch.log);
979
+ this.switch?.log.info(`Set switch initial onOff to ${this.intervalOnOff}`);
859
980
  if (this.config.useInterval) {
860
981
  this.switchInterval = setInterval(async () => {
861
- const status = this.switch?.getAttribute(OnOff.Cluster.id, 'onOff', this.switch.log);
862
- if (isValidBoolean(status)) {
863
- await this.switch?.setAttribute(OnOff.Cluster.id, 'onOff', !status, this.switch.log);
864
- this.switch?.log.info(`Set switch onOff to ${!status}`);
865
- }
982
+ await this.switch?.setAttribute(OnOff.Cluster.id, 'onOff', this.intervalOnOff, this.switch.log);
983
+ await this.mountedOnOffSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', this.intervalOnOff, this.mountedOnOffSwitch.log);
984
+ this.log.info(`Set switches onOff to ${this.intervalOnOff}`);
985
+ this.intervalOnOff = !this.intervalOnOff;
866
986
  }, 60 * 1000 + 100);
867
987
  }
868
988
  await this.lightOnOff?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightOnOff.log);
869
989
  this.lightOnOff?.log.info('Set light initial onOff to false.');
870
990
  await this.dimmer?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.dimmer.log);
871
991
  await this.dimmer?.setAttribute(LevelControl.Cluster.id, 'currentLevel', 1, this.dimmer.log);
992
+ await this.mountedDimmerSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.mountedDimmerSwitch.log);
993
+ await this.mountedDimmerSwitch?.setAttribute(LevelControl.Cluster.id, 'currentLevel', 1, this.mountedDimmerSwitch.log);
872
994
  this.dimmer?.log.info(`Set dimmer initial onOff to false, currentLevel to 1.`);
873
995
  await this.light?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.light.log);
874
996
  await this.light?.setAttribute(LevelControl.Cluster.id, 'currentLevel', 200, this.light.log);
@@ -895,42 +1017,48 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
895
1017
  this.lightCT?.log.info('Set light CT initial onOff to true, currentLevel to 128, colorTemperatureMireds to 250.');
896
1018
  if (this.config.useInterval) {
897
1019
  this.lightInterval = setInterval(async () => {
898
- const state = this.light?.getAttribute(OnOff.Cluster.id, 'onOff', this.light.log);
899
- let level = this.light?.getAttribute(LevelControl.Cluster.id, 'currentLevel', this.light.log);
900
- if (isValidBoolean(state) && isValidNumber(level, 0, 254)) {
901
- level += 10;
902
- if (level >= 250) {
903
- level = 1;
904
- await this.lightOnOff?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightOnOff.log);
905
- await this.dimmer?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.dimmer.log);
906
- await this.light?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.light.log);
907
- await this.lightXY?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightXY.log);
908
- await this.lightHS?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightHS.log);
909
- await this.lightCT?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightCT.log);
910
- this.log.info('Set lights onOff to false');
911
- await this.dimmer?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.dimmer.log);
912
- await this.light?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.light.log);
913
- await this.lightXY?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightXY.log);
914
- await this.lightHS?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightHS.log);
915
- await this.lightCT?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightCT.log);
916
- this.log.info(`Set lights currentLevel to ${level}`);
917
- }
918
- else {
919
- await this.lightOnOff?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightOnOff?.log);
920
- await this.dimmer?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.dimmer.log);
921
- await this.light?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.light.log);
922
- await this.lightXY?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightXY.log);
923
- await this.lightHS?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightHS.log);
924
- await this.lightCT?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightCT.log);
925
- this.log.info('Set lights onOff to true');
926
- await this.dimmer?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.dimmer.log);
927
- await this.light?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.light.log);
928
- await this.lightXY?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightXY.log);
929
- await this.lightHS?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightHS.log);
930
- await this.lightCT?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightCT.log);
931
- this.log.info(`Set lights currentLevel to ${level}`);
932
- }
1020
+ this.intervalLevel += 10;
1021
+ if (this.intervalLevel >= 250) {
1022
+ this.intervalLevel = 0;
1023
+ await this.lightOnOff?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightOnOff.log);
1024
+ await this.dimmer?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.dimmer.log);
1025
+ await this.mountedDimmerSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.mountedDimmerSwitch.log);
1026
+ await this.light?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.light.log);
1027
+ await this.lightXY?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightXY.log);
1028
+ await this.lightHS?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightHS.log);
1029
+ await this.lightCT?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightCT.log);
1030
+ this.log.info('Set lights onOff to false');
1031
+ }
1032
+ else {
1033
+ await this.lightOnOff?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightOnOff?.log);
1034
+ await this.dimmer?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.dimmer.log);
1035
+ await this.mountedDimmerSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.mountedDimmerSwitch.log);
1036
+ await this.light?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.light.log);
1037
+ await this.lightXY?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightXY.log);
1038
+ await this.lightHS?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightHS.log);
1039
+ await this.lightCT?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightCT.log);
1040
+ this.log.info('Set lights onOff to true');
1041
+ await this.dimmer?.setAttribute(LevelControl.Cluster.id, 'currentLevel', this.intervalLevel, this.dimmer.log);
1042
+ await this.mountedDimmerSwitch?.setAttribute(LevelControl.Cluster.id, 'currentLevel', this.intervalLevel, this.mountedDimmerSwitch.log);
1043
+ await this.light?.setAttribute(LevelControl.Cluster.id, 'currentLevel', this.intervalLevel, this.light.log);
1044
+ await this.lightXY?.setAttribute(LevelControl.Cluster.id, 'currentLevel', this.intervalLevel, this.lightXY.log);
1045
+ await this.lightHS?.setAttribute(LevelControl.Cluster.id, 'currentLevel', this.intervalLevel, this.lightHS.log);
1046
+ await this.lightCT?.setAttribute(LevelControl.Cluster.id, 'currentLevel', this.intervalLevel, this.lightCT.log);
1047
+ this.log.info(`Set lights currentLevel to ${this.intervalLevel}`);
1048
+ }
1049
+ this.intervalColorTemperature += 50;
1050
+ if (this.intervalColorTemperature > 500) {
1051
+ this.intervalColorTemperature = 147;
933
1052
  }
1053
+ await this.light?.setAttribute(ColorControl.Cluster.id, 'colorTemperatureMireds', this.intervalColorTemperature, this.light.log);
1054
+ await this.lightHS?.setAttribute(ColorControl.Cluster.id, 'colorTemperatureMireds', this.intervalColorTemperature, this.lightHS.log);
1055
+ await this.lightXY?.setAttribute(ColorControl.Cluster.id, 'colorTemperatureMireds', this.intervalColorTemperature, this.lightXY.log);
1056
+ await this.lightCT?.setAttribute(ColorControl.Cluster.id, 'colorTemperatureMireds', this.intervalColorTemperature, this.lightCT.log);
1057
+ await this.light?.configureColorControlMode(ColorControl.ColorMode.ColorTemperatureMireds);
1058
+ await this.lightHS?.configureColorControlMode(ColorControl.ColorMode.ColorTemperatureMireds);
1059
+ await this.lightXY?.configureColorControlMode(ColorControl.ColorMode.ColorTemperatureMireds);
1060
+ await this.lightCT?.configureColorControlMode(ColorControl.ColorMode.ColorTemperatureMireds);
1061
+ this.log.info(`Set lights colorTemperatureMireds to ${this.intervalColorTemperature}`);
934
1062
  }, 60 * 1000 + 200);
935
1063
  }
936
1064
  await this.outlet?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.outlet.log);
@@ -1110,6 +1238,24 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
1110
1238
  }
1111
1239
  }, 60 * 1000 + 1100);
1112
1240
  }
1241
+ const airConditionerDevice = this.bridgedDevices.get('Air Conditioner');
1242
+ await airConditionerDevice?.setAttribute(OnOff.Cluster.id, 'onOff', true, airConditionerDevice.log);
1243
+ const laundryWasherDevice = this.bridgedDevices.get('Laundry Washer');
1244
+ await laundryWasherDevice?.setAttribute(OnOff.Cluster.id, 'onOff', true, laundryWasherDevice.log);
1245
+ const laundryDryerDevice = this.bridgedDevices.get('Laundry Dryer');
1246
+ await laundryDryerDevice?.setAttribute(OnOff.Cluster.id, 'onOff', true, laundryDryerDevice.log);
1247
+ const dishwasherDevice = this.bridgedDevices.get('Dishwasher');
1248
+ await dishwasherDevice?.setAttribute(OnOff.Cluster.id, 'onOff', true, dishwasherDevice.log);
1249
+ this.log.info(`Set appliances dead front OnOff to on`);
1250
+ const cooktopDevice = this.bridgedDevices.get('Cooktop');
1251
+ await cooktopDevice?.setAttribute(OnOff.Cluster.id, 'onOff', true, cooktopDevice.log);
1252
+ cooktopDevice?.log.info(`Set Cooktop onOff only OnOff to on`);
1253
+ const surface1 = cooktopDevice?.getChildEndpointByName('Surface1');
1254
+ await surface1?.setAttribute(OnOff.Cluster.id, 'onOff', true, surface1.log);
1255
+ surface1?.log.info(`Set Surface 1 onOff only OnOff to on`);
1256
+ const surface2 = cooktopDevice?.getChildEndpointByName('Surface2');
1257
+ await surface2?.setAttribute(OnOff.Cluster.id, 'onOff', true, surface2.log);
1258
+ surface2?.log.info(`Set Surface 2 onOff only OnOff to on`);
1113
1259
  if (this.config.useInterval) {
1114
1260
  this.genericSwitchInterval = setInterval(async () => {
1115
1261
  if (this.genericSwitchLastEvent === 'Release') {
@@ -1153,6 +1299,6 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
1153
1299
  await super.onShutdown(reason);
1154
1300
  this.log.info('onShutdown called with reason:', reason ?? 'none');
1155
1301
  if (this.config.unregisterOnShutdown === true)
1156
- await this.unregisterAllDevices();
1302
+ await this.unregisterAllDevices(500);
1157
1303
  }
1158
1304
  }
package/dist/robot.js ADDED
@@ -0,0 +1,215 @@
1
+ import { Matterbridge, MatterbridgeServer, MatterbridgeEndpoint, roboticVacuumCleaner, dishwasher } from 'matterbridge';
2
+ import { LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, EndpointServer, logEndpoint, DeviceTypeId, VendorId, } from 'matterbridge/matter';
3
+ import { ModeBase, OperationalState, PowerSource, RvcRunMode, RvcCleanMode, RvcOperationalState, ServiceArea } from 'matterbridge/matter/clusters';
4
+ import { ActionsServer, RvcCleanModeBehavior, RvcOperationalStateBehavior, RvcRunModeBehavior, ServiceAreaBehavior } from 'matterbridge/matter/behaviors';
5
+ import { AnsiLogger } from 'matterbridge/logger';
6
+ import { Appliances } from './appliances.js';
7
+ export class Robot extends MatterbridgeEndpoint {
8
+ constructor(name, serial) {
9
+ super(roboticVacuumCleaner, { uniqueStorageKey: `${name}-${serial}` }, true);
10
+ this.createDefaultIdentifyClusterServer()
11
+ .createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Robot Vacuum Cleaner')
12
+ .createDefaultRvcRunModeClusterServer()
13
+ .createDefaultRvcOperationalStateClusterServer()
14
+ .createDefaultRvcCleanModeClusterServer()
15
+ .createDefaultServiceAreaClusterServer()
16
+ .createDefaultPowerSourceRechargeableBatteryClusterServer(80, PowerSource.BatChargeLevel.Ok, 5900);
17
+ }
18
+ createDefaultRvcRunModeClusterServer(currentMode, supportedModes) {
19
+ this.behaviors.require(MatterbridgeRvcRunModeServer, {
20
+ supportedModes: supportedModes ?? [
21
+ { label: 'Idle', mode: 1, modeTags: [{ value: RvcRunMode.ModeTag.Idle }] },
22
+ { label: 'Cleaning', mode: 2, modeTags: [{ value: RvcRunMode.ModeTag.Cleaning }] },
23
+ { label: 'Mapping', mode: 3, modeTags: [{ value: RvcRunMode.ModeTag.Mapping }] },
24
+ { label: 'SpotCleaning', mode: 4, modeTags: [{ value: RvcRunMode.ModeTag.Cleaning }, { value: RvcRunMode.ModeTag.Max }] },
25
+ ],
26
+ currentMode: currentMode ?? 1,
27
+ });
28
+ return this;
29
+ }
30
+ createDefaultRvcCleanModeClusterServer(currentMode, supportedModes) {
31
+ this.behaviors.require(MatterbridgeRvcCleanModeServer, {
32
+ supportedModes: supportedModes ?? [
33
+ { label: 'Vacuum', mode: 1, modeTags: [{ value: RvcCleanMode.ModeTag.Vacuum }] },
34
+ { label: 'Mop', mode: 2, modeTags: [{ value: RvcCleanMode.ModeTag.Mop }] },
35
+ { label: 'Clean', mode: 3, modeTags: [{ value: RvcCleanMode.ModeTag.DeepClean }] },
36
+ ],
37
+ currentMode: currentMode ?? 1,
38
+ });
39
+ return this;
40
+ }
41
+ createDefaultServiceAreaClusterServer(supportedAreas, selectedAreas) {
42
+ this.behaviors.require(MatterbridgeServiceAreaServer, {
43
+ supportedAreas: supportedAreas ?? [
44
+ {
45
+ areaId: 1,
46
+ mapId: null,
47
+ areaInfo: { locationInfo: { locationName: 'Living', floorNumber: null, areaType: null }, landmarkInfo: null },
48
+ },
49
+ {
50
+ areaId: 2,
51
+ mapId: null,
52
+ areaInfo: { locationInfo: { locationName: 'Kitchen', floorNumber: null, areaType: null }, landmarkInfo: null },
53
+ },
54
+ {
55
+ areaId: 3,
56
+ mapId: null,
57
+ areaInfo: { locationInfo: { locationName: 'Bedroom', floorNumber: null, areaType: null }, landmarkInfo: null },
58
+ },
59
+ {
60
+ areaId: 4,
61
+ mapId: null,
62
+ areaInfo: { locationInfo: { locationName: 'Bathroom', floorNumber: null, areaType: null }, landmarkInfo: null },
63
+ },
64
+ ],
65
+ selectedAreas: selectedAreas ?? [],
66
+ currentArea: 1,
67
+ estimatedEndTime: null,
68
+ });
69
+ return this;
70
+ }
71
+ createDefaultRvcOperationalStateClusterServer(phaseList = null, currentPhase = null, operationalStateList, operationalState, operationalError) {
72
+ this.behaviors.require(MatterbridgeRvcOperationalStateServer, {
73
+ phaseList,
74
+ currentPhase,
75
+ operationalStateList: operationalStateList ?? [
76
+ { operationalStateId: RvcOperationalState.OperationalState.Stopped, operationalStateLabel: 'Stopped' },
77
+ { operationalStateId: RvcOperationalState.OperationalState.Running, operationalStateLabel: 'Running' },
78
+ { operationalStateId: RvcOperationalState.OperationalState.Paused, operationalStateLabel: 'Paused' },
79
+ { operationalStateId: RvcOperationalState.OperationalState.Error, operationalStateLabel: 'Error' },
80
+ { operationalStateId: RvcOperationalState.OperationalState.SeekingCharger, operationalStateLabel: 'SeekingCharger' },
81
+ { operationalStateId: RvcOperationalState.OperationalState.Charging, operationalStateLabel: 'Charging' },
82
+ { operationalStateId: RvcOperationalState.OperationalState.Docked, operationalStateLabel: 'Docked' },
83
+ ],
84
+ operationalState: operationalState ?? RvcOperationalState.OperationalState.Docked,
85
+ operationalError: operationalError ?? { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' },
86
+ });
87
+ return this;
88
+ }
89
+ }
90
+ export class MatterbridgeServiceAreaServer extends ServiceAreaBehavior {
91
+ initialize() {
92
+ }
93
+ selectAreas({ newAreas }) {
94
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
95
+ for (const area of newAreas) {
96
+ const supportedArea = this.state.supportedAreas.find((supportedArea) => supportedArea.areaId === area);
97
+ if (!supportedArea) {
98
+ device.log.error('MatterbridgeServiceAreaServer selectAreas called with unsupported area:', area);
99
+ return { status: ServiceArea.SelectAreasStatus.UnsupportedArea, statusText: 'Unsupported areas' };
100
+ }
101
+ }
102
+ this.state.selectedAreas = newAreas;
103
+ this.state.currentArea = newAreas[0];
104
+ device.log.info(`***MatterbridgeServiceAreaServer selectAreas called with: ${newAreas.map((area) => area.toString()).join(', ')}`);
105
+ return { status: ServiceArea.SelectAreasStatus.Success, statusText: 'Succesfully selected new areas' };
106
+ }
107
+ }
108
+ export class MatterbridgeRvcRunModeServer extends RvcRunModeBehavior {
109
+ initialize() {
110
+ this.state.currentMode = 1;
111
+ }
112
+ changeToMode({ newMode }) {
113
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
114
+ const changedMode = this.state.supportedModes.find((mode) => mode.mode === newMode);
115
+ if (!changedMode) {
116
+ device.log.error('MatterbridgeRvcRunModeServer changeToMode called with unsupported newMode:', newMode);
117
+ return { status: ModeBase.ModeChangeStatus.InvalidInMode, statusText: 'Invalid mode' };
118
+ }
119
+ device.changeToMode({ newMode });
120
+ this.state.currentMode = newMode;
121
+ if (changedMode.modeTags.find((tag) => tag.value === RvcRunMode.ModeTag.Cleaning)) {
122
+ device.log.info('***MatterbridgeRvcRunModeServer changeToMode called with newMode Cleaning => Running');
123
+ this.agent.get(MatterbridgeRvcOperationalStateServer).state.operationalState = RvcOperationalState.OperationalState.Running;
124
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Running' };
125
+ }
126
+ else if (changedMode.modeTags.find((tag) => tag.value === RvcRunMode.ModeTag.Idle)) {
127
+ device.log.info('***MatterbridgeRvcRunModeServer changeToMode called with newMode Idle => Docked');
128
+ this.agent.get(MatterbridgeRvcOperationalStateServer).state.operationalState = RvcOperationalState.OperationalState.Docked;
129
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Docked' };
130
+ }
131
+ device.log.info(`***MatterbridgeRvcRunModeServer changeToMode called with newMode ${newMode} => ${changedMode.label}`);
132
+ this.agent.get(MatterbridgeRvcOperationalStateServer).state.operationalState = RvcOperationalState.OperationalState.Running;
133
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
134
+ }
135
+ }
136
+ export class MatterbridgeRvcCleanModeServer extends RvcCleanModeBehavior {
137
+ initialize() {
138
+ this.state.currentMode = 1;
139
+ }
140
+ changeToMode({ newMode }) {
141
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
142
+ const supported = this.state.supportedModes.find((mode) => mode.mode === newMode);
143
+ if (!supported) {
144
+ device.log.error('***MatterbridgeRvcCleanModeServer changeToMode called with unsupported newMode:', newMode);
145
+ return { status: ModeBase.ModeChangeStatus.InvalidInMode, statusText: 'Invalid mode' };
146
+ }
147
+ device.changeToMode({ newMode });
148
+ this.state.currentMode = newMode;
149
+ device.log.info(`***MatterbridgeRvcCleanModeServer changeToMode called with newMode ${newMode} => ${supported.label}`);
150
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
151
+ }
152
+ }
153
+ export class MatterbridgeRvcOperationalStateServer extends RvcOperationalStateBehavior {
154
+ initialize() {
155
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
156
+ device.log.info('***MatterbridgeRvcOperationalStateServer initialized: setting operational state to Docked');
157
+ this.state.operationalState = RvcOperationalState.OperationalState.Docked;
158
+ this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' };
159
+ }
160
+ pause() {
161
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
162
+ device.log.info('MatterbridgeRvcOperationalStateServer: pause called setting operational state to Paused and currentMode to Idle');
163
+ this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 1;
164
+ this.state.operationalState = RvcOperationalState.OperationalState.Paused;
165
+ this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' };
166
+ return {
167
+ commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
168
+ };
169
+ }
170
+ resume() {
171
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
172
+ device.log.info('MatterbridgeRvcOperationalStateServer: resume called setting operational state to Running and currentMode to Cleaning');
173
+ this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 2;
174
+ this.state.operationalState = RvcOperationalState.OperationalState.Running;
175
+ this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' };
176
+ return {
177
+ commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
178
+ };
179
+ }
180
+ goHome() {
181
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
182
+ device.log.info('MatterbridgeRvcOperationalStateServer: goHome called setting operational state to Docked and currentMode to Idle');
183
+ this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 1;
184
+ this.state.operationalState = RvcOperationalState.OperationalState.Docked;
185
+ this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' };
186
+ return {
187
+ commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
188
+ };
189
+ }
190
+ }
191
+ function createEndpointActionsClusterServer(endpoint, endpointLists) {
192
+ endpoint.behaviors.require(ActionsServer, {
193
+ actionList: [],
194
+ endpointLists,
195
+ });
196
+ return endpoint;
197
+ }
198
+ if (process.argv.includes('-testRobot')) {
199
+ const matterbridge = await Matterbridge.loadInstance(false);
200
+ matterbridge.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: "debug" });
201
+ matterbridge.environment.vars.set('log.level', MatterLogLevel.DEBUG);
202
+ matterbridge.environment.vars.set('log.format', MatterLogFormat.ANSI);
203
+ matterbridge.environment.vars.set('path.root', 'matterstorage');
204
+ matterbridge.environment.vars.set('runtime.signals', true);
205
+ matterbridge.environment.vars.set('runtime.exitcode', true);
206
+ matterbridge.environment.vars.set('mdns.networkInterface', 'Wi-Fi');
207
+ await matterbridge.startMatterStorage();
208
+ const deviceType = dishwasher;
209
+ const context = await matterbridge.createServerNodeContext('Matterbridge', deviceType.name, DeviceTypeId(deviceType.code), VendorId(0xfff1), 'Matterbridge', 0x8000, 'Matterbridge device');
210
+ const server = (await matterbridge.createServerNode(context));
211
+ const device = new Appliances(deviceType, 'Dish Washer', '97754248654');
212
+ await server.add(device);
213
+ await matterbridge.startServerNode(server);
214
+ logEndpoint(EndpointServer.forEndpoint(server));
215
+ }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "1.1.9",
3
+ "version": "1.2.0-edge.10",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-example-dynamic-platform",
9
- "version": "1.1.9",
9
+ "version": "1.2.0-edge.10",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "node-ansi-logger": "3.0.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "1.1.9",
3
+ "version": "1.2.0-edge.10",
4
4
  "description": "Matterbridge dynamic plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "MIT",
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ // enable isolatedModules just for ts-jest
5
+ "isolatedModules": true
6
+ },
7
+ "include": ["**/*.spec.ts", "**/*.test.ts", "**/__test__/*"]
8
+ }