matterbridge 3.3.7-dev-20251105-440e549 → 3.3.7-dev-20251108-f254812
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 +7 -2
- package/README-SERVICE-LOCAL.md +4 -2
- package/README-SERVICE-OPT.md +168 -0
- package/README.md +4 -2
- package/dist/broadcastServer.js +18 -8
- package/dist/deviceManager.js +31 -2
- package/dist/frontend.js +16 -11
- package/dist/matterbridge.js +10 -4
- package/dist/pluginManager.js +206 -36
- package/frontend/package-lock.json +14 -55
- package/frontend/package.json +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -28,17 +28,22 @@ Advantages:
|
|
|
28
28
|
- individual plugin isolation in childbridge mode;
|
|
29
29
|
- ability to update the plugin in childbridge mode without restarting matterbridge;
|
|
30
30
|
|
|
31
|
-
## [3.3.7] - 2025-11
|
|
31
|
+
## [3.3.7] - 2025-11-07
|
|
32
32
|
|
|
33
33
|
### Breaking Changes
|
|
34
34
|
|
|
35
|
+
- [frontend]: When a plugin is first added, it will not be anymore started to allow to configure it before restarting.
|
|
36
|
+
|
|
35
37
|
### Added
|
|
36
38
|
|
|
37
|
-
- [matterbridge]: Added a first check for plugin existence (docker pull or Hass add-on rebuild) and reinstall it before parsing the plugin.
|
|
39
|
+
- [matterbridge]: Added a first check for plugin existence (docker pull or Hass add-on rebuild) and reinstall it before parsing the plugin. The error messages have been removed.
|
|
40
|
+
- [service]: Added [configuration](README-SERVICE-OPT.md) to run matterbridge as a daemon with systemctl (Linux only) and with private global node_modules (user matterbridge and no sudo required).
|
|
38
41
|
|
|
39
42
|
### Changed
|
|
40
43
|
|
|
41
44
|
- [frontend]: Bumped `frontend` version to 3.3.1.
|
|
45
|
+
- [PluginManager]: Bumped `PluginManager` version to 1.3.0.
|
|
46
|
+
- [DeviceManager]: Bumped `DeviceManager` version to 1.1.0.
|
|
42
47
|
- [frontend]: Readded password dialog when running in Ingress.
|
|
43
48
|
|
|
44
49
|
### Fixed
|
package/README-SERVICE-LOCAL.md
CHANGED
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
|
|
19
19
|
## Run matterbridge as a daemon with systemctl (Linux only) with local global node_modules
|
|
20
20
|
|
|
21
|
-
The advantage of this setup is that the global node_modules are private for
|
|
21
|
+
The advantage of this setup is that the global node_modules are private for the user and sudo is not required.
|
|
22
22
|
|
|
23
|
-
The service runs rootless.
|
|
23
|
+
The service runs rootless like the current user.
|
|
24
24
|
|
|
25
25
|
The storage position is compatible with the traditional setup (~/Matterbridge ~/.matterbridge ~/.mattercert).
|
|
26
26
|
|
|
@@ -37,6 +37,8 @@ chown -R $USER:$USER ~/Matterbridge ~/.matterbridge ~/.mattercert ~/.npm-global
|
|
|
37
37
|
chmod -R 755 ~/Matterbridge ~/.matterbridge ~/.mattercert ~/.npm-global # ✅ Secure permissions
|
|
38
38
|
NPM_CONFIG_PREFIX=~/.npm-global npm install matterbridge --omit=dev --verbose --global # ✅ Install matterbridge in the local global node_modules, no sudo
|
|
39
39
|
sudo ln -sf /home/$USER/.npm-global/bin/matterbridge /usr/local/bin/matterbridge # ✅ Create a link to matterbridge bin
|
|
40
|
+
sudo ln -sf /home/$USER/.npm-global/bin/mb_mdns /usr/local/bin/mb_mdns # ✅ Create a link to mb_mdns bin
|
|
41
|
+
sudo ln -sf /home/$USER/.npm-global/bin/mb_coap /usr/local/bin/mb_coap # ✅ Create a link to mb_coap bin
|
|
40
42
|
hash -r # ✅ Clear bash command cache as a precaution
|
|
41
43
|
which matterbridge # ✅ Check it
|
|
42
44
|
matterbridge --version # ✅ Will output the matterbridge version
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px"> Matterbridge systemd configuration with private 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 user matterbridge and private global node_modules
|
|
20
|
+
|
|
21
|
+
The advantage of this setup is that the global node_modules are private for matterbridge and sudo is not required.
|
|
22
|
+
|
|
23
|
+
The service runs with user matterbridge and the system has full protection.
|
|
24
|
+
|
|
25
|
+
The storage position is **not compatible** with the traditional setup (~/Matterbridge ~/.matterbridge ~/.mattercert).
|
|
26
|
+
|
|
27
|
+
### 1 - Create the matterbridge user and group
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
sudo groupadd --system matterbridge 2>/dev/null || true
|
|
31
|
+
sudo useradd --system \
|
|
32
|
+
--home-dir /opt/matterbridge \
|
|
33
|
+
--shell /usr/sbin/nologin \
|
|
34
|
+
--gid matterbridge \
|
|
35
|
+
matterbridge 2>/dev/null || true
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2 - Create the Matterbridge directories and set the correct permissions
|
|
39
|
+
|
|
40
|
+
This will create the required directories if they don't exist
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cd ~
|
|
44
|
+
# ✅ Safe precaution if matterbridge was already running with the traditional setup
|
|
45
|
+
sudo systemctl stop matterbridge
|
|
46
|
+
# ✅ We need to uninstall from the global node_modules
|
|
47
|
+
sudo npm uninstall matterbridge -g
|
|
48
|
+
# ✅ Creates all needed dirs
|
|
49
|
+
sudo mkdir -p /opt/matterbridge /opt/matterbridge/Matterbridge /opt/matterbridge/.matterbridge /opt/matterbridge/.mattercert /opt/matterbridge/.npm-global
|
|
50
|
+
# ✅ Ensures ownership
|
|
51
|
+
sudo chown -R matterbridge:matterbridge /opt/matterbridge /opt/matterbridge/Matterbridge /opt/matterbridge/.matterbridge /opt/matterbridge/.mattercert /opt/matterbridge/.npm-global
|
|
52
|
+
# ✅ Secure permissions
|
|
53
|
+
sudo chmod -R 755 /opt/matterbridge /opt/matterbridge/Matterbridge /opt/matterbridge/.matterbridge /opt/matterbridge/.mattercert /opt/matterbridge/.npm-global
|
|
54
|
+
# make sure the “bin” dir exists for global executables
|
|
55
|
+
sudo -u matterbridge mkdir -p /opt/matterbridge/.npm-global/bin
|
|
56
|
+
# ✅ Install matterbridge in the private global node_modules
|
|
57
|
+
sudo -u matterbridge NPM_CONFIG_PREFIX=/opt/matterbridge/.npm-global npm install matterbridge --omit=dev --verbose --global
|
|
58
|
+
# ✅ Create a link to matterbridge bin
|
|
59
|
+
sudo ln -sf /opt/matterbridge/.npm-global/bin/matterbridge /usr/bin/matterbridge
|
|
60
|
+
sudo ln -sf /opt/matterbridge/.npm-global/bin/mb_mdns /usr/bin/mb_mdns
|
|
61
|
+
sudo ln -sf /opt/matterbridge/.npm-global/bin/mb_coap /usr/bin/mb_coap
|
|
62
|
+
# ✅ Clear bash command cache as a precaution
|
|
63
|
+
hash -r
|
|
64
|
+
# ✅ Check if matterbridge is /usr/bin/matterbridge
|
|
65
|
+
which matterbridge
|
|
66
|
+
# ✅ Will output the matterbridge version
|
|
67
|
+
matterbridge --version
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The storage position is **not compatible** with the traditional setup (~/Matterbridge ~/.matterbridge ~/.mattercert).
|
|
71
|
+
|
|
72
|
+
You may want to copy the contents to the new directories.
|
|
73
|
+
|
|
74
|
+
### 3 - Create a systemctl configuration file for Matterbridge
|
|
75
|
+
|
|
76
|
+
Create a systemctl configuration file for Matterbridge
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
sudo nano /etc/systemd/system/matterbridge.service
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Add the following to this file:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
[Unit]
|
|
86
|
+
Description=matterbridge
|
|
87
|
+
After=network.target
|
|
88
|
+
Wants=network.target
|
|
89
|
+
|
|
90
|
+
[Service]
|
|
91
|
+
Type=simple
|
|
92
|
+
Environment=NODE_ENV=production
|
|
93
|
+
Environment="NPM_CONFIG_PREFIX=/opt/matterbridge/.npm-global"
|
|
94
|
+
ExecStart=matterbridge --service --nosudo
|
|
95
|
+
WorkingDirectory=/opt/matterbridge/Matterbridge
|
|
96
|
+
StandardOutput=inherit
|
|
97
|
+
StandardError=inherit
|
|
98
|
+
Restart=always
|
|
99
|
+
User=matterbridge
|
|
100
|
+
Group=matterbridge
|
|
101
|
+
NoNewPrivileges=true
|
|
102
|
+
PrivateTmp=true
|
|
103
|
+
ProtectSystem=full
|
|
104
|
+
ProtectHome=true
|
|
105
|
+
ReadWritePaths=/opt/matterbridge
|
|
106
|
+
|
|
107
|
+
[Install]
|
|
108
|
+
WantedBy=multi-user.target
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If you use the frontend with **-ssl** -frontend 443 and get an error message: "Port 443 requires elevated privileges",
|
|
112
|
+
add this:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
[Service]
|
|
116
|
+
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
If you use the **matterbridge-bthome** plugin add this:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
[Service]
|
|
123
|
+
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_NET_ADMIN
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Now and if you modify matterbridge.service after, run:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
sudo systemctl daemon-reload
|
|
130
|
+
sudo systemctl restart matterbridge.service
|
|
131
|
+
sudo systemctl status matterbridge.service
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Start Matterbridge
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
sudo systemctl start matterbridge
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Stop Matterbridge
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
sudo systemctl stop matterbridge
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Show Matterbridge status
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
sudo systemctl status matterbridge.service
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Enable Matterbridge to start automatically on boot
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
sudo systemctl enable matterbridge.service
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Disable Matterbridge from starting automatically on boot
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
sudo systemctl disable matterbridge.service
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### View the log of Matterbridge in real time (this will show the log with colors)
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
sudo journalctl -u matterbridge.service -n 1000 -f --output cat
|
|
168
|
+
```
|
package/README.md
CHANGED
|
@@ -30,11 +30,11 @@ Matterbridge creates a device that can be paired with any ecosystem, such as App
|
|
|
30
30
|
|
|
31
31
|
You don't need a hub or a dedicated new machine.
|
|
32
32
|
|
|
33
|
-
No complex setup: just copy paste the installation scripts.
|
|
33
|
+
No complex setup: just copy paste the installation scripts (available for Docker, Nginx, Linux systemctl and macOS launchctl).
|
|
34
34
|
|
|
35
35
|
Matterbridge is lightweight and also runs on slow Linux machines with as little as 512MB of memory.
|
|
36
36
|
|
|
37
|
-
It
|
|
37
|
+
It runs perfectly on Linux, macOS and Windows.
|
|
38
38
|
|
|
39
39
|
If you like this project and find it useful, please consider giving it a star on GitHub at https://github.com/Luligu/matterbridge and sponsoring it.
|
|
40
40
|
|
|
@@ -64,6 +64,8 @@ https://www.matteralpha.com/how-to/how-to-configure-an-open-source-matter-bridge
|
|
|
64
64
|
|
|
65
65
|
https://matter-smarthome.de/en/interview/an-alternative-to-the-official-matter-sdk/
|
|
66
66
|
|
|
67
|
+
https://blog.adafruit.com/2025/11/03/matterbridge-a-matter-plugin-manager/
|
|
68
|
+
|
|
67
69
|
## Prerequisites
|
|
68
70
|
|
|
69
71
|
To run Matterbridge, you need either a [Node.js](https://nodejs.org/en) environment or [Docker](https://docs.docker.com/get-started/get-docker/) installed on your system.
|
package/dist/broadcastServer.js
CHANGED
|
@@ -3,11 +3,14 @@ if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
|
3
3
|
import { EventEmitter } from 'node:events';
|
|
4
4
|
import { BroadcastChannel } from 'node:worker_threads';
|
|
5
5
|
import { debugStringify } from 'node-ansi-logger';
|
|
6
|
+
import { hasParameter } from './utils/commandLine.js';
|
|
6
7
|
export class BroadcastServer extends EventEmitter {
|
|
7
8
|
name;
|
|
8
9
|
log;
|
|
9
10
|
channel;
|
|
10
11
|
broadcastChannel;
|
|
12
|
+
debug = hasParameter('debug') || hasParameter('verbose');
|
|
13
|
+
verbose = hasParameter('verbose');
|
|
11
14
|
constructor(name, log, channel = 'broadcast-channel') {
|
|
12
15
|
super();
|
|
13
16
|
this.name = name;
|
|
@@ -31,10 +34,13 @@ export class BroadcastServer extends EventEmitter {
|
|
|
31
34
|
}
|
|
32
35
|
broadcastMessageHandler(event) {
|
|
33
36
|
const data = event.data;
|
|
34
|
-
this.
|
|
37
|
+
if (this.verbose)
|
|
38
|
+
this.log.debug(`Received broadcast message: ${debugStringify(data)}`);
|
|
35
39
|
this.emit('broadcast_message', data);
|
|
36
40
|
}
|
|
37
41
|
broadcast(message) {
|
|
42
|
+
if (this.verbose)
|
|
43
|
+
this.log.debug(`Broadcasting message: ${debugStringify(message)}`);
|
|
38
44
|
this.broadcastChannel.postMessage(message);
|
|
39
45
|
}
|
|
40
46
|
request(message) {
|
|
@@ -48,7 +54,8 @@ export class BroadcastServer extends EventEmitter {
|
|
|
48
54
|
this.log.error(`Invalid request message format for broadcast: ${debugStringify(message)}`);
|
|
49
55
|
return;
|
|
50
56
|
}
|
|
51
|
-
this.
|
|
57
|
+
if (this.verbose)
|
|
58
|
+
this.log.debug(`Broadcasting request message: ${debugStringify(message)}`);
|
|
52
59
|
this.broadcastChannel.postMessage(message);
|
|
53
60
|
}
|
|
54
61
|
respond(message) {
|
|
@@ -59,23 +66,26 @@ export class BroadcastServer extends EventEmitter {
|
|
|
59
66
|
this.log.error(`Invalid response message format for broadcast: ${debugStringify(message)}`);
|
|
60
67
|
return;
|
|
61
68
|
}
|
|
62
|
-
this.
|
|
69
|
+
if (this.verbose)
|
|
70
|
+
this.log.debug(`Broadcasting response message: ${debugStringify(message)}`);
|
|
63
71
|
this.broadcastChannel.postMessage(message);
|
|
64
72
|
}
|
|
65
|
-
async fetch(message) {
|
|
73
|
+
async fetch(message, timeout = 100) {
|
|
66
74
|
if (message.id === undefined) {
|
|
67
75
|
message.id = this.getUniqueId();
|
|
68
76
|
}
|
|
69
77
|
if (message.timestamp === undefined) {
|
|
70
78
|
message.timestamp = Date.now();
|
|
71
79
|
}
|
|
72
|
-
this.
|
|
80
|
+
if (this.verbose)
|
|
81
|
+
this.log.debug(`Fetching message: ${debugStringify(message)}`);
|
|
73
82
|
return new Promise((resolve, reject) => {
|
|
74
83
|
const responseHandler = (msg) => {
|
|
75
84
|
if (this.isWorkerResponse(msg, message.type) && msg.id === message.id) {
|
|
76
85
|
clearTimeout(timeoutId);
|
|
77
86
|
this.off('broadcast_message', responseHandler);
|
|
78
|
-
this.
|
|
87
|
+
if (this.verbose)
|
|
88
|
+
this.log.debug(`Fetch response: ${debugStringify(msg)}`);
|
|
79
89
|
resolve(msg);
|
|
80
90
|
}
|
|
81
91
|
};
|
|
@@ -83,8 +93,8 @@ export class BroadcastServer extends EventEmitter {
|
|
|
83
93
|
this.request(message);
|
|
84
94
|
const timeoutId = setTimeout(() => {
|
|
85
95
|
this.off('broadcast_message', responseHandler);
|
|
86
|
-
reject(new Error(`Fetch timeout after
|
|
87
|
-
},
|
|
96
|
+
reject(new Error(`Fetch timeout after ${timeout}ms for message id ${message.id}`));
|
|
97
|
+
}, timeout);
|
|
88
98
|
});
|
|
89
99
|
}
|
|
90
100
|
}
|
package/dist/deviceManager.js
CHANGED
|
@@ -6,6 +6,8 @@ export class DeviceManager {
|
|
|
6
6
|
_devices = new Map();
|
|
7
7
|
log;
|
|
8
8
|
server;
|
|
9
|
+
debug = hasParameter('debug') || hasParameter('verbose');
|
|
10
|
+
verbose = hasParameter('verbose');
|
|
9
11
|
constructor() {
|
|
10
12
|
this.log = new AnsiLogger({ logName: 'DeviceManager', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
11
13
|
this.log.debug('Matterbridge device manager starting...');
|
|
@@ -18,7 +20,8 @@ export class DeviceManager {
|
|
|
18
20
|
}
|
|
19
21
|
async msgHandler(msg) {
|
|
20
22
|
if (this.server.isWorkerRequest(msg, msg.type) && (msg.dst === 'all' || msg.dst === 'devices')) {
|
|
21
|
-
this.
|
|
23
|
+
if (this.verbose)
|
|
24
|
+
this.log.debug(`Received request message ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
|
|
22
25
|
switch (msg.type) {
|
|
23
26
|
case 'get_log_level':
|
|
24
27
|
this.server.respond({ ...msg, response: { success: true, logLevel: this.log.logLevel } });
|
|
@@ -49,8 +52,12 @@ export class DeviceManager {
|
|
|
49
52
|
this.clear();
|
|
50
53
|
this.server.respond({ ...msg, response: { success: true } });
|
|
51
54
|
break;
|
|
55
|
+
case 'devices_basearray':
|
|
56
|
+
this.server.respond({ ...msg, response: { devices: this.baseArray(msg.params.pluginName) } });
|
|
57
|
+
break;
|
|
52
58
|
default:
|
|
53
|
-
this.
|
|
59
|
+
if (this.verbose)
|
|
60
|
+
this.log.debug(`Unknown broadcast message ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
63
|
}
|
|
@@ -84,9 +91,31 @@ export class DeviceManager {
|
|
|
84
91
|
clear() {
|
|
85
92
|
this._devices.clear();
|
|
86
93
|
}
|
|
94
|
+
toBaseDevice(device) {
|
|
95
|
+
return {
|
|
96
|
+
pluginName: device.plugin,
|
|
97
|
+
deviceType: device.deviceType,
|
|
98
|
+
number: device.maybeNumber,
|
|
99
|
+
id: device.maybeId,
|
|
100
|
+
deviceName: device.deviceName,
|
|
101
|
+
serialNumber: device.serialNumber,
|
|
102
|
+
uniqueId: device.uniqueId,
|
|
103
|
+
productUrl: device.productUrl,
|
|
104
|
+
configUrl: device.configUrl,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
87
107
|
array() {
|
|
88
108
|
return Array.from(this._devices.values());
|
|
89
109
|
}
|
|
110
|
+
baseArray(pluginName) {
|
|
111
|
+
const devices = [];
|
|
112
|
+
for (const device of this._devices.values()) {
|
|
113
|
+
if (pluginName && pluginName !== device.plugin)
|
|
114
|
+
continue;
|
|
115
|
+
devices.push(this.toBaseDevice(device));
|
|
116
|
+
}
|
|
117
|
+
return devices;
|
|
118
|
+
}
|
|
90
119
|
[Symbol.iterator]() {
|
|
91
120
|
return this._devices.values();
|
|
92
121
|
}
|
package/dist/frontend.js
CHANGED
|
@@ -32,6 +32,8 @@ export class Frontend extends EventEmitter {
|
|
|
32
32
|
httpsServer;
|
|
33
33
|
webSocketServer;
|
|
34
34
|
server;
|
|
35
|
+
debug = hasParameter('debug') || hasParameter('verbose');
|
|
36
|
+
verbose = hasParameter('verbose');
|
|
35
37
|
constructor(matterbridge) {
|
|
36
38
|
super();
|
|
37
39
|
this.matterbridge = matterbridge;
|
|
@@ -45,7 +47,8 @@ export class Frontend extends EventEmitter {
|
|
|
45
47
|
}
|
|
46
48
|
async msgHandler(msg) {
|
|
47
49
|
if (this.server.isWorkerRequest(msg, msg.type) && (msg.dst === 'all' || msg.dst === 'frontend')) {
|
|
48
|
-
this.
|
|
50
|
+
if (this.verbose)
|
|
51
|
+
this.log.debug(`Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
|
|
49
52
|
switch (msg.type) {
|
|
50
53
|
case 'get_log_level':
|
|
51
54
|
this.server.respond({ ...msg, response: { success: true, logLevel: this.log.logLevel } });
|
|
@@ -87,11 +90,13 @@ export class Frontend extends EventEmitter {
|
|
|
87
90
|
this.server.respond({ ...msg, response: { success: true } });
|
|
88
91
|
break;
|
|
89
92
|
default:
|
|
90
|
-
this.
|
|
93
|
+
if (this.verbose)
|
|
94
|
+
this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
|
|
91
95
|
}
|
|
92
96
|
}
|
|
93
97
|
if (this.server.isWorkerResponse(msg, msg.type)) {
|
|
94
|
-
this.
|
|
98
|
+
if (this.verbose)
|
|
99
|
+
this.log.debug(`Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
|
|
95
100
|
switch (msg.type) {
|
|
96
101
|
case 'get_log_level':
|
|
97
102
|
case 'set_log_level':
|
|
@@ -119,7 +124,8 @@ export class Frontend extends EventEmitter {
|
|
|
119
124
|
}
|
|
120
125
|
break;
|
|
121
126
|
default:
|
|
122
|
-
this.
|
|
127
|
+
if (this.verbose)
|
|
128
|
+
this.log.debug(`Unknown broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
|
|
123
129
|
}
|
|
124
130
|
}
|
|
125
131
|
}
|
|
@@ -1192,22 +1198,19 @@ export class Frontend extends EventEmitter {
|
|
|
1192
1198
|
this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
|
|
1193
1199
|
return;
|
|
1194
1200
|
}
|
|
1201
|
+
this.wssSendSnackbarMessage(`Adding plugin ${data.params.pluginNameOrPath}...`, 5);
|
|
1202
|
+
this.log.debug(`Adding plugin ${data.params.pluginNameOrPath}...`);
|
|
1195
1203
|
data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
|
|
1196
|
-
if (this.matterbridge.plugins.has(data.params.pluginNameOrPath)) {
|
|
1197
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} already added` });
|
|
1198
|
-
this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} already added`, 10, 'warning');
|
|
1199
|
-
return;
|
|
1200
|
-
}
|
|
1201
1204
|
const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
|
|
1202
1205
|
if (plugin) {
|
|
1203
1206
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1204
1207
|
this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
|
|
1205
1208
|
this.matterbridge.plugins
|
|
1206
|
-
.load(plugin
|
|
1209
|
+
.load(plugin)
|
|
1207
1210
|
.then(() => {
|
|
1208
1211
|
this.wssSendRefreshRequired('plugins');
|
|
1209
1212
|
this.wssSendRefreshRequired('devices');
|
|
1210
|
-
this.wssSendSnackbarMessage(`
|
|
1213
|
+
this.wssSendSnackbarMessage(`Loaded plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
|
|
1211
1214
|
return;
|
|
1212
1215
|
})
|
|
1213
1216
|
.catch((_error) => {
|
|
@@ -1223,6 +1226,8 @@ export class Frontend extends EventEmitter {
|
|
|
1223
1226
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/removeplugin' });
|
|
1224
1227
|
return;
|
|
1225
1228
|
}
|
|
1229
|
+
this.wssSendSnackbarMessage(`Removing plugin ${data.params.pluginName}...`, 5);
|
|
1230
|
+
this.log.debug(`Removing plugin ${data.params.pluginName}...`);
|
|
1226
1231
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1227
1232
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
1228
1233
|
await this.matterbridge.plugins.remove(data.params.pluginName);
|
package/dist/matterbridge.js
CHANGED
|
@@ -120,6 +120,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
120
120
|
aggregatorUniqueId = getParameter('uniqueId');
|
|
121
121
|
advertisingNodes = new Map();
|
|
122
122
|
server;
|
|
123
|
+
debug = hasParameter('debug') || hasParameter('verbose');
|
|
124
|
+
verbose = hasParameter('verbose');
|
|
123
125
|
constructor() {
|
|
124
126
|
super();
|
|
125
127
|
this.log.logNameColor = '\x1b[38;5;115m';
|
|
@@ -131,7 +133,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
131
133
|
}
|
|
132
134
|
async msgHandler(msg) {
|
|
133
135
|
if (this.server.isWorkerRequest(msg, msg.type) && (msg.dst === 'all' || msg.dst === 'matterbridge')) {
|
|
134
|
-
this.
|
|
136
|
+
if (this.verbose)
|
|
137
|
+
this.log.debug(`Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
|
|
135
138
|
switch (msg.type) {
|
|
136
139
|
case 'get_log_level':
|
|
137
140
|
this.server.respond({ ...msg, response: { success: true, logLevel: this.log.logLevel } });
|
|
@@ -141,17 +144,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
141
144
|
this.server.respond({ ...msg, response: { success: true, logLevel: this.log.logLevel } });
|
|
142
145
|
break;
|
|
143
146
|
default:
|
|
144
|
-
this.
|
|
147
|
+
if (this.verbose)
|
|
148
|
+
this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
|
|
145
149
|
}
|
|
146
150
|
}
|
|
147
151
|
if (this.server.isWorkerResponse(msg, msg.type)) {
|
|
148
|
-
this.
|
|
152
|
+
if (this.verbose)
|
|
153
|
+
this.log.debug(`Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
|
|
149
154
|
switch (msg.type) {
|
|
150
155
|
case 'get_log_level':
|
|
151
156
|
case 'set_log_level':
|
|
152
157
|
break;
|
|
153
158
|
default:
|
|
154
|
-
this.
|
|
159
|
+
if (this.verbose)
|
|
160
|
+
this.log.debug(`Unknown broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
|
|
155
161
|
}
|
|
156
162
|
}
|
|
157
163
|
}
|
package/dist/pluginManager.js
CHANGED
|
@@ -5,10 +5,12 @@ import { inspectError, logError } from './utils/error.js';
|
|
|
5
5
|
import { hasParameter } from './utils/commandLine.js';
|
|
6
6
|
import { BroadcastServer } from './broadcastServer.js';
|
|
7
7
|
export class PluginManager extends EventEmitter {
|
|
8
|
-
_plugins = new Map();
|
|
9
8
|
matterbridge;
|
|
9
|
+
_plugins = new Map();
|
|
10
10
|
log;
|
|
11
11
|
server;
|
|
12
|
+
debug = hasParameter('debug') || hasParameter('verbose');
|
|
13
|
+
verbose = hasParameter('verbose');
|
|
12
14
|
constructor(matterbridge) {
|
|
13
15
|
super();
|
|
14
16
|
this.matterbridge = matterbridge;
|
|
@@ -23,7 +25,8 @@ export class PluginManager extends EventEmitter {
|
|
|
23
25
|
}
|
|
24
26
|
async msgHandler(msg) {
|
|
25
27
|
if (this.server.isWorkerRequest(msg, msg.type) && (msg.dst === 'all' || msg.dst === 'plugins')) {
|
|
26
|
-
this.
|
|
28
|
+
if (this.verbose)
|
|
29
|
+
this.log.debug(`Received request message ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
|
|
27
30
|
switch (msg.type) {
|
|
28
31
|
case 'get_log_level':
|
|
29
32
|
this.server.respond({ ...msg, response: { success: true, logLevel: this.log.logLevel } });
|
|
@@ -42,13 +45,24 @@ export class PluginManager extends EventEmitter {
|
|
|
42
45
|
this.server.respond({ ...msg, response: { has: this.has(msg.params.name) } });
|
|
43
46
|
break;
|
|
44
47
|
case 'plugins_get':
|
|
45
|
-
|
|
48
|
+
{
|
|
49
|
+
const plugin = this.get(msg.params.name);
|
|
50
|
+
if (plugin) {
|
|
51
|
+
this.server.respond({ ...msg, response: { plugin: this.toApiPlugin(plugin) } });
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
this.server.respond({ ...msg, response: { plugin: undefined } });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
46
57
|
break;
|
|
47
58
|
case 'plugins_set':
|
|
48
59
|
this.server.respond({ ...msg, response: { plugin: this.set(msg.params.plugin) } });
|
|
49
60
|
break;
|
|
50
|
-
case '
|
|
51
|
-
this.server.respond({ ...msg, response: { plugins: this.
|
|
61
|
+
case 'plugins_storagepluginarray':
|
|
62
|
+
this.server.respond({ ...msg, response: { plugins: this.storagePluginArray() } });
|
|
63
|
+
break;
|
|
64
|
+
case 'plugins_apipluginarray':
|
|
65
|
+
this.server.respond({ ...msg, response: { plugins: this.apiPluginArray() } });
|
|
52
66
|
break;
|
|
53
67
|
case 'plugins_install':
|
|
54
68
|
this.server.respond({ ...msg, response: { packageName: msg.params.packageName, success: await this.install(msg.params.packageName) } });
|
|
@@ -56,8 +70,97 @@ export class PluginManager extends EventEmitter {
|
|
|
56
70
|
case 'plugins_uninstall':
|
|
57
71
|
this.server.respond({ ...msg, response: { packageName: msg.params.packageName, success: await this.uninstall(msg.params.packageName) } });
|
|
58
72
|
break;
|
|
73
|
+
case 'plugins_add':
|
|
74
|
+
{
|
|
75
|
+
const plugin = await this.add(msg.params.nameOrPath);
|
|
76
|
+
if (plugin) {
|
|
77
|
+
this.server.respond({ ...msg, response: { plugin: this.toApiPlugin(plugin) } });
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
this.server.respond({ ...msg, response: { plugin } });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
case 'plugins_remove':
|
|
85
|
+
{
|
|
86
|
+
const plugin = await this.remove(msg.params.nameOrPath);
|
|
87
|
+
if (plugin) {
|
|
88
|
+
this.server.respond({ ...msg, response: { plugin: this.toApiPlugin(plugin) } });
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
this.server.respond({ ...msg, response: { plugin } });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
case 'plugins_enable':
|
|
96
|
+
{
|
|
97
|
+
const plugin = await this.enable(msg.params.nameOrPath);
|
|
98
|
+
if (plugin) {
|
|
99
|
+
this.server.respond({ ...msg, response: { plugin: this.toApiPlugin(plugin) } });
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
this.server.respond({ ...msg, response: { plugin } });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
case 'plugins_disable':
|
|
107
|
+
{
|
|
108
|
+
const plugin = await this.disable(msg.params.nameOrPath);
|
|
109
|
+
if (plugin) {
|
|
110
|
+
this.server.respond({ ...msg, response: { plugin: this.toApiPlugin(plugin) } });
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
this.server.respond({ ...msg, response: { plugin } });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
117
|
+
case 'plugins_load':
|
|
118
|
+
{
|
|
119
|
+
const platform = await this.load(msg.params.plugin);
|
|
120
|
+
if (platform) {
|
|
121
|
+
this.server.respond({ ...msg, params: {}, response: { platform: {} } });
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
this.server.respond({ ...msg, response: { platform } });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
case 'plugins_start':
|
|
129
|
+
{
|
|
130
|
+
const plugin = await this.start(msg.params.plugin, msg.params.message, msg.params.configure);
|
|
131
|
+
if (plugin) {
|
|
132
|
+
this.server.respond({ ...msg, params: {}, response: { plugin: this.toApiPlugin(plugin) } });
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this.server.respond({ ...msg, response: { plugin } });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
case 'plugins_configure':
|
|
140
|
+
{
|
|
141
|
+
const plugin = await this.configure(msg.params.plugin);
|
|
142
|
+
if (plugin) {
|
|
143
|
+
this.server.respond({ ...msg, params: {}, response: { plugin: this.toApiPlugin(plugin) } });
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
this.server.respond({ ...msg, response: { plugin } });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
break;
|
|
150
|
+
case 'plugins_shutdown':
|
|
151
|
+
{
|
|
152
|
+
const plugin = await this.shutdown(msg.params.plugin, msg.params.reason, msg.params.removeAllDevices, msg.params.force);
|
|
153
|
+
if (plugin) {
|
|
154
|
+
this.server.respond({ ...msg, params: {}, response: { plugin: this.toApiPlugin(plugin) } });
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
this.server.respond({ ...msg, response: { plugin } });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
59
161
|
default:
|
|
60
|
-
this.
|
|
162
|
+
if (this.verbose)
|
|
163
|
+
this.log.debug(`Unknown broadcast message ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
|
|
61
164
|
}
|
|
62
165
|
}
|
|
63
166
|
}
|
|
@@ -80,41 +183,62 @@ export class PluginManager extends EventEmitter {
|
|
|
80
183
|
clear() {
|
|
81
184
|
this._plugins.clear();
|
|
82
185
|
}
|
|
186
|
+
toStoragePlugin(plugin) {
|
|
187
|
+
return {
|
|
188
|
+
name: plugin.name,
|
|
189
|
+
path: plugin.path,
|
|
190
|
+
type: plugin.type,
|
|
191
|
+
version: plugin.version,
|
|
192
|
+
description: plugin.description,
|
|
193
|
+
author: plugin.author,
|
|
194
|
+
enabled: plugin.enabled,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
toApiPlugin(plugin) {
|
|
198
|
+
return {
|
|
199
|
+
name: plugin.name,
|
|
200
|
+
version: plugin.version,
|
|
201
|
+
description: plugin.description,
|
|
202
|
+
author: plugin.author,
|
|
203
|
+
path: plugin.path,
|
|
204
|
+
type: plugin.type,
|
|
205
|
+
latestVersion: plugin.latestVersion,
|
|
206
|
+
devVersion: plugin.devVersion,
|
|
207
|
+
homepage: plugin.homepage,
|
|
208
|
+
help: plugin.help,
|
|
209
|
+
changelog: plugin.changelog,
|
|
210
|
+
funding: plugin.funding,
|
|
211
|
+
locked: plugin.locked,
|
|
212
|
+
error: plugin.error,
|
|
213
|
+
enabled: plugin.enabled,
|
|
214
|
+
loaded: plugin.loaded,
|
|
215
|
+
started: plugin.started,
|
|
216
|
+
configured: plugin.configured,
|
|
217
|
+
restartRequired: plugin.restartRequired,
|
|
218
|
+
registeredDevices: plugin.registeredDevices,
|
|
219
|
+
configJson: plugin.configJson,
|
|
220
|
+
schemaJson: plugin.schemaJson,
|
|
221
|
+
hasWhiteList: plugin.hasWhiteList,
|
|
222
|
+
hasBlackList: plugin.hasBlackList,
|
|
223
|
+
matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
83
226
|
array() {
|
|
84
227
|
return Array.from(this._plugins.values());
|
|
85
228
|
}
|
|
86
|
-
|
|
87
|
-
const
|
|
229
|
+
storagePluginArray() {
|
|
230
|
+
const storagePlugins = [];
|
|
88
231
|
for (const plugin of this._plugins.values()) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
devVersion: plugin.devVersion,
|
|
98
|
-
homepage: plugin.homepage,
|
|
99
|
-
help: plugin.help,
|
|
100
|
-
changelog: plugin.changelog,
|
|
101
|
-
funding: plugin.funding,
|
|
102
|
-
locked: plugin.locked,
|
|
103
|
-
error: plugin.error,
|
|
104
|
-
enabled: plugin.enabled,
|
|
105
|
-
loaded: plugin.loaded,
|
|
106
|
-
started: plugin.started,
|
|
107
|
-
configured: plugin.configured,
|
|
108
|
-
restartRequired: plugin.restartRequired,
|
|
109
|
-
registeredDevices: plugin.registeredDevices,
|
|
110
|
-
configJson: plugin.configJson,
|
|
111
|
-
schemaJson: plugin.schemaJson,
|
|
112
|
-
hasWhiteList: plugin.hasWhiteList,
|
|
113
|
-
hasBlackList: plugin.hasBlackList,
|
|
114
|
-
matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
|
|
115
|
-
});
|
|
232
|
+
storagePlugins.push(this.toStoragePlugin(plugin));
|
|
233
|
+
}
|
|
234
|
+
return storagePlugins;
|
|
235
|
+
}
|
|
236
|
+
apiPluginArray() {
|
|
237
|
+
const apiPlugins = [];
|
|
238
|
+
for (const plugin of this._plugins.values()) {
|
|
239
|
+
apiPlugins.push(this.toApiPlugin(plugin));
|
|
116
240
|
}
|
|
117
|
-
return
|
|
241
|
+
return apiPlugins;
|
|
118
242
|
}
|
|
119
243
|
[Symbol.iterator]() {
|
|
120
244
|
return this._plugins.values();
|
|
@@ -244,6 +368,7 @@ export class PluginManager extends EventEmitter {
|
|
|
244
368
|
}
|
|
245
369
|
}
|
|
246
370
|
async install(packageName) {
|
|
371
|
+
this.log.debug(`Installing plugin ${plg}${packageName}${db}...`);
|
|
247
372
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
248
373
|
try {
|
|
249
374
|
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', packageName, '--omit=dev', '--verbose'], 'install', packageName);
|
|
@@ -262,14 +387,17 @@ export class PluginManager extends EventEmitter {
|
|
|
262
387
|
await this.matterbridge.shutdownProcess();
|
|
263
388
|
}
|
|
264
389
|
}
|
|
390
|
+
this.log.debug(`Installed plugin ${plg}${packageName}${db} successfully`);
|
|
265
391
|
return true;
|
|
266
392
|
}
|
|
267
393
|
catch (error) {
|
|
268
394
|
inspectError(this.log, `Failed to install package ${plg}${packageName}${er}`, error);
|
|
395
|
+
this.log.debug(`Failed to install plugin ${plg}${packageName}${db}`);
|
|
269
396
|
return false;
|
|
270
397
|
}
|
|
271
398
|
}
|
|
272
399
|
async uninstall(packageName) {
|
|
400
|
+
this.log.debug(`Uninstalling plugin ${plg}${packageName}${db}...`);
|
|
273
401
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
274
402
|
packageName = packageName.replace(/@.*$/, '');
|
|
275
403
|
if (packageName === 'matterbridge')
|
|
@@ -282,10 +410,12 @@ export class PluginManager extends EventEmitter {
|
|
|
282
410
|
await this.remove(packageName);
|
|
283
411
|
}
|
|
284
412
|
await spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', packageName, '--verbose'], 'uninstall', packageName);
|
|
413
|
+
this.log.debug(`Uninstalled plugin ${plg}${packageName}${db} successfully`);
|
|
285
414
|
return true;
|
|
286
415
|
}
|
|
287
416
|
catch (error) {
|
|
288
417
|
inspectError(this.log, `Failed to uninstall package ${plg}${packageName}${er}`, error);
|
|
418
|
+
this.log.debug(`Failed to uninstall plugin ${plg}${packageName}${db}`);
|
|
289
419
|
return false;
|
|
290
420
|
}
|
|
291
421
|
}
|
|
@@ -346,6 +476,14 @@ export class PluginManager extends EventEmitter {
|
|
|
346
476
|
}
|
|
347
477
|
async parse(plugin) {
|
|
348
478
|
const { promises } = await import('node:fs');
|
|
479
|
+
if (typeof plugin === 'string') {
|
|
480
|
+
const p = this._plugins.get(plugin);
|
|
481
|
+
if (!p) {
|
|
482
|
+
this.log.error(`Plugin ${plg}${plugin}${er} not found`);
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
plugin = p;
|
|
486
|
+
}
|
|
349
487
|
try {
|
|
350
488
|
this.log.debug(`Parsing package.json of plugin ${plg}${plugin.name}${db}`);
|
|
351
489
|
const packageJson = JSON.parse(await promises.readFile(plugin.path, 'utf8'));
|
|
@@ -570,6 +708,14 @@ export class PluginManager extends EventEmitter {
|
|
|
570
708
|
async load(plugin, start = false, message = '', configure = false) {
|
|
571
709
|
const { promises } = await import('node:fs');
|
|
572
710
|
const { default: path } = await import('node:path');
|
|
711
|
+
if (typeof plugin === 'string') {
|
|
712
|
+
const p = this._plugins.get(plugin);
|
|
713
|
+
if (!p) {
|
|
714
|
+
this.log.error(`Plugin ${plg}${plugin}${er} not found`);
|
|
715
|
+
return undefined;
|
|
716
|
+
}
|
|
717
|
+
plugin = p;
|
|
718
|
+
}
|
|
573
719
|
if (!plugin.enabled) {
|
|
574
720
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not enabled`);
|
|
575
721
|
return undefined;
|
|
@@ -636,6 +782,14 @@ export class PluginManager extends EventEmitter {
|
|
|
636
782
|
return undefined;
|
|
637
783
|
}
|
|
638
784
|
async start(plugin, message, configure = false) {
|
|
785
|
+
if (typeof plugin === 'string') {
|
|
786
|
+
const p = this._plugins.get(plugin);
|
|
787
|
+
if (!p) {
|
|
788
|
+
this.log.error(`Plugin ${plg}${plugin}${er} not found`);
|
|
789
|
+
return undefined;
|
|
790
|
+
}
|
|
791
|
+
plugin = p;
|
|
792
|
+
}
|
|
639
793
|
if (!plugin.loaded) {
|
|
640
794
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded`);
|
|
641
795
|
return undefined;
|
|
@@ -666,6 +820,14 @@ export class PluginManager extends EventEmitter {
|
|
|
666
820
|
return undefined;
|
|
667
821
|
}
|
|
668
822
|
async configure(plugin) {
|
|
823
|
+
if (typeof plugin === 'string') {
|
|
824
|
+
const p = this._plugins.get(plugin);
|
|
825
|
+
if (!p) {
|
|
826
|
+
this.log.error(`Plugin ${plg}${plugin}${er} not found`);
|
|
827
|
+
return undefined;
|
|
828
|
+
}
|
|
829
|
+
plugin = p;
|
|
830
|
+
}
|
|
669
831
|
if (!plugin.loaded) {
|
|
670
832
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded`);
|
|
671
833
|
return undefined;
|
|
@@ -697,6 +859,14 @@ export class PluginManager extends EventEmitter {
|
|
|
697
859
|
return undefined;
|
|
698
860
|
}
|
|
699
861
|
async shutdown(plugin, reason, removeAllDevices = false, force = false) {
|
|
862
|
+
if (typeof plugin === 'string') {
|
|
863
|
+
const p = this._plugins.get(plugin);
|
|
864
|
+
if (!p) {
|
|
865
|
+
this.log.error(`Plugin ${plg}${plugin}${er} not found`);
|
|
866
|
+
return undefined;
|
|
867
|
+
}
|
|
868
|
+
plugin = p;
|
|
869
|
+
}
|
|
700
870
|
this.log.debug(`Shutting down plugin ${plg}${plugin.name}${db}`);
|
|
701
871
|
if (!plugin.loaded) {
|
|
702
872
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} not loaded`);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frontend",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "frontend",
|
|
9
|
-
"version": "3.3.
|
|
9
|
+
"version": "3.3.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@emotion/react": "^11.14.0",
|
|
12
12
|
"@emotion/styled": "^11.14.1",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
|
34
34
|
"@typescript-eslint/parser": "^8.46.2",
|
|
35
35
|
"@vitejs/plugin-react": "^5.1.0",
|
|
36
|
-
"eslint": "9.
|
|
36
|
+
"eslint": "^9.39.1",
|
|
37
37
|
"eslint-config-prettier": "^10.1.8",
|
|
38
38
|
"eslint-plugin-prettier": "^5.5.4",
|
|
39
39
|
"eslint-plugin-react": "^7.37.5",
|
|
@@ -1232,7 +1232,7 @@
|
|
|
1232
1232
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
1233
1233
|
}
|
|
1234
1234
|
},
|
|
1235
|
-
"node_modules/@eslint/
|
|
1235
|
+
"node_modules/@eslint/core": {
|
|
1236
1236
|
"version": "0.17.0",
|
|
1237
1237
|
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
|
|
1238
1238
|
"integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
|
|
@@ -1245,19 +1245,6 @@
|
|
|
1245
1245
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
1246
1246
|
}
|
|
1247
1247
|
},
|
|
1248
|
-
"node_modules/@eslint/core": {
|
|
1249
|
-
"version": "0.16.0",
|
|
1250
|
-
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
|
|
1251
|
-
"integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
|
|
1252
|
-
"dev": true,
|
|
1253
|
-
"license": "Apache-2.0",
|
|
1254
|
-
"dependencies": {
|
|
1255
|
-
"@types/json-schema": "^7.0.15"
|
|
1256
|
-
},
|
|
1257
|
-
"engines": {
|
|
1258
|
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
1259
|
-
}
|
|
1260
|
-
},
|
|
1261
1248
|
"node_modules/@eslint/eslintrc": {
|
|
1262
1249
|
"version": "3.3.1",
|
|
1263
1250
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
|
|
@@ -1341,9 +1328,9 @@
|
|
|
1341
1328
|
}
|
|
1342
1329
|
},
|
|
1343
1330
|
"node_modules/@eslint/js": {
|
|
1344
|
-
"version": "9.
|
|
1345
|
-
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.
|
|
1346
|
-
"integrity": "sha512-
|
|
1331
|
+
"version": "9.39.1",
|
|
1332
|
+
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
|
|
1333
|
+
"integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
|
|
1347
1334
|
"dev": true,
|
|
1348
1335
|
"license": "MIT",
|
|
1349
1336
|
"engines": {
|
|
@@ -1377,19 +1364,6 @@
|
|
|
1377
1364
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
1378
1365
|
}
|
|
1379
1366
|
},
|
|
1380
|
-
"node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
|
|
1381
|
-
"version": "0.17.0",
|
|
1382
|
-
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
|
|
1383
|
-
"integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
|
|
1384
|
-
"dev": true,
|
|
1385
|
-
"license": "Apache-2.0",
|
|
1386
|
-
"dependencies": {
|
|
1387
|
-
"@types/json-schema": "^7.0.15"
|
|
1388
|
-
},
|
|
1389
|
-
"engines": {
|
|
1390
|
-
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
1391
|
-
}
|
|
1392
|
-
},
|
|
1393
1367
|
"node_modules/@humanfs/core": {
|
|
1394
1368
|
"version": "0.19.1",
|
|
1395
1369
|
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
|
@@ -3878,9 +3852,9 @@
|
|
|
3878
3852
|
}
|
|
3879
3853
|
},
|
|
3880
3854
|
"node_modules/eslint": {
|
|
3881
|
-
"version": "9.
|
|
3882
|
-
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.
|
|
3883
|
-
"integrity": "sha512-
|
|
3855
|
+
"version": "9.39.1",
|
|
3856
|
+
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
|
|
3857
|
+
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
|
3884
3858
|
"dev": true,
|
|
3885
3859
|
"license": "MIT",
|
|
3886
3860
|
"peer": true,
|
|
@@ -3888,11 +3862,11 @@
|
|
|
3888
3862
|
"@eslint-community/eslint-utils": "^4.8.0",
|
|
3889
3863
|
"@eslint-community/regexpp": "^4.12.1",
|
|
3890
3864
|
"@eslint/config-array": "^0.21.1",
|
|
3891
|
-
"@eslint/config-helpers": "^0.4.
|
|
3892
|
-
"@eslint/core": "^0.
|
|
3865
|
+
"@eslint/config-helpers": "^0.4.2",
|
|
3866
|
+
"@eslint/core": "^0.17.0",
|
|
3893
3867
|
"@eslint/eslintrc": "^3.3.1",
|
|
3894
|
-
"@eslint/js": "9.
|
|
3895
|
-
"@eslint/plugin-kit": "^0.4.
|
|
3868
|
+
"@eslint/js": "9.39.1",
|
|
3869
|
+
"@eslint/plugin-kit": "^0.4.1",
|
|
3896
3870
|
"@humanfs/node": "^0.16.6",
|
|
3897
3871
|
"@humanwhocodes/module-importer": "^1.0.1",
|
|
3898
3872
|
"@humanwhocodes/retry": "^0.4.2",
|
|
@@ -7766,21 +7740,6 @@
|
|
|
7766
7740
|
"dev": true,
|
|
7767
7741
|
"license": "ISC"
|
|
7768
7742
|
},
|
|
7769
|
-
"node_modules/yaml": {
|
|
7770
|
-
"version": "2.8.1",
|
|
7771
|
-
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
|
|
7772
|
-
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
|
|
7773
|
-
"dev": true,
|
|
7774
|
-
"license": "ISC",
|
|
7775
|
-
"optional": true,
|
|
7776
|
-
"peer": true,
|
|
7777
|
-
"bin": {
|
|
7778
|
-
"yaml": "bin.mjs"
|
|
7779
|
-
},
|
|
7780
|
-
"engines": {
|
|
7781
|
-
"node": ">= 14.6"
|
|
7782
|
-
}
|
|
7783
|
-
},
|
|
7784
7743
|
"node_modules/yocto-queue": {
|
|
7785
7744
|
"version": "0.1.0",
|
|
7786
7745
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
package/frontend/package.json
CHANGED
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
|
67
67
|
"@typescript-eslint/parser": "^8.46.2",
|
|
68
68
|
"@vitejs/plugin-react": "^5.1.0",
|
|
69
|
-
"eslint": "9.
|
|
69
|
+
"eslint": "^9.39.1",
|
|
70
70
|
"eslint-config-prettier": "^10.1.8",
|
|
71
71
|
"eslint-plugin-prettier": "^5.5.4",
|
|
72
72
|
"eslint-plugin-react": "^7.37.5",
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge",
|
|
3
|
-
"version": "3.3.7-dev-
|
|
3
|
+
"version": "3.3.7-dev-20251108-f254812",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "matterbridge",
|
|
9
|
-
"version": "3.3.7-dev-
|
|
9
|
+
"version": "3.3.7-dev-20251108-f254812",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@matter/main": "0.15.6",
|
package/package.json
CHANGED