@matterbridge/core 3.6.2-dev-20260315-d23fd18 → 3.6.2-dev-20260316-0b03ae0

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.
@@ -22,6 +22,7 @@ export declare class Frontend extends EventEmitter<FrontendEvents> {
22
22
  private httpsServer;
23
23
  private webSocketServer;
24
24
  private readonly server;
25
+ private serverFetchTimeout;
25
26
  private readonly debug;
26
27
  private readonly verbose;
27
28
  constructor(matterbridge: Matterbridge);
@@ -56,6 +57,7 @@ export declare class Frontend extends EventEmitter<FrontendEvents> {
56
57
  wssSendCloseSnackbarMessage(message: string): void;
57
58
  wssSendAttributeChangedMessage(plugin: string, serialNumber: string, uniqueId: string, number: EndpointNumber, id: string, cluster: string, attribute: string, value: number | string | boolean | null): void;
58
59
  wssBroadcastMessage(msg: WsMessageBroadcast): void;
60
+ spawn(command: 'install' | 'uninstall', packageName: string): void;
59
61
  zip(command: 'zip' | 'verify' | 'unzip', archivePath: string, sourcePaths: string[], destinationPath: string): void;
60
62
  }
61
63
  export {};
package/dist/frontend.js CHANGED
@@ -32,6 +32,7 @@ export class Frontend extends EventEmitter {
32
32
  httpsServer;
33
33
  webSocketServer;
34
34
  server;
35
+ serverFetchTimeout = 2000;
35
36
  debug = hasParameter('debug') || hasParameter('verbose');
36
37
  verbose = hasParameter('verbose');
37
38
  constructor(matterbridge) {
@@ -1275,21 +1276,7 @@ export class Frontend extends EventEmitter {
1275
1276
  else if (data.method === '/api/install') {
1276
1277
  if (isValidString(data.params.packageName, 12) && isValidBoolean(data.params.restart)) {
1277
1278
  this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}...`, 0);
1278
- this.server.request({
1279
- type: 'manager_run',
1280
- src: 'frontend',
1281
- dst: 'manager',
1282
- params: {
1283
- name: 'SpawnCommand',
1284
- workerData: {
1285
- threadName: 'SpawnCommand',
1286
- command: 'npm',
1287
- args: ['install', '-g', data.params.packageName, '--omit=dev', '--verbose'],
1288
- packageCommand: 'install',
1289
- packageName: data.params.packageName,
1290
- },
1291
- },
1292
- });
1279
+ this.spawn('install', data.params.packageName);
1293
1280
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1294
1281
  }
1295
1282
  else {
@@ -1299,21 +1286,7 @@ export class Frontend extends EventEmitter {
1299
1286
  else if (data.method === '/api/uninstall') {
1300
1287
  if (isValidString(data.params.packageName, 12)) {
1301
1288
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1302
- this.server.request({
1303
- type: 'manager_run',
1304
- src: 'frontend',
1305
- dst: 'manager',
1306
- params: {
1307
- name: 'SpawnCommand',
1308
- workerData: {
1309
- threadName: 'SpawnCommand',
1310
- command: 'npm',
1311
- args: ['uninstall', '-g', data.params.packageName, '--omit=dev', '--verbose'],
1312
- packageCommand: 'uninstall',
1313
- packageName: data.params.packageName,
1314
- },
1315
- },
1316
- });
1289
+ this.spawn('uninstall', data.params.packageName);
1317
1290
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1318
1291
  }
1319
1292
  else {
@@ -1327,26 +1300,24 @@ export class Frontend extends EventEmitter {
1327
1300
  this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
1328
1301
  return;
1329
1302
  }
1330
- this.wssSendSnackbarMessage(`Adding plugin ${data.params.pluginNameOrPath}...`, 5);
1303
+ this.wssSendSnackbarMessage(`Adding plugin ${data.params.pluginNameOrPath}...`, 0);
1331
1304
  this.log.debug(`Adding plugin ${data.params.pluginNameOrPath}...`);
1332
1305
  data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
1333
- const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
1306
+ const plugin = (await this.server.fetch({ type: 'plugins_add', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginNameOrPath } }, this.serverFetchTimeout)).result.plugin;
1334
1307
  if (plugin) {
1335
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1308
+ this.wssSendCloseSnackbarMessage(`Adding plugin ${data.params.pluginNameOrPath}...`);
1336
1309
  this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
1337
- this.matterbridge.plugins
1338
- .load(plugin)
1339
- .then(() => {
1340
- this.wssSendRefreshRequired('plugins');
1341
- this.wssSendRefreshRequired('devices');
1342
- this.wssSendSnackbarMessage(`Loaded plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
1343
- return;
1344
- })
1345
- .catch((_error) => { });
1310
+ await this.server.fetch({ type: 'plugins_load', src: this.server.name, dst: 'plugins', params: { plugin: plugin.name } }, this.serverFetchTimeout);
1311
+ this.wssSendRestartRequired();
1312
+ this.wssSendRefreshRequired('plugins');
1313
+ this.wssSendRefreshRequired('devices');
1314
+ this.wssSendSnackbarMessage(`Loaded plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
1315
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1346
1316
  }
1347
1317
  else {
1348
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
1318
+ this.wssSendCloseSnackbarMessage(`Adding plugin ${data.params.pluginNameOrPath}...`);
1349
1319
  this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
1320
+ sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
1350
1321
  }
1351
1322
  }
1352
1323
  else if (data.method === '/api/removeplugin') {
@@ -1354,11 +1325,17 @@ export class Frontend extends EventEmitter {
1354
1325
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/removeplugin' });
1355
1326
  return;
1356
1327
  }
1357
- this.wssSendSnackbarMessage(`Removing plugin ${data.params.pluginName}...`, 5);
1328
+ this.wssSendSnackbarMessage(`Removing plugin ${data.params.pluginName}...`, 0);
1358
1329
  this.log.debug(`Removing plugin ${data.params.pluginName}...`);
1359
- const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1360
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
1361
- await this.matterbridge.plugins.remove(data.params.pluginName);
1330
+ const devices = (await this.server.fetch({ type: 'devices_basearray', src: this.server.name, dst: 'devices', params: { pluginName: data.params.pluginName } }, this.serverFetchTimeout)).result.devices;
1331
+ for (const device of devices.filter((d) => d.mode === 'server')) {
1332
+ if (device.uniqueId)
1333
+ await this.server.fetch({ type: 'matterbridge_stop_device_server', src: this.server.name, dst: 'matterbridge', params: { deviceUniqueId: device.uniqueId } }, this.serverFetchTimeout);
1334
+ }
1335
+ await this.server.fetch({ type: 'plugins_shutdown', src: this.server.name, dst: 'plugins', params: { plugin: data.params.pluginName, reason: 'The plugin has been removed.', removeAllDevices: true, force: true } }, this.serverFetchTimeout);
1336
+ await this.server.fetch({ type: 'matterbridge_stop_plugin_server', src: this.server.name, dst: 'matterbridge', params: { pluginName: data.params.pluginName } }, this.serverFetchTimeout);
1337
+ await this.server.fetch({ type: 'plugins_remove', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginName } }, this.serverFetchTimeout);
1338
+ this.wssSendCloseSnackbarMessage(`Removing plugin ${data.params.pluginName}...`);
1362
1339
  this.wssSendSnackbarMessage(`Removed plugin ${data.params.pluginName}`, 5, 'success');
1363
1340
  this.wssSendRefreshRequired('plugins');
1364
1341
  this.wssSendRefreshRequired('devices');
@@ -1370,44 +1347,35 @@ export class Frontend extends EventEmitter {
1370
1347
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/enableplugin' });
1371
1348
  return;
1372
1349
  }
1373
- const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1374
- plugin.locked = undefined;
1375
- plugin.error = undefined;
1376
- plugin.loaded = undefined;
1377
- plugin.started = undefined;
1378
- plugin.configured = undefined;
1379
- plugin.platform = undefined;
1380
- plugin.registeredDevices = undefined;
1381
- plugin.matter = undefined;
1382
- await this.matterbridge.plugins.enable(data.params.pluginName);
1350
+ await this.server.fetch({ type: 'plugins_enable', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginName } }, this.serverFetchTimeout);
1383
1351
  this.wssSendSnackbarMessage(`Enabled plugin ${data.params.pluginName}`, 5, 'success');
1384
- setImmediate(async () => {
1385
- await this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true);
1386
- if (plugin.serverNode)
1387
- await this.matterbridge.startServerNode(plugin.serverNode);
1388
- for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode))
1389
- await this.matterbridge.startServerNode(device.serverNode);
1390
- this.wssSendSnackbarMessage(`Started plugin ${localData.params.pluginName}`, 5, 'success');
1391
- this.wssSendRefreshRequired('plugins');
1392
- this.wssSendRefreshRequired('devices');
1393
- sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, success: true });
1394
- });
1352
+ await this.server.fetch({ type: 'plugins_load', src: this.server.name, dst: 'plugins', params: { plugin: data.params.pluginName } }, this.serverFetchTimeout * 10);
1353
+ await this.server.fetch({ type: 'plugins_start', src: this.server.name, dst: 'plugins', params: { plugin: data.params.pluginName } }, this.serverFetchTimeout * 10);
1354
+ await this.server.fetch({ type: 'plugins_configure', src: this.server.name, dst: 'plugins', params: { plugin: data.params.pluginName } }, this.serverFetchTimeout * 10);
1355
+ await this.server.fetch({ type: 'matterbridge_start_plugin_server', src: this.server.name, dst: 'matterbridge', params: { pluginName: data.params.pluginName } }, this.serverFetchTimeout);
1356
+ const devices = (await this.server.fetch({ type: 'devices_basearray', src: this.server.name, dst: 'devices', params: { pluginName: data.params.pluginName } }, this.serverFetchTimeout)).result.devices;
1357
+ for (const device of devices.filter((d) => d.mode === 'server')) {
1358
+ if (device.uniqueId)
1359
+ await this.server.fetch({ type: 'matterbridge_start_device_server', src: this.server.name, dst: 'matterbridge', params: { deviceUniqueId: device.uniqueId } }, this.serverFetchTimeout);
1360
+ }
1361
+ this.wssSendSnackbarMessage(`Started plugin ${localData.params.pluginName}`, 5, 'success');
1362
+ this.wssSendRefreshRequired('plugins');
1363
+ this.wssSendRefreshRequired('devices');
1364
+ sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, success: true });
1395
1365
  }
1396
1366
  else if (data.method === '/api/disableplugin') {
1397
1367
  if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
1398
1368
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/disableplugin' });
1399
1369
  return;
1400
1370
  }
1401
- const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1402
- for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode)) {
1403
- await this.matterbridge.stopServerNode(device.serverNode);
1404
- device.serverNode = undefined;
1371
+ const devices = (await this.server.fetch({ type: 'devices_basearray', src: this.server.name, dst: 'devices', params: { pluginName: data.params.pluginName } }, this.serverFetchTimeout)).result.devices;
1372
+ for (const device of devices.filter((d) => d.mode === 'server')) {
1373
+ if (device.uniqueId)
1374
+ await this.server.fetch({ type: 'matterbridge_stop_device_server', src: this.server.name, dst: 'matterbridge', params: { deviceUniqueId: device.uniqueId } }, this.serverFetchTimeout);
1405
1375
  }
1406
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
1407
- await this.matterbridge.plugins.disable(data.params.pluginName);
1408
- if (plugin.serverNode)
1409
- await this.matterbridge.stopServerNode(plugin.serverNode);
1410
- plugin.serverNode = undefined;
1376
+ await this.server.fetch({ type: 'plugins_shutdown', src: this.server.name, dst: 'plugins', params: { plugin: data.params.pluginName, reason: 'The plugin has been disabled.', removeAllDevices: true, force: true } }, this.serverFetchTimeout * 10);
1377
+ await this.server.fetch({ type: 'matterbridge_stop_plugin_server', src: this.server.name, dst: 'matterbridge', params: { pluginName: data.params.pluginName } }, this.serverFetchTimeout);
1378
+ await this.server.fetch({ type: 'plugins_disable', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginName } }, this.serverFetchTimeout);
1411
1379
  this.wssSendSnackbarMessage(`Disabled plugin ${data.params.pluginName}`, 5, 'success');
1412
1380
  this.wssSendRefreshRequired('plugins');
1413
1381
  this.wssSendRefreshRequired('devices');
@@ -1525,7 +1493,7 @@ export class Frontend extends EventEmitter {
1525
1493
  else if (data.method === '/api/create-config-backup') {
1526
1494
  this.wssSendSnackbarMessage('Creating config backup...', 0);
1527
1495
  this.log.notice(`Creating config backup...`);
1528
- const plugins = (await this.server.fetch({ type: 'plugins_storagepluginarray', src: this.server.name, dst: 'plugins' }, 5000)).result.plugins || [];
1496
+ const plugins = (await this.server.fetch({ type: 'plugins_storagepluginarray', src: this.server.name, dst: 'plugins' }, this.serverFetchTimeout)).result.plugins || [];
1529
1497
  const pluginsPaths = plugins.map((p) => path.join(this.matterbridge.matterbridgeDirectory, p.name + '.config.json'));
1530
1498
  this.zip('zip', path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), pluginsPaths, '');
1531
1499
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -2152,6 +2120,23 @@ export class Frontend extends EventEmitter {
2152
2120
  }
2153
2121
  });
2154
2122
  }
2123
+ spawn(command, packageName) {
2124
+ this.server.request({
2125
+ type: 'manager_run',
2126
+ src: 'frontend',
2127
+ dst: 'manager',
2128
+ params: {
2129
+ name: 'SpawnCommand',
2130
+ workerData: {
2131
+ threadName: 'SpawnCommand',
2132
+ command: 'npm',
2133
+ args: [command, '-g', packageName, '--omit=dev', '--verbose'],
2134
+ packageCommand: command,
2135
+ packageName: packageName,
2136
+ },
2137
+ },
2138
+ });
2139
+ }
2155
2140
  zip(command, archivePath, sourcePaths, destinationPath) {
2156
2141
  this.server.request({
2157
2142
  type: 'manager_run',
@@ -238,6 +238,42 @@ export class Matterbridge extends EventEmitter {
238
238
  case 'matterbridge_shared':
239
239
  this.server.respond({ ...msg, result: { data: this.getSharedMatterbridge(), success: true } });
240
240
  break;
241
+ case 'matterbridge_start_plugin_server':
242
+ {
243
+ const plugin = this.plugins.get(msg.params.pluginName);
244
+ if (plugin && plugin.serverNode)
245
+ await this.startServerNode(plugin.serverNode);
246
+ this.server.respond({ ...msg, result: { success: true } });
247
+ }
248
+ break;
249
+ case 'matterbridge_stop_plugin_server':
250
+ {
251
+ const plugin = this.plugins.get(msg.params.pluginName);
252
+ if (plugin && plugin.serverNode)
253
+ await this.stopServerNode(plugin.serverNode);
254
+ if (plugin && plugin.serverNode)
255
+ plugin.serverNode = undefined;
256
+ this.server.respond({ ...msg, result: { success: true } });
257
+ }
258
+ break;
259
+ case 'matterbridge_start_device_server':
260
+ {
261
+ const device = this.devices.get(msg.params.deviceUniqueId);
262
+ if (device && device.serverNode)
263
+ await this.startServerNode(device.serverNode);
264
+ this.server.respond({ ...msg, result: { success: true } });
265
+ }
266
+ break;
267
+ case 'matterbridge_stop_device_server':
268
+ {
269
+ const device = this.devices.get(msg.params.deviceUniqueId);
270
+ if (device && device.serverNode)
271
+ await this.stopServerNode(device.serverNode);
272
+ if (device && device.serverNode)
273
+ device.serverNode = undefined;
274
+ this.server.respond({ ...msg, result: { success: true } });
275
+ }
276
+ break;
241
277
  default:
242
278
  if (this.verbose)
243
279
  this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
@@ -132,10 +132,10 @@ export class PluginManager extends EventEmitter {
132
132
  {
133
133
  const plugin = await this.remove(msg.params.nameOrPath);
134
134
  if (plugin) {
135
- this.server.respond({ ...msg, result: { plugin: this.toApiPlugin(plugin) } });
135
+ this.server.respond({ ...msg, result: { success: true } });
136
136
  }
137
137
  else {
138
- this.server.respond({ ...msg, result: { plugin } });
138
+ this.server.respond({ ...msg, result: { success: false } });
139
139
  }
140
140
  }
141
141
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/core",
3
- "version": "3.6.2-dev-20260315-d23fd18",
3
+ "version": "3.6.2-dev-20260316-0b03ae0",
4
4
  "description": "Matterbridge core library",
5
5
  "author": "https://github.com/Luligu",
6
6
  "homepage": "https://matterbridge.io/",
@@ -112,7 +112,7 @@
112
112
  }
113
113
  },
114
114
  "engines": {
115
- "node": ">=20.0.0 <21.0.0 || >=22.0.0 <23.0.0 || >=24.0.0 <25.0.0"
115
+ "node": ">=20.19.0 <21.0.0 || >=22.13.0 <23.0.0 || >=24.0.0 <25.0.0"
116
116
  },
117
117
  "files": [
118
118
  "bin",
@@ -122,10 +122,10 @@
122
122
  ],
123
123
  "dependencies": {
124
124
  "@matter/main": "0.16.10",
125
- "@matterbridge/dgram": "3.6.2-dev-20260315-d23fd18",
126
- "@matterbridge/thread": "3.6.2-dev-20260315-d23fd18",
127
- "@matterbridge/types": "3.6.2-dev-20260315-d23fd18",
128
- "@matterbridge/utils": "3.6.2-dev-20260315-d23fd18",
125
+ "@matterbridge/dgram": "3.6.2-dev-20260316-0b03ae0",
126
+ "@matterbridge/thread": "3.6.2-dev-20260316-0b03ae0",
127
+ "@matterbridge/types": "3.6.2-dev-20260316-0b03ae0",
128
+ "@matterbridge/utils": "3.6.2-dev-20260316-0b03ae0",
129
129
  "express": "5.2.1",
130
130
  "multer": "2.1.1",
131
131
  "node-ansi-logger": "3.2.0",