matterbridge-example-dynamic-platform 3.0.2-dev-20260803-06c5b3e → 3.0.2-dev-20260808-eeaaae3

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
@@ -31,21 +31,24 @@ If you like this project and find it useful, please consider giving it a star on
31
31
 
32
32
  ### Breaking changes
33
33
 
34
- - [matterbridge]: Require matterbridge v.3.10.3 with matter v.1.6.0.
34
+ - [matterbridge]: Require matterbridge v.3.10.5 with matter v.1.6.0.
35
35
 
36
36
  ### Added
37
37
 
38
- - [platform]: Add `closureGarageDoor` device.
39
38
  - [chip]: Add chip-test toolchain agents instruction and chip-test runner.
39
+ - [chip]: Add `chipTests.json`/`chipTests.md` with the generic device basic composition, conformance and default warnings tests, and document known CHIP test suite gaps.
40
40
  - [frontend]: Add plugin-frontend agents instructions.
41
+ - [platform]: Add `closureGarageDoor` device.
42
+ - [platform]: Add `closureVenetianBlind` device with Lift and Tilt panels.
41
43
 
42
44
  ### Changed
43
45
 
44
46
  - [package]: Upgrade package.
45
47
  - [package]: Update dependencies.
46
- - [package]: Bump `@types/node` to v.26.1.2.
47
- - [package]: Bump `oxfmt` to v.0.61.0.
48
- - [package]: Bump `oxlint` to v.1.76.0.
48
+ - [package]: Bump `typescript` to v.7.0.2.
49
+ - [package]: Bump `@types/node` to v.26.2.0.
50
+ - [package]: Bump `oxfmt` to v.0.62.0.
51
+ - [package]: Bump `oxlint` to v.1.77.0.
49
52
  - [package]: Bump `oxlint-tsgolint` to v.7.0.2001.
50
53
  - [package]: Update agents configs.
51
54
 
package/README.md CHANGED
@@ -23,7 +23,7 @@
23
23
 
24
24
  Matterbridge dynamic platform example plugin is a template to develop your own plugin using the dynamic platform.
25
25
 
26
- It exposes 71 virtual devices:
26
+ It exposes 72 virtual devices:
27
27
 
28
28
  - a door contact sensor
29
29
  - a motion sensor
@@ -96,9 +96,12 @@ It exposes 71 virtual devices:
96
96
  - an irrigation system (Matter 1.5.0)
97
97
  - an irrigation system with four zones (Matter 1.5.0)
98
98
  - a closure device (Matter 1.5.0, supported by SmartThings)
99
+ - a closure device with Lift and Tilt panels (Matter 1.5.0, supported by SmartThings)
99
100
 
100
101
  All these devices continuously change their state and position. The plugin also shows how to use all the command handlers (so you can control all the devices), how to subscribe to attributes, and how to trigger events.
101
102
 
103
+ All devices pass the generic Matter conformance test suite (`TC_DeviceBasicComposition.py`, `TC_DeviceConformance.py` and `TC_DefaultWarnings.py`) run via the CHIP test harness, with one known upstream test-suite gap for the Closure devices — see [chipTests.md](./chipTests.md) for details and how to run the suite against this plugin.
104
+
102
105
  If you want to write your own plugin, the easiest way to get started is to clone the [Matterbridge Plugin Template](https://github.com/Luligu/matterbridge-plugin-template). It has **Dev Container support for an instant development environment**, with all tools and extensions (like Node.js, npm, TypeScript, ESLint, Prettier, Jest, and Vitest) already loaded and configured.
103
106
 
104
107
  If you like this project and find it useful, please consider giving it a star on [GitHub](https://github.com/Luligu/matterbridge-example-dynamic-platform) and sponsoring it.
@@ -111,11 +114,11 @@ If you like this project and find it useful, please consider giving it a star on
111
114
 
112
115
  See the guidelines on [Matterbridge](https://github.com/Luligu/matterbridge/blob/main/README.md) for more information.
113
116
 
114
- ## Repository setup
117
+ ## Repository toolchain
115
118
 
116
- > **Note:** This repository uses a new toolchain. It replaces the traditional TypeScript / ESLint / Prettier / Jest stack with a faster, lighter setup.
119
+ > **Note:** This repository uses a new toolchain. It replaces the traditional TypeScript / ESLint / Prettier / Jest stack with a faster and lighter setup.
117
120
 
118
- - **No `typescript` package** — replaced by [TypeScript Native](https://github.com/microsoft/typescript-go). The `typescript` package is kept only as a publish-time dependency while tsgo is still in preview.
121
+ - **No `typescript 6.x` package** — replaced by [TypeScript Native 7.x](https://github.com/microsoft/typescript-go).
119
122
  - **No ESLint, no Prettier** — replaced by the [oxc](https://oxc.rs) stack: [oxlint](https://oxc.rs/docs/guide/usage/linter.html) for linting and [oxfmt](https://oxc.rs/docs/guide/usage/formatter.html) for formatting.
120
123
  - **No Jest** — replaced by [Vitest](https://vitest.dev), which is much faster and natively supports ESM without extra configuration.
121
124
  - **Far fewer development dependencies** — the number of installed packages drops from **~600** to **~75**. A clean install is much faster.
@@ -129,25 +132,33 @@ See also the [Style Guide](./STYLEGUIDE.md) for JSDoc, naming, and logging conve
129
132
 
130
133
  ## Copilot instructions
131
134
 
132
- | File | Notes |
133
- | ---------------------------------------------------------------- | ---------------------------------------------------------------- |
134
- | `.github/copilot-instructions.md` | Main project instructions — always loaded |
135
- | `.github/instructions/matterbridge/matterbridge.instructions.md` | Matterbridge endpoint guide — dedicated Copilot instruction file |
136
- | `.github/instructions/testing/unit-tests.instructions.md` | Testing standards — scoped to `**/*.test.ts` |
135
+ | File | Notes |
136
+ | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
137
+ | `.github/copilot-instructions.md` | Main project instructions — always loaded |
138
+ | `.github/instructions/chip-tests/chip-tests.instructions.md` | CHIP conformance test harness — scoped to CHIP test files |
139
+ | `.github/instructions/matterbridge/matterbridge.instructions.md` | Matterbridge endpoint guide — dedicated Copilot instruction file |
140
+ | `.github/instructions/plugin-frontend/plugin-frontend.instructions.md` | Plugin frontend SPA and custom REST API guide — scoped to frontend and plugin code |
141
+ | `.github/instructions/testing/unit-tests.instructions.md` | Testing standards — scoped to `**/*.test.ts` |
137
142
 
138
143
  ## Claude instructions
139
144
 
140
- | File | Notes |
141
- | --------------------------------------------------------- | ----------------------------------------------------- |
142
- | `CLAUDE.md` | Main project instructions — always loaded |
143
- | `.claude/rules/matterbridge/matterbridge.instructions.md` | Matterbridge endpoint guide — loaded for all contexts |
144
- | `.claude/rules/testing/unit-tests.instructions.md` | Testing standards — scoped to `**/*.test.ts` |
145
+ | File | Notes |
146
+ | --------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
147
+ | `CLAUDE.md` | Main project instructions — always loaded |
148
+ | `.claude/rules/chip-tests/chip-tests.instructions.md` | CHIP conformance test harness — scoped to CHIP test files |
149
+ | `.claude/rules/matterbridge/matterbridge.instructions.md` | Matterbridge endpoint guide — loaded for all contexts |
150
+ | `.claude/rules/plugin-frontend/plugin-frontend.instructions.md` | Plugin frontend SPA and custom REST API guide — scoped to frontend and plugin code |
151
+ | `.claude/rules/testing/unit-tests.instructions.md` | Testing standards — scoped to `**/*.test.ts` |
145
152
 
146
153
  ## Codex/Agents instructions
147
154
 
148
155
  | File | Notes |
149
156
  | ---------------------------- | ------------------------------------------------- |
150
157
  | `AGENTS.md` | Main project instructions |
158
+ | `.agents/chip-tests.md` | CHIP conformance test harness |
159
+ | `.agents/matterbridge.md` | Matterbridge endpoint guide |
160
+ | `.agents/plugin-frontend.md` | Plugin frontend SPA and custom REST API guide |
161
+ | `.agents/testing.md` | Testing and validation expectations |
151
162
  | `.codex/config.toml` | Codex project permissions, approvals, and profile |
152
163
  | `.codex/rules/default.rules` | Codex command allow, prompt, and deny rules |
153
164
 
package/dist/module.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { aggregator, airPurifier, airQualitySensor, bridgedNode, colorTemperatureLight, contactSensor, windowCovering, dimmableLight, mountedDimmableLoadControl, dimmablePlugInUnit, doorLock, electricalSensor, extendedColorLight, fan, flowSensor, genericSwitch, getSemtag, humiditySensor, lightSensor, MatterbridgeDynamicPlatform, MatterbridgeEndpoint, modeSelect, occupancySensor, onOffLight, mountedOnOffControl, onOffPlugInUnit, onOffLightSwitch, powerSource, pressureSensor, pump, rainSensor, smokeCoAlarm, temperatureSensor, thermostat, waterFreezeDetector, waterLeakDetector, waterValve, } from 'matterbridge';
2
2
  import { AirConditioner, BasicVideoPlayer, BatteryStorage, Closure, Cooktop, Dishwasher, Evse, ExtractorHood, HeatPump, IrrigationSystem, LaundryDryer, LaundryWasher, MicrowaveOven, Oven, Refrigerator, RoboticVacuumCleaner, SoilSensor, SolarPower, Speaker, WaterHeater, } from 'matterbridge/devices';
3
3
  import { debugStringify } from 'matterbridge/logger';
4
- import { ClosureTag, CommonAreaNamespaceTag, CommonLocationTag, CommonNumberTag, CommonPositionTag, RefrigeratorTag, SwitchesTag, UINT16_MAX, UINT32_MAX, } from 'matterbridge/matter';
5
- import { AirQuality, BooleanState, BridgedDeviceBasicInformation, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, ClosureControl, ColorControl, Descriptor, DeviceEnergyManagement, DoorLock, ElectricalEnergyMeasurement, ElectricalPowerMeasurement, EnergyEvse, EnergyEvseMode, FanControl, FlowMeasurement, FormaldehydeConcentrationMeasurement, Identify, IlluminanceMeasurement, LevelControl, NitrogenDioxideConcentrationMeasurement, OccupancySensing, OnOff, OperationalState, OvenMode, OzoneConcentrationMeasurement, Pm1ConcentrationMeasurement, Pm10ConcentrationMeasurement, Pm25ConcentrationMeasurement, PowerSource, PressureMeasurement, RadonConcentrationMeasurement, RelativeHumidityMeasurement, RvcCleanMode, RvcOperationalState, RvcRunMode, SmokeCoAlarm, TemperatureMeasurement, Thermostat, TotalVolatileOrganicCompoundsConcentrationMeasurement, WindowCovering, } from 'matterbridge/matter/clusters';
4
+ import { ClosureCoveringTag, ClosurePanelTag, ClosureTag, CommonAreaNamespaceTag, CommonLocationTag, CommonNumberTag, CommonPositionTag, RefrigeratorTag, SwitchesTag, UINT16_MAX, UINT32_MAX, } from 'matterbridge/matter';
5
+ import { AirQuality, BooleanState, BridgedDeviceBasicInformation, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, ClosureControl, ClosureDimension, ColorControl, Descriptor, DeviceEnergyManagement, DoorLock, ElectricalEnergyMeasurement, ElectricalPowerMeasurement, EnergyEvse, EnergyEvseMode, FanControl, FlowMeasurement, FormaldehydeConcentrationMeasurement, Identify, IlluminanceMeasurement, LevelControl, NitrogenDioxideConcentrationMeasurement, OccupancySensing, OnOff, OperationalState, OvenMode, OzoneConcentrationMeasurement, Pm1ConcentrationMeasurement, Pm10ConcentrationMeasurement, Pm25ConcentrationMeasurement, PowerSource, PressureMeasurement, RadonConcentrationMeasurement, RelativeHumidityMeasurement, RvcCleanMode, RvcOperationalState, RvcRunMode, SmokeCoAlarm, TemperatureMeasurement, Thermostat, TotalVolatileOrganicCompoundsConcentrationMeasurement, WindowCovering, } from 'matterbridge/matter/clusters';
6
6
  import { fireAndForget, getEnumDescription, isValidBoolean, isValidNumber, isValidObject, isValidString } from 'matterbridge/utils';
7
7
  function luxToMatter(lux) {
8
8
  if (!Number.isFinite(lux) || lux <= 0)
@@ -35,6 +35,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
35
35
  irrigation;
36
36
  irrigationSystem;
37
37
  closureGarageDoor;
38
+ closureVenetianBlind;
38
39
  select;
39
40
  climate;
40
41
  switch;
@@ -107,8 +108,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
107
108
  constructor(matterbridge, log, config) {
108
109
  super(matterbridge, log, config);
109
110
  this.config = config;
110
- if (typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.10.3')) {
111
- throw new Error(`This plugin requires Matterbridge version >= "3.10.3". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
111
+ if (typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.10.5')) {
112
+ throw new Error(`This plugin requires Matterbridge version >= "3.10.5". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
112
113
  }
113
114
  this.log.info('Initializing platform:', this.config.name);
114
115
  }
@@ -167,7 +168,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
167
168
  this.flow = await this.addDevice(this.flow);
168
169
  this.soil = new SoilSensor('Soil Sensor', 'SOI000067', { soilMoistureMeasuredValue: 45, temperatureMeasuredValue: 3500, batteryPowered: true });
169
170
  this.soil = await this.addDevice(this.soil);
170
- this.irrigation = new IrrigationSystem('Irrigation System', 'IRR000068', { singleZone: true, batteryPowered: true, flowMeasuredValue: 15 });
171
+ this.irrigation = new IrrigationSystem('Irrigation System', 'IRR000068', { batteryPowered: true, flowMeasuredValue: 15 }).addZone(CommonNumberTag.One);
171
172
  this.irrigation = (await this.addDevice(this.irrigation));
172
173
  this.irrigationSystem = new IrrigationSystem('Irrigation System 4 zones', 'IRR000069', { flowMeasuredValue: 60 })
173
174
  .addZone(CommonNumberTag.One)
@@ -179,26 +180,127 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
179
180
  tagList: [getSemtag(ClosureTag.GarageDoor)],
180
181
  });
181
182
  this.closureGarageDoor = (await this.addDevice(this.closureGarageDoor));
182
- this.closureGarageDoor?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed } }) => {
183
+ let closureGarageDoorMoveTimeout;
184
+ function closureTargetToCurrentPosition(position) {
185
+ if (position === ClosureControl.TargetPosition.MoveToFullyClosed)
186
+ return ClosureControl.CurrentPosition.FullyClosed;
187
+ if (position === ClosureControl.TargetPosition.MoveToFullyOpen)
188
+ return ClosureControl.CurrentPosition.FullyOpened;
189
+ if (position === ClosureControl.TargetPosition.MoveToPedestrianPosition)
190
+ return ClosureControl.CurrentPosition.OpenedForPedestrian;
191
+ if (position === ClosureControl.TargetPosition.MoveToVentilationPosition)
192
+ return ClosureControl.CurrentPosition.OpenedForVentilation;
193
+ return ClosureControl.CurrentPosition.OpenedAtSignature;
194
+ }
195
+ this.closureGarageDoor?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed }, attributes }) => {
183
196
  this.closureGarageDoor?.log.info(`Command moveTo called position:${position} latch:${latch} speed:${speed}`);
184
- const currentStateTimeout = setTimeout(() => {
185
- const targetState = this.closureGarageDoor?.getAttribute(ClosureControl.id, 'overallTargetState');
186
- const currentState = this.closureGarageDoor?.getAttribute(ClosureControl.id, 'overallCurrentState');
187
- if (targetState === undefined || targetState === null)
197
+ attributes.countdownTime = 1;
198
+ closureGarageDoorMoveTimeout = setTimeout(() => {
199
+ const targetState = this.closureGarageDoor?.getAttribute(ClosureControl, 'overallTargetState');
200
+ if (targetState === undefined || targetState === null || targetState.position === undefined || targetState.position === null)
188
201
  return;
189
- void this.closureGarageDoor?.setAttribute(ClosureControl.id, 'mainState', ClosureControl.MainState.Stopped, this.closureGarageDoor.log);
190
- void this.closureGarageDoor?.setAttribute(ClosureControl.id, 'overallCurrentState', {
191
- position: targetState.position,
202
+ void this.closureGarageDoor?.setAttribute(ClosureControl, 'mainState', ClosureControl.MainState.Stopped, this.closureGarageDoor.log);
203
+ void this.closureGarageDoor?.setAttribute(ClosureControl, 'countdownTime', 0, this.closureGarageDoor.log);
204
+ void this.closureGarageDoor?.setAttribute(ClosureControl, 'overallCurrentState', {
205
+ position: closureTargetToCurrentPosition(targetState.position),
192
206
  latch: targetState.latch,
193
207
  speed: targetState.speed,
194
- secureState: targetState.position === ClosureControl.TargetPosition.MoveToFullyClosed ? true : (currentState?.secureState ?? false),
208
+ secureState: targetState.position === ClosureControl.TargetPosition.MoveToFullyClosed && targetState.latch === true,
195
209
  }, this.closureGarageDoor.log);
196
- }, 1000);
197
- currentStateTimeout.unref();
210
+ }, 1000).unref();
198
211
  });
199
212
  this.closureGarageDoor?.addCommandHandler('ClosureControl.stop', () => {
200
213
  this.closureGarageDoor?.log.info('Command stop called');
201
- });
214
+ clearTimeout(closureGarageDoorMoveTimeout);
215
+ closureGarageDoorMoveTimeout = undefined;
216
+ });
217
+ this.closureVenetianBlind = new Closure('Venetian Blind', 'VEN000072', {
218
+ tagList: [getSemtag(ClosureTag.Covering), getSemtag(ClosureCoveringTag.Venetian)],
219
+ });
220
+ const closureVenetianBlindLift = this.closureVenetianBlind.addPanel('Lift', [getSemtag(ClosurePanelTag.Lift)], 'lift');
221
+ const closureVenetianBlindTilt = this.closureVenetianBlind.addPanel('Tilt', [getSemtag(ClosurePanelTag.Tilt)], 'tilt');
222
+ this.closureVenetianBlind = (await this.addDevice(this.closureVenetianBlind));
223
+ const closurePanelTargetPercent = (position) => {
224
+ if (position === ClosureControl.TargetPosition.MoveToFullyOpen)
225
+ return 0;
226
+ if (position === ClosureControl.TargetPosition.MoveToFullyClosed)
227
+ return 10000;
228
+ return 5000;
229
+ };
230
+ const closurePanelReachTimeouts = new Map();
231
+ let closureVenetianBlindMoveTimeout;
232
+ const clearClosureVenetianBlindTimeouts = () => {
233
+ clearTimeout(closureVenetianBlindMoveTimeout);
234
+ closureVenetianBlindMoveTimeout = undefined;
235
+ for (const pending of closurePanelReachTimeouts.values())
236
+ clearTimeout(pending);
237
+ closurePanelReachTimeouts.clear();
238
+ };
239
+ const syncClosureVenetianBlindFromPanels = () => {
240
+ const liftTarget = closureVenetianBlindLift.getAttribute(ClosureDimension, 'targetState');
241
+ const liftCurrent = closureVenetianBlindLift.getAttribute(ClosureDimension, 'currentState');
242
+ const tiltTarget = closureVenetianBlindTilt.getAttribute(ClosureDimension, 'targetState');
243
+ const tiltCurrent = closureVenetianBlindTilt.getAttribute(ClosureDimension, 'currentState');
244
+ if (!liftTarget || !liftCurrent || !tiltTarget || !tiltCurrent)
245
+ return;
246
+ if (liftCurrent.position !== liftTarget.position || tiltCurrent.position !== tiltTarget.position)
247
+ return;
248
+ const overallPosition = liftCurrent.position === 0
249
+ ? ClosureControl.CurrentPosition.FullyOpened
250
+ : liftCurrent.position === 10000
251
+ ? ClosureControl.CurrentPosition.FullyClosed
252
+ : ClosureControl.CurrentPosition.PartiallyOpened;
253
+ void this.closureVenetianBlind?.setAttribute(ClosureControl, 'mainState', ClosureControl.MainState.Stopped, this.closureVenetianBlind.log);
254
+ void this.closureVenetianBlind?.setAttribute(ClosureControl, 'overallCurrentState', { position: overallPosition, latch: liftCurrent.latch ?? true, speed: liftCurrent.speed, secureState: liftCurrent.position === 10000 && liftCurrent.latch === true }, this.closureVenetianBlind?.log);
255
+ };
256
+ const simulateClosurePanelReachingTarget = (panel) => {
257
+ const pending = closurePanelReachTimeouts.get(panel);
258
+ if (pending)
259
+ clearTimeout(pending);
260
+ const currentStateTimeout = setTimeout(() => {
261
+ void (async () => {
262
+ closurePanelReachTimeouts.delete(panel);
263
+ const targetState = panel.getAttribute(ClosureDimension, 'targetState');
264
+ if (targetState === undefined || targetState === null)
265
+ return;
266
+ await panel.setAttribute(ClosureDimension, 'currentState', targetState, panel.log);
267
+ syncClosureVenetianBlindFromPanels();
268
+ })();
269
+ }, 1000);
270
+ currentStateTimeout.unref();
271
+ closurePanelReachTimeouts.set(panel, currentStateTimeout);
272
+ };
273
+ const moveClosurePanelTo = (panel, position, latch, speed) => {
274
+ void panel.setAttribute(ClosureDimension, 'targetState', { position, latch: latch ?? true, speed }, panel.log);
275
+ simulateClosurePanelReachingTarget(panel);
276
+ };
277
+ this.closureVenetianBlind?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed }, attributes }) => {
278
+ this.closureVenetianBlind?.log.info(`Command moveTo called position:${position} latch:${latch} speed:${speed}`);
279
+ attributes.countdownTime = 1;
280
+ clearClosureVenetianBlindTimeouts();
281
+ closureVenetianBlindMoveTimeout = setTimeout(() => {
282
+ void this.closureVenetianBlind?.setAttribute(ClosureControl, 'countdownTime', 0, this.closureVenetianBlind.log);
283
+ closureVenetianBlindMoveTimeout = undefined;
284
+ const targetState = this.closureVenetianBlind?.getAttribute(ClosureControl, 'overallTargetState');
285
+ if (targetState === undefined || targetState === null || targetState.position === undefined || targetState.position === null)
286
+ return;
287
+ const panelPercent = closurePanelTargetPercent(targetState.position);
288
+ moveClosurePanelTo(closureVenetianBlindLift, panelPercent, targetState.latch, targetState.speed);
289
+ moveClosurePanelTo(closureVenetianBlindTilt, panelPercent, targetState.latch, targetState.speed);
290
+ }, 1000).unref();
291
+ });
292
+ this.closureVenetianBlind?.addCommandHandler('ClosureControl.stop', () => {
293
+ this.closureVenetianBlind?.log.info('Command stop called');
294
+ clearClosureVenetianBlindTimeouts();
295
+ });
296
+ const addClosurePanelSetTargetSimulation = (panel) => {
297
+ panel.addCommandHandler('ClosureDimension.setTarget', ({ request: { position, latch, speed } }) => {
298
+ panel.log.info(`Command setTarget called position:${position} latch:${latch} speed:${speed}`);
299
+ simulateClosurePanelReachingTarget(panel);
300
+ });
301
+ };
302
+ addClosurePanelSetTargetSimulation(closureVenetianBlindLift);
303
+ addClosurePanelSetTargetSimulation(closureVenetianBlindTilt);
202
304
  this.climate = new MatterbridgeEndpoint([bridgedNode, powerSource], { id: 'Climate' }, this.config.debug)
203
305
  .createDefaultBridgedDeviceBasicInformationClusterServer('Climate', 'CLI00008', 0xfff1, 'Matterbridge', 'Matterbridge Climate')
204
306
  .createDefaultPowerSourceReplaceableBatteryClusterServer(90, PowerSource.BatChargeLevel.Ok, 2990, '2 x AA', 2, PowerSource.BatReplaceability.UserReplaceable)
@@ -223,9 +325,11 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
223
325
  this.switch = new MatterbridgeEndpoint([onOffLightSwitch, bridgedNode, powerSource], { id: 'Switch' }, this.config.debug)
224
326
  .createDefaultIdentifyClusterServer()
225
327
  .createDefaultBridgedDeviceBasicInformationClusterServer('Switch', 'SWI00010', 0xfff1, 'Matterbridge', 'Matterbridge Switch')
226
- .createDefaultOnOffClusterServer()
227
328
  .createDefaultPowerSourceWiredClusterServer()
228
329
  .addRequiredClusters();
330
+ if (process.env.MATTERBRIDGE_CHIP_TEST !== '1') {
331
+ this.switch.createDefaultOnOffClusterServer();
332
+ }
229
333
  this.switch = await this.addDevice(this.switch);
230
334
  this.switch?.addCommandHandler('identify', ({ request: { identifyTime } }) => {
231
335
  this.log.info(`Command identify called identifyTime:${identifyTime}`);
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "3.0.2-dev-20260803-06c5b3e",
3
+ "version": "3.0.2-dev-20260808-eeaaae3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-example-dynamic-platform",
9
- "version": "3.0.2-dev-20260803-06c5b3e",
9
+ "version": "3.0.2-dev-20260808-eeaaae3",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "node-ansi-logger": "3.3.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "3.0.2-dev-20260803-06c5b3e",
3
+ "version": "3.0.2-dev-20260808-eeaaae3",
4
4
  "description": "Matterbridge dynamic plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",