matterbridge-example-dynamic-platform 3.0.3 → 3.0.4-dev-20260902-044750d

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
@@ -29,6 +29,14 @@ If you like this project and find it useful, please consider giving it a star on
29
29
 
30
30
  <a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/assets/bmc-button.svg" alt="Buy me a coffee" width="120"></a>
31
31
 
32
+ ## [3.0.4] - Dev branch
33
+
34
+ ### Breaking changes
35
+
36
+ - [matterbridge]: Require matterbridge v.3.10.8 with matter v.1.6.0.
37
+
38
+ <a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/assets/bmc-button.svg" alt="Buy me a coffee" width="80"></a>
39
+
32
40
  ## [3.0.3] - 2026-08-30
33
41
 
34
42
  ### Breaking changes
@@ -38,6 +46,7 @@ If you like this project and find it useful, please consider giving it a star on
38
46
 
39
47
  ### Added
40
48
 
49
+ - [platform]: Add `ScheduleLock` device: a door lock with the User, PinCredential, and optional WeekDayAccessSchedules, YearDayAccessSchedules, and HolidaySchedules features.
41
50
  - [platform]: Add `closureSlidingGate` device with pedestrian opening support. Thanks Ludovic BOUÉ.
42
51
  - [platform]: Add `closureRoofWindow` device with ventilation opening support. Thanks Ludovic BOUÉ.
43
52
  - [devcontainer]: Upgrade the `Dev Container` to v.2.0.0, using the Node.js and Bun stack.
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 76 virtual devices:
26
+ It exposes 77 virtual devices:
27
27
 
28
28
  - a door contact sensor
29
29
  - a motion sensor
@@ -49,6 +49,8 @@ It exposes 76 virtual devices:
49
49
  - a cover with windowCovering cluster and lift feature
50
50
  - a cover with windowCovering cluster and both lift and tilt features
51
51
  - a lock with doorLock cluster
52
+ - a lock with doorLock cluster and User and PinCredential support (userPinLock)
53
+ - a lock with doorLock cluster, User and PinCredential support, and WeekDay, YearDay and Holiday access schedules (scheduleLock)
52
54
  - a thermostat auto mode (i.e. with Auto Heat and Cool features) with thermostat cluster and 3 sub endpoints with flowMeasurement cluster, temperatureMeasurement cluster
53
55
  and relativeHumidityMeasurement cluster (to show how to create a composed device with sub endpoints)
54
56
  - a thermostat with auto mode (i.e. with Auto Heat and Cool features), occupancy and outdoorTemperature
package/dist/module.js CHANGED
@@ -1,22 +1,9 @@
1
- import { aggregator, airPurifier, airQualitySensor, bridgedNode, closure, 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
- import { AirConditioner, BasicVideoPlayer, BatteryStorage, Closure, Cooktop, Dishwasher, Evse, ExtractorHood, HeatPump, IrrigationSystem, LaundryDryer, LaundryWasher, MatterbridgeClosureControlServer, MicrowaveOven, Oven, Refrigerator, RoboticVacuumCleaner, SoilSensor, SolarPower, Speaker, WaterHeater, } from 'matterbridge/devices';
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
+ 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
4
  import { ClosureCoveringTag, ClosurePanelTag, ClosureTag, ClosureWindowTag, CommonAreaNamespaceTag, CommonLocationTag, CommonNumberTag, CommonPositionTag, RefrigeratorTag, SwitchesTag, UINT16_MAX, UINT32_MAX, } from 'matterbridge/matter';
5
5
  import { AirQuality, BooleanState, BridgedDeviceBasicInformation, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, ClosureControl, ClosureDimension, ColorControl, 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
- import { ThreeLevelAuto } from 'matterbridge/matter/types';
7
- import { fireAndForget, getEnumDescription, isValidBoolean, isValidNumber, isValidObject, isValidString, parseVersionString } from 'matterbridge/utils';
8
- function luxToMatter(lux) {
9
- if (!Number.isFinite(lux) || lux < 1)
10
- return 0;
11
- const encoded = Math.round(10000 * Math.log10(lux) + 1);
12
- return Math.min(encoded, 0xfffe);
13
- }
14
- function matterToLux(value) {
15
- if (!Number.isFinite(value) || value <= 0)
16
- return 0;
17
- const v = Math.min(value, 0xfffe);
18
- return Math.round(Math.pow(10, (v - 1) / 10000));
19
- }
6
+ import { fireAndForget, getEnumDescription, isValidBoolean, isValidNumber, isValidObject, isValidString, luxToMatter, matterToLux, parseVersionString } from 'matterbridge/utils';
20
7
  export default function initializePlugin(matterbridge, log, config) {
21
8
  return new ExampleMatterbridgeDynamicPlatform(matterbridge, log, config);
22
9
  }
@@ -58,6 +45,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
58
45
  coverLiftTilt;
59
46
  lock;
60
47
  userPinLock;
48
+ scheduleLock;
61
49
  thermoAuto;
62
50
  thermoAutoOccupancy;
63
51
  thermoAutoPresets;
@@ -110,8 +98,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
110
98
  constructor(matterbridge, log, config) {
111
99
  super(matterbridge, log, config);
112
100
  this.config = config;
113
- if (typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.10.7')) {
114
- throw new Error(`This plugin requires Matterbridge version >= "3.10.7". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
101
+ if (typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.10.8')) {
102
+ throw new Error(`This plugin requires Matterbridge version >= "3.10.8". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
115
103
  }
116
104
  this.log.info('Initializing platform:', this.config.name);
117
105
  }
@@ -180,41 +168,42 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
180
168
  this.irrigationSystem = await this.addDevice(this.irrigationSystem);
181
169
  this.closureGarageDoor = new Closure('Garage Door', 'GAR000071', {
182
170
  tagList: [getSemtag(ClosureTag.GarageDoor)],
171
+ movementDuration: 2000,
183
172
  });
184
173
  this.closureGarageDoor = await this.addDevice(this.closureGarageDoor);
185
- let closureGarageDoorMoveTimeout;
186
- function closureTargetToCurrentPosition(position) {
187
- if (position === ClosureControl.TargetPosition.MoveToFullyClosed)
188
- return ClosureControl.CurrentPosition.FullyClosed;
189
- if (position === ClosureControl.TargetPosition.MoveToFullyOpen)
190
- return ClosureControl.CurrentPosition.FullyOpened;
191
- if (position === ClosureControl.TargetPosition.MoveToPedestrianPosition)
192
- return ClosureControl.CurrentPosition.OpenedForPedestrian;
193
- if (position === ClosureControl.TargetPosition.MoveToVentilationPosition)
194
- return ClosureControl.CurrentPosition.OpenedForVentilation;
195
- return ClosureControl.CurrentPosition.OpenedAtSignature;
196
- }
197
- this.closureGarageDoor?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed }, attributes }) => {
174
+ this.closureGarageDoor?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed } }) => {
198
175
  this.closureGarageDoor?.log.info(`Command moveTo called position:${position} latch:${latch} speed:${speed}`);
199
- attributes.countdownTime = 1;
200
- closureGarageDoorMoveTimeout = setTimeout(() => {
201
- const targetState = this.closureGarageDoor?.getAttribute(ClosureControl, 'overallTargetState');
202
- if (targetState === undefined || targetState === null || targetState.position === undefined || targetState.position === null)
203
- return;
204
- void this.closureGarageDoor?.setState({
205
- position: closureTargetToCurrentPosition(targetState.position),
206
- latch: targetState.latch,
207
- speed: targetState.speed,
208
- secureState: targetState.position === ClosureControl.TargetPosition.MoveToFullyClosed && targetState.latch === true,
209
- }, targetState, ClosureControl.MainState.Stopped, 0, []);
210
- void this.closureGarageDoor?.triggerMovementCompleted();
211
- }, 1000).unref();
212
176
  });
213
- this.closureGarageDoor?.addCommandHandler('ClosureControl.stop', ({ attributes }) => {
177
+ this.closureGarageDoor?.addCommandHandler('ClosureControl.stop', () => {
214
178
  this.closureGarageDoor?.log.info('Command stop called');
215
- attributes.countdownTime = 0;
216
- clearTimeout(closureGarageDoorMoveTimeout);
217
- closureGarageDoorMoveTimeout = undefined;
179
+ });
180
+ this.closureSlidingGate = new Closure('Sliding Gate', 'GAT000073', {
181
+ tagList: [getSemtag(ClosureTag.Gate)],
182
+ movementDuration: 2000,
183
+ motionLatching: true,
184
+ speed: true,
185
+ pedestrian: true,
186
+ });
187
+ this.closureSlidingGate = await this.addDevice(this.closureSlidingGate);
188
+ this.closureSlidingGate?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed } }) => {
189
+ this.closureSlidingGate?.log.info(`Command moveTo called position:${position} latch:${latch} speed:${speed}`);
190
+ });
191
+ this.closureSlidingGate?.addCommandHandler('ClosureControl.stop', () => {
192
+ this.closureSlidingGate?.log.info('Command stop called');
193
+ });
194
+ this.closureRoofWindow = new Closure('Roof Window', 'ROO000074', {
195
+ tagList: [getSemtag(ClosureTag.Window), getSemtag(ClosureWindowTag.Roof)],
196
+ movementDuration: 2000,
197
+ motionLatching: true,
198
+ speed: true,
199
+ ventilation: true,
200
+ });
201
+ this.closureRoofWindow = await this.addDevice(this.closureRoofWindow);
202
+ this.closureRoofWindow?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed } }) => {
203
+ this.closureRoofWindow?.log.info(`Command moveTo called position:${position} latch:${latch} speed:${speed}`);
204
+ });
205
+ this.closureRoofWindow?.addCommandHandler('ClosureControl.stop', () => {
206
+ this.closureRoofWindow?.log.info('Command stop called');
218
207
  });
219
208
  this.closureVenetianBlind = new Closure('Venetian Blind', 'VEN000072', {
220
209
  tagList: [getSemtag(ClosureTag.Covering), getSemtag(ClosureCoveringTag.Venetian)],
@@ -222,13 +211,6 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
222
211
  const closureVenetianBlindLift = this.closureVenetianBlind.addPanel('Lift', [getSemtag(ClosurePanelTag.Lift)], 'lift');
223
212
  const closureVenetianBlindTilt = this.closureVenetianBlind.addPanel('Tilt', [getSemtag(ClosurePanelTag.Tilt)], 'tilt');
224
213
  this.closureVenetianBlind = await this.addDevice(this.closureVenetianBlind);
225
- const closurePanelTargetPercent = (position) => {
226
- if (position === ClosureControl.TargetPosition.MoveToFullyOpen)
227
- return 0;
228
- if (position === ClosureControl.TargetPosition.MoveToFullyClosed)
229
- return 10000;
230
- return 5000;
231
- };
232
214
  const closurePanelReachTimeouts = new Map();
233
215
  let closureVenetianBlindMoveTimeout;
234
216
  const clearClosureVenetianBlindTimeouts = () => {
@@ -253,7 +235,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
253
235
  ? ClosureControl.CurrentPosition.FullyClosed
254
236
  : ClosureControl.CurrentPosition.PartiallyOpened;
255
237
  void this.closureVenetianBlind?.setAttribute(ClosureControl, 'mainState', ClosureControl.MainState.Stopped, this.closureVenetianBlind.log);
256
- 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);
238
+ void this.closureVenetianBlind?.setAttribute(ClosureControl, 'overallCurrentState', { position: overallPosition, secureState: liftCurrent.position === 10000 }, this.closureVenetianBlind?.log);
257
239
  };
258
240
  const simulateClosurePanelReachingTarget = (panel) => {
259
241
  clearTimeout(closurePanelReachTimeouts.get(panel));
@@ -268,8 +250,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
268
250
  }, 1000).unref();
269
251
  closurePanelReachTimeouts.set(panel, currentStateTimeout);
270
252
  };
271
- const moveClosurePanelTo = (panel, position, latch, speed) => {
272
- void panel.setAttribute(ClosureDimension, 'targetState', { position, latch: latch ?? true, speed }, panel.log);
253
+ const moveClosurePanelTo = (panel, position) => {
254
+ void panel.setAttribute(ClosureDimension, 'targetState', { position }, panel.log);
273
255
  simulateClosurePanelReachingTarget(panel);
274
256
  };
275
257
  this.closureVenetianBlind?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed }, attributes }) => {
@@ -282,9 +264,11 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
282
264
  const targetState = this.closureVenetianBlind?.getAttribute(ClosureControl, 'overallTargetState');
283
265
  if (targetState === undefined || targetState === null || targetState.position === undefined || targetState.position === null)
284
266
  return;
285
- const panelPercent = closurePanelTargetPercent(targetState.position);
286
- moveClosurePanelTo(closureVenetianBlindLift, panelPercent, targetState.latch, targetState.speed);
287
- moveClosurePanelTo(closureVenetianBlindTilt, panelPercent, targetState.latch, targetState.speed);
267
+ const panelPercent = this.closureVenetianBlind?.targetPositionToTargetPercent(targetState.position);
268
+ if (panelPercent === undefined)
269
+ return;
270
+ moveClosurePanelTo(closureVenetianBlindLift, panelPercent);
271
+ moveClosurePanelTo(closureVenetianBlindTilt, panelPercent);
288
272
  }, 1000).unref();
289
273
  });
290
274
  this.closureVenetianBlind?.addCommandHandler('ClosureControl.stop', () => {
@@ -307,96 +291,6 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
307
291
  };
308
292
  addClosurePanelStepSimulation(closureVenetianBlindLift);
309
293
  addClosurePanelStepSimulation(closureVenetianBlindTilt);
310
- this.closureSlidingGate = new MatterbridgeEndpoint([closure, powerSource], {
311
- id: 'SlidingGate-GAT000073',
312
- tagList: [getSemtag(ClosureTag.Gate)],
313
- }, this.config.debug)
314
- .createDefaultIdentifyClusterServer()
315
- .createDefaultBasicInformationClusterServer('Sliding Gate', 'GAT000073', 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Closure')
316
- .createDefaultPowerSourceWiredClusterServer();
317
- this.closureSlidingGate.behaviors.require(MatterbridgeClosureControlServer.with(ClosureControl.Feature.Positioning, ClosureControl.Feature.MotionLatching, ClosureControl.Feature.Speed, ClosureControl.Feature.Pedestrian), {
318
- countdownTime: 0,
319
- mainState: ClosureControl.MainState.Stopped,
320
- currentErrorList: [],
321
- overallCurrentState: { position: ClosureControl.CurrentPosition.FullyClosed, latch: true, speed: ThreeLevelAuto.Auto, secureState: true },
322
- overallTargetState: { position: ClosureControl.TargetPosition.MoveToFullyClosed, latch: true, speed: ThreeLevelAuto.Auto },
323
- latchControlModes: { remoteLatching: true, remoteUnlatching: true },
324
- });
325
- this.closureSlidingGate = await this.addDevice(this.closureSlidingGate);
326
- let closureSlidingGateMoveTimeout;
327
- this.closureSlidingGate?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed }, attributes }) => {
328
- this.closureSlidingGate?.log.info(`Command moveTo called position:${position} latch:${latch} speed:${speed}`);
329
- attributes.countdownTime = 1;
330
- closureSlidingGateMoveTimeout = setTimeout(() => {
331
- void (async () => {
332
- const targetState = this.closureSlidingGate?.getAttribute(ClosureControl, 'overallTargetState');
333
- if (targetState === undefined || targetState === null || targetState.position === undefined || targetState.position === null)
334
- return;
335
- await this.closureSlidingGate?.setAttribute(ClosureControl, 'countdownTime', 0, this.closureSlidingGate.log);
336
- await this.closureSlidingGate?.setAttribute(ClosureControl, 'mainState', ClosureControl.MainState.Stopped, this.closureSlidingGate.log);
337
- await this.closureSlidingGate?.setAttribute(ClosureControl, 'currentErrorList', [], this.closureSlidingGate.log);
338
- await this.closureSlidingGate?.setAttribute(ClosureControl, 'overallCurrentState', {
339
- position: closureTargetToCurrentPosition(targetState.position),
340
- latch: targetState.latch,
341
- speed: targetState.speed,
342
- secureState: targetState.position === ClosureControl.TargetPosition.MoveToFullyClosed && targetState.latch === true,
343
- }, this.closureSlidingGate.log);
344
- await this.closureSlidingGate?.setAttribute(ClosureControl, 'overallTargetState', targetState, this.closureSlidingGate.log);
345
- await this.closureSlidingGate?.triggerEvent(ClosureControl, 'movementCompleted', undefined, this.closureSlidingGate.log);
346
- })();
347
- }, 1000).unref();
348
- });
349
- this.closureSlidingGate?.addCommandHandler('ClosureControl.stop', ({ attributes }) => {
350
- this.closureSlidingGate?.log.info('Command stop called');
351
- attributes.countdownTime = 0;
352
- clearTimeout(closureSlidingGateMoveTimeout);
353
- closureSlidingGateMoveTimeout = undefined;
354
- });
355
- this.closureRoofWindow = new MatterbridgeEndpoint([closure, powerSource], {
356
- id: 'RoofWindow-ROO000074',
357
- tagList: [getSemtag(ClosureTag.Window), getSemtag(ClosureWindowTag.Roof)],
358
- }, this.config.debug)
359
- .createDefaultIdentifyClusterServer()
360
- .createDefaultBasicInformationClusterServer('Roof Window', 'ROO000074', 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Closure')
361
- .createDefaultPowerSourceWiredClusterServer();
362
- this.closureRoofWindow.behaviors.require(MatterbridgeClosureControlServer.with(ClosureControl.Feature.Positioning, ClosureControl.Feature.MotionLatching, ClosureControl.Feature.Speed, ClosureControl.Feature.Ventilation), {
363
- countdownTime: 0,
364
- mainState: ClosureControl.MainState.Stopped,
365
- currentErrorList: [],
366
- overallCurrentState: { position: ClosureControl.CurrentPosition.FullyClosed, latch: true, speed: ThreeLevelAuto.Auto, secureState: true },
367
- overallTargetState: { position: ClosureControl.TargetPosition.MoveToFullyClosed, latch: true, speed: ThreeLevelAuto.Auto },
368
- latchControlModes: { remoteLatching: true, remoteUnlatching: true },
369
- });
370
- this.closureRoofWindow = await this.addDevice(this.closureRoofWindow);
371
- let closureRoofWindowMoveTimeout;
372
- this.closureRoofWindow?.addCommandHandler('ClosureControl.moveTo', ({ request: { position, latch, speed }, attributes }) => {
373
- this.closureRoofWindow?.log.info(`Command moveTo called position:${position} latch:${latch} speed:${speed}`);
374
- attributes.countdownTime = 1;
375
- closureRoofWindowMoveTimeout = setTimeout(() => {
376
- void (async () => {
377
- const targetState = this.closureRoofWindow?.getAttribute(ClosureControl, 'overallTargetState');
378
- if (targetState === undefined || targetState === null || targetState.position === undefined || targetState.position === null)
379
- return;
380
- await this.closureRoofWindow?.setAttribute(ClosureControl, 'countdownTime', 0, this.closureRoofWindow.log);
381
- await this.closureRoofWindow?.setAttribute(ClosureControl, 'mainState', ClosureControl.MainState.Stopped, this.closureRoofWindow.log);
382
- await this.closureRoofWindow?.setAttribute(ClosureControl, 'currentErrorList', [], this.closureRoofWindow.log);
383
- await this.closureRoofWindow?.setAttribute(ClosureControl, 'overallCurrentState', {
384
- position: closureTargetToCurrentPosition(targetState.position),
385
- latch: targetState.latch,
386
- speed: targetState.speed,
387
- secureState: targetState.position === ClosureControl.TargetPosition.MoveToFullyClosed && targetState.latch === true,
388
- }, this.closureRoofWindow.log);
389
- await this.closureRoofWindow?.setAttribute(ClosureControl, 'overallTargetState', targetState, this.closureRoofWindow.log);
390
- await this.closureRoofWindow?.triggerEvent(ClosureControl, 'movementCompleted', undefined, this.closureRoofWindow.log);
391
- })();
392
- }, 1000).unref();
393
- });
394
- this.closureRoofWindow?.addCommandHandler('ClosureControl.stop', ({ attributes }) => {
395
- this.closureRoofWindow?.log.info('Command stop called');
396
- attributes.countdownTime = 0;
397
- clearTimeout(closureRoofWindowMoveTimeout);
398
- closureRoofWindowMoveTimeout = undefined;
399
- });
400
294
  this.climate = new MatterbridgeEndpoint([bridgedNode, powerSource], { id: 'Climate' }, this.config.debug)
401
295
  .createDefaultBridgedDeviceBasicInformationClusterServer('Climate', 'CLI00008', 0xfff1, 'Matterbridge', 'Matterbridge Climate')
402
296
  .createDefaultPowerSourceReplaceableBatteryClusterServer(90, PowerSource.BatChargeLevel.Ok, 2990, '2 x AA', 2, PowerSource.BatReplaceability.UserReplaceable)
@@ -965,6 +859,34 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
965
859
  this.userPinLock?.subscribeAttribute(DoorLock, 'userCodeTemporaryDisableTime', (value) => {
966
860
  this.userPinLock?.log.info(`Subscribe userCodeTemporaryDisableTime called with: ${value}`);
967
861
  }, this.userPinLock.log);
862
+ this.scheduleLock = new MatterbridgeEndpoint([doorLock, bridgedNode, powerSource], { id: 'ScheduleLock' }, this.config.debug)
863
+ .createDefaultIdentifyClusterServer()
864
+ .createDefaultBridgedDeviceBasicInformationClusterServer('Lock with Schedules', 'LSC00080', 0xfff1, 'Matterbridge', 'Matterbridge Lock')
865
+ .createUserPinDoorLockClusterServer(DoorLock.LockState.Locked, DoorLock.LockType.DeadBolt, 0, 4, 10, 2, 3, 4)
866
+ .createDefaultPowerSourceRechargeableBatteryClusterServer(95)
867
+ .addRequiredClusterServers();
868
+ this.scheduleLock = await this.addDevice(this.scheduleLock);
869
+ this.scheduleLock?.addCommandHandler('Identify.identify', ({ request: { identifyTime } }) => {
870
+ this.scheduleLock?.log.info(`Command identify called identifyTime:${identifyTime}`);
871
+ });
872
+ this.scheduleLock?.addCommandHandler('DoorLock.lockDoor', () => {
873
+ this.scheduleLock?.log.info('Command lockDoor called');
874
+ });
875
+ this.scheduleLock?.addCommandHandler('DoorLock.unlockDoor', () => {
876
+ this.scheduleLock?.log.info('Command unlockDoor called');
877
+ });
878
+ this.scheduleLock?.addCommandHandler('DoorLock.setWeekDaySchedule', ({ request: { weekDayIndex, userIndex } }) => {
879
+ this.scheduleLock?.log.info(`Command setWeekDaySchedule called for weekDayIndex:${weekDayIndex} userIndex:${userIndex}`);
880
+ });
881
+ this.scheduleLock?.addCommandHandler('DoorLock.setYearDaySchedule', ({ request: { yearDayIndex, userIndex } }) => {
882
+ this.scheduleLock?.log.info(`Command setYearDaySchedule called for yearDayIndex:${yearDayIndex} userIndex:${userIndex}`);
883
+ });
884
+ this.scheduleLock?.addCommandHandler('DoorLock.setHolidaySchedule', ({ request: { holidayIndex } }) => {
885
+ this.scheduleLock?.log.info(`Command setHolidaySchedule called for holidayIndex:${holidayIndex}`);
886
+ });
887
+ this.scheduleLock?.subscribeAttribute(DoorLock, 'operatingMode', (value) => {
888
+ this.scheduleLock?.log.info(`Subscribe operatingMode called with: ${getEnumDescription(DoorLock.OperatingMode, value)}`);
889
+ }, this.scheduleLock.log);
968
890
  this.thermoAuto = new MatterbridgeEndpoint([thermostat, bridgedNode, powerSource], { id: 'Thermostat (AutoMode)' }, this.config.debug)
969
891
  .createDefaultIdentifyClusterServer()
970
892
  .createDefaultBridgedDeviceBasicInformationClusterServer('Thermostat (Auto)', 'TAU00023', 0xfff1, 'Matterbridge', 'Matterbridge Thermostat')
@@ -2451,6 +2373,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
2451
2373
  }
2452
2374
  }, 60 * 1000 + 500);
2453
2375
  }
2376
+ await this.scheduleLock?.setAttribute(DoorLock.id, 'lockState', DoorLock.LockState.Locked, this.scheduleLock.log);
2377
+ this.scheduleLock?.log.info('Set schedule lock initial lockState to Locked');
2454
2378
  await this.thermoAuto?.setAttribute(Thermostat.id, 'localTemperature', 16 * 100, this.thermoAuto.log);
2455
2379
  await this.thermoAuto?.setAttribute(Thermostat.id, 'systemMode', Thermostat.SystemMode.Auto, this.thermoAuto.log);
2456
2380
  if (this.thermoAuto?.hasAttributeServer(Thermostat.id, 'thermostatRunningState')) {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "3.0.3",
3
+ "version": "3.0.4-dev-20260902-044750d",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-example-dynamic-platform",
9
- "version": "3.0.3",
9
+ "version": "3.0.4-dev-20260902-044750d",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "node-ansi-logger": "3.3.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "3.0.3",
3
+ "version": "3.0.4-dev-20260902-044750d",
4
4
  "description": "Matterbridge dynamic plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",