@matterbridge/core 3.9.2-dev-20260620-2b9580a → 3.9.2-dev-20260622-2bad6bf

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.
@@ -1,5 +1,5 @@
1
1
  import type { EndpointNumber } from '@matter/types/datatype';
2
- import type { ApiMatter, RefreshRequiredChanged, SharedMatterbridge, WsMessageBroadcast } from '@matterbridge/types';
2
+ import type { ApiMatter, BridgeStatus, PluginStatusUpdate, RefreshRequiredChanged, SharedMatterbridge, WsMessageBroadcast } from '@matterbridge/types';
3
3
  import { WebSocketServer } from 'ws';
4
4
  import type { Backend } from './backend.js';
5
5
  export declare class BackendWsServer {
@@ -27,6 +27,8 @@ export declare class BackendWsServer {
27
27
  wssSendRestartNotRequired(snackbar?: boolean): void;
28
28
  wssSendUpdateRequired(version: string, devVersion?: boolean): void;
29
29
  wssSendPluginUpdateRequired(plugin: string, version: string, devVersion?: boolean): void;
30
+ wssSendPluginStatusUpdate(plugin: string, status: PluginStatusUpdate): void;
31
+ wssSendMatterbridgeStatusUpdate(status: BridgeStatus): void;
30
32
  wssSendCpuUpdate(cpuUsage: number, processCpuUsage: number): void;
31
33
  wssSendMemoryUpdate(totalMemory: string, freeMemory: string, rss: string, heapTotal: string, heapUsed: string, external: string, arrayBuffers: string): void;
32
34
  wssSendUptimeUpdate(systemUptime: string, processUptime: string): void;
@@ -236,16 +236,30 @@ export class BackendWsServer {
236
236
  if (!this.hasActiveClients())
237
237
  return;
238
238
  if (this.verbose)
239
- this.log.debug('Sending an update required message to all connected clients');
239
+ this.log.debug('Sending a matterbridge version update required message to all connected clients');
240
240
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { version, devVersion } });
241
241
  }
242
242
  wssSendPluginUpdateRequired(plugin, version, devVersion = false) {
243
243
  if (!this.hasActiveClients())
244
244
  return;
245
245
  if (this.verbose)
246
- this.log.debug('Sending a plugin update required message to all connected clients');
246
+ this.log.debug('Sending a plugin version update required message to all connected clients');
247
247
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'plugin_update_required', success: true, response: { plugin, version, devVersion } });
248
248
  }
249
+ wssSendPluginStatusUpdate(plugin, status) {
250
+ if (!this.hasActiveClients())
251
+ return;
252
+ if (this.verbose)
253
+ this.log.debug('Sending a plugin status update message to all connected clients');
254
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'plugin_status_update', success: true, response: { plugin, status } });
255
+ }
256
+ wssSendMatterbridgeStatusUpdate(status) {
257
+ if (!this.hasActiveClients())
258
+ return;
259
+ if (this.verbose)
260
+ this.log.debug('Sending a matterbridge status update message to all connected clients');
261
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'matterbridge_status_update', success: true, response: { status } });
262
+ }
249
263
  wssSendCpuUpdate(cpuUsage, processCpuUsage) {
250
264
  if (!this.hasActiveClients())
251
265
  return;
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'node:events';
2
2
  import { type EndpointNumber } from '@matter/types/datatype';
3
- import type { ApiClusters, ApiDevice, ApiMatter, ApiPlugin, ApiSettings, RefreshRequiredChanged, WsMessageBroadcast } from '@matterbridge/types';
3
+ import type { ApiClusters, ApiDevice, ApiMatter, ApiPlugin, ApiSettings, BridgeStatus, PluginStatusUpdate, RefreshRequiredChanged, WsMessageBroadcast } from '@matterbridge/types';
4
4
  import { LogLevel } from 'node-ansi-logger';
5
5
  import type { Matterbridge } from './matterbridge.js';
6
6
  interface FrontendEvents {
@@ -59,6 +59,8 @@ export declare class Frontend extends EventEmitter<FrontendEvents> {
59
59
  wssSendRestartNotRequired(snackbar?: boolean): void;
60
60
  wssSendUpdateRequired(version: string, devVersion?: boolean): void;
61
61
  wssSendPluginUpdateRequired(plugin: string, version: string, devVersion?: boolean): void;
62
+ wssSendPluginStatusUpdate(plugin: string, status: PluginStatusUpdate): void;
63
+ wssSendMatterbridgeStatusUpdate(status: BridgeStatus): void;
62
64
  wssSendCpuUpdate(cpuUsage: number, processCpuUsage: number): void;
63
65
  wssSendMemoryUpdate(totalMemory: string, freeMemory: string, rss: string, heapTotal: string, heapUsed: string, external: string, arrayBuffers: string): void;
64
66
  wssSendUptimeUpdate(systemUptime: string, processUptime: string): void;
package/dist/frontend.js CHANGED
@@ -100,10 +100,22 @@ export class Frontend extends EventEmitter {
100
100
  this.wssSendPluginUpdateRequired(msg.params.plugin, msg.params.version, msg.params.devVersion);
101
101
  this.server.respond({ ...msg, result: { success: true } });
102
102
  break;
103
+ case 'frontend_pluginstatusupdate':
104
+ this.wssSendPluginStatusUpdate(msg.params.plugin, msg.params.status);
105
+ this.server.respond({ ...msg, result: { success: true } });
106
+ break;
107
+ case 'frontend_matterbridgestatusupdate':
108
+ this.wssSendMatterbridgeStatusUpdate(msg.params.status);
109
+ this.server.respond({ ...msg, result: { success: true } });
110
+ break;
103
111
  case 'frontend_snackbarmessage':
104
112
  this.wssSendSnackbarMessage(msg.params.message, msg.params.timeout, msg.params.severity);
105
113
  this.server.respond({ ...msg, result: { success: true } });
106
114
  break;
115
+ case 'frontend_closesnackbarmessage':
116
+ this.wssSendCloseSnackbarMessage(msg.params.message);
117
+ this.server.respond({ ...msg, result: { success: true } });
118
+ break;
107
119
  case 'frontend_attributechanged':
108
120
  this.wssSendAttributeChangedMessage(msg.params.plugin, msg.params.serialNumber, msg.params.uniqueId, msg.params.number, msg.params.id, msg.params.cluster, msg.params.attribute, msg.params.value);
109
121
  this.server.respond({ ...msg, result: { success: true } });
@@ -935,6 +947,7 @@ export class Frontend extends EventEmitter {
935
947
  bridgeMode: this.matterbridge.bridgeMode,
936
948
  restartMode: this.matterbridge.restartMode,
937
949
  virtualMode: this.matterbridge.virtualMode,
950
+ bridgeStatus: this.matterbridge.bridgeStatus,
938
951
  profile: this.matterbridge.profile,
939
952
  loggerLevel: this.matterbridge.logLevel,
940
953
  fileLogger: this.matterbridge.fileLogger,
@@ -2120,16 +2133,28 @@ export class Frontend extends EventEmitter {
2120
2133
  wssSendUpdateRequired(version, devVersion = false) {
2121
2134
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2122
2135
  return;
2123
- this.log.debug('Sending an update required message to all connected clients');
2136
+ this.log.debug('Sending a matterbridge version update required message to all connected clients');
2124
2137
  this.updateRequired = true;
2125
2138
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { version, devVersion } });
2126
2139
  }
2127
2140
  wssSendPluginUpdateRequired(plugin, version, devVersion = false) {
2128
2141
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2129
2142
  return;
2130
- this.log.debug('Sending a plugin update required message to all connected clients');
2143
+ this.log.debug('Sending a plugin version update required message to all connected clients');
2131
2144
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'plugin_update_required', success: true, response: { plugin, version, devVersion } });
2132
2145
  }
2146
+ wssSendPluginStatusUpdate(plugin, status) {
2147
+ if (!this.listening || this.webSocketServer?.clients.size === 0)
2148
+ return;
2149
+ this.log.debug('Sending a plugin status update message to all connected clients');
2150
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'plugin_status_update', success: true, response: { plugin, status } });
2151
+ }
2152
+ wssSendMatterbridgeStatusUpdate(status) {
2153
+ if (!this.listening || this.webSocketServer?.clients.size === 0)
2154
+ return;
2155
+ this.log.debug('Sending a matterbridge status update message to all connected clients');
2156
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'matterbridge_status_update', success: true, response: { status } });
2157
+ }
2133
2158
  wssSendCpuUpdate(cpuUsage, processCpuUsage) {
2134
2159
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2135
2160
  return;
package/dist/helpers.js CHANGED
@@ -61,7 +61,7 @@ export async function addVirtualDevices(matterbridge, aggregatorEndpoint) {
61
61
  if (matterbridge.virtualMode !== 'disabled' && matterbridge.bridgeMode === 'bridge' && aggregatorEndpoint) {
62
62
  matterbridge.log.notice(`Creating virtual devices for Matterbridge server node...`);
63
63
  await addVirtualDevice(aggregatorEndpoint, 'Restart Matterbridge', matterbridge.virtualMode, async () => {
64
- if (matterbridge.restartMode === '')
64
+ if (matterbridge.restartMode === 'none')
65
65
  await matterbridge.restartProcess();
66
66
  else
67
67
  await matterbridge.shutdownProcess();
@@ -5,7 +5,7 @@ import { Endpoint, ServerNode } from '@matter/node';
5
5
  import { AggregatorEndpoint } from '@matter/node/endpoints/aggregator';
6
6
  import { type DeviceCertification } from '@matter/protocol';
7
7
  import { DeviceTypeId, VendorId } from '@matter/types/datatype';
8
- import type { ApiMatter, ApiSettings, MaybePromise, PlatformMatterbridge, SharedMatterbridge, SystemInformation } from '@matterbridge/types';
8
+ import type { ApiMatter, ApiSettings, BridgeMode, BridgeStatus, MaybePromise, PlatformMatterbridge, RestartMode, SharedMatterbridge, SystemInformation, VirtualMode } from '@matterbridge/types';
9
9
  import { AnsiLogger, LogLevel } from 'node-ansi-logger';
10
10
  import { type NodeStorage, NodeStorageManager } from 'node-persist-manager';
11
11
  import { DeviceManager } from './deviceManager.js';
@@ -50,9 +50,10 @@ export declare class Matterbridge extends EventEmitter<MatterbridgeEvents> {
50
50
  dockerVersion: string | undefined;
51
51
  dockerLatestVersion: string | undefined;
52
52
  dockerDevVersion: string | undefined;
53
- bridgeMode: 'bridge' | 'childbridge' | 'controller' | '';
54
- restartMode: 'service' | 'docker' | '';
55
- virtualMode: 'disabled' | 'outlet' | 'light' | 'switch' | 'mounted_switch';
53
+ bridgeMode: BridgeMode;
54
+ restartMode: RestartMode;
55
+ virtualMode: VirtualMode;
56
+ bridgeStatus: BridgeStatus;
56
57
  readonly profile: string | undefined;
57
58
  readonly log: AnsiLogger;
58
59
  logLevel: LogLevel;
@@ -84,6 +85,7 @@ export declare class Matterbridge extends EventEmitter<MatterbridgeEvents> {
84
85
  private readonly environment;
85
86
  matterStorageService: StorageService | undefined;
86
87
  matterStorageManager: StorageManager | undefined;
88
+ private serverNodeStorageManagers;
87
89
  matterbridgeContext: StorageContext | undefined;
88
90
  controllerContext: StorageContext | undefined;
89
91
  mdnsInterface: string | undefined;
@@ -135,6 +137,7 @@ export declare class Matterbridge extends EventEmitter<MatterbridgeEvents> {
135
137
  shutdownProcessAndReset(): Promise<void>;
136
138
  shutdownProcessAndFactoryReset(): Promise<void>;
137
139
  private cleanup;
140
+ private sendPluginStatusUpdate;
138
141
  private startBridge;
139
142
  private startChildbridge;
140
143
  private startController;
@@ -72,9 +72,10 @@ export class Matterbridge extends EventEmitter {
72
72
  dockerVersion;
73
73
  dockerLatestVersion;
74
74
  dockerDevVersion;
75
- bridgeMode = '';
76
- restartMode = '';
75
+ bridgeMode = 'none';
76
+ restartMode = 'none';
77
77
  virtualMode = 'outlet';
78
+ bridgeStatus = 'inactive';
78
79
  profile = getParameter('profile');
79
80
  log = new AnsiLogger({
80
81
  logName: 'Matterbridge',
@@ -116,6 +117,7 @@ export class Matterbridge extends EventEmitter {
116
117
  environment = Environment.default;
117
118
  matterStorageService;
118
119
  matterStorageManager;
120
+ serverNodeStorageManagers = new Map();
119
121
  matterbridgeContext;
120
122
  controllerContext;
121
123
  mdnsInterface;
@@ -230,6 +232,7 @@ export class Matterbridge extends EventEmitter {
230
232
  bridgeMode: this.bridgeMode,
231
233
  restartMode: this.restartMode,
232
234
  virtualMode: this.virtualMode,
235
+ bridgeStatus: this.bridgeStatus,
233
236
  profile: this.profile,
234
237
  readOnly: hasParameter('readonly'),
235
238
  shellyBoard: false,
@@ -344,7 +347,7 @@ export class Matterbridge extends EventEmitter {
344
347
  const packageName = msg.result.packageName.replace(/@.*$/, '');
345
348
  if (packageName === 'matterbridge') {
346
349
  this.log.info('Matterbridge has been updated. Full restart required.');
347
- if (this.restartMode !== '')
350
+ if (this.restartMode !== 'none')
348
351
  await this.cleanup('updating...', false);
349
352
  }
350
353
  }
@@ -732,7 +735,7 @@ export class Matterbridge extends EventEmitter {
732
735
  (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : '') +
733
736
  (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge') ? 'mode childbridge ' : '') +
734
737
  (hasParameter('controller') ? 'mode controller ' : '') +
735
- (this.restartMode === '' ? '' : 'restart mode ' + this.restartMode + ' ') +
738
+ (this.restartMode === 'none' ? '' : 'restart mode ' + this.restartMode + ' ') +
736
739
  'running on ' + this.systemInformation.osType + ' (v.' + this.systemInformation.osRelease + ') platform ' + this.systemInformation.osPlatform + ' arch ' + this.systemInformation.osArch);
737
740
  const minNodeVersion = 20;
738
741
  const nodeVersion = process.versions.node;
@@ -829,6 +832,7 @@ export class Matterbridge extends EventEmitter {
829
832
  const storageContext = storageManager?.createContext('persist');
830
833
  await storageContext?.set('serialNumber', this.aggregatorSerialNumber);
831
834
  await storageContext?.set('uniqueId', this.aggregatorUniqueId);
835
+ await storageManager.close();
832
836
  }
833
837
  }
834
838
  catch (error) {
@@ -853,6 +857,7 @@ export class Matterbridge extends EventEmitter {
853
857
  await matterStorageManager.createContext('root')?.clearAll();
854
858
  await matterStorageManager.createContext('sessions')?.clearAll();
855
859
  await matterStorageManager.createContext('persist')?.clearAll();
860
+ await matterStorageManager.close();
856
861
  this.log.notice(`Reset commissioning for plugin ${plg}${plugin.name}${nt} done! Remove the device from the controller.`);
857
862
  }
858
863
  else {
@@ -1206,6 +1211,7 @@ export class Matterbridge extends EventEmitter {
1206
1211
  this.emit('cleanup_started');
1207
1212
  this.hasCleanupStarted = true;
1208
1213
  this.log.info(message);
1214
+ this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'stopping') } });
1209
1215
  if (this.startMatterInterval) {
1210
1216
  clearInterval(this.startMatterInterval);
1211
1217
  this.startMatterInterval = undefined;
@@ -1296,6 +1302,7 @@ export class Matterbridge extends EventEmitter {
1296
1302
  await storageManager?.createContext('fabrics')?.clearAll();
1297
1303
  await storageManager?.createContext('root')?.clearAll();
1298
1304
  await storageManager?.createContext('sessions')?.clearAll();
1305
+ await storageManager?.close();
1299
1306
  }
1300
1307
  }
1301
1308
  for (const device of this.devices.array()) {
@@ -1306,6 +1313,7 @@ export class Matterbridge extends EventEmitter {
1306
1313
  await storageManager?.createContext('fabrics')?.clearAll();
1307
1314
  await storageManager?.createContext('root')?.clearAll();
1308
1315
  await storageManager?.createContext('sessions')?.clearAll();
1316
+ await storageManager?.close();
1309
1317
  }
1310
1318
  }
1311
1319
  this.log.info('Matter storage reset done! Remove the bridge from the controller.');
@@ -1354,6 +1362,7 @@ export class Matterbridge extends EventEmitter {
1354
1362
  unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_DIR, device.deviceName.replace(/[ .]/g, ''), 'root.subscriptions.subscriptions'), this.log);
1355
1363
  }
1356
1364
  }
1365
+ this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'stopped') } });
1357
1366
  await this.frontend.stop();
1358
1367
  this.frontend.destroy();
1359
1368
  this.plugins.destroy();
@@ -1442,16 +1451,36 @@ export class Matterbridge extends EventEmitter {
1442
1451
  this.log.debug('Cleanup already started...');
1443
1452
  }
1444
1453
  }
1454
+ sendPluginStatusUpdate(plugin) {
1455
+ this.server.request({
1456
+ type: 'frontend_pluginstatusupdate',
1457
+ src: 'matterbridge',
1458
+ dst: 'frontend',
1459
+ params: {
1460
+ plugin: plugin.name,
1461
+ status: {
1462
+ locked: plugin.locked,
1463
+ error: plugin.error,
1464
+ enabled: plugin.enabled,
1465
+ loaded: plugin.loaded,
1466
+ started: plugin.started,
1467
+ configured: plugin.configured,
1468
+ registeredDevices: plugin.registeredDevices,
1469
+ },
1470
+ },
1471
+ });
1472
+ }
1445
1473
  async startBridge() {
1446
1474
  if (!this.matterStorageManager)
1447
1475
  throw new Error('No storage manager initialized');
1448
1476
  if (!this.matterbridgeContext)
1449
1477
  throw new Error('No storage context initialized');
1478
+ this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'starting') } });
1450
1479
  this.serverNode = await this.createServerNode(this.matterbridgeContext, this.port ? this.port++ : undefined, this.passcode ? this.passcode++ : undefined, this.discriminator ? this.discriminator++ : undefined);
1451
1480
  this.aggregatorNode = await this.createAggregatorNode(this.matterbridgeContext);
1452
1481
  await this.serverNode.add(this.aggregatorNode);
1453
1482
  await addVirtualDevices(this, this.aggregatorNode);
1454
- await this.startPlugins();
1483
+ await this.startPlugins(false, true);
1455
1484
  this.log.debug('Starting start matter interval in bridge mode...');
1456
1485
  this.frontend.wssSendSnackbarMessage(`The bridge is starting...`, 0, 'info');
1457
1486
  let failCount = 0;
@@ -1459,7 +1488,6 @@ export class Matterbridge extends EventEmitter {
1459
1488
  void (async () => {
1460
1489
  if (failCount && failCount % 10 === 0) {
1461
1490
  this.frontend.wssSendSnackbarMessage(`The bridge is still starting...`, 10, 'info');
1462
- this.frontend.wssSendRefreshRequired('plugins');
1463
1491
  }
1464
1492
  for (const plugin of this.plugins) {
1465
1493
  if (!plugin.enabled)
@@ -1475,8 +1503,10 @@ export class Matterbridge extends EventEmitter {
1475
1503
  this.frontend.wssSendSnackbarMessage(`The bridge is offline. Startup halted due to plugin errors.`, 0, 'error');
1476
1504
  this.frontend.wssSendRefreshRequired('plugins');
1477
1505
  this.frontend.wssSendCloseSnackbarMessage(`The bridge is starting...`);
1506
+ this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'error') } });
1478
1507
  return;
1479
1508
  }
1509
+ this.sendPluginStatusUpdate(plugin);
1480
1510
  if (!plugin.loaded || !plugin.started) {
1481
1511
  this.log.debug(`Waiting (failSafeCount=${failCount}/${this.failCountLimit}) in startMatterInterval interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
1482
1512
  failCount++;
@@ -1512,20 +1542,20 @@ export class Matterbridge extends EventEmitter {
1512
1542
  plugin.error = true;
1513
1543
  this.log.error(`Error configuring plugin ${plg}${plugin.name}${er}`, error);
1514
1544
  }
1545
+ this.sendPluginStatusUpdate(plugin);
1515
1546
  }
1516
- this.frontend.wssSendRefreshRequired('plugins');
1517
1547
  })();
1518
1548
  }, 30 * 1000).unref();
1519
1549
  this.reachabilityTimeout = setTimeout(() => {
1520
1550
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1521
1551
  if (this.aggregatorNode)
1522
- fireAndForget(this.setAggregatorReachability(this.aggregatorNode, true), this.log, `Set aggregator node reachability for Matterbridge`);
1552
+ fireAndForget(this.setAggregatorReachability(this.aggregatorNode, true), this.log, `Failed to set aggregator node reachability for Matterbridge`);
1523
1553
  }, 60 * 1000).unref();
1524
1554
  this.emit('bridge_started');
1525
1555
  this.log.notice('Matterbridge bridge started successfully');
1526
- this.frontend.wssSendRefreshRequired('settings');
1527
- this.frontend.wssSendRefreshRequired('plugins');
1528
- this.frontend.wssSendCloseSnackbarMessage(`The bridge is starting...`);
1556
+ this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'started') } });
1557
+ this.server.request({ type: 'frontend_refreshrequired', src: 'matterbridge', dst: 'frontend', params: { changed: 'plugins' } });
1558
+ this.server.request({ type: 'frontend_closesnackbarmessage', src: 'matterbridge', dst: 'frontend', params: { message: `The bridge is starting...` } });
1529
1559
  })();
1530
1560
  }, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
1531
1561
  }
@@ -1533,6 +1563,7 @@ export class Matterbridge extends EventEmitter {
1533
1563
  if (!this.matterStorageManager)
1534
1564
  throw new Error('No storage manager initialized');
1535
1565
  const { wait } = await import('@matterbridge/utils/wait');
1566
+ this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'starting') } });
1536
1567
  this.log.debug('Loading all plugins in childbridge mode...');
1537
1568
  await this.startPlugins(true, false);
1538
1569
  this.log.debug('Creating server nodes for DynamicPlatform plugins and starting all plugins in childbridge mode...');
@@ -1548,7 +1579,6 @@ export class Matterbridge extends EventEmitter {
1548
1579
  void (async () => {
1549
1580
  if (failCount && failCount % 10 === 0) {
1550
1581
  this.frontend.wssSendSnackbarMessage(`The bridge is still starting...`, 10, 'info');
1551
- this.frontend.wssSendRefreshRequired('plugins');
1552
1582
  }
1553
1583
  let allStarted = true;
1554
1584
  for (const plugin of this.plugins.array()) {
@@ -1565,8 +1595,10 @@ export class Matterbridge extends EventEmitter {
1565
1595
  this.frontend.wssSendSnackbarMessage(`The bridge is offline. Startup halted due to plugin errors.`, 0, 'error');
1566
1596
  this.frontend.wssSendRefreshRequired('plugins');
1567
1597
  this.frontend.wssSendCloseSnackbarMessage(`The bridge is starting...`);
1598
+ this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'error') } });
1568
1599
  return;
1569
1600
  }
1601
+ this.sendPluginStatusUpdate(plugin);
1570
1602
  this.log.debug(`Checking plugin ${plg}${plugin.name}${db} to start matter in childbridge mode...`);
1571
1603
  if (!plugin.loaded || !plugin.started) {
1572
1604
  allStarted = false;
@@ -1600,8 +1632,8 @@ export class Matterbridge extends EventEmitter {
1600
1632
  plugin.error = true;
1601
1633
  this.log.error(`Error configuring plugin ${plg}${plugin.name}${er}`, error);
1602
1634
  }
1635
+ this.sendPluginStatusUpdate(plugin);
1603
1636
  }
1604
- this.frontend.wssSendRefreshRequired('plugins');
1605
1637
  })();
1606
1638
  }, 30 * 1000).unref();
1607
1639
  for (const plugin of this.plugins.array()) {
@@ -1638,9 +1670,9 @@ export class Matterbridge extends EventEmitter {
1638
1670
  }
1639
1671
  this.emit('childbridge_started');
1640
1672
  this.log.notice('Matterbridge childbridge started successfully');
1641
- this.frontend.wssSendRefreshRequired('settings');
1642
- this.frontend.wssSendRefreshRequired('plugins');
1643
- this.frontend.wssSendCloseSnackbarMessage(`The bridge is starting...`);
1673
+ this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'started') } });
1674
+ this.server.request({ type: 'frontend_refreshrequired', src: 'matterbridge', dst: 'frontend', params: { changed: 'plugins' } });
1675
+ this.server.request({ type: 'frontend_closesnackbarmessage', src: 'matterbridge', dst: 'frontend', params: { message: `The bridge is starting...` } });
1644
1676
  })();
1645
1677
  }, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
1646
1678
  }
@@ -1670,6 +1702,11 @@ export class Matterbridge extends EventEmitter {
1670
1702
  async stopMatterStorage() {
1671
1703
  this.log.info('Closing matter node storage...');
1672
1704
  await this.matterStorageManager?.close();
1705
+ for (const [storeId, storageManager] of this.serverNodeStorageManagers) {
1706
+ this.log.debug(`Closing server node storage context manager for ${plg}${storeId}${db}...`);
1707
+ await storageManager.close();
1708
+ }
1709
+ this.serverNodeStorageManagers.clear();
1673
1710
  this.matterStorageService = undefined;
1674
1711
  this.matterStorageManager = undefined;
1675
1712
  this.matterbridgeContext = undefined;
@@ -1683,6 +1720,8 @@ export class Matterbridge extends EventEmitter {
1683
1720
  this.log.info(`Creating server node storage context "${storeId}.persist" for ${storeId}...`);
1684
1721
  await createDirectory(path.join(this.matterbridgeDirectory, MATTER_STORAGE_DIR, storeId), `Matter node storage directory for ${storeId}`, this.log);
1685
1722
  const storageManager = await this.matterStorageService.open(storeId);
1723
+ await this.serverNodeStorageManagers.get(storeId)?.close();
1724
+ this.serverNodeStorageManagers.set(storeId, storageManager);
1686
1725
  const storageContext = storageManager.createContext('persist');
1687
1726
  const random = randomBytes(8).toString('hex');
1688
1727
  await storageContext.set('storeId', storeId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/core",
3
- "version": "3.9.2-dev-20260620-2b9580a",
3
+ "version": "3.9.2-dev-20260622-2bad6bf",
4
4
  "description": "Matterbridge core library",
5
5
  "author": "https://github.com/Luligu",
6
6
  "homepage": "https://matterbridge.io/",
@@ -129,11 +129,11 @@
129
129
  "CHANGELOG.md"
130
130
  ],
131
131
  "dependencies": {
132
- "@matter/main": "0.17.3",
133
- "@matterbridge/dgram": "3.9.2-dev-20260620-2b9580a",
134
- "@matterbridge/thread": "3.9.2-dev-20260620-2b9580a",
135
- "@matterbridge/types": "3.9.2-dev-20260620-2b9580a",
136
- "@matterbridge/utils": "3.9.2-dev-20260620-2b9580a",
132
+ "@matter/main": "0.17.4-alpha.0-20260621-81ba50a6a",
133
+ "@matterbridge/dgram": "3.9.2-dev-20260622-2bad6bf",
134
+ "@matterbridge/thread": "3.9.2-dev-20260622-2bad6bf",
135
+ "@matterbridge/types": "3.9.2-dev-20260622-2bad6bf",
136
+ "@matterbridge/utils": "3.9.2-dev-20260622-2bad6bf",
137
137
  "escape-html": "1.0.3",
138
138
  "express": "5.2.1",
139
139
  "express-rate-limit": "8.5.2",