matterbridge 2.2.8-dev.1 → 3.0.0-edge.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -29,7 +29,37 @@ Features:
29
29
  - It is possible to choose the method: GET or POST.
30
30
  - The webhook can be tested directly in the frontend.
31
31
 
32
- ## [2.2.8] - 2025-04-09
32
+ ## Breaking changes
33
+
34
+ Matter 1.4
35
+
36
+ New device types:
37
+
38
+ - onOffMountedSwitch: Mounted On/Off Control (an onOff switch without client cluster!).
39
+ - dimmableMountedSwitch: Mounted Dimmable Load Control (a dimmer switch without client cluster!).
40
+
41
+ Modified clusters:
42
+
43
+ - OccupancySensing cluster.
44
+
45
+ ## [3.0.0] - 2025-04-??
46
+
47
+ ### Added
48
+
49
+ - [addEndpoint]: Added an error handler with deep stack on aggregatorNode.add() and serverNode.add() calls.
50
+
51
+ ### Changed
52
+
53
+ - [deviceTypes]: Updated device types to Matter 1.4
54
+ - [matter.js]: Update to 0.13.0-alpha.0-20250405-7fc7db48.
55
+ - [matter.js]: Update to 0.13.0-alpha.0-20250408-c916c7e8.
56
+
57
+ ### Fixed
58
+
59
+ - [doorLock]: Fixed supportedOperatingModes inverted bitmap (Thanks Apollon).
60
+ - [DevicesIcon]: Fixed rendering of leak freeze and rain sensors.
61
+
62
+ ## [2.2.8] - 2025-04-08
33
63
 
34
64
  ### Added
35
65
 
@@ -44,7 +74,6 @@ Features:
44
74
  ### Fixed
45
75
 
46
76
  - [homepage]: Fixed warning log for homepage property in package.json.
47
- - [DevicesIcon]: Fixed rendering of rain, freeze and leak sensors.
48
77
 
49
78
  <a href="https://www.buymeacoffee.com/luligugithub">
50
79
  <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
@@ -1 +1,205 @@
1
- export {};
1
+ import { ModeBase } from '@matter/main/clusters/mode-base';
2
+ import { Attribute, BitFlag, ClusterRegistry, Command, Event, EventPriority, FixedAttribute, MutableCluster, OptionalAttribute, OptionalCommand, OptionalEvent, TlvArray, TlvEnum, TlvField, TlvNoArguments, TlvNullable, TlvObject, TlvOptionalField, TlvString, TlvUInt32, TlvUInt8, TlvVendorId, WritableAttribute, } from '@matter/main/types';
3
+ import { OperationalState as OperationalStateNamespace } from '@matter/main/clusters/operational-state';
4
+ export var RvcRunMode;
5
+ (function (RvcRunMode) {
6
+ let Feature;
7
+ (function (Feature) {
8
+ Feature["OnOff"] = "OnOff";
9
+ })(Feature = RvcRunMode.Feature || (RvcRunMode.Feature = {}));
10
+ let ModeTag;
11
+ (function (ModeTag) {
12
+ ModeTag[ModeTag["Auto"] = 0] = "Auto";
13
+ ModeTag[ModeTag["Quick"] = 1] = "Quick";
14
+ ModeTag[ModeTag["Quiet"] = 2] = "Quiet";
15
+ ModeTag[ModeTag["LowNoise"] = 3] = "LowNoise";
16
+ ModeTag[ModeTag["LowEnergy"] = 4] = "LowEnergy";
17
+ ModeTag[ModeTag["Vacation"] = 5] = "Vacation";
18
+ ModeTag[ModeTag["Min"] = 6] = "Min";
19
+ ModeTag[ModeTag["Max"] = 7] = "Max";
20
+ ModeTag[ModeTag["Night"] = 8] = "Night";
21
+ ModeTag[ModeTag["Day"] = 9] = "Day";
22
+ ModeTag[ModeTag["Idle"] = 16384] = "Idle";
23
+ ModeTag[ModeTag["Cleaning"] = 16385] = "Cleaning";
24
+ ModeTag[ModeTag["Mapping"] = 16386] = "Mapping";
25
+ })(ModeTag = RvcRunMode.ModeTag || (RvcRunMode.ModeTag = {}));
26
+ RvcRunMode.TlvModeTagStruct = TlvObject({
27
+ mfgCode: TlvOptionalField(0, TlvVendorId),
28
+ value: TlvField(1, TlvEnum()),
29
+ });
30
+ RvcRunMode.TlvModeOption = TlvObject({
31
+ label: TlvField(0, TlvString.bound({ maxLength: 64 })),
32
+ mode: TlvField(1, TlvUInt8),
33
+ modeTags: TlvField(2, TlvArray(RvcRunMode.TlvModeTagStruct, { maxLength: 8 })),
34
+ });
35
+ let ModeChangeStatus;
36
+ (function (ModeChangeStatus) {
37
+ ModeChangeStatus[ModeChangeStatus["Stuck"] = 65] = "Stuck";
38
+ ModeChangeStatus[ModeChangeStatus["DustBinMissing"] = 66] = "DustBinMissing";
39
+ ModeChangeStatus[ModeChangeStatus["DustBinFull"] = 67] = "DustBinFull";
40
+ ModeChangeStatus[ModeChangeStatus["WaterTankEmpty"] = 68] = "WaterTankEmpty";
41
+ ModeChangeStatus[ModeChangeStatus["WaterTankMissing"] = 69] = "WaterTankMissing";
42
+ ModeChangeStatus[ModeChangeStatus["WaterTankLidOpen"] = 70] = "WaterTankLidOpen";
43
+ ModeChangeStatus[ModeChangeStatus["MopCleaningPadMissing"] = 71] = "MopCleaningPadMissing";
44
+ ModeChangeStatus[ModeChangeStatus["BatteryLow"] = 72] = "BatteryLow";
45
+ })(ModeChangeStatus = RvcRunMode.ModeChangeStatus || (RvcRunMode.ModeChangeStatus = {}));
46
+ RvcRunMode.OnOffComponent = MutableCluster.Component({
47
+ attributes: {
48
+ startUpMode: WritableAttribute(0x2, TlvNullable(TlvUInt8), { persistent: true }),
49
+ onMode: WritableAttribute(0x3, TlvNullable(TlvUInt8), { persistent: true, default: null }),
50
+ },
51
+ });
52
+ RvcRunMode.Base = MutableCluster.Component({
53
+ id: 0x54,
54
+ name: 'RvcRunMode',
55
+ revision: 3,
56
+ features: {
57
+ onOff: BitFlag(0),
58
+ },
59
+ attributes: {
60
+ supportedModes: FixedAttribute(0x0, TlvArray(RvcRunMode.TlvModeOption, { minLength: 2, maxLength: 255 }), { default: [] }),
61
+ currentMode: Attribute(0x1, TlvUInt8, { persistent: true }),
62
+ },
63
+ commands: {
64
+ changeToMode: Command(0x0, ModeBase.TlvChangeToModeRequest, 0x1, ModeBase.TlvChangeToModeResponse),
65
+ },
66
+ extensions: MutableCluster.Extensions({ flags: { onOff: true }, component: RvcRunMode.OnOffComponent }),
67
+ });
68
+ RvcRunMode.ClusterInstance = MutableCluster(RvcRunMode.Base);
69
+ RvcRunMode.Cluster = RvcRunMode.ClusterInstance;
70
+ RvcRunMode.Complete = RvcRunMode.Cluster;
71
+ })(RvcRunMode || (RvcRunMode = {}));
72
+ export const RvcRunModeCluster = RvcRunMode.Cluster;
73
+ ClusterRegistry.register(RvcRunMode.Complete);
74
+ export var RvcCleanMode;
75
+ (function (RvcCleanMode) {
76
+ let Feature;
77
+ (function (Feature) {
78
+ Feature["OnOff"] = "OnOff";
79
+ })(Feature = RvcCleanMode.Feature || (RvcCleanMode.Feature = {}));
80
+ let ModeTag;
81
+ (function (ModeTag) {
82
+ ModeTag[ModeTag["Auto"] = 0] = "Auto";
83
+ ModeTag[ModeTag["Quick"] = 1] = "Quick";
84
+ ModeTag[ModeTag["Quiet"] = 2] = "Quiet";
85
+ ModeTag[ModeTag["LowNoise"] = 3] = "LowNoise";
86
+ ModeTag[ModeTag["LowEnergy"] = 4] = "LowEnergy";
87
+ ModeTag[ModeTag["Vacation"] = 5] = "Vacation";
88
+ ModeTag[ModeTag["Min"] = 6] = "Min";
89
+ ModeTag[ModeTag["Max"] = 7] = "Max";
90
+ ModeTag[ModeTag["Night"] = 8] = "Night";
91
+ ModeTag[ModeTag["Day"] = 9] = "Day";
92
+ ModeTag[ModeTag["DeepClean"] = 16384] = "DeepClean";
93
+ ModeTag[ModeTag["Vacuum"] = 16385] = "Vacuum";
94
+ ModeTag[ModeTag["Mop"] = 16386] = "Mop";
95
+ })(ModeTag = RvcCleanMode.ModeTag || (RvcCleanMode.ModeTag = {}));
96
+ RvcCleanMode.TlvModeTagStruct = TlvObject({
97
+ mfgCode: TlvOptionalField(0, TlvVendorId),
98
+ value: TlvField(1, TlvEnum()),
99
+ });
100
+ RvcCleanMode.TlvModeOption = TlvObject({
101
+ label: TlvField(0, TlvString.bound({ maxLength: 64 })),
102
+ mode: TlvField(1, TlvUInt8),
103
+ modeTags: TlvField(2, TlvArray(RvcCleanMode.TlvModeTagStruct, { maxLength: 8 })),
104
+ });
105
+ let ModeChangeStatus;
106
+ (function (ModeChangeStatus) {
107
+ ModeChangeStatus[ModeChangeStatus["CleaningInProgress"] = 64] = "CleaningInProgress";
108
+ })(ModeChangeStatus = RvcCleanMode.ModeChangeStatus || (RvcCleanMode.ModeChangeStatus = {}));
109
+ RvcCleanMode.OnOffComponent = MutableCluster.Component({
110
+ attributes: {
111
+ startUpMode: WritableAttribute(0x2, TlvNullable(TlvUInt8), { persistent: true }),
112
+ onMode: WritableAttribute(0x3, TlvNullable(TlvUInt8), { persistent: true, default: null }),
113
+ },
114
+ });
115
+ RvcCleanMode.Base = MutableCluster.Component({
116
+ id: 0x55,
117
+ name: 'RvcCleanMode',
118
+ revision: 3,
119
+ features: {
120
+ onOff: BitFlag(0),
121
+ },
122
+ attributes: {
123
+ supportedModes: FixedAttribute(0x0, TlvArray(RvcCleanMode.TlvModeOption, { minLength: 2, maxLength: 255 }), { default: [] }),
124
+ currentMode: Attribute(0x1, TlvUInt8, { persistent: true }),
125
+ },
126
+ commands: {
127
+ changeToMode: Command(0x0, ModeBase.TlvChangeToModeRequest, 0x1, ModeBase.TlvChangeToModeResponse),
128
+ },
129
+ extensions: MutableCluster.Extensions({ flags: { onOff: true }, component: RvcCleanMode.OnOffComponent }),
130
+ });
131
+ RvcCleanMode.ClusterInstance = MutableCluster(RvcCleanMode.Base);
132
+ RvcCleanMode.Cluster = RvcCleanMode.ClusterInstance;
133
+ RvcCleanMode.Complete = RvcCleanMode.Cluster;
134
+ })(RvcCleanMode || (RvcCleanMode = {}));
135
+ export const RvcCleanModeCluster = RvcCleanMode.Cluster;
136
+ ClusterRegistry.register(RvcCleanMode.Complete);
137
+ export var RvcOperationalState;
138
+ (function (RvcOperationalState) {
139
+ let OperationalState;
140
+ (function (OperationalState) {
141
+ OperationalState[OperationalState["Stopped"] = 0] = "Stopped";
142
+ OperationalState[OperationalState["Running"] = 1] = "Running";
143
+ OperationalState[OperationalState["Paused"] = 2] = "Paused";
144
+ OperationalState[OperationalState["Error"] = 3] = "Error";
145
+ OperationalState[OperationalState["SeekingCharger"] = 64] = "SeekingCharger";
146
+ OperationalState[OperationalState["Charging"] = 65] = "Charging";
147
+ OperationalState[OperationalState["Docked"] = 66] = "Docked";
148
+ })(OperationalState = RvcOperationalState.OperationalState || (RvcOperationalState.OperationalState = {}));
149
+ RvcOperationalState.TlvOperationalStateStruct = TlvObject({
150
+ operationalStateId: TlvField(0, TlvEnum()),
151
+ operationalStateLabel: TlvOptionalField(1, TlvString.bound({ maxLength: 64 })),
152
+ });
153
+ let ErrorState;
154
+ (function (ErrorState) {
155
+ ErrorState[ErrorState["NoError"] = 0] = "NoError";
156
+ ErrorState[ErrorState["UnableToStartOrResume"] = 1] = "UnableToStartOrResume";
157
+ ErrorState[ErrorState["UnableToCompleteOperation"] = 2] = "UnableToCompleteOperation";
158
+ ErrorState[ErrorState["CommandInvalidInState"] = 3] = "CommandInvalidInState";
159
+ ErrorState[ErrorState["FailedToFindChargingDock"] = 64] = "FailedToFindChargingDock";
160
+ ErrorState[ErrorState["Stuck"] = 65] = "Stuck";
161
+ ErrorState[ErrorState["DustBinMissing"] = 66] = "DustBinMissing";
162
+ ErrorState[ErrorState["DustBinFull"] = 67] = "DustBinFull";
163
+ ErrorState[ErrorState["WaterTankEmpty"] = 68] = "WaterTankEmpty";
164
+ ErrorState[ErrorState["WaterTankMissing"] = 69] = "WaterTankMissing";
165
+ ErrorState[ErrorState["WaterTankLidOpen"] = 70] = "WaterTankLidOpen";
166
+ ErrorState[ErrorState["MopCleaningPadMissing"] = 71] = "MopCleaningPadMissing";
167
+ })(ErrorState = RvcOperationalState.ErrorState || (RvcOperationalState.ErrorState = {}));
168
+ RvcOperationalState.TlvErrorStateStruct = TlvObject({
169
+ errorStateId: TlvField(0, TlvEnum()),
170
+ errorStateLabel: TlvOptionalField(1, TlvString.bound({ maxLength: 64 })),
171
+ errorStateDetails: TlvOptionalField(2, TlvString.bound({ maxLength: 64 })),
172
+ });
173
+ RvcOperationalState.TlvOperationalCommandResponse = TlvObject({
174
+ commandResponseState: TlvField(0, RvcOperationalState.TlvErrorStateStruct),
175
+ });
176
+ RvcOperationalState.TlvOperationalErrorEvent = TlvObject({ errorState: TlvField(0, RvcOperationalState.TlvErrorStateStruct) });
177
+ RvcOperationalState.ClusterInstance = MutableCluster({
178
+ id: 0x61,
179
+ name: 'RvcOperationalState',
180
+ revision: 2,
181
+ attributes: {
182
+ phaseList: Attribute(0x0, TlvNullable(TlvArray(TlvString, { maxLength: 32 }))),
183
+ currentPhase: Attribute(0x1, TlvNullable(TlvUInt8)),
184
+ countdownTime: OptionalAttribute(0x2, TlvNullable(TlvUInt32.bound({ max: 259200 })), { default: null }),
185
+ operationalStateList: Attribute(0x3, TlvArray(RvcOperationalState.TlvOperationalStateStruct), { default: [] }),
186
+ operationalState: Attribute(0x4, TlvEnum()),
187
+ operationalError: Attribute(0x5, RvcOperationalState.TlvErrorStateStruct),
188
+ },
189
+ commands: {
190
+ pause: OptionalCommand(0x0, TlvNoArguments, 0x4, RvcOperationalState.TlvOperationalCommandResponse),
191
+ stop: OptionalCommand(0x1, TlvNoArguments, 0x4, RvcOperationalState.TlvOperationalCommandResponse),
192
+ start: OptionalCommand(0x2, TlvNoArguments, 0x4, RvcOperationalState.TlvOperationalCommandResponse),
193
+ resume: OptionalCommand(0x3, TlvNoArguments, 0x4, RvcOperationalState.TlvOperationalCommandResponse),
194
+ goHome: OptionalCommand(0x80, TlvNoArguments, 0x4, RvcOperationalState.TlvOperationalCommandResponse),
195
+ },
196
+ events: {
197
+ operationalError: Event(0x0, EventPriority.Critical, RvcOperationalState.TlvOperationalErrorEvent),
198
+ operationCompletion: OptionalEvent(0x1, EventPriority.Info, OperationalStateNamespace.TlvOperationCompletionEvent),
199
+ },
200
+ });
201
+ RvcOperationalState.Cluster = RvcOperationalState.ClusterInstance;
202
+ RvcOperationalState.Complete = RvcOperationalState.Cluster;
203
+ })(RvcOperationalState || (RvcOperationalState = {}));
204
+ export const RvcOperationalStateCluster = RvcOperationalState.Cluster;
205
+ ClusterRegistry.register(RvcOperationalState.Complete);
@@ -2,6 +2,7 @@ import os from 'node:os';
2
2
  import path from 'node:path';
3
3
  import { promises as fs } from 'node:fs';
4
4
  import EventEmitter from 'node:events';
5
+ import { inspect } from 'node:util';
5
6
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from './logger/export.js';
6
7
  import { NodeStorageManager } from './storage/export.js';
7
8
  import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout } from './utils/export.js';
@@ -306,30 +307,30 @@ export class Matterbridge extends EventEmitter {
306
307
  if (hasParameter('matterlogger')) {
307
308
  const level = getParameter('matterlogger');
308
309
  if (level === 'debug') {
309
- Logger.defaultLogLevel = MatterLogLevel.DEBUG;
310
+ Logger.level = MatterLogLevel.DEBUG;
310
311
  }
311
312
  else if (level === 'info') {
312
- Logger.defaultLogLevel = MatterLogLevel.INFO;
313
+ Logger.level = MatterLogLevel.INFO;
313
314
  }
314
315
  else if (level === 'notice') {
315
- Logger.defaultLogLevel = MatterLogLevel.NOTICE;
316
+ Logger.level = MatterLogLevel.NOTICE;
316
317
  }
317
318
  else if (level === 'warn') {
318
- Logger.defaultLogLevel = MatterLogLevel.WARN;
319
+ Logger.level = MatterLogLevel.WARN;
319
320
  }
320
321
  else if (level === 'error') {
321
- Logger.defaultLogLevel = MatterLogLevel.ERROR;
322
+ Logger.level = MatterLogLevel.ERROR;
322
323
  }
323
324
  else if (level === 'fatal') {
324
- Logger.defaultLogLevel = MatterLogLevel.FATAL;
325
+ Logger.level = MatterLogLevel.FATAL;
325
326
  }
326
327
  else {
327
328
  this.log.warn(`Invalid matter.js logger level: ${level}. Using default level "info".`);
328
- Logger.defaultLogLevel = MatterLogLevel.INFO;
329
+ Logger.level = MatterLogLevel.INFO;
329
330
  }
330
331
  }
331
332
  else {
332
- Logger.defaultLogLevel = await this.nodeContext.get('matterLogLevel', this.matterbridgeInformation.shellyBoard ? MatterLogLevel.NOTICE : MatterLogLevel.INFO);
333
+ Logger.level = (await this.nodeContext.get('matterLogLevel', this.matterbridgeInformation.shellyBoard ? MatterLogLevel.NOTICE : MatterLogLevel.INFO));
333
334
  }
334
335
  Logger.format = MatterLogFormat.ANSI;
335
336
  Logger.setLogger('default', this.createMatterLogger());
@@ -1327,7 +1328,7 @@ export class Matterbridge extends EventEmitter {
1327
1328
  }
1328
1329
  this.startServerNode(plugin.serverNode);
1329
1330
  plugin.reachabilityTimeout = setTimeout(() => {
1330
- this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggragator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
1331
+ this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggregator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
1331
1332
  if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
1332
1333
  this.setAggregatorReachability(plugin.aggregatorNode, true);
1333
1334
  this.frontend.wssSendRefreshRequired('reachability');
@@ -1430,6 +1431,7 @@ export class Matterbridge extends EventEmitter {
1430
1431
  softwareVersionString: await storageContext.get('softwareVersionString'),
1431
1432
  hardwareVersion: await storageContext.get('hardwareVersion'),
1432
1433
  hardwareVersionString: await storageContext.get('hardwareVersionString'),
1434
+ reachable: true,
1433
1435
  },
1434
1436
  });
1435
1437
  const sanitizeFabrics = (fabrics, resetSessions = false) => {
@@ -1638,18 +1640,46 @@ export class Matterbridge extends EventEmitter {
1638
1640
  this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
1639
1641
  if (!this.aggregatorNode)
1640
1642
  this.log.error('Aggregator node not found for Matterbridge');
1641
- await this.aggregatorNode?.add(device);
1643
+ try {
1644
+ await this.aggregatorNode?.add(device);
1645
+ }
1646
+ catch (error) {
1647
+ const errorMessage = error instanceof Error ? error.message : '';
1648
+ const errorStack = error instanceof Error ? error.stack : '';
1649
+ const errorDebug = inspect(error, { depth: 10 });
1650
+ this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) for plugin ${plg}${pluginName}${er}: ${error} ${errorMessage} ${errorStack} ${errorDebug}`);
1651
+ return;
1652
+ }
1642
1653
  }
1643
1654
  else if (this.bridgeMode === 'childbridge') {
1644
1655
  if (plugin.type === 'AccessoryPlatform') {
1645
- await this.createAccessoryPlugin(plugin, device);
1656
+ try {
1657
+ this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
1658
+ await this.createAccessoryPlugin(plugin, device);
1659
+ }
1660
+ catch (error) {
1661
+ const errorMessage = error instanceof Error ? error.message : '';
1662
+ const errorStack = error instanceof Error ? error.stack : '';
1663
+ const errorDebug = inspect(error, { depth: 10 });
1664
+ this.log.error(`Error creating endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) for AccessoryPlatform plugin ${plg}${pluginName}${er} server node: ${error} ${errorMessage} ${errorStack} ${errorDebug}`);
1665
+ return;
1666
+ }
1646
1667
  }
1647
1668
  if (plugin.type === 'DynamicPlatform') {
1648
1669
  plugin.locked = true;
1649
- this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to ${plg}${plugin.name}${db} aggregator node`);
1670
+ this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
1650
1671
  if (!plugin.aggregatorNode)
1651
1672
  this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${db}`);
1652
- await plugin.aggregatorNode?.add(device);
1673
+ try {
1674
+ await plugin.aggregatorNode?.add(device);
1675
+ }
1676
+ catch (error) {
1677
+ const errorMessage = error instanceof Error ? error.message : '';
1678
+ const errorStack = error instanceof Error ? error.stack : '';
1679
+ const errorDebug = inspect(error, { depth: 10 });
1680
+ this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) for DynamicPlatform plugin ${plg}${pluginName}${er} aggregator node: ${error} ${errorMessage} ${errorStack} ${errorDebug}`);
1681
+ return;
1682
+ }
1653
1683
  }
1654
1684
  }
1655
1685
  if (plugin.registeredDevices !== undefined)
@@ -1,4 +1,5 @@
1
- import { Behavior } from '@matter/main';
1
+ import { Behavior, ClusterBehavior } from '@matter/main';
2
+ import { Status } from '@matter/main/types';
2
3
  import { BooleanStateConfiguration } from '@matter/main/clusters/boolean-state-configuration';
3
4
  import { ColorControl } from '@matter/main/clusters/color-control';
4
5
  import { FanControl } from '@matter/main/clusters/fan-control';
@@ -14,11 +15,15 @@ import { ColorControlServer } from '@matter/main/behaviors/color-control';
14
15
  import { WindowCoveringServer } from '@matter/main/behaviors/window-covering';
15
16
  import { DoorLockServer } from '@matter/main/behaviors/door-lock';
16
17
  import { FanControlServer } from '@matter/main/behaviors/fan-control';
17
- import { ThermostatServer } from '@matter/main/behaviors/thermostat';
18
- import { ValveConfigurationAndControlServer } from '@matter/main/behaviors/valve-configuration-and-control';
18
+ import { ThermostatBehavior } from '@matter/main/behaviors/thermostat';
19
+ import { ValveConfigurationAndControlBehavior } from '@matter/main/behaviors/valve-configuration-and-control';
19
20
  import { ModeSelectServer } from '@matter/main/behaviors/mode-select';
20
21
  import { SmokeCoAlarmServer } from '@matter/main/behaviors/smoke-co-alarm';
21
22
  import { SwitchServer } from '@matter/main/behaviors/switch';
23
+ import { RvcCleanMode } from 'matterbridge/cluster';
24
+ import { RvcRunMode } from 'matterbridge/cluster';
25
+ import { RvcOperationalState } from 'matterbridge/cluster';
26
+ import { OperationalState } from '@matter/main/clusters/operational-state';
22
27
  export class MatterbridgeBehaviorDevice {
23
28
  log;
24
29
  commandHandler;
@@ -292,7 +297,7 @@ export class MatterbridgeFanControlServer extends FanControlServer.with(FanContr
292
297
  super.step({ direction, wrap, lowestOff });
293
298
  }
294
299
  }
295
- export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermostat.Feature.Cooling, Thermostat.Feature.Heating, Thermostat.Feature.AutoMode) {
300
+ export class MatterbridgeThermostatServer extends ThermostatBehavior.with(Thermostat.Feature.Cooling, Thermostat.Feature.Heating, Thermostat.Feature.AutoMode) {
296
301
  async setpointRaiseLower({ mode, amount }) {
297
302
  const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand;
298
303
  device.setpointRaiseLower({ mode, amount });
@@ -310,19 +315,24 @@ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermost
310
315
  this.state.occupiedCoolingSetpoint = setpoint * 100;
311
316
  device.log.debug('Set occupiedCoolingSetpoint to:', setpoint);
312
317
  }
313
- super.setpointRaiseLower({ mode, amount });
314
318
  }
315
319
  }
316
- export class MatterbridgeValveConfigurationAndControlServer extends ValveConfigurationAndControlServer.with(ValveConfigurationAndControl.Feature.Level) {
317
- async open({ openDuration, targetLevel }) {
320
+ export class MatterbridgeValveConfigurationAndControlServer extends ValveConfigurationAndControlBehavior.with(ValveConfigurationAndControl.Feature.Level) {
321
+ initialize() {
322
+ }
323
+ open({ openDuration, targetLevel }) {
318
324
  const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand;
325
+ device.log.debug(`Command open called with openDuration: ${openDuration} targetLevel: ${targetLevel}`);
319
326
  device.open({ openDuration, targetLevel });
320
- super.open({ openDuration, targetLevel });
327
+ this.state.targetLevel = targetLevel ?? 100;
328
+ this.state.currentLevel = targetLevel ?? 100;
321
329
  }
322
- async close() {
330
+ close() {
323
331
  const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand;
332
+ device.log.debug(`Command close called`);
324
333
  device.close();
325
- super.close();
334
+ this.state.targetLevel = 0;
335
+ this.state.currentLevel = 0;
326
336
  }
327
337
  }
328
338
  export class MatterbridgeSmokeCoAlarmServer extends SmokeCoAlarmServer.with(SmokeCoAlarm.Feature.SmokeAlarm, SmokeCoAlarm.Feature.CoAlarm) {
@@ -343,3 +353,71 @@ export class MatterbridgeSwitchServer extends SwitchServer {
343
353
  initialize() {
344
354
  }
345
355
  }
356
+ export const RvcRunModeBehavior = ClusterBehavior.withInterface().for(RvcRunMode.Cluster);
357
+ export class MatterbridgeRvcRunModeServer extends RvcRunModeBehavior.with(RvcRunMode.Feature.OnOff) {
358
+ initialize() {
359
+ }
360
+ changeToMode({ newMode }) {
361
+ const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand;
362
+ device.changeToMode({ newMode });
363
+ this.state.currentMode = newMode;
364
+ console.log('MatterbridgeRvcRunModeServer changeToMode called with newMode:', newMode);
365
+ return { status: Status.Success, statusText: 'Success' };
366
+ }
367
+ }
368
+ export const RvcCleanModeBehavior = ClusterBehavior.withInterface().for(RvcCleanMode.Cluster);
369
+ export class MatterbridgeRvcCleanModeServer extends RvcCleanModeBehavior.with(RvcRunMode.Feature.OnOff) {
370
+ initialize() {
371
+ }
372
+ changeToMode({ newMode }) {
373
+ const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand;
374
+ device.changeToMode({ newMode });
375
+ this.state.currentMode = newMode;
376
+ console.log('MatterbridgeRvcCleanModeServer changeToMode called with newMode:', newMode);
377
+ return { status: Status.Success, statusText: 'Success' };
378
+ }
379
+ }
380
+ export const RvcOperationalStateBehavior = ClusterBehavior.withInterface().for(RvcOperationalState.Cluster);
381
+ export class MatterbridgeRvcOperationalStateServer extends RvcOperationalStateBehavior {
382
+ initialize() {
383
+ console.log('MatterbridgeRvcOperationalStateServer initialized: setting operational state to Docked');
384
+ this.state.operationalState = RvcOperationalState.OperationalState.Docked;
385
+ this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error' };
386
+ this.reactTo(this.agent.get(OnOffServer).events.onOff$Changed, this.handleOnOffChange);
387
+ }
388
+ handleOnOffChange(onOff) {
389
+ if (onOff) {
390
+ console.log('OnOffServer changed to ON: setting operational state to Running');
391
+ this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 2;
392
+ this.state.operationalState = RvcOperationalState.OperationalState.Running;
393
+ this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error' };
394
+ }
395
+ else {
396
+ console.log('OnOffServer changed to OFF: setting operational state to Docked');
397
+ this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 1;
398
+ this.state.operationalState = RvcOperationalState.OperationalState.Docked;
399
+ this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error' };
400
+ }
401
+ }
402
+ pause() {
403
+ console.log('MatterbridgeRvcOperationalStateServer: pause called setting operational state to Paused and currentMode to Idle');
404
+ this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 1;
405
+ this.state.operationalState = RvcOperationalState.OperationalState.Paused;
406
+ this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error' };
407
+ return { commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error' } };
408
+ }
409
+ resume() {
410
+ console.log('MatterbridgeRvcOperationalStateServer: resume called setting operational state to Running and currentMode to Cleaning');
411
+ this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 2;
412
+ this.state.operationalState = RvcOperationalState.OperationalState.Running;
413
+ this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error' };
414
+ return { commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error' } };
415
+ }
416
+ goHome() {
417
+ console.log('MatterbridgeRvcOperationalStateServer: go home called setting operational state to Docked and currentMode to Idle');
418
+ this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 1;
419
+ this.state.operationalState = RvcOperationalState.OperationalState.Docked;
420
+ this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error' };
421
+ return { commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error' } };
422
+ }
423
+ }