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 +9 -0
- package/README.md +3 -1
- package/dist/module.js +75 -151
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
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
|
|
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,
|
|
2
|
-
import { AirConditioner, BasicVideoPlayer, BatteryStorage, Closure, Cooktop, Dishwasher, Evse, ExtractorHood, HeatPump, IrrigationSystem, LaundryDryer, LaundryWasher,
|
|
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 {
|
|
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.
|
|
114
|
-
throw new Error(`This plugin requires Matterbridge version >= "3.10.
|
|
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
|
-
|
|
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', (
|
|
177
|
+
this.closureGarageDoor?.addCommandHandler('ClosureControl.stop', () => {
|
|
214
178
|
this.closureGarageDoor?.log.info('Command stop called');
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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,
|
|
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
|
|
272
|
-
void panel.setAttribute(ClosureDimension, 'targetState', { position
|
|
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 =
|
|
286
|
-
|
|
287
|
-
|
|
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')) {
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge-example-dynamic-platform",
|
|
3
|
-
"version": "3.0.
|
|
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.
|
|
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",
|