@matterbridge/core 3.10.6 → 3.10.7-dev-20260823-f911eb8

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.
@@ -1,3 +1,4 @@
1
+ import type { MaybePromise } from '@matter/general';
1
2
  import { ColorControlServer } from '@matter/node/behaviors/color-control';
2
3
  import { ColorControl } from '@matter/types/clusters/color-control';
3
4
  declare const MatterbridgeColorControlServer_base: import("@matter/node").ClusterBehavior.Type<typeof ColorControlServer, import("@matter/types").ClusterType.WithSupportedFeatures<ColorControl, {
@@ -8,6 +9,7 @@ declare const MatterbridgeColorControlServer_base: import("@matter/node").Cluste
8
9
  xy: true;
9
10
  }>, import("@matter/types").ClusterType.Concrete, typeof import("@matter/node/behaviors/color-control").ColorControlBaseServer.Internal, "colorControl">;
10
11
  export declare class MatterbridgeColorControlServer extends MatterbridgeColorControlServer_base {
12
+ initialize(): MaybePromise;
11
13
  moveToHue(request: ColorControl.MoveToHueRequest): Promise<void>;
12
14
  moveHue(request: ColorControl.MoveHueRequest): Promise<void>;
13
15
  stepHue(request: ColorControl.StepHueRequest): Promise<void>;
@@ -2,9 +2,18 @@ import { ColorControlServer } from '@matter/node/behaviors/color-control';
2
2
  import { ColorControl } from '@matter/types/clusters/color-control';
3
3
  import { MatterbridgeServer } from './matterbridgeServer.js';
4
4
  export class MatterbridgeColorControlServer extends ColorControlServer.with(ColorControl.Feature.HueSaturation, ColorControl.Feature.Xy, ColorControl.Feature.ColorTemperature, ColorControl.Feature.EnhancedHue) {
5
+ initialize() {
6
+ if (process.env.MATTERBRIDGE_CHIP_TEST)
7
+ this.state.managedTransitionTimeHandling = true;
8
+ return super.initialize();
9
+ }
5
10
  async moveToHue(request) {
6
11
  const device = this.endpoint.stateOf(MatterbridgeServer);
7
12
  device.log.info(`Setting hue to ${request.hue} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
13
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
14
+ await super.moveToHue(request);
15
+ return;
16
+ }
8
17
  await device.commandHandler.executeHandler('ColorControl.moveToHue', {
9
18
  command: 'moveToHue',
10
19
  request,
@@ -19,6 +28,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
19
28
  async moveHue(request) {
20
29
  const device = this.endpoint.stateOf(MatterbridgeServer);
21
30
  device.log.info(`Moving hue with mode ${request.moveMode} and rate ${request.rate} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
31
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
32
+ await super.moveHue(request);
33
+ return;
34
+ }
22
35
  await device.commandHandler.executeHandler('ColorControl.moveHue', {
23
36
  command: 'moveHue',
24
37
  request,
@@ -33,6 +46,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
33
46
  async stepHue(request) {
34
47
  const device = this.endpoint.stateOf(MatterbridgeServer);
35
48
  device.log.info(`Stepping hue with mode ${request.stepMode} and size ${request.stepSize} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
49
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
50
+ await super.stepHue(request);
51
+ return;
52
+ }
36
53
  await device.commandHandler.executeHandler('ColorControl.stepHue', {
37
54
  command: 'stepHue',
38
55
  request,
@@ -47,6 +64,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
47
64
  async enhancedMoveToHue(request) {
48
65
  const device = this.endpoint.stateOf(MatterbridgeServer);
49
66
  device.log.info(`Setting enhanced hue to ${request.enhancedHue} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
67
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
68
+ await super.enhancedMoveToHue(request);
69
+ return;
70
+ }
50
71
  await device.commandHandler.executeHandler('ColorControl.enhancedMoveToHue', {
51
72
  command: 'enhancedMoveToHue',
52
73
  request,
@@ -61,6 +82,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
61
82
  async enhancedMoveHue(request) {
62
83
  const device = this.endpoint.stateOf(MatterbridgeServer);
63
84
  device.log.info(`Moving enhanced hue with mode ${request.moveMode} and rate ${request.rate} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
85
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
86
+ await super.enhancedMoveHue(request);
87
+ return;
88
+ }
64
89
  await device.commandHandler.executeHandler('ColorControl.enhancedMoveHue', {
65
90
  command: 'enhancedMoveHue',
66
91
  request,
@@ -75,6 +100,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
75
100
  async enhancedStepHue(request) {
76
101
  const device = this.endpoint.stateOf(MatterbridgeServer);
77
102
  device.log.info(`Stepping enhanced hue with mode ${request.stepMode} and size ${request.stepSize} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
103
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
104
+ await super.enhancedStepHue(request);
105
+ return;
106
+ }
78
107
  await device.commandHandler.executeHandler('ColorControl.enhancedStepHue', {
79
108
  command: 'enhancedStepHue',
80
109
  request,
@@ -89,6 +118,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
89
118
  async moveToSaturation(request) {
90
119
  const device = this.endpoint.stateOf(MatterbridgeServer);
91
120
  device.log.info(`Setting saturation to ${request.saturation} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
121
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
122
+ await super.moveToSaturation(request);
123
+ return;
124
+ }
92
125
  await device.commandHandler.executeHandler('ColorControl.moveToSaturation', {
93
126
  command: 'moveToSaturation',
94
127
  request,
@@ -103,6 +136,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
103
136
  async moveSaturation(request) {
104
137
  const device = this.endpoint.stateOf(MatterbridgeServer);
105
138
  device.log.info(`Moving saturation with mode ${request.moveMode} and rate ${request.rate} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
139
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
140
+ await super.moveSaturation(request);
141
+ return;
142
+ }
106
143
  await device.commandHandler.executeHandler('ColorControl.moveSaturation', {
107
144
  command: 'moveSaturation',
108
145
  request,
@@ -117,6 +154,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
117
154
  async stepSaturation(request) {
118
155
  const device = this.endpoint.stateOf(MatterbridgeServer);
119
156
  device.log.info(`Stepping saturation with mode ${request.stepMode} and size ${request.stepSize} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
157
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
158
+ await super.stepSaturation(request);
159
+ return;
160
+ }
120
161
  await device.commandHandler.executeHandler('ColorControl.stepSaturation', {
121
162
  command: 'stepSaturation',
122
163
  request,
@@ -131,6 +172,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
131
172
  async moveToHueAndSaturation(request) {
132
173
  const device = this.endpoint.stateOf(MatterbridgeServer);
133
174
  device.log.info(`Setting hue to ${request.hue} and saturation to ${request.saturation} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
175
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
176
+ await super.moveToHueAndSaturation(request);
177
+ return;
178
+ }
134
179
  await device.commandHandler.executeHandler('ColorControl.moveToHueAndSaturation', {
135
180
  command: 'moveToHueAndSaturation',
136
181
  request,
@@ -145,6 +190,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
145
190
  async enhancedMoveToHueAndSaturation(request) {
146
191
  const device = this.endpoint.stateOf(MatterbridgeServer);
147
192
  device.log.info(`Setting enhanced hue to ${request.enhancedHue} and saturation to ${request.saturation} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
193
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
194
+ await super.enhancedMoveToHueAndSaturation(request);
195
+ return;
196
+ }
148
197
  await device.commandHandler.executeHandler('ColorControl.enhancedMoveToHueAndSaturation', {
149
198
  command: 'enhancedMoveToHueAndSaturation',
150
199
  request,
@@ -159,6 +208,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
159
208
  async moveToColor(request) {
160
209
  const device = this.endpoint.stateOf(MatterbridgeServer);
161
210
  device.log.info(`Setting color to ${request.colorX}, ${request.colorY} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
211
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
212
+ await super.moveToColor(request);
213
+ return;
214
+ }
162
215
  await device.commandHandler.executeHandler('ColorControl.moveToColor', {
163
216
  command: 'moveToColor',
164
217
  request,
@@ -173,6 +226,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
173
226
  async moveColor(request) {
174
227
  const device = this.endpoint.stateOf(MatterbridgeServer);
175
228
  device.log.info(`Moving color with rateX ${request.rateX} and rateY ${request.rateY} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
229
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
230
+ await super.moveColor(request);
231
+ return;
232
+ }
176
233
  await device.commandHandler.executeHandler('ColorControl.moveColor', {
177
234
  command: 'moveColor',
178
235
  request,
@@ -187,6 +244,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
187
244
  async stepColor(request) {
188
245
  const device = this.endpoint.stateOf(MatterbridgeServer);
189
246
  device.log.info(`Stepping color with stepX ${request.stepX} and stepY ${request.stepY} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
247
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
248
+ await super.stepColor(request);
249
+ return;
250
+ }
190
251
  await device.commandHandler.executeHandler('ColorControl.stepColor', {
191
252
  command: 'stepColor',
192
253
  request,
@@ -201,6 +262,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
201
262
  async moveToColorTemperature(request) {
202
263
  const device = this.endpoint.stateOf(MatterbridgeServer);
203
264
  device.log.info(`Setting color temperature to ${request.colorTemperatureMireds} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
265
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
266
+ await super.moveToColorTemperature(request);
267
+ return;
268
+ }
204
269
  await device.commandHandler.executeHandler('ColorControl.moveToColorTemperature', {
205
270
  command: 'moveToColorTemperature',
206
271
  request,
@@ -215,6 +280,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
215
280
  async moveColorTemperature(request) {
216
281
  const device = this.endpoint.stateOf(MatterbridgeServer);
217
282
  device.log.info(`Moving color temperature with mode ${request.moveMode} and rate ${request.rate} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
283
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
284
+ await super.moveColorTemperature(request);
285
+ return;
286
+ }
218
287
  await device.commandHandler.executeHandler('ColorControl.moveColorTemperature', {
219
288
  command: 'moveColorTemperature',
220
289
  request,
@@ -229,6 +298,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
229
298
  async stepColorTemperature(request) {
230
299
  const device = this.endpoint.stateOf(MatterbridgeServer);
231
300
  device.log.info(`Stepping color temperature with mode ${request.stepMode} and size ${request.stepSize} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
301
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
302
+ await super.stepColorTemperature(request);
303
+ return;
304
+ }
232
305
  await device.commandHandler.executeHandler('ColorControl.stepColorTemperature', {
233
306
  command: 'stepColorTemperature',
234
307
  request,
@@ -243,6 +316,10 @@ export class MatterbridgeColorControlServer extends ColorControlServer.with(Colo
243
316
  async stopMoveStep(request) {
244
317
  const device = this.endpoint.stateOf(MatterbridgeServer);
245
318
  device.log.info(`Stopping color move/step (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
319
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
320
+ await super.stopMoveStep(request);
321
+ return;
322
+ }
246
323
  await device.commandHandler.executeHandler('ColorControl.stopMoveStep', {
247
324
  command: 'stopMoveStep',
248
325
  request,
@@ -1,6 +1,8 @@
1
+ import type { MaybePromise } from '@matter/general';
1
2
  import { LevelControlServer } from '@matter/node/behaviors/level-control';
2
3
  import type { LevelControl } from '@matter/types/clusters/level-control';
3
4
  export declare class MatterbridgeLevelControlServer extends LevelControlServer {
5
+ initialize(): MaybePromise;
4
6
  moveToLevel(request: LevelControl.MoveToLevelRequest): Promise<void>;
5
7
  moveToLevelWithOnOff(request: LevelControl.MoveToLevelRequest): Promise<void>;
6
8
  move(request: LevelControl.MoveRequest): Promise<void>;
@@ -1,9 +1,18 @@
1
1
  import { LevelControlServer } from '@matter/node/behaviors/level-control';
2
2
  import { MatterbridgeServer } from './matterbridgeServer.js';
3
3
  export class MatterbridgeLevelControlServer extends LevelControlServer {
4
+ initialize() {
5
+ if (process.env.MATTERBRIDGE_CHIP_TEST)
6
+ this.state.managedTransitionTimeHandling = true;
7
+ return super.initialize();
8
+ }
4
9
  async moveToLevel(request) {
5
10
  const device = this.endpoint.stateOf(MatterbridgeServer);
6
11
  device.log.info(`Setting level to ${request.level} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
12
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
13
+ await super.moveToLevel(request);
14
+ return;
15
+ }
7
16
  await device.commandHandler.executeHandler('LevelControl.moveToLevel', {
8
17
  command: 'moveToLevel',
9
18
  request,
@@ -18,6 +27,10 @@ export class MatterbridgeLevelControlServer extends LevelControlServer {
18
27
  async moveToLevelWithOnOff(request) {
19
28
  const device = this.endpoint.stateOf(MatterbridgeServer);
20
29
  device.log.info(`Setting level to ${request.level} with transitionTime ${request.transitionTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
30
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
31
+ await super.moveToLevelWithOnOff(request);
32
+ return;
33
+ }
21
34
  await device.commandHandler.executeHandler('LevelControl.moveToLevelWithOnOff', {
22
35
  command: 'moveToLevelWithOnOff',
23
36
  request,
@@ -32,6 +45,10 @@ export class MatterbridgeLevelControlServer extends LevelControlServer {
32
45
  async move(request) {
33
46
  const device = this.endpoint.stateOf(MatterbridgeServer);
34
47
  device.log.info(`Moving level with mode ${request.moveMode} and rate ${request.rate} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
48
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
49
+ await super.move(request);
50
+ return;
51
+ }
35
52
  await device.commandHandler.executeHandler('LevelControl.move', {
36
53
  command: 'move',
37
54
  request,
@@ -46,6 +63,10 @@ export class MatterbridgeLevelControlServer extends LevelControlServer {
46
63
  async moveWithOnOff(request) {
47
64
  const device = this.endpoint.stateOf(MatterbridgeServer);
48
65
  device.log.info(`Moving level with mode ${request.moveMode} and rate ${request.rate} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
66
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
67
+ await super.moveWithOnOff(request);
68
+ return;
69
+ }
49
70
  await device.commandHandler.executeHandler('LevelControl.moveWithOnOff', {
50
71
  command: 'moveWithOnOff',
51
72
  request,
@@ -60,6 +81,10 @@ export class MatterbridgeLevelControlServer extends LevelControlServer {
60
81
  async step(request) {
61
82
  const device = this.endpoint.stateOf(MatterbridgeServer);
62
83
  device.log.info(`Stepping level with mode ${request.stepMode} and size ${request.stepSize} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
84
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
85
+ await super.step(request);
86
+ return;
87
+ }
63
88
  await device.commandHandler.executeHandler('LevelControl.step', {
64
89
  command: 'step',
65
90
  request,
@@ -74,6 +99,10 @@ export class MatterbridgeLevelControlServer extends LevelControlServer {
74
99
  async stepWithOnOff(request) {
75
100
  const device = this.endpoint.stateOf(MatterbridgeServer);
76
101
  device.log.info(`Stepping level with mode ${request.stepMode} and size ${request.stepSize} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
102
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
103
+ await super.stepWithOnOff(request);
104
+ return;
105
+ }
77
106
  await device.commandHandler.executeHandler('LevelControl.stepWithOnOff', {
78
107
  command: 'stepWithOnOff',
79
108
  request,
@@ -88,6 +117,10 @@ export class MatterbridgeLevelControlServer extends LevelControlServer {
88
117
  async stop(request) {
89
118
  const device = this.endpoint.stateOf(MatterbridgeServer);
90
119
  device.log.info(`Stopping level change (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
120
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
121
+ await super.stop(request);
122
+ return;
123
+ }
91
124
  await device.commandHandler.executeHandler('LevelControl.stop', {
92
125
  command: 'stop',
93
126
  request,
@@ -102,6 +135,10 @@ export class MatterbridgeLevelControlServer extends LevelControlServer {
102
135
  async stopWithOnOff(request) {
103
136
  const device = this.endpoint.stateOf(MatterbridgeServer);
104
137
  device.log.info(`Stopping level change (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
138
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
139
+ await super.stopWithOnOff(request);
140
+ return;
141
+ }
105
142
  await device.commandHandler.executeHandler('LevelControl.stopWithOnOff', {
106
143
  command: 'stopWithOnOff',
107
144
  request,
@@ -5,96 +5,108 @@ export class MatterbridgeOnOffServer extends OnOffServer.with(OnOff.Feature.Ligh
5
5
  async on() {
6
6
  const device = this.endpoint.stateOf(MatterbridgeServer);
7
7
  device.log.info(`Switching device on (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
8
- if (!process.env.MATTERBRIDGE_CHIP_TEST) {
9
- await device.commandHandler.executeHandler('OnOff.on', {
10
- command: 'on',
11
- request: {},
12
- cluster: OnOffServer.id,
13
- attributes: this.state,
14
- endpoint: this.endpoint,
15
- context: this.context,
16
- });
8
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
9
+ await super.on();
10
+ return;
17
11
  }
12
+ await device.commandHandler.executeHandler('OnOff.on', {
13
+ command: 'on',
14
+ request: {},
15
+ cluster: OnOffServer.id,
16
+ attributes: this.state,
17
+ endpoint: this.endpoint,
18
+ context: this.context,
19
+ });
18
20
  device.log.debug(`MatterbridgeOnOffServer: on called`);
19
21
  await super.on();
20
22
  }
21
23
  async off() {
22
24
  const device = this.endpoint.stateOf(MatterbridgeServer);
23
25
  device.log.info(`Switching device off (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
24
- if (!process.env.MATTERBRIDGE_CHIP_TEST) {
25
- await device.commandHandler.executeHandler('OnOff.off', {
26
- command: 'off',
27
- request: {},
28
- cluster: OnOffServer.id,
29
- attributes: this.state,
30
- endpoint: this.endpoint,
31
- context: this.context,
32
- });
26
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
27
+ await super.off();
28
+ return;
33
29
  }
30
+ await device.commandHandler.executeHandler('OnOff.off', {
31
+ command: 'off',
32
+ request: {},
33
+ cluster: OnOffServer.id,
34
+ attributes: this.state,
35
+ endpoint: this.endpoint,
36
+ context: this.context,
37
+ });
34
38
  device.log.debug(`MatterbridgeOnOffServer: off called`);
35
39
  await super.off();
36
40
  }
37
41
  async toggle() {
38
42
  const device = this.endpoint.stateOf(MatterbridgeServer);
39
43
  device.log.info(`Toggle device on/off (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
40
- if (!process.env.MATTERBRIDGE_CHIP_TEST) {
41
- await device.commandHandler.executeHandler('OnOff.toggle', {
42
- command: 'toggle',
43
- request: {},
44
- cluster: OnOffServer.id,
45
- attributes: this.state,
46
- endpoint: this.endpoint,
47
- context: this.context,
48
- });
44
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
45
+ await super.toggle();
46
+ return;
49
47
  }
48
+ await device.commandHandler.executeHandler('OnOff.toggle', {
49
+ command: 'toggle',
50
+ request: {},
51
+ cluster: OnOffServer.id,
52
+ attributes: this.state,
53
+ endpoint: this.endpoint,
54
+ context: this.context,
55
+ });
50
56
  device.log.debug(`MatterbridgeOnOffServer: toggle called`);
51
57
  await super.toggle();
52
58
  }
53
59
  async offWithEffect(request) {
54
60
  const device = this.endpoint.stateOf(MatterbridgeServer);
55
61
  device.log.info(`Switching device off with effect ${request.effectIdentifier} and variant ${request.effectVariant} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
56
- if (!process.env.MATTERBRIDGE_CHIP_TEST) {
57
- await device.commandHandler.executeHandler('OnOff.offWithEffect', {
58
- command: 'offWithEffect',
59
- request,
60
- cluster: OnOffServer.id,
61
- attributes: this.state,
62
- endpoint: this.endpoint,
63
- context: this.context,
64
- });
62
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
63
+ await super.offWithEffect(request);
64
+ return;
65
65
  }
66
+ await device.commandHandler.executeHandler('OnOff.offWithEffect', {
67
+ command: 'offWithEffect',
68
+ request,
69
+ cluster: OnOffServer.id,
70
+ attributes: this.state,
71
+ endpoint: this.endpoint,
72
+ context: this.context,
73
+ });
66
74
  device.log.debug(`MatterbridgeOnOffServer: offWithEffect called`);
67
75
  await super.offWithEffect(request);
68
76
  }
69
77
  async onWithRecallGlobalScene() {
70
78
  const device = this.endpoint.stateOf(MatterbridgeServer);
71
79
  device.log.info(`Switching device on with recall global scene (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
72
- if (!process.env.MATTERBRIDGE_CHIP_TEST) {
73
- await device.commandHandler.executeHandler('OnOff.onWithRecallGlobalScene', {
74
- command: 'onWithRecallGlobalScene',
75
- request: {},
76
- cluster: OnOffServer.id,
77
- attributes: this.state,
78
- endpoint: this.endpoint,
79
- context: this.context,
80
- });
80
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
81
+ await super.onWithRecallGlobalScene();
82
+ return;
81
83
  }
84
+ await device.commandHandler.executeHandler('OnOff.onWithRecallGlobalScene', {
85
+ command: 'onWithRecallGlobalScene',
86
+ request: {},
87
+ cluster: OnOffServer.id,
88
+ attributes: this.state,
89
+ endpoint: this.endpoint,
90
+ context: this.context,
91
+ });
82
92
  device.log.debug(`MatterbridgeOnOffServer: onWithRecallGlobalScene called`);
83
93
  await super.onWithRecallGlobalScene();
84
94
  }
85
95
  async onWithTimedOff(request) {
86
96
  const device = this.endpoint.stateOf(MatterbridgeServer);
87
97
  device.log.info(`Switching device on with timed off control ${JSON.stringify(request.onOffControl)}, offWaitTime ${request.offWaitTime} and onTime ${request.onTime} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
88
- if (!process.env.MATTERBRIDGE_CHIP_TEST) {
89
- await device.commandHandler.executeHandler('OnOff.onWithTimedOff', {
90
- command: 'onWithTimedOff',
91
- request,
92
- cluster: OnOffServer.id,
93
- attributes: this.state,
94
- endpoint: this.endpoint,
95
- context: this.context,
96
- });
98
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
99
+ await super.onWithTimedOff(request);
100
+ return;
97
101
  }
102
+ await device.commandHandler.executeHandler('OnOff.onWithTimedOff', {
103
+ command: 'onWithTimedOff',
104
+ request,
105
+ cluster: OnOffServer.id,
106
+ attributes: this.state,
107
+ endpoint: this.endpoint,
108
+ context: this.context,
109
+ });
98
110
  device.log.debug(`MatterbridgeOnOffServer: onWithTimedOff called`);
99
111
  await super.onWithTimedOff(request);
100
112
  }
package/dist/chipTests.js CHANGED
@@ -6,6 +6,7 @@ import { BridgedDeviceBasicInformationServer } from '@matter/node/behaviors/brid
6
6
  import { GeneralDiagnosticsServer } from '@matter/node/behaviors/general-diagnostics';
7
7
  import { Status, StatusResponseError } from '@matter/types';
8
8
  import { BooleanStateConfiguration } from '@matter/types/clusters/boolean-state-configuration';
9
+ import { ClosureControl } from '@matter/types/clusters/closure-control';
9
10
  import { DeviceEnergyManagement } from '@matter/types/clusters/device-energy-management';
10
11
  import { ElectricalEnergyMeasurement } from '@matter/types/clusters/electrical-energy-measurement';
11
12
  import { ElectricalPowerMeasurement } from '@matter/types/clusters/electrical-power-measurement';
@@ -47,6 +48,12 @@ const deviceEnergyManagementForecastTrigger = 0x009800000000000fn;
47
48
  const deviceEnergyManagementForecastClearTrigger = 0x0098000000000010n;
48
49
  const booleanStateConfigurationSensorTrigger = 0x0080000000000000n;
49
50
  const booleanStateConfigurationSensorUntriggerTrigger = 0x0080000000000001n;
51
+ const closureControlErrorTrigger = 0x0104000000000000n;
52
+ const closureControlProtectedTrigger = 0x0104000000000001n;
53
+ const closureControlDisengagedTrigger = 0x0104000000000002n;
54
+ const closureControlSetupRequiredTrigger = 0x0104000000000003n;
55
+ const closureControlClearTrigger = 0x0104000000000004n;
56
+ const closureControlEndpointTriggerMask = 0x0000ffff00000000n;
50
57
  export const chipTestEnableKey = Uint8Array.from({ length: 16 }, (_, index) => index);
51
58
  const smokeCoAlarmChipTestEnableKey = Uint8Array.from([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]);
52
59
  let chipTestMatterbridge;
@@ -198,6 +205,14 @@ async function handleChipTestEventTrigger(eventTrigger) {
198
205
  if (eventTrigger === booleanStateConfigurationSensorTrigger || eventTrigger === booleanStateConfigurationSensorUntriggerTrigger) {
199
206
  return await handleBooleanStateConfigurationTestEventTrigger(eventTrigger);
200
207
  }
208
+ const closureControlTrigger = eventTrigger & ~closureControlEndpointTriggerMask;
209
+ if (closureControlTrigger === closureControlErrorTrigger ||
210
+ closureControlTrigger === closureControlProtectedTrigger ||
211
+ closureControlTrigger === closureControlDisengagedTrigger ||
212
+ closureControlTrigger === closureControlSetupRequiredTrigger ||
213
+ closureControlTrigger === closureControlClearTrigger) {
214
+ return await handleClosureControlTestEventTrigger(closureControlTrigger);
215
+ }
201
216
  if (eventTrigger !== smokeCoAlarmWarningSmokeAlarmTrigger &&
202
217
  eventTrigger !== smokeCoAlarmCriticalSmokeAlarmTrigger &&
203
218
  eventTrigger !== smokeCoAlarmSmokeAlarmClearTrigger &&
@@ -354,6 +369,44 @@ async function applyBooleanStateConfigurationTestEventTrigger(endpoint, eventTri
354
369
  return;
355
370
  }
356
371
  }
372
+ async function handleClosureControlTestEventTrigger(eventTrigger) {
373
+ if (!chipTestMatterbridge || chipTestActiveEndpointId === undefined)
374
+ return false;
375
+ const endpoint = getChipTestEndpoint(chipTestMatterbridge, chipTestActiveEndpointId);
376
+ if (!endpoint || !endpoint.hasClusterServer(ClosureControl.id))
377
+ return false;
378
+ await applyClosureControlTestEventTrigger(endpoint, eventTrigger, chipTestMatterbridge.log);
379
+ return true;
380
+ }
381
+ async function applyClosureControlTestEventTrigger(endpoint, eventTrigger, log) {
382
+ switch (eventTrigger) {
383
+ case closureControlErrorTrigger: {
384
+ const errorState = [ClosureControl.ClosureError.PhysicallyBlocked];
385
+ await endpoint.setCluster(ClosureControl, { mainState: ClosureControl.MainState.Error, currentErrorList: errorState }, log);
386
+ await endpoint.triggerEvent(ClosureControl, 'operationalError', { errorState }, log);
387
+ return;
388
+ }
389
+ case closureControlProtectedTrigger:
390
+ await endpoint.setCluster(ClosureControl, { mainState: ClosureControl.MainState.Protected }, log);
391
+ return;
392
+ case closureControlDisengagedTrigger:
393
+ await endpoint.setCluster(ClosureControl, { mainState: ClosureControl.MainState.Disengaged }, log);
394
+ await endpoint.triggerEvent(ClosureControl, 'engageStateChanged', { engageValue: false }, log);
395
+ return;
396
+ case closureControlSetupRequiredTrigger:
397
+ await endpoint.setCluster(ClosureControl, { mainState: ClosureControl.MainState.SetupRequired }, log);
398
+ return;
399
+ case closureControlClearTrigger: {
400
+ const wasDisengaged = endpoint.getAttribute(ClosureControl.id, 'mainState') === ClosureControl.MainState.Disengaged;
401
+ await endpoint.setCluster(ClosureControl, { mainState: ClosureControl.MainState.Stopped, currentErrorList: [] }, log);
402
+ if (wasDisengaged)
403
+ await endpoint.triggerEvent(ClosureControl, 'engageStateChanged', { engageValue: true }, log);
404
+ return;
405
+ }
406
+ default:
407
+ return;
408
+ }
409
+ }
357
410
  const electricalPowerMeasurementIdleReading = { voltage: 220_000, activeCurrent: 1_000, activePower: 220_000_000, frequency: 50_000 };
358
411
  const electricalEnergyMeasurementImportedEnergyPerTick = 278;
359
412
  const electricalEnergyMeasurementExportedEnergyPerTick = 833;
@@ -1,4 +1,4 @@
1
- import { CommonNumberTag } from '@matter/node';
1
+ import { ClosureTag, ClosureWindowTag, CommonNumberTag } from '@matter/node';
2
2
  import { AirQuality } from '@matter/types/clusters/air-quality';
3
3
  import { FanControl } from '@matter/types/clusters/fan-control';
4
4
  import { PowerSource } from '@matter/types/clusters/power-source';
@@ -32,7 +32,7 @@ export async function createDemoDevices(matterbridge) {
32
32
  description: 'Matterbridge demo devices',
33
33
  author: 'Matterbridge',
34
34
  enabled: false,
35
- private: false,
35
+ private: true,
36
36
  registeredDevices: 0,
37
37
  });
38
38
  const registerDevice = async (device, deviceName, serialNumber) => {
@@ -201,10 +201,51 @@ export async function createDemoDevices(matterbridge) {
201
201
  await registerDevice(ep, 'Door Lock Controller', 'ENTRY-08-02');
202
202
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('WindowCovering'), bridgedNode, powerSource], { id: 'WindowCovering', number: EndpointNumber(8_03) });
203
203
  await registerDevice(ep, 'Window Covering', 'ENTRY-08-03');
204
+ ep = new MatterbridgeEndpoint([getSupportedDeviceType('WindowCovering'), bridgedNode, powerSource], { id: 'WindowCoveringTilt', number: EndpointNumber(8_03_1) });
205
+ ep.createDefaultLiftTiltWindowCoveringClusterServer();
206
+ await registerDevice(ep, 'Window Covering Tilt', 'ENTRY-08-03-1');
204
207
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('WindowCoveringController'), bridgedNode, powerSource], { id: 'WindowCoveringController', number: EndpointNumber(8_04) });
205
208
  await registerDevice(ep, 'Window Covering Controller', 'ENTRY-08-04');
206
- ep = new Closure('Closure', 'ENTRY-08-05', { id: 'Closure', number: EndpointNumber(8_05) });
209
+ ep = new Closure('Closure', 'ENTRY-08-05', { id: 'Closure', number: EndpointNumber(8_05), movementDuration: 2000, calibrationDuration: 2000 });
207
210
  await registerDevice(ep, 'Closure', 'ENTRY-08-05');
211
+ ep = new Closure('Closure Pedestrian', 'ENTRY-08-05-1', {
212
+ id: 'ClosurePedestrian',
213
+ number: EndpointNumber(8_05_1),
214
+ movementDuration: 2000,
215
+ calibrationDuration: 2000,
216
+ pedestrian: true,
217
+ tagList: [getSemtag(ClosureTag.Gate)],
218
+ });
219
+ await registerDevice(ep, 'Closure Pedestrian', 'ENTRY-08-05-1');
220
+ ep = new Closure('Closure Ventilation', 'ENTRY-08-05-2', {
221
+ id: 'ClosureVentilation',
222
+ number: EndpointNumber(8_05_2),
223
+ movementDuration: 2000,
224
+ calibrationDuration: 2000,
225
+ ventilation: true,
226
+ tagList: [getSemtag(ClosureTag.Window), getSemtag(ClosureWindowTag.Facade)],
227
+ });
228
+ await registerDevice(ep, 'Closure Ventilation', 'ENTRY-08-05-2');
229
+ ep = new Closure('Closure Calibrate', 'ENTRY-08-05-3', {
230
+ id: 'ClosureCalibrate',
231
+ number: EndpointNumber(8_05_3),
232
+ movementDuration: 2000,
233
+ calibrationDuration: 2000,
234
+ calibration: true,
235
+ tagList: [getSemtag(ClosureTag.GarageDoor)],
236
+ });
237
+ await registerDevice(ep, 'Closure Calibrate', 'ENTRY-08-05-3');
238
+ ep = new Closure('Closure Complete', 'ENTRY-08-05-4', {
239
+ id: 'ClosureComplete',
240
+ number: EndpointNumber(8_05_4),
241
+ movementDuration: 2000,
242
+ calibrationDuration: 2000,
243
+ ventilation: true,
244
+ pedestrian: true,
245
+ calibration: true,
246
+ tagList: [getSemtag(ClosureTag.Door)],
247
+ });
248
+ await registerDevice(ep, 'Closure Complete', 'ENTRY-08-05-4');
208
249
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('ClosureController'), bridgedNode, powerSource], { id: 'ClosureController', number: EndpointNumber(8_07) });
209
250
  await registerDevice(ep, 'Closure Controller', 'ENTRY-08-07');
210
251
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('Thermostat'), bridgedNode, powerSource], { id: 'Thermostat', number: EndpointNumber(9_01) });
@@ -5,8 +5,8 @@ import { Identify } from '@matter/types/clusters/identify';
5
5
  import type { Semtag } from '@matter/types/globals';
6
6
  import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
7
7
  import { type ClosureDimensionType, type ClosurePanelOptions } from './closurePanel.js';
8
- declare const MatterbridgeClosureControlServer_base: import("@matter/node").ClusterBehavior.Type<typeof ClosureControlServer, import("@matter/types").ClusterType.WithSupportedFeatures<ClosureControl, {
9
- calibration: false;
8
+ declare const MatterbridgeClosureControlServerBase: import("@matter/node").ClusterBehavior.Type<typeof ClosureControlServer, import("@matter/types").ClusterType.WithSupportedFeatures<ClosureControl, {
9
+ calibration: true;
10
10
  instantaneous: false;
11
11
  manuallyOperable: false;
12
12
  motionLatching: true;
@@ -16,9 +16,24 @@ declare const MatterbridgeClosureControlServer_base: import("@matter/node").Clus
16
16
  speed: true;
17
17
  ventilation: false;
18
18
  }>, import("@matter/types").ClusterType.Concrete, new () => {}, "closureControl">;
19
- export declare class MatterbridgeClosureControlServer extends MatterbridgeClosureControlServer_base {
19
+ export declare class MatterbridgeClosureControlServer extends MatterbridgeClosureControlServerBase {
20
+ readonly state: MatterbridgeClosureControlServer.State;
21
+ protected internal: MatterbridgeClosureControlServer.Internal;
20
22
  moveTo: (request: ClosureControl.MoveToRequest) => Promise<void>;
23
+ private completeMoveTo;
21
24
  stop: () => Promise<void>;
25
+ calibrate: () => Promise<void>;
26
+ private completeCalibrate;
27
+ }
28
+ export declare namespace MatterbridgeClosureControlServer {
29
+ class Internal extends MatterbridgeClosureControlServerBase.Internal {
30
+ movementTimer?: NodeJS.Timeout;
31
+ calibrateTimer?: NodeJS.Timeout;
32
+ }
33
+ class State extends MatterbridgeClosureControlServerBase.State {
34
+ movementDuration: number;
35
+ calibrationDuration: number;
36
+ }
22
37
  }
23
38
  export interface ClosureOptions {
24
39
  identifyTime?: number;
@@ -30,6 +45,11 @@ export interface ClosureOptions {
30
45
  overallCurrentState?: ClosureControl.OverallCurrentState;
31
46
  overallTargetState?: ClosureControl.OverallTargetState;
32
47
  latchControlModes?: ClosureControl.LatchControlModes;
48
+ movementDuration?: number;
49
+ calibrationDuration?: number;
50
+ ventilation?: boolean;
51
+ pedestrian?: boolean;
52
+ calibration?: boolean;
33
53
  id?: string;
34
54
  number?: EndpointNumber;
35
55
  tagList?: Semtag[];
@@ -9,7 +9,15 @@ import { closure, closurePanel, powerSource } from '../matterbridgeDeviceTypes.j
9
9
  import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
10
10
  import { getSemtag } from '../matterbridgeEndpointHelpers.js';
11
11
  import { createClosureDimensionClusterServer } from './closurePanel.js';
12
- export class MatterbridgeClosureControlServer extends ClosureControlServer.with(ClosureControl.Feature.Positioning, ClosureControl.Feature.MotionLatching, ClosureControl.Feature.Speed) {
12
+ const targetToCurrentPosition = {
13
+ [ClosureControl.TargetPosition.MoveToFullyClosed]: ClosureControl.CurrentPosition.FullyClosed,
14
+ [ClosureControl.TargetPosition.MoveToFullyOpen]: ClosureControl.CurrentPosition.FullyOpened,
15
+ [ClosureControl.TargetPosition.MoveToPedestrianPosition]: ClosureControl.CurrentPosition.OpenedForPedestrian,
16
+ [ClosureControl.TargetPosition.MoveToVentilationPosition]: ClosureControl.CurrentPosition.OpenedForVentilation,
17
+ [ClosureControl.TargetPosition.MoveToSignaturePosition]: ClosureControl.CurrentPosition.OpenedAtSignature,
18
+ };
19
+ const MatterbridgeClosureControlServerBase = ClosureControlServer.with(ClosureControl.Feature.Positioning, ClosureControl.Feature.MotionLatching, ClosureControl.Feature.Speed, ClosureControl.Feature.Calibration);
20
+ export class MatterbridgeClosureControlServer extends MatterbridgeClosureControlServerBase {
13
21
  moveTo = async (request) => {
14
22
  const device = this.endpoint.stateOf(MatterbridgeServer);
15
23
  device.log.info(`MoveTo (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
@@ -20,6 +28,15 @@ export class MatterbridgeClosureControlServer extends ClosureControlServer.with(
20
28
  attributes: this.state,
21
29
  endpoint: this.endpoint,
22
30
  });
31
+ if (request.position !== undefined && !(request.position in targetToCurrentPosition)) {
32
+ throw new StatusResponse.ConstraintErrorError('ClosureControl.moveTo Position is not a valid TargetPositionEnum value');
33
+ }
34
+ if (request.latch !== undefined && typeof request.latch !== 'boolean') {
35
+ throw new StatusResponse.ConstraintErrorError('ClosureControl.moveTo Latch is not a boolean value');
36
+ }
37
+ if (request.speed !== undefined && !(request.speed in ThreeLevelAuto)) {
38
+ throw new StatusResponse.ConstraintErrorError('ClosureControl.moveTo Speed is not a valid ThreeLevelAutoEnum value');
39
+ }
23
40
  if (request.position === undefined && request.latch === undefined && request.speed === undefined) {
24
41
  throw new StatusResponse.InvalidCommandError('ClosureControl.moveTo requires at least one of position, latch, or speed to be present');
25
42
  }
@@ -47,6 +64,38 @@ export class MatterbridgeClosureControlServer extends ClosureControlServer.with(
47
64
  (nextTarget.latch === undefined || nextTarget.latch === currentState.latch) &&
48
65
  nextTarget.speed === currentState.speed;
49
66
  this.state.mainState = isAtTarget ? ClosureControl.MainState.Stopped : ClosureControl.MainState.Moving;
67
+ clearTimeout(this.internal.movementTimer);
68
+ if (isAtTarget) {
69
+ this.internal.movementTimer = undefined;
70
+ this.state.countdownTime = 0;
71
+ }
72
+ else if (currentState === null || this.state.movementDuration <= 0) {
73
+ this.internal.movementTimer = undefined;
74
+ }
75
+ else {
76
+ this.state.countdownTime = this.state.movementDuration / 1000;
77
+ const previousState = currentState;
78
+ this.internal.movementTimer = setTimeout(() => {
79
+ this.internal.movementTimer = undefined;
80
+ void this.completeMoveTo(nextTarget, previousState);
81
+ }, this.state.movementDuration);
82
+ }
83
+ };
84
+ completeMoveTo = async (targetState, previousState) => {
85
+ const closure = this.endpoint;
86
+ let position = previousState.position;
87
+ if (targetState.position !== undefined && targetState.position !== null) {
88
+ const mappedPosition = targetToCurrentPosition[targetState.position];
89
+ if (mappedPosition !== undefined)
90
+ position = mappedPosition;
91
+ }
92
+ const latch = targetState.latch ?? previousState.latch;
93
+ const secureState = latch === true;
94
+ await closure.setState({ position, latch, speed: targetState.speed, secureState }, targetState, ClosureControl.MainState.Stopped, 0);
95
+ if (secureState !== previousState.secureState) {
96
+ await closure.triggerSecureStateChanged(secureState);
97
+ }
98
+ await closure.triggerMovementCompleted();
50
99
  };
51
100
  stop = async () => {
52
101
  const device = this.endpoint.stateOf(MatterbridgeServer);
@@ -58,11 +107,62 @@ export class MatterbridgeClosureControlServer extends ClosureControlServer.with(
58
107
  attributes: this.state,
59
108
  endpoint: this.endpoint,
60
109
  });
110
+ clearTimeout(this.internal.movementTimer);
111
+ this.internal.movementTimer = undefined;
112
+ clearTimeout(this.internal.calibrateTimer);
113
+ this.internal.calibrateTimer = undefined;
61
114
  if ([ClosureControl.MainState.Moving, ClosureControl.MainState.WaitingForMotion, ClosureControl.MainState.Calibrating].includes(this.state.mainState)) {
62
115
  this.state.mainState = ClosureControl.MainState.Stopped;
116
+ this.state.countdownTime = 0;
63
117
  }
64
118
  };
119
+ calibrate = async () => {
120
+ const device = this.endpoint.stateOf(MatterbridgeServer);
121
+ device.log.info(`Calibrate (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
122
+ await device.commandHandler.executeHandler('ClosureControl.calibrate', {
123
+ command: 'calibrate',
124
+ request: {},
125
+ cluster: ClosureControlServer.id,
126
+ attributes: this.state,
127
+ endpoint: this.endpoint,
128
+ });
129
+ if (this.state.mainState === ClosureControl.MainState.Calibrating) {
130
+ return;
131
+ }
132
+ if (![ClosureControl.MainState.Stopped, ClosureControl.MainState.SetupRequired].includes(this.state.mainState)) {
133
+ throw new StatusResponse.InvalidInStateError('ClosureControl.calibrate is only allowed while Stopped or SetupRequired');
134
+ }
135
+ this.state.mainState = ClosureControl.MainState.Calibrating;
136
+ clearTimeout(this.internal.calibrateTimer);
137
+ if (this.state.calibrationDuration <= 0) {
138
+ this.internal.calibrateTimer = undefined;
139
+ }
140
+ else {
141
+ this.state.countdownTime = this.state.calibrationDuration / 1000;
142
+ this.internal.calibrateTimer = setTimeout(() => {
143
+ this.internal.calibrateTimer = undefined;
144
+ void this.completeCalibrate();
145
+ }, this.state.calibrationDuration);
146
+ }
147
+ };
148
+ completeCalibrate = async () => {
149
+ const closure = this.endpoint;
150
+ await closure.setAttribute(ClosureControl, 'countdownTime', 0);
151
+ await closure.setAttribute(ClosureControl, 'mainState', ClosureControl.MainState.Stopped);
152
+ };
65
153
  }
154
+ (function (MatterbridgeClosureControlServer) {
155
+ class Internal extends MatterbridgeClosureControlServerBase.Internal {
156
+ movementTimer;
157
+ calibrateTimer;
158
+ }
159
+ MatterbridgeClosureControlServer.Internal = Internal;
160
+ class State extends MatterbridgeClosureControlServerBase.State {
161
+ movementDuration = 0;
162
+ calibrationDuration = 0;
163
+ }
164
+ MatterbridgeClosureControlServer.State = State;
165
+ })(MatterbridgeClosureControlServer || (MatterbridgeClosureControlServer = {}));
66
166
  export class Closure extends MatterbridgeEndpoint {
67
167
  constructor(name, serial, options = {}) {
68
168
  const { identifyTime = 0, identifyType = Identify.IdentifyType.None, powerSourceType = 'Wired', countdownTime = 0, mainState = ClosureControl.MainState.Stopped, currentErrorList = [], overallCurrentState = {
@@ -74,7 +174,7 @@ export class Closure extends MatterbridgeEndpoint {
74
174
  position: ClosureControl.TargetPosition.MoveToFullyClosed,
75
175
  latch: true,
76
176
  speed: ThreeLevelAuto.Auto,
77
- }, latchControlModes = { remoteLatching: true, remoteUnlatching: true }, id, number, tagList = [getSemtag(ClosureTag.Covering)], } = options;
177
+ }, latchControlModes = { remoteLatching: true, remoteUnlatching: true }, movementDuration = 0, calibrationDuration = 0, ventilation = false, pedestrian = false, calibration = false, id, number, tagList = [getSemtag(ClosureTag.Covering)], } = options;
78
178
  super(powerSourceType === 'None' ? [closure] : [closure, powerSource], {
79
179
  id: id ?? `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}`,
80
180
  number,
@@ -98,14 +198,19 @@ export class Closure extends MatterbridgeEndpoint {
98
198
  case 'None':
99
199
  break;
100
200
  }
101
- this.behaviors.require(MatterbridgeClosureControlServer, {
201
+ const closureControlOptions = {
102
202
  countdownTime,
103
203
  mainState,
104
204
  currentErrorList,
105
205
  overallCurrentState,
106
206
  overallTargetState,
107
207
  latchControlModes,
108
- });
208
+ movementDuration,
209
+ calibrationDuration,
210
+ };
211
+ this.behaviors.require(calibration || ventilation || pedestrian
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);
109
214
  }
110
215
  getMainState() {
111
216
  return this.getAttribute(ClosureControlServer, 'mainState');
package/dist/frontend.js CHANGED
@@ -17,6 +17,7 @@ import { SoilMeasurement } from '@matter/types/clusters/soil-measurement';
17
17
  import { ValveConfigurationAndControl } from '@matter/types/clusters/valve-configuration-and-control';
18
18
  import { CommissioningOptions } from '@matter/types/commissioning';
19
19
  import { FabricIndex } from '@matter/types/datatype';
20
+ import { ThreeLevelAuto } from '@matter/types/globals';
20
21
  import { BroadcastServer } from '@matterbridge/thread/server';
21
22
  import { MATTER_LOGGER_FILE, MATTER_STORAGE_DIR, MATTERBRIDGE_DIAGNOSTIC_FILE, MATTERBRIDGE_HISTORY_FILE, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg, } from '@matterbridge/types';
22
23
  import { isBun } from '@matterbridge/utils/bun';
@@ -1126,11 +1127,12 @@ export class Frontend extends EventEmitter {
1126
1127
  if (clusterName === 'doorLock' && attributeName === 'lockState')
1127
1128
  attributes += `State: ${attributeValue === 1 ? 'Locked' : 'Not locked'} `;
1128
1129
  if (clusterName === 'closureControl' && attributeName === 'overallCurrentState' && attributeValue === null)
1129
- attributes += `Position: unknown Latch: unknown SecureState: unknown `;
1130
+ attributes += `Position: unknown Latch: unknown Speed: unknown SecureState: unknown `;
1130
1131
  if (clusterName === 'closureControl' && attributeName === 'overallCurrentState' && isValidObject(attributeValue)) {
1131
1132
  const overallCurrentState = attributeValue;
1132
1133
  attributes += `Position: ${getEnumDescription(ClosureControl.CurrentPosition, overallCurrentState.position, { fallback: 'unknown' })} `;
1133
1134
  attributes += `Latch: ${overallCurrentState.latch ?? 'unknown'} `;
1135
+ attributes += `Speed: ${getEnumDescription(ThreeLevelAuto, overallCurrentState.speed, { fallback: 'unknown' })} `;
1134
1136
  attributes += `SecureState: ${overallCurrentState.secureState ?? 'unknown'} `;
1135
1137
  }
1136
1138
  if (clusterName === 'thermostat' && attributeName === 'localTemperature' && isValidNumber(attributeValue))
@@ -376,6 +376,13 @@ export type CommandHandlerDataMap = {
376
376
  attributes: ClusterAttributeValues<(typeof ClosureControl)['attributes']>;
377
377
  endpoint: MatterbridgeEndpoint;
378
378
  };
379
+ 'ClosureControl.calibrate': {
380
+ command: 'calibrate';
381
+ request: {};
382
+ cluster: 'closureControl';
383
+ attributes: ClusterAttributeValues<(typeof ClosureControl)['attributes']>;
384
+ endpoint: MatterbridgeEndpoint;
385
+ };
379
386
  'setTarget': CommandHandlerData<'ClosureDimension.setTarget'>;
380
387
  'ClosureDimension.setTarget': {
381
388
  command: 'setTarget';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/core",
3
- "version": "3.10.6",
3
+ "version": "3.10.7-dev-20260823-f911eb8",
4
4
  "description": "Matterbridge core library",
5
5
  "author": "https://github.com/Luligu",
6
6
  "homepage": "https://matterbridge.io/",
@@ -132,10 +132,10 @@
132
132
  },
133
133
  "dependencies": {
134
134
  "@matter/main": "0.17.9",
135
- "@matterbridge/dgram": "3.10.6",
136
- "@matterbridge/thread": "3.10.6",
137
- "@matterbridge/types": "3.10.6",
138
- "@matterbridge/utils": "3.10.6",
135
+ "@matterbridge/dgram": "3.10.7-dev-20260823-f911eb8",
136
+ "@matterbridge/thread": "3.10.7-dev-20260823-f911eb8",
137
+ "@matterbridge/types": "3.10.7-dev-20260823-f911eb8",
138
+ "@matterbridge/utils": "3.10.7-dev-20260823-f911eb8",
139
139
  "escape-html": "1.0.3",
140
140
  "express": "5.2.1",
141
141
  "express-rate-limit": "8.6.2",