matterbridge 1.5.10 → 1.6.0

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +23 -3
  2. package/README-SERVICE.md +1 -1
  3. package/README.md +7 -7
  4. package/dist/cluster/export.js +1 -1
  5. package/dist/cluster/export.js.map +1 -1
  6. package/dist/defaultConfigSchema.d.ts.map +1 -1
  7. package/dist/defaultConfigSchema.js +9 -1
  8. package/dist/defaultConfigSchema.js.map +1 -1
  9. package/dist/matterbridge.d.ts +72 -47
  10. package/dist/matterbridge.d.ts.map +1 -1
  11. package/dist/matterbridge.js +144 -118
  12. package/dist/matterbridge.js.map +1 -1
  13. package/dist/matterbridgeDevice.d.ts +137 -1029
  14. package/dist/matterbridgeDevice.d.ts.map +1 -1
  15. package/dist/matterbridgeDevice.js +55 -361
  16. package/dist/matterbridgeDevice.js.map +1 -1
  17. package/dist/matterbridgeEdge.d.ts +90 -0
  18. package/dist/matterbridgeEdge.d.ts.map +1 -0
  19. package/dist/matterbridgeEdge.js +555 -0
  20. package/dist/matterbridgeEdge.js.map +1 -0
  21. package/dist/matterbridgeEndpoint.d.ts +5195 -0
  22. package/dist/matterbridgeEndpoint.d.ts.map +1 -0
  23. package/dist/matterbridgeEndpoint.js +2196 -0
  24. package/dist/matterbridgeEndpoint.js.map +1 -0
  25. package/dist/matterbridgeTypes.d.ts +5 -0
  26. package/dist/matterbridgeTypes.d.ts.map +1 -1
  27. package/dist/matterbridgeTypes.js.map +1 -1
  28. package/dist/matterbridgeWebsocket.d.ts +49 -0
  29. package/dist/matterbridgeWebsocket.d.ts.map +1 -0
  30. package/dist/matterbridgeWebsocket.js +145 -0
  31. package/dist/matterbridgeWebsocket.js.map +1 -0
  32. package/dist/pluginManager.d.ts +134 -1
  33. package/dist/pluginManager.d.ts.map +1 -1
  34. package/dist/pluginManager.js +145 -12
  35. package/dist/pluginManager.js.map +1 -1
  36. package/frontend/build/asset-manifest.json +3 -3
  37. package/frontend/build/index.html +1 -1
  38. package/frontend/build/static/js/{main.96d6324b.js → main.045d08f7.js} +3 -3
  39. package/frontend/build/static/js/main.045d08f7.js.map +1 -0
  40. package/npm-shrinkwrap.json +813 -6207
  41. package/package.json +2 -78
  42. package/dist/history/export.d.ts +0 -2
  43. package/dist/history/export.d.ts.map +0 -1
  44. package/dist/history/export.js +0 -2
  45. package/dist/history/export.js.map +0 -1
  46. package/frontend/build/static/js/main.96d6324b.js.map +0 -1
  47. /package/frontend/build/static/js/{main.96d6324b.js.LICENSE.txt → main.045d08f7.js.LICENSE.txt} +0 -0
@@ -50,6 +50,7 @@ import { StorageBackendDisk, StorageBackendJsonFile, StorageManager } from '@pro
50
50
  import { getParameter, getIntParameter, hasParameter } from '@project-chip/matter-node.js/util';
51
51
  import { CryptoNode } from '@project-chip/matter-node.js/crypto';
52
52
  import { Specification } from '@project-chip/matter-node.js/model';
53
+ import { WS_ID_LOG, WS_ID_REFRESH_NEEDED, WS_ID_RESTART_NEEDED, wsMessageHandler } from './matterbridgeWebsocket.js';
53
54
  // Default colors
54
55
  const plg = '\u001B[38;5;33m';
55
56
  const dev = '\u001B[38;5;79m';
@@ -130,8 +131,6 @@ export class Matterbridge extends EventEmitter {
130
131
  initialized = false;
131
132
  execRunningCount = 0;
132
133
  startMatterInterval;
133
- cleanupTimeout1;
134
- cleanupTimeout2;
135
134
  checkUpdateInterval;
136
135
  configureTimeout;
137
136
  reachabilityTimeout;
@@ -160,7 +159,10 @@ export class Matterbridge extends EventEmitter {
160
159
  // We load asyncronously so is private
161
160
  constructor() {
162
161
  super();
162
+ // Bind the handler to the instance
163
+ this.matterbridgeMessageHandler = wsMessageHandler.bind(this);
163
164
  }
165
+ matterbridgeMessageHandler;
164
166
  /** ***********************************************************************************************************************************/
165
167
  /** loadInstance() and cleanup() methods */
166
168
  /** ***********************************************************************************************************************************/
@@ -521,8 +523,6 @@ export class Matterbridge extends EventEmitter {
521
523
  }
522
524
  // Start the matter storage and create the matterbridge context
523
525
  await this.startMatterStorage('json', path.join(this.matterbridgeDirectory, this.matterStorageName));
524
- this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
525
- this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
526
526
  if (hasParameter('reset') && getParameter('reset') === undefined) {
527
527
  this.log.info('Resetting Matterbridge commissioning information...');
528
528
  await this.matterbridgeContext?.clearAll();
@@ -560,12 +560,13 @@ export class Matterbridge extends EventEmitter {
560
560
  this.getPluginLatestVersion(plugin);
561
561
  }
562
562
  }, 60 * 60 * 1000);
563
+ // Start the matterbridge in mode test
563
564
  if (hasParameter('test')) {
564
565
  this.bridgeMode = 'bridge';
565
566
  MatterbridgeDevice.bridgeMode = 'bridge';
566
- await this.startTest();
567
567
  return;
568
568
  }
569
+ // Start the matterbridge in mode controller
569
570
  if (hasParameter('controller')) {
570
571
  this.bridgeMode = 'controller';
571
572
  await this.startController();
@@ -576,91 +577,63 @@ export class Matterbridge extends EventEmitter {
576
577
  this.log.info('Setting default matterbridge start mode to bridge');
577
578
  await this.nodeContext?.set('bridgeMode', 'bridge');
578
579
  }
580
+ // Start matterbridge in bridge mode
579
581
  if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
580
582
  this.bridgeMode = 'bridge';
581
583
  MatterbridgeDevice.bridgeMode = 'bridge';
582
- if (!this.storageManager)
583
- throw new Error('No storage manager initialized');
584
- this.log.debug('Starting matterbridge in mode', this.bridgeMode);
585
- this.matterServer = this.createMatterServer(this.storageManager);
586
- this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
587
- this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
588
- this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
589
- this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext, 'Matterbridge');
590
- this.log.debug('Adding matterbridge aggregator to commissioning server');
591
- this.commissioningServer.addDevice(this.matterAggregator);
592
- this.log.debug('Adding matterbridge commissioning server to matter server');
593
- await this.matterServer.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
594
- for (const plugin of this.plugins) {
595
- plugin.configJson = await this.plugins.loadConfig(plugin);
596
- plugin.schemaJson = await this.plugins.loadSchema(plugin);
597
- // Check if the plugin is available
598
- if (!(await this.plugins.resolve(plugin.path))) {
599
- this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
600
- plugin.enabled = false;
601
- plugin.error = true;
602
- continue;
603
- }
604
- // Check if the plugin has a new version
605
- this.getPluginLatestVersion(plugin); // No await do it asyncronously
606
- if (!plugin.enabled) {
607
- this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
608
- continue;
609
- }
610
- plugin.error = false;
611
- plugin.locked = false;
612
- plugin.loaded = false;
613
- plugin.started = false;
614
- plugin.configured = false;
615
- plugin.connected = undefined;
616
- plugin.registeredDevices = undefined;
617
- plugin.addedDevices = undefined;
618
- plugin.qrPairingCode = undefined;
619
- plugin.manualPairingCode = undefined;
620
- this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
621
- }
584
+ this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
622
585
  await this.startBridge();
623
586
  return;
624
587
  }
588
+ // Start matterbridge in childbridge mode
625
589
  if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
626
590
  this.bridgeMode = 'childbridge';
627
591
  MatterbridgeDevice.bridgeMode = 'childbridge';
628
- if (!this.storageManager)
629
- throw new Error('No storage manager initialized');
630
- this.log.debug('Starting matterbridge in mode', this.bridgeMode);
631
- this.matterServer = this.createMatterServer(this.storageManager);
632
- for (const plugin of this.plugins) {
633
- plugin.configJson = await this.plugins.loadConfig(plugin);
634
- plugin.schemaJson = await this.plugins.loadSchema(plugin);
635
- // Check if the plugin is available
636
- if (!(await this.plugins.resolve(plugin.path))) {
637
- this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
638
- plugin.enabled = false;
639
- plugin.error = true;
640
- continue;
641
- }
642
- // Check if the plugin has a new version
643
- this.getPluginLatestVersion(plugin); // No await do it asyncronously
644
- if (!plugin.enabled) {
645
- this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
646
- continue;
647
- }
648
- plugin.error = false;
649
- plugin.locked = false;
650
- plugin.loaded = false;
651
- plugin.started = false;
652
- plugin.configured = false;
653
- plugin.connected = false;
654
- plugin.registeredDevices = undefined;
655
- plugin.addedDevices = undefined;
656
- plugin.qrPairingCode = undefined;
657
- plugin.manualPairingCode = undefined;
658
- this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
659
- }
592
+ this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
660
593
  await this.startChildbridge();
661
594
  return;
662
595
  }
663
596
  }
597
+ /**
598
+ * Asynchronously loads and starts the registered plugins.
599
+ *
600
+ * This method is responsible for initializing and staarting all enabled plugins.
601
+ * It ensures that each plugin is properly loaded and started before the ridge starts.
602
+ *
603
+ * @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
604
+ */
605
+ async startPlugins() {
606
+ // Check, load and start the plugins
607
+ for (const plugin of this.plugins) {
608
+ plugin.configJson = await this.plugins.loadConfig(plugin);
609
+ plugin.schemaJson = await this.plugins.loadSchema(plugin);
610
+ // Check if the plugin is available
611
+ if (!(await this.plugins.resolve(plugin.path))) {
612
+ this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
613
+ plugin.enabled = false;
614
+ plugin.error = true;
615
+ continue;
616
+ }
617
+ // Check if the plugin has a new version
618
+ this.getPluginLatestVersion(plugin); // No await do it asyncronously
619
+ if (!plugin.enabled) {
620
+ this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
621
+ continue;
622
+ }
623
+ plugin.error = false;
624
+ plugin.locked = false;
625
+ plugin.loaded = false;
626
+ plugin.started = false;
627
+ plugin.configured = false;
628
+ plugin.connected = undefined;
629
+ plugin.registeredDevices = undefined;
630
+ plugin.addedDevices = undefined;
631
+ plugin.qrPairingCode = undefined;
632
+ plugin.manualPairingCode = undefined;
633
+ this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
634
+ }
635
+ this.wssSendRefreshRequired();
636
+ }
664
637
  /**
665
638
  * Registers the signal handlers for SIGINT and SIGTERM.
666
639
  * When either of these signals are received, the cleanup method is called with an appropriate message.
@@ -1155,7 +1128,6 @@ export class Matterbridge extends EventEmitter {
1155
1128
  });
1156
1129
  this.webSocketServer = undefined;
1157
1130
  }
1158
- // this.cleanupTimeout1 = setTimeout(async () => {
1159
1131
  // Closing matter
1160
1132
  await this.stopMatterServer();
1161
1133
  // Closing matter storage
@@ -1201,9 +1173,6 @@ export class Matterbridge extends EventEmitter {
1201
1173
  }
1202
1174
  this.plugins.clear();
1203
1175
  this.devices.clear();
1204
- // this.registeredDevices = [];
1205
- // this.log.info('Waiting for matter to deliver last messages...');
1206
- // this.cleanupTimeout2 = setTimeout(async () => {
1207
1176
  if (restart) {
1208
1177
  if (message === 'updating...') {
1209
1178
  this.log.info('Cleanup completed. Updating...');
@@ -1238,8 +1207,6 @@ export class Matterbridge extends EventEmitter {
1238
1207
  }
1239
1208
  this.hasCleanupStarted = false;
1240
1209
  this.initialized = false;
1241
- // }, 2 * 1000);
1242
- // }, 3 * 1000);
1243
1210
  }
1244
1211
  }
1245
1212
  /**
@@ -1249,17 +1216,17 @@ export class Matterbridge extends EventEmitter {
1249
1216
  * @returns {Promise<void>} - A promise that resolves when the device is added.
1250
1217
  */
1251
1218
  async addBridgedDevice(pluginName, device) {
1252
- this.log.debug(`Adding bridged device ${dev}${device.deviceName}${db} (${dev}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
1219
+ this.log.debug(`Adding bridged device ${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
1253
1220
  // Check if the plugin is registered
1254
1221
  const plugin = this.plugins.get(pluginName);
1255
1222
  if (!plugin) {
1256
- this.log.error(`Error adding bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) plugin ${plg}${pluginName}${er} not found`);
1223
+ this.log.error(`Error adding bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) plugin ${plg}${pluginName}${er} not found`);
1257
1224
  return;
1258
1225
  }
1259
1226
  // Register and add the device to matterbridge aggregator in bridge mode
1260
1227
  if (this.bridgeMode === 'bridge') {
1261
1228
  if (!this.matterAggregator) {
1262
- this.log.error(`Adding bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
1229
+ this.log.error(`Adding bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
1263
1230
  return;
1264
1231
  }
1265
1232
  this.matterAggregator.addBridgedDevice(device);
@@ -1314,17 +1281,17 @@ export class Matterbridge extends EventEmitter {
1314
1281
  * @returns A Promise that resolves when the device is successfully removed.
1315
1282
  */
1316
1283
  async removeBridgedDevice(pluginName, device) {
1317
- this.log.debug(`Removing bridged device ${dev}${device.deviceName}${db} (${dev}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
1284
+ this.log.debug(`Removing bridged device ${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
1318
1285
  // Check if the plugin is registered
1319
1286
  const plugin = this.plugins.get(pluginName);
1320
1287
  if (!plugin) {
1321
- this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
1288
+ this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
1322
1289
  return;
1323
1290
  }
1324
1291
  // Remove the device from matterbridge aggregator in bridge mode
1325
1292
  if (this.bridgeMode === 'bridge') {
1326
1293
  if (!this.matterAggregator) {
1327
- this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er}: matterAggregator not found`);
1294
+ this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: matterAggregator not found`);
1328
1295
  return;
1329
1296
  }
1330
1297
  if (device.number !== undefined) {
@@ -1334,7 +1301,7 @@ export class Matterbridge extends EventEmitter {
1334
1301
  // device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerShutDownEvent({});
1335
1302
  // device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerLeaveEvent({});
1336
1303
  this.matterAggregator?.removeBridgedDevice(device);
1337
- this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
1304
+ this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${zb}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
1338
1305
  if (plugin.registeredDevices !== undefined)
1339
1306
  plugin.registeredDevices--;
1340
1307
  if (plugin.addedDevices !== undefined)
@@ -1344,13 +1311,13 @@ export class Matterbridge extends EventEmitter {
1344
1311
  if (this.bridgeMode === 'childbridge') {
1345
1312
  if (plugin.type === 'AccessoryPlatform') {
1346
1313
  if (!plugin.commissioningServer) {
1347
- this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er}: commissioning server not found`);
1314
+ this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: commissioning server not found`);
1348
1315
  return;
1349
1316
  }
1350
1317
  }
1351
1318
  else if (plugin.type === 'DynamicPlatform') {
1352
1319
  if (!plugin.aggregator) {
1353
- this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator not found`);
1320
+ this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator not found`);
1354
1321
  return;
1355
1322
  }
1356
1323
  if (device.number !== undefined) {
@@ -1359,7 +1326,7 @@ export class Matterbridge extends EventEmitter {
1359
1326
  }
1360
1327
  plugin.aggregator.removeBridgedDevice(device);
1361
1328
  }
1362
- this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
1329
+ this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${zb}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
1363
1330
  if (plugin.registeredDevices !== undefined)
1364
1331
  plugin.registeredDevices--;
1365
1332
  if (plugin.addedDevices !== undefined)
@@ -1388,18 +1355,28 @@ export class Matterbridge extends EventEmitter {
1388
1355
  }
1389
1356
  });
1390
1357
  }
1391
- async startTest() {
1392
- // Start the Matterbridge test
1393
- }
1394
1358
  /**
1395
1359
  * Starts the Matterbridge in bridge mode.
1396
1360
  * @private
1397
1361
  * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1398
1362
  */
1399
1363
  async startBridge() {
1400
- // Plugins are loaded and started by loadPlugin on startup and plugin.loaded and plugin.started are set to true
1401
1364
  // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1402
- this.log.debug('Starting startMatterInterval in bridge mode');
1365
+ if (!this.storageManager)
1366
+ throw new Error('No storage manager initialized');
1367
+ if (!this.matterbridgeContext)
1368
+ throw new Error('No storage context initialized');
1369
+ this.matterServer = this.createMatterServer(this.storageManager);
1370
+ this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
1371
+ this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
1372
+ this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
1373
+ this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext, 'Matterbridge');
1374
+ this.log.debug('Adding matterbridge aggregator to commissioning server');
1375
+ this.commissioningServer.addDevice(this.matterAggregator);
1376
+ this.log.debug('Adding matterbridge commissioning server to matter server');
1377
+ await this.matterServer.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
1378
+ await this.startPlugins();
1379
+ this.log.debug('Starting start matter interval in bridge mode');
1403
1380
  let failCount = 0;
1404
1381
  this.startMatterInterval = setInterval(async () => {
1405
1382
  for (const plugin of this.plugins) {
@@ -1428,8 +1405,11 @@ export class Matterbridge extends EventEmitter {
1428
1405
  clearInterval(this.startMatterInterval);
1429
1406
  this.startMatterInterval = undefined;
1430
1407
  this.log.debug('Cleared startMatterInterval interval for Matterbridge');
1408
+ // Start the Matter server
1431
1409
  await this.startMatterServer();
1432
1410
  this.log.notice('Matter server started');
1411
+ // Show the QR code for commissioning or log the already commissioned message
1412
+ await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
1433
1413
  // Configure the plugins
1434
1414
  this.configureTimeout = setTimeout(async () => {
1435
1415
  for (const plugin of this.plugins) {
@@ -1443,9 +1423,8 @@ export class Matterbridge extends EventEmitter {
1443
1423
  this.log.error(`Error configuring plugin ${plg}${plugin.name}${er}`, error);
1444
1424
  }
1445
1425
  }
1426
+ this.wssSendRefreshRequired();
1446
1427
  }, 30 * 1000);
1447
- // Show the QR code for commissioning or log the already commissioned message
1448
- await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
1449
1428
  // Setting reachability to true
1450
1429
  this.reachabilityTimeout = setTimeout(() => {
1451
1430
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
@@ -1462,9 +1441,12 @@ export class Matterbridge extends EventEmitter {
1462
1441
  * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1463
1442
  */
1464
1443
  async startChildbridge() {
1465
- // Plugins are loaded and started by loadPlugin on startup and plugin.loaded and plugin.started are set to true
1466
- // addDevice and addBridgedDeevice create the commissionig servers and add the devices to the the commissioning server or to the aggregator
1444
+ // Matterbridge.addBridgedDevice creates the commissionig servers and add the devices to the the commissioning server or to the aggregator
1467
1445
  // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1446
+ if (!this.storageManager)
1447
+ throw new Error('No storage manager initialized');
1448
+ this.matterServer = this.createMatterServer(this.storageManager);
1449
+ await this.startPlugins();
1468
1450
  this.log.debug('Starting start matter interval in childbridge mode...');
1469
1451
  let failCount = 0;
1470
1452
  this.startMatterInterval = setInterval(async () => {
@@ -1476,7 +1458,7 @@ export class Matterbridge extends EventEmitter {
1476
1458
  if (plugin.error) {
1477
1459
  clearInterval(this.startMatterInterval);
1478
1460
  this.startMatterInterval = undefined;
1479
- this.log.debug('Cleared startMatterInterval interval for Matterbridge for plugin in error state');
1461
+ this.log.debug('Cleared startMatterInterval interval for a plugin in error state');
1480
1462
  this.log.error(`The plugin ${plg}${plugin.name}${er} is in error state.`);
1481
1463
  this.log.error('The bridge will not start until the problem is solved to prevent the controllers from deleting all registered devices.');
1482
1464
  this.log.error('If you want to start the bridge disable the plugin in error state and restart.');
@@ -1498,6 +1480,7 @@ export class Matterbridge extends EventEmitter {
1498
1480
  clearInterval(this.startMatterInterval);
1499
1481
  this.startMatterInterval = undefined;
1500
1482
  this.log.debug('Cleared startMatterInterval interval in childbridge mode');
1483
+ // Start the Matter server
1501
1484
  await this.startMatterServer();
1502
1485
  this.log.notice('Matter server started');
1503
1486
  // Configure the plugins
@@ -1513,6 +1496,7 @@ export class Matterbridge extends EventEmitter {
1513
1496
  this.log.error(`Error configuring plugin ${plg}${plugin.name}${er}`, error);
1514
1497
  }
1515
1498
  }
1499
+ this.wssSendRefreshRequired();
1516
1500
  }, 30 * 1000);
1517
1501
  for (const plugin of this.plugins) {
1518
1502
  if (!plugin.enabled || plugin.error)
@@ -1753,7 +1737,7 @@ export class Matterbridge extends EventEmitter {
1753
1737
  await this.storageManager.initialize();
1754
1738
  this.log.debug('Storage initialized');
1755
1739
  if (storageType === 'json') {
1756
- await this.backupJsonMatterStorage(storageName, storageName.replace('.json', '') + '.backup.json');
1740
+ await this.backupMatterStorage(storageName, storageName.replace('.json', '') + '.backup.json');
1757
1741
  }
1758
1742
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
1759
1743
  }
@@ -1762,6 +1746,8 @@ export class Matterbridge extends EventEmitter {
1762
1746
  this.log.error(`Please delete it and rename ${storageName.replace('.json', '.backup.json')} to ${storageName} and try to restart Matterbridge.`);
1763
1747
  await this.cleanup('Storage initialize() error!');
1764
1748
  }
1749
+ this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
1750
+ this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
1765
1751
  }
1766
1752
  /**
1767
1753
  * Makes a backup copy of the specified matter JSON storage file.
@@ -1769,7 +1755,7 @@ export class Matterbridge extends EventEmitter {
1769
1755
  * @param storageName - The name of the JSON storage file to be backed up.
1770
1756
  * @param backupName - The name of the backup file to be created.
1771
1757
  */
1772
- async backupJsonMatterStorage(storageName, backupName) {
1758
+ async backupMatterStorage(storageName, backupName) {
1773
1759
  try {
1774
1760
  this.log.debug(`Making backup copy of ${storageName}`);
1775
1761
  await fs.copyFile(storageName, backupName);
@@ -2010,6 +1996,7 @@ export class Matterbridge extends EventEmitter {
2010
1996
  }
2011
1997
  }
2012
1998
  }
1999
+ this.wssSendRefreshRequired();
2013
2000
  },
2014
2001
  commissioningChangedCallback: async (fabricIndex) => {
2015
2002
  const fabricInfo = commissioningServer.getCommissionedFabricInformation(fabricIndex);
@@ -2052,6 +2039,7 @@ export class Matterbridge extends EventEmitter {
2052
2039
  }
2053
2040
  }
2054
2041
  }
2042
+ this.wssSendRefreshRequired();
2055
2043
  },
2056
2044
  });
2057
2045
  if (this.passcode !== undefined)
@@ -2211,6 +2199,7 @@ export class Matterbridge extends EventEmitter {
2211
2199
  }
2212
2200
  }
2213
2201
  }
2202
+ this.wssSendRefreshRequired();
2214
2203
  }
2215
2204
  /**
2216
2205
  * Sanitizes the fabric information by converting bigint properties to string cause res.json doesn't know bigint.
@@ -2333,6 +2322,9 @@ export class Matterbridge extends EventEmitter {
2333
2322
  case 4701:
2334
2323
  vendorName = '(Tuya)';
2335
2324
  break;
2325
+ case 4718:
2326
+ vendorName = '(Xiaomi)';
2327
+ break;
2336
2328
  case 4742:
2337
2329
  vendorName = '(eWeLink)';
2338
2330
  break;
@@ -2508,7 +2500,33 @@ export class Matterbridge extends EventEmitter {
2508
2500
  // Send the message to all connected clients
2509
2501
  this.webSocketServer?.clients.forEach((client) => {
2510
2502
  if (client.readyState === WebSocket.OPEN) {
2511
- client.send(JSON.stringify({ level, time, name, message }));
2503
+ client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
2504
+ }
2505
+ });
2506
+ }
2507
+ /**
2508
+ * Sends a need to refresh WebSocket message to all connected clients.
2509
+ *
2510
+ */
2511
+ wssSendRefreshRequired() {
2512
+ this.matterbridgeInformation.refreshRequired = true;
2513
+ // Send the message to all connected clients
2514
+ this.webSocketServer?.clients.forEach((client) => {
2515
+ if (client.readyState === WebSocket.OPEN) {
2516
+ client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Matterbridge', method: 'refresh_required', params: {} }));
2517
+ }
2518
+ });
2519
+ }
2520
+ /**
2521
+ * Sends a need to restart WebSocket message to all connected clients.
2522
+ *
2523
+ */
2524
+ wssSendRestartRequired() {
2525
+ this.matterbridgeInformation.restartRequired = true;
2526
+ // Send the message to all connected clients
2527
+ this.webSocketServer?.clients.forEach((client) => {
2528
+ if (client.readyState === WebSocket.OPEN) {
2529
+ client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Matterbridge', method: 'restart_required', params: {} }));
2512
2530
  }
2513
2531
  });
2514
2532
  }
@@ -2627,7 +2645,7 @@ export class Matterbridge extends EventEmitter {
2627
2645
  }
2628
2646
  if (initializeError)
2629
2647
  return;
2630
- // Createe a WebSocket server and attach it to the http server
2648
+ // Createe a WebSocket server and attach it to the http or https server
2631
2649
  const wssPort = port;
2632
2650
  const wssHost = hasParameter('ssl') ? `wss://${this.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.systemInformation.ipv4Address}:${wssPort}`;
2633
2651
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
@@ -2637,6 +2655,14 @@ export class Matterbridge extends EventEmitter {
2637
2655
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
2638
2656
  ws.on('message', (message) => {
2639
2657
  this.log.debug(`WebSocket client message: ${message}`);
2658
+ this.matterbridgeMessageHandler(ws, message);
2659
+ });
2660
+ ws.on('ping', () => {
2661
+ this.log.debug('WebSocket client ping');
2662
+ ws.pong();
2663
+ });
2664
+ ws.on('pong', () => {
2665
+ this.log.debug('WebSocket client pong');
2640
2666
  });
2641
2667
  ws.on('close', () => {
2642
2668
  this.log.info('WebSocket client disconnected');
@@ -2699,12 +2725,8 @@ export class Matterbridge extends EventEmitter {
2699
2725
  this.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridgeQrPairingCode;
2700
2726
  this.matterbridgeInformation.matterbridgeManualPairingCode = this.matterbridgeManualPairingCode;
2701
2727
  this.matterbridgeInformation.matterbridgeFabricInformations = this.matterbridgeFabricInformations;
2702
- // this.matterbridgeInformation.matterbridgeSessionInformations = this.matterbridgeSessionInformations;
2703
2728
  this.matterbridgeInformation.matterbridgeSessionInformations = Array.from(this.matterbridgeSessionInformations.values());
2704
- // console.log('this.matterbridgeSessionInformations:', this.matterbridgeSessionInformations);
2705
- if (this.profile)
2706
- this.matterbridgeInformation.profile = this.profile;
2707
- // const response = { wssHost, ssl: hasParameter('ssl'), qrPairingCode, manualPairingCode, systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
2729
+ this.matterbridgeInformation.profile = this.profile;
2708
2730
  const response = { wssHost, ssl: hasParameter('ssl'), systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
2709
2731
  // this.log.debug('Response:', debugStringify(response));
2710
2732
  res.json(response);
@@ -2785,7 +2807,7 @@ export class Matterbridge extends EventEmitter {
2785
2807
  });
2786
2808
  });
2787
2809
  device.getChildEndpoints().forEach((childEndpoint) => {
2788
- const name = device.getChildEndpointName(childEndpoint);
2810
+ const name = childEndpoint.uniqueStorageKey;
2789
2811
  const clusterServers = childEndpoint.getAllClusterServers();
2790
2812
  clusterServers.forEach((clusterServer) => {
2791
2813
  Object.entries(clusterServer.attributes).forEach(([key, value]) => {
@@ -2819,7 +2841,7 @@ export class Matterbridge extends EventEmitter {
2819
2841
  });
2820
2842
  res.json(data);
2821
2843
  });
2822
- // Endpoint to send the log
2844
+ // Endpoint to view the log
2823
2845
  this.expressApp.get('/api/view-log', async (req, res) => {
2824
2846
  this.log.debug('The frontend sent /api/log');
2825
2847
  try {
@@ -2941,7 +2963,7 @@ export class Matterbridge extends EventEmitter {
2941
2963
  // Handle the command setbridgemode from Settings
2942
2964
  if (command === 'setbridgemode') {
2943
2965
  this.log.debug(`setbridgemode: ${param}`);
2944
- this.matterbridgeInformation.restartRequired = true;
2966
+ this.wssSendRestartRequired();
2945
2967
  await this.nodeContext?.set('bridgeMode', param);
2946
2968
  res.json({ message: 'Command received' });
2947
2969
  return;
@@ -3121,7 +3143,7 @@ export class Matterbridge extends EventEmitter {
3121
3143
  this.log.error('Error updating matterbridge');
3122
3144
  }
3123
3145
  await this.updateProcess();
3124
- this.matterbridgeInformation.restartRequired = true;
3146
+ this.wssSendRestartRequired();
3125
3147
  res.json({ message: 'Command received' });
3126
3148
  return;
3127
3149
  }
@@ -3139,7 +3161,7 @@ export class Matterbridge extends EventEmitter {
3139
3161
  return;
3140
3162
  this.plugins.saveConfigFromJson(plugin, req.body);
3141
3163
  }
3142
- this.matterbridgeInformation.restartRequired = true;
3164
+ this.wssSendRestartRequired();
3143
3165
  res.json({ message: 'Command received' });
3144
3166
  return;
3145
3167
  }
@@ -3155,7 +3177,7 @@ export class Matterbridge extends EventEmitter {
3155
3177
  catch (error) {
3156
3178
  this.log.error(`Error installing plugin ${plg}${param}${er}`);
3157
3179
  }
3158
- this.matterbridgeInformation.restartRequired = true;
3180
+ this.wssSendRestartRequired();
3159
3181
  // Also add the plugin to matterbridge so no return!
3160
3182
  // res.json({ message: 'Command received' });
3161
3183
  // return;
@@ -3168,6 +3190,7 @@ export class Matterbridge extends EventEmitter {
3168
3190
  this.plugins.load(plugin, true, 'The plugin has been added', true); // No await do it in the background
3169
3191
  }
3170
3192
  res.json({ message: 'Command received' });
3193
+ this.wssSendRefreshRequired();
3171
3194
  return;
3172
3195
  }
3173
3196
  // Handle the command removeplugin from Home
@@ -3181,6 +3204,7 @@ export class Matterbridge extends EventEmitter {
3181
3204
  await this.plugins.remove(param);
3182
3205
  }
3183
3206
  res.json({ message: 'Command received' });
3207
+ this.wssSendRefreshRequired();
3184
3208
  return;
3185
3209
  }
3186
3210
  // Handle the command enableplugin from Home
@@ -3205,6 +3229,7 @@ export class Matterbridge extends EventEmitter {
3205
3229
  }
3206
3230
  }
3207
3231
  res.json({ message: 'Command received' });
3232
+ this.wssSendRefreshRequired();
3208
3233
  return;
3209
3234
  }
3210
3235
  // Handle the command disableplugin from Home
@@ -3220,6 +3245,7 @@ export class Matterbridge extends EventEmitter {
3220
3245
  }
3221
3246
  }
3222
3247
  res.json({ message: 'Command received' });
3248
+ this.wssSendRefreshRequired();
3223
3249
  return;
3224
3250
  }
3225
3251
  });