@matterbridge/core 3.10.8-dev-20260831-e73fc65 → 3.10.8-dev-20260901-c4963a1

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.
@@ -122,11 +122,10 @@ export class MatterbridgeDoorLockServer extends DoorLockServer.with(DoorLock.Fea
122
122
  context: this.context,
123
123
  });
124
124
  device.log.debug(`MatterbridgeDoorLockServer: setCredential called for userIndex ${request.userIndex} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
125
- const auth = this.auth;
126
- if (auth.isDuplicateCredential(request.credential.credentialType, request.credentialData, request.credential.credentialIndex)) {
125
+ if (this.auth.isDuplicateCredential(request.credential.credentialType, request.credentialData, request.credential.credentialIndex)) {
127
126
  throw new DoorLock.DuplicateError(`MatterbridgeDoorLockServer: credential data duplicates another credential of the same type (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
128
127
  }
129
- if (request.operationType === DoorLock.DataOperationType.Add && auth.findCredential(request.credential.credentialType, request.credential.credentialIndex)) {
128
+ if (request.operationType === DoorLock.DataOperationType.Add && this.auth.findCredential(request.credential.credentialType, request.credential.credentialIndex)) {
130
129
  throw new DoorLock.OccupiedError(`MatterbridgeDoorLockServer: add operation targets an occupied credential index (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
131
130
  }
132
131
  return await super.setCredential(request);
@@ -1,6 +1,6 @@
1
1
  import { Bytes } from '@matter/general';
2
2
  import { ThermostatServer } from '@matter/node/behaviors/thermostat';
3
- import { StatusResponse } from '@matter/types';
3
+ import { Status, StatusResponseError } from '@matter/types';
4
4
  import { Thermostat } from '@matter/types/clusters/thermostat';
5
5
  import { MatterbridgeServer } from './matterbridgeServer.js';
6
6
  export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermostat.Feature.Cooling, Thermostat.Feature.Heating, Thermostat.Feature.AutoMode, Thermostat.Feature.Presets, Thermostat.Feature.MatterScheduleConfiguration, Thermostat.Feature.ThermostatSuggestions) {
@@ -30,18 +30,18 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
30
30
  return;
31
31
  const device = this.endpoint.stateOf(MatterbridgeServer);
32
32
  if (remainingSuggestions.length !== currentSuggestions.length) {
33
- device.log.info(`Removing ${currentSuggestions.length - remainingSuggestions.length} thermostat suggestion(s) referencing removed preset(s) (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
33
+ device.log.info(`MatterbridgeThermostatServer: removing ${currentSuggestions.length - remainingSuggestions.length} thermostat suggestion(s) referencing removed preset(s) (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
34
34
  this.state.thermostatSuggestions = remainingSuggestions;
35
35
  }
36
36
  if (clearCurrentSuggestion) {
37
- device.log.info(`Clearing current thermostat suggestion referencing a removed preset (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
37
+ device.log.info(`MatterbridgeThermostatServer: clearing current thermostat suggestion referencing a removed preset (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
38
38
  this.state.currentThermostatSuggestion = null;
39
39
  this.state.thermostatSuggestionNotFollowingReason = null;
40
40
  }
41
41
  }
42
42
  async setpointRaiseLower(request) {
43
43
  const device = this.endpoint.stateOf(MatterbridgeServer);
44
- device.log.info(`Setting setpoint by ${request.amount} in mode ${request.mode} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
44
+ device.log.info(`MatterbridgeThermostatServer: setting setpoint by ${request.amount} in mode ${request.mode} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
45
45
  await device.commandHandler.executeHandler('Thermostat.setpointRaiseLower', {
46
46
  command: 'setpointRaiseLower',
47
47
  request,
@@ -51,13 +51,13 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
51
51
  context: this.context,
52
52
  });
53
53
  const lookupSetpointAdjustMode = ['Heat', 'Cool', 'Both'];
54
- device.log.debug(`MatterbridgeThermostatServer: setpointRaiseLower called with mode: ${lookupSetpointAdjustMode[request.mode]} amount: ${request.amount / 10}`);
54
+ device.log.debug(`MatterbridgeThermostatServer: setpointRaiseLower called with mode: ${lookupSetpointAdjustMode[request.mode]} amount: ${request.amount / 10} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
55
55
  await super.setpointRaiseLower(request);
56
56
  }
57
57
  async setActivePresetRequest(request) {
58
58
  const device = this.endpoint.stateOf(MatterbridgeServer);
59
59
  const presetHandle = request.presetHandle ? `0x${Buffer.from(request.presetHandle).toString('hex')}` : 'null';
60
- device.log.info(`Setting preset to ${presetHandle} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
60
+ device.log.info(`MatterbridgeThermostatServer: setting preset to ${presetHandle} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
61
61
  await device.commandHandler.executeHandler('Thermostat.setActivePresetRequest', {
62
62
  command: 'setActivePresetRequest',
63
63
  request,
@@ -66,15 +66,15 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
66
66
  endpoint: this.endpoint,
67
67
  context: this.context,
68
68
  });
69
- device.log.debug(`MatterbridgeThermostatServer: setActivePresetRequest called with presetHandle: ${presetHandle}`);
69
+ device.log.debug(`MatterbridgeThermostatServer: setActivePresetRequest called with presetHandle: ${presetHandle} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
70
70
  await super.setActivePresetRequest(request);
71
71
  const activePresetHandle = this.state.activePresetHandle ? `0x${Buffer.from(this.state.activePresetHandle).toString('hex')}` : 'null';
72
- device.log.debug(`MatterbridgeThermostatServer: setActivePresetRequest completed with activePresetHandle: ${activePresetHandle} occupiedHeatingSetpoint: ${this.state.occupiedHeatingSetpoint} occupiedCoolingSetpoint: ${this.state.occupiedCoolingSetpoint}`);
72
+ device.log.debug(`MatterbridgeThermostatServer: setActivePresetRequest completed with activePresetHandle: ${activePresetHandle} occupiedHeatingSetpoint: ${this.state.occupiedHeatingSetpoint} occupiedCoolingSetpoint: ${this.state.occupiedCoolingSetpoint} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
73
73
  }
74
74
  async setActiveScheduleRequest(request) {
75
75
  const device = this.endpoint.stateOf(MatterbridgeServer);
76
76
  const scheduleHandle = `0x${Buffer.from(request.scheduleHandle).toString('hex')}`;
77
- device.log.info(`Setting schedule to ${scheduleHandle} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
77
+ device.log.info(`MatterbridgeThermostatServer: setting schedule to ${scheduleHandle} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
78
78
  await device.commandHandler.executeHandler('Thermostat.setActiveScheduleRequest', {
79
79
  command: 'setActiveScheduleRequest',
80
80
  request,
@@ -85,10 +85,10 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
85
85
  });
86
86
  const schedule = this.state.schedules.find((s) => s.scheduleHandle !== null && Bytes.areEqual(s.scheduleHandle, request.scheduleHandle));
87
87
  if (schedule === undefined) {
88
- throw new StatusResponse.InvalidCommandError('Requested ScheduleHandle not found');
88
+ throw new StatusResponseError(`MatterbridgeThermostatServer: requested ScheduleHandle not found (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`, Status.InvalidCommand);
89
89
  }
90
90
  this.state.activeScheduleHandle = Uint8Array.from(request.scheduleHandle);
91
- device.log.debug(`MatterbridgeThermostatServer: setActiveScheduleRequest completed with activeScheduleHandle: ${scheduleHandle}`);
91
+ device.log.debug(`MatterbridgeThermostatServer: setActiveScheduleRequest completed with activeScheduleHandle: ${scheduleHandle} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
92
92
  }
93
93
  removeExpiredThermostatSuggestions() {
94
94
  const now = Math.floor(Date.now() / 1000);
@@ -112,7 +112,7 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
112
112
  async addThermostatSuggestion(request) {
113
113
  const device = this.endpoint.stateOf(MatterbridgeServer);
114
114
  const presetHandle = `0x${Buffer.from(request.presetHandle).toString('hex')}`;
115
- device.log.info(`Adding thermostat suggestion for preset ${presetHandle} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
115
+ device.log.info(`MatterbridgeThermostatServer: adding thermostat suggestion for preset ${presetHandle} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
116
116
  await device.commandHandler.executeHandler('Thermostat.addThermostatSuggestion', {
117
117
  command: 'addThermostatSuggestion',
118
118
  request,
@@ -122,17 +122,17 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
122
122
  context: this.context,
123
123
  });
124
124
  if (this.state.presets.find((p) => p.presetHandle !== null && Bytes.areEqual(p.presetHandle, request.presetHandle)) === undefined) {
125
- throw new StatusResponse.NotFoundError('Requested PresetHandle not found');
125
+ throw new StatusResponseError(`MatterbridgeThermostatServer: requested PresetHandle not found (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`, Status.NotFound);
126
126
  }
127
127
  this.removeExpiredThermostatSuggestions();
128
128
  this.reEvaluateCurrentThermostatSuggestion();
129
129
  if (this.state.thermostatSuggestions.length >= this.state.maxThermostatSuggestions) {
130
- throw new StatusResponse.ResourceExhaustedError('Maximum number of thermostat suggestions reached');
130
+ throw new StatusResponseError(`MatterbridgeThermostatServer: maximum number of thermostat suggestions reached (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`, Status.ResourceExhausted);
131
131
  }
132
132
  const currentTime = Math.floor(Date.now() / 1000);
133
133
  const effectiveTime = request.effectiveTime ?? currentTime;
134
134
  if (effectiveTime > currentTime + 24 * 60 * 60) {
135
- throw new StatusResponse.InvalidCommandError('EffectiveTime cannot be more than 24 hours in the future');
135
+ throw new StatusResponseError(`MatterbridgeThermostatServer: requested EffectiveTime is more than 24 hours in the future (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`, Status.InvalidCommand);
136
136
  }
137
137
  const usedUniqueIds = new Set(this.state.thermostatSuggestions.map((s) => s.uniqueId));
138
138
  let uniqueId = 0;
@@ -146,12 +146,12 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
146
146
  };
147
147
  this.state.thermostatSuggestions = [...this.state.thermostatSuggestions, suggestion];
148
148
  this.reEvaluateCurrentThermostatSuggestion();
149
- device.log.debug(`MatterbridgeThermostatServer: addThermostatSuggestion completed with uniqueId: ${uniqueId}`);
149
+ device.log.debug(`MatterbridgeThermostatServer: addThermostatSuggestion completed with uniqueId: ${uniqueId} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
150
150
  return { uniqueId };
151
151
  }
152
152
  async removeThermostatSuggestion(request) {
153
153
  const device = this.endpoint.stateOf(MatterbridgeServer);
154
- device.log.info(`Removing thermostat suggestion ${request.uniqueId} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
154
+ device.log.info(`MatterbridgeThermostatServer: removing thermostat suggestion ${request.uniqueId} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
155
155
  await device.commandHandler.executeHandler('Thermostat.removeThermostatSuggestion', {
156
156
  command: 'removeThermostatSuggestion',
157
157
  request,
@@ -162,11 +162,11 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
162
162
  });
163
163
  const suggestion = this.state.thermostatSuggestions.find((s) => s.uniqueId === request.uniqueId);
164
164
  if (suggestion === undefined) {
165
- throw new StatusResponse.NotFoundError('Requested UniqueID not found');
165
+ throw new StatusResponseError(`MatterbridgeThermostatServer: requested UniqueID not found (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`, Status.NotFound);
166
166
  }
167
167
  this.state.thermostatSuggestions = this.state.thermostatSuggestions.filter((s) => s.uniqueId !== request.uniqueId);
168
168
  this.removeExpiredThermostatSuggestions();
169
169
  this.reEvaluateCurrentThermostatSuggestion();
170
- device.log.debug(`MatterbridgeThermostatServer: removeThermostatSuggestion completed for uniqueId: ${request.uniqueId}`);
170
+ device.log.debug(`MatterbridgeThermostatServer: removeThermostatSuggestion completed for uniqueId: ${request.uniqueId} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
171
171
  }
172
172
  }
@@ -1,4 +1,4 @@
1
- import { type WindowCoveringBaseServer, WindowCoveringServer } from '@matter/node/behaviors/window-covering';
1
+ import { WindowCoveringBaseServer, WindowCoveringServer } from '@matter/node/behaviors/window-covering';
2
2
  import { WindowCovering } from '@matter/types/clusters/window-covering';
3
3
  declare const MatterbridgeWindowCoveringServer_base: import("@matter/node").ClusterBehavior.Type<typeof WindowCoveringServer, import("@matter/types").ClusterType.WithSupportedFeatures<WindowCovering, {
4
4
  lift: true;
@@ -7,7 +7,9 @@ declare const MatterbridgeWindowCoveringServer_base: import("@matter/node").Clus
7
7
  tilt: true;
8
8
  }>, import("@matter/types").ClusterType.Concrete, typeof WindowCoveringBaseServer.Internal, "windowCovering">;
9
9
  export declare class MatterbridgeWindowCoveringServer extends MatterbridgeWindowCoveringServer_base {
10
- protected internal: WindowCoveringBaseServer.Internal;
10
+ #private;
11
+ readonly state: MatterbridgeWindowCoveringServer.State;
12
+ protected internal: MatterbridgeWindowCoveringServer.Internal;
11
13
  lookupMovementStatus: string[];
12
14
  private getMovementStatusLabel;
13
15
  initialize(): void;
@@ -16,6 +18,14 @@ export declare class MatterbridgeWindowCoveringServer extends MatterbridgeWindow
16
18
  stopMotion(): Promise<void>;
17
19
  goToLiftPercentage(request: WindowCovering.GoToLiftPercentageRequest): Promise<void>;
18
20
  goToTiltPercentage(request: WindowCovering.GoToTiltPercentageRequest): Promise<void>;
19
- handleMovement(): void;
21
+ }
22
+ export declare namespace MatterbridgeWindowCoveringServer {
23
+ class Internal extends WindowCoveringBaseServer.Internal {
24
+ liftMovementTimer?: NodeJS.Timeout;
25
+ tiltMovementTimer?: NodeJS.Timeout;
26
+ }
27
+ class State extends WindowCoveringBaseServer.State {
28
+ movementDuration: number;
29
+ }
20
30
  }
21
31
  export {};
@@ -1,4 +1,4 @@
1
- import { WindowCoveringServer } from '@matter/node/behaviors/window-covering';
1
+ import { WindowCoveringBaseServer, WindowCoveringServer } from '@matter/node/behaviors/window-covering';
2
2
  import { WindowCovering } from '@matter/types/clusters/window-covering';
3
3
  import { MatterbridgeServer } from './matterbridgeServer.js';
4
4
  export class MatterbridgeWindowCoveringServer extends WindowCoveringServer.with(WindowCovering.Feature.Lift, WindowCovering.Feature.PositionAwareLift, WindowCovering.Feature.Tilt, WindowCovering.Feature.PositionAwareTilt) {
@@ -8,13 +8,77 @@ export class MatterbridgeWindowCoveringServer extends WindowCoveringServer.with(
8
8
  }
9
9
  initialize() {
10
10
  const device = this.endpoint.stateOf(MatterbridgeServer);
11
- device.log.info(`Initializing MatterbridgeWindowCoveringServer (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
11
+ device.log.info(`MatterbridgeWindowCoveringServer: initializing (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
12
12
  this.internal.disableOperationalModeHandling = true;
13
+ if (process.env.MATTERBRIDGE_CHIP_TEST) {
14
+ this.state.movementDuration = 3000;
15
+ }
13
16
  super.initialize();
17
+ if (this.features.positionAwareLift)
18
+ this.reactTo(this.events.currentPositionLiftPercent100ths$Changing, this.#syncLiftCurrentPositionPercentage);
19
+ if (this.features.positionAwareTilt)
20
+ this.reactTo(this.events.currentPositionTiltPercent100ths$Changing, this.#syncTiltCurrentPositionPercentage);
14
21
  }
22
+ #syncLiftCurrentPositionPercentage(percent100ths) {
23
+ this.state.currentPositionLiftPercentage = percent100ths === null ? percent100ths : Math.floor(percent100ths / 100);
24
+ }
25
+ #syncTiltCurrentPositionPercentage(percent100ths) {
26
+ this.state.currentPositionTiltPercentage = percent100ths === null ? percent100ths : Math.floor(percent100ths / 100);
27
+ }
28
+ #computeMovementStatus(target, current) {
29
+ if (current === null || target === null || current === target)
30
+ return WindowCovering.MovementStatus.Stopped;
31
+ return current < target ? WindowCovering.MovementStatus.Closing : WindowCovering.MovementStatus.Opening;
32
+ }
33
+ #updateGlobalOperationalStatus() {
34
+ const { lift, tilt } = this.state.operationalStatus;
35
+ this.state.operationalStatus.global = lift === WindowCovering.MovementStatus.Stopped ? tilt : lift;
36
+ }
37
+ #startLiftMovement(targetPercent100ths) {
38
+ clearTimeout(this.internal.liftMovementTimer);
39
+ this.internal.liftMovementTimer = undefined;
40
+ if (this.state.movementDuration <= 0)
41
+ return;
42
+ const status = this.#computeMovementStatus(targetPercent100ths, this.state.currentPositionLiftPercent100ths);
43
+ this.state.operationalStatus.lift = status;
44
+ this.#updateGlobalOperationalStatus();
45
+ if (status === WindowCovering.MovementStatus.Stopped)
46
+ return;
47
+ this.internal.liftMovementTimer = setTimeout(() => {
48
+ this.internal.liftMovementTimer = undefined;
49
+ void this.#completeLiftMovement(targetPercent100ths);
50
+ }, this.state.movementDuration);
51
+ }
52
+ #completeLiftMovement = async (targetPercent100ths) => {
53
+ const windowCovering = this.endpoint;
54
+ await windowCovering.setAttribute(WindowCovering, 'currentPositionLiftPercent100ths', targetPercent100ths);
55
+ const tilt = windowCovering.getAttribute(WindowCovering, 'operationalStatus')?.tilt ?? WindowCovering.MovementStatus.Stopped;
56
+ await windowCovering.setAttribute(WindowCovering, 'operationalStatus', { global: tilt, lift: WindowCovering.MovementStatus.Stopped, tilt });
57
+ };
58
+ #startTiltMovement(targetPercent100ths) {
59
+ clearTimeout(this.internal.tiltMovementTimer);
60
+ this.internal.tiltMovementTimer = undefined;
61
+ if (this.state.movementDuration <= 0)
62
+ return;
63
+ const status = this.#computeMovementStatus(targetPercent100ths, this.state.currentPositionTiltPercent100ths);
64
+ this.state.operationalStatus.tilt = status;
65
+ this.#updateGlobalOperationalStatus();
66
+ if (status === WindowCovering.MovementStatus.Stopped)
67
+ return;
68
+ this.internal.tiltMovementTimer = setTimeout(() => {
69
+ this.internal.tiltMovementTimer = undefined;
70
+ void this.#completeTiltMovement(targetPercent100ths);
71
+ }, this.state.movementDuration);
72
+ }
73
+ #completeTiltMovement = async (targetPercent100ths) => {
74
+ const windowCovering = this.endpoint;
75
+ await windowCovering.setAttribute(WindowCovering, 'currentPositionTiltPercent100ths', targetPercent100ths);
76
+ const lift = windowCovering.getAttribute(WindowCovering, 'operationalStatus')?.lift ?? WindowCovering.MovementStatus.Stopped;
77
+ await windowCovering.setAttribute(WindowCovering, 'operationalStatus', { global: lift, lift, tilt: WindowCovering.MovementStatus.Stopped });
78
+ };
15
79
  async upOrOpen() {
16
80
  const device = this.endpoint.stateOf(MatterbridgeServer);
17
- device.log.info(`Opening cover (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
81
+ device.log.info(`MatterbridgeWindowCoveringServer: opening cover (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
18
82
  await device.commandHandler.executeHandler('WindowCovering.upOrOpen', {
19
83
  command: 'upOrOpen',
20
84
  request: {},
@@ -23,13 +87,17 @@ export class MatterbridgeWindowCoveringServer extends WindowCoveringServer.with(
23
87
  endpoint: this.endpoint,
24
88
  context: this.context,
25
89
  });
26
- device.log.debug(`MatterbridgeWindowCoveringServer: upOrOpen called`);
90
+ device.log.debug(`MatterbridgeWindowCoveringServer: upOrOpen called (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
27
91
  await super.upOrOpen();
28
- device.log.debug(`MatterbridgeWindowCoveringServer: upOrOpen result target ${this.state.targetPositionLiftPercent100ths} current ${this.state.currentPositionLiftPercent100ths} status global ${this.getMovementStatusLabel(this.state.operationalStatus.global)} lift ${this.getMovementStatusLabel(this.state.operationalStatus.lift)} tilt ${this.getMovementStatusLabel(this.state.operationalStatus.tilt)}`);
92
+ if (this.features.positionAwareLift && this.state.targetPositionLiftPercent100ths !== null)
93
+ this.#startLiftMovement(this.state.targetPositionLiftPercent100ths);
94
+ if (this.features.positionAwareTilt && this.state.targetPositionTiltPercent100ths !== null)
95
+ this.#startTiltMovement(this.state.targetPositionTiltPercent100ths);
96
+ device.log.debug(`MatterbridgeWindowCoveringServer: upOrOpen result target ${this.state.targetPositionLiftPercent100ths} current ${this.state.currentPositionLiftPercent100ths} status global ${this.getMovementStatusLabel(this.state.operationalStatus.global)} lift ${this.getMovementStatusLabel(this.state.operationalStatus.lift)} tilt ${this.getMovementStatusLabel(this.state.operationalStatus.tilt)} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
29
97
  }
30
98
  async downOrClose() {
31
99
  const device = this.endpoint.stateOf(MatterbridgeServer);
32
- device.log.info(`Closing cover (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
100
+ device.log.info(`MatterbridgeWindowCoveringServer: closing cover (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
33
101
  await device.commandHandler.executeHandler('WindowCovering.downOrClose', {
34
102
  command: 'downOrClose',
35
103
  request: {},
@@ -38,13 +106,17 @@ export class MatterbridgeWindowCoveringServer extends WindowCoveringServer.with(
38
106
  endpoint: this.endpoint,
39
107
  context: this.context,
40
108
  });
41
- device.log.debug(`MatterbridgeWindowCoveringServer: downOrClose called`);
109
+ device.log.debug(`MatterbridgeWindowCoveringServer: downOrClose called (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
42
110
  await super.downOrClose();
43
- device.log.debug(`MatterbridgeWindowCoveringServer: downOrClose result target ${this.state.targetPositionLiftPercent100ths} current ${this.state.currentPositionLiftPercent100ths} status global ${this.getMovementStatusLabel(this.state.operationalStatus.global)} lift ${this.getMovementStatusLabel(this.state.operationalStatus.lift)} tilt ${this.getMovementStatusLabel(this.state.operationalStatus.tilt)}`);
111
+ if (this.features.positionAwareLift && this.state.targetPositionLiftPercent100ths !== null)
112
+ this.#startLiftMovement(this.state.targetPositionLiftPercent100ths);
113
+ if (this.features.positionAwareTilt && this.state.targetPositionTiltPercent100ths !== null)
114
+ this.#startTiltMovement(this.state.targetPositionTiltPercent100ths);
115
+ device.log.debug(`MatterbridgeWindowCoveringServer: downOrClose result target ${this.state.targetPositionLiftPercent100ths} current ${this.state.currentPositionLiftPercent100ths} status global ${this.getMovementStatusLabel(this.state.operationalStatus.global)} lift ${this.getMovementStatusLabel(this.state.operationalStatus.lift)} tilt ${this.getMovementStatusLabel(this.state.operationalStatus.tilt)} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
44
116
  }
45
117
  async stopMotion() {
46
118
  const device = this.endpoint.stateOf(MatterbridgeServer);
47
- device.log.info(`Stopping cover (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
119
+ device.log.info(`MatterbridgeWindowCoveringServer: stopping cover (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
48
120
  await device.commandHandler.executeHandler('WindowCovering.stopMotion', {
49
121
  command: 'stopMotion',
50
122
  request: {},
@@ -53,13 +125,24 @@ export class MatterbridgeWindowCoveringServer extends WindowCoveringServer.with(
53
125
  endpoint: this.endpoint,
54
126
  context: this.context,
55
127
  });
56
- device.log.debug(`MatterbridgeWindowCoveringServer: stopMotion called`);
128
+ device.log.debug(`MatterbridgeWindowCoveringServer: stopMotion called (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
57
129
  await super.stopMotion();
58
- device.log.debug(`MatterbridgeWindowCoveringServer: stopMotion result target ${this.state.targetPositionLiftPercent100ths} current ${this.state.currentPositionLiftPercent100ths} status global ${this.getMovementStatusLabel(this.state.operationalStatus.global)} lift ${this.getMovementStatusLabel(this.state.operationalStatus.lift)} tilt ${this.getMovementStatusLabel(this.state.operationalStatus.tilt)}`);
130
+ clearTimeout(this.internal.liftMovementTimer);
131
+ this.internal.liftMovementTimer = undefined;
132
+ clearTimeout(this.internal.tiltMovementTimer);
133
+ this.internal.tiltMovementTimer = undefined;
134
+ if (this.state.movementDuration > 0) {
135
+ if (this.features.positionAwareLift)
136
+ this.state.targetPositionLiftPercent100ths = this.state.currentPositionLiftPercent100ths;
137
+ if (this.features.positionAwareTilt)
138
+ this.state.targetPositionTiltPercent100ths = this.state.currentPositionTiltPercent100ths;
139
+ this.state.operationalStatus = { global: WindowCovering.MovementStatus.Stopped, lift: WindowCovering.MovementStatus.Stopped, tilt: WindowCovering.MovementStatus.Stopped };
140
+ }
141
+ device.log.debug(`MatterbridgeWindowCoveringServer: stopMotion result target ${this.state.targetPositionLiftPercent100ths} current ${this.state.currentPositionLiftPercent100ths} status global ${this.getMovementStatusLabel(this.state.operationalStatus.global)} lift ${this.getMovementStatusLabel(this.state.operationalStatus.lift)} tilt ${this.getMovementStatusLabel(this.state.operationalStatus.tilt)} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
59
142
  }
60
143
  async goToLiftPercentage(request) {
61
144
  const device = this.endpoint.stateOf(MatterbridgeServer);
62
- device.log.info(`Setting cover lift percentage to ${request.liftPercent100thsValue} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
145
+ device.log.info(`MatterbridgeWindowCoveringServer: setting cover lift percentage to ${request.liftPercent100thsValue} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
63
146
  await device.commandHandler.executeHandler('WindowCovering.goToLiftPercentage', {
64
147
  command: 'goToLiftPercentage',
65
148
  request,
@@ -68,13 +151,15 @@ export class MatterbridgeWindowCoveringServer extends WindowCoveringServer.with(
68
151
  endpoint: this.endpoint,
69
152
  context: this.context,
70
153
  });
71
- device.log.debug(`MatterbridgeWindowCoveringServer: goToLiftPercentage with ${request.liftPercent100thsValue}`);
154
+ device.log.debug(`MatterbridgeWindowCoveringServer: goToLiftPercentage with ${request.liftPercent100thsValue} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
72
155
  await super.goToLiftPercentage(request);
73
- device.log.debug(`MatterbridgeWindowCoveringServer: goToLiftPercentage result target ${this.state.targetPositionLiftPercent100ths} current ${this.state.currentPositionLiftPercent100ths} status global ${this.getMovementStatusLabel(this.state.operationalStatus.global)} lift ${this.getMovementStatusLabel(this.state.operationalStatus.lift)} tilt ${this.getMovementStatusLabel(this.state.operationalStatus.tilt)}`);
156
+ if (this.state.targetPositionLiftPercent100ths !== null)
157
+ this.#startLiftMovement(this.state.targetPositionLiftPercent100ths);
158
+ device.log.debug(`MatterbridgeWindowCoveringServer: goToLiftPercentage result target ${this.state.targetPositionLiftPercent100ths} current ${this.state.currentPositionLiftPercent100ths} status global ${this.getMovementStatusLabel(this.state.operationalStatus.global)} lift ${this.getMovementStatusLabel(this.state.operationalStatus.lift)} tilt ${this.getMovementStatusLabel(this.state.operationalStatus.tilt)} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
74
159
  }
75
160
  async goToTiltPercentage(request) {
76
161
  const device = this.endpoint.stateOf(MatterbridgeServer);
77
- device.log.info(`Setting cover tilt percentage to ${request.tiltPercent100thsValue} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
162
+ device.log.info(`MatterbridgeWindowCoveringServer: setting cover tilt percentage to ${request.tiltPercent100thsValue} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
78
163
  await device.commandHandler.executeHandler('WindowCovering.goToTiltPercentage', {
79
164
  command: 'goToTiltPercentage',
80
165
  request,
@@ -83,10 +168,21 @@ export class MatterbridgeWindowCoveringServer extends WindowCoveringServer.with(
83
168
  endpoint: this.endpoint,
84
169
  context: this.context,
85
170
  });
86
- device.log.debug(`MatterbridgeWindowCoveringServer: goToTiltPercentage with ${request.tiltPercent100thsValue}`);
171
+ device.log.debug(`MatterbridgeWindowCoveringServer: goToTiltPercentage with ${request.tiltPercent100thsValue} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
87
172
  await super.goToTiltPercentage(request);
88
- device.log.debug(`MatterbridgeWindowCoveringServer: goToTiltPercentage result target ${this.state.targetPositionTiltPercent100ths} current ${this.state.currentPositionTiltPercent100ths} status global ${this.getMovementStatusLabel(this.state.operationalStatus.global)} lift ${this.getMovementStatusLabel(this.state.operationalStatus.lift)} tilt ${this.getMovementStatusLabel(this.state.operationalStatus.tilt)}`);
89
- }
90
- handleMovement() {
173
+ if (this.state.targetPositionTiltPercent100ths !== null)
174
+ this.#startTiltMovement(this.state.targetPositionTiltPercent100ths);
175
+ device.log.debug(`MatterbridgeWindowCoveringServer: goToTiltPercentage result target ${this.state.targetPositionTiltPercent100ths} current ${this.state.currentPositionTiltPercent100ths} status global ${this.getMovementStatusLabel(this.state.operationalStatus.global)} lift ${this.getMovementStatusLabel(this.state.operationalStatus.lift)} tilt ${this.getMovementStatusLabel(this.state.operationalStatus.tilt)} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
91
176
  }
92
177
  }
178
+ (function (MatterbridgeWindowCoveringServer) {
179
+ class Internal extends WindowCoveringBaseServer.Internal {
180
+ liftMovementTimer;
181
+ tiltMovementTimer;
182
+ }
183
+ MatterbridgeWindowCoveringServer.Internal = Internal;
184
+ class State extends WindowCoveringBaseServer.State {
185
+ movementDuration = 0;
186
+ }
187
+ MatterbridgeWindowCoveringServer.State = State;
188
+ })(MatterbridgeWindowCoveringServer || (MatterbridgeWindowCoveringServer = {}));
@@ -10,6 +10,7 @@ import { PowerTopology } from '@matter/types/clusters/power-topology';
10
10
  import { ResourceMonitoring } from '@matter/types/clusters/resource-monitoring';
11
11
  import { RvcCleanMode } from '@matter/types/clusters/rvc-clean-mode';
12
12
  import { RvcRunMode } from '@matter/types/clusters/rvc-run-mode';
13
+ import { Thermostat } from '@matter/types/clusters/thermostat';
13
14
  import { EndpointNumber } from '@matter/types/datatype';
14
15
  import { getErrorMessage } from '@matterbridge/utils/error';
15
16
  import { AirConditioner } from './devices/airConditioner.js';
@@ -348,11 +349,15 @@ export async function createDemoDevices(matterbridge) {
348
349
  await registerDevice(ep, 'Door Lock User PIN Schedules', 'ENTRY-08-01-2');
349
350
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('DoorLockController'), bridgedNode, powerSource], { id: 'DoorLockController', number: EndpointNumber(8_02) });
350
351
  await registerDevice(ep, 'Door Lock Controller', 'ENTRY-08-02');
351
- ep = new MatterbridgeEndpoint([getSupportedDeviceType('WindowCovering'), bridgedNode, powerSource], { id: 'WindowCovering', number: EndpointNumber(8_03) });
352
- await registerDevice(ep, 'Window Covering', 'ENTRY-08-03');
352
+ ep = new MatterbridgeEndpoint([getSupportedDeviceType('WindowCovering'), bridgedNode, powerSource], { id: 'WindowCoveringLift', number: EndpointNumber(8_03) });
353
+ ep.createDefaultWindowCoveringClusterServer();
354
+ await registerDevice(ep, 'Window Covering Lift', 'ENTRY-08-03');
353
355
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('WindowCovering'), bridgedNode, powerSource], { id: 'WindowCoveringTilt', number: EndpointNumber(8_03_1) });
354
- ep.createDefaultLiftTiltWindowCoveringClusterServer();
356
+ ep.createDefaultTiltWindowCoveringClusterServer();
355
357
  await registerDevice(ep, 'Window Covering Tilt', 'ENTRY-08-03-1');
358
+ ep = new MatterbridgeEndpoint([getSupportedDeviceType('WindowCovering'), bridgedNode, powerSource], { id: 'WindowCoveringLiftTilt', number: EndpointNumber(8_03_2) });
359
+ ep.createDefaultLiftTiltWindowCoveringClusterServer();
360
+ await registerDevice(ep, 'Window Covering LiftTilt', 'ENTRY-08-03-2');
356
361
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('WindowCoveringController'), bridgedNode, powerSource], { id: 'WindowCoveringController', number: EndpointNumber(8_04) });
357
362
  await registerDevice(ep, 'Window Covering Controller', 'ENTRY-08-04');
358
363
  ep = new Closure('Closure', 'ENTRY-08-05', {
@@ -443,8 +448,9 @@ export async function createDemoDevices(matterbridge) {
443
448
  await registerDevice(ep, 'Closure Panel Smart-Glass', 'ENTRY-08-06-5');
444
449
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('ClosureController'), bridgedNode, powerSource], { id: 'ClosureController', number: EndpointNumber(8_07) });
445
450
  await registerDevice(ep, 'Closure Controller', 'ENTRY-08-07');
446
- ep = new MatterbridgeEndpoint([getSupportedDeviceType('Thermostat'), bridgedNode, powerSource], { id: 'Thermostat', number: EndpointNumber(9_01) });
447
- await registerDevice(ep, 'Thermostat', 'HVAC-09-01');
451
+ ep = new MatterbridgeEndpoint([getSupportedDeviceType('Thermostat'), bridgedNode, powerSource], { id: 'ThermostatAuto', number: EndpointNumber(9_01) });
452
+ ep.createDefaultThermostatClusterServer(23, 21, 25, 2, 0, 47, 3, 50);
453
+ await registerDevice(ep, 'Thermostat Auto', 'HVAC-09-01');
448
454
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('Thermostat'), bridgedNode, powerSource], { id: 'ThermostatHeating', number: EndpointNumber(9_01_1) });
449
455
  ep.createDefaultHeatingThermostatClusterServer();
450
456
  await registerDevice(ep, 'Thermostat Heating', 'HVAC-09-01-1');
@@ -458,7 +464,13 @@ export async function createDemoDevices(matterbridge) {
458
464
  ep.createDefaultSchedulesThermostatClusterServer();
459
465
  await registerDevice(ep, 'Thermostat Schedules', 'HVAC-09-01-4');
460
466
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('Thermostat'), bridgedNode, powerSource], { id: 'ThermostatSuggestions', number: EndpointNumber(9_01_5) });
461
- ep.createDefaultThermostatSuggestionsClusterServer();
467
+ ep.createDefaultThermostatSuggestionsClusterServer(23, 21, 25, 0, 0, 50, 0, 50, undefined, undefined, undefined, undefined, null, [
468
+ { presetHandle: Uint8Array.from([0]), presetScenario: Thermostat.PresetScenario.Occupied, name: 'Occupied', coolingSetpoint: 2500, heatingSetpoint: 2100, builtIn: true },
469
+ { presetHandle: Uint8Array.from([1]), presetScenario: Thermostat.PresetScenario.Unoccupied, name: 'Unoccupied', coolingSetpoint: 2700, heatingSetpoint: 1900, builtIn: true },
470
+ ], [
471
+ { presetScenario: Thermostat.PresetScenario.Occupied, numberOfPresets: 4, presetTypeFeatures: { automatic: false, supportsNames: true } },
472
+ { presetScenario: Thermostat.PresetScenario.Unoccupied, numberOfPresets: 4, presetTypeFeatures: { automatic: false, supportsNames: true } },
473
+ ]);
462
474
  await registerDevice(ep, 'Thermostat Suggestions', 'HVAC-09-01-5');
463
475
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('Fan'), bridgedNode, powerSource], { id: 'Fan', number: EndpointNumber(9_02) });
464
476
  await registerDevice(ep, 'Fan OffLowMedHighAuto', 'HVAC-09-02');
@@ -407,7 +407,16 @@ export class MatterNode extends EventEmitter {
407
407
  this.log.warn(`Invalid discriminator ${discriminator} for server node ${storeId}. Discriminator must be between 0 and 4095 (0xFFF). Generating a random discriminator...`);
408
408
  discriminator = PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
409
409
  }
410
- const rootEndpoint = hasParameter('root-power-source') ? ServerNode.RootEndpoint.with(PowerSourceServer.with(PowerSource.Feature.Wired)) : ServerNode.RootEndpoint;
410
+ let rootEndpoint;
411
+ if (hasParameter('root-power-source') || process.env.MATTERBRIDGE_CHIP_TEST) {
412
+ this.log.warn(' ****************************************************************************************');
413
+ this.log.warn(' * Adding the PowerSource cluster server to the root endpoint. *');
414
+ this.log.warn(' ****************************************************************************************');
415
+ rootEndpoint = ServerNode.RootEndpoint.with(PowerSourceServer.with(PowerSource.Feature.Wired));
416
+ }
417
+ else {
418
+ rootEndpoint = ServerNode.RootEndpoint;
419
+ }
411
420
  const serverNode = await ServerNode.create(rootEndpoint, {
412
421
  id: storeId,
413
422
  environment: this.environment,
@@ -1893,7 +1893,16 @@ export class Matterbridge extends EventEmitter {
1893
1893
  this.log.warn(`Invalid discriminator ${discriminator} for server node ${storeId}. Discriminator must be between 0 and 4095 (0xFFF). Generating a random discriminator...`);
1894
1894
  discriminator = PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
1895
1895
  }
1896
- let rootEndpoint = hasParameter('root-power-source') ? ServerNode.RootEndpoint.with(PowerSourceServer.with(PowerSource.Feature.Wired)) : ServerNode.RootEndpoint;
1896
+ let rootEndpoint;
1897
+ if (hasParameter('root-power-source') || process.env.MATTERBRIDGE_CHIP_TEST) {
1898
+ this.log.warn(' ****************************************************************************************');
1899
+ this.log.warn(' * Adding the PowerSource cluster server to the root endpoint. *');
1900
+ this.log.warn(' ****************************************************************************************');
1901
+ rootEndpoint = ServerNode.RootEndpoint.with(PowerSourceServer.with(PowerSource.Feature.Wired));
1902
+ }
1903
+ else {
1904
+ rootEndpoint = ServerNode.RootEndpoint;
1905
+ }
1897
1906
  if (process.env.MATTERBRIDGE_CHIP_TEST && fs.existsSync(path.join(path.dirname(fileURLToPath(import.meta.url)), 'chipTests.js'))) {
1898
1907
  this.log.warn(' ****************************************************************************************');
1899
1908
  this.log.warn(' * MATTERBRIDGE_CHIP_TEST environment variable is set. Running TestEventTrigger server. *');
@@ -171,8 +171,9 @@ export declare class MatterbridgeEndpoint extends Endpoint {
171
171
  createCtColorControlClusterServer(colorTemperatureMireds?: number, colorTempPhysicalMinMireds?: number, colorTempPhysicalMaxMireds?: number): this;
172
172
  configureColorControlMode(colorMode: ColorControl.ColorMode): Promise<void>;
173
173
  configureEnhancedColorControlMode(colorMode: ColorControl.EnhancedColorMode): Promise<void>;
174
- createDefaultWindowCoveringClusterServer(positionPercent100ths?: number, type?: WindowCovering.WindowCoveringType, endProductType?: WindowCovering.EndProductType): this;
175
- createDefaultLiftTiltWindowCoveringClusterServer(positionLiftPercent100ths?: number, positionTiltPercent100ths?: number, type?: WindowCovering.WindowCoveringType, endProductType?: WindowCovering.EndProductType): this;
174
+ createDefaultWindowCoveringClusterServer(positionPercent100ths?: number | null, type?: WindowCovering.WindowCoveringType, endProductType?: WindowCovering.EndProductType, movementDuration?: number): this;
175
+ createDefaultLiftTiltWindowCoveringClusterServer(positionLiftPercent100ths?: number | null, positionTiltPercent100ths?: number | null, type?: WindowCovering.WindowCoveringType, endProductType?: WindowCovering.EndProductType, movementDuration?: number): this;
176
+ createDefaultTiltWindowCoveringClusterServer(positionTiltPercent100ths?: number | null, type?: WindowCovering.WindowCoveringType, endProductType?: WindowCovering.EndProductType, movementDuration?: number): this;
176
177
  setWindowCoveringTargetAsCurrentAndStopped(): Promise<void>;
177
178
  setWindowCoveringCurrentTargetStatus(current: number, target: number, status: WindowCovering.MovementStatus): Promise<void>;
178
179
  setWindowCoveringStatus(status: WindowCovering.MovementStatus): Promise<void>;
@@ -825,7 +825,7 @@ export class MatterbridgeEndpoint extends Endpoint {
825
825
  await this.setAttribute(ColorControl.id, 'enhancedColorMode', colorMode, this.log);
826
826
  }
827
827
  }
828
- createDefaultWindowCoveringClusterServer(positionPercent100ths, type = WindowCovering.WindowCoveringType.Rollershade, endProductType = WindowCovering.EndProductType.RollerShade) {
828
+ createDefaultWindowCoveringClusterServer(positionPercent100ths = 0, type = WindowCovering.WindowCoveringType.Rollershade, endProductType = WindowCovering.EndProductType.RollerShade, movementDuration = 0) {
829
829
  this.behaviors.require(MatterbridgeWindowCoveringServer.with(WindowCovering.Feature.Lift, WindowCovering.Feature.PositionAwareLift), {
830
830
  type,
831
831
  numberOfActuationsLift: 0,
@@ -841,12 +841,14 @@ export class MatterbridgeEndpoint extends Endpoint {
841
841
  operationalStatus: { global: WindowCovering.MovementStatus.Stopped, lift: WindowCovering.MovementStatus.Stopped, tilt: WindowCovering.MovementStatus.Stopped },
842
842
  endProductType,
843
843
  mode: { motorDirectionReversed: false, calibrationMode: false, maintenanceMode: false, ledFeedback: false },
844
- targetPositionLiftPercent100ths: positionPercent100ths ?? 0,
845
- currentPositionLiftPercent100ths: positionPercent100ths ?? 0,
844
+ targetPositionLiftPercent100ths: positionPercent100ths,
845
+ currentPositionLiftPercent100ths: positionPercent100ths,
846
+ currentPositionLiftPercentage: positionPercent100ths === null ? null : Math.floor(positionPercent100ths / 100),
847
+ movementDuration,
846
848
  });
847
849
  return this;
848
850
  }
849
- createDefaultLiftTiltWindowCoveringClusterServer(positionLiftPercent100ths, positionTiltPercent100ths, type = WindowCovering.WindowCoveringType.TiltBlindLift, endProductType = WindowCovering.EndProductType.InteriorBlind) {
851
+ createDefaultLiftTiltWindowCoveringClusterServer(positionLiftPercent100ths = 0, positionTiltPercent100ths = 0, type = WindowCovering.WindowCoveringType.TiltBlindLift, endProductType = WindowCovering.EndProductType.InteriorBlind, movementDuration = 0) {
850
852
  this.behaviors.require(MatterbridgeWindowCoveringServer.with(WindowCovering.Feature.Lift, WindowCovering.Feature.PositionAwareLift, WindowCovering.Feature.Tilt, WindowCovering.Feature.PositionAwareTilt), {
851
853
  type,
852
854
  numberOfActuationsLift: 0,
@@ -863,10 +865,36 @@ export class MatterbridgeEndpoint extends Endpoint {
863
865
  operationalStatus: { global: WindowCovering.MovementStatus.Stopped, lift: WindowCovering.MovementStatus.Stopped, tilt: WindowCovering.MovementStatus.Stopped },
864
866
  endProductType,
865
867
  mode: { motorDirectionReversed: false, calibrationMode: false, maintenanceMode: false, ledFeedback: false },
866
- targetPositionLiftPercent100ths: positionLiftPercent100ths ?? 0,
867
- currentPositionLiftPercent100ths: positionLiftPercent100ths ?? 0,
868
- targetPositionTiltPercent100ths: positionTiltPercent100ths ?? 0,
869
- currentPositionTiltPercent100ths: positionTiltPercent100ths ?? 0,
868
+ targetPositionLiftPercent100ths: positionLiftPercent100ths,
869
+ currentPositionLiftPercent100ths: positionLiftPercent100ths,
870
+ currentPositionLiftPercentage: positionLiftPercent100ths === null ? null : Math.floor(positionLiftPercent100ths / 100),
871
+ targetPositionTiltPercent100ths: positionTiltPercent100ths,
872
+ currentPositionTiltPercent100ths: positionTiltPercent100ths,
873
+ currentPositionTiltPercentage: positionTiltPercent100ths === null ? null : Math.floor(positionTiltPercent100ths / 100),
874
+ movementDuration,
875
+ });
876
+ return this;
877
+ }
878
+ createDefaultTiltWindowCoveringClusterServer(positionTiltPercent100ths = 0, type = WindowCovering.WindowCoveringType.TiltBlindTiltOnly, endProductType = WindowCovering.EndProductType.InteriorVenetianBlind, movementDuration = 0) {
879
+ this.behaviors.require(MatterbridgeWindowCoveringServer.with(WindowCovering.Feature.Tilt, WindowCovering.Feature.PositionAwareTilt), {
880
+ type,
881
+ numberOfActuationsTilt: 0,
882
+ configStatus: {
883
+ operational: true,
884
+ onlineReserved: false,
885
+ liftMovementReversed: false,
886
+ liftPositionAware: false,
887
+ tiltPositionAware: true,
888
+ liftEncoderControlled: false,
889
+ tiltEncoderControlled: false,
890
+ },
891
+ operationalStatus: { global: WindowCovering.MovementStatus.Stopped, lift: WindowCovering.MovementStatus.Stopped, tilt: WindowCovering.MovementStatus.Stopped },
892
+ endProductType,
893
+ mode: { motorDirectionReversed: false, calibrationMode: false, maintenanceMode: false, ledFeedback: false },
894
+ targetPositionTiltPercent100ths: positionTiltPercent100ths,
895
+ currentPositionTiltPercent100ths: positionTiltPercent100ths,
896
+ currentPositionTiltPercentage: positionTiltPercent100ths === null ? null : Math.floor(positionTiltPercent100ths / 100),
897
+ movementDuration,
870
898
  });
871
899
  return this;
872
900
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/core",
3
- "version": "3.10.8-dev-20260831-e73fc65",
3
+ "version": "3.10.8-dev-20260901-c4963a1",
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.8-dev-20260831-e73fc65",
136
- "@matterbridge/thread": "3.10.8-dev-20260831-e73fc65",
137
- "@matterbridge/types": "3.10.8-dev-20260831-e73fc65",
138
- "@matterbridge/utils": "3.10.8-dev-20260831-e73fc65",
135
+ "@matterbridge/dgram": "3.10.8-dev-20260901-c4963a1",
136
+ "@matterbridge/thread": "3.10.8-dev-20260901-c4963a1",
137
+ "@matterbridge/types": "3.10.8-dev-20260901-c4963a1",
138
+ "@matterbridge/utils": "3.10.8-dev-20260901-c4963a1",
139
139
  "escape-html": "1.0.3",
140
140
  "express": "5.2.1",
141
141
  "express-rate-limit": "8.7.0",