matterbridge-example-dynamic-platform 1.3.10 → 1.3.12-dev-20250910-60c32aa

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
@@ -23,6 +23,33 @@ If you like this project and find it useful, please consider giving it a star on
23
23
  <img src="bmc-button.svg" alt="Buy me a coffee" width="120">
24
24
  </a>
25
25
 
26
+ ## [1.3.12] - 2025-09-??
27
+
28
+ ### Added
29
+
30
+ - [jest]: Added jest helper module v. 1.0.5.
31
+
32
+ ### Changed
33
+
34
+ - [package]: Updated dependencies.
35
+ - [package]: Updated to Automator v. 2.0.6.
36
+ - [workflows]: Ignore any .md anywhere.
37
+
38
+ <a href="https://www.buymeacoffee.com/luligugithub">
39
+ <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
40
+ </a>
41
+
42
+ ## [1.3.11] - 2025-09-09
43
+
44
+ ### Changed
45
+
46
+ - [package]: Updated dependencies.
47
+ - [platform]: Removed attributes setting when is managed by the Matterbridge servers. This add support for **Apple Home Adaptive Lighting**. See https://github.com/Luligu/matterbridge/discussions/390.
48
+
49
+ <a href="https://www.buymeacoffee.com/luligugithub">
50
+ <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
51
+ </a>
52
+
26
53
  ## [1.3.10] - 2025-09-06
27
54
 
28
55
  ### Added
@@ -0,0 +1,231 @@
1
+ import { rmSync } from 'node:fs';
2
+ import { inspect } from 'node:util';
3
+ import path from 'node:path';
4
+ import { jest } from '@jest/globals';
5
+ import { DeviceTypeId, Endpoint, Environment, MdnsService, ServerNode, ServerNodeStore, VendorId, LogFormat as MatterLogFormat, LogLevel as MatterLogLevel, Lifecycle, } from 'matterbridge/matter';
6
+ import { RootEndpoint, AggregatorEndpoint } from 'matterbridge/matter/endpoints';
7
+ import { AnsiLogger } from 'matterbridge/logger';
8
+ export let loggerLogSpy;
9
+ export let consoleLogSpy;
10
+ export let consoleDebugSpy;
11
+ export let consoleInfoSpy;
12
+ export let consoleWarnSpy;
13
+ export let consoleErrorSpy;
14
+ export function setupTest(name, debug = false) {
15
+ expect(name).toBeDefined();
16
+ expect(typeof name).toBe('string');
17
+ expect(name.length).toBeGreaterThanOrEqual(4);
18
+ rmSync(path.join('jest', name), { recursive: true, force: true });
19
+ if (debug) {
20
+ loggerLogSpy = jest.spyOn(AnsiLogger.prototype, 'log');
21
+ consoleLogSpy = jest.spyOn(console, 'log');
22
+ consoleDebugSpy = jest.spyOn(console, 'debug');
23
+ consoleInfoSpy = jest.spyOn(console, 'info');
24
+ consoleWarnSpy = jest.spyOn(console, 'warn');
25
+ consoleErrorSpy = jest.spyOn(console, 'error');
26
+ }
27
+ else {
28
+ loggerLogSpy = jest.spyOn(AnsiLogger.prototype, 'log').mockImplementation(() => { });
29
+ consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
30
+ consoleDebugSpy = jest.spyOn(console, 'debug').mockImplementation(() => { });
31
+ consoleInfoSpy = jest.spyOn(console, 'info').mockImplementation(() => { });
32
+ consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { });
33
+ consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
34
+ }
35
+ }
36
+ export function setDebug(debug) {
37
+ if (debug) {
38
+ loggerLogSpy.mockRestore();
39
+ consoleLogSpy.mockRestore();
40
+ consoleDebugSpy.mockRestore();
41
+ consoleInfoSpy.mockRestore();
42
+ consoleWarnSpy.mockRestore();
43
+ consoleErrorSpy.mockRestore();
44
+ loggerLogSpy = jest.spyOn(AnsiLogger.prototype, 'log');
45
+ consoleLogSpy = jest.spyOn(console, 'log');
46
+ consoleDebugSpy = jest.spyOn(console, 'debug');
47
+ consoleInfoSpy = jest.spyOn(console, 'info');
48
+ consoleWarnSpy = jest.spyOn(console, 'warn');
49
+ consoleErrorSpy = jest.spyOn(console, 'error');
50
+ }
51
+ else {
52
+ loggerLogSpy = jest.spyOn(AnsiLogger.prototype, 'log').mockImplementation(() => { });
53
+ consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
54
+ consoleDebugSpy = jest.spyOn(console, 'debug').mockImplementation(() => { });
55
+ consoleInfoSpy = jest.spyOn(console, 'info').mockImplementation(() => { });
56
+ consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { });
57
+ consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
58
+ }
59
+ }
60
+ export function createTestEnvironment(homeDir) {
61
+ expect(homeDir).toBeDefined();
62
+ expect(typeof homeDir).toBe('string');
63
+ expect(homeDir.length).toBeGreaterThanOrEqual(4);
64
+ rmSync(homeDir, { recursive: true, force: true });
65
+ const environment = Environment.default;
66
+ environment.vars.set('log.level', MatterLogLevel.DEBUG);
67
+ environment.vars.set('log.format', MatterLogFormat.ANSI);
68
+ environment.vars.set('path.root', homeDir);
69
+ environment.vars.set('runtime.signals', false);
70
+ environment.vars.set('runtime.exitcode', false);
71
+ return environment;
72
+ }
73
+ export async function flushAsync(ticks = 3, microTurns = 10, pause = 100) {
74
+ for (let i = 0; i < ticks; i++)
75
+ await new Promise((resolve) => setImmediate(resolve));
76
+ for (let i = 0; i < microTurns; i++)
77
+ await Promise.resolve();
78
+ if (pause)
79
+ await new Promise((resolve) => setTimeout(resolve, pause));
80
+ }
81
+ export async function flushAllEndpointNumberPersistence(targetServer, rounds = 2) {
82
+ const nodeStore = targetServer.env.get(ServerNodeStore);
83
+ for (let i = 0; i < rounds; i++) {
84
+ await new Promise((resolve) => setImmediate(resolve));
85
+ await nodeStore.endpointStores.close();
86
+ }
87
+ }
88
+ function collectAllEndpoints(root) {
89
+ const list = [];
90
+ const walk = (ep) => {
91
+ list.push(ep);
92
+ if (ep.parts) {
93
+ for (const child of ep.parts) {
94
+ walk(child);
95
+ }
96
+ }
97
+ };
98
+ walk(root);
99
+ return list;
100
+ }
101
+ export async function assertAllEndpointNumbersPersisted(targetServer) {
102
+ const nodeStore = targetServer.env.get(ServerNodeStore);
103
+ await nodeStore.endpointStores.close();
104
+ const all = collectAllEndpoints(targetServer);
105
+ for (const ep of all) {
106
+ const store = nodeStore.storeForEndpoint(ep);
107
+ if (ep.maybeNumber === 0) {
108
+ expect(store.number ?? 0).toBe(0);
109
+ }
110
+ else {
111
+ expect(store.number).toBeGreaterThan(0);
112
+ }
113
+ }
114
+ return all.length;
115
+ }
116
+ export async function startServerNode(name, port) {
117
+ const server = await ServerNode.create({
118
+ id: name + 'ServerNode',
119
+ productDescription: {
120
+ name: name + 'ServerNode',
121
+ deviceType: DeviceTypeId(RootEndpoint.deviceType),
122
+ vendorId: VendorId(0xfff1),
123
+ productId: 0x8000,
124
+ },
125
+ basicInformation: {
126
+ vendorId: VendorId(0xfff1),
127
+ vendorName: 'Matterbridge',
128
+ productId: 0x8000,
129
+ productName: 'Matterbridge ' + name,
130
+ nodeLabel: name + 'ServerNode',
131
+ hardwareVersion: 1,
132
+ softwareVersion: 1,
133
+ reachable: true,
134
+ },
135
+ network: {
136
+ port,
137
+ },
138
+ });
139
+ expect(server).toBeDefined();
140
+ expect(server.lifecycle.isReady).toBeTruthy();
141
+ const aggregator = new Endpoint(AggregatorEndpoint, {
142
+ id: name + 'AggregatorNode',
143
+ });
144
+ expect(aggregator).toBeDefined();
145
+ await server.add(aggregator);
146
+ expect(server.parts.has(aggregator.id)).toBeTruthy();
147
+ expect(server.parts.has(aggregator)).toBeTruthy();
148
+ expect(aggregator.lifecycle.isReady).toBeTruthy();
149
+ expect(server.lifecycle.isOnline).toBeFalsy();
150
+ await new Promise((resolve) => {
151
+ server.lifecycle.online.on(async () => {
152
+ resolve();
153
+ });
154
+ server.start();
155
+ });
156
+ expect(server.lifecycle.isReady).toBeTruthy();
157
+ expect(server.lifecycle.isOnline).toBeTruthy();
158
+ expect(server.lifecycle.isCommissioned).toBeFalsy();
159
+ expect(server.lifecycle.isPartsReady).toBeTruthy();
160
+ expect(server.lifecycle.hasId).toBeTruthy();
161
+ expect(server.lifecycle.hasNumber).toBeTruthy();
162
+ expect(aggregator.lifecycle.isReady).toBeTruthy();
163
+ expect(aggregator.lifecycle.isInstalled).toBeTruthy();
164
+ expect(aggregator.lifecycle.isPartsReady).toBeTruthy();
165
+ expect(aggregator.lifecycle.hasId).toBeTruthy();
166
+ expect(aggregator.lifecycle.hasNumber).toBeTruthy();
167
+ await flushAsync();
168
+ return [server, aggregator];
169
+ }
170
+ export async function stopServerNode(server) {
171
+ await flushAllEndpointNumberPersistence(server);
172
+ await assertAllEndpointNumbersPersisted(server);
173
+ expect(server).toBeDefined();
174
+ expect(server.lifecycle.isReady).toBeTruthy();
175
+ expect(server.lifecycle.isOnline).toBeTruthy();
176
+ await server.close();
177
+ expect(server.lifecycle.isReady).toBeTruthy();
178
+ expect(server.lifecycle.isOnline).toBeFalsy();
179
+ await server.env.get(MdnsService)[Symbol.asyncDispose]();
180
+ await flushAsync();
181
+ }
182
+ export async function addDevice(owner, device, pause = 10) {
183
+ expect(owner).toBeDefined();
184
+ expect(device).toBeDefined();
185
+ expect(owner.lifecycle.isReady).toBeTruthy();
186
+ expect(owner.construction.status).toBe(Lifecycle.Status.Active);
187
+ expect(owner.lifecycle.isPartsReady).toBeTruthy();
188
+ try {
189
+ await owner.add(device);
190
+ }
191
+ catch (error) {
192
+ const errorMessage = error instanceof Error ? error.message : error;
193
+ const errorInspect = inspect(error, { depth: 10 });
194
+ console.error(`Error adding device ${device.maybeId}.${device.maybeNumber}: ${errorMessage}\nstack: ${errorInspect}`);
195
+ return false;
196
+ }
197
+ expect(owner.parts.has(device)).toBeTruthy();
198
+ expect(owner.lifecycle.isPartsReady).toBeTruthy();
199
+ expect(device.lifecycle.isReady).toBeTruthy();
200
+ expect(device.lifecycle.isInstalled).toBeTruthy();
201
+ expect(device.lifecycle.hasId).toBeTruthy();
202
+ expect(device.lifecycle.hasNumber).toBeTruthy();
203
+ expect(device.construction.status).toBe(Lifecycle.Status.Active);
204
+ await flushAsync(1, 1, pause);
205
+ return true;
206
+ }
207
+ export async function deleteDevice(owner, device, pause = 10) {
208
+ expect(owner).toBeDefined();
209
+ expect(device).toBeDefined();
210
+ expect(owner.lifecycle.isReady).toBeTruthy();
211
+ expect(owner.construction.status).toBe(Lifecycle.Status.Active);
212
+ expect(owner.lifecycle.isPartsReady).toBeTruthy();
213
+ try {
214
+ await device.delete();
215
+ }
216
+ catch (error) {
217
+ const errorMessage = error instanceof Error ? error.message : error;
218
+ const errorInspect = inspect(error, { depth: 10 });
219
+ console.error(`Error deleting device ${device.maybeId}.${device.maybeNumber}: ${errorMessage}\nstack: ${errorInspect}`);
220
+ return false;
221
+ }
222
+ expect(owner.parts.has(device)).toBeFalsy();
223
+ expect(owner.lifecycle.isPartsReady).toBeTruthy();
224
+ expect(device.lifecycle.isReady).toBeFalsy();
225
+ expect(device.lifecycle.isInstalled).toBeFalsy();
226
+ expect(device.lifecycle.hasId).toBeTruthy();
227
+ expect(device.lifecycle.hasNumber).toBeTruthy();
228
+ expect(device.construction.status).toBe(Lifecycle.Status.Destroyed);
229
+ await flushAsync(1, 1, pause);
230
+ return true;
231
+ }
package/dist/platform.js CHANGED
@@ -202,11 +202,9 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
202
202
  this.log.info(`Command identify called identifyTime:${identifyTime}`);
203
203
  });
204
204
  this.switch?.addCommandHandler('on', async () => {
205
- await this.switch?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.switch.log);
206
205
  this.switch?.log.info('Command on called');
207
206
  });
208
207
  this.switch?.addCommandHandler('off', async () => {
209
- await this.switch?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.switch.log);
210
208
  this.switch?.log.info('Command off called');
211
209
  });
212
210
  this.mountedOnOffSwitch = new MatterbridgeEndpoint([onOffMountedSwitch, bridgedNode, powerSource], { uniqueStorageKey: 'OnOffMountedSwitch' }, this.config.debug)
@@ -220,11 +218,9 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
220
218
  this.mountedOnOffSwitch?.log.info(`Command identify called identifyTime:${identifyTime}`);
221
219
  });
222
220
  this.mountedOnOffSwitch?.addCommandHandler('on', async () => {
223
- await this.mountedOnOffSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.mountedOnOffSwitch.log);
224
221
  this.mountedOnOffSwitch?.log.info('Command on called');
225
222
  });
226
223
  this.mountedOnOffSwitch?.addCommandHandler('off', async () => {
227
- await this.mountedOnOffSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.mountedOnOffSwitch.log);
228
224
  this.mountedOnOffSwitch?.log.info('Command off called');
229
225
  });
230
226
  this.mountedDimmerSwitch = new MatterbridgeEndpoint([dimmableMountedSwitch, bridgedNode, powerSource], { uniqueStorageKey: 'DimmerMountedSwitch' }, this.config.debug)
@@ -240,19 +236,15 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
240
236
  this.mountedDimmerSwitch?.log.info(`Command identify called identifyTime:${identifyTime}`);
241
237
  });
242
238
  this.mountedDimmerSwitch?.addCommandHandler('on', async () => {
243
- await this.mountedDimmerSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.mountedDimmerSwitch.log);
244
239
  this.mountedDimmerSwitch?.log.info('Command on called');
245
240
  });
246
241
  this.mountedDimmerSwitch?.addCommandHandler('off', async () => {
247
- await this.mountedDimmerSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.mountedDimmerSwitch.log);
248
242
  this.mountedDimmerSwitch?.log.info('Command off called');
249
243
  });
250
244
  this.mountedDimmerSwitch?.addCommandHandler('moveToLevel', async ({ request: { level } }) => {
251
- await this.mountedDimmerSwitch?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.mountedDimmerSwitch.log);
252
245
  this.mountedDimmerSwitch?.log.debug(`Command moveToLevel called request: ${level}`);
253
246
  });
254
247
  this.mountedDimmerSwitch?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => {
255
- await this.mountedDimmerSwitch?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.mountedDimmerSwitch.log);
256
248
  this.mountedDimmerSwitch?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`);
257
249
  });
258
250
  this.lightOnOff = new MatterbridgeEndpoint([onOffLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (on/off)' }, this.config.debug)
@@ -266,11 +258,9 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
266
258
  this.lightOnOff?.log.info(`Command identify called identifyTime:${identifyTime}`);
267
259
  });
268
260
  this.lightOnOff?.addCommandHandler('on', async () => {
269
- await this.lightOnOff?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightOnOff?.log);
270
261
  this.lightOnOff?.log.info('Command on called');
271
262
  });
272
263
  this.lightOnOff?.addCommandHandler('off', async () => {
273
- await this.lightOnOff?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightOnOff?.log);
274
264
  this.lightOnOff?.log.info('Command off called');
275
265
  });
276
266
  this.dimmer = new MatterbridgeEndpoint([dimmableLight, bridgedNode, powerSource], { uniqueStorageKey: 'Dimmer' }, this.config.debug)
@@ -285,19 +275,15 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
285
275
  this.dimmer?.log.info(`Command identify called identifyTime:${identifyTime}`);
286
276
  });
287
277
  this.dimmer?.addCommandHandler('on', async () => {
288
- await this.dimmer?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.dimmer.log);
289
278
  this.dimmer?.log.info('Command on called');
290
279
  });
291
280
  this.dimmer?.addCommandHandler('off', async () => {
292
- await this.dimmer?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.dimmer.log);
293
281
  this.dimmer?.log.info('Command off called');
294
282
  });
295
283
  this.dimmer?.addCommandHandler('moveToLevel', async ({ request: { level } }) => {
296
- await this.dimmer?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.dimmer.log);
297
284
  this.dimmer?.log.debug(`Command moveToLevel called request: ${level}`);
298
285
  });
299
286
  this.dimmer?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => {
300
- await this.dimmer?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.dimmer.log);
301
287
  this.dimmer?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`);
302
288
  });
303
289
  this.light = new MatterbridgeEndpoint([extendedColorLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (XY, HS and CT)' }, this.config.debug)
@@ -313,41 +299,30 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
313
299
  this.light?.log.info(`Command identify called identifyTime:${identifyTime}`);
314
300
  });
315
301
  this.light?.addCommandHandler('on', async () => {
316
- await this.light?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.light?.log);
317
302
  this.light?.log.info('Command on called');
318
303
  });
319
304
  this.light?.addCommandHandler('off', async () => {
320
- await this.light?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.light?.log);
321
305
  this.light?.log.info('Command off called');
322
306
  });
323
307
  this.light?.addCommandHandler('moveToLevel', async ({ request: { level } }) => {
324
- await this.light?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.light?.log);
325
308
  this.light?.log.debug(`Command moveToLevel called request: ${level}`);
326
309
  });
327
310
  this.light?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => {
328
- await this.light?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.light?.log);
329
311
  this.light?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`);
330
312
  });
331
313
  this.light?.addCommandHandler('moveToColor', async ({ request: { colorX, colorY } }) => {
332
- await this.light?.setAttribute(ColorControl.Cluster.id, 'currentX', colorX, this.light?.log);
333
- await this.light?.setAttribute(ColorControl.Cluster.id, 'currentY', colorY, this.light?.log);
334
314
  this.light?.log.debug(`Command moveToColor called request: X ${colorX / 65536} Y ${colorY / 65536}`);
335
315
  });
336
316
  this.light?.addCommandHandler('moveToHueAndSaturation', async ({ request: { hue, saturation } }) => {
337
- await this.light?.setAttribute(ColorControl.Cluster.id, 'currentHue', hue, this.light?.log);
338
- await this.light?.setAttribute(ColorControl.Cluster.id, 'currentSaturation', saturation, this.light?.log);
339
317
  this.light?.log.debug(`Command moveToHueAndSaturation called request: hue ${hue} saturation ${saturation}`);
340
318
  });
341
319
  this.light?.addCommandHandler('moveToHue', async ({ request: { hue } }) => {
342
- await this.light?.setAttribute(ColorControl.Cluster.id, 'currentHue', hue, this.light?.log);
343
320
  this.light?.log.debug(`Command moveToHue called request: hue ${hue}`);
344
321
  });
345
322
  this.light?.addCommandHandler('moveToSaturation', async ({ request: { saturation } }) => {
346
- await this.light?.setAttribute(ColorControl.Cluster.id, 'currentSaturation', saturation, this.light?.log);
347
323
  this.light?.log.debug(`Command moveToSaturation called request: saturation ${saturation}}`);
348
324
  });
349
325
  this.light?.addCommandHandler('moveToColorTemperature', async ({ request: { colorTemperatureMireds } }) => {
350
- await this.light?.setAttribute(ColorControl.Cluster.id, 'colorTemperatureMireds', colorTemperatureMireds, this.light?.log);
351
326
  this.light?.log.debug(`Command moveToColorTemperature called request: ${colorTemperatureMireds}`);
352
327
  });
353
328
  this.lightHS = new MatterbridgeEndpoint([colorTemperatureLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (HS, CT)' }, this.config.debug)
@@ -363,36 +338,27 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
363
338
  this.lightHS?.log.info(`Command identify called identifyTime:${identifyTime}`);
364
339
  });
365
340
  this.lightHS?.addCommandHandler('on', async () => {
366
- await this.lightHS?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightHS?.log);
367
341
  this.lightHS?.log.info('Command on called');
368
342
  });
369
343
  this.lightHS?.addCommandHandler('off', async () => {
370
- await this.lightHS?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightHS?.log);
371
344
  this.lightHS?.log.info('Command off called');
372
345
  });
373
346
  this.lightHS?.addCommandHandler('moveToLevel', async ({ request: { level } }) => {
374
- await this.lightHS?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightHS?.log);
375
347
  this.lightHS?.log.debug(`Command moveToLevel called request: ${level}`);
376
348
  });
377
349
  this.lightHS?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => {
378
- await this.lightHS?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightHS?.log);
379
350
  this.lightHS?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`);
380
351
  });
381
352
  this.lightHS?.addCommandHandler('moveToHueAndSaturation', async ({ request: { hue, saturation } }) => {
382
- await this.lightHS?.setAttribute(ColorControl.Cluster.id, 'currentHue', hue, this.lightHS?.log);
383
- await this.lightHS?.setAttribute(ColorControl.Cluster.id, 'currentSaturation', saturation, this.lightHS?.log);
384
353
  this.lightHS?.log.debug(`Command moveToHueAndSaturation called request: hue ${hue} saturation ${saturation}}`);
385
354
  });
386
355
  this.lightHS?.addCommandHandler('moveToHue', async ({ request: { hue } }) => {
387
- await this.lightHS?.setAttribute(ColorControl.Cluster.id, 'currentHue', hue, this.lightHS?.log);
388
356
  this.lightHS?.log.debug(`Command moveToHue called request: hue ${hue}`);
389
357
  });
390
358
  this.lightHS?.addCommandHandler('moveToSaturation', async ({ request: { saturation } }) => {
391
- await this.lightHS?.setAttribute(ColorControl.Cluster.id, 'currentSaturation', saturation, this.lightHS?.log);
392
359
  this.lightHS?.log.debug(`Command moveToSaturation called request: saturation ${saturation}`);
393
360
  });
394
361
  this.lightHS?.addCommandHandler('moveToColorTemperature', async ({ request: { colorTemperatureMireds } }) => {
395
- await this.lightHS?.setAttribute(ColorControl.Cluster.id, 'colorTemperatureMireds', colorTemperatureMireds, this.lightHS?.log);
396
362
  this.lightHS?.log.debug(`Command moveToColorTemperature called request: ${colorTemperatureMireds}`);
397
363
  });
398
364
  this.lightXY = new MatterbridgeEndpoint([extendedColorLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (XY, CT)' }, this.config.debug)
@@ -408,28 +374,21 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
408
374
  this.lightXY?.log.info(`Command identify called identifyTime:${identifyTime}`);
409
375
  });
410
376
  this.lightXY?.addCommandHandler('on', async () => {
411
- await this.lightXY?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightXY?.log);
412
377
  this.lightXY?.log.info('Command on called');
413
378
  });
414
379
  this.lightXY?.addCommandHandler('off', async () => {
415
- await this.lightXY?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightXY?.log);
416
380
  this.lightXY?.log.info('Command off called');
417
381
  });
418
382
  this.lightXY?.addCommandHandler('moveToLevel', async ({ request: { level } }) => {
419
- await this.lightXY?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightXY?.log);
420
383
  this.lightXY?.log.debug(`Command moveToLevel called request: ${level}`);
421
384
  });
422
385
  this.lightXY?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => {
423
- await this.lightXY?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightXY?.log);
424
386
  this.lightXY?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`);
425
387
  });
426
388
  this.lightXY?.addCommandHandler('moveToColor', async ({ request: { colorX, colorY } }) => {
427
- await this.lightXY?.setAttribute(ColorControl.Cluster.id, 'currentX', colorX, this.lightXY?.log);
428
- await this.lightXY?.setAttribute(ColorControl.Cluster.id, 'currentY', colorY, this.lightXY?.log);
429
389
  this.lightXY?.log.debug(`Command moveToColor called request: X ${colorX / 65536} Y ${colorY / 65536}`);
430
390
  });
431
391
  this.lightXY?.addCommandHandler('moveToColorTemperature', async ({ request: { colorTemperatureMireds } }) => {
432
- await this.lightXY?.setAttribute(ColorControl.Cluster.id, 'colorTemperatureMireds', colorTemperatureMireds, this.lightXY?.log);
433
392
  this.lightXY?.log.debug(`Command moveToColorTemperature called request: ${colorTemperatureMireds}`);
434
393
  });
435
394
  this.lightCT = new MatterbridgeEndpoint([colorTemperatureLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (CT)' }, this.config.debug)
@@ -445,23 +404,18 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
445
404
  this.lightCT?.log.info(`Command identify called identifyTime:${identifyTime}`);
446
405
  });
447
406
  this.lightCT?.addCommandHandler('on', async () => {
448
- await this.lightCT?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightCT?.log);
449
407
  this.lightCT?.log.info('Command on called');
450
408
  });
451
409
  this.lightCT?.addCommandHandler('off', async () => {
452
- await this.lightCT?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightCT?.log);
453
410
  this.lightCT?.log.info('Command off called');
454
411
  });
455
412
  this.lightCT?.addCommandHandler('moveToLevel', async ({ request: { level } }) => {
456
- await this.lightCT?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightCT?.log);
457
413
  this.lightCT?.log.debug(`Command moveToLevel called request: ${level}`);
458
414
  });
459
415
  this.lightCT?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => {
460
- await this.lightCT?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightCT?.log);
461
416
  this.lightCT?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`);
462
417
  });
463
418
  this.lightCT?.addCommandHandler('moveToColorTemperature', async ({ request: { colorTemperatureMireds } }) => {
464
- await this.lightCT?.setAttribute(ColorControl.Cluster.id, 'colorTemperatureMireds', colorTemperatureMireds, this.lightCT?.log);
465
419
  this.lightCT?.log.debug(`Command moveToColorTemperature called request: ${colorTemperatureMireds}`);
466
420
  });
467
421
  this.outlet = new MatterbridgeEndpoint([onOffOutlet, bridgedNode, powerSource], { uniqueStorageKey: 'Outlet' }, this.config.debug)
@@ -475,11 +429,9 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
475
429
  this.outlet?.log.info(`Command identify called identifyTime:${identifyTime}`);
476
430
  });
477
431
  this.outlet?.addCommandHandler('on', async () => {
478
- await this.outlet?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.outlet?.log);
479
432
  this.outlet?.log.info('Command on called');
480
433
  });
481
434
  this.outlet?.addCommandHandler('off', async () => {
482
- await this.outlet?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.outlet?.log);
483
435
  this.outlet?.log.info('Command off called');
484
436
  });
485
437
  this.coverLift = new MatterbridgeEndpoint([coverDevice, bridgedNode, powerSource], { uniqueStorageKey: 'CoverLift' }, this.config.debug)
@@ -549,11 +501,9 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
549
501
  this.lock?.log.info(`Command identify called identifyTime:${identifyTime}`);
550
502
  });
551
503
  this.lock?.addCommandHandler('lockDoor', async () => {
552
- await this.lock?.setAttribute(DoorLock.Cluster.id, 'lockState', DoorLock.LockState.Locked, this.lock?.log);
553
504
  this.lock?.log.info('Command lockDoor called');
554
505
  });
555
506
  this.lock?.addCommandHandler('unlockDoor', async () => {
556
- await this.lock?.setAttribute(DoorLock.Cluster.id, 'lockState', DoorLock.LockState.Unlocked, this.lock?.log);
557
507
  this.lock?.log.info('Command unlockDoor called');
558
508
  });
559
509
  this.thermoAuto = new MatterbridgeEndpoint([thermostatDevice, bridgedNode, powerSource], { uniqueStorageKey: 'Thermostat (AutoMode)' }, this.config.debug)
@@ -584,16 +534,6 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
584
534
  this.thermoAuto?.addCommandHandler('setpointRaiseLower', async ({ request: { mode, amount } }) => {
585
535
  const lookupSetpointAdjustMode = ['Heat', 'Cool', 'Both'];
586
536
  this.thermoAuto?.log.info(`Command setpointRaiseLower called with mode: ${lookupSetpointAdjustMode[mode]} amount: ${amount / 10}`);
587
- if (mode === Thermostat.SetpointRaiseLowerMode.Heat || mode === Thermostat.SetpointRaiseLowerMode.Both) {
588
- const setpoint = this.thermoAuto?.getAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', this.thermoAuto?.log) / 100 + amount / 10;
589
- await this.thermoAuto?.setAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', setpoint * 100, this.thermoAuto?.log);
590
- this.thermoAuto?.log.info('Set occupiedHeatingSetpoint:', setpoint);
591
- }
592
- if (mode === Thermostat.SetpointRaiseLowerMode.Cool || mode === Thermostat.SetpointRaiseLowerMode.Both) {
593
- const setpoint = this.thermoAuto?.getAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', this.thermoAuto?.log) / 100 + amount / 10;
594
- await this.thermoAuto?.setAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', setpoint * 100, this.thermoAuto?.log);
595
- this.thermoAuto?.log.info('Set occupiedCoolingSetpoint:', setpoint);
596
- }
597
537
  });
598
538
  await this.thermoAuto?.subscribeAttribute(ThermostatCluster.id, 'systemMode', (value) => {
599
539
  const lookupSystemMode = ['Off', 'Auto', '', 'Cool', 'Heat', 'EmergencyHeat', 'Precooling', 'FanOnly', 'Dry', 'Sleep'];
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "1.3.10",
3
+ "version": "1.3.12-dev-20250910-60c32aa",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-example-dynamic-platform",
9
- "version": "1.3.10",
9
+ "version": "1.3.12-dev-20250910-60c32aa",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "node-ansi-logger": "3.1.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "1.3.10",
3
+ "version": "1.3.12-dev-20250910-60c32aa",
4
4
  "description": "Matterbridge dynamic plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",