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 +9 -0
- package/README-DOCKER.md +4 -1
- package/README-SERVICE-LOCAL.md +226 -0
- package/README-SERVICE.md +4 -3
- package/dist/broadcastServer.js +3 -0
- package/dist/cli.js +3 -1
- package/dist/matterbridge.js +31 -2
- package/dist/matterbridgeBehaviors.js +19 -0
- package/dist/matterbridgeDeviceTypes.js +4 -2
- package/dist/matterbridgeEndpoint.js +32 -97
- package/dist/matterbridgeEndpointHelpers.js +143 -62
- package/dist/matterbridgePlatform.js +2 -2
- package/npm-shrinkwrap.json +5 -5
- package/package.json +1 -1
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
|
-
|
|
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"> Matterbridge systemd configuration with local global node_modules
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/matterbridge)
|
|
4
|
+
[](https://www.npmjs.com/package/matterbridge)
|
|
5
|
+
[](https://hub.docker.com/r/luligu/matterbridge)
|
|
6
|
+
[](https://hub.docker.com/r/luligu/matterbridge)
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+
[](https://codecov.io/gh/Luligu/matterbridge)
|
|
10
|
+
|
|
11
|
+
[](https://www.npmjs.com/package/matter-history)
|
|
12
|
+
[](https://www.npmjs.com/package/node-ansi-logger)
|
|
13
|
+
[](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
|
|
32
|
-
mkdir -p
|
|
33
|
-
|
|
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
|
package/dist/broadcastServer.js
CHANGED
|
@@ -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) {
|
package/dist/matterbridge.js
CHANGED
|
@@ -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
|
-
|
|
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 {
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
83
|
-
import {
|
|
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
|
|
542
|
-
return optionsFor(
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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
|
|
557
|
-
return optionsFor(
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
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
|
|
565
|
-
return optionsFor(
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
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';
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge",
|
|
3
|
-
"version": "3.3.4-dev-
|
|
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-
|
|
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.
|
|
371
|
-
"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.
|
|
372
|
-
"integrity": "sha512-
|
|
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