matterbridge 3.3.7-dev-20251104-7c779b9 → 3.3.7-dev-20251106-de2d9ea

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -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) 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
@@ -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 matterbridge and sudo is not required.
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">&nbsp;&nbsp;&nbsp;Matterbridge systemd configuration with private global node_modules
2
+
3
+ [![npm version](https://img.shields.io/npm/v/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
4
+ [![npm downloads](https://img.shields.io/npm/dt/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
5
+ [![Docker Version](https://img.shields.io/docker/v/luligu/matterbridge?label=docker%20version&sort=semver)](https://hub.docker.com/r/luligu/matterbridge)
6
+ [![Docker Pulls](https://img.shields.io/docker/pulls/luligu/matterbridge.svg)](https://hub.docker.com/r/luligu/matterbridge)
7
+ ![Node.js CI](https://github.com/Luligu/matterbridge/actions/workflows/build.yml/badge.svg)
8
+ ![CodeQL](https://github.com/Luligu/matterbridge/actions/workflows/codeql.yml/badge.svg)
9
+ [![codecov](https://codecov.io/gh/Luligu/matterbridge/branch/main/graph/badge.svg)](https://codecov.io/gh/Luligu/matterbridge)
10
+
11
+ [![power by](https://img.shields.io/badge/powered%20by-matter--history-blue)](https://www.npmjs.com/package/matter-history)
12
+ [![power by](https://img.shields.io/badge/powered%20by-node--ansi--logger-blue)](https://www.npmjs.com/package/node-ansi-logger)
13
+ [![power by](https://img.shields.io/badge/powered%20by-node--persist--manager-blue)](https://www.npmjs.com/package/node-persist-manager)
14
+
15
+ ---
16
+
17
+ # Advanced configuration
18
+
19
+ ## Run matterbridge as a daemon with systemctl (Linux only) with 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 also runs perfectly on macOS and Windows.
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.
@@ -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.log.debug(`Received broadcast message: ${debugStringify(data)}`);
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.log.debug(`Broadcasting request message: ${debugStringify(message)}`);
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.log.debug(`Broadcasting response message: ${debugStringify(message)}`);
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.log.debug(`Fetching message: ${debugStringify(message)}`);
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.log.debug(`Fetch response: ${debugStringify(msg)}`);
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 100ms for message id ${message.id}`));
87
- }, 100);
96
+ reject(new Error(`Fetch timeout after ${timeout}ms for message id ${message.id}`));
97
+ }, timeout);
88
98
  });
89
99
  }
90
100
  }
@@ -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.log.debug(`**Received request message ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
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.log.debug(`Unknown broadcast message ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
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.log.debug(`Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
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.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
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.log.debug(`Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
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.log.debug(`Unknown broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
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, true, 'The plugin has been added', true)
1209
+ .load(plugin)
1207
1210
  .then(() => {
1208
1211
  this.wssSendRefreshRequired('plugins');
1209
1212
  this.wssSendRefreshRequired('devices');
1210
- this.wssSendSnackbarMessage(`Started plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
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);
@@ -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.log.debug(`**Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
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.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
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.log.debug(`**Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
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.log.debug(`Unknown broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
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
  }
@@ -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.log.debug(`**Received request message ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
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
- this.server.respond({ ...msg, response: { plugin: this.get(msg.params.name) } });
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 'plugins_baseArray':
51
- this.server.respond({ ...msg, response: { plugins: this.baseArray() } });
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,75 @@ 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_shutdown':
129
+ {
130
+ const plugin = await this.shutdown(msg.params.plugin, msg.params.reason, msg.params.removeAllDevices, msg.params.force);
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;
59
139
  default:
60
- this.log.debug(`Unknown broadcast message ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
140
+ if (this.verbose)
141
+ this.log.debug(`Unknown broadcast message ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
61
142
  }
62
143
  }
63
144
  }
@@ -80,41 +161,62 @@ export class PluginManager extends EventEmitter {
80
161
  clear() {
81
162
  this._plugins.clear();
82
163
  }
164
+ toStoragePlugin(plugin) {
165
+ return {
166
+ name: plugin.name,
167
+ path: plugin.path,
168
+ type: plugin.type,
169
+ version: plugin.version,
170
+ description: plugin.description,
171
+ author: plugin.author,
172
+ enabled: plugin.enabled,
173
+ };
174
+ }
175
+ toApiPlugin(plugin) {
176
+ return {
177
+ name: plugin.name,
178
+ version: plugin.version,
179
+ description: plugin.description,
180
+ author: plugin.author,
181
+ path: plugin.path,
182
+ type: plugin.type,
183
+ latestVersion: plugin.latestVersion,
184
+ devVersion: plugin.devVersion,
185
+ homepage: plugin.homepage,
186
+ help: plugin.help,
187
+ changelog: plugin.changelog,
188
+ funding: plugin.funding,
189
+ locked: plugin.locked,
190
+ error: plugin.error,
191
+ enabled: plugin.enabled,
192
+ loaded: plugin.loaded,
193
+ started: plugin.started,
194
+ configured: plugin.configured,
195
+ restartRequired: plugin.restartRequired,
196
+ registeredDevices: plugin.registeredDevices,
197
+ configJson: plugin.configJson,
198
+ schemaJson: plugin.schemaJson,
199
+ hasWhiteList: plugin.hasWhiteList,
200
+ hasBlackList: plugin.hasBlackList,
201
+ matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
202
+ };
203
+ }
83
204
  array() {
84
205
  return Array.from(this._plugins.values());
85
206
  }
86
- baseArray() {
87
- const basePlugins = [];
207
+ storagePluginArray() {
208
+ const storagePlugins = [];
88
209
  for (const plugin of this._plugins.values()) {
89
- basePlugins.push({
90
- name: plugin.name,
91
- version: plugin.version,
92
- description: plugin.description,
93
- author: plugin.author,
94
- path: plugin.path,
95
- type: plugin.type,
96
- latestVersion: plugin.latestVersion,
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
- });
210
+ storagePlugins.push(this.toStoragePlugin(plugin));
211
+ }
212
+ return storagePlugins;
213
+ }
214
+ apiPluginArray() {
215
+ const apiPlugins = [];
216
+ for (const plugin of this._plugins.values()) {
217
+ apiPlugins.push(this.toApiPlugin(plugin));
116
218
  }
117
- return basePlugins;
219
+ return apiPlugins;
118
220
  }
119
221
  [Symbol.iterator]() {
120
222
  return this._plugins.values();
@@ -346,6 +448,14 @@ export class PluginManager extends EventEmitter {
346
448
  }
347
449
  async parse(plugin) {
348
450
  const { promises } = await import('node:fs');
451
+ if (typeof plugin === 'string') {
452
+ const p = this._plugins.get(plugin);
453
+ if (!p) {
454
+ this.log.error(`Plugin ${plg}${plugin}${er} not found`);
455
+ return null;
456
+ }
457
+ plugin = p;
458
+ }
349
459
  try {
350
460
  this.log.debug(`Parsing package.json of plugin ${plg}${plugin.name}${db}`);
351
461
  const packageJson = JSON.parse(await promises.readFile(plugin.path, 'utf8'));
@@ -570,6 +680,14 @@ export class PluginManager extends EventEmitter {
570
680
  async load(plugin, start = false, message = '', configure = false) {
571
681
  const { promises } = await import('node:fs');
572
682
  const { default: path } = await import('node:path');
683
+ if (typeof plugin === 'string') {
684
+ const p = this._plugins.get(plugin);
685
+ if (!p) {
686
+ this.log.error(`Plugin ${plg}${plugin}${er} not found`);
687
+ return undefined;
688
+ }
689
+ plugin = p;
690
+ }
573
691
  if (!plugin.enabled) {
574
692
  this.log.error(`Plugin ${plg}${plugin.name}${er} not enabled`);
575
693
  return undefined;
@@ -636,6 +754,14 @@ export class PluginManager extends EventEmitter {
636
754
  return undefined;
637
755
  }
638
756
  async start(plugin, message, configure = false) {
757
+ if (typeof plugin === 'string') {
758
+ const p = this._plugins.get(plugin);
759
+ if (!p) {
760
+ this.log.error(`Plugin ${plg}${plugin}${er} not found`);
761
+ return undefined;
762
+ }
763
+ plugin = p;
764
+ }
639
765
  if (!plugin.loaded) {
640
766
  this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded`);
641
767
  return undefined;
@@ -666,6 +792,14 @@ export class PluginManager extends EventEmitter {
666
792
  return undefined;
667
793
  }
668
794
  async configure(plugin) {
795
+ if (typeof plugin === 'string') {
796
+ const p = this._plugins.get(plugin);
797
+ if (!p) {
798
+ this.log.error(`Plugin ${plg}${plugin}${er} not found`);
799
+ return undefined;
800
+ }
801
+ plugin = p;
802
+ }
669
803
  if (!plugin.loaded) {
670
804
  this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded`);
671
805
  return undefined;
@@ -697,6 +831,14 @@ export class PluginManager extends EventEmitter {
697
831
  return undefined;
698
832
  }
699
833
  async shutdown(plugin, reason, removeAllDevices = false, force = false) {
834
+ if (typeof plugin === 'string') {
835
+ const p = this._plugins.get(plugin);
836
+ if (!p) {
837
+ this.log.error(`Plugin ${plg}${plugin}${er} not found`);
838
+ return undefined;
839
+ }
840
+ plugin = p;
841
+ }
700
842
  this.log.debug(`Shutting down plugin ${plg}${plugin.name}${db}`);
701
843
  if (!plugin.loaded) {
702
844
  this.log.debug(`Plugin ${plg}${plugin.name}${db} not loaded`);
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "frontend",
3
- "version": "3.3.0",
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.0",
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.38.0",
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/config-helpers/node_modules/@eslint/core": {
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.38.0",
1345
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz",
1346
- "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==",
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.38.0",
3882
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz",
3883
- "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
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.1",
3892
- "@eslint/core": "^0.16.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.38.0",
3895
- "@eslint/plugin-kit": "^0.4.0",
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",
@@ -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.38.0",
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",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.3.7-dev-20251104-7c779b9",
3
+ "version": "3.3.7-dev-20251106-de2d9ea",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge",
9
- "version": "3.3.7-dev-20251104-7c779b9",
9
+ "version": "3.3.7-dev-20251106-de2d9ea",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@matter/main": "0.15.6",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.3.7-dev-20251104-7c779b9",
3
+ "version": "3.3.7-dev-20251106-de2d9ea",
4
4
  "description": "Matterbridge plugin manager for Matter",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",