matterbridge-example-dynamic-platform 1.4.0 → 2.0.0

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
@@ -1,3 +1,5 @@
1
+ <!-- markdownlint-disable MD024 MD033 -->
2
+
1
3
  # <img src="matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge dynamic platform example plugin changelog
2
4
 
3
5
  [![npm version](https://img.shields.io/npm/v/matterbridge-example-dynamic-platform.svg)](https://www.npmjs.com/package/matterbridge-example-dynamic-platform)
@@ -17,31 +19,46 @@
17
19
 
18
20
  All notable changes to this project will be documented in this file.
19
21
 
20
- If you like this project and find it useful, please consider giving it a star on GitHub at https://github.com/Luligu/matterbridge-example-dynamic-platform and sponsoring it.
22
+ If you like this project and find it useful, please consider giving it a star on GitHub at <https://github.com/Luligu/matterbridge-example-dynamic-platform> and sponsoring it.
21
23
 
22
24
  <a href="https://www.buymeacoffee.com/luligugithub">
23
25
  <img src="bmc-button.svg" alt="Buy me a coffee" width="120">
24
26
  </a>
25
27
 
28
+ ## [2.0.0] - 2025-10-24
29
+
30
+ ### Changed
31
+
32
+ - [package]: Bumped platform to v. 2.0.0.
33
+ - [package]: Updated dependencies.
34
+ - [package]: Bumped package to automator version 2.0.9
35
+ - [jest]: Updated jestHelpers to v. 1.0.9.
36
+
37
+ <a href="https://www.buymeacoffee.com/luligugithub">
38
+ <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
39
+ </a>
40
+
26
41
  ## [1.4.0] - 2025-10-15
27
42
 
28
43
  ### Added
29
44
 
30
45
  - [jest]: Added jest helper module v. 1.0.6.
31
46
  - [platform]: Added a thermostat with auto mode and occupancy and outdoorTemperature.
32
- - [platform]: Added animation to Heat pump. Thanks Ludovic BOUÉ (https://github.com/Luligu/matterbridge-example-dynamic-platform/pull/35).
33
- - [platform]: Added Toggle thermostatRunningState heat and cool. Thanks Ludovic BOUÉ (https://github.com/Luligu/matterbridge-example-dynamic-platform/pull/36).
47
+ - [platform]: Added animation to Heat pump. Thanks Ludovic BOUÉ (<https://github.com/Luligu/matterbridge-example-dynamic-platform/pull/35>).
48
+ - [platform]: Added Toggle thermostatRunningState heat and cool. Thanks Ludovic BOUÉ (<https://github.com/Luligu/matterbridge-example-dynamic-platform/pull/36>).
34
49
 
35
50
  ### Changed
36
51
 
37
52
  - [package]: Require matterbridge 3.3.0.
38
53
  - [index]: Updated to new signature PlatformMatterbridge.
39
54
  - [platform]: Updated to new signature PlatformMatterbridge.
40
- - [platform]: Added whiteList and blackList.
41
- - [platform]: Typed TestPlatformConfig.
42
55
  - [package]: Updated dependencies.
43
56
  - [package]: Bumped package to automator version 2.0.7
44
- - [workflows]: Ignore any .md anywhere in all workflows.
57
+ - [workflows]: Ignore any .md in build.yaml.
58
+ - [workflows]: Ignore any .md in codeql.yaml.
59
+ - [workflows]: Ignore any .md in codecov.yaml.
60
+ - [template]: Updated bug_report.md.
61
+ - [jest]: Updated jestHelpers to v. 1.0.6.
45
62
 
46
63
  <a href="https://www.buymeacoffee.com/luligugithub">
47
64
  <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
@@ -52,7 +69,7 @@ If you like this project and find it useful, please consider giving it a star on
52
69
  ### Added
53
70
 
54
71
  - [jest]: Added jest helper module v. 1.0.5.
55
- - [rvc]: Added DustBinFull to the Rvc animation (https://github.com/Luligu/matterbridge-example-dynamic-platform/pull/31).
72
+ - [rvc]: Added DustBinFull to the Rvc animation (<https://github.com/Luligu/matterbridge-example-dynamic-platform/pull/31>).
56
73
 
57
74
  ### Changed
58
75
 
@@ -69,7 +86,7 @@ If you like this project and find it useful, please consider giving it a star on
69
86
  ### Changed
70
87
 
71
88
  - [package]: Updated dependencies.
72
- - [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.
89
+ - [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>.
73
90
 
74
91
  <a href="https://www.buymeacoffee.com/luligugithub">
75
92
  <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
@@ -333,7 +350,7 @@ If you like this project and find it useful, please consider giving it a star on
333
350
 
334
351
  ### Added
335
352
 
336
- - [platform]: Added Robot Vacuum Cleaner device (supported by SmartThings, Alexa, Home Assistant and partially by Apple Home). Read carefully the readme please and also https://github.com/Luligu/matterbridge/discussions/264.
353
+ - [platform]: Added Robot Vacuum Cleaner device (supported by SmartThings, Alexa, Home Assistant and partially by Apple Home). Read carefully the readme please and also <https://github.com/Luligu/matterbridge/discussions/264>.
337
354
  - [platform]: Added OnOff Mounted Switch device (supported by SmartThings, Alexa, Home Assistant).
338
355
  - [platform]: Added Dimmer Mounted Switch device (supported by SmartThings, Alexa, Home Assistant).
339
356
  - [platform]: Added Laundry Washer device (supported by SmartThings, Alexa and Home Assistant).
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ <!-- markdownlint-disable MD033 -->
2
+
1
3
  # <img src="matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge dynamic platform example plugin
2
4
 
3
5
  [![npm version](https://img.shields.io/npm/v/matterbridge-example-dynamic-platform.svg)](https://www.npmjs.com/package/matterbridge-example-dynamic-platform)
@@ -61,7 +63,7 @@ It exposes 58 virtual devices:
61
63
  - an airQuality device with all concentration measurements clusters (supported by Apple Home with the concentration measurements from version 18.5)
62
64
  - a momentary switch composed by three switches with Single Double Long (tagged with One Two Three and Top Middle Bottom) and three switches with Single only.
63
65
  - a latching switch
64
- - a Robot Vacuum Cleaner device (supported by SmartThings, Alexa, Home Assistant and partially by Apple Home). Read also https://github.com/Luligu/matterbridge/discussions/264.
66
+ - a Robot Vacuum Cleaner device (supported by SmartThings, Alexa, Home Assistant and partially by Apple Home). Read also <https://github.com/Luligu/matterbridge/discussions/264>.
65
67
  - a onOff Mounted Switch device (supported by SmartThings, Alexa, Home Assistant)
66
68
  - a dimmer Mounted Switch device (supported by SmartThings, Alexa, Home Assistant)
67
69
  - a laundry Washer device (supported by SmartThings, Alexa and Home Assistant)
@@ -83,7 +85,7 @@ All these devices continuously change state and position. The plugin also shows
83
85
 
84
86
  If you want to write your plugin, the easiest way to start create a new plugin is to clone the [Matterbridge Plugin Template](https://github.com/Luligu/matterbridge-plugin-template) which has **Dev Container support for instant development environment** and all tools and extensions (like Node.js, npm, TypeScript, ESLint, Prettier, Jest and Vitest) already loaded and configured.
85
87
 
86
- If you like this project and find it useful, please consider giving it a star on GitHub at https://github.com/Luligu/matterbridge-example-dynamic-platform and sponsoring it.
88
+ If you like this project and find it useful, please consider giving it a star on GitHub at <https://github.com/Luligu/matterbridge-example-dynamic-platform> and sponsoring it.
87
89
 
88
90
  <a href="https://www.buymeacoffee.com/luligugithub">
89
91
  <img src="bmc-button.svg" alt="Buy me a coffee" width="120">
@@ -19,6 +19,9 @@ function matterToLux(value) {
19
19
  const lux = Math.pow(10, v / 10000);
20
20
  return Math.round(lux < 0 ? 0 : lux);
21
21
  }
22
+ export default function initializePlugin(matterbridge, log, config) {
23
+ return new ExampleMatterbridgeDynamicPlatform(matterbridge, log, config);
24
+ }
22
25
  export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatform {
23
26
  config;
24
27
  door;
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "1.4.0",
3
+ "version": "2.0.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-example-dynamic-platform",
9
- "version": "1.4.0",
9
+ "version": "2.0.0",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "node-ansi-logger": "3.1.1",
13
13
  "node-persist-manager": "2.0.0"
14
14
  },
15
15
  "engines": {
16
- "node": ">=18.0.0 <19.0.0 || >=20.0.0 <21.0.0 || >=22.0.0 <23.0.0 || >=24.0.0 <25.0.0"
16
+ "node": ">=20.0.0 <21.0.0 || >=22.0.0 <23.0.0 || >=24.0.0 <25.0.0"
17
17
  },
18
18
  "funding": {
19
19
  "type": "buymeacoffee",
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "1.4.0",
3
+ "version": "2.0.0",
4
4
  "description": "Matterbridge dynamic plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",
7
7
  "homepage": "https://www.npmjs.com/package/matterbridge-example-dynamic-platform",
8
8
  "type": "module",
9
- "main": "dist/index.js",
9
+ "main": "dist/module.js",
10
10
  "repository": {
11
11
  "type": "git",
12
12
  "url": "git+https://github.com/Luligu/matterbridge-example-dynamic-platform.git"
@@ -31,7 +31,7 @@
31
31
  "virtual devices"
32
32
  ],
33
33
  "engines": {
34
- "node": ">=18.0.0 <19.0.0 || >=20.0.0 <21.0.0 || >=22.0.0 <23.0.0 || >=24.0.0 <25.0.0"
34
+ "node": ">=20.0.0 <21.0.0 || >=22.0.0 <23.0.0 || >=24.0.0 <25.0.0"
35
35
  },
36
36
  "dependencies": {
37
37
  "node-ansi-logger": "3.1.1",
package/dist/index.js DELETED
@@ -1,4 +0,0 @@
1
- import { ExampleMatterbridgeDynamicPlatform } from './platform.js';
2
- export default function initializePlugin(matterbridge, log, config) {
3
- return new ExampleMatterbridgeDynamicPlatform(matterbridge, log, config);
4
- }
@@ -1,231 +0,0 @@
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(undefined, undefined, 200);
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(undefined, undefined, 200);
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
- }