@matterbridge/core 3.10.8-dev-20260829-e779e7d → 3.10.8-dev-20260831-3e81235
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/dist/demoDevices.js +49 -2
- package/dist/devices/closure.d.ts +7 -0
- package/dist/devices/closure.js +61 -32
- package/dist/devices/closurePanel.d.ts +21 -2
- package/dist/devices/closurePanel.js +79 -20
- package/dist/frontend.js +4 -2
- package/dist/matterbridgeEndpoint.d.ts +3 -1
- package/package.json +9 -9
package/dist/demoDevices.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { ClosureTag, ClosureWindowTag, CommodityTariffChronologyTag, CommodityTariffCommodityTag, CommodityTariffFlowTag, CommonNumberTag, CommonPositionTag, ElectricalMeasurementTag, PowerSourceTag, RefrigeratorTag, } from '@matter/node';
|
|
3
|
+
import { ClosureCoveringTag, ClosurePanelTag, ClosureTag, ClosureWindowTag, CommodityTariffChronologyTag, CommodityTariffCommodityTag, CommodityTariffFlowTag, CommonNumberTag, CommonPositionTag, ElectricalMeasurementTag, PowerSourceTag, RefrigeratorTag, } from '@matter/node';
|
|
4
4
|
import { AirQuality } from '@matter/types/clusters/air-quality';
|
|
5
|
+
import { ClosureDimension } from '@matter/types/clusters/closure-dimension';
|
|
5
6
|
import { DoorLock } from '@matter/types/clusters/door-lock';
|
|
6
7
|
import { FanControl } from '@matter/types/clusters/fan-control';
|
|
7
8
|
import { PowerSource } from '@matter/types/clusters/power-source';
|
|
@@ -354,13 +355,19 @@ export async function createDemoDevices(matterbridge) {
|
|
|
354
355
|
await registerDevice(ep, 'Window Covering Tilt', 'ENTRY-08-03-1');
|
|
355
356
|
ep = new MatterbridgeEndpoint([getSupportedDeviceType('WindowCoveringController'), bridgedNode, powerSource], { id: 'WindowCoveringController', number: EndpointNumber(8_04) });
|
|
356
357
|
await registerDevice(ep, 'Window Covering Controller', 'ENTRY-08-04');
|
|
357
|
-
ep = new Closure('Closure', 'ENTRY-08-05', {
|
|
358
|
+
ep = new Closure('Closure', 'ENTRY-08-05', {
|
|
359
|
+
id: 'Closure',
|
|
360
|
+
number: EndpointNumber(8_05),
|
|
361
|
+
movementDuration: 2000,
|
|
362
|
+
});
|
|
358
363
|
await registerDevice(ep, 'Closure', 'ENTRY-08-05');
|
|
359
364
|
ep = new Closure('Closure Pedestrian', 'ENTRY-08-05-1', {
|
|
360
365
|
id: 'ClosurePedestrian',
|
|
361
366
|
number: EndpointNumber(8_05_1),
|
|
362
367
|
movementDuration: 2000,
|
|
363
368
|
calibrationDuration: 2000,
|
|
369
|
+
motionLatching: true,
|
|
370
|
+
speed: true,
|
|
364
371
|
pedestrian: true,
|
|
365
372
|
tagList: [getSemtag(ClosureTag.Gate)],
|
|
366
373
|
});
|
|
@@ -370,6 +377,8 @@ export async function createDemoDevices(matterbridge) {
|
|
|
370
377
|
number: EndpointNumber(8_05_2),
|
|
371
378
|
movementDuration: 2000,
|
|
372
379
|
calibrationDuration: 2000,
|
|
380
|
+
motionLatching: true,
|
|
381
|
+
speed: true,
|
|
373
382
|
ventilation: true,
|
|
374
383
|
tagList: [getSemtag(ClosureTag.Window), getSemtag(ClosureWindowTag.Facade)],
|
|
375
384
|
});
|
|
@@ -379,6 +388,8 @@ export async function createDemoDevices(matterbridge) {
|
|
|
379
388
|
number: EndpointNumber(8_05_3),
|
|
380
389
|
movementDuration: 2000,
|
|
381
390
|
calibrationDuration: 2000,
|
|
391
|
+
motionLatching: true,
|
|
392
|
+
speed: true,
|
|
382
393
|
calibration: true,
|
|
383
394
|
tagList: [getSemtag(ClosureTag.GarageDoor)],
|
|
384
395
|
});
|
|
@@ -388,12 +399,48 @@ export async function createDemoDevices(matterbridge) {
|
|
|
388
399
|
number: EndpointNumber(8_05_4),
|
|
389
400
|
movementDuration: 2000,
|
|
390
401
|
calibrationDuration: 2000,
|
|
402
|
+
motionLatching: true,
|
|
403
|
+
speed: true,
|
|
391
404
|
ventilation: true,
|
|
392
405
|
pedestrian: true,
|
|
393
406
|
calibration: true,
|
|
394
407
|
tagList: [getSemtag(ClosureTag.Door)],
|
|
395
408
|
});
|
|
396
409
|
await registerDevice(ep, 'Closure Complete', 'ENTRY-08-05-4');
|
|
410
|
+
const closurePanelRoller = new Closure('Closure Panel Roller', 'ENTRY-08-06-1', {
|
|
411
|
+
id: 'ClosurePanelRoller',
|
|
412
|
+
number: EndpointNumber(8_06_1),
|
|
413
|
+
movementDuration: 2000,
|
|
414
|
+
signaturePosition: 50_00,
|
|
415
|
+
tagList: [getSemtag(ClosureTag.Covering)],
|
|
416
|
+
});
|
|
417
|
+
closurePanelRoller.addPanel('Roller', [getSemtag(ClosurePanelTag.Lift)], 'lift', { number: EndpointNumber(8_06_2), movementDuration: 2000 });
|
|
418
|
+
ep = closurePanelRoller;
|
|
419
|
+
await registerDevice(ep, 'Closure Panel Roller', 'ENTRY-08-06-1');
|
|
420
|
+
const closurePanelVenetian = new Closure('Closure Panel Venetian', 'ENTRY-08-06-3', {
|
|
421
|
+
id: 'ClosurePanelVenetian',
|
|
422
|
+
number: EndpointNumber(8_06_3),
|
|
423
|
+
movementDuration: 2000,
|
|
424
|
+
signaturePosition: 20_00,
|
|
425
|
+
tagList: [getSemtag(ClosureTag.Covering), getSemtag(ClosureCoveringTag.Venetian)],
|
|
426
|
+
});
|
|
427
|
+
closurePanelVenetian.addPanel('Venetian', [getSemtag(ClosurePanelTag.Tilt)], 'tilt', { number: EndpointNumber(8_06_4), movementDuration: 2000 });
|
|
428
|
+
ep = closurePanelVenetian;
|
|
429
|
+
await registerDevice(ep, 'Closure Panel Venetian', 'ENTRY-08-06-3');
|
|
430
|
+
const closurePanelSmartGlass = new Closure('Closure Panel Smart-Glass', 'ENTRY-08-06-5', {
|
|
431
|
+
id: 'ClosurePanelSmartGlass',
|
|
432
|
+
number: EndpointNumber(8_06_5),
|
|
433
|
+
movementDuration: 2000,
|
|
434
|
+
signaturePosition: 10_00,
|
|
435
|
+
tagList: [getSemtag(ClosureTag.Window)],
|
|
436
|
+
});
|
|
437
|
+
closurePanelSmartGlass.addPanel('Smart-Glass', [getSemtag(ClosurePanelTag.Lift, 'Opacity')], 'modulation', {
|
|
438
|
+
number: EndpointNumber(8_06_6),
|
|
439
|
+
modulationType: ClosureDimension.ModulationType.Opacity,
|
|
440
|
+
movementDuration: 2000,
|
|
441
|
+
});
|
|
442
|
+
ep = closurePanelSmartGlass;
|
|
443
|
+
await registerDevice(ep, 'Closure Panel Smart-Glass', 'ENTRY-08-06-5');
|
|
397
444
|
ep = new MatterbridgeEndpoint([getSupportedDeviceType('ClosureController'), bridgedNode, powerSource], { id: 'ClosureController', number: EndpointNumber(8_07) });
|
|
398
445
|
await registerDevice(ep, 'Closure Controller', 'ENTRY-08-07');
|
|
399
446
|
ep = new MatterbridgeEndpoint([getSupportedDeviceType('Thermostat'), bridgedNode, powerSource], { id: 'Thermostat', number: EndpointNumber(9_01) });
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { MaybePromise } from '@matter/general';
|
|
1
2
|
import { ClosureControlServer } from '@matter/node/behaviors/closure-control';
|
|
2
3
|
import { type EndpointNumber } from '@matter/types';
|
|
3
4
|
import { ClosureControl } from '@matter/types/clusters/closure-control';
|
|
@@ -19,6 +20,7 @@ declare const MatterbridgeClosureControlServerBase: import("@matter/node").Clust
|
|
|
19
20
|
export declare class MatterbridgeClosureControlServer extends MatterbridgeClosureControlServerBase {
|
|
20
21
|
readonly state: MatterbridgeClosureControlServer.State;
|
|
21
22
|
protected internal: MatterbridgeClosureControlServer.Internal;
|
|
23
|
+
initialize(): MaybePromise;
|
|
22
24
|
moveTo: (request: ClosureControl.MoveToRequest) => Promise<void>;
|
|
23
25
|
private completeMoveTo;
|
|
24
26
|
stop: () => Promise<void>;
|
|
@@ -33,6 +35,7 @@ export declare namespace MatterbridgeClosureControlServer {
|
|
|
33
35
|
class State extends MatterbridgeClosureControlServerBase.State {
|
|
34
36
|
movementDuration: number;
|
|
35
37
|
calibrationDuration: number;
|
|
38
|
+
signaturePosition: number;
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
41
|
export interface ClosureOptions {
|
|
@@ -47,6 +50,9 @@ export interface ClosureOptions {
|
|
|
47
50
|
latchControlModes?: ClosureControl.LatchControlModes;
|
|
48
51
|
movementDuration?: number;
|
|
49
52
|
calibrationDuration?: number;
|
|
53
|
+
signaturePosition?: number;
|
|
54
|
+
motionLatching?: boolean;
|
|
55
|
+
speed?: boolean;
|
|
50
56
|
ventilation?: boolean;
|
|
51
57
|
pedestrian?: boolean;
|
|
52
58
|
calibration?: boolean;
|
|
@@ -57,6 +63,7 @@ export interface ClosureOptions {
|
|
|
57
63
|
export declare class Closure extends MatterbridgeEndpoint {
|
|
58
64
|
constructor(name: string, serial: string, options?: ClosureOptions);
|
|
59
65
|
getMainState(): ClosureControl.MainState | undefined;
|
|
66
|
+
getSignaturePosition(): number | undefined;
|
|
60
67
|
setState(currentState: ClosureControl.OverallCurrentState, targetState: ClosureControl.OverallTargetState, mainState?: ClosureControl.MainState, countdownTime?: number, currentErrorList?: ClosureControl.ClosureError[]): Promise<void>;
|
|
61
68
|
setFullyClosed(): Promise<void>;
|
|
62
69
|
setFullOpened(): Promise<void>;
|
package/dist/devices/closure.js
CHANGED
|
@@ -18,6 +18,13 @@ const targetToCurrentPosition = {
|
|
|
18
18
|
};
|
|
19
19
|
const MatterbridgeClosureControlServerBase = ClosureControlServer.with(ClosureControl.Feature.Positioning, ClosureControl.Feature.MotionLatching, ClosureControl.Feature.Speed, ClosureControl.Feature.Calibration);
|
|
20
20
|
export class MatterbridgeClosureControlServer extends MatterbridgeClosureControlServerBase {
|
|
21
|
+
initialize() {
|
|
22
|
+
if (process.env.MATTERBRIDGE_CHIP_TEST) {
|
|
23
|
+
this.state.movementDuration = 2000;
|
|
24
|
+
this.state.calibrationDuration = 2000;
|
|
25
|
+
}
|
|
26
|
+
return super.initialize();
|
|
27
|
+
}
|
|
21
28
|
moveTo = async (request) => {
|
|
22
29
|
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
23
30
|
device.log.info(`MoveTo (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
@@ -31,38 +38,40 @@ export class MatterbridgeClosureControlServer extends MatterbridgeClosureControl
|
|
|
31
38
|
if (request.position !== undefined && !(request.position in targetToCurrentPosition)) {
|
|
32
39
|
throw new StatusResponse.ConstraintErrorError('ClosureControl.moveTo Position is not a valid TargetPositionEnum value');
|
|
33
40
|
}
|
|
34
|
-
if (request.
|
|
41
|
+
if (request.position === undefined && request.latch === undefined && request.speed === undefined) {
|
|
42
|
+
throw new StatusResponse.InvalidCommandError('ClosureControl.moveTo requires at least one of position, latch, or speed to be present');
|
|
43
|
+
}
|
|
44
|
+
if (request.position === undefined && (!this.features.motionLatching || request.latch === undefined) && (!this.features.speed || request.speed === undefined))
|
|
45
|
+
return;
|
|
46
|
+
if (this.features.motionLatching && request.latch !== undefined && typeof request.latch !== 'boolean') {
|
|
35
47
|
throw new StatusResponse.ConstraintErrorError('ClosureControl.moveTo Latch is not a boolean value');
|
|
36
48
|
}
|
|
37
|
-
if (request.speed !== undefined && !(request.speed in ThreeLevelAuto)) {
|
|
49
|
+
if (this.features.speed && request.speed !== undefined && !(request.speed in ThreeLevelAuto)) {
|
|
38
50
|
throw new StatusResponse.ConstraintErrorError('ClosureControl.moveTo Speed is not a valid ThreeLevelAutoEnum value');
|
|
39
51
|
}
|
|
40
|
-
if (request.position === undefined && request.latch === undefined && request.speed === undefined) {
|
|
41
|
-
throw new StatusResponse.InvalidCommandError('ClosureControl.moveTo requires at least one of position, latch, or speed to be present');
|
|
42
|
-
}
|
|
43
52
|
if (![ClosureControl.MainState.Moving, ClosureControl.MainState.WaitingForMotion, ClosureControl.MainState.Stopped].includes(this.state.mainState)) {
|
|
44
53
|
throw new StatusResponse.InvalidInStateError('ClosureControl.moveTo is only allowed while Moving, WaitingForMotion, or Stopped');
|
|
45
54
|
}
|
|
46
55
|
let currentState = this.state.overallCurrentState;
|
|
47
|
-
if (currentState?.latch === true && request.position !== undefined && request.latch !== false) {
|
|
56
|
+
if (this.features.motionLatching && currentState?.latch === true && request.position !== undefined && request.latch !== false) {
|
|
48
57
|
throw new StatusResponse.InvalidInStateError('ClosureControl.moveTo position changes require latch false while the closure is latched');
|
|
49
58
|
}
|
|
50
59
|
const previousTarget = this.state.overallTargetState ?? {};
|
|
51
60
|
const nextTarget = {
|
|
52
61
|
...previousTarget,
|
|
53
62
|
...(request?.position !== undefined ? { position: request.position } : null),
|
|
54
|
-
...(request?.latch !== undefined ? { latch: request.latch } : null),
|
|
55
|
-
speed: request?.speed ?? ThreeLevelAuto.Auto,
|
|
63
|
+
...(this.features.motionLatching && request?.latch !== undefined ? { latch: request.latch } : null),
|
|
64
|
+
...(this.features.speed ? { speed: request?.speed ?? ThreeLevelAuto.Auto } : null),
|
|
56
65
|
};
|
|
57
66
|
this.state.overallTargetState = nextTarget;
|
|
58
|
-
if (currentState !== null) {
|
|
67
|
+
if (this.features.speed && currentState !== null) {
|
|
59
68
|
currentState = { ...currentState, speed: nextTarget.speed };
|
|
60
69
|
this.state.overallCurrentState = currentState;
|
|
61
70
|
}
|
|
62
71
|
const isAtTarget = currentState !== null &&
|
|
63
72
|
(nextTarget.position === undefined || nextTarget.position === currentState.position) &&
|
|
64
|
-
(nextTarget.latch === undefined || nextTarget.latch === currentState.latch) &&
|
|
65
|
-
nextTarget.speed === currentState.speed;
|
|
73
|
+
(!this.features.motionLatching || nextTarget.latch === undefined || nextTarget.latch === currentState.latch) &&
|
|
74
|
+
(!this.features.speed || nextTarget.speed === currentState.speed);
|
|
66
75
|
this.state.mainState = isAtTarget ? ClosureControl.MainState.Stopped : ClosureControl.MainState.Moving;
|
|
67
76
|
clearTimeout(this.internal.movementTimer);
|
|
68
77
|
if (isAtTarget) {
|
|
@@ -90,8 +99,13 @@ export class MatterbridgeClosureControlServer extends MatterbridgeClosureControl
|
|
|
90
99
|
position = mappedPosition;
|
|
91
100
|
}
|
|
92
101
|
const latch = targetState.latch ?? previousState.latch;
|
|
93
|
-
const secureState = latch === true;
|
|
94
|
-
await closure.setState({
|
|
102
|
+
const secureState = this.features.motionLatching ? latch === true : position === ClosureControl.CurrentPosition.FullyClosed;
|
|
103
|
+
await closure.setState({
|
|
104
|
+
position,
|
|
105
|
+
...(this.features.motionLatching ? { latch } : null),
|
|
106
|
+
...(this.features.speed ? { speed: targetState.speed } : null),
|
|
107
|
+
secureState,
|
|
108
|
+
}, targetState, ClosureControl.MainState.Stopped, 0);
|
|
95
109
|
if (secureState !== previousState.secureState) {
|
|
96
110
|
await closure.triggerSecureStateChanged(secureState);
|
|
97
111
|
}
|
|
@@ -160,21 +174,13 @@ export class MatterbridgeClosureControlServer extends MatterbridgeClosureControl
|
|
|
160
174
|
class State extends MatterbridgeClosureControlServerBase.State {
|
|
161
175
|
movementDuration = 0;
|
|
162
176
|
calibrationDuration = 0;
|
|
177
|
+
signaturePosition = 50_00;
|
|
163
178
|
}
|
|
164
179
|
MatterbridgeClosureControlServer.State = State;
|
|
165
180
|
})(MatterbridgeClosureControlServer || (MatterbridgeClosureControlServer = {}));
|
|
166
181
|
export class Closure extends MatterbridgeEndpoint {
|
|
167
182
|
constructor(name, serial, options = {}) {
|
|
168
|
-
const { identifyTime = 0, identifyType = Identify.IdentifyType.None, powerSourceType = 'Wired', countdownTime = 0, mainState = ClosureControl.MainState.Stopped, currentErrorList = [], overallCurrentState =
|
|
169
|
-
position: ClosureControl.CurrentPosition.FullyClosed,
|
|
170
|
-
latch: true,
|
|
171
|
-
speed: ThreeLevelAuto.Auto,
|
|
172
|
-
secureState: true,
|
|
173
|
-
}, overallTargetState = {
|
|
174
|
-
position: ClosureControl.TargetPosition.MoveToFullyClosed,
|
|
175
|
-
latch: true,
|
|
176
|
-
speed: ThreeLevelAuto.Auto,
|
|
177
|
-
}, latchControlModes = { remoteLatching: true, remoteUnlatching: true }, movementDuration = 0, calibrationDuration = 0, ventilation = false, pedestrian = false, calibration = false, id, number, tagList = [getSemtag(ClosureTag.Covering)], } = options;
|
|
183
|
+
const { identifyTime = 0, identifyType = Identify.IdentifyType.None, powerSourceType = 'Wired', countdownTime = 0, mainState = ClosureControl.MainState.Stopped, currentErrorList = [], overallCurrentState, overallTargetState, latchControlModes, movementDuration = 0, calibrationDuration = 0, signaturePosition = 50_00, motionLatching = false, speed = false, ventilation = false, pedestrian = false, calibration = false, id, number, tagList = [getSemtag(ClosureTag.Covering)], } = options;
|
|
178
184
|
super(powerSourceType === 'None' ? [closure] : [closure, powerSource], {
|
|
179
185
|
id: id ?? `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}`,
|
|
180
186
|
number,
|
|
@@ -202,25 +208,48 @@ export class Closure extends MatterbridgeEndpoint {
|
|
|
202
208
|
countdownTime,
|
|
203
209
|
mainState,
|
|
204
210
|
currentErrorList,
|
|
205
|
-
overallCurrentState
|
|
206
|
-
|
|
207
|
-
|
|
211
|
+
overallCurrentState: {
|
|
212
|
+
position: overallCurrentState?.position ?? ClosureControl.CurrentPosition.FullyClosed,
|
|
213
|
+
...(motionLatching ? { latch: overallCurrentState?.latch ?? true } : null),
|
|
214
|
+
...(speed ? { speed: overallCurrentState?.speed ?? ThreeLevelAuto.Auto } : null),
|
|
215
|
+
secureState: overallCurrentState?.secureState ?? true,
|
|
216
|
+
},
|
|
217
|
+
overallTargetState: {
|
|
218
|
+
position: overallTargetState?.position ?? ClosureControl.TargetPosition.MoveToFullyClosed,
|
|
219
|
+
...(motionLatching ? { latch: overallTargetState?.latch ?? true } : null),
|
|
220
|
+
...(speed ? { speed: overallTargetState?.speed ?? ThreeLevelAuto.Auto } : null),
|
|
221
|
+
},
|
|
222
|
+
...(motionLatching ? { latchControlModes: latchControlModes ?? { remoteLatching: true, remoteUnlatching: true } } : null),
|
|
208
223
|
movementDuration,
|
|
209
224
|
calibrationDuration,
|
|
225
|
+
signaturePosition,
|
|
210
226
|
};
|
|
211
|
-
this.behaviors.require(calibration
|
|
212
|
-
? MatterbridgeClosureControlServer.with(ClosureControl.Feature.Positioning, ClosureControl.Feature.MotionLatching, ClosureControl.Feature.Speed, ...(calibration ? [ClosureControl.Feature.Calibration] : []), ...(ventilation ? [ClosureControl.Feature.Ventilation] : []), ...(pedestrian ? [ClosureControl.Feature.Pedestrian] : []))
|
|
213
|
-
: MatterbridgeClosureControlServer, closureControlOptions);
|
|
227
|
+
this.behaviors.require(MatterbridgeClosureControlServer.with(ClosureControl.Feature.Positioning, ...(motionLatching ? [ClosureControl.Feature.MotionLatching] : []), ...(speed ? [ClosureControl.Feature.Speed] : []), ...(calibration ? [ClosureControl.Feature.Calibration] : []), ...(ventilation ? [ClosureControl.Feature.Ventilation] : []), ...(pedestrian ? [ClosureControl.Feature.Pedestrian] : [])), closureControlOptions);
|
|
214
228
|
}
|
|
215
229
|
getMainState() {
|
|
216
230
|
return this.getAttribute(ClosureControlServer, 'mainState');
|
|
217
231
|
}
|
|
232
|
+
getSignaturePosition() {
|
|
233
|
+
return this.getAttribute(MatterbridgeClosureControlServer, 'signaturePosition');
|
|
234
|
+
}
|
|
218
235
|
async setState(currentState, targetState, mainState = ClosureControl.MainState.Stopped, countdownTime = 0, currentErrorList = []) {
|
|
236
|
+
const featureMap = this.getAttribute(ClosureControl.id, 'featureMap');
|
|
237
|
+
const supportsMotionLatching = featureMap?.motionLatching === true;
|
|
238
|
+
const supportsSpeed = featureMap?.speed === true;
|
|
219
239
|
await this.setAttribute(ClosureControl, 'countdownTime', countdownTime);
|
|
220
240
|
await this.setAttribute(ClosureControl, 'mainState', mainState);
|
|
221
241
|
await this.setAttribute(ClosureControl, 'currentErrorList', currentErrorList);
|
|
222
|
-
await this.setAttribute(ClosureControl, 'overallCurrentState',
|
|
223
|
-
|
|
242
|
+
await this.setAttribute(ClosureControl, 'overallCurrentState', {
|
|
243
|
+
position: currentState.position,
|
|
244
|
+
...(supportsMotionLatching ? { latch: currentState.latch } : null),
|
|
245
|
+
...(supportsSpeed ? { speed: currentState.speed } : null),
|
|
246
|
+
secureState: currentState.secureState,
|
|
247
|
+
});
|
|
248
|
+
await this.setAttribute(ClosureControl, 'overallTargetState', {
|
|
249
|
+
position: targetState.position,
|
|
250
|
+
...(supportsMotionLatching ? { latch: targetState.latch } : null),
|
|
251
|
+
...(supportsSpeed ? { speed: targetState.speed } : null),
|
|
252
|
+
});
|
|
224
253
|
}
|
|
225
254
|
async setFullyClosed() {
|
|
226
255
|
await this.setState({
|
|
@@ -270,7 +299,7 @@ export class Closure extends MatterbridgeEndpoint {
|
|
|
270
299
|
await this.triggerEvent(ClosureControl, 'secureStateChanged', { secureValue });
|
|
271
300
|
}
|
|
272
301
|
addPanel(name, tagList, dimensionType, options = {}) {
|
|
273
|
-
const panel = this.addChildDeviceType(name, closurePanel, { tagList });
|
|
302
|
+
const panel = this.addChildDeviceType(name, closurePanel, { number: options.number, tagList });
|
|
274
303
|
createClosureDimensionClusterServer(panel, dimensionType, options);
|
|
275
304
|
return panel;
|
|
276
305
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { MaybePromise } from '@matter/general';
|
|
1
2
|
import { ClosureDimensionServer } from '@matter/node/behaviors/closure-dimension';
|
|
2
3
|
import { ClosureDimension } from '@matter/types/clusters/closure-dimension';
|
|
4
|
+
import type { EndpointNumber } from '@matter/types/datatype';
|
|
3
5
|
import type { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
|
|
4
6
|
export type ClosureDimensionType = 'lift' | 'tilt' | 'modulation';
|
|
5
|
-
declare const
|
|
7
|
+
declare const MatterbridgeClosureDimensionServerBase: import("@matter/node").ClusterBehavior.Type<typeof ClosureDimensionServer, import("@matter/types").ClusterType.WithSupportedFeatures<ClosureDimension, {
|
|
6
8
|
limitation: false;
|
|
7
9
|
modulation: false;
|
|
8
10
|
motionLatching: true;
|
|
@@ -12,16 +14,33 @@ declare const MatterbridgeClosureDimensionServer_base: import("@matter/node").Cl
|
|
|
12
14
|
translation: false;
|
|
13
15
|
unit: false;
|
|
14
16
|
}>, import("@matter/types").ClusterType.Concrete, new () => {}, "closureDimension">;
|
|
15
|
-
export declare class MatterbridgeClosureDimensionServer extends
|
|
17
|
+
export declare class MatterbridgeClosureDimensionServer extends MatterbridgeClosureDimensionServerBase {
|
|
18
|
+
readonly state: MatterbridgeClosureDimensionServer.State;
|
|
19
|
+
protected internal: MatterbridgeClosureDimensionServer.Internal;
|
|
20
|
+
initialize(): MaybePromise;
|
|
16
21
|
setTarget: (request: ClosureDimension.SetTargetRequest) => Promise<void>;
|
|
17
22
|
step: (request: ClosureDimension.StepRequest) => Promise<void>;
|
|
23
|
+
private scheduleMovement;
|
|
24
|
+
private completeMovement;
|
|
25
|
+
}
|
|
26
|
+
export declare namespace MatterbridgeClosureDimensionServer {
|
|
27
|
+
class Internal extends MatterbridgeClosureDimensionServerBase.Internal {
|
|
28
|
+
movementTimer?: NodeJS.Timeout;
|
|
29
|
+
}
|
|
30
|
+
class State extends MatterbridgeClosureDimensionServerBase.State {
|
|
31
|
+
movementDuration: number;
|
|
32
|
+
}
|
|
18
33
|
}
|
|
19
34
|
export interface ClosurePanelOptions {
|
|
35
|
+
number?: EndpointNumber;
|
|
20
36
|
currentState?: ClosureDimension.DimensionState;
|
|
21
37
|
targetState?: ClosureDimension.DimensionState;
|
|
22
38
|
resolution?: number;
|
|
23
39
|
stepValue?: number;
|
|
40
|
+
motionLatching?: boolean;
|
|
41
|
+
speed?: boolean;
|
|
24
42
|
latchControlModes?: ClosureDimension.LatchControlModes;
|
|
43
|
+
movementDuration?: number;
|
|
25
44
|
translationDirection?: ClosureDimension.TranslationDirection;
|
|
26
45
|
rotationAxis?: ClosureDimension.RotationAxis;
|
|
27
46
|
overflow?: ClosureDimension.Overflow;
|
|
@@ -5,7 +5,14 @@ import { ClosureControl } from '@matter/types/clusters/closure-control';
|
|
|
5
5
|
import { ClosureDimension } from '@matter/types/clusters/closure-dimension';
|
|
6
6
|
import { ThreeLevelAuto } from '@matter/types/globals';
|
|
7
7
|
import { MatterbridgeServer } from '../behaviors/matterbridgeServer.js';
|
|
8
|
-
|
|
8
|
+
const MatterbridgeClosureDimensionServerBase = ClosureDimensionServer.with(ClosureDimension.Feature.Positioning, ClosureDimension.Feature.MotionLatching, ClosureDimension.Feature.Speed);
|
|
9
|
+
export class MatterbridgeClosureDimensionServer extends MatterbridgeClosureDimensionServerBase {
|
|
10
|
+
initialize() {
|
|
11
|
+
if (process.env.MATTERBRIDGE_CHIP_TEST) {
|
|
12
|
+
this.state.movementDuration = 2000;
|
|
13
|
+
}
|
|
14
|
+
return super.initialize();
|
|
15
|
+
}
|
|
9
16
|
setTarget = async (request) => {
|
|
10
17
|
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
11
18
|
device.log.info(`SetTarget (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
|
|
@@ -22,11 +29,16 @@ export class MatterbridgeClosureDimensionServer extends ClosureDimensionServer.w
|
|
|
22
29
|
if (request.position !== undefined && (request.position < 0 || request.position > 10000)) {
|
|
23
30
|
throw new StatusResponse.ConstraintErrorError('ClosureDimension.setTarget position must be between 0 and 10000');
|
|
24
31
|
}
|
|
32
|
+
const hasSupportedField = request.position !== undefined || (this.features.motionLatching && request.latch !== undefined) || (this.features.speed && request.speed !== undefined);
|
|
33
|
+
if (!hasSupportedField)
|
|
34
|
+
return;
|
|
25
35
|
const latchControlModes = this.state.latchControlModes;
|
|
26
|
-
if (
|
|
36
|
+
if (this.features.motionLatching &&
|
|
37
|
+
request.latch !== undefined &&
|
|
38
|
+
((request.latch && !latchControlModes?.remoteLatching) || (!request.latch && !latchControlModes?.remoteUnlatching))) {
|
|
27
39
|
throw new StatusResponse.InvalidInStateError('ClosureDimension.setTarget latch change requires manual intervention per LatchControlModes');
|
|
28
40
|
}
|
|
29
|
-
if (request.speed !== undefined && (request.speed < ThreeLevelAuto.Auto || request.speed > ThreeLevelAuto.High)) {
|
|
41
|
+
if (this.features.speed && request.speed !== undefined && (request.speed < ThreeLevelAuto.Auto || request.speed > ThreeLevelAuto.High)) {
|
|
30
42
|
throw new StatusResponse.ConstraintErrorError('ClosureDimension.setTarget speed must be a valid ThreeLevelAutoEnum value');
|
|
31
43
|
}
|
|
32
44
|
const associatedMainState = this.endpoint.owner?.maybeStateOf(ClosureControlServer)?.mainState;
|
|
@@ -41,7 +53,7 @@ export class MatterbridgeClosureDimensionServer extends ClosureDimensionServer.w
|
|
|
41
53
|
throw new StatusResponse.InvalidInStateError('ClosureDimension.setTarget is not allowed while the associated ClosureControl is Disengaged, Protected, Calibrating, SetupRequired, or Error');
|
|
42
54
|
}
|
|
43
55
|
const currentState = this.state.currentState;
|
|
44
|
-
if (request.position !== undefined && currentState?.latch === true && request.latch !== false) {
|
|
56
|
+
if (this.features.motionLatching && request.position !== undefined && currentState?.latch === true && request.latch !== false) {
|
|
45
57
|
throw new StatusResponse.InvalidInStateError('ClosureDimension.setTarget position changes require latch false while the closure is latched');
|
|
46
58
|
}
|
|
47
59
|
const previousTarget = this.state.targetState ?? {};
|
|
@@ -49,16 +61,17 @@ export class MatterbridgeClosureDimensionServer extends ClosureDimensionServer.w
|
|
|
49
61
|
const nextTarget = {
|
|
50
62
|
...previousTarget,
|
|
51
63
|
...(request?.position !== undefined ? { position: Math.round(request.position / resolution) * resolution } : null),
|
|
52
|
-
...(request?.latch !== undefined ? { latch: request.latch } : null),
|
|
53
|
-
speed: request?.speed ?? ThreeLevelAuto.Auto,
|
|
64
|
+
...(this.features.motionLatching && request?.latch !== undefined ? { latch: request.latch } : null),
|
|
65
|
+
...(this.features.speed ? { speed: request?.speed ?? ThreeLevelAuto.Auto } : null),
|
|
54
66
|
};
|
|
55
67
|
const matchesCurrentState = currentState !== null &&
|
|
56
68
|
(nextTarget.position === undefined || nextTarget.position === currentState.position) &&
|
|
57
|
-
(nextTarget.latch === undefined || nextTarget.latch === currentState.latch) &&
|
|
58
|
-
nextTarget.speed === currentState.speed;
|
|
69
|
+
(!this.features.motionLatching || nextTarget.latch === undefined || nextTarget.latch === currentState.latch) &&
|
|
70
|
+
(!this.features.speed || nextTarget.speed === currentState.speed);
|
|
59
71
|
if (matchesCurrentState)
|
|
60
72
|
return;
|
|
61
73
|
this.state.targetState = nextTarget;
|
|
74
|
+
this.scheduleMovement(nextTarget, currentState);
|
|
62
75
|
};
|
|
63
76
|
step = async (request) => {
|
|
64
77
|
const device = this.endpoint.stateOf(MatterbridgeServer);
|
|
@@ -76,11 +89,11 @@ export class MatterbridgeClosureDimensionServer extends ClosureDimensionServer.w
|
|
|
76
89
|
if (request.numberOfSteps < 1) {
|
|
77
90
|
throw new StatusResponse.ConstraintErrorError('ClosureDimension.step numberOfSteps must be at least 1');
|
|
78
91
|
}
|
|
79
|
-
if (request.speed !== undefined && (request.speed < ThreeLevelAuto.Auto || request.speed > ThreeLevelAuto.High)) {
|
|
92
|
+
if (this.features.speed && request.speed !== undefined && (request.speed < ThreeLevelAuto.Auto || request.speed > ThreeLevelAuto.High)) {
|
|
80
93
|
throw new StatusResponse.ConstraintErrorError('ClosureDimension.step speed must be a valid ThreeLevelAutoEnum value');
|
|
81
94
|
}
|
|
82
95
|
const currentState = this.state.currentState;
|
|
83
|
-
if (currentState?.latch === true) {
|
|
96
|
+
if (this.features.motionLatching && currentState?.latch === true) {
|
|
84
97
|
throw new StatusResponse.InvalidInStateError('ClosureDimension.step is not allowed while the closure is latched');
|
|
85
98
|
}
|
|
86
99
|
const associatedMainState = this.endpoint.owner?.maybeStateOf(ClosureControlServer)?.mainState;
|
|
@@ -102,36 +115,82 @@ export class MatterbridgeClosureDimensionServer extends ClosureDimensionServer.w
|
|
|
102
115
|
let nextPosition = isIncrease ? currentPosition + delta : currentPosition - delta;
|
|
103
116
|
nextPosition = Math.max(0, Math.min(10000, nextPosition));
|
|
104
117
|
const previousTarget = this.state.targetState ?? {};
|
|
105
|
-
|
|
118
|
+
const nextTarget = {
|
|
106
119
|
...previousTarget,
|
|
107
120
|
position: nextPosition,
|
|
108
|
-
...(request.speed !== undefined ? { speed: request.speed } : null),
|
|
121
|
+
...(this.features.speed && request.speed !== undefined ? { speed: request.speed } : null),
|
|
109
122
|
};
|
|
123
|
+
this.state.targetState = nextTarget;
|
|
124
|
+
this.scheduleMovement(nextTarget, currentState);
|
|
125
|
+
};
|
|
126
|
+
scheduleMovement(targetState, currentState) {
|
|
127
|
+
clearTimeout(this.internal.movementTimer);
|
|
128
|
+
if (currentState === null || this.state.movementDuration <= 0) {
|
|
129
|
+
this.internal.movementTimer = undefined;
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const previousState = currentState;
|
|
133
|
+
this.internal.movementTimer = setTimeout(() => {
|
|
134
|
+
this.internal.movementTimer = undefined;
|
|
135
|
+
void this.completeMovement(targetState, previousState);
|
|
136
|
+
}, this.state.movementDuration);
|
|
137
|
+
}
|
|
138
|
+
completeMovement = async (targetState, previousState) => {
|
|
139
|
+
const endpoint = this.endpoint;
|
|
140
|
+
const position = targetState.position ?? previousState.position;
|
|
141
|
+
const latch = targetState.latch ?? previousState.latch;
|
|
142
|
+
const speed = targetState.speed ?? previousState.speed;
|
|
143
|
+
await endpoint.setAttribute(ClosureDimensionServer, 'currentState', {
|
|
144
|
+
position,
|
|
145
|
+
...(this.features.motionLatching ? { latch } : null),
|
|
146
|
+
...(this.features.speed ? { speed } : null),
|
|
147
|
+
});
|
|
110
148
|
};
|
|
111
149
|
}
|
|
150
|
+
(function (MatterbridgeClosureDimensionServer) {
|
|
151
|
+
class Internal extends MatterbridgeClosureDimensionServerBase.Internal {
|
|
152
|
+
movementTimer;
|
|
153
|
+
}
|
|
154
|
+
MatterbridgeClosureDimensionServer.Internal = Internal;
|
|
155
|
+
class State extends MatterbridgeClosureDimensionServerBase.State {
|
|
156
|
+
movementDuration = 0;
|
|
157
|
+
}
|
|
158
|
+
MatterbridgeClosureDimensionServer.State = State;
|
|
159
|
+
})(MatterbridgeClosureDimensionServer || (MatterbridgeClosureDimensionServer = {}));
|
|
112
160
|
export function createClosureDimensionClusterServer(endpoint, dimensionType, options) {
|
|
161
|
+
const motionLatching = options.motionLatching ?? false;
|
|
162
|
+
const speed = options.speed ?? false;
|
|
113
163
|
const commonOptions = {
|
|
114
|
-
currentState:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
164
|
+
currentState: {
|
|
165
|
+
position: options.currentState?.position ?? 0,
|
|
166
|
+
...(motionLatching ? { latch: options.currentState?.latch ?? true } : null),
|
|
167
|
+
...(speed ? { speed: options.currentState?.speed ?? ThreeLevelAuto.Auto } : null),
|
|
168
|
+
},
|
|
169
|
+
targetState: {
|
|
170
|
+
position: options.targetState?.position ?? 0,
|
|
171
|
+
...(motionLatching ? { latch: options.targetState?.latch ?? true } : null),
|
|
172
|
+
...(speed ? { speed: options.targetState?.speed ?? ThreeLevelAuto.Auto } : null),
|
|
173
|
+
},
|
|
174
|
+
resolution: Math.max(1, options.resolution ?? 100),
|
|
175
|
+
stepValue: Math.max(1, options.stepValue ?? 100),
|
|
176
|
+
...(motionLatching ? { latchControlModes: options.latchControlModes ?? { remoteLatching: true, remoteUnlatching: true } } : null),
|
|
177
|
+
movementDuration: options.movementDuration ?? 0,
|
|
119
178
|
};
|
|
120
179
|
if (dimensionType === 'lift') {
|
|
121
|
-
endpoint.behaviors.require(MatterbridgeClosureDimensionServer.with(ClosureDimension.Feature.Positioning, ClosureDimension.Feature.
|
|
180
|
+
endpoint.behaviors.require(MatterbridgeClosureDimensionServer.with(ClosureDimension.Feature.Positioning, ClosureDimension.Feature.Translation, ...(motionLatching ? [ClosureDimension.Feature.MotionLatching] : []), ...(speed ? [ClosureDimension.Feature.Speed] : [])), {
|
|
122
181
|
...commonOptions,
|
|
123
182
|
translationDirection: options.translationDirection ?? ClosureDimension.TranslationDirection.Downward,
|
|
124
183
|
});
|
|
125
184
|
}
|
|
126
185
|
else if (dimensionType === 'tilt') {
|
|
127
|
-
endpoint.behaviors.require(MatterbridgeClosureDimensionServer.with(ClosureDimension.Feature.Positioning, ClosureDimension.Feature.
|
|
186
|
+
endpoint.behaviors.require(MatterbridgeClosureDimensionServer.with(ClosureDimension.Feature.Positioning, ClosureDimension.Feature.Rotation, ...(motionLatching ? [ClosureDimension.Feature.MotionLatching] : []), ...(speed ? [ClosureDimension.Feature.Speed] : [])), {
|
|
128
187
|
...commonOptions,
|
|
129
188
|
rotationAxis: options.rotationAxis ?? ClosureDimension.RotationAxis.CenteredHorizontal,
|
|
130
189
|
overflow: options.overflow ?? ClosureDimension.Overflow.NoOverflow,
|
|
131
190
|
});
|
|
132
191
|
}
|
|
133
192
|
else {
|
|
134
|
-
endpoint.behaviors.require(MatterbridgeClosureDimensionServer.with(ClosureDimension.Feature.Positioning, ClosureDimension.Feature.
|
|
193
|
+
endpoint.behaviors.require(MatterbridgeClosureDimensionServer.with(ClosureDimension.Feature.Positioning, ClosureDimension.Feature.Modulation, ...(motionLatching ? [ClosureDimension.Feature.MotionLatching] : []), ...(speed ? [ClosureDimension.Feature.Speed] : [])), {
|
|
135
194
|
...commonOptions,
|
|
136
195
|
modulationType: options.modulationType ?? ClosureDimension.ModulationType.SlatsOrientation,
|
|
137
196
|
});
|
package/dist/frontend.js
CHANGED
|
@@ -1131,8 +1131,10 @@ export class Frontend extends EventEmitter {
|
|
|
1131
1131
|
if (clusterName === 'closureControl' && attributeName === 'overallCurrentState' && isValidObject(attributeValue)) {
|
|
1132
1132
|
const overallCurrentState = attributeValue;
|
|
1133
1133
|
attributes += `Position: ${getEnumDescription(ClosureControl.CurrentPosition, overallCurrentState.position, { fallback: 'unknown' })} `;
|
|
1134
|
-
|
|
1135
|
-
|
|
1134
|
+
if (overallCurrentState.latch !== undefined)
|
|
1135
|
+
attributes += `Latch: ${overallCurrentState.latch} `;
|
|
1136
|
+
if (overallCurrentState.speed !== undefined)
|
|
1137
|
+
attributes += `Speed: ${getEnumDescription(ThreeLevelAuto, overallCurrentState.speed)} `;
|
|
1136
1138
|
attributes += `SecureState: ${overallCurrentState.secureState ?? 'unknown'} `;
|
|
1137
1139
|
}
|
|
1138
1140
|
if (clusterName === 'thermostat' && attributeName === 'localTemperature' && isValidNumber(attributeValue))
|
|
@@ -90,7 +90,9 @@ export declare class MatterbridgeEndpoint extends Endpoint {
|
|
|
90
90
|
getDeviceTypes(): DeviceTypeDefinition[];
|
|
91
91
|
hasClusterServer(cluster: Behavior.Type | ClusterType | ClusterId | string): boolean;
|
|
92
92
|
hasAttributeServer(cluster: Behavior.Type | ClusterType | ClusterId | string, attribute: string): boolean;
|
|
93
|
-
getClusterServerOptions
|
|
93
|
+
getClusterServerOptions<T extends Behavior.Type>(cluster: T): Partial<Behavior.StateOf<T>> | undefined;
|
|
94
|
+
getClusterServerOptions<T extends ClusterType>(cluster: T): Partial<ClusterAttributesOf<T>> | undefined;
|
|
95
|
+
getClusterServerOptions(cluster: ClusterId | string): Record<string, boolean | number | bigint | string | object | null> | undefined;
|
|
94
96
|
getAttribute<T extends Behavior.Type, A extends keyof Behavior.StateOf<T>>(cluster: T, attribute: A, log?: AnsiLogger): Behavior.StateOf<T>[A] | undefined;
|
|
95
97
|
getAttribute<T extends ClusterType, A extends keyof ClusterAttributesOf<T>>(cluster: T, attribute: A, log?: AnsiLogger): ClusterAttributesOf<T>[A] | undefined;
|
|
96
98
|
getAttribute(cluster: ClusterId | string, attribute: string, log?: AnsiLogger): any;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matterbridge/core",
|
|
3
|
-
"version": "3.10.8-dev-
|
|
3
|
+
"version": "3.10.8-dev-20260831-3e81235",
|
|
4
4
|
"description": "Matterbridge core library",
|
|
5
5
|
"author": "https://github.com/Luligu",
|
|
6
6
|
"homepage": "https://matterbridge.io/",
|
|
@@ -132,16 +132,16 @@
|
|
|
132
132
|
},
|
|
133
133
|
"dependencies": {
|
|
134
134
|
"@matter/main": "0.17.9",
|
|
135
|
-
"@matterbridge/dgram": "3.10.8-dev-
|
|
136
|
-
"@matterbridge/thread": "3.10.8-dev-
|
|
137
|
-
"@matterbridge/types": "3.10.8-dev-
|
|
138
|
-
"@matterbridge/utils": "3.10.8-dev-
|
|
135
|
+
"@matterbridge/dgram": "3.10.8-dev-20260831-3e81235",
|
|
136
|
+
"@matterbridge/thread": "3.10.8-dev-20260831-3e81235",
|
|
137
|
+
"@matterbridge/types": "3.10.8-dev-20260831-3e81235",
|
|
138
|
+
"@matterbridge/utils": "3.10.8-dev-20260831-3e81235",
|
|
139
139
|
"escape-html": "1.0.3",
|
|
140
140
|
"express": "5.2.1",
|
|
141
|
-
"express-rate-limit": "8.
|
|
142
|
-
"multer": "2.
|
|
143
|
-
"node-ansi-logger": "3.3.
|
|
144
|
-
"node-persist-manager": "2.1.
|
|
141
|
+
"express-rate-limit": "8.7.0",
|
|
142
|
+
"multer": "2.3.0",
|
|
143
|
+
"node-ansi-logger": "3.3.1",
|
|
144
|
+
"node-persist-manager": "2.1.1",
|
|
145
145
|
"ws": "8.21.3"
|
|
146
146
|
}
|
|
147
147
|
}
|