matterbridge 3.3.4-dev-20251020-df40d12 → 3.3.4-dev-20251022-681420c

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
@@ -14,6 +14,10 @@ The project will evolve to a multi-threaded architecture (the CLI will become th
14
14
 
15
15
  - matterbridge;
16
16
  - frontend;
17
+ - plugins;
18
+ - devices;
19
+ - check_updates;
20
+ - npm_install;
17
21
  - all plugins in bridge mode;
18
22
  - each plugin in childbridge mode;
19
23
 
@@ -33,13 +37,18 @@ Advantages:
33
37
 
34
38
  - [frontend]: Added debounce to MatterSettings.
35
39
  - [cli]: Bumped `cli` version to 3.0.0 with backport of Traker and Inspector from thread module.
40
+ - [powerSource]: Added MatterbridgePowerSourceServer. It initializes the enpointList of the PowerSource cluster.
41
+ - [thread]: Added BroadcastServer to Matterbridge.
42
+ - [service]: Added configuration [guide](README-SERVICE-LOCAL.md) to run matterbridge as a daemon with systemctl (Linux only) and with local global node_modules (no sudo required).
36
43
 
37
44
  ### Changed
38
45
 
39
46
  - [package]: Updated dependencies.
40
47
  - [package]: Optimized @matter imports.
48
+ - [endpoint]: Optimized memory requirements.
41
49
  - [matter]: Bumped `matter.js` version to 0.15.6. Thanks matter.js!
42
50
  - [frontend]: Bumped `frontend` version to 3.2.3.
51
+ - [thread]: Bumped `BroadcastServer` version to 1.0.1.
43
52
 
44
53
  <a href="https://www.buymeacoffee.com/luligugithub">
45
54
  <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
package/README-DOCKER.md CHANGED
@@ -53,7 +53,8 @@ This will create the required directories in your home directory if they don't e
53
53
  cd ~
54
54
  mkdir -p ~/Matterbridge
55
55
  mkdir -p ~/.matterbridge
56
- sudo chown -R $USER:$USER ~/Matterbridge ~/.matterbridge
56
+ mkdir -p ~/.mattercert
57
+ sudo chown -R $USER:$USER ~/Matterbridge ~/.matterbridge ~/.mattercert
57
58
  ```
58
59
 
59
60
  You may need to adapt the script to your setup.
@@ -77,6 +78,7 @@ The container must have full access to the host network (needed for mdns and Mat
77
78
  sudo docker run --name matterbridge \
78
79
  -v ~/Matterbridge:/root/Matterbridge \
79
80
  -v ~/.matterbridge:/root/.matterbridge \
81
+ -v ~/.mattercert:/root/.mattercert \
80
82
  --network host --restart always -d luligu/matterbridge:latest
81
83
  ```
82
84
 
@@ -96,6 +98,7 @@ services:
96
98
  volumes:
97
99
  - "${HOME}/Matterbridge:/root/Matterbridge" # Mounts the Matterbridge plugin directory
98
100
  - "${HOME}/.matterbridge:/root/.matterbridge" # Mounts the Matterbridge storage directory
101
+ - "${HOME}/.mattercert:/root/.mattercert" # Mounts the Matterbridge certificate directory
99
102
  ```
100
103
 
101
104
  Copy it in the home directory or edit the existing one to add the matterbridge service.
@@ -0,0 +1,226 @@
1
+ # <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge systemd configuration with local global node_modules
2
+
3
+ [![npm version](https://img.shields.io/npm/v/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
4
+ [![npm downloads](https://img.shields.io/npm/dt/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
5
+ [![Docker Version](https://img.shields.io/docker/v/luligu/matterbridge?label=docker%20version&sort=semver)](https://hub.docker.com/r/luligu/matterbridge)
6
+ [![Docker Pulls](https://img.shields.io/docker/pulls/luligu/matterbridge.svg)](https://hub.docker.com/r/luligu/matterbridge)
7
+ ![Node.js CI](https://github.com/Luligu/matterbridge/actions/workflows/build.yml/badge.svg)
8
+ ![CodeQL](https://github.com/Luligu/matterbridge/actions/workflows/codeql.yml/badge.svg)
9
+ [![codecov](https://codecov.io/gh/Luligu/matterbridge/branch/main/graph/badge.svg)](https://codecov.io/gh/Luligu/matterbridge)
10
+
11
+ [![power by](https://img.shields.io/badge/powered%20by-matter--history-blue)](https://www.npmjs.com/package/matter-history)
12
+ [![power by](https://img.shields.io/badge/powered%20by-node--ansi--logger-blue)](https://www.npmjs.com/package/node-ansi-logger)
13
+ [![power by](https://img.shields.io/badge/powered%20by-node--persist--manager-blue)](https://www.npmjs.com/package/node-persist-manager)
14
+
15
+ ---
16
+
17
+ # Advanced configuration
18
+
19
+ ## Run matterbridge as a daemon with systemctl (Linux only) with local global node_modules
20
+
21
+ The easiest way to add systemctl is to use [Matterbridge service cli for linux](https://github.com/Luligu/mb-service-linux).
22
+
23
+ If your setup is too complex or you prefer to do it manually follow this method. You can still use mb-service to manage systemd after.
24
+
25
+ ### First create the Matterbridge directories and set the correct permissions
26
+
27
+ This will create the required directories if they don't exist
28
+
29
+ ```bash
30
+ cd ~
31
+ sudo systemctl stop matterbridge # ✅ Safe precaution
32
+ mkdir -p ~/Matterbridge ~/.matterbridge ~/.mattercert ~/.npm-global # ✅ Creates all needed dirs
33
+ chown -R $USER:$USER ~/Matterbridge ~/.matterbridge ~/.mattercert ~/.npm-global # ✅ Ensures ownership
34
+ chmod -R 755 ~/Matterbridge ~/.matterbridge ~/.mattercert ~/.npm-global # ✅ Secure permissions
35
+ NPM_CONFIG_PREFIX=~/.npm-global npm install matterbridge --omit=dev --verbose --global # ✅ Install, no sudo
36
+ ```
37
+
38
+ ### Then create a system-wide symlink
39
+
40
+ ```bash
41
+ sudo ln -sf /home/$USER/.npm-global/bin/matterbridge /usr/local/bin/matterbridge
42
+ which matterbridge
43
+ matterbridge --version
44
+ ```
45
+
46
+ ### Then create a systemctl configuration file for Matterbridge
47
+
48
+ Create a systemctl configuration file for Matterbridge
49
+
50
+ ```bash
51
+ sudo nano /etc/systemd/system/matterbridge.service
52
+ ```
53
+
54
+ Add the following to this file, replacing 5 times (!) USER with your user name (e.g. WorkingDirectory=/home/pi/Matterbridge, User=pi and Group=pi and Environment="NPM_CONFIG_PREFIX=/home/pi/.npm-global"):
55
+
56
+ ```
57
+ [Unit]
58
+ Description=matterbridge
59
+ After=network.target
60
+ Wants=network.target
61
+
62
+ [Service]
63
+ Type=simple
64
+ Environment="NPM_CONFIG_PREFIX=/home/<USER>/.npm-global"
65
+ ExecStart=matterbridge --service --nosudo
66
+ WorkingDirectory=/home/<USER>/Matterbridge
67
+ StandardOutput=inherit
68
+ StandardError=inherit
69
+ Restart=always
70
+ User=<USER>
71
+ Group=<USER>
72
+
73
+ [Install]
74
+ WantedBy=multi-user.target
75
+ ```
76
+
77
+ If you use the frontend with **-ssl** -frontend 443 and get an error message: "Port 443 requires elevated privileges",
78
+ add this:
79
+
80
+ ```
81
+ [Service]
82
+ AmbientCapabilities=CAP_NET_BIND_SERVICE
83
+ ```
84
+
85
+ If you use the **matterbridge-bthome** plugin add this:
86
+
87
+ ```
88
+ [Service]
89
+ AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_NET_ADMIN
90
+ ```
91
+
92
+ Now and if you modify matterbridge.service after, run:
93
+
94
+ ```bash
95
+ sudo systemctl daemon-reload
96
+ sudo systemctl restart matterbridge.service
97
+ ```
98
+
99
+ ### Start Matterbridge
100
+
101
+ ```bash
102
+ sudo systemctl start matterbridge
103
+ ```
104
+
105
+ ### Stop Matterbridge
106
+
107
+ ```bash
108
+ sudo systemctl stop matterbridge
109
+ ```
110
+
111
+ ### Show Matterbridge status
112
+
113
+ ```bash
114
+ sudo systemctl status matterbridge.service
115
+ ```
116
+
117
+ ### Enable Matterbridge to start automatically on boot
118
+
119
+ ```bash
120
+ sudo systemctl enable matterbridge.service
121
+ ```
122
+
123
+ ### Disable Matterbridge from starting automatically on boot
124
+
125
+ ```bash
126
+ sudo systemctl disable matterbridge.service
127
+ ```
128
+
129
+ ### View the log of Matterbridge in real time (this will show the log with colors)
130
+
131
+ ```bash
132
+ sudo journalctl -u matterbridge.service -n 1000 -f --output cat
133
+ ```
134
+
135
+ ### Delete the logs older then 3 days (all of them not only the ones of Matterbridge!)
136
+
137
+ Check the space used
138
+
139
+ ```bash
140
+ sudo journalctl --disk-usage
141
+ ```
142
+
143
+ remove all log older then 3 days
144
+
145
+ ```bash
146
+ sudo journalctl --rotate
147
+ sudo journalctl --vacuum-time=3d
148
+ ```
149
+
150
+ ## Prevent the journal logs to grow
151
+
152
+ If you want to make the setting permanent to prevent the journal logs to grow too much, run
153
+
154
+ ```bash
155
+ sudo nano /etc/systemd/journald.conf
156
+ ```
157
+
158
+ add
159
+
160
+ ```bash
161
+ Compress=yes # Compress logs
162
+ MaxRetentionSec=3days # Keep logs for a maximum of 3 days.
163
+ MaxFileSec=1day # Rotate logs daily within the 3-day retention period.
164
+ ForwardToSyslog=no # Disable forwarding to syslog to prevent duplicate logging.
165
+ SystemMaxUse=100M # Limit persistent logs in /var/log/journal to 100 MB.
166
+ RuntimeMaxUse=100M # Limit runtime logs in /run/log/journal to 100 MB.
167
+ ```
168
+
169
+ save it and run
170
+
171
+ ```bash
172
+ sudo systemctl restart systemd-journald
173
+ ```
174
+
175
+ ## Verify that with your distro you can run sudo npm install -g matterbridge without the password
176
+
177
+ Run the following command to verify if you can install Matterbridge globally without being prompted for a password:
178
+
179
+ ```bash
180
+ sudo npm install -g matterbridge --omit=dev
181
+ ```
182
+
183
+ If you are not prompted for a password, no further action is required.
184
+
185
+ If that is not the case, open the sudoers file for editing using visudo
186
+
187
+ ```bash
188
+ sudo visudo
189
+ ```
190
+
191
+ verify the presence of of a line
192
+
193
+ ```
194
+ @includedir /etc/sudoers.d
195
+ ```
196
+
197
+ exit and create a configuration file for sudoers
198
+
199
+ ```bash
200
+ sudo nano /etc/sudoers.d/matterbridge
201
+ ```
202
+
203
+ add this line replacing USER with your user name (e.g. radxa ALL=(ALL) NOPASSWD: ALL)
204
+
205
+ ```
206
+ <USER> ALL=(ALL) NOPASSWD: ALL
207
+ ```
208
+
209
+ or if you prefers to only give access to npm without password try with (e.g. radxa ALL=(ALL) NOPASSWD: /usr/bin/npm)
210
+
211
+ ```
212
+ <USER> ALL=(ALL) NOPASSWD: /usr/bin/npm
213
+ ```
214
+
215
+ save the file and reload the settings with:
216
+
217
+ ```bash
218
+ sudo chmod 0440 /etc/sudoers.d/matterbridge
219
+ sudo visudo -c
220
+ ```
221
+
222
+ Verify if you can install Matterbridge globally without being prompted for a password:
223
+
224
+ ```bash
225
+ sudo npm install -g matterbridge --omit=dev
226
+ ```
package/README-SERVICE.md CHANGED
@@ -28,9 +28,10 @@ This will create the required directories if they don't exist
28
28
 
29
29
  ```bash
30
30
  cd ~
31
- mkdir -p ./Matterbridge
32
- mkdir -p ./.matterbridge
33
- sudo chown -R $USER:$USER ./Matterbridge ./.matterbridge
31
+ mkdir -p ~/Matterbridge
32
+ mkdir -p ~/.matterbridge
33
+ mkdir -p ~/.mattercert
34
+ sudo chown -R $USER:$USER ~/Matterbridge ~/.matterbridge ~/.mattercert
34
35
  ```
35
36
 
36
37
  ### Then create a systemctl configuration file for Matterbridge
@@ -34,6 +34,9 @@ export class BroadcastServer extends EventEmitter {
34
34
  this.log.debug(`*Received broadcast message: ${debugStringify(data)}`);
35
35
  this.emit('broadcast_message', data);
36
36
  }
37
+ broadcast(message) {
38
+ this.broadcastChannel.postMessage(message);
39
+ }
37
40
  request(message) {
38
41
  if (message.id === undefined) {
39
42
  message.id = this.getUniqueId();
package/dist/cli.js CHANGED
@@ -2,7 +2,6 @@ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
2
  console.log('\u001B[32mCli loaded.\u001B[40;0m');
3
3
  import { AnsiLogger } from 'node-ansi-logger';
4
4
  import { cliEmitter } from './cliEmitter.js';
5
- import { Matterbridge } from './matterbridge.js';
6
5
  import { hasParameter, hasAnyParameter } from './utils/commandLine.js';
7
6
  import { inspectError } from './utils/error.js';
8
7
  import { Tracker } from './utils/tracker.js';
@@ -67,11 +66,13 @@ async function shutdown() {
67
66
  }
68
67
  async function restart() {
69
68
  log.debug('Received restart event, loading...');
69
+ const { Matterbridge } = await import('./matterbridge.js');
70
70
  instance = await Matterbridge.loadInstance(true);
71
71
  registerHandlers();
72
72
  }
73
73
  async function update() {
74
74
  log.debug('Received update event, updating...');
75
+ const { Matterbridge } = await import('./matterbridge.js');
75
76
  instance = await Matterbridge.loadInstance(true);
76
77
  registerHandlers();
77
78
  }
@@ -89,6 +90,7 @@ async function main() {
89
90
  if (hasParameter('inspect'))
90
91
  await startInspector();
91
92
  log.debug(`***Matterbridge.loadInstance(true) called`);
93
+ const { Matterbridge } = await import('./matterbridge.js');
92
94
  instance = await Matterbridge.loadInstance(true);
93
95
  log.debug(`***Matterbridge.loadInstance(true) exited`);
94
96
  if (!instance || instance.shutdown) {
@@ -8,10 +8,10 @@ import { inspect } from 'node:util';
8
8
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt, BLUE, or } from 'node-ansi-logger';
9
9
  import { NodeStorageManager } from 'node-persist-manager';
10
10
  import '@matter/nodejs';
11
- import { Endpoint, ServerNode } from '@matter/node';
12
11
  import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, UINT32_MAX, UINT16_MAX, Crypto, Environment, StorageService } from '@matter/general';
13
- import { DeviceTypeId, VendorId } from '@matter/types';
14
12
  import { FabricAction, PaseClient } from '@matter/protocol';
13
+ import { Endpoint, ServerNode } from '@matter/node';
14
+ import { DeviceTypeId, VendorId } from '@matter/types/datatype';
15
15
  import { AggregatorEndpoint } from '@matter/node/endpoints';
16
16
  import { BasicInformationServer } from '@matter/node/behaviors/basic-information';
17
17
  import { getParameter, getIntParameter, hasParameter } from './utils/commandLine.js';
@@ -27,6 +27,7 @@ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
27
27
  import { bridge } from './matterbridgeDeviceTypes.js';
28
28
  import { Frontend } from './frontend.js';
29
29
  import { addVirtualDevices } from './helpers.js';
30
+ import { BroadcastServer } from './broadcastServer.js';
30
31
  export class Matterbridge extends EventEmitter {
31
32
  systemInformation = {
32
33
  interfaceName: '',
@@ -117,9 +118,35 @@ export class Matterbridge extends EventEmitter {
117
118
  aggregatorSerialNumber = getParameter('serialNumber');
118
119
  aggregatorUniqueId = getParameter('uniqueId');
119
120
  advertisingNodes = new Map();
121
+ server;
120
122
  constructor() {
121
123
  super();
122
124
  this.log.logNameColor = '\x1b[38;5;115m';
125
+ this.server = new BroadcastServer('matterbridge', this.log);
126
+ this.server.on('broadcast_message', this.msgHandler.bind(this));
127
+ }
128
+ async msgHandler(msg) {
129
+ if (this.server.isWorkerRequest(msg, msg.type) && (msg.dst === 'all' || msg.dst === 'matterbridge')) {
130
+ this.log.debug(`**Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
131
+ switch (msg.type) {
132
+ case 'get_log_level':
133
+ this.server.respond({ ...msg, response: { success: true, logLevel: this.log.logLevel } });
134
+ break;
135
+ case 'set_log_level':
136
+ this.log.logLevel = msg.params.logLevel;
137
+ this.server.respond({ ...msg, response: { success: true, logLevel: this.log.logLevel } });
138
+ break;
139
+ default:
140
+ this.log.warn(`Unknown broadcast request ${CYAN}${msg.type}${wr} from ${CYAN}${msg.src}${wr}`);
141
+ }
142
+ }
143
+ if (this.server.isWorkerResponse(msg, msg.type)) {
144
+ this.log.debug(`**Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
145
+ switch (msg.type) {
146
+ default:
147
+ this.log.warn(`Unknown broadcast response ${CYAN}${msg.type}${wr} from ${CYAN}${msg.src}${wr}`);
148
+ }
149
+ }
123
150
  }
124
151
  static async loadInstance(initialize = false) {
125
152
  if (!Matterbridge.instance) {
@@ -821,6 +848,7 @@ export class Matterbridge extends EventEmitter {
821
848
  callbackLogLevel = "debug";
822
849
  AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
823
850
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
851
+ return logLevel;
824
852
  }
825
853
  getLogLevel() {
826
854
  return this.log.logLevel;
@@ -993,6 +1021,7 @@ export class Matterbridge extends EventEmitter {
993
1021
  this.frontend.destroy();
994
1022
  this.plugins.destroy();
995
1023
  this.devices.destroy();
1024
+ this.server.close();
996
1025
  if (this.nodeStorage && this.nodeContext) {
997
1026
  this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
998
1027
  await this.nodeContext.close();
@@ -31,6 +31,7 @@ import { DeviceEnergyManagementServer } from '@matter/node/behaviors/device-ener
31
31
  import { DeviceEnergyManagementModeServer } from '@matter/node/behaviors/device-energy-management-mode';
32
32
  import { HepaFilterMonitoringServer } from '@matter/node/behaviors/hepa-filter-monitoring';
33
33
  import { ActivatedCarbonFilterMonitoringServer } from '@matter/node/behaviors/activated-carbon-filter-monitoring';
34
+ import { PowerSourceServer } from '@matter/node/behaviors/power-source';
34
35
  export class MatterbridgeServer extends Behavior {
35
36
  static id = 'matterbridge';
36
37
  initialize() {
@@ -45,6 +46,24 @@ export class MatterbridgeServer extends Behavior {
45
46
  }
46
47
  MatterbridgeServer.State = State;
47
48
  })(MatterbridgeServer || (MatterbridgeServer = {}));
49
+ export class MatterbridgePowerSourceServer extends PowerSourceServer {
50
+ initialize() {
51
+ const device = this.endpoint.stateOf(MatterbridgeServer);
52
+ device.log.info(`Initializing MatterbridgePowerSourceServer (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
53
+ this.state.endpointList = [this.endpoint.number];
54
+ this.endpoint.construction.onSuccess(() => {
55
+ device.log.debug(`MatterbridgePowerSourceServer: endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber} construction completed`);
56
+ const endpointList = [this.endpoint.number];
57
+ for (const endpoint of this.endpoint.parts) {
58
+ if (endpoint.lifecycle.isReady) {
59
+ endpointList.push(endpoint.number);
60
+ }
61
+ }
62
+ this.endpoint.setStateOf(PowerSourceServer, { endpointList });
63
+ device.log.debug(`MatterbridgePowerSourceServer: endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber} construction completed with endpointList: ${endpointList.join(', ')}`);
64
+ });
65
+ }
66
+ }
48
67
  export class MatterbridgeIdentifyServer extends IdentifyServer {
49
68
  identify(request) {
50
69
  const device = this.endpoint.stateOf(MatterbridgeServer);
@@ -1,4 +1,6 @@
1
- import { DeviceTypeId } from '@matter/types';
1
+ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
+ console.log('\u001B[32mMatterbridgeDeviceTypes loaded.\u001B[40;0m');
3
+ import { DeviceTypeId } from '@matter/types/datatype';
2
4
  import { AccountLogin } from '@matter/types/clusters/account-login';
3
5
  import { Actions } from '@matter/types/clusters/actions';
4
6
  import { ActivatedCarbonFilterMonitoring } from '@matter/types/clusters/activated-carbon-filter-monitoring';
@@ -90,9 +92,9 @@ export var DeviceClasses;
90
92
  DeviceClasses["Utility"] = "Utility";
91
93
  DeviceClasses["Simple"] = "Simple";
92
94
  DeviceClasses["Dynamic"] = "Dynamic";
95
+ DeviceClasses["Composed"] = "Composed";
93
96
  DeviceClasses["Client"] = "Client";
94
97
  DeviceClasses["Server"] = "Server";
95
- DeviceClasses["Composed"] = "Composed";
96
98
  DeviceClasses["Duplicate"] = "Duplicate";
97
99
  DeviceClasses["BridgedPowerSourceInfo"] = "BridgedPowerSourceInfo";
98
100
  })(DeviceClasses || (DeviceClasses = {}));
@@ -1,9 +1,10 @@
1
1
  if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
2
  console.log('\u001B[32mMatterbridgeEndpoint loaded.\u001B[40;0m');
3
3
  import { AnsiLogger, CYAN, YELLOW, db, debugStringify, hk, or, zb } from 'node-ansi-logger';
4
- import { Endpoint, MutableEndpoint, SupportedBehaviors } from '@matter/node';
5
4
  import { Lifecycle, NamedHandler, UINT16_MAX, UINT32_MAX } from '@matter/general';
6
- import { getClusterNameById, VendorId } from '@matter/types';
5
+ import { Endpoint, MutableEndpoint, SupportedBehaviors } from '@matter/node';
6
+ import { getClusterNameById } from '@matter/types/cluster';
7
+ import { VendorId } from '@matter/types/datatype';
7
8
  import { Descriptor } from '@matter/types/clusters/descriptor';
8
9
  import { PowerSource } from '@matter/types/clusters/power-source';
9
10
  import { BridgedDeviceBasicInformation } from '@matter/types/clusters/bridged-device-basic-information';
@@ -29,10 +30,8 @@ import { OccupancySensing } from '@matter/types/clusters/occupancy-sensing';
29
30
  import { ThermostatUserInterfaceConfiguration } from '@matter/types/clusters/thermostat-user-interface-configuration';
30
31
  import { OperationalState } from '@matter/types/clusters/operational-state';
31
32
  import { DeviceEnergyManagement } from '@matter/types/clusters/device-energy-management';
32
- import { DeviceEnergyManagementMode } from '@matter/types/clusters/device-energy-management-mode';
33
33
  import { ResourceMonitoring } from '@matter/types/clusters/resource-monitoring';
34
34
  import { DescriptorServer } from '@matter/node/behaviors/descriptor';
35
- import { PowerSourceServer } from '@matter/node/behaviors/power-source';
36
35
  import { BridgedDeviceBasicInformationServer } from '@matter/node/behaviors/bridged-device-basic-information';
37
36
  import { GroupsServer } from '@matter/node/behaviors/groups';
38
37
  import { ScenesManagementServer } from '@matter/node/behaviors/scenes-management';
@@ -62,8 +61,8 @@ import { TotalVolatileOrganicCompoundsConcentrationMeasurementServer } from '@ma
62
61
  import { FanControlServer } from '@matter/node/behaviors/fan-control';
63
62
  import { ThermostatUserInterfaceConfigurationServer } from '@matter/node/behaviors/thermostat-user-interface-configuration';
64
63
  import { isValidNumber, isValidObject, isValidString } from './utils/isvalid.js';
65
- import { MatterbridgeServer, MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeLiftWindowCoveringServer, MatterbridgeLiftTiltWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, MatterbridgeSwitchServer, MatterbridgeOperationalStateServer, MatterbridgeDeviceEnergyManagementModeServer, MatterbridgeDeviceEnergyManagementServer, MatterbridgeActivatedCarbonFilterMonitoringServer, MatterbridgeHepaFilterMonitoringServer, MatterbridgeEnhancedColorControlServer, } from './matterbridgeBehaviors.js';
66
- import { addClusterServers, addFixedLabel, addOptionalClusterServers, addRequiredClusterServers, addUserLabel, createUniqueId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getDefaultOperationalStateClusterServer, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, getDefaultOccupancySensingClusterServer, getDefaultElectricalEnergyMeasurementClusterServer, getDefaultElectricalPowerMeasurementClusterServer, getApparentElectricalPowerMeasurementClusterServer, lowercaseFirstLetter, updateAttribute, getClusterId, getAttributeId, setAttribute, getAttribute, checkNotLatinCharacters, generateUniqueId, subscribeAttribute, invokeBehaviorCommand, triggerEvent, featuresFor, } from './matterbridgeEndpointHelpers.js';
64
+ import { MatterbridgeServer, MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeLiftWindowCoveringServer, MatterbridgeLiftTiltWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, MatterbridgeSwitchServer, MatterbridgeOperationalStateServer, MatterbridgeDeviceEnergyManagementModeServer, MatterbridgeDeviceEnergyManagementServer, MatterbridgeActivatedCarbonFilterMonitoringServer, MatterbridgeHepaFilterMonitoringServer, MatterbridgeEnhancedColorControlServer, MatterbridgePowerSourceServer, } from './matterbridgeBehaviors.js';
65
+ import { addClusterServers, addFixedLabel, addOptionalClusterServers, addRequiredClusterServers, addUserLabel, createUniqueId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getDefaultOperationalStateClusterServer, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, getDefaultOccupancySensingClusterServer, getDefaultElectricalEnergyMeasurementClusterServer, getDefaultElectricalPowerMeasurementClusterServer, getApparentElectricalPowerMeasurementClusterServer, lowercaseFirstLetter, updateAttribute, getClusterId, getAttributeId, setAttribute, getAttribute, checkNotLatinCharacters, generateUniqueId, subscribeAttribute, invokeBehaviorCommand, triggerEvent, featuresFor, getDefaultPowerSourceWiredClusterServer, getDefaultPowerSourceReplaceableBatteryClusterServer, getDefaultPowerSourceRechargeableBatteryClusterServer, getDefaultDeviceEnergyManagementClusterServer, getDefaultDeviceEnergyManagementModeClusterServer, } from './matterbridgeEndpointHelpers.js';
67
66
  export class MatterbridgeEndpoint extends Endpoint {
68
67
  static logLevel = "info";
69
68
  mode = undefined;
@@ -406,49 +405,15 @@ export class MatterbridgeEndpoint extends Endpoint {
406
405
  return device;
407
406
  }
408
407
  createDefaultPowerSourceWiredClusterServer(wiredCurrentType = PowerSource.WiredCurrentType.Ac) {
409
- this.behaviors.require(PowerSourceServer.with(PowerSource.Feature.Wired), {
410
- status: PowerSource.PowerSourceStatus.Active,
411
- order: 0,
412
- description: wiredCurrentType === PowerSource.WiredCurrentType.Ac ? 'AC Power' : 'DC Power',
413
- endpointList: [],
414
- wiredCurrentType,
415
- });
408
+ this.behaviors.require(MatterbridgePowerSourceServer.with(PowerSource.Feature.Wired), getDefaultPowerSourceWiredClusterServer(wiredCurrentType));
416
409
  return this;
417
410
  }
418
411
  createDefaultPowerSourceReplaceableBatteryClusterServer(batPercentRemaining = 100, batChargeLevel = PowerSource.BatChargeLevel.Ok, batVoltage = 1500, batReplacementDescription = 'Battery type', batQuantity = 1, batReplaceability = PowerSource.BatReplaceability.UserReplaceable) {
419
- this.behaviors.require(PowerSourceServer.with(PowerSource.Feature.Battery, PowerSource.Feature.Replaceable), {
420
- status: PowerSource.PowerSourceStatus.Active,
421
- order: 0,
422
- description: 'Primary battery',
423
- endpointList: [],
424
- batVoltage,
425
- batPercentRemaining: Math.min(Math.max(batPercentRemaining * 2, 0), 200),
426
- batChargeLevel,
427
- batReplacementNeeded: false,
428
- batReplaceability,
429
- activeBatFaults: undefined,
430
- batReplacementDescription,
431
- batQuantity,
432
- });
412
+ this.behaviors.require(MatterbridgePowerSourceServer.with(PowerSource.Feature.Battery, PowerSource.Feature.Replaceable), getDefaultPowerSourceReplaceableBatteryClusterServer(batPercentRemaining, batChargeLevel, batVoltage, batReplacementDescription, batQuantity, batReplaceability));
433
413
  return this;
434
414
  }
435
415
  createDefaultPowerSourceRechargeableBatteryClusterServer(batPercentRemaining = 100, batChargeLevel = PowerSource.BatChargeLevel.Ok, batVoltage = 1500, batReplaceability = PowerSource.BatReplaceability.Unspecified) {
436
- this.behaviors.require(PowerSourceServer.with(PowerSource.Feature.Battery, PowerSource.Feature.Rechargeable), {
437
- status: PowerSource.PowerSourceStatus.Active,
438
- order: 0,
439
- description: 'Primary battery',
440
- endpointList: [],
441
- batVoltage,
442
- batPercentRemaining: Math.min(Math.max(batPercentRemaining * 2, 0), 200),
443
- batTimeRemaining: null,
444
- batChargeLevel,
445
- batReplacementNeeded: false,
446
- batReplaceability,
447
- batPresent: true,
448
- activeBatFaults: [],
449
- batChargeState: PowerSource.BatChargeState.IsNotCharging,
450
- batFunctionalWhileCharging: true,
451
- });
416
+ this.behaviors.require(MatterbridgePowerSourceServer.with(PowerSource.Feature.Battery, PowerSource.Feature.Rechargeable), getDefaultPowerSourceRechargeableBatteryClusterServer(batPercentRemaining, batChargeLevel, batVoltage, batReplaceability));
452
417
  return this;
453
418
  }
454
419
  createDefaultBasicInformationClusterServer(deviceName, serialNumber, vendorId = 0xfff1, vendorName = 'Matterbridge', productId = 0x8000, productName = 'Matterbridge device', softwareVersion = 1, softwareVersionString = '1.0.0', hardwareVersion = 1, hardwareVersionString = '1.0.0') {
@@ -498,6 +463,30 @@ export class MatterbridgeEndpoint extends Endpoint {
498
463
  });
499
464
  return this;
500
465
  }
466
+ createDefaultPowerTopologyClusterServer() {
467
+ this.behaviors.require(PowerTopologyServer.with(PowerTopology.Feature.TreeTopology));
468
+ return this;
469
+ }
470
+ createDefaultElectricalEnergyMeasurementClusterServer(energyImported = null, energyExported = null) {
471
+ this.behaviors.require(ElectricalEnergyMeasurementServer.with(ElectricalEnergyMeasurement.Feature.ImportedEnergy, ElectricalEnergyMeasurement.Feature.ExportedEnergy, ElectricalEnergyMeasurement.Feature.CumulativeEnergy), getDefaultElectricalEnergyMeasurementClusterServer(energyImported, energyExported));
472
+ return this;
473
+ }
474
+ createDefaultElectricalPowerMeasurementClusterServer(voltage = null, current = null, power = null, frequency = null) {
475
+ this.behaviors.require(ElectricalPowerMeasurementServer.with(ElectricalPowerMeasurement.Feature.AlternatingCurrent), getDefaultElectricalPowerMeasurementClusterServer(voltage, current, power, frequency));
476
+ return this;
477
+ }
478
+ createApparentElectricalPowerMeasurementClusterServer(voltage = null, apparentCurrent = null, apparentPower = null, frequency = null) {
479
+ this.behaviors.require(ElectricalPowerMeasurementServer.with(ElectricalPowerMeasurement.Feature.AlternatingCurrent), getApparentElectricalPowerMeasurementClusterServer(voltage, apparentCurrent, apparentPower, frequency));
480
+ return this;
481
+ }
482
+ createDefaultDeviceEnergyManagementClusterServer(esaType = DeviceEnergyManagement.EsaType.Other, esaCanGenerate = false, esaState = DeviceEnergyManagement.EsaState.Online, absMinPower = 0, absMaxPower = 0) {
483
+ this.behaviors.require(MatterbridgeDeviceEnergyManagementServer.with(DeviceEnergyManagement.Feature.PowerForecastReporting, DeviceEnergyManagement.Feature.PowerAdjustment), getDefaultDeviceEnergyManagementClusterServer(esaType, esaCanGenerate, esaState, absMinPower, absMaxPower));
484
+ return this;
485
+ }
486
+ createDefaultDeviceEnergyManagementModeClusterServer(currentMode, supportedModes) {
487
+ this.behaviors.require(MatterbridgeDeviceEnergyManagementModeServer, getDefaultDeviceEnergyManagementModeClusterServer(currentMode, supportedModes));
488
+ return this;
489
+ }
501
490
  createDefaultIdentifyClusterServer(identifyTime = 0, identifyType = Identify.IdentifyType.None) {
502
491
  this.behaviors.require(MatterbridgeIdentifyServer, {
503
492
  identifyTime,
@@ -1158,60 +1147,6 @@ export class MatterbridgeEndpoint extends Endpoint {
1158
1147
  });
1159
1148
  return this;
1160
1149
  }
1161
- createDefaultDeviceEnergyManagementClusterServer(esaType = DeviceEnergyManagement.EsaType.Other, esaCanGenerate = false, esaState = DeviceEnergyManagement.EsaState.Online, absMinPower = 0, absMaxPower = 0) {
1162
- this.behaviors.require(MatterbridgeDeviceEnergyManagementServer.with(DeviceEnergyManagement.Feature.PowerForecastReporting, DeviceEnergyManagement.Feature.PowerAdjustment), {
1163
- esaType,
1164
- esaCanGenerate,
1165
- esaState,
1166
- absMinPower,
1167
- absMaxPower,
1168
- powerAdjustmentCapability: null,
1169
- optOutState: DeviceEnergyManagement.OptOutState.NoOptOut,
1170
- forecast: null,
1171
- });
1172
- return this;
1173
- }
1174
- createDefaultDeviceEnergyManagementModeClusterServer(currentMode, supportedModes) {
1175
- this.behaviors.require(MatterbridgeDeviceEnergyManagementModeServer, {
1176
- supportedModes: supportedModes ?? [
1177
- { label: 'No Energy Management (Forecast reporting only)', mode: 1, modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.NoOptimization }] },
1178
- {
1179
- label: 'Device Energy Management',
1180
- mode: 2,
1181
- modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.DeviceOptimization }, { value: DeviceEnergyManagementMode.ModeTag.LocalOptimization }],
1182
- },
1183
- {
1184
- label: 'Home Energy Management',
1185
- mode: 3,
1186
- modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.GridOptimization }, { value: DeviceEnergyManagementMode.ModeTag.LocalOptimization }],
1187
- },
1188
- { label: 'Grid Energy Managemen', mode: 4, modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.GridOptimization }] },
1189
- {
1190
- label: 'Full Energy Management',
1191
- mode: 5,
1192
- modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.DeviceOptimization }, { value: DeviceEnergyManagementMode.ModeTag.LocalOptimization }, { value: DeviceEnergyManagementMode.ModeTag.GridOptimization }],
1193
- },
1194
- ],
1195
- currentMode: currentMode ?? 1,
1196
- });
1197
- return this;
1198
- }
1199
- createDefaultPowerTopologyClusterServer() {
1200
- this.behaviors.require(PowerTopologyServer.with(PowerTopology.Feature.TreeTopology));
1201
- return this;
1202
- }
1203
- createDefaultElectricalEnergyMeasurementClusterServer(energyImported = null, energyExported = null) {
1204
- this.behaviors.require(ElectricalEnergyMeasurementServer.with(ElectricalEnergyMeasurement.Feature.ImportedEnergy, ElectricalEnergyMeasurement.Feature.ExportedEnergy, ElectricalEnergyMeasurement.Feature.CumulativeEnergy), getDefaultElectricalEnergyMeasurementClusterServer(energyImported, energyExported));
1205
- return this;
1206
- }
1207
- createDefaultElectricalPowerMeasurementClusterServer(voltage = null, current = null, power = null, frequency = null) {
1208
- this.behaviors.require(ElectricalPowerMeasurementServer.with(ElectricalPowerMeasurement.Feature.AlternatingCurrent), getDefaultElectricalPowerMeasurementClusterServer(voltage, current, power, frequency));
1209
- return this;
1210
- }
1211
- createApparentElectricalPowerMeasurementClusterServer(voltage = null, apparentCurrent = null, apparentPower = null, frequency = null) {
1212
- this.behaviors.require(ElectricalPowerMeasurementServer.with(ElectricalPowerMeasurement.Feature.AlternatingCurrent), getApparentElectricalPowerMeasurementClusterServer(voltage, apparentCurrent, apparentPower, frequency));
1213
- return this;
1214
- }
1215
1150
  createDefaultTemperatureMeasurementClusterServer(measuredValue = null, minMeasuredValue = null, maxMeasuredValue = null) {
1216
1151
  this.behaviors.require(TemperatureMeasurementServer, getDefaultTemperatureMeasurementClusterServer(measuredValue, minMeasuredValue, maxMeasuredValue));
1217
1152
  return this;
@@ -79,8 +79,10 @@ import { Pm10ConcentrationMeasurementServer } from '@matter/node/behaviors/pm10-
79
79
  import { RadonConcentrationMeasurementServer } from '@matter/node/behaviors/radon-concentration-measurement';
80
80
  import { TotalVolatileOrganicCompoundsConcentrationMeasurementServer } from '@matter/node/behaviors/total-volatile-organic-compounds-concentration-measurement';
81
81
  import { DeviceEnergyManagementServer } from '@matter/node/behaviors/device-energy-management';
82
- import { deepCopy, deepEqual, isValidArray } from './utils/export.js';
83
- import { MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeLiftWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, MatterbridgeOperationalStateServer, MatterbridgeDeviceEnergyManagementModeServer, } from './matterbridgeBehaviors.js';
82
+ import { deepCopy } from './utils/deepCopy.js';
83
+ import { deepEqual } from './utils/deepEqual.js';
84
+ import { isValidArray } from './utils/isvalid.js';
85
+ import { MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeLiftWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, MatterbridgeOperationalStateServer, MatterbridgeDeviceEnergyManagementModeServer, MatterbridgePowerSourceServer, MatterbridgeDeviceEnergyManagementServer, } from './matterbridgeBehaviors.js';
84
86
  export function capitalizeFirstLetter(name) {
85
87
  if (!name)
86
88
  return name;
@@ -538,70 +540,47 @@ export async function triggerEvent(endpoint, cluster, event, payload, log) {
538
540
  log?.info(`${db}Trigger event ${hk}${capitalizeFirstLetter(clusterName)}${db}.${hk}${event}${db} with ${debugStringify(payload)}${db} on endpoint ${or}${endpoint.id}${db}:${or}${endpoint.number}${db} `);
539
541
  return true;
540
542
  }
541
- export function getDefaultOperationalStateClusterServer(operationalState = OperationalState.OperationalStateEnum.Stopped) {
542
- return optionsFor(MatterbridgeOperationalStateServer, {
543
- phaseList: [],
544
- currentPhase: null,
545
- countdownTime: null,
546
- operationalStateList: [
547
- { operationalStateId: OperationalState.OperationalStateEnum.Stopped, operationalStateLabel: 'Stopped' },
548
- { operationalStateId: OperationalState.OperationalStateEnum.Running, operationalStateLabel: 'Running' },
549
- { operationalStateId: OperationalState.OperationalStateEnum.Paused, operationalStateLabel: 'Paused' },
550
- { operationalStateId: OperationalState.OperationalStateEnum.Error, operationalStateLabel: 'Error' },
551
- ],
552
- operationalState,
553
- operationalError: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
543
+ export function getDefaultPowerSourceWiredClusterServer(wiredCurrentType = PowerSource.WiredCurrentType.Ac) {
544
+ return optionsFor(MatterbridgePowerSourceServer.with(PowerSource.Feature.Wired), {
545
+ status: PowerSource.PowerSourceStatus.Active,
546
+ order: 0,
547
+ description: wiredCurrentType === PowerSource.WiredCurrentType.Ac ? 'AC Power' : 'DC Power',
548
+ endpointList: [],
549
+ wiredCurrentType,
554
550
  });
555
551
  }
556
- export function getDefaultTemperatureMeasurementClusterServer(measuredValue = null, minMeasuredValue = null, maxMeasuredValue = null) {
557
- return optionsFor(TemperatureMeasurementServer, {
558
- measuredValue,
559
- minMeasuredValue,
560
- maxMeasuredValue,
561
- tolerance: 0,
552
+ export function getDefaultPowerSourceReplaceableBatteryClusterServer(batPercentRemaining = 100, batChargeLevel = PowerSource.BatChargeLevel.Ok, batVoltage = 1500, batReplacementDescription = 'Battery type', batQuantity = 1, batReplaceability = PowerSource.BatReplaceability.UserReplaceable) {
553
+ return optionsFor(MatterbridgePowerSourceServer.with(PowerSource.Feature.Battery, PowerSource.Feature.Replaceable), {
554
+ status: PowerSource.PowerSourceStatus.Active,
555
+ order: 0,
556
+ description: 'Primary battery',
557
+ endpointList: [],
558
+ batVoltage,
559
+ batPercentRemaining: Math.min(Math.max(batPercentRemaining * 2, 0), 200),
560
+ batChargeLevel,
561
+ batReplacementNeeded: false,
562
+ batReplaceability,
563
+ activeBatFaults: undefined,
564
+ batReplacementDescription,
565
+ batQuantity,
562
566
  });
563
567
  }
564
- export function getDefaultRelativeHumidityMeasurementClusterServer(measuredValue = null, minMeasuredValue = null, maxMeasuredValue = null) {
565
- return optionsFor(RelativeHumidityMeasurementServer, {
566
- measuredValue,
567
- minMeasuredValue,
568
- maxMeasuredValue,
569
- tolerance: 0,
570
- });
571
- }
572
- export function getDefaultPressureMeasurementClusterServer(measuredValue = null, minMeasuredValue = null, maxMeasuredValue = null) {
573
- return optionsFor(PressureMeasurementServer, {
574
- measuredValue,
575
- minMeasuredValue,
576
- maxMeasuredValue,
577
- tolerance: 0,
578
- });
579
- }
580
- export function getDefaultIlluminanceMeasurementClusterServer(measuredValue = null, minMeasuredValue = null, maxMeasuredValue = null) {
581
- return optionsFor(IlluminanceMeasurementServer, {
582
- measuredValue,
583
- minMeasuredValue,
584
- maxMeasuredValue,
585
- tolerance: 0,
586
- });
587
- }
588
- export function getDefaultFlowMeasurementClusterServer(measuredValue = null, minMeasuredValue = null, maxMeasuredValue = null) {
589
- return optionsFor(FlowMeasurementServer, {
590
- measuredValue,
591
- minMeasuredValue,
592
- maxMeasuredValue,
593
- tolerance: 0,
594
- });
595
- }
596
- export function getDefaultOccupancySensingClusterServer(occupied = false, holdTime = 30, holdTimeMin = 1, holdTimeMax = 300) {
597
- return optionsFor(OccupancySensingServer.with(OccupancySensing.Feature.PassiveInfrared), {
598
- occupancy: { occupied },
599
- occupancySensorType: OccupancySensing.OccupancySensorType.Pir,
600
- occupancySensorTypeBitmap: { pir: true, ultrasonic: false, physicalContact: false },
601
- pirOccupiedToUnoccupiedDelay: holdTime,
602
- pirUnoccupiedToOccupiedDelay: holdTime,
603
- holdTime,
604
- holdTimeLimits: { holdTimeMin, holdTimeMax, holdTimeDefault: holdTime },
568
+ export function getDefaultPowerSourceRechargeableBatteryClusterServer(batPercentRemaining = 100, batChargeLevel = PowerSource.BatChargeLevel.Ok, batVoltage = 1500, batReplaceability = PowerSource.BatReplaceability.Unspecified) {
569
+ return optionsFor(MatterbridgePowerSourceServer.with(PowerSource.Feature.Battery, PowerSource.Feature.Rechargeable), {
570
+ status: PowerSource.PowerSourceStatus.Active,
571
+ order: 0,
572
+ description: 'Primary battery',
573
+ endpointList: [],
574
+ batVoltage,
575
+ batPercentRemaining: Math.min(Math.max(batPercentRemaining * 2, 0), 200),
576
+ batTimeRemaining: null,
577
+ batChargeLevel,
578
+ batReplacementNeeded: false,
579
+ batReplaceability,
580
+ batPresent: true,
581
+ activeBatFaults: [],
582
+ batChargeState: PowerSource.BatChargeState.IsNotCharging,
583
+ batFunctionalWhileCharging: true,
605
584
  });
606
585
  }
607
586
  export function getDefaultElectricalEnergyMeasurementClusterServer(energyImported = null, energyExported = null) {
@@ -698,3 +677,105 @@ export function getApparentElectricalPowerMeasurementClusterServer(voltage = nul
698
677
  frequency: frequency,
699
678
  });
700
679
  }
680
+ export function getDefaultDeviceEnergyManagementClusterServer(esaType = DeviceEnergyManagement.EsaType.Other, esaCanGenerate = false, esaState = DeviceEnergyManagement.EsaState.Online, absMinPower = 0, absMaxPower = 0) {
681
+ return optionsFor(MatterbridgeDeviceEnergyManagementServer.with(DeviceEnergyManagement.Feature.PowerForecastReporting, DeviceEnergyManagement.Feature.PowerAdjustment), {
682
+ esaType,
683
+ esaCanGenerate,
684
+ esaState,
685
+ absMinPower,
686
+ absMaxPower,
687
+ powerAdjustmentCapability: null,
688
+ optOutState: DeviceEnergyManagement.OptOutState.NoOptOut,
689
+ forecast: null,
690
+ });
691
+ }
692
+ export function getDefaultDeviceEnergyManagementModeClusterServer(currentMode, supportedModes) {
693
+ return optionsFor(MatterbridgeDeviceEnergyManagementModeServer, {
694
+ supportedModes: supportedModes ?? [
695
+ { label: 'No Energy Management (Forecast reporting only)', mode: 1, modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.NoOptimization }] },
696
+ {
697
+ label: 'Device Energy Management',
698
+ mode: 2,
699
+ modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.DeviceOptimization }, { value: DeviceEnergyManagementMode.ModeTag.LocalOptimization }],
700
+ },
701
+ {
702
+ label: 'Home Energy Management',
703
+ mode: 3,
704
+ modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.GridOptimization }, { value: DeviceEnergyManagementMode.ModeTag.LocalOptimization }],
705
+ },
706
+ { label: 'Grid Energy Managemen', mode: 4, modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.GridOptimization }] },
707
+ {
708
+ label: 'Full Energy Management',
709
+ mode: 5,
710
+ modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.DeviceOptimization }, { value: DeviceEnergyManagementMode.ModeTag.LocalOptimization }, { value: DeviceEnergyManagementMode.ModeTag.GridOptimization }],
711
+ },
712
+ ],
713
+ currentMode: currentMode ?? 1,
714
+ });
715
+ }
716
+ export function getDefaultOperationalStateClusterServer(operationalState = OperationalState.OperationalStateEnum.Stopped) {
717
+ return optionsFor(MatterbridgeOperationalStateServer, {
718
+ phaseList: [],
719
+ currentPhase: null,
720
+ countdownTime: null,
721
+ operationalStateList: [
722
+ { operationalStateId: OperationalState.OperationalStateEnum.Stopped, operationalStateLabel: 'Stopped' },
723
+ { operationalStateId: OperationalState.OperationalStateEnum.Running, operationalStateLabel: 'Running' },
724
+ { operationalStateId: OperationalState.OperationalStateEnum.Paused, operationalStateLabel: 'Paused' },
725
+ { operationalStateId: OperationalState.OperationalStateEnum.Error, operationalStateLabel: 'Error' },
726
+ ],
727
+ operationalState,
728
+ operationalError: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
729
+ });
730
+ }
731
+ export function getDefaultTemperatureMeasurementClusterServer(measuredValue = null, minMeasuredValue = null, maxMeasuredValue = null) {
732
+ return optionsFor(TemperatureMeasurementServer, {
733
+ measuredValue,
734
+ minMeasuredValue,
735
+ maxMeasuredValue,
736
+ tolerance: 0,
737
+ });
738
+ }
739
+ export function getDefaultRelativeHumidityMeasurementClusterServer(measuredValue = null, minMeasuredValue = null, maxMeasuredValue = null) {
740
+ return optionsFor(RelativeHumidityMeasurementServer, {
741
+ measuredValue,
742
+ minMeasuredValue,
743
+ maxMeasuredValue,
744
+ tolerance: 0,
745
+ });
746
+ }
747
+ export function getDefaultPressureMeasurementClusterServer(measuredValue = null, minMeasuredValue = null, maxMeasuredValue = null) {
748
+ return optionsFor(PressureMeasurementServer, {
749
+ measuredValue,
750
+ minMeasuredValue,
751
+ maxMeasuredValue,
752
+ tolerance: 0,
753
+ });
754
+ }
755
+ export function getDefaultIlluminanceMeasurementClusterServer(measuredValue = null, minMeasuredValue = null, maxMeasuredValue = null) {
756
+ return optionsFor(IlluminanceMeasurementServer, {
757
+ measuredValue,
758
+ minMeasuredValue,
759
+ maxMeasuredValue,
760
+ tolerance: 0,
761
+ });
762
+ }
763
+ export function getDefaultFlowMeasurementClusterServer(measuredValue = null, minMeasuredValue = null, maxMeasuredValue = null) {
764
+ return optionsFor(FlowMeasurementServer, {
765
+ measuredValue,
766
+ minMeasuredValue,
767
+ maxMeasuredValue,
768
+ tolerance: 0,
769
+ });
770
+ }
771
+ export function getDefaultOccupancySensingClusterServer(occupied = false, holdTime = 30, holdTimeMin = 1, holdTimeMax = 300) {
772
+ return optionsFor(OccupancySensingServer.with(OccupancySensing.Feature.PassiveInfrared), {
773
+ occupancy: { occupied },
774
+ occupancySensorType: OccupancySensing.OccupancySensorType.Pir,
775
+ occupancySensorTypeBitmap: { pir: true, ultrasonic: false, physicalContact: false },
776
+ pirOccupiedToUnoccupiedDelay: holdTime,
777
+ pirUnoccupiedToOccupiedDelay: holdTime,
778
+ holdTime,
779
+ holdTimeLimits: { holdTimeMin, holdTimeMax, holdTimeDefault: holdTime },
780
+ });
781
+ }
@@ -1,10 +1,10 @@
1
1
  if (process.argv.includes('--loader') || process.argv.includes('-loader'))
2
2
  console.log('\u001B[32mMatterbridgePlatform loaded.\u001B[40;0m');
3
3
  import path from 'node:path';
4
- import { Descriptor } from '@matter/types/clusters/descriptor';
5
- import { BridgedDeviceBasicInformation } from '@matter/types/clusters/bridged-device-basic-information';
6
4
  import { CYAN, db, er, nf, wr } from 'node-ansi-logger';
7
5
  import { NodeStorageManager } from 'node-persist-manager';
6
+ import { Descriptor } from '@matter/types/clusters/descriptor';
7
+ import { BridgedDeviceBasicInformation } from '@matter/types/clusters/bridged-device-basic-information';
8
8
  import { checkNotLatinCharacters } from './matterbridgeEndpointHelpers.js';
9
9
  import { bridgedNode } from './matterbridgeDeviceTypes.js';
10
10
  import { isValidArray, isValidObject, isValidString } from './utils/isvalid.js';
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.3.4-dev-20251020-df40d12",
3
+ "version": "3.3.4-dev-20251022-681420c",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge",
9
- "version": "3.3.4-dev-20251020-df40d12",
9
+ "version": "3.3.4-dev-20251022-681420c",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@matter/main": "0.15.6",
@@ -367,9 +367,9 @@
367
367
  "license": "MIT"
368
368
  },
369
369
  "node_modules/bare-events": {
370
- "version": "2.8.0",
371
- "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.0.tgz",
372
- "integrity": "sha512-AOhh6Bg5QmFIXdViHbMc2tLDsBIRxdkIaIddPslJF9Z5De3APBScuqGP2uThXnIpqFrgoxMNC6km7uXNIMLHXA==",
370
+ "version": "2.8.1",
371
+ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.1.tgz",
372
+ "integrity": "sha512-oxSAxTS1hRfnyit2CL5QpAOS5ixfBjj6ex3yTNvXyY/kE719jQ/IjuESJBK2w5v4wwQRAHGseVJXx9QBYOtFGQ==",
373
373
  "license": "Apache-2.0",
374
374
  "peerDependencies": {
375
375
  "bare-abort-controller": "*"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.3.4-dev-20251020-df40d12",
3
+ "version": "3.3.4-dev-20251022-681420c",
4
4
  "description": "Matterbridge plugin manager for Matter",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",