@matterbridge/core 3.10.8-dev-20260902-361500d → 3.10.8-dev-20260903-014e30e

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.
@@ -19,6 +19,8 @@ export * from './pumpConfigurationAndControlServer.js';
19
19
  export * from './serviceAreaServer.js';
20
20
  export * from './smokeCoAlarmServer.js';
21
21
  export * from './switchServer.js';
22
+ export * from './temperatureAlarmServer.js';
22
23
  export * from './thermostatServer.js';
23
24
  export * from './valveConfigurationAndControlServer.js';
25
+ export * from './waterTankLevelMonitoringServer.js';
24
26
  export * from './windowCoveringServer.js';
@@ -19,6 +19,8 @@ export * from './pumpConfigurationAndControlServer.js';
19
19
  export * from './serviceAreaServer.js';
20
20
  export * from './smokeCoAlarmServer.js';
21
21
  export * from './switchServer.js';
22
+ export * from './temperatureAlarmServer.js';
22
23
  export * from './thermostatServer.js';
23
24
  export * from './valveConfigurationAndControlServer.js';
25
+ export * from './waterTankLevelMonitoringServer.js';
24
26
  export * from './windowCoveringServer.js';
@@ -0,0 +1,26 @@
1
+ import type { MaybePromise } from '@matter/general';
2
+ import { TemperatureAlarmServer } from '@matter/node/behaviors/temperature-alarm';
3
+ import { TemperatureAlarm } from '@matter/types/clusters/temperature-alarm';
4
+ declare class TemperatureAlarmBaseServer extends TemperatureAlarmServer {
5
+ static readonly schema: import("@matter/model").ClusterModel;
6
+ }
7
+ declare const MatterbridgeTemperatureAlarmServer_base: import("@matter/node").ClusterBehavior.Type<typeof TemperatureAlarmBaseServer, import("@matter/types").ClusterType.WithSupportedFeatures<TemperatureAlarm, {
8
+ majorThreshold: false;
9
+ minorThreshold: false;
10
+ overCriticalAdjustable: false;
11
+ overMajorAdjustable: false;
12
+ overMinorAdjustable: false;
13
+ overTemperature: true;
14
+ reset: true;
15
+ underCriticalAdjustable: false;
16
+ underMajorAdjustable: false;
17
+ underMinorAdjustable: false;
18
+ underTemperature: true;
19
+ }>, import("@matter/types").ClusterType.Concrete, new () => {}, "temperatureAlarm">;
20
+ export declare class MatterbridgeTemperatureAlarmServer extends MatterbridgeTemperatureAlarmServer_base {
21
+ #private;
22
+ initialize(): MaybePromise;
23
+ reset(request: TemperatureAlarm.ResetRequest): Promise<void>;
24
+ modifyEnabledAlarms(request: TemperatureAlarm.ModifyEnabledAlarmsRequest): Promise<void>;
25
+ }
26
+ export {};
@@ -0,0 +1,96 @@
1
+ import { AttributeElement, CommandElement, EventElement, FieldElement } from '@matter/model';
2
+ import { TemperatureAlarmServer } from '@matter/node/behaviors/temperature-alarm';
3
+ import { Status, StatusResponseError } from '@matter/types';
4
+ import { TemperatureAlarm } from '@matter/types/clusters/temperature-alarm';
5
+ import { debugStringify, nf } from 'node-ansi-logger';
6
+ import { MatterbridgeServer } from './matterbridgeServer.js';
7
+ const MatterbridgeTemperatureAlarmSchema = TemperatureAlarmServer.schema.extend({}, AttributeElement({ name: 'Mask', id: 0x0000, type: 'AlarmBitmap', access: 'R V', conformance: 'M' }), AttributeElement({ name: 'Latch', id: 0x0001, type: 'AlarmBitmap', access: 'R V', conformance: 'RESET', quality: 'F' }), AttributeElement({ name: 'State', id: 0x0002, type: 'AlarmBitmap', access: 'R V', conformance: 'M' }), AttributeElement({ name: 'Supported', id: 0x0003, type: 'AlarmBitmap', access: 'R V', conformance: 'M', quality: 'F' }), CommandElement({ name: 'Reset', id: 0x0000, access: 'O', conformance: 'RESET', direction: 'request', response: 'status' }, FieldElement({ name: 'Alarms', id: 0x0000, type: 'AlarmBitmap', conformance: 'M' })), CommandElement({ name: 'ModifyEnabledAlarms', id: 0x0001, access: 'O', conformance: 'O', direction: 'request', response: 'status' }, FieldElement({ name: 'Mask', id: 0x0000, type: 'AlarmBitmap', conformance: 'M' })), EventElement({ name: 'Notify', id: 0x0000, access: 'V', conformance: 'M', priority: 'info' }, FieldElement({ name: 'Active', id: 0x0000, type: 'AlarmBitmap', conformance: 'M' }), FieldElement({ name: 'Inactive', id: 0x0001, type: 'AlarmBitmap', conformance: 'M' }), FieldElement({ name: 'State', id: 0x0002, type: 'AlarmBitmap', conformance: 'M' }), FieldElement({ name: 'Mask', id: 0x0003, type: 'AlarmBitmap', conformance: 'M' })));
8
+ class TemperatureAlarmBaseServer extends TemperatureAlarmServer {
9
+ static schema = MatterbridgeTemperatureAlarmSchema;
10
+ }
11
+ export class MatterbridgeTemperatureAlarmServer extends TemperatureAlarmBaseServer.with(TemperatureAlarm.Feature.Reset, TemperatureAlarm.Feature.OverTemperature, TemperatureAlarm.Feature.UnderTemperature) {
12
+ initialize() {
13
+ this.reactTo(this.events.state$Changed, this.#emitNotify);
14
+ }
15
+ #emitNotify(state, oldState) {
16
+ const active = {
17
+ criticalOverTemperatureAlarm: Boolean(state.criticalOverTemperatureAlarm && !oldState.criticalOverTemperatureAlarm),
18
+ majorOverTemperatureAlarm: Boolean(state.majorOverTemperatureAlarm && !oldState.majorOverTemperatureAlarm),
19
+ minorOverTemperatureAlarm: Boolean(state.minorOverTemperatureAlarm && !oldState.minorOverTemperatureAlarm),
20
+ minorUnderTemperatureAlarm: Boolean(state.minorUnderTemperatureAlarm && !oldState.minorUnderTemperatureAlarm),
21
+ majorUnderTemperatureAlarm: Boolean(state.majorUnderTemperatureAlarm && !oldState.majorUnderTemperatureAlarm),
22
+ criticalUnderTemperatureAlarm: Boolean(state.criticalUnderTemperatureAlarm && !oldState.criticalUnderTemperatureAlarm),
23
+ };
24
+ const inactive = {
25
+ criticalOverTemperatureAlarm: Boolean(!state.criticalOverTemperatureAlarm && oldState.criticalOverTemperatureAlarm),
26
+ majorOverTemperatureAlarm: Boolean(!state.majorOverTemperatureAlarm && oldState.majorOverTemperatureAlarm),
27
+ minorOverTemperatureAlarm: Boolean(!state.minorOverTemperatureAlarm && oldState.minorOverTemperatureAlarm),
28
+ minorUnderTemperatureAlarm: Boolean(!state.minorUnderTemperatureAlarm && oldState.minorUnderTemperatureAlarm),
29
+ majorUnderTemperatureAlarm: Boolean(!state.majorUnderTemperatureAlarm && oldState.majorUnderTemperatureAlarm),
30
+ criticalUnderTemperatureAlarm: Boolean(!state.criticalUnderTemperatureAlarm && oldState.criticalUnderTemperatureAlarm),
31
+ };
32
+ this.events.notify.emit({ active, inactive, state, mask: this.state.mask }, this.context);
33
+ }
34
+ #assertAlarmsSupported(alarms, status) {
35
+ const unsupported = {
36
+ criticalOverTemperatureAlarm: Boolean(alarms.criticalOverTemperatureAlarm && !this.state.supported.criticalOverTemperatureAlarm),
37
+ majorOverTemperatureAlarm: Boolean(alarms.majorOverTemperatureAlarm && !this.state.supported.majorOverTemperatureAlarm),
38
+ minorOverTemperatureAlarm: Boolean(alarms.minorOverTemperatureAlarm && !this.state.supported.minorOverTemperatureAlarm),
39
+ minorUnderTemperatureAlarm: Boolean(alarms.minorUnderTemperatureAlarm && !this.state.supported.minorUnderTemperatureAlarm),
40
+ majorUnderTemperatureAlarm: Boolean(alarms.majorUnderTemperatureAlarm && !this.state.supported.majorUnderTemperatureAlarm),
41
+ criticalUnderTemperatureAlarm: Boolean(alarms.criticalUnderTemperatureAlarm && !this.state.supported.criticalUnderTemperatureAlarm),
42
+ };
43
+ if (Object.values(unsupported).some(Boolean)) {
44
+ throw new StatusResponseError(`MatterbridgeTemperatureAlarmServer: requested alarm is not supported (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`, status);
45
+ }
46
+ }
47
+ #clearAlarms(alarms) {
48
+ return {
49
+ criticalOverTemperatureAlarm: Boolean(this.state.state.criticalOverTemperatureAlarm && !alarms.criticalOverTemperatureAlarm),
50
+ majorOverTemperatureAlarm: Boolean(this.state.state.majorOverTemperatureAlarm && !alarms.majorOverTemperatureAlarm),
51
+ minorOverTemperatureAlarm: Boolean(this.state.state.minorOverTemperatureAlarm && !alarms.minorOverTemperatureAlarm),
52
+ minorUnderTemperatureAlarm: Boolean(this.state.state.minorUnderTemperatureAlarm && !alarms.minorUnderTemperatureAlarm),
53
+ majorUnderTemperatureAlarm: Boolean(this.state.state.majorUnderTemperatureAlarm && !alarms.majorUnderTemperatureAlarm),
54
+ criticalUnderTemperatureAlarm: Boolean(this.state.state.criticalUnderTemperatureAlarm && !alarms.criticalUnderTemperatureAlarm),
55
+ };
56
+ }
57
+ #applyMaskToState(mask) {
58
+ return {
59
+ criticalOverTemperatureAlarm: Boolean(this.state.state.criticalOverTemperatureAlarm && mask.criticalOverTemperatureAlarm),
60
+ majorOverTemperatureAlarm: Boolean(this.state.state.majorOverTemperatureAlarm && mask.majorOverTemperatureAlarm),
61
+ minorOverTemperatureAlarm: Boolean(this.state.state.minorOverTemperatureAlarm && mask.minorOverTemperatureAlarm),
62
+ minorUnderTemperatureAlarm: Boolean(this.state.state.minorUnderTemperatureAlarm && mask.minorUnderTemperatureAlarm),
63
+ majorUnderTemperatureAlarm: Boolean(this.state.state.majorUnderTemperatureAlarm && mask.majorUnderTemperatureAlarm),
64
+ criticalUnderTemperatureAlarm: Boolean(this.state.state.criticalUnderTemperatureAlarm && mask.criticalUnderTemperatureAlarm),
65
+ };
66
+ }
67
+ async reset(request) {
68
+ const device = this.endpoint.stateOf(MatterbridgeServer);
69
+ device.log.info(`MatterbridgeTemperatureAlarmServer: resetting alarms ${debugStringify(request.alarms)}${nf} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
70
+ await device.commandHandler.executeHandler('TemperatureAlarm.reset', {
71
+ command: 'reset',
72
+ request,
73
+ cluster: MatterbridgeTemperatureAlarmServer.id,
74
+ attributes: this.state,
75
+ endpoint: this.endpoint,
76
+ context: this.context,
77
+ });
78
+ this.#assertAlarmsSupported(request.alarms, Status.Failure);
79
+ this.state.state = this.#clearAlarms(request.alarms);
80
+ }
81
+ async modifyEnabledAlarms(request) {
82
+ const device = this.endpoint.stateOf(MatterbridgeServer);
83
+ device.log.info(`MatterbridgeTemperatureAlarmServer: modifying enabled alarms ${debugStringify(request.mask)}${nf} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
84
+ await device.commandHandler.executeHandler('TemperatureAlarm.modifyEnabledAlarms', {
85
+ command: 'modifyEnabledAlarms',
86
+ request,
87
+ cluster: MatterbridgeTemperatureAlarmServer.id,
88
+ attributes: this.state,
89
+ endpoint: this.endpoint,
90
+ context: this.context,
91
+ });
92
+ this.#assertAlarmsSupported(request.mask, Status.InvalidCommand);
93
+ this.state.mask = request.mask;
94
+ this.state.state = this.#applyMaskToState(request.mask);
95
+ }
96
+ }
@@ -0,0 +1,11 @@
1
+ import { WaterTankLevelMonitoringServer } from '@matter/node/behaviors/water-tank-level-monitoring';
2
+ import type { WaterTankLevelMonitoring } from '@matter/types/clusters/water-tank-level-monitoring';
3
+ declare const MatterbridgeWaterTankLevelMonitoringServer_base: import("@matter/node").ClusterBehavior.Type<typeof WaterTankLevelMonitoringServer, import("@matter/types").ClusterType.WithSupportedFeatures<WaterTankLevelMonitoring, {
4
+ condition: true;
5
+ replacementProductList: true;
6
+ warning: true;
7
+ }>, import("@matter/types").ClusterType.Concrete, new () => {}, "waterTankLevelMonitoring">;
8
+ export declare class MatterbridgeWaterTankLevelMonitoringServer extends MatterbridgeWaterTankLevelMonitoringServer_base {
9
+ resetCondition(): Promise<void>;
10
+ }
11
+ export {};
@@ -0,0 +1,21 @@
1
+ import { WaterTankLevelMonitoringServer } from '@matter/node/behaviors/water-tank-level-monitoring';
2
+ import { ResourceMonitoring } from '@matter/types/clusters/resource-monitoring';
3
+ import { MatterbridgeServer } from './matterbridgeServer.js';
4
+ export class MatterbridgeWaterTankLevelMonitoringServer extends WaterTankLevelMonitoringServer.with(ResourceMonitoring.Feature.Condition, ResourceMonitoring.Feature.Warning, ResourceMonitoring.Feature.ReplacementProductList) {
5
+ async resetCondition() {
6
+ const device = this.endpoint.stateOf(MatterbridgeServer);
7
+ device.log.info(`MatterbridgeWaterTankLevelMonitoringServer: resetting condition (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
8
+ await device.commandHandler.executeHandler('WaterTankLevelMonitoring.resetCondition', {
9
+ command: 'resetCondition',
10
+ request: {},
11
+ cluster: MatterbridgeWaterTankLevelMonitoringServer.id,
12
+ attributes: this.state,
13
+ endpoint: this.endpoint,
14
+ context: this.context,
15
+ });
16
+ this.state.condition = 100;
17
+ this.state.changeIndication = ResourceMonitoring.ChangeIndication.Ok;
18
+ this.state.lastChangedTime = Math.floor(new Date().getTime() / 1000);
19
+ device.log.debug(`MatterbridgeWaterTankLevelMonitoringServer: resetCondition called (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
20
+ }
21
+ }
@@ -32,7 +32,8 @@ export class MatterbridgeWindowCoveringServer extends WindowCoveringServer.with(
32
32
  return current < target ? WindowCovering.MovementStatus.Closing : WindowCovering.MovementStatus.Opening;
33
33
  }
34
34
  #updateGlobalOperationalStatus() {
35
- const { lift, tilt } = this.state.operationalStatus;
35
+ const lift = this.state.operationalStatus.lift ?? WindowCovering.MovementStatus.Stopped;
36
+ const tilt = this.state.operationalStatus.tilt ?? WindowCovering.MovementStatus.Stopped;
36
37
  this.state.operationalStatus.global = lift === WindowCovering.MovementStatus.Stopped ? tilt : lift;
37
38
  }
38
39
  #startLiftMovement(targetPercent100ths) {
@@ -133,7 +134,11 @@ export class MatterbridgeWindowCoveringServer extends WindowCoveringServer.with(
133
134
  this.state.targetPositionLiftPercent100ths = this.state.currentPositionLiftPercent100ths;
134
135
  if (this.features.positionAwareTilt)
135
136
  this.state.targetPositionTiltPercent100ths = this.state.currentPositionTiltPercent100ths;
136
- this.state.operationalStatus = { global: WindowCovering.MovementStatus.Stopped, lift: WindowCovering.MovementStatus.Stopped, tilt: WindowCovering.MovementStatus.Stopped };
137
+ this.state.operationalStatus = {
138
+ global: WindowCovering.MovementStatus.Stopped,
139
+ ...(this.features.lift ? { lift: WindowCovering.MovementStatus.Stopped } : {}),
140
+ ...(this.features.tilt ? { tilt: WindowCovering.MovementStatus.Stopped } : {}),
141
+ };
137
142
  }
138
143
  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})`);
139
144
  }
package/dist/chipTests.js CHANGED
@@ -24,7 +24,6 @@ import { SmokeCoAlarm } from '@matter/types/clusters/smoke-co-alarm';
24
24
  import { TariffPriceType, TariffUnit } from '@matter/types/globals';
25
25
  import { MatterbridgeOccupancySensingServer } from './behaviors/occupancySensingServer.js';
26
26
  import { cliEmitter } from './cliEmitter.js';
27
- import { MatterbridgeRefrigeratorAlarmServer } from './devices/refrigerator.js';
28
27
  import { MatterbridgeRvcOperationalStateServer, MatterbridgeRvcRunModeServer } from './devices/roboticVacuumCleaner.js';
29
28
  import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
30
29
  const chipTestAppPipePath = '/tmp/matterbridge-chip-test-app-pipe';
@@ -1019,14 +1018,7 @@ async function handleChipTestAppPipeCommand(matterbridge, command) {
1019
1018
  return;
1020
1019
  case 'SetRefrigeratorDoorStatus': {
1021
1020
  const doorOpen = Boolean(command.DoorOpen);
1022
- const wasDoorOpen = Boolean(endpoint.stateOf(MatterbridgeRefrigeratorAlarmServer).state.doorOpen);
1023
1021
  await endpoint.setCluster(RefrigeratorAlarm, { state: { doorOpen } }, matterbridge.log);
1024
- await endpoint.triggerEvent('RefrigeratorAlarm', 'notify', {
1025
- active: { doorOpen: doorOpen && !wasDoorOpen },
1026
- inactive: { doorOpen: !doorOpen && wasDoorOpen },
1027
- state: { doorOpen },
1028
- mask: endpoint.stateOf(MatterbridgeRefrigeratorAlarmServer).mask,
1029
- }, matterbridge.log);
1030
1022
  matterbridge.log.info(`CHIP test app pipe set RefrigeratorAlarm.State.DoorOpen to ${doorOpen} on endpoint ${endpointId}`);
1031
1023
  return;
1032
1024
  }
@@ -372,16 +372,20 @@ export async function createDemoDevices(matterbridge) {
372
372
  ep.createDefaultPowerSourceBatteryClusterServer();
373
373
  ep.createUserPinDoorLockClusterServer(DoorLock.LockState.Locked, DoorLock.LockType.DeadBolt, 0, 4, 10, 2, 3, 4);
374
374
  await registerDevice(ep, 'Door Lock User PIN Schedules', 'ENTRY-08-01-2');
375
+ ep = new MatterbridgeEndpoint([getSupportedDeviceType('DoorLock'), bridgedNode, powerSource], { id: 'DoorLockUserPinExpiring', number: EndpointNumber(8_01_3) });
376
+ ep.createDefaultPowerSourceBatteryClusterServer();
377
+ ep.createUserPinDoorLockClusterServer(DoorLock.LockState.Locked, DoorLock.LockType.DeadBolt, 0, 4, 10, undefined, undefined, undefined, 10);
378
+ await registerDevice(ep, 'Door Lock User PIN Expiring', 'ENTRY-08-01-3');
375
379
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('DoorLockController'), bridgedNode, powerSource], { id: 'DoorLockController', number: EndpointNumber(8_02) });
376
380
  await registerDevice(ep, 'Door Lock Controller', 'ENTRY-08-02');
377
381
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('WindowCovering'), bridgedNode, powerSource], { id: 'WindowCoveringLift', number: EndpointNumber(8_03) });
378
- ep.createDefaultWindowCoveringClusterServer();
382
+ ep.createDefaultWindowCoveringClusterServer(100_00, undefined, undefined, 10_000);
379
383
  await registerDevice(ep, 'Window Covering Lift', 'ENTRY-08-03');
380
384
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('WindowCovering'), bridgedNode, powerSource], { id: 'WindowCoveringTilt', number: EndpointNumber(8_03_1) });
381
- ep.createDefaultTiltWindowCoveringClusterServer();
385
+ ep.createDefaultTiltWindowCoveringClusterServer(100_00, undefined, undefined, 10_000);
382
386
  await registerDevice(ep, 'Window Covering Tilt', 'ENTRY-08-03-1');
383
387
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('WindowCovering'), bridgedNode, powerSource], { id: 'WindowCoveringLiftTilt', number: EndpointNumber(8_03_2) });
384
- ep.createDefaultLiftTiltWindowCoveringClusterServer();
388
+ ep.createDefaultLiftTiltWindowCoveringClusterServer(100_00, 100_00, undefined, undefined, 10_000);
385
389
  await registerDevice(ep, 'Window Covering LiftTilt', 'ENTRY-08-03-2');
386
390
  ep = new MatterbridgeEndpoint([getSupportedDeviceType('WindowCoveringController'), bridgedNode, powerSource], { id: 'WindowCoveringController', number: EndpointNumber(8_04) });
387
391
  await registerDevice(ep, 'Window Covering Controller', 'ENTRY-08-04');
@@ -1,6 +1,8 @@
1
+ import type { MaybePromise } from '@matter/general';
1
2
  import { DishwasherAlarmServer } from '@matter/node/behaviors/dishwasher-alarm';
2
3
  import { DishwasherModeServer } from '@matter/node/behaviors/dishwasher-mode';
3
- import type { EndpointNumber } from '@matter/types';
4
+ import { type EndpointNumber } from '@matter/types';
5
+ import { DishwasherAlarm } from '@matter/types/clusters/dishwasher-alarm';
4
6
  import { DishwasherMode } from '@matter/types/clusters/dishwasher-mode';
5
7
  import { ModeBase } from '@matter/types/clusters/mode-base';
6
8
  import type { OperationalState } from '@matter/types/clusters/operational-state';
@@ -32,6 +34,16 @@ export declare class MatterbridgeDishwasherModeServer extends DishwasherModeServ
32
34
  protected handleOnOffChange(onOff: boolean): void;
33
35
  changeToMode(request: ModeBase.ChangeToModeRequest): Promise<ModeBase.ChangeToModeResponse>;
34
36
  }
35
- export declare class MatterbridgeDishwasherAlarmServer extends DishwasherAlarmServer {
37
+ declare class DishwasherAlarmBaseServer extends DishwasherAlarmServer {
36
38
  static readonly schema: import("@matter/model").ClusterModel;
37
39
  }
40
+ declare const MatterbridgeDishwasherAlarmServer_base: import("@matter/node").ClusterBehavior.Type<typeof DishwasherAlarmBaseServer, import("@matter/types").ClusterType.WithSupportedFeatures<DishwasherAlarm, {
41
+ reset: true;
42
+ }>, import("@matter/types").ClusterType.Concrete, new () => {}, "dishwasherAlarm">;
43
+ export declare class MatterbridgeDishwasherAlarmServer extends MatterbridgeDishwasherAlarmServer_base {
44
+ #private;
45
+ initialize(): MaybePromise;
46
+ reset(request: DishwasherAlarm.ResetRequest): Promise<void>;
47
+ modifyEnabledAlarms(request: DishwasherAlarm.ModifyEnabledAlarmsRequest): Promise<void>;
48
+ }
49
+ export {};
@@ -1,9 +1,12 @@
1
- import { AttributeElement, EventElement, FieldElement } from '@matter/model';
1
+ import { AttributeElement, CommandElement, EventElement, FieldElement } from '@matter/model';
2
2
  import { DishwasherAlarmServer } from '@matter/node/behaviors/dishwasher-alarm';
3
3
  import { DishwasherModeServer } from '@matter/node/behaviors/dishwasher-mode';
4
+ import { Status, StatusResponseError } from '@matter/types';
5
+ import { DishwasherAlarm } from '@matter/types/clusters/dishwasher-alarm';
4
6
  import { DishwasherMode } from '@matter/types/clusters/dishwasher-mode';
5
7
  import { ModeBase } from '@matter/types/clusters/mode-base';
6
8
  import { OnOff } from '@matter/types/clusters/on-off';
9
+ import { debugStringify, nf } from 'node-ansi-logger';
7
10
  import { MatterbridgeServer } from '../behaviors/matterbridgeServer.js';
8
11
  import { MatterbridgeOnOffServer } from '../behaviors/onOffServer.js';
9
12
  import { dishwasher, powerSource } from '../matterbridgeDeviceTypes.js';
@@ -56,6 +59,7 @@ export class Dishwasher extends MatterbridgeEndpoint {
56
59
  createDefaultDishwasherAlarmClusterServer() {
57
60
  this.behaviors.require(MatterbridgeDishwasherAlarmServer, {
58
61
  mask: { inflowError: false, drainError: false, doorError: true, tempTooLow: false, tempTooHigh: false, waterLevelError: false },
62
+ latch: { inflowError: false, drainError: false, doorError: false, tempTooLow: false, tempTooHigh: false, waterLevelError: false },
59
63
  state: { inflowError: false, drainError: false, doorError: false, tempTooLow: false, tempTooHigh: false, waterLevelError: false },
60
64
  supported: { inflowError: false, drainError: false, doorError: true, tempTooLow: false, tempTooHigh: false, waterLevelError: false },
61
65
  });
@@ -98,7 +102,93 @@ export class MatterbridgeDishwasherModeServer extends DishwasherModeServer {
98
102
  }
99
103
  }
100
104
  }
101
- const MatterbridgeDishwasherAlarmSchema = DishwasherAlarmServer.schema.extend({}, AttributeElement({ name: 'Mask', id: 0x0000, type: 'AlarmBitmap', access: 'R V', conformance: 'M' }), AttributeElement({ name: 'State', id: 0x0002, type: 'AlarmBitmap', access: 'R V', conformance: 'M' }), AttributeElement({ name: 'Supported', id: 0x0003, type: 'AlarmBitmap', access: 'R V', conformance: 'M', quality: 'F' }), EventElement({ name: 'Notify', id: 0x0000, access: 'V', conformance: 'M', priority: 'info' }, FieldElement({ name: 'Active', id: 0x0000, type: 'AlarmBitmap', conformance: 'M' }), FieldElement({ name: 'Inactive', id: 0x0001, type: 'AlarmBitmap', conformance: 'M' }), FieldElement({ name: 'State', id: 0x0002, type: 'AlarmBitmap', conformance: 'M' }), FieldElement({ name: 'Mask', id: 0x0003, type: 'AlarmBitmap', conformance: 'M' })));
102
- export class MatterbridgeDishwasherAlarmServer extends DishwasherAlarmServer {
105
+ const MatterbridgeDishwasherAlarmSchema = DishwasherAlarmServer.schema.extend({}, AttributeElement({ name: 'Mask', id: 0x0000, type: 'AlarmBitmap', access: 'R V', conformance: 'M' }), AttributeElement({ name: 'Latch', id: 0x0001, type: 'AlarmBitmap', access: 'R V', conformance: 'RESET', quality: 'F' }), AttributeElement({ name: 'State', id: 0x0002, type: 'AlarmBitmap', access: 'R V', conformance: 'M' }), AttributeElement({ name: 'Supported', id: 0x0003, type: 'AlarmBitmap', access: 'R V', conformance: 'M', quality: 'F' }), CommandElement({ name: 'Reset', id: 0x0000, access: 'O', conformance: 'RESET', direction: 'request', response: 'status' }, FieldElement({ name: 'Alarms', id: 0x0000, type: 'AlarmBitmap', conformance: 'M' })), CommandElement({ name: 'ModifyEnabledAlarms', id: 0x0001, access: 'O', conformance: 'O', direction: 'request', response: 'status' }, FieldElement({ name: 'Mask', id: 0x0000, type: 'AlarmBitmap', conformance: 'M' })), EventElement({ name: 'Notify', id: 0x0000, access: 'V', conformance: 'M', priority: 'info' }, FieldElement({ name: 'Active', id: 0x0000, type: 'AlarmBitmap', conformance: 'M' }), FieldElement({ name: 'Inactive', id: 0x0001, type: 'AlarmBitmap', conformance: 'M' }), FieldElement({ name: 'State', id: 0x0002, type: 'AlarmBitmap', conformance: 'M' }), FieldElement({ name: 'Mask', id: 0x0003, type: 'AlarmBitmap', conformance: 'M' })));
106
+ class DishwasherAlarmBaseServer extends DishwasherAlarmServer {
103
107
  static schema = MatterbridgeDishwasherAlarmSchema;
104
108
  }
109
+ export class MatterbridgeDishwasherAlarmServer extends DishwasherAlarmBaseServer.with(DishwasherAlarm.Feature.Reset) {
110
+ initialize() {
111
+ this.reactTo(this.events.state$Changed, this.#emitNotify);
112
+ }
113
+ #emitNotify(state, oldState) {
114
+ const active = {
115
+ inflowError: Boolean(state.inflowError && !oldState.inflowError),
116
+ drainError: Boolean(state.drainError && !oldState.drainError),
117
+ doorError: Boolean(state.doorError && !oldState.doorError),
118
+ tempTooLow: Boolean(state.tempTooLow && !oldState.tempTooLow),
119
+ tempTooHigh: Boolean(state.tempTooHigh && !oldState.tempTooHigh),
120
+ waterLevelError: Boolean(state.waterLevelError && !oldState.waterLevelError),
121
+ };
122
+ const inactive = {
123
+ inflowError: Boolean(!state.inflowError && oldState.inflowError),
124
+ drainError: Boolean(!state.drainError && oldState.drainError),
125
+ doorError: Boolean(!state.doorError && oldState.doorError),
126
+ tempTooLow: Boolean(!state.tempTooLow && oldState.tempTooLow),
127
+ tempTooHigh: Boolean(!state.tempTooHigh && oldState.tempTooHigh),
128
+ waterLevelError: Boolean(!state.waterLevelError && oldState.waterLevelError),
129
+ };
130
+ this.events.notify.emit({ active, inactive, state, mask: this.state.mask }, this.context);
131
+ }
132
+ #assertAlarmsSupported(alarms, status) {
133
+ const unsupported = {
134
+ inflowError: Boolean(alarms.inflowError && !this.state.supported.inflowError),
135
+ drainError: Boolean(alarms.drainError && !this.state.supported.drainError),
136
+ doorError: Boolean(alarms.doorError && !this.state.supported.doorError),
137
+ tempTooLow: Boolean(alarms.tempTooLow && !this.state.supported.tempTooLow),
138
+ tempTooHigh: Boolean(alarms.tempTooHigh && !this.state.supported.tempTooHigh),
139
+ waterLevelError: Boolean(alarms.waterLevelError && !this.state.supported.waterLevelError),
140
+ };
141
+ if (Object.values(unsupported).some(Boolean)) {
142
+ throw new StatusResponseError(`MatterbridgeDishwasherAlarmServer: requested alarm is not supported (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`, status);
143
+ }
144
+ }
145
+ #clearAlarms(alarms) {
146
+ return {
147
+ inflowError: Boolean(this.state.state.inflowError && !alarms.inflowError),
148
+ drainError: Boolean(this.state.state.drainError && !alarms.drainError),
149
+ doorError: Boolean(this.state.state.doorError && !alarms.doorError),
150
+ tempTooLow: Boolean(this.state.state.tempTooLow && !alarms.tempTooLow),
151
+ tempTooHigh: Boolean(this.state.state.tempTooHigh && !alarms.tempTooHigh),
152
+ waterLevelError: Boolean(this.state.state.waterLevelError && !alarms.waterLevelError),
153
+ };
154
+ }
155
+ #applyMaskToState(mask) {
156
+ return {
157
+ inflowError: Boolean(this.state.state.inflowError && mask.inflowError),
158
+ drainError: Boolean(this.state.state.drainError && mask.drainError),
159
+ doorError: Boolean(this.state.state.doorError && mask.doorError),
160
+ tempTooLow: Boolean(this.state.state.tempTooLow && mask.tempTooLow),
161
+ tempTooHigh: Boolean(this.state.state.tempTooHigh && mask.tempTooHigh),
162
+ waterLevelError: Boolean(this.state.state.waterLevelError && mask.waterLevelError),
163
+ };
164
+ }
165
+ async reset(request) {
166
+ const device = this.endpoint.stateOf(MatterbridgeServer);
167
+ device.log.info(`MatterbridgeDishwasherAlarmServer: resetting alarms ${debugStringify(request.alarms)}${nf} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
168
+ await device.commandHandler.executeHandler('DishwasherAlarm.reset', {
169
+ command: 'reset',
170
+ request,
171
+ cluster: MatterbridgeDishwasherAlarmServer.id,
172
+ attributes: this.state,
173
+ endpoint: this.endpoint,
174
+ context: this.context,
175
+ });
176
+ this.#assertAlarmsSupported(request.alarms, Status.Failure);
177
+ this.state.state = this.#clearAlarms(request.alarms);
178
+ }
179
+ async modifyEnabledAlarms(request) {
180
+ const device = this.endpoint.stateOf(MatterbridgeServer);
181
+ device.log.info(`MatterbridgeDishwasherAlarmServer: modifying enabled alarms ${debugStringify(request.mask)}${nf} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
182
+ await device.commandHandler.executeHandler('DishwasherAlarm.modifyEnabledAlarms', {
183
+ command: 'modifyEnabledAlarms',
184
+ request,
185
+ cluster: MatterbridgeDishwasherAlarmServer.id,
186
+ attributes: this.state,
187
+ endpoint: this.endpoint,
188
+ context: this.context,
189
+ });
190
+ this.#assertAlarmsSupported(request.mask, Status.InvalidCommand);
191
+ this.state.mask = request.mask;
192
+ this.state.state = this.#applyMaskToState(request.mask);
193
+ }
194
+ }
@@ -38,5 +38,7 @@ export declare class MatterbridgeRefrigeratorAndTemperatureControlledCabinetMode
38
38
  changeToMode(request: ModeBase.ChangeToModeRequest): MaybePromise<ModeBase.ChangeToModeResponse>;
39
39
  }
40
40
  export declare class MatterbridgeRefrigeratorAlarmServer extends RefrigeratorAlarmServer {
41
+ #private;
41
42
  static readonly schema: import("@matter/model").ClusterModel;
43
+ initialize(): MaybePromise;
42
44
  }
@@ -100,4 +100,12 @@ export class MatterbridgeRefrigeratorAndTemperatureControlledCabinetModeServer e
100
100
  const MatterbridgeRefrigeratorAlarmSchema = RefrigeratorAlarmServer.schema.extend({}, AttributeElement({ name: 'Mask', id: 0x0000, type: 'AlarmBitmap', access: 'R V', conformance: 'M' }), AttributeElement({ name: 'State', id: 0x0002, type: 'AlarmBitmap', access: 'R V', conformance: 'M' }), AttributeElement({ name: 'Supported', id: 0x0003, type: 'AlarmBitmap', access: 'R V', conformance: 'M', quality: 'F' }), EventElement({ name: 'Notify', id: 0x0000, access: 'V', conformance: 'M', priority: 'info' }, FieldElement({ name: 'Active', id: 0x0000, type: 'AlarmBitmap', conformance: 'M' }), FieldElement({ name: 'Inactive', id: 0x0001, type: 'AlarmBitmap', conformance: 'M' }), FieldElement({ name: 'State', id: 0x0002, type: 'AlarmBitmap', conformance: 'M' }), FieldElement({ name: 'Mask', id: 0x0003, type: 'AlarmBitmap', conformance: 'M' })));
101
101
  export class MatterbridgeRefrigeratorAlarmServer extends RefrigeratorAlarmServer {
102
102
  static schema = MatterbridgeRefrigeratorAlarmSchema;
103
+ initialize() {
104
+ this.reactTo(this.events.state$Changed, this.#emitNotify);
105
+ }
106
+ #emitNotify(state, oldState) {
107
+ const active = { doorOpen: Boolean(state.doorOpen && !oldState.doorOpen) };
108
+ const inactive = { doorOpen: Boolean(!state.doorOpen && oldState.doorOpen) };
109
+ this.events.notify.emit({ active, inactive, state, mask: this.state.mask }, this.context);
110
+ }
103
111
  }
package/dist/export.d.ts CHANGED
@@ -19,6 +19,8 @@ export * from './behaviors/smokeCoAlarmServer.js';
19
19
  export * from './behaviors/switchServer.js';
20
20
  export * from './behaviors/thermostatServer.js';
21
21
  export * from './behaviors/valveConfigurationAndControlServer.js';
22
+ export * from './behaviors/temperatureAlarmServer.js';
23
+ export * from './behaviors/waterTankLevelMonitoringServer.js';
22
24
  export * from './behaviors/windowCoveringServer.js';
23
25
  export { addVirtualDevice } from './helpers.js';
24
26
  export * from './matterbridgeAccessoryPlatform.js';
package/dist/export.js CHANGED
@@ -21,6 +21,8 @@ export * from './behaviors/smokeCoAlarmServer.js';
21
21
  export * from './behaviors/switchServer.js';
22
22
  export * from './behaviors/thermostatServer.js';
23
23
  export * from './behaviors/valveConfigurationAndControlServer.js';
24
+ export * from './behaviors/temperatureAlarmServer.js';
25
+ export * from './behaviors/waterTankLevelMonitoringServer.js';
24
26
  export * from './behaviors/windowCoveringServer.js';
25
27
  export { addVirtualDevice } from './helpers.js';
26
28
  export * from './matterbridgeAccessoryPlatform.js';
package/dist/frontend.js CHANGED
@@ -1200,6 +1200,10 @@ export class Frontend extends EventEmitter {
1200
1200
  attributes += `Hepa filter: ${attributeValue}% `;
1201
1201
  if (clusterName === 'activatedCarbonFilterMonitoring' && attributeName === 'condition')
1202
1202
  attributes += `Carbon filter: ${attributeValue}% `;
1203
+ if (clusterName === 'waterTankLevelMonitoring' && attributeName === 'condition')
1204
+ attributes += `Water tank: ${attributeValue}% `;
1205
+ if (clusterName === 'temperatureAlarm' && attributeName === 'state' && isValidObject(attributeValue))
1206
+ attributes += `Temp alarm: ${stringify(attributeValue)} `;
1203
1207
  if (clusterName === 'occupancySensing' && attributeName === 'occupancy' && isValidObject(attributeValue, 1))
1204
1208
  attributes += `Occupancy: ${attributeValue.occupied} `;
1205
1209
  if (clusterName === 'illuminanceMeasurement' && attributeName === 'measuredValue') {
@@ -104,6 +104,7 @@ import { ValveConfigurationAndControl } from '@matter/types/clusters/valve-confi
104
104
  import { WakeOnLan } from '@matter/types/clusters/wake-on-lan';
105
105
  import { WaterHeaterManagement } from '@matter/types/clusters/water-heater-management';
106
106
  import { WaterHeaterMode } from '@matter/types/clusters/water-heater-mode';
107
+ import { WaterTankLevelMonitoring } from '@matter/types/clusters/water-tank-level-monitoring';
107
108
  import { WebRtcTransportProvider } from '@matter/types/clusters/web-rtc-transport-provider';
108
109
  import { WebRtcTransportRequestor } from '@matter/types/clusters/web-rtc-transport-requestor';
109
110
  import { WindowCovering } from '@matter/types/clusters/window-covering';
@@ -1264,6 +1265,7 @@ export const supportedClusters = [
1264
1265
  SoilMeasurement,
1265
1266
  Switch,
1266
1267
  TargetNavigator,
1268
+ TemperatureAlarm,
1267
1269
  TemperatureControl,
1268
1270
  TemperatureMeasurement,
1269
1271
  Thermostat,
@@ -1276,6 +1278,7 @@ export const supportedClusters = [
1276
1278
  WakeOnLan,
1277
1279
  WaterHeaterManagement,
1278
1280
  WaterHeaterMode,
1281
+ WaterTankLevelMonitoring,
1279
1282
  WebRtcTransportProvider,
1280
1283
  WebRtcTransportRequestor,
1281
1284
  WindowCovering,
@@ -18,6 +18,7 @@ import { PowerTopology } from '@matter/types/clusters/power-topology';
18
18
  import { PumpConfigurationAndControl } from '@matter/types/clusters/pump-configuration-and-control';
19
19
  import { ResourceMonitoring } from '@matter/types/clusters/resource-monitoring';
20
20
  import { SmokeCoAlarm } from '@matter/types/clusters/smoke-co-alarm';
21
+ import type { TemperatureAlarm } from '@matter/types/clusters/temperature-alarm';
21
22
  import { Thermostat } from '@matter/types/clusters/thermostat';
22
23
  import { ThermostatUserInterfaceConfiguration } from '@matter/types/clusters/thermostat-user-interface-configuration';
23
24
  import { ValveConfigurationAndControl } from '@matter/types/clusters/valve-configuration-and-control';
@@ -119,9 +120,9 @@ export declare class MatterbridgeEndpoint extends Endpoint {
119
120
  addCommandHandler<C extends CommandHandlers>(command: C, handler: CommandHandlerFunction<C>): this;
120
121
  removeCommandHandler<C extends CommandHandlers>(command: C, handler: CommandHandlerFunction<C>): this;
121
122
  executeCommandHandler<C extends CommandHandlers>(command: C, request: CommandHandlerData<C>['request'], cluster: CommandHandlerData<C>['cluster'], attributes: CommandHandlerData<C>['attributes'], endpoint: CommandHandlerData<C>['endpoint']): Promise<CommandHandlerExecutionResult<C>>;
122
- invokeBehaviorCommand<T extends Behavior.Type, C extends BehaviorCommandName<T>>(cluster: T, command: C, params?: BehaviorCommandParams<T, C>): Promise<void>;
123
- invokeBehaviorCommand<T extends ClusterType, C extends ClusterCommandName<T>>(cluster: T, command: C, params?: ClusterCommandParams<T, C>): Promise<void>;
124
- invokeBehaviorCommand(cluster: ClusterId | string, command: CommandHandlers, params?: Record<string, boolean | number | bigint | string | object | null>): Promise<void>;
123
+ invokeBehaviorCommand<T extends Behavior.Type, C extends BehaviorCommandName<T>>(cluster: T, command: C, params?: BehaviorCommandParams<T, C>, waiterCondition?: () => boolean): Promise<void>;
124
+ invokeBehaviorCommand<T extends ClusterType, C extends ClusterCommandName<T>>(cluster: T, command: C, params?: ClusterCommandParams<T, C>, waiterCondition?: () => boolean): Promise<void>;
125
+ invokeBehaviorCommand(cluster: ClusterId | string, command: CommandHandlers, params?: Record<string, boolean | number | bigint | string | object | null>, waiterCondition?: () => boolean): Promise<void>;
125
126
  addRequiredClusters(): MatterbridgeEndpoint;
126
127
  addRequiredClusterServers(): MatterbridgeEndpoint;
127
128
  addOptionalClusterServers(): MatterbridgeEndpoint;
@@ -207,8 +208,10 @@ export declare class MatterbridgeEndpoint extends Endpoint {
207
208
  }, airflowDirection?: FanControl.AirflowDirection): this;
208
209
  createDefaultHepaFilterMonitoringClusterServer(condition?: number, changeIndication?: ResourceMonitoring.ChangeIndication, inPlaceIndicator?: boolean | undefined, lastChangedTime?: number | null | undefined, replacementProductList?: ResourceMonitoring.ReplacementProduct[]): this;
209
210
  createDefaultActivatedCarbonFilterMonitoringClusterServer(condition?: number, changeIndication?: ResourceMonitoring.ChangeIndication, inPlaceIndicator?: boolean | undefined, lastChangedTime?: number | null | undefined, replacementProductList?: ResourceMonitoring.ReplacementProduct[]): this;
211
+ createDefaultTemperatureAlarmClusterServer(criticalOverTemperatureThreshold?: number, criticalUnderTemperatureThreshold?: number, supported?: TemperatureAlarm.Alarm, mask?: TemperatureAlarm.Alarm, latch?: TemperatureAlarm.Alarm, state?: TemperatureAlarm.Alarm): this;
212
+ createDefaultWaterTankLevelMonitoringClusterServer(condition?: number, changeIndication?: ResourceMonitoring.ChangeIndication, inPlaceIndicator?: boolean | undefined, lastChangedTime?: number | null | undefined, replacementProductList?: ResourceMonitoring.ReplacementProduct[]): this;
210
213
  createDefaultDoorLockClusterServer(lockState?: DoorLock.LockState, lockType?: DoorLock.LockType, autoRelockTime?: number, numberOfWeekDaySchedulesSupportedPerUser?: number, numberOfYearDaySchedulesSupportedPerUser?: number, numberOfHolidaySchedulesSupported?: number): this;
211
- createUserPinDoorLockClusterServer(lockState?: DoorLock.LockState, lockType?: DoorLock.LockType, autoRelockTime?: number, minPinCodeLength?: number, maxPinCodeLength?: number, numberOfWeekDaySchedulesSupportedPerUser?: number, numberOfYearDaySchedulesSupportedPerUser?: number, numberOfHolidaySchedulesSupported?: number): this;
214
+ createUserPinDoorLockClusterServer(lockState?: DoorLock.LockState, lockType?: DoorLock.LockType, autoRelockTime?: number, minPinCodeLength?: number, maxPinCodeLength?: number, numberOfWeekDaySchedulesSupportedPerUser?: number, numberOfYearDaySchedulesSupportedPerUser?: number, numberOfHolidaySchedulesSupported?: number, expiringUserTimeout?: number, numberOfRfidUsersSupported?: number, minRfidCodeLength?: number, maxRfidCodeLength?: number): this;
212
215
  createDefaultModeSelectClusterServer(description: string, supportedModes: ModeSelect.ModeOption[], currentMode?: number, startUpMode?: number): this;
213
216
  createDefaultValveConfigurationAndControlClusterServer(valveState?: ValveConfigurationAndControl.ValveState, valveLevel?: number, movementDuration?: number, autoClose?: boolean): this;
214
217
  createDefaultPumpConfigurationAndControlClusterServer(pumpMode?: PumpConfigurationAndControl.OperationMode, maxPressure?: number | null, maxSpeed?: number | null, maxFlow?: number | null, minConstSpeed?: number | null, maxConstSpeed?: number | null, speed?: number | null): this;
@@ -79,8 +79,10 @@ import { MatterbridgePowerSourceServer } from './behaviors/powerSourceServer.js'
79
79
  import { MatterbridgePumpConfigurationAndControlServer } from './behaviors/pumpConfigurationAndControlServer.js';
80
80
  import { MatterbridgeSmokeCoAlarmServer } from './behaviors/smokeCoAlarmServer.js';
81
81
  import { MatterbridgeSwitchServer } from './behaviors/switchServer.js';
82
+ import { MatterbridgeTemperatureAlarmServer } from './behaviors/temperatureAlarmServer.js';
82
83
  import { MatterbridgeThermostatServer } from './behaviors/thermostatServer.js';
83
84
  import { MatterbridgeValveConfigurationAndControlServer } from './behaviors/valveConfigurationAndControlServer.js';
85
+ import { MatterbridgeWaterTankLevelMonitoringServer } from './behaviors/waterTankLevelMonitoringServer.js';
84
86
  import { MatterbridgeWindowCoveringServer } from './behaviors/windowCoveringServer.js';
85
87
  import { CommandHandler, } from './matterbridgeEndpointCommandHandler.js';
86
88
  import { addClusterClients, addClusterServers, addFixedLabel, addOptionalClusterClients, addOptionalClusterServers, addRequiredClusterClients, addRequiredClusterServers, addUserLabel, checkNotLatinCharacters, createUniqueId, defaultFor, featuresFor, generateUniqueId, getApparentElectricalPowerMeasurementClusterServer, getAttribute, getAttributeId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getCluster, getClusterId, getDefaultDeviceEnergyManagementClusterServer, getDefaultDeviceEnergyManagementModeClusterServer, getDefaultElectricalEnergyMeasurementClusterServer, getDefaultElectricalPowerMeasurementClusterServer, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultOccupancySensingClusterServer, getDefaultOperationalStateClusterServer, getDefaultPowerSourceBatteryClusterServer, getDefaultPowerSourceRechargeableBatteryClusterServer, getDefaultPowerSourceReplaceableBatteryClusterServer, getDefaultPowerSourceWiredClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultSoilMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, getExportedElectricalEnergyMeasurementClusterServer, getImportedElectricalEnergyMeasurementClusterServer, invokeBehaviorCommand, lowercaseFirstLetter, setAttribute, setCluster, subscribeAttribute, triggerEvent, updateAttribute, } from './matterbridgeEndpointHelpers.js';
@@ -277,8 +279,8 @@ export class MatterbridgeEndpoint extends Endpoint {
277
279
  async executeCommandHandler(command, request, cluster, attributes, endpoint) {
278
280
  return this.commandHandler.executeHandler(command, { command, request, cluster, attributes, endpoint });
279
281
  }
280
- async invokeBehaviorCommand(cluster, command, params) {
281
- await invokeBehaviorCommand(this, cluster, command, params);
282
+ async invokeBehaviorCommand(cluster, command, params, waiterCondition) {
283
+ await invokeBehaviorCommand(this, cluster, command, params, waiterCondition);
282
284
  }
283
285
  addRequiredClusters() {
284
286
  addRequiredClusterServers(this);
@@ -831,14 +833,13 @@ export class MatterbridgeEndpoint extends Endpoint {
831
833
  numberOfActuationsLift: 0,
832
834
  configStatus: {
833
835
  operational: true,
834
- onlineReserved: false,
835
836
  liftMovementReversed: false,
836
837
  liftPositionAware: true,
837
838
  tiltPositionAware: false,
838
839
  liftEncoderControlled: false,
839
840
  tiltEncoderControlled: false,
840
841
  },
841
- operationalStatus: { global: WindowCovering.MovementStatus.Stopped, lift: WindowCovering.MovementStatus.Stopped, tilt: WindowCovering.MovementStatus.Stopped },
842
+ operationalStatus: { global: WindowCovering.MovementStatus.Stopped, lift: WindowCovering.MovementStatus.Stopped },
842
843
  endProductType,
843
844
  mode: { motorDirectionReversed: false, calibrationMode: false, maintenanceMode: false, ledFeedback: false },
844
845
  targetPositionLiftPercent100ths: positionPercent100ths,
@@ -855,7 +856,6 @@ export class MatterbridgeEndpoint extends Endpoint {
855
856
  numberOfActuationsTilt: 0,
856
857
  configStatus: {
857
858
  operational: true,
858
- onlineReserved: false,
859
859
  liftMovementReversed: false,
860
860
  liftPositionAware: true,
861
861
  tiltPositionAware: true,
@@ -881,14 +881,13 @@ export class MatterbridgeEndpoint extends Endpoint {
881
881
  numberOfActuationsTilt: 0,
882
882
  configStatus: {
883
883
  operational: true,
884
- onlineReserved: false,
885
884
  liftMovementReversed: false,
886
885
  liftPositionAware: false,
887
886
  tiltPositionAware: true,
888
887
  liftEncoderControlled: false,
889
888
  tiltEncoderControlled: false,
890
889
  },
891
- operationalStatus: { global: WindowCovering.MovementStatus.Stopped, lift: WindowCovering.MovementStatus.Stopped, tilt: WindowCovering.MovementStatus.Stopped },
890
+ operationalStatus: { global: WindowCovering.MovementStatus.Stopped, tilt: WindowCovering.MovementStatus.Stopped },
892
891
  endProductType,
893
892
  mode: { motorDirectionReversed: false, calibrationMode: false, maintenanceMode: false, ledFeedback: false },
894
893
  targetPositionTiltPercent100ths: positionTiltPercent100ths,
@@ -899,39 +898,44 @@ export class MatterbridgeEndpoint extends Endpoint {
899
898
  return this;
900
899
  }
901
900
  async setWindowCoveringTargetAsCurrentAndStopped() {
902
- const position = this.getAttribute(WindowCovering, 'currentPositionLiftPercent100ths', this.log);
903
- if (isValidNumber(position, 0, 10000)) {
904
- await this.setAttribute(WindowCovering, 'targetPositionLiftPercent100ths', position, this.log);
905
- await this.setAttribute(WindowCovering, 'operationalStatus', {
906
- global: WindowCovering.MovementStatus.Stopped,
907
- lift: WindowCovering.MovementStatus.Stopped,
908
- tilt: WindowCovering.MovementStatus.Stopped,
909
- }, this.log);
901
+ const { lift, tilt } = featuresFor(this, WindowCovering);
902
+ if (lift) {
903
+ const position = this.getAttribute(WindowCovering, 'currentPositionLiftPercent100ths', this.log);
904
+ if (isValidNumber(position, 0, 10000)) {
905
+ await this.setAttribute(WindowCovering, 'targetPositionLiftPercent100ths', position, this.log);
906
+ }
907
+ this.log.debug(`Set WindowCovering currentPositionLiftPercent100ths and targetPositionLiftPercent100ths to ${position} and operationalStatus to Stopped.`);
910
908
  }
911
- this.log.debug(`Set WindowCovering currentPositionLiftPercent100ths and targetPositionLiftPercent100ths to ${position} and operationalStatus to Stopped.`);
912
- if (this.hasAttributeServer(WindowCovering, 'currentPositionTiltPercent100ths')) {
909
+ if (tilt) {
913
910
  const position = this.getAttribute(WindowCovering, 'currentPositionTiltPercent100ths', this.log);
914
911
  if (isValidNumber(position, 0, 10000)) {
915
912
  await this.setAttribute(WindowCovering, 'targetPositionTiltPercent100ths', position, this.log);
916
913
  }
917
914
  this.log.debug(`Set WindowCovering currentPositionTiltPercent100ths and targetPositionTiltPercent100ths to ${position} and operationalStatus to Stopped.`);
918
915
  }
916
+ await this.setAttribute(WindowCovering, 'operationalStatus', {
917
+ global: WindowCovering.MovementStatus.Stopped,
918
+ ...(lift ? { lift: WindowCovering.MovementStatus.Stopped } : {}),
919
+ ...(tilt ? { tilt: WindowCovering.MovementStatus.Stopped } : {}),
920
+ }, this.log);
919
921
  }
920
922
  async setWindowCoveringCurrentTargetStatus(current, target, status) {
921
923
  await this.setAttribute(WindowCovering, 'currentPositionLiftPercent100ths', current, this.log);
922
924
  await this.setAttribute(WindowCovering, 'targetPositionLiftPercent100ths', target, this.log);
925
+ const { lift, tilt } = featuresFor(this, WindowCovering);
923
926
  await this.setAttribute(WindowCovering, 'operationalStatus', {
924
927
  global: status,
925
- lift: status,
926
- tilt: status,
928
+ ...(lift ? { lift: status } : {}),
929
+ ...(tilt ? { tilt: status } : {}),
927
930
  }, this.log);
928
931
  this.log.debug(`Set WindowCovering currentPositionLiftPercent100ths: ${current}, targetPositionLiftPercent100ths: ${target} and operationalStatus: ${status}.`);
929
932
  }
930
933
  async setWindowCoveringStatus(status) {
934
+ const { lift, tilt } = featuresFor(this, WindowCovering);
931
935
  await this.setAttribute(WindowCovering, 'operationalStatus', {
932
936
  global: status,
933
- lift: status,
934
- tilt: status,
937
+ ...(lift ? { lift: status } : {}),
938
+ ...(tilt ? { tilt: status } : {}),
935
939
  }, this.log);
936
940
  this.log.debug(`Set WindowCovering operationalStatus: ${status}`);
937
941
  }
@@ -947,7 +951,7 @@ export class MatterbridgeEndpoint extends Endpoint {
947
951
  await this.setAttribute(WindowCovering, 'currentPositionLiftPercent100ths', liftPosition, this.log);
948
952
  await this.setAttribute(WindowCovering, 'targetPositionLiftPercent100ths', liftPosition, this.log);
949
953
  this.log.debug(`Set WindowCovering currentPositionLiftPercent100ths: ${liftPosition} and targetPositionLiftPercent100ths: ${liftPosition}.`);
950
- if (tiltPosition && this.hasAttributeServer(WindowCovering, 'currentPositionTiltPercent100ths')) {
954
+ if (tiltPosition && featuresFor(this, WindowCovering).tilt) {
951
955
  await this.setAttribute(WindowCovering, 'currentPositionTiltPercent100ths', tiltPosition, this.log);
952
956
  await this.setAttribute(WindowCovering, 'targetPositionTiltPercent100ths', tiltPosition, this.log);
953
957
  this.log.debug(`Set WindowCovering currentPositionTiltPercent100ths: ${tiltPosition} and targetPositionTiltPercent100ths: ${tiltPosition}.`);
@@ -1282,6 +1286,36 @@ export class MatterbridgeEndpoint extends Endpoint {
1282
1286
  });
1283
1287
  return this;
1284
1288
  }
1289
+ createDefaultTemperatureAlarmClusterServer(criticalOverTemperatureThreshold = 60 * 100, criticalUnderTemperatureThreshold = -10 * 100, supported = { criticalOverTemperatureAlarm: true, criticalUnderTemperatureAlarm: true }, mask = supported, latch = {}, state = {}) {
1290
+ const bitmap = (alarm) => ({
1291
+ criticalOverTemperatureAlarm: Boolean(alarm.criticalOverTemperatureAlarm),
1292
+ majorOverTemperatureAlarm: false,
1293
+ minorOverTemperatureAlarm: false,
1294
+ minorUnderTemperatureAlarm: false,
1295
+ majorUnderTemperatureAlarm: false,
1296
+ criticalUnderTemperatureAlarm: Boolean(alarm.criticalUnderTemperatureAlarm),
1297
+ });
1298
+ this.behaviors.require(MatterbridgeTemperatureAlarmServer, {
1299
+ criticalOverTemperatureThreshold,
1300
+ criticalUnderTemperatureThreshold,
1301
+ latch: bitmap(latch),
1302
+ supported: bitmap(supported),
1303
+ mask: bitmap(mask),
1304
+ state: bitmap(state),
1305
+ });
1306
+ return this;
1307
+ }
1308
+ createDefaultWaterTankLevelMonitoringClusterServer(condition = 100, changeIndication = ResourceMonitoring.ChangeIndication.Ok, inPlaceIndicator = true, lastChangedTime = null, replacementProductList = []) {
1309
+ this.behaviors.require(MatterbridgeWaterTankLevelMonitoringServer.with(ResourceMonitoring.Feature.Condition, ResourceMonitoring.Feature.Warning, ResourceMonitoring.Feature.ReplacementProductList), {
1310
+ condition,
1311
+ degradationDirection: ResourceMonitoring.DegradationDirection.Down,
1312
+ replacementProductList,
1313
+ changeIndication,
1314
+ inPlaceIndicator,
1315
+ lastChangedTime,
1316
+ });
1317
+ return this;
1318
+ }
1285
1319
  createDefaultDoorLockClusterServer(lockState = DoorLock.LockState.Locked, lockType = DoorLock.LockType.DeadBolt, autoRelockTime = 0, numberOfWeekDaySchedulesSupportedPerUser, numberOfYearDaySchedulesSupportedPerUser, numberOfHolidaySchedulesSupported) {
1286
1320
  this.behaviors.require(MatterbridgeDoorLockServer.with(...(numberOfWeekDaySchedulesSupportedPerUser !== undefined ? [DoorLock.Feature.WeekDayAccessSchedules] : []), ...(numberOfYearDaySchedulesSupportedPerUser !== undefined ? [DoorLock.Feature.YearDayAccessSchedules] : []), ...(numberOfHolidaySchedulesSupported !== undefined ? [DoorLock.Feature.HolidaySchedules] : [])).enable({
1287
1321
  events: { doorLockAlarm: true, lockOperation: true, lockOperationError: true },
@@ -1299,8 +1333,8 @@ export class MatterbridgeEndpoint extends Endpoint {
1299
1333
  });
1300
1334
  return this;
1301
1335
  }
1302
- createUserPinDoorLockClusterServer(lockState = DoorLock.LockState.Locked, lockType = DoorLock.LockType.DeadBolt, autoRelockTime = 0, minPinCodeLength = 4, maxPinCodeLength = 10, numberOfWeekDaySchedulesSupportedPerUser, numberOfYearDaySchedulesSupportedPerUser, numberOfHolidaySchedulesSupported) {
1303
- this.behaviors.require(MatterbridgeDoorLockServer.with(DoorLock.Feature.User, DoorLock.Feature.PinCredential, ...(numberOfWeekDaySchedulesSupportedPerUser !== undefined ? [DoorLock.Feature.WeekDayAccessSchedules] : []), ...(numberOfYearDaySchedulesSupportedPerUser !== undefined ? [DoorLock.Feature.YearDayAccessSchedules] : []), ...(numberOfHolidaySchedulesSupported !== undefined ? [DoorLock.Feature.HolidaySchedules] : [])).enable({
1336
+ createUserPinDoorLockClusterServer(lockState = DoorLock.LockState.Locked, lockType = DoorLock.LockType.DeadBolt, autoRelockTime = 0, minPinCodeLength = 4, maxPinCodeLength = 10, numberOfWeekDaySchedulesSupportedPerUser, numberOfYearDaySchedulesSupportedPerUser, numberOfHolidaySchedulesSupported, expiringUserTimeout, numberOfRfidUsersSupported, minRfidCodeLength = 8, maxRfidCodeLength = 20) {
1337
+ this.behaviors.require(MatterbridgeDoorLockServer.with(DoorLock.Feature.User, DoorLock.Feature.PinCredential, ...(numberOfWeekDaySchedulesSupportedPerUser !== undefined ? [DoorLock.Feature.WeekDayAccessSchedules] : []), ...(numberOfYearDaySchedulesSupportedPerUser !== undefined ? [DoorLock.Feature.YearDayAccessSchedules] : []), ...(numberOfHolidaySchedulesSupported !== undefined ? [DoorLock.Feature.HolidaySchedules] : []), ...(numberOfRfidUsersSupported !== undefined ? [DoorLock.Feature.RfidCredential] : [])).enable({
1304
1338
  events: { doorLockAlarm: true, lockOperation: true, lockOperationError: true },
1305
1339
  commands: { lockDoor: true, unlockDoor: true, unlockWithTimeout: true },
1306
1340
  }), {
@@ -1318,9 +1352,11 @@ export class MatterbridgeEndpoint extends Endpoint {
1318
1352
  numberOfTotalUsersSupported: 10,
1319
1353
  credentialRulesSupport: { single: true },
1320
1354
  numberOfCredentialsSupportedPerUser: 10,
1355
+ ...(expiringUserTimeout !== undefined ? { expiringUserTimeout } : {}),
1321
1356
  ...(numberOfWeekDaySchedulesSupportedPerUser !== undefined ? { numberOfWeekDaySchedulesSupportedPerUser } : {}),
1322
1357
  ...(numberOfYearDaySchedulesSupportedPerUser !== undefined ? { numberOfYearDaySchedulesSupportedPerUser } : {}),
1323
1358
  ...(numberOfHolidaySchedulesSupported !== undefined ? { numberOfHolidaySchedulesSupported } : {}),
1359
+ ...(numberOfRfidUsersSupported !== undefined ? { numberOfRfidUsersSupported, minRfidCodeLength, maxRfidCodeLength } : {}),
1324
1360
  });
1325
1361
  return this;
1326
1362
  }
@@ -8,6 +8,7 @@ import type { CommodityPrice } from '@matter/types/clusters/commodity-price';
8
8
  import type { CommodityTariff } from '@matter/types/clusters/commodity-tariff';
9
9
  import type { DeviceEnergyManagement } from '@matter/types/clusters/device-energy-management';
10
10
  import type { DeviceEnergyManagementMode } from '@matter/types/clusters/device-energy-management-mode';
11
+ import type { DishwasherAlarm } from '@matter/types/clusters/dishwasher-alarm';
11
12
  import type { DishwasherMode } from '@matter/types/clusters/dishwasher-mode';
12
13
  import type { DoorLock } from '@matter/types/clusters/door-lock';
13
14
  import type { EnergyEvse } from '@matter/types/clusters/energy-evse';
@@ -30,6 +31,7 @@ import type { RvcOperationalState } from '@matter/types/clusters/rvc-operational
30
31
  import type { RvcRunMode } from '@matter/types/clusters/rvc-run-mode';
31
32
  import type { ServiceArea } from '@matter/types/clusters/service-area';
32
33
  import type { SmokeCoAlarm } from '@matter/types/clusters/smoke-co-alarm';
34
+ import type { TemperatureAlarm } from '@matter/types/clusters/temperature-alarm';
33
35
  import type { TemperatureControl } from '@matter/types/clusters/temperature-control';
34
36
  import type { Thermostat } from '@matter/types/clusters/thermostat';
35
37
  import type { ThreadNetworkDiagnostics } from '@matter/types/clusters/thread-network-diagnostics';
@@ -37,6 +39,7 @@ import type { TimeSynchronization } from '@matter/types/clusters/time-synchroniz
37
39
  import type { ValveConfigurationAndControl } from '@matter/types/clusters/valve-configuration-and-control';
38
40
  import type { WaterHeaterManagement } from '@matter/types/clusters/water-heater-management';
39
41
  import type { WaterHeaterMode } from '@matter/types/clusters/water-heater-mode';
42
+ import type { WaterTankLevelMonitoring } from '@matter/types/clusters/water-tank-level-monitoring';
40
43
  import type { WindowCovering } from '@matter/types/clusters/window-covering';
41
44
  import type { ClosureControl } from './clusters/closure-control.js';
42
45
  import type { ClosureDimension } from './clusters/closure-dimension.js';
@@ -673,6 +676,34 @@ export type CommandHandlerDataMap = {
673
676
  attributes: ClusterAttributeValues<(typeof SmokeCoAlarm)['attributes']>;
674
677
  endpoint: MatterbridgeEndpoint;
675
678
  };
679
+ 'DishwasherAlarm.reset': {
680
+ command: 'reset';
681
+ request: DishwasherAlarm.ResetRequest;
682
+ cluster: 'dishwasherAlarm';
683
+ attributes: ClusterAttributeValues<(typeof DishwasherAlarm)['attributes']>;
684
+ endpoint: MatterbridgeEndpoint;
685
+ };
686
+ 'DishwasherAlarm.modifyEnabledAlarms': {
687
+ command: 'modifyEnabledAlarms';
688
+ request: DishwasherAlarm.ModifyEnabledAlarmsRequest;
689
+ cluster: 'dishwasherAlarm';
690
+ attributes: ClusterAttributeValues<(typeof DishwasherAlarm)['attributes']>;
691
+ endpoint: MatterbridgeEndpoint;
692
+ };
693
+ 'TemperatureAlarm.reset': {
694
+ command: 'reset';
695
+ request: TemperatureAlarm.ResetRequest;
696
+ cluster: 'temperatureAlarm';
697
+ attributes: ClusterAttributeValues<(typeof TemperatureAlarm)['attributes']>;
698
+ endpoint: MatterbridgeEndpoint;
699
+ };
700
+ 'TemperatureAlarm.modifyEnabledAlarms': {
701
+ command: 'modifyEnabledAlarms';
702
+ request: TemperatureAlarm.ModifyEnabledAlarmsRequest;
703
+ cluster: 'temperatureAlarm';
704
+ attributes: ClusterAttributeValues<(typeof TemperatureAlarm)['attributes']>;
705
+ endpoint: MatterbridgeEndpoint;
706
+ };
676
707
  'resetCounts': CommandHandlerData<'ThreadNetworkDiagnostics.resetCounts'>;
677
708
  'ThreadNetworkDiagnostics.resetCounts': {
678
709
  command: 'resetCounts';
@@ -991,6 +1022,13 @@ export type CommandHandlerDataMap = {
991
1022
  attributes: ClusterAttributeValues<(typeof ActivatedCarbonFilterMonitoring)['attributes']>;
992
1023
  endpoint: MatterbridgeEndpoint;
993
1024
  };
1025
+ 'WaterTankLevelMonitoring.resetCondition': {
1026
+ command: 'resetCondition';
1027
+ request: {};
1028
+ cluster: 'waterTankLevelMonitoring';
1029
+ attributes: ClusterAttributeValues<(typeof WaterTankLevelMonitoring)['attributes']>;
1030
+ endpoint: MatterbridgeEndpoint;
1031
+ };
994
1032
  'CommodityPrice.getDetailedPriceRequest': {
995
1033
  command: 'getDetailedPriceRequest';
996
1034
  request: CommodityPrice.GetDetailedPriceRequest;
@@ -41,7 +41,7 @@ export declare function getBehaviourTypesFromClusterClientIds(clusterClientList:
41
41
  export declare function getBehaviourTypeFromClusterServerId(clusterId: ClusterId): Behavior.Type | undefined;
42
42
  export declare function getBehaviourTypeFromClusterClientId(clusterId: ClusterId): ClusterBehavior.Type | undefined;
43
43
  export declare function getBehavior(endpoint: MatterbridgeEndpoint, cluster: Behavior.Type | ClusterType | ClusterId | string): Behavior.Type | undefined;
44
- export declare function invokeBehaviorCommand(endpoint: MatterbridgeEndpoint, cluster: Behavior.Type | ClusterType | ClusterId | string, command: CommandHandlers, params?: Record<string, boolean | number | bigint | string | object | null>): Promise<boolean>;
44
+ export declare function invokeBehaviorCommand(endpoint: MatterbridgeEndpoint, cluster: Behavior.Type | ClusterType | ClusterId | string, command: CommandHandlers, params?: Record<string, boolean | number | bigint | string | object | null>, waiterCondition?: () => boolean): Promise<boolean>;
45
45
  export declare function invokeSubscribeHandler(endpoint: MatterbridgeEndpoint, cluster: Behavior.Type | ClusterType | ClusterId | string, attribute: string, newValue: unknown, oldValue: unknown): Promise<boolean>;
46
46
  export declare function addRequiredClusterServers(endpoint: MatterbridgeEndpoint): void;
47
47
  export declare function addOptionalClusterServers(endpoint: MatterbridgeEndpoint): void;
@@ -48,6 +48,7 @@ import { ThermostatClient } from '@matter/node/behaviors/thermostat';
48
48
  import { ThermostatUserInterfaceConfigurationServer } from '@matter/node/behaviors/thermostat-user-interface-configuration';
49
49
  import { TotalVolatileOrganicCompoundsConcentrationMeasurementServer } from '@matter/node/behaviors/total-volatile-organic-compounds-concentration-measurement';
50
50
  import { UserLabelServer } from '@matter/node/behaviors/user-label';
51
+ import { WaterTankLevelMonitoringServer } from '@matter/node/behaviors/water-tank-level-monitoring';
51
52
  import { WindowCoveringClient } from '@matter/node/behaviors/window-covering';
52
53
  import { getClusterNameById } from '@matter/types/cluster';
53
54
  import { ActivatedCarbonFilterMonitoring } from '@matter/types/clusters/activated-carbon-filter-monitoring';
@@ -98,18 +99,21 @@ import { ScenesManagement } from '@matter/types/clusters/scenes-management';
98
99
  import { SmokeCoAlarm } from '@matter/types/clusters/smoke-co-alarm';
99
100
  import { SoilMeasurement } from '@matter/types/clusters/soil-measurement';
100
101
  import { Switch } from '@matter/types/clusters/switch';
102
+ import { TemperatureAlarm } from '@matter/types/clusters/temperature-alarm';
101
103
  import { TemperatureMeasurement } from '@matter/types/clusters/temperature-measurement';
102
104
  import { Thermostat } from '@matter/types/clusters/thermostat';
103
105
  import { ThermostatUserInterfaceConfiguration } from '@matter/types/clusters/thermostat-user-interface-configuration';
104
106
  import { TotalVolatileOrganicCompoundsConcentrationMeasurement } from '@matter/types/clusters/total-volatile-organic-compounds-concentration-measurement';
105
107
  import { UserLabel } from '@matter/types/clusters/user-label';
106
108
  import { ValveConfigurationAndControl } from '@matter/types/clusters/valve-configuration-and-control';
109
+ import { WaterTankLevelMonitoring } from '@matter/types/clusters/water-tank-level-monitoring';
107
110
  import { WindowCovering } from '@matter/types/clusters/window-covering';
108
111
  import { NodeId } from '@matter/types/datatype';
109
112
  import { MeasurementType } from '@matter/types/globals';
110
113
  import { deepEqual } from '@matterbridge/utils/deep-equal';
111
114
  import { logModuleLoaded } from '@matterbridge/utils/loader';
112
115
  import { isValidArray } from '@matterbridge/utils/validate';
116
+ import { waiter } from '@matterbridge/utils/wait';
113
117
  import { BLUE, CYAN, db, debugStringify, er, hk, nf, or, wr, YELLOW, zb } from 'node-ansi-logger';
114
118
  import { MatterbridgeBindingServer } from './behaviors/bindingServer.js';
115
119
  import { MatterbridgeBooleanStateConfigurationServer } from './behaviors/booleanStateConfigurationServer.js';
@@ -127,6 +131,7 @@ import { MatterbridgeOperationalStateServer } from './behaviors/operationalState
127
131
  import { MatterbridgePowerSourceServer } from './behaviors/powerSourceServer.js';
128
132
  import { MatterbridgePumpConfigurationAndControlServer } from './behaviors/pumpConfigurationAndControlServer.js';
129
133
  import { MatterbridgeSmokeCoAlarmServer } from './behaviors/smokeCoAlarmServer.js';
134
+ import { MatterbridgeTemperatureAlarmServer } from './behaviors/temperatureAlarmServer.js';
130
135
  import { MatterbridgeThermostatServer } from './behaviors/thermostatServer.js';
131
136
  import { MatterbridgeValveConfigurationAndControlServer } from './behaviors/valveConfigurationAndControlServer.js';
132
137
  import { MatterbridgeWindowCoveringServer } from './behaviors/windowCoveringServer.js';
@@ -305,6 +310,8 @@ export function getBehaviourTypeFromClusterServerId(clusterId) {
305
310
  return BooleanStateServer.enable({ events: { stateChange: true } });
306
311
  if (clusterId === BooleanStateConfiguration.id)
307
312
  return MatterbridgeBooleanStateConfigurationServer;
313
+ if (clusterId === TemperatureAlarm.id)
314
+ return MatterbridgeTemperatureAlarmServer;
308
315
  if (clusterId === PowerTopology.id)
309
316
  return PowerTopologyServer.with('TreeTopology');
310
317
  if (clusterId === ElectricalPowerMeasurement.id)
@@ -331,6 +338,8 @@ export function getBehaviourTypeFromClusterServerId(clusterId) {
331
338
  return HepaFilterMonitoringServer.with('Condition', 'Warning', 'ReplacementProductList');
332
339
  if (clusterId === ActivatedCarbonFilterMonitoring.id)
333
340
  return ActivatedCarbonFilterMonitoringServer.with('Condition', 'Warning', 'ReplacementProductList');
341
+ if (clusterId === WaterTankLevelMonitoring.id)
342
+ return WaterTankLevelMonitoringServer.with('Condition', 'Warning', 'ReplacementProductList');
334
343
  if (clusterId === CarbonMonoxideConcentrationMeasurement.id)
335
344
  return CarbonMonoxideConcentrationMeasurementServer.with('NumericMeasurement');
336
345
  if (clusterId === CarbonDioxideConcentrationMeasurement.id)
@@ -422,7 +431,7 @@ export function getBehavior(endpoint, cluster) {
422
431
  }
423
432
  return behavior;
424
433
  }
425
- export async function invokeBehaviorCommand(endpoint, cluster, command, params) {
434
+ export async function invokeBehaviorCommand(endpoint, cluster, command, params, waiterCondition) {
426
435
  const behaviorId = getBehavior(endpoint, cluster)?.id;
427
436
  if (!behaviorId) {
428
437
  endpoint.log?.error(`invokeBehaviorCommand error: command ${hk}${command}${er} not found on endpoint ${or}${endpoint.maybeId}${er}:${or}${endpoint.maybeNumber}${er}`);
@@ -439,6 +448,11 @@ export async function invokeBehaviorCommand(endpoint, cluster, command, params)
439
448
  return;
440
449
  }
441
450
  void behavior?.['state'];
451
+ if (endpoint.behaviors.supported[behaviorId]?.lockOnInvoke) {
452
+ const transaction = agent.context.transaction;
453
+ await transaction.addResources(behavior);
454
+ await transaction.begin();
455
+ }
442
456
  const injectedSubject = { kind: 'node', id: NodeId(100) };
443
457
  const patchedContext = new Proxy(agent.context, { get: (t, k) => (k === 'fabric' ? 1 : k === 'subject' ? injectedSubject : Reflect.get(t, k, t)) });
444
458
  Object.defineProperty(behavior, 'context', { configurable: true, value: patchedContext });
@@ -450,6 +464,8 @@ export async function invokeBehaviorCommand(endpoint, cluster, command, params)
450
464
  delete behavior.context;
451
465
  }
452
466
  });
467
+ if (invoked && waiterCondition)
468
+ return await waiter(`invokeBehaviorCommand ${command}`, waiterCondition, false, 1000, 50);
453
469
  return invoked;
454
470
  }
455
471
  export async function invokeSubscribeHandler(endpoint, cluster, attribute, newValue, oldValue) {
@@ -538,6 +554,8 @@ export function addClusterServers(endpoint, serverList) {
538
554
  endpoint.createDefaultBooleanStateClusterServer();
539
555
  if (serverList.includes(BooleanStateConfiguration.id))
540
556
  endpoint.createDefaultBooleanStateConfigurationClusterServer();
557
+ if (serverList.includes(TemperatureAlarm.id))
558
+ endpoint.createDefaultTemperatureAlarmClusterServer();
541
559
  if (serverList.includes(PowerTopology.id))
542
560
  endpoint.createDefaultPowerTopologyClusterServer();
543
561
  if (serverList.includes(ElectricalPowerMeasurement.id))
@@ -564,6 +582,8 @@ export function addClusterServers(endpoint, serverList) {
564
582
  endpoint.createDefaultHepaFilterMonitoringClusterServer();
565
583
  if (serverList.includes(ActivatedCarbonFilterMonitoring.id))
566
584
  endpoint.createDefaultActivatedCarbonFilterMonitoringClusterServer();
585
+ if (serverList.includes(WaterTankLevelMonitoring.id))
586
+ endpoint.createDefaultWaterTankLevelMonitoringClusterServer();
567
587
  if (serverList.includes(CarbonMonoxideConcentrationMeasurement.id))
568
588
  endpoint.createDefaultCarbonMonoxideConcentrationMeasurementClusterServer();
569
589
  if (serverList.includes(CarbonDioxideConcentrationMeasurement.id))
@@ -46,12 +46,15 @@ export async function getServerBehaviorFromClusterId(clusterId, features) {
46
46
  const base = mod[`${name}Server`];
47
47
  if (!base || !ClusterBehavior.isType(base) || isClientBehavior(base))
48
48
  return undefined;
49
+ const knownFeatureNames = new Set(Object.keys(base.cluster.features ?? {}).map((key) => pascalCase(key)));
49
50
  const featureNames = (Array.isArray(features)
50
51
  ? features
51
52
  : Object.entries(features ?? {})
52
53
  .filter(([, enabled]) => enabled)
53
- .map(([key]) => key)).map((key) => pascalCase(key));
54
- return featureNames.length > 0 ? base.with(...featureNames) : base;
54
+ .map(([key]) => key))
55
+ .map((key) => pascalCase(key))
56
+ .filter((key) => knownFeatureNames.has(key));
57
+ return features === undefined ? base : base.with(...featureNames);
55
58
  }
56
59
  export async function getClientBehaviorFromClusterId(clusterId) {
57
60
  const name = getClusterNameById(clusterId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/core",
3
- "version": "3.10.8-dev-20260902-361500d",
3
+ "version": "3.10.8-dev-20260903-014e30e",
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-20260902-361500d",
136
- "@matterbridge/thread": "3.10.8-dev-20260902-361500d",
137
- "@matterbridge/types": "3.10.8-dev-20260902-361500d",
138
- "@matterbridge/utils": "3.10.8-dev-20260902-361500d",
135
+ "@matterbridge/dgram": "3.10.8-dev-20260903-014e30e",
136
+ "@matterbridge/thread": "3.10.8-dev-20260903-014e30e",
137
+ "@matterbridge/types": "3.10.8-dev-20260903-014e30e",
138
+ "@matterbridge/utils": "3.10.8-dev-20260903-014e30e",
139
139
  "escape-html": "1.0.3",
140
140
  "express": "5.2.1",
141
141
  "express-rate-limit": "8.7.0",