matterbridge 3.0.3-dev-20250518-a216d52 → 3.0.3-dev-20250519-acb2774

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,24 @@ 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.3] - 2025-05-??
11
+ ## [3.0.3] - 2025-05-18
12
+
13
+ ### New plugins
14
+
15
+ [Dyson robot](https://github.com/thoukydides/matterbridge-dyson-robot)
16
+
17
+ A Matterbridge plugin that connects Dyson robot vacuums and air treatment devices.
18
+ to the Matter smart home ecosystem via their local MQTT APIs.
19
+
20
+ [Aeg robot](https://github.com/thoukydides/matterbridge-aeg-robot)
21
+
22
+ AEG RX 9 / Electrolux Pure i9 robot vacuum plugin for Matterbridge.
12
23
 
13
24
  ### Added
14
25
 
15
26
  - [virtual] Added virtual devices configuration mode in the Matterbridge Settings: 'Disabled', 'Light', 'Outlet', 'Switch', 'Mounted_switch'. Switch is not supported by Alexa. Mounted Switch is not supported by Apple.
16
- - [deviceTypes] Add waterHeater device type.
27
+ - [deviceTypes] Added evse, waterHeater, solarPower, batteryStorage and heatPump device type.
28
+ - [waterHeater] Added WaterHeater class to create a Water Heater Device Type in one line of code (thanks https://github.com/lboue).
17
29
 
18
30
  ### Changed
19
31
 
@@ -21,6 +33,7 @@ If you like this project and find it useful, please consider giving it a star on
21
33
  - [export]: Removed long deprecated Matter exports from matterbridge. Use matterbridge/matter.
22
34
  - [matterbridge]: Refactored initialize() and cleanup() methods.
23
35
  - [matterbridge]: Updated -help informations.
36
+ - [rvc]: Added the parameters in the RoboticVacuumCleaner class constructor.
24
37
 
25
38
  ### Fixed
26
39
 
package/README.md CHANGED
@@ -353,6 +353,15 @@ The Eve app only shows the history when the plugins run like an AccessoryPlatfor
353
353
 
354
354
  A matterbridge plugin that allows connecting Loxone devices to Matter.
355
355
 
356
+ ### [Dyson robot](https://github.com/thoukydides/matterbridge-dyson-robot)
357
+
358
+ A Matterbridge plugin that connects Dyson robot vacuums and air treatment devices.
359
+ to the Matter smart home ecosystem via their local MQTT APIs.
360
+
361
+ ### [Aeg robot](https://github.com/thoukydides/matterbridge-aeg-robot)
362
+
363
+ AEG RX 9 / Electrolux Pure i9 robot vacuum plugin for Matterbridge.
364
+
356
365
  ## How to install and add a plugin with the frontend (best option)
357
366
 
358
367
  Just open the frontend on the link provided in the log, select a plugin and click install.
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ export * from './matterbridgePlatform.js';
11
11
  export * from './matterbridgeAccessoryPlatform.js';
12
12
  export * from './matterbridgeDynamicPlatform.js';
13
13
  export * from './roboticVacuumCleaner.js';
14
+ export * from './waterHeater.js';
14
15
  export { addVirtualDevice } from './helpers.js';
15
16
  const log = new AnsiLogger({ logName: 'Main', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
16
17
  async function main() {
@@ -214,6 +214,7 @@ export class Matterbridge extends EventEmitter {
214
214
  }
215
215
  async initialize() {
216
216
  this.emit('initialize_started');
217
+ this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
217
218
  if (hasParameter('service'))
218
219
  this.restartMode = 'service';
219
220
  if (hasParameter('docker'))
@@ -240,7 +241,6 @@ export class Matterbridge extends EventEmitter {
240
241
  this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
241
242
  this.environment.vars.set('runtime.signals', false);
242
243
  this.environment.vars.set('runtime.exitcode', false);
243
- this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
244
244
  this.registerProcessHandlers();
245
245
  try {
246
246
  this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
@@ -289,7 +289,15 @@ export class Matterbridge extends EventEmitter {
289
289
  await fs.access(pairingFilePath, fs.constants.R_OK);
290
290
  const pairingFileContent = await fs.readFile(pairingFilePath, 'utf8');
291
291
  const pairingFileJson = JSON.parse(pairingFileContent);
292
- if (pairingFileJson.passcode && pairingFileJson.discriminator) {
292
+ if (isValidNumber(pairingFileJson.vendorId))
293
+ this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
294
+ if (isValidString(pairingFileJson.vendorName, 3))
295
+ this.aggregatorVendorName = pairingFileJson.vendorName;
296
+ if (isValidNumber(pairingFileJson.productId))
297
+ this.aggregatorProductId = VendorId(pairingFileJson.productId);
298
+ if (isValidString(pairingFileJson.productName, 3))
299
+ this.aggregatorProductName = pairingFileJson.productName;
300
+ if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
293
301
  this.passcode = pairingFileJson.passcode;
294
302
  this.discriminator = pairingFileJson.discriminator;
295
303
  this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
@@ -1072,8 +1080,8 @@ export class Matterbridge extends EventEmitter {
1072
1080
  await this.matterbridgeContext?.clearAll();
1073
1081
  this.log.info('Matter storage reset done! Remove the bridge from the controller.');
1074
1082
  }
1075
- await withTimeout(this.stopMatterStorage(), 10000, false);
1076
- await withTimeout(this.frontend.stop(), 10000, false);
1083
+ await this.stopMatterStorage();
1084
+ await this.frontend.stop();
1077
1085
  try {
1078
1086
  Logger.removeLogger('matterfilelogger');
1079
1087
  }
@@ -1641,6 +1649,7 @@ export class Matterbridge extends EventEmitter {
1641
1649
  async createAggregatorNode(storageContext) {
1642
1650
  this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
1643
1651
  const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
1652
+ this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
1644
1653
  return aggregatorNode;
1645
1654
  }
1646
1655
  async addBridgedEndpoint(pluginName, device) {
@@ -1932,11 +1941,11 @@ export class Matterbridge extends EventEmitter {
1932
1941
  this.log.info(`Created ${name}: ${path}`);
1933
1942
  }
1934
1943
  catch (err) {
1935
- this.log.error(`Error creating ${name}: ${err}`);
1944
+ this.log.error(`Error creating dir ${name} path ${path}: ${err}`);
1936
1945
  }
1937
1946
  }
1938
1947
  else {
1939
- this.log.error(`Error accessing ${name}: ${err}`);
1948
+ this.log.error(`Error accessing dir ${name} path ${path}: ${err}`);
1940
1949
  }
1941
1950
  }
1942
1951
  }
@@ -12,6 +12,7 @@ import { ModeBase } from '@matter/main/clusters/mode-base';
12
12
  import { RvcRunMode } from '@matter/main/clusters/rvc-run-mode';
13
13
  import { RvcOperationalState } from '@matter/main/clusters/rvc-operational-state';
14
14
  import { ServiceArea } from '@matter/main/clusters/service-area';
15
+ import { WaterHeaterManagement } from '@matter/main/clusters/water-heater-management';
15
16
  import { IdentifyServer } from '@matter/main/behaviors/identify';
16
17
  import { OnOffServer } from '@matter/main/behaviors/on-off';
17
18
  import { LevelControlServer } from '@matter/main/behaviors/level-control';
@@ -29,6 +30,8 @@ import { RvcRunModeServer } from '@matter/main/behaviors/rvc-run-mode';
29
30
  import { RvcCleanModeServer } from '@matter/main/behaviors/rvc-clean-mode';
30
31
  import { RvcOperationalStateServer } from '@matter/main/behaviors/rvc-operational-state';
31
32
  import { ServiceAreaServer } from '@matter/main/behaviors/service-area';
33
+ import { WaterHeaterModeServer } from '@matter/main/behaviors/water-heater-mode';
34
+ import { WaterHeaterManagementServer } from '@matter/main/behaviors/water-heater-management';
32
35
  export class MatterbridgeServerDevice {
33
36
  log;
34
37
  commandHandler;
@@ -174,6 +177,14 @@ export class MatterbridgeServerDevice {
174
177
  this.log.info(`Selecting areas ${newAreas} (endpoint ${this.endpointId}.${this.endpointNumber})`);
175
178
  this.commandHandler.executeHandler('selectAreas', { request: { newAreas }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } });
176
179
  }
180
+ boost({ boostInfo }) {
181
+ this.log.info(`Boost (endpoint ${this.endpointId}.${this.endpointNumber})`);
182
+ this.commandHandler.executeHandler('boost', { request: { boostInfo }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } });
183
+ }
184
+ cancelBoost() {
185
+ this.log.info(`Cancel boost (endpoint ${this.endpointId}.${this.endpointNumber})`);
186
+ this.commandHandler.executeHandler('cancelBoost', { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } });
187
+ }
177
188
  }
178
189
  export class MatterbridgeServer extends Behavior {
179
190
  static id = 'matterbridge';
@@ -538,3 +549,31 @@ export class MatterbridgeServiceAreaServer extends ServiceAreaServer {
538
549
  return { status: ServiceArea.SelectAreasStatus.Success, statusText: 'Succesfully selected new areas' };
539
550
  }
540
551
  }
552
+ export class MatterbridgeWaterHeaterManagementServer extends WaterHeaterManagementServer {
553
+ boost({ boostInfo }) {
554
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
555
+ device.boost({ boostInfo });
556
+ device.log.info(`MatterbridgeWaterHeaterManagementServer boost called with: ${JSON.stringify(boostInfo)}`);
557
+ this.state.boostState = WaterHeaterManagement.BoostState.Active;
558
+ }
559
+ cancelBoost() {
560
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
561
+ device.cancelBoost();
562
+ device.log.info(`MatterbridgeWaterHeaterManagementServer cancelBoost called`);
563
+ this.state.boostState = WaterHeaterManagement.BoostState.Inactive;
564
+ }
565
+ }
566
+ export class MatterbridgeWaterHeaterModeServer extends WaterHeaterModeServer {
567
+ changeToMode({ newMode }) {
568
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
569
+ const supported = this.state.supportedModes.find((mode) => mode.mode === newMode);
570
+ if (!supported) {
571
+ device.log.error(`MatterbridgeWaterHeaterModeServer changeToMode called with unsupported newMode: ${newMode}`);
572
+ return { status: ModeBase.ModeChangeStatus.UnsupportedMode, statusText: 'Unsupported mode' };
573
+ }
574
+ device.changeToMode({ newMode });
575
+ this.state.currentMode = newMode;
576
+ device.log.info(`MatterbridgeWaterHeaterModeServer changeToMode called with newMode ${newMode} => ${supported.label}`);
577
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
578
+ }
579
+ }
@@ -3,15 +3,15 @@ import { roboticVacuumCleaner } from './matterbridgeDeviceTypes.js';
3
3
  import { MatterbridgeRvcCleanModeServer, MatterbridgeRvcOperationalStateServer, MatterbridgeRvcRunModeServer, MatterbridgeServiceAreaServer } from './matterbridgeBehaviors.js';
4
4
  import { PowerSource, RvcRunMode, RvcCleanMode, RvcOperationalState } from '@matter/main/clusters';
5
5
  export class RoboticVacuumCleaner extends MatterbridgeEndpoint {
6
- constructor(name, serial) {
6
+ constructor(name, serial, currentRunMode, supportedRunModes, currentCleanMode, supportedCleanModes, currentPhase = null, phaseList = null, operationalState, operationalStateList, supportedAreas, selectedAreas, currentArea) {
7
7
  super(roboticVacuumCleaner, { uniqueStorageKey: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
8
8
  this.createDefaultIdentifyClusterServer()
9
9
  .createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Robot Vacuum Cleaner')
10
10
  .createDefaultPowerSourceRechargeableBatteryClusterServer(80, PowerSource.BatChargeLevel.Ok, 5900)
11
- .createDefaultRvcRunModeClusterServer()
12
- .createDefaultRvcCleanModeClusterServer()
13
- .createDefaultRvcOperationalStateClusterServer()
14
- .createDefaultServiceAreaClusterServer();
11
+ .createDefaultRvcRunModeClusterServer(currentRunMode, supportedRunModes)
12
+ .createDefaultRvcCleanModeClusterServer(currentCleanMode, supportedCleanModes)
13
+ .createDefaultRvcOperationalStateClusterServer(phaseList, currentPhase, operationalStateList, operationalState)
14
+ .createDefaultServiceAreaClusterServer(supportedAreas, selectedAreas, currentArea);
15
15
  }
16
16
  createDefaultRvcRunModeClusterServer(currentMode, supportedModes) {
17
17
  this.behaviors.require(MatterbridgeRvcRunModeServer, {
@@ -0,0 +1,45 @@
1
+ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
2
+ import { waterHeater } from './matterbridgeDeviceTypes.js';
3
+ import { MatterbridgeWaterHeaterManagementServer, MatterbridgeWaterHeaterModeServer } from './matterbridgeBehaviors.js';
4
+ import { WaterHeaterManagement, WaterHeaterMode } from '@matter/main/clusters';
5
+ export class WaterHeater extends MatterbridgeEndpoint {
6
+ constructor(name, serial, waterTemperature = 50, minHeatSetpointLimit = 20, maxHeatSetpointLimit = 80, heaterTypes = { immersionElement1: true }) {
7
+ super(waterHeater, { uniqueStorageKey: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
8
+ this.createDefaultIdentifyClusterServer()
9
+ .createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Water Heater')
10
+ .createDefaultPowerSourceWiredClusterServer()
11
+ .createDefaultHeatingThermostatClusterServer(waterTemperature, waterTemperature, minHeatSetpointLimit, maxHeatSetpointLimit)
12
+ .createDefaultWaterHeaterManagementClusterServer(heaterTypes)
13
+ .createDefaultWaterHeaterModeClusterServer();
14
+ }
15
+ createDefaultWaterHeaterManagementClusterServer(heaterTypes, heatDemand, tankPercentage, boostState) {
16
+ this.behaviors.require(MatterbridgeWaterHeaterManagementServer.with(WaterHeaterManagement.Feature.TankPercent), {
17
+ heaterTypes: heaterTypes ?? { immersionElement1: true },
18
+ heatDemand: heatDemand ?? {},
19
+ tankPercentage: tankPercentage ?? 100,
20
+ boostState: boostState ?? WaterHeaterManagement.BoostState.Inactive,
21
+ });
22
+ return this;
23
+ }
24
+ createDefaultWaterHeaterModeClusterServer(currentMode, supportedModes) {
25
+ this.behaviors.require(MatterbridgeWaterHeaterModeServer, {
26
+ supportedModes: supportedModes ?? [
27
+ { label: 'Auto', mode: 1, modeTags: [{ value: WaterHeaterMode.ModeTag.Auto }] },
28
+ { label: 'Quick', mode: 2, modeTags: [{ value: WaterHeaterMode.ModeTag.Quick }] },
29
+ { label: 'Quiet', mode: 3, modeTags: [{ value: WaterHeaterMode.ModeTag.Quiet }] },
30
+ { label: 'LowNoise', mode: 4, modeTags: [{ value: WaterHeaterMode.ModeTag.LowNoise }] },
31
+ { label: 'LowEnergy', mode: 5, modeTags: [{ value: WaterHeaterMode.ModeTag.LowEnergy }] },
32
+ { label: 'Vacation', mode: 6, modeTags: [{ value: WaterHeaterMode.ModeTag.Vacation }] },
33
+ { label: 'Min', mode: 7, modeTags: [{ value: WaterHeaterMode.ModeTag.Min }] },
34
+ { label: 'Max', mode: 8, modeTags: [{ value: WaterHeaterMode.ModeTag.Max }] },
35
+ { label: 'Night', mode: 9, modeTags: [{ value: WaterHeaterMode.ModeTag.Night }] },
36
+ { label: 'Day', mode: 10, modeTags: [{ value: WaterHeaterMode.ModeTag.Day }] },
37
+ { label: 'Off', mode: 11, modeTags: [{ value: WaterHeaterMode.ModeTag.Off }] },
38
+ { label: 'Manual', mode: 12, modeTags: [{ value: WaterHeaterMode.ModeTag.Manual }] },
39
+ { label: 'Timed', mode: 13, modeTags: [{ value: WaterHeaterMode.ModeTag.Timed }] },
40
+ ],
41
+ currentMode: currentMode ?? 1,
42
+ });
43
+ return this;
44
+ }
45
+ }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.0.3-dev-20250518-a216d52",
3
+ "version": "3.0.3-dev-20250519-acb2774",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge",
9
- "version": "3.0.3-dev-20250518-a216d52",
9
+ "version": "3.0.3-dev-20250519-acb2774",
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.3-dev-20250518-a216d52",
3
+ "version": "3.0.3-dev-20250519-acb2774",
4
4
  "description": "Matterbridge plugin manager for Matter",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",