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 +27 -0
- package/dist/jestHelpers.js +231 -0
- package/dist/platform.js +0 -60
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
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'];
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge-example-dynamic-platform",
|
|
3
|
-
"version": "1.3.
|
|
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.
|
|
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",
|