matterbridge 3.0.4-dev-20250525-b1cbfb7 → 3.0.5-dev-20250526-422f029

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
@@ -8,12 +8,26 @@ If you like this project and find it useful, please consider giving it a star on
8
8
  <img src="bmc-button.svg" alt="Buy me a coffee" width="120">
9
9
  </a>
10
10
 
11
- ## [3.0.4] - 2025-05-??
11
+ ## [3.0.5] - 2025-05-??
12
+
13
+ ### Added
14
+
15
+ ### Changed
16
+
17
+ ### Fixed
18
+
19
+ - [selectAreas]: Fixed MatterbridgeServiceAreaServer.selectAreas.
20
+
21
+ <a href="https://www.buymeacoffee.com/luligugithub">
22
+ <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
23
+ </a>
24
+
25
+ ## [3.0.4] - 2025-05-26
12
26
 
13
27
  ### Added
14
28
 
15
29
  - [jsdoc]: Improved jsdoc for cluster helpers.
16
- - [cover]: Added createDefaultTiltWindowCoveringClusterServer().
30
+ - [cover]: Added createDefaultLiftTiltWindowCoveringClusterServer() that create a window covering cluser with both lift and tilt features (supported by Apple Home).
17
31
 
18
32
  ### Changed
19
33
 
package/dist/cli.js CHANGED
@@ -100,12 +100,20 @@ async function stopCpuMemoryCheck() {
100
100
  async function startInspector() {
101
101
  const { Session } = await import('node:inspector');
102
102
  log.debug(`Starting heap sampling...`);
103
- session = new Session();
104
- session.connect();
105
- await new Promise((resolve, reject) => {
106
- session?.post('HeapProfiler.startSampling', {}, (err) => (err ? reject(err) : resolve()));
107
- });
108
- log.debug(`Started heap sampling`);
103
+ try {
104
+ session = new Session();
105
+ session.connect();
106
+ await new Promise((resolve, reject) => {
107
+ session?.post('HeapProfiler.startSampling', (err) => (err ? reject(err) : resolve()));
108
+ });
109
+ log.debug(`Started heap sampling`);
110
+ }
111
+ catch (err) {
112
+ log.error(`Failed to start heap sampling: ${err instanceof Error ? err.message : err}`);
113
+ session?.disconnect();
114
+ session = undefined;
115
+ return;
116
+ }
109
117
  }
110
118
  async function stopInspector() {
111
119
  const { writeFileSync } = await import('node:fs');
@@ -0,0 +1,122 @@
1
+ import { OperationalState } from '@matter/main/clusters/operational-state';
2
+ import { LaundryWasherControls } from '@matter/main/clusters/laundry-washer-controls';
3
+ import { LaundryWasherMode } from '@matter/main/clusters/laundry-washer-mode';
4
+ import { TemperatureControl } from '@matter/main/clusters/temperature-control';
5
+ import { ModeBase } from '@matter/main/clusters/mode-base';
6
+ import { TemperatureControlServer } from '@matter/main/behaviors/temperature-control';
7
+ import { LaundryWasherModeServer } from '@matter/main/behaviors/laundry-washer-mode';
8
+ import { LaundryWasherControlsServer } from '@matter/main/behaviors/laundry-washer-controls';
9
+ import { laundryWasher } from './matterbridgeDeviceTypes.js';
10
+ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
11
+ import { MatterbridgeOnOffServer, MatterbridgeServer } from './matterbridgeBehaviors.js';
12
+ export class LaundryWasher extends MatterbridgeEndpoint {
13
+ constructor(name, serial) {
14
+ super(laundryWasher, { uniqueStorageKey: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
15
+ this.createDefaultIdentifyClusterServer();
16
+ this.createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Laundry Washer');
17
+ this.createDefaultPowerSourceWiredClusterServer();
18
+ this.createDeadFrontOnOffClusterServer();
19
+ this.createLevelTemperatureControlClusterServer(3, ['Cold', '30°', '40°', '60°', '80°']);
20
+ this.createDefaultLaundryWasherControlsClusterServer();
21
+ this.createDefaultLaundryWasherModeClusterServer();
22
+ this.createDefaultOperationalStateClusterServer(OperationalState.OperationalStateEnum.Stopped);
23
+ }
24
+ createDefaultLaundryWasherControlsClusterServer(spinSpeedCurrent = 3, spinSpeeds = ['400', '800', '1200', '1600'], numberOfRinses = LaundryWasherControls.NumberOfRinses.Normal, supportedRinses = [LaundryWasherControls.NumberOfRinses.None, LaundryWasherControls.NumberOfRinses.Normal, LaundryWasherControls.NumberOfRinses.Max, LaundryWasherControls.NumberOfRinses.Extra]) {
25
+ this.behaviors.require(LaundryWasherControlsServer.with(LaundryWasherControls.Feature.Spin, LaundryWasherControls.Feature.Rinse), {
26
+ spinSpeeds,
27
+ spinSpeedCurrent,
28
+ supportedRinses,
29
+ numberOfRinses,
30
+ });
31
+ return this;
32
+ }
33
+ createDefaultLaundryWasherModeClusterServer(currentMode = 2, supportedModes = [
34
+ { label: 'Delicate', mode: 1, modeTags: [{ value: LaundryWasherMode.ModeTag.Delicate }] },
35
+ { label: 'Normal', mode: 2, modeTags: [{ value: LaundryWasherMode.ModeTag.Normal }] },
36
+ { label: 'Heavy', mode: 3, modeTags: [{ value: LaundryWasherMode.ModeTag.Heavy }] },
37
+ { label: 'Whites', mode: 4, modeTags: [{ value: LaundryWasherMode.ModeTag.Whites }] },
38
+ ]) {
39
+ this.behaviors.require(MatterbridgeLaundryWasherModeServer, {
40
+ supportedModes,
41
+ currentMode,
42
+ });
43
+ return this;
44
+ }
45
+ createLevelTemperatureControlClusterServer(selectedTemperatureLevel = 1, supportedTemperatureLevels = ['Cold', 'Warm', 'Hot']) {
46
+ this.behaviors.require(MatterbridgeLevelTemperatureControlServer.with(TemperatureControl.Feature.TemperatureLevel), {
47
+ selectedTemperatureLevel,
48
+ supportedTemperatureLevels,
49
+ });
50
+ return this;
51
+ }
52
+ createNumberTemperatureControlClusterServer(temperatureSetpoint = 40 * 100, minTemperature = 30 * 100, maxTemperature = 60 * 100, step = 10 * 100) {
53
+ this.behaviors.require(MatterbridgeNumberTemperatureControlServer.with(TemperatureControl.Feature.TemperatureNumber, TemperatureControl.Feature.TemperatureStep), {
54
+ temperatureSetpoint,
55
+ minTemperature,
56
+ maxTemperature,
57
+ step,
58
+ });
59
+ return this;
60
+ }
61
+ }
62
+ class MatterbridgeLevelTemperatureControlServer extends TemperatureControlServer.with(TemperatureControl.Feature.TemperatureLevel) {
63
+ initialize() {
64
+ if (this.state.supportedTemperatureLevels.length >= 2) {
65
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
66
+ device.log.info('MatterbridgeLevelTemperatureControlServer initialized');
67
+ }
68
+ }
69
+ setTemperature(request) {
70
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
71
+ if (request.targetTemperatureLevel !== undefined && request.targetTemperatureLevel >= 0 && request.targetTemperatureLevel < this.state.supportedTemperatureLevels.length) {
72
+ device.log.info(`MatterbridgeLevelTemperatureControlServer: setTemperature called setting selectedTemperatureLevel to ${request.targetTemperatureLevel}: ${this.state.supportedTemperatureLevels[request.targetTemperatureLevel]}`);
73
+ this.state.selectedTemperatureLevel = request.targetTemperatureLevel;
74
+ }
75
+ else {
76
+ device.log.error(`MatterbridgeLevelTemperatureControlServer: setTemperature called with invalid targetTemperatureLevel ${request.targetTemperatureLevel}`);
77
+ }
78
+ }
79
+ }
80
+ class MatterbridgeNumberTemperatureControlServer extends TemperatureControlServer.with(TemperatureControl.Feature.TemperatureNumber) {
81
+ initialize() {
82
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
83
+ device.log.info('MatterbridgeNumberTemperatureControlServer initialized');
84
+ }
85
+ setTemperature(request) {
86
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
87
+ if (request.targetTemperature !== undefined && request.targetTemperature >= this.state.minTemperature && request.targetTemperature <= this.state.maxTemperature) {
88
+ device.log.info(`MatterbridgeNumberTemperatureControlServer: setTemperature called setting temperatureSetpoint to ${request.targetTemperature}`);
89
+ this.state.temperatureSetpoint = request.targetTemperature;
90
+ }
91
+ else {
92
+ device.log.error(`MatterbridgeNumberTemperatureControlServer: setTemperature called with invalid targetTemperature ${request.targetTemperature}`);
93
+ }
94
+ }
95
+ }
96
+ class MatterbridgeLaundryWasherModeServer extends LaundryWasherModeServer {
97
+ initialize() {
98
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
99
+ device.log.info(`LaundryWasherModeServer initialized: currentMode is ${this.state.currentMode}`);
100
+ this.reactTo(this.agent.get(MatterbridgeOnOffServer).events.onOff$Changed, this.handleOnOffChange);
101
+ }
102
+ handleOnOffChange(onOff) {
103
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
104
+ if (onOff === false) {
105
+ device.log.notice('OnOffServer changed to OFF: setting Dead Front state to Manufacturer Specific');
106
+ this.state.currentMode = 2;
107
+ }
108
+ }
109
+ changeToMode(request) {
110
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
111
+ const supportedMode = this.state.supportedModes.find((supportedMode) => supportedMode.mode === request.newMode);
112
+ if (supportedMode) {
113
+ device.log.info(`LaundryWasherModeServer: changeToMode called with mode ${supportedMode.mode} = ${supportedMode.label}`);
114
+ this.state.currentMode = request.newMode;
115
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
116
+ }
117
+ else {
118
+ device.log.error(`LaundryWasherModeServer: changeToMode called with invalid mode ${request.newMode}`);
119
+ return { status: ModeBase.ModeChangeStatus.InvalidInMode, statusText: 'Invalid mode' };
120
+ }
121
+ }
122
+ }
@@ -581,9 +581,7 @@ export class MatterbridgeServiceAreaServer extends ServiceAreaServer {
581
581
  }
582
582
  device.selectAreas({ newAreas });
583
583
  this.state.selectedAreas = newAreas;
584
- this.state.currentArea = newAreas[0];
585
584
  device.log.info(`MatterbridgeServiceAreaServer selectAreas called with: ${newAreas.map((area) => area.toString()).join(', ')}`);
586
- device.selectAreas({ newAreas });
587
585
  return { status: ServiceArea.SelectAreasStatus.Success, statusText: 'Succesfully selected new areas' };
588
586
  }
589
587
  }
@@ -384,7 +384,7 @@ export function getClusterId(endpoint, cluster) {
384
384
  }
385
385
  export function getAttributeId(endpoint, cluster, attribute) {
386
386
  const clusterBehavior = endpoint.behaviors.supported[lowercaseFirstLetter(cluster)];
387
- return clusterBehavior?.cluster.attributes[lowercaseFirstLetter(attribute)]?.id;
387
+ return clusterBehavior?.cluster?.attributes[lowercaseFirstLetter(attribute)]?.id;
388
388
  }
389
389
  export function getAttribute(endpoint, cluster, attribute, log) {
390
390
  const clusterName = getBehavior(endpoint, cluster)?.id;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.0.4-dev-20250525-b1cbfb7",
3
+ "version": "3.0.5-dev-20250526-422f029",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge",
9
- "version": "3.0.4-dev-20250525-b1cbfb7",
9
+ "version": "3.0.5-dev-20250526-422f029",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@matter/main": "0.13.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.0.4-dev-20250525-b1cbfb7",
3
+ "version": "3.0.5-dev-20250526-422f029",
4
4
  "description": "Matterbridge plugin manager for Matter",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",