matterbridge 3.1.1-dev-20250630-ea4c889 → 3.1.1-dev-20250703-80c685d

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,11 +8,12 @@ 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.1.1] - 2025-07-??
11
+ ## [3.1.1] - 2025-07-01
12
12
 
13
13
  ### Development Breaking Changes
14
14
 
15
- - [devices]: The single devices (i.e. Rvc, Evse etc...) are only exported from matterbridge/devices. Please update your imports to use the new export path. Refer to the [documentation](README-DEV.md) for details on imports.
15
+ - [devices]: The single devices (i.e. Rvc, Evse etc...) are only exported from `matterbridge/devices`. Please update your imports to use the new export path. Refer to the [documentation](README-DEV.md) for details on imports.
16
+ - [MatterbridgeEndpoint]: Added the mode property: `server` will make the device indipendent from its plugin. It has its own server node: QRCode, Fabrics and Sessions are visible in the Devices section of the Home page. This a workaround for the Rvc Apple issue. So the Rvc (like any other device) can be paired directly to the controller and is a native Matter device and is not bridged.
16
17
 
17
18
  ### Added
18
19
 
@@ -25,6 +26,9 @@ If you like this project and find it useful, please consider giving it a star on
25
26
  ### Changed
26
27
 
27
28
  - [package]: Updated dependencies.
29
+ - [frontend]: Added all esa devices.
30
+ - [frontend]: New default values: select on the home page and icon view on devices page.
31
+ - [matter.js]: Bumped `matter.js` to 0.15.1 (https://github.com/project-chip/matter.js/discussions/2220). Great job matter.js!
28
32
 
29
33
  <a href="https://www.buymeacoffee.com/luligugithub">
30
34
  <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
package/README-DOCKER.md CHANGED
@@ -120,11 +120,11 @@ docker compose down
120
120
 
121
121
  ### Update with docker compose
122
122
 
123
- This will pull the new matterbridge image and restart the matterbridge container.
123
+ This will pull the new matterbridge image and restart only the matterbridge container.
124
124
 
125
125
  ```bash
126
126
  docker compose pull matterbridge
127
- docker compose up -d --no-deps matterbridge
127
+ docker compose up -d --no-deps --force-recreate matterbridge
128
128
  ```
129
129
 
130
130
  ### Inspect the container
package/README.md CHANGED
@@ -69,15 +69,18 @@ https://matter-smarthome.de/en/interview/an-alternative-to-the-official-matter-s
69
69
  To run Matterbridge, you need either a [Node.js](https://nodejs.org/en) environment or [Docker](https://docs.docker.com/get-started/get-docker/) installed on your system.
70
70
 
71
71
  If you don't have Node.js already install, please use this method to install it on a debian device: https://github.com/nodesource/distributions.
72
- The supported versions of node are 18, 20 and 22. Please install node 22 LTS.
73
- Node 23 is not supported.
72
+ The supported versions of node are 18, 20 and 22. Please install Node.js 22 LTS.
73
+ Node.js 23, like all odd-numbered versions, is not supported.
74
74
  Nvm is not a good choice and should not be used for production.
75
75
 
76
76
  If you don't have Docker already install, please use this method to install it on a debian device: https://docs.docker.com/desktop/setup/install/linux/debian/.
77
+ After follow the guidelines for the [Docker configurations](README-DOCKER.md).
78
+
79
+ I suggest using Docker for its simplicity.
77
80
 
78
81
  Since as stated in the Matter specifications "Matter aims to build a universal IPv6-based communication protocol for smart home devices", ipv6 should be enabled in the network.
79
82
 
80
- Avoid using VLAN and firewall blocking the communications between the controller and Matterbridge.
83
+ Avoid using VLAN and firewall blocking the communications between the controllers and Matterbridge.
81
84
 
82
85
  ## Installation
83
86
 
@@ -5,7 +5,7 @@ import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
5
5
  import { deviceEnergyManagement, electricalSensor, batteryStorage, powerSource } from '../matterbridgeDeviceTypes.js';
6
6
  export class BatteryStorage extends MatterbridgeEndpoint {
7
7
  constructor(name, serial, batPercentRemaining = 100, batChargeLevel = PowerSource.BatChargeLevel.Ok, voltage = null, current = null, power = null, energyImported = null, energyExported = null, absMinPower = 0, absMaxPower = 0) {
8
- super([batteryStorage, powerSource, electricalSensor, deviceEnergyManagement], { id: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
8
+ super([batteryStorage, electricalSensor, deviceEnergyManagement], { id: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
9
9
  this.createDefaultIdentifyClusterServer()
10
10
  .createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Solar Power')
11
11
  .createDefaultPowerTopologyClusterServer()
@@ -7,14 +7,14 @@ import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
7
7
  import { MatterbridgeServer } from '../matterbridgeBehaviors.js';
8
8
  import { deviceEnergyManagement, electricalSensor, evse, powerSource } from '../matterbridgeDeviceTypes.js';
9
9
  export class Evse extends MatterbridgeEndpoint {
10
- constructor(name, serial, currentMode, supportedModes, state, supplyState, faultState, absMinPower, absMaxPower) {
10
+ constructor(name, serial, currentMode, supportedModes, state, supplyState, faultState, voltage = null, current = null, power = null, energy = null, absMinPower, absMaxPower) {
11
11
  super([evse, powerSource, electricalSensor, deviceEnergyManagement], { uniqueStorageKey: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
12
12
  this.createDefaultIdentifyClusterServer()
13
- .createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge EVSE')
13
+ .createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Evse')
14
14
  .createDefaultPowerSourceWiredClusterServer()
15
15
  .createDefaultPowerTopologyClusterServer()
16
- .createDefaultElectricalPowerMeasurementClusterServer()
17
- .createDefaultElectricalEnergyMeasurementClusterServer()
16
+ .createDefaultElectricalPowerMeasurementClusterServer(voltage, current, power)
17
+ .createDefaultElectricalEnergyMeasurementClusterServer(energy, 0)
18
18
  .createDefaultDeviceEnergyManagementClusterServer(DeviceEnergyManagement.EsaType.Evse, false, DeviceEnergyManagement.EsaState.Online, absMinPower, absMaxPower)
19
19
  .createDefaultDeviceEnergyManagementModeClusterServer()
20
20
  .createDefaultEnergyEvseClusterServer(state, supplyState, faultState)
@@ -23,16 +23,17 @@ export class Evse extends MatterbridgeEndpoint {
23
23
  }
24
24
  createDefaultEnergyEvseClusterServer(state, supplyState, faultState) {
25
25
  this.behaviors.require(MatterbridgeEnergyEvseServer, {
26
- state: state ?? EnergyEvse.State.NotPluggedIn,
27
- supplyState: supplyState ?? EnergyEvse.SupplyState.ChargingEnabled,
28
- faultState: faultState ?? EnergyEvse.FaultState.NoError,
29
- chargingEnabledUntil: 0,
30
- circuitCapacity: 0,
31
- minimumChargeCurrent: 6000,
32
- maximumChargeCurrent: 0,
26
+ state: state !== undefined ? state : EnergyEvse.State.NotPluggedIn,
27
+ supplyState: supplyState !== undefined ? supplyState : EnergyEvse.SupplyState.ChargingEnabled,
28
+ faultState: faultState !== undefined ? faultState : EnergyEvse.FaultState.NoError,
29
+ chargingEnabledUntil: null,
30
+ circuitCapacity: 32_000,
31
+ minimumChargeCurrent: 6_000,
32
+ maximumChargeCurrent: 32_000,
33
+ userMaximumChargeCurrent: 32_000,
33
34
  sessionId: null,
34
- sessionDuration: 0,
35
- sessionEnergyCharged: 0,
35
+ sessionDuration: null,
36
+ sessionEnergyCharged: null,
36
37
  });
37
38
  return this;
38
39
  }
@@ -1,20 +1,26 @@
1
1
  import { ModeBase } from '@matter/main/clusters/mode-base';
2
2
  import { WaterHeaterManagement } from '@matter/main/clusters/water-heater-management';
3
3
  import { WaterHeaterMode } from '@matter/main/clusters/water-heater-mode';
4
+ import { DeviceEnergyManagement } from '@matter/main/clusters/device-energy-management';
4
5
  import { WaterHeaterManagementServer } from '@matter/main/behaviors/water-heater-management';
5
6
  import { WaterHeaterModeServer } from '@matter/main/behaviors/water-heater-mode';
6
7
  import { MatterbridgeServer } from '../matterbridgeBehaviors.js';
7
8
  import { electricalSensor, powerSource, waterHeater } from '../matterbridgeDeviceTypes.js';
8
9
  import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
9
10
  export class WaterHeater extends MatterbridgeEndpoint {
10
- constructor(name, serial, waterTemperature = 50, targetWaterTemperature = 55, minHeatSetpointLimit = 20, maxHeatSetpointLimit = 80, heaterTypes = { immersionElement1: true }, tankPercentage = 90) {
11
+ constructor(name, serial, waterTemperature = 50, targetWaterTemperature = 55, minHeatSetpointLimit = 20, maxHeatSetpointLimit = 80, heaterTypes = { immersionElement1: true }, tankPercentage = 90, voltage = null, current = null, power = null, energy = null, absMinPower = 0, absMaxPower = 0) {
11
12
  super([waterHeater, powerSource, electricalSensor], { uniqueStorageKey: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
12
13
  this.createDefaultIdentifyClusterServer()
13
14
  .createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Water Heater')
14
15
  .createDefaultPowerSourceWiredClusterServer()
15
16
  .createDefaultHeatingThermostatClusterServer(waterTemperature, targetWaterTemperature, minHeatSetpointLimit, maxHeatSetpointLimit)
16
17
  .createDefaultWaterHeaterManagementClusterServer(heaterTypes, {}, tankPercentage)
17
- .createDefaultWaterHeaterModeClusterServer();
18
+ .createDefaultWaterHeaterModeClusterServer()
19
+ .createDefaultPowerTopologyClusterServer()
20
+ .createDefaultElectricalPowerMeasurementClusterServer(voltage, current, power)
21
+ .createDefaultElectricalEnergyMeasurementClusterServer(energy)
22
+ .createDefaultDeviceEnergyManagementClusterServer(DeviceEnergyManagement.EsaType.WaterHeating, true, DeviceEnergyManagement.EsaState.Online, absMinPower, absMaxPower)
23
+ .createDefaultDeviceEnergyManagementModeClusterServer();
18
24
  }
19
25
  createDefaultWaterHeaterManagementClusterServer(heaterTypes, heatDemand, tankPercentage, boostState) {
20
26
  this.behaviors.require(MatterbridgeWaterHeaterManagementServer.with(WaterHeaterManagement.Feature.TankPercent), {
package/dist/frontend.js CHANGED
@@ -566,6 +566,8 @@ export class Frontend {
566
566
  return false;
567
567
  if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
568
568
  return device.getAttribute(BridgedDeviceBasicInformation.Cluster.id, 'reachable');
569
+ if (device.mode === 'server' && device.serverNode && device.serverNode.state.basicInformation.reachable !== undefined)
570
+ return device.serverNode.state.basicInformation.reachable;
569
571
  if (this.matterbridge.bridgeMode === 'childbridge')
570
572
  return true;
571
573
  return false;
@@ -592,6 +594,17 @@ export class Frontend {
592
594
  return powerSource(child);
593
595
  }
594
596
  }
597
+ getMatterDataFromDevice(device) {
598
+ if (device.mode === 'server' && device.serverNode && device.serverContext) {
599
+ return {
600
+ commissioned: device.serverNode.state.commissioning.commissioned,
601
+ qrPairingCode: device.serverNode.state.commissioning.pairingCodes.qrPairingCode,
602
+ manualPairingCode: device.serverNode.state.commissioning.pairingCodes.manualPairingCode,
603
+ fabricInformations: this.matterbridge.sanitizeFabricInformations(Object.values(device.serverNode.state.commissioning.fabrics)),
604
+ sessionInformations: this.matterbridge.sanitizeSessionInformation(Object.values(device.serverNode.state.sessions.sessions)),
605
+ };
606
+ }
607
+ }
595
608
  getClusterTextFromDevice(device) {
596
609
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
597
610
  return '';
@@ -766,12 +779,11 @@ export class Frontend {
766
779
  }
767
780
  async getDevices(pluginName) {
768
781
  const devices = [];
769
- this.matterbridge.devices.forEach(async (device) => {
782
+ for (const device of this.matterbridge.devices.array()) {
770
783
  if (pluginName && pluginName !== device.plugin)
771
- return;
784
+ continue;
772
785
  if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
773
- return;
774
- const cluster = this.getClusterTextFromDevice(device);
786
+ continue;
775
787
  devices.push({
776
788
  pluginName: device.plugin,
777
789
  type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
@@ -783,9 +795,10 @@ export class Frontend {
783
795
  uniqueId: device.uniqueId,
784
796
  reachable: this.getReachability(device),
785
797
  powerSource: this.getPowerSource(device),
786
- cluster: cluster,
798
+ matter: this.getMatterDataFromDevice(device),
799
+ cluster: this.getClusterTextFromDevice(device),
787
800
  });
788
- });
801
+ }
789
802
  return devices;
790
803
  }
791
804
  getClusters(pluginName, endpointNumber) {
@@ -1849,8 +1849,8 @@ export class Matterbridge extends EventEmitter {
1849
1849
  };
1850
1850
  });
1851
1851
  }
1852
- sanitizeSessionInformation(session) {
1853
- return session
1852
+ sanitizeSessionInformation(sessions) {
1853
+ return sessions
1854
1854
  .filter((session) => session.isPeerActive)
1855
1855
  .map((session) => {
1856
1856
  return {
@@ -215,8 +215,8 @@ export class MatterbridgeEndpoint extends Endpoint {
215
215
  this.commandHandler.addHandler(command, handler);
216
216
  return this;
217
217
  }
218
- async executeCommandHandler(command, request) {
219
- await this.commandHandler.executeHandler(command, { request });
218
+ async executeCommandHandler(command, request, cluster, attributes, endpoint) {
219
+ await this.commandHandler.executeHandler(command, { request, cluster, attributes, endpoint });
220
220
  }
221
221
  async invokeBehaviorCommand(cluster, command, params) {
222
222
  await invokeBehaviorCommand(this, cluster, command, params);
@@ -1116,8 +1116,8 @@ export class MatterbridgeEndpoint extends Endpoint {
1116
1116
  accuracyRanges: [{ rangeMin: Number.MIN_SAFE_INTEGER, rangeMax: Number.MAX_SAFE_INTEGER, fixedMax: 1 }],
1117
1117
  },
1118
1118
  cumulativeEnergyReset: null,
1119
- cumulativeEnergyImported: energyImported ? { energy: energyImported } : null,
1120
- cumulativeEnergyExported: energyExported ? { energy: energyExported } : null,
1119
+ cumulativeEnergyImported: energyImported !== null && energyImported >= 0 ? { energy: energyImported } : null,
1120
+ cumulativeEnergyExported: energyExported !== null && energyExported >= 0 ? { energy: energyExported } : null,
1121
1121
  });
1122
1122
  return this;
1123
1123
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "files": {
3
3
  "main.css": "./static/css/main.944b63c3.css",
4
- "main.js": "./static/js/main.2486c3e3.js",
4
+ "main.js": "./static/js/main.1d25e0d8.js",
5
5
  "static/js/453.d855a71b.chunk.js": "./static/js/453.d855a71b.chunk.js",
6
6
  "static/media/roboto-latin-700-normal.woff2": "./static/media/roboto-latin-700-normal.c4d6cab43bec89049809.woff2",
7
7
  "static/media/roboto-latin-500-normal.woff2": "./static/media/roboto-latin-500-normal.599f66a60bdf974e578e.woff2",
@@ -77,11 +77,11 @@
77
77
  "static/media/roboto-greek-ext-300-normal.woff": "./static/media/roboto-greek-ext-300-normal.60729cafbded24073dfb.woff",
78
78
  "index.html": "./index.html",
79
79
  "main.944b63c3.css.map": "./static/css/main.944b63c3.css.map",
80
- "main.2486c3e3.js.map": "./static/js/main.2486c3e3.js.map",
80
+ "main.1d25e0d8.js.map": "./static/js/main.1d25e0d8.js.map",
81
81
  "453.d855a71b.chunk.js.map": "./static/js/453.d855a71b.chunk.js.map"
82
82
  },
83
83
  "entrypoints": [
84
84
  "static/css/main.944b63c3.css",
85
- "static/js/main.2486c3e3.js"
85
+ "static/js/main.1d25e0d8.js"
86
86
  ]
87
87
  }
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="./"><link rel="icon" href="./matterbridge 32x32.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>Matterbridge</title><link rel="manifest" href="./manifest.json"/><script defer="defer" src="./static/js/main.2486c3e3.js"></script><link href="./static/css/main.944b63c3.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="./"><link rel="icon" href="./matterbridge 32x32.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>Matterbridge</title><link rel="manifest" href="./manifest.json"/><script defer="defer" src="./static/js/main.1d25e0d8.js"></script><link href="./static/css/main.944b63c3.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>