matterbridge 1.3.4 → 1.3.5

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 (50) hide show
  1. package/CHANGELOG.md +598 -551
  2. package/README.md +641 -594
  3. package/dist/cluster/export.d.ts +25 -0
  4. package/dist/cluster/export.d.ts.map +1 -0
  5. package/dist/cluster/export.js +25 -0
  6. package/dist/cluster/export.js.map +1 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +0 -2
  9. package/dist/index.js.map +1 -1
  10. package/dist/log/export.d.ts +2 -0
  11. package/dist/log/export.d.ts.map +1 -0
  12. package/dist/log/export.js +2 -0
  13. package/dist/log/export.js.map +1 -0
  14. package/dist/matterbridge.d.ts +6 -0
  15. package/dist/matterbridge.d.ts.map +1 -1
  16. package/dist/matterbridge.js +172 -306
  17. package/dist/matterbridge.js.map +1 -1
  18. package/dist/matterbridgeController.d.ts.map +1 -1
  19. package/dist/matterbridgeController.js +0 -3
  20. package/dist/matterbridgeController.js.map +1 -1
  21. package/dist/matterbridgeDevice.d.ts +213 -4
  22. package/dist/matterbridgeDevice.d.ts.map +1 -1
  23. package/dist/matterbridgeDevice.js +129 -42
  24. package/dist/matterbridgeDevice.js.map +1 -1
  25. package/dist/storage/export.d.ts +2 -0
  26. package/dist/storage/export.d.ts.map +1 -0
  27. package/dist/storage/export.js +2 -0
  28. package/dist/storage/export.js.map +1 -0
  29. package/dist/utils/colorUtils.d.ts +61 -0
  30. package/dist/utils/colorUtils.d.ts.map +1 -0
  31. package/dist/utils/colorUtils.js +313 -0
  32. package/dist/utils/colorUtils.js.map +1 -0
  33. package/dist/utils/export.d.ts +3 -0
  34. package/dist/utils/export.d.ts.map +1 -0
  35. package/dist/utils/export.js +3 -0
  36. package/dist/utils/export.js.map +1 -0
  37. package/dist/utils/utils.d.ts +73 -0
  38. package/dist/utils/utils.d.ts.map +1 -0
  39. package/dist/utils/utils.js +266 -0
  40. package/dist/utils/utils.js.map +1 -0
  41. package/dist/utils.d.ts +1 -0
  42. package/dist/utils.d.ts.map +1 -1
  43. package/dist/utils.js +4 -0
  44. package/dist/utils.js.map +1 -1
  45. package/frontend/build/asset-manifest.json +3 -3
  46. package/frontend/build/index.html +1 -1
  47. package/frontend/build/static/js/{main.cbfc6c9b.js → main.c3b5dfce.js} +3 -3
  48. package/frontend/build/static/js/{main.cbfc6c9b.js.map → main.c3b5dfce.js.map} +1 -1
  49. package/package.json +34 -20
  50. /package/frontend/build/static/js/{main.cbfc6c9b.js.LICENSE.txt → main.c3b5dfce.js.LICENSE.txt} +0 -0
@@ -35,6 +35,7 @@ import WebSocket, { WebSocketServer } from 'ws';
35
35
  import { MatterbridgeDevice } from './matterbridgeDevice.js';
36
36
  import { shelly_config, somfytahoma_config, zigbee2mqtt_config } from './defaultConfigSchema.js';
37
37
  import { BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster } from './cluster/BridgedDeviceBasicInformationCluster.js';
38
+ import { logInterfaces } from './utils/utils.js';
38
39
  // @project-chip/matter-node.js
39
40
  import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
40
41
  import { BasicInformationCluster, ClusterServer, FixedLabelCluster, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById } from '@project-chip/matter-node.js/cluster';
@@ -54,6 +55,7 @@ const typ = '\u001B[38;5;207m';
54
55
  */
55
56
  export class Matterbridge extends EventEmitter {
56
57
  systemInformation = {
58
+ interfaceName: '',
57
59
  macAddress: '',
58
60
  ipv4Address: '',
59
61
  ipv6Address: '',
@@ -100,6 +102,8 @@ export class Matterbridge extends EventEmitter {
100
102
  port = 5540;
101
103
  log;
102
104
  hasCleanupStarted = false;
105
+ plugins = new Map();
106
+ devices = new Map();
103
107
  registeredPlugins = [];
104
108
  registeredDevices = [];
105
109
  nodeStorage;
@@ -256,7 +260,8 @@ export class Matterbridge extends EventEmitter {
256
260
  - bridge: start Matterbridge in bridge mode
257
261
  - childbridge: start Matterbridge in childbridge mode
258
262
  - frontend [port]: start the frontend on the given port (default 8283)
259
- - debug: enable debug mode (default false)
263
+ - debug: enable the Matterbridge debug mode (default false)
264
+ - matterlogger: set the matter.js logger level: debug | info | notice | warn | error | fatal (default info)
260
265
  - reset: remove the commissioning for Matterbridge (bridge mode). Shutdown Matterbridge before using it!
261
266
  - factoryreset: remove all commissioning information and reset all internal storages. Shutdown Matterbridge before using it!
262
267
  - list: list the registered plugins
@@ -305,9 +310,7 @@ export class Matterbridge extends EventEmitter {
305
310
  }
306
311
  // Log system info and create .matterbridge directory
307
312
  await this.logNodeAndSystemInfo();
308
- this.log.info(
309
- // eslint-disable-next-line max-len
310
- `Matterbridge version ${this.matterbridgeVersion} mode ${hasParameter('bridge') ? 'bridge' : ''}${hasParameter('childbridge') ? 'childbridge' : ''}${hasParameter('controller') ? 'controller' : ''} ` +
313
+ this.log.info(`Matterbridge version ${this.matterbridgeVersion} mode ${hasParameter('bridge') ? 'bridge' : ''}${hasParameter('childbridge') ? 'childbridge' : ''}${hasParameter('controller') ? 'controller' : ''} ` +
311
314
  `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}running on ${this.systemInformation.osType} ${this.systemInformation.osRelease} ${this.systemInformation.osPlatform} ${this.systemInformation.osArch}`);
312
315
  // Check node version and throw error
313
316
  requireMinNodeVersion(18);
@@ -380,7 +383,7 @@ export class Matterbridge extends EventEmitter {
380
383
  process.exit(0);
381
384
  }
382
385
  if (hasParameter('logstorage')) {
383
- this.log.info(`${plg}matterbridge${nf} storage log`);
386
+ this.log.info(`${plg}Matterbridge${nf} storage log`);
384
387
  await this.nodeContext?.logStorage();
385
388
  for (const plugin of this.registeredPlugins) {
386
389
  this.log.info(`${plg}${plugin.name}${nf} storage log`);
@@ -389,6 +392,12 @@ export class Matterbridge extends EventEmitter {
389
392
  this.emit('shutdown');
390
393
  process.exit(0);
391
394
  }
395
+ if (hasParameter('loginterfaces')) {
396
+ this.log.info(`${plg}Matterbridge${nf} network interfaces log`);
397
+ logInterfaces();
398
+ this.emit('shutdown');
399
+ process.exit(0);
400
+ }
392
401
  if (getParameter('add')) {
393
402
  this.log.debug(`Registering plugin ${getParameter('add')}`);
394
403
  await this.executeCommandLine(getParameter('add'), 'add');
@@ -507,10 +516,13 @@ export class Matterbridge extends EventEmitter {
507
516
  continue;
508
517
  }
509
518
  plugin.error = false;
519
+ plugin.locked = false;
510
520
  plugin.loaded = false;
511
521
  plugin.started = false;
512
522
  plugin.configured = false;
513
523
  plugin.connected = undefined;
524
+ plugin.registeredDevices = undefined;
525
+ plugin.addedDevices = undefined;
514
526
  plugin.qrPairingCode = undefined;
515
527
  plugin.manualPairingCode = undefined;
516
528
  this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
@@ -537,10 +549,13 @@ export class Matterbridge extends EventEmitter {
537
549
  continue;
538
550
  }
539
551
  plugin.error = false;
552
+ plugin.locked = false;
540
553
  plugin.loaded = false;
541
554
  plugin.started = false;
542
555
  plugin.configured = false;
543
556
  plugin.connected = false;
557
+ plugin.registeredDevices = undefined;
558
+ plugin.addedDevices = undefined;
544
559
  plugin.qrPairingCode = (await plugin.nodeContext?.get('qrPairingCode', undefined)) ?? undefined;
545
560
  plugin.manualPairingCode = (await plugin.nodeContext?.get('manualPairingCode', undefined)) ?? undefined;
546
561
  this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
@@ -549,6 +564,28 @@ export class Matterbridge extends EventEmitter {
549
564
  return;
550
565
  }
551
566
  }
567
+ async savePluginsToStorage() {
568
+ if (!this.nodeContext) {
569
+ this.log.error('loadPluginsFromStorage() error: the node context is not initialized');
570
+ return;
571
+ }
572
+ // Convert the map to an array
573
+ // const pluginArray = Array.from(this.plugins.values());
574
+ // await this.nodeContext.set('plugins', pluginArray);
575
+ // TODO remove after migration done
576
+ await this.nodeContext.set('plugins', await this.getBaseRegisteredPlugins());
577
+ }
578
+ async loadPluginsFromStorage() {
579
+ if (!this.nodeContext) {
580
+ this.log.error('loadPluginsFromStorage() error: the node context is not initialized');
581
+ return;
582
+ }
583
+ // Load the array from storage and convert it back to a map
584
+ // const pluginArray = await this.nodeContext.get<RegisteredPlugin[]>('plugins', []);
585
+ // for (const plugin of pluginArray) this.plugins.set(plugin.name, plugin);
586
+ // TODO remove after migration done
587
+ this.registeredPlugins = await this.nodeContext.get('plugins', []);
588
+ }
552
589
  /**
553
590
  * Resolves the name of a plugin by loading and parsing its package.json file.
554
591
  * @param pluginPath - The path to the plugin or the path to the plugin's package.json file.
@@ -573,7 +610,6 @@ export class Matterbridge extends EventEmitter {
573
610
  this.log.debug(`Package.json not found at ${packageJsonPath}`);
574
611
  this.log.debug(`Trying at ${this.globalModulesDirectory}`);
575
612
  packageJsonPath = path.join(this.globalModulesDirectory, pluginPath);
576
- // this.log.debug(`Got ${packageJsonPath}`);
577
613
  }
578
614
  try {
579
615
  // Load the package.json of the plugin
@@ -1236,8 +1272,8 @@ export class Matterbridge extends EventEmitter {
1236
1272
  await fs.access(configFile);
1237
1273
  const data = await fs.readFile(configFile, 'utf8');
1238
1274
  const config = JSON.parse(data);
1239
- // this.log.debug(`Config file found: ${configFile}.\nConfig:${rs}\n`, config);
1240
1275
  this.log.debug(`Config file found: ${configFile}.`);
1276
+ // this.log.debug(`Config file found: ${configFile}.\nConfig:${rs}\n`, config);
1241
1277
  /* The first time a plugin is added to the system, the config file is created with the plugin name and type "".*/
1242
1278
  config.name = plugin.name;
1243
1279
  config.type = plugin.type;
@@ -1547,7 +1583,6 @@ export class Matterbridge extends EventEmitter {
1547
1583
  this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
1548
1584
  for (const nodeId of nodeIds) {
1549
1585
  this.log.info(`***Connecting to commissioned node: ${nodeId}`);
1550
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1551
1586
  const node = await this.commissioningController.connectNode(nodeId, {
1552
1587
  attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) => this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
1553
1588
  eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) => this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
@@ -1590,9 +1625,7 @@ export class Matterbridge extends EventEmitter {
1590
1625
  });
1591
1626
  this.log.warn(`Cluster: ${cluster.name} attributes:`);
1592
1627
  attributes.forEach((attribute) => {
1593
- this.log.info(
1594
- // eslint-disable-next-line max-len
1595
- `- endpoint ${attribute.path.endpointId} cluster ${getClusterNameById(attribute.path.clusterId)} (${attribute.path.clusterId}) attribute ${attribute.path.attributeName} (${attribute.path.attributeId}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
1628
+ this.log.info(`- endpoint ${attribute.path.endpointId} cluster ${getClusterNameById(attribute.path.clusterId)} (${attribute.path.clusterId}) attribute ${attribute.path.attributeName} (${attribute.path.attributeId}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
1596
1629
  });
1597
1630
  // Log PowerSourceCluster
1598
1631
  cluster = PowerSourceCluster;
@@ -1601,9 +1634,7 @@ export class Matterbridge extends EventEmitter {
1601
1634
  });
1602
1635
  this.log.warn(`Cluster: ${cluster.name} attributes:`);
1603
1636
  attributes.forEach((attribute) => {
1604
- this.log.info(
1605
- // eslint-disable-next-line max-len
1606
- `- endpoint ${attribute.path.endpointId} cluster ${getClusterNameById(attribute.path.clusterId)} (${attribute.path.clusterId}) attribute ${attribute.path.attributeName} (${attribute.path.attributeId}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
1637
+ this.log.info(`- endpoint ${attribute.path.endpointId} cluster ${getClusterNameById(attribute.path.clusterId)} (${attribute.path.clusterId}) attribute ${attribute.path.attributeName} (${attribute.path.attributeId}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
1607
1638
  });
1608
1639
  // Log ThreadNetworkDiagnostics
1609
1640
  cluster = ThreadNetworkDiagnosticsCluster;
@@ -1612,9 +1643,7 @@ export class Matterbridge extends EventEmitter {
1612
1643
  });
1613
1644
  this.log.warn(`Cluster: ${cluster.name} attributes:`);
1614
1645
  attributes.forEach((attribute) => {
1615
- this.log.info(
1616
- // eslint-disable-next-line max-len
1617
- `- endpoint ${attribute.path.endpointId} cluster ${getClusterNameById(attribute.path.clusterId)} (${attribute.path.clusterId}) attribute ${attribute.path.attributeName} (${attribute.path.attributeId}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
1646
+ this.log.info(`- endpoint ${attribute.path.endpointId} cluster ${getClusterNameById(attribute.path.clusterId)} (${attribute.path.clusterId}) attribute ${attribute.path.attributeName} (${attribute.path.attributeId}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
1618
1647
  });
1619
1648
  }
1620
1649
  }
@@ -1635,7 +1664,6 @@ export class Matterbridge extends EventEmitter {
1635
1664
  let failCount = 0;
1636
1665
  const startMatterInterval = setInterval(async () => {
1637
1666
  for (const plugin of this.registeredPlugins) {
1638
- // if (!plugin.enabled || plugin.error) continue;
1639
1667
  // new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
1640
1668
  if (!plugin.enabled)
1641
1669
  continue;
@@ -1661,53 +1689,20 @@ export class Matterbridge extends EventEmitter {
1661
1689
  this.log.debug('***Cleared startMatterInterval interval for Matterbridge');
1662
1690
  await this.startMatterServer();
1663
1691
  this.log.info('Matter server started');
1664
- await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
1692
+ // Configure the plugins
1665
1693
  /*
1666
- const gdcCluster = this.commissioningServer?.getRootClusterServer(GeneralDiagnosticsCluster);
1667
- if (gdcCluster) {
1668
- console.log('GeneralDiagnosticsCluster', gdcCluster);
1669
- const net = gdcCluster.getNetworkInterfacesAttribute();
1670
- console.log('NetworkInterfaces', net);
1671
-
1672
- // We have like "30:f6:ef:69:2b:c5" in this.systemInformation.macAddress
1673
- const macArray = this.systemInformation.macAddress.split(':').map((hex) => parseInt(hex, 16));
1674
- let hardwareAddress = new Uint8Array(macArray);
1675
- if (hardwareAddress.length === 6) hardwareAddress = Uint8Array.from([0, 0, ...hardwareAddress]);
1676
- // We have like "192.168.1.189" in this.systemInformation.ipv4Address
1677
- const ipv4Array = this.systemInformation.ipv4Address.split('.').map((num) => parseInt(num));
1678
- const iPv4Address = new Uint8Array(ipv4Array);
1679
- // We have like "fd78:cbf8:4939:746:d555:85a9:74f6:9c6" in this.systemInformation.ipv6Address
1680
- const ipv6Groups = this.systemInformation.ipv6Address.split(':');
1681
- const ipv6Array = [];
1682
- for (const group of ipv6Groups) {
1683
- const decimal = parseInt(group, 16);
1684
- ipv6Array.push(decimal >> 8); // High byte
1685
- ipv6Array.push(decimal & 0xff); // Low byte
1686
- }
1687
- const iPv6Address = new Uint8Array(ipv6Array);
1688
- this.log.warn(`GeneralDiagnosticsCluster for hardwareAddress ${this.systemInformation.macAddress} => ${debugStringify(hardwareAddress)}`);
1689
- this.log.warn(`GeneralDiagnosticsCluster for iPv4Address ${this.systemInformation.ipv4Address} => ${debugStringify(iPv4Address)}`);
1690
- this.log.warn(`GeneralDiagnosticsCluster for iPv6Address ${this.systemInformation.ipv6Address} => ${debugStringify(iPv6Address)}`);
1694
+ for (const plugin of this.registeredPlugins) {
1695
+ if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error) continue;
1691
1696
  try {
1692
- gdcCluster.setNetworkInterfacesAttribute([
1693
- {
1694
- name: 'eth0',
1695
- isOperational: true,
1696
- offPremiseServicesReachableIPv4: null,
1697
- offPremiseServicesReachableIPv6: null,
1698
- hardwareAddress,
1699
- iPv4Addresses: [iPv4Address],
1700
- iPv6Addresses: [iPv6Address],
1701
- type: GeneralDiagnostics.InterfaceType.Ethernet,
1702
- },
1703
- ]);
1704
- const net = gdcCluster.getNetworkInterfacesAttribute();
1705
- console.log('NetworkInterfaces', net);
1697
+ this.configurePlugin(plugin); // No await do it asyncronously
1706
1698
  } catch (error) {
1707
- this.log.error('GeneralDiagnosticsCluster.setNetworkInterfacesAttribute error:', error);
1699
+ plugin.error = true;
1700
+ this.log.error(`Error configuring plugin ${plg}${plugin.name}${er}`, error);
1708
1701
  }
1709
1702
  }
1710
1703
  */
1704
+ // Show the QR code for commissioning or log the already commissioned message
1705
+ await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
1711
1706
  // Setting reachability to true
1712
1707
  setTimeout(() => {
1713
1708
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
@@ -1723,14 +1718,11 @@ export class Matterbridge extends EventEmitter {
1723
1718
  // addDevice and addBridgedDeevice create the commissionig servers and add the devices to the the commissioning server or to the aggregator
1724
1719
  // Plugins are configured by callback when the plugin is commissioned
1725
1720
  // Start the interval to check if all plugins are loaded and started and so start the matter server
1726
- // TODO set a counter or a timeout
1727
1721
  this.log.debug('***Starting start matter interval in childbridge mode...');
1728
1722
  let failCount = 0;
1729
1723
  const startMatterInterval = setInterval(async () => {
1730
1724
  let allStarted = true;
1731
- // this.registeredPlugins.forEach((plugin) => {
1732
1725
  for (const plugin of this.registeredPlugins) {
1733
- // if (!plugin.enabled || plugin.error) return;
1734
1726
  // new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
1735
1727
  if (!plugin.enabled)
1736
1728
  continue;
@@ -2093,9 +2085,7 @@ export class Matterbridge extends EventEmitter {
2093
2085
  const info = commissioningServer.getActiveSessionInformation(fabricIndex);
2094
2086
  let connected = false;
2095
2087
  info.forEach((session) => {
2096
- this.log.info(
2097
- // eslint-disable-next-line max-len
2098
- `*Active session changed on fabric ${zb}${fabricIndex}${nf} id ${zb}${session.fabric?.fabricId}${nf} vendor ${zb}${session.fabric?.rootVendorId}${nf} ${this.getVendorIdName(session.fabric?.rootVendorId)} ${session.fabric?.label} for ${plg}${pluginName}${nf}`, debugStringify(session));
2088
+ this.log.info(`*Active session changed on fabric ${zb}${fabricIndex}${nf} id ${zb}${session.fabric?.fabricId}${nf} vendor ${zb}${session.fabric?.rootVendorId}${nf} ${this.getVendorIdName(session.fabric?.rootVendorId)} ${session.fabric?.label} for ${plg}${pluginName}${nf}`, debugStringify(session));
2099
2089
  if (session.isPeerActive === true && session.secure === true && session.numberOfActiveSubscriptions >= 1) {
2100
2090
  this.log.info(`*Controller ${zb}${session.fabric?.rootVendorId}${nf} ${this.getVendorIdName(session.fabric?.rootVendorId)} ${session.fabric?.label} connected to ${plg}${pluginName}${nf} on session ${session.name}`);
2101
2091
  connected = true;
@@ -2172,48 +2162,6 @@ export class Matterbridge extends EventEmitter {
2172
2162
  }
2173
2163
  },
2174
2164
  });
2175
- /*
2176
- const gdcCluster = commissioningServer.getRootClusterServer(GeneralDiagnosticsCluster);
2177
- if (gdcCluster) {
2178
- // console.log('GeneralDiagnosticsCluster found for', plg, pluginName, db);
2179
- // console.log('GeneralDiagnosticsCluster', gdcCluster);
2180
- // We have like "30:f6:ef:69:2b:c5" in this.systemInformation.macAddress
2181
- const macArray = this.systemInformation.macAddress.split(':').map((hex) => parseInt(hex, 16));
2182
- let hardwareAddress = new Uint8Array(macArray);
2183
- if (hardwareAddress.length === 6) hardwareAddress = Uint8Array.from([0, 0, ...hardwareAddress]);
2184
- // We have like "192.168.1.189" in this.systemInformation.ipv4Address
2185
- const ipv4Array = this.systemInformation.ipv4Address.split('.').map((num) => parseInt(num));
2186
- const iPv4Address = new Uint8Array(ipv4Array);
2187
- // We have like "fd78:cbf8:4939:746:d555:85a9:74f6:9c6" in this.systemInformation.ipv6Address
2188
- const ipv6Groups = this.systemInformation.ipv6Address.split(':');
2189
- const ipv6Array = [];
2190
- for (const group of ipv6Groups) {
2191
- const decimal = parseInt(group, 16);
2192
- ipv6Array.push(decimal >> 8); // High byte
2193
- ipv6Array.push(decimal & 0xff); // Low byte
2194
- }
2195
- const iPv6Address = new Uint8Array(ipv6Array);
2196
- this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} hardwareAddress ${this.systemInformation.macAddress} => ${debugStringify(hardwareAddress)}`);
2197
- this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} iPv4Address ${this.systemInformation.ipv4Address} => ${debugStringify(iPv4Address)}`);
2198
- this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} iPv6Address ${this.systemInformation.ipv6Address} => ${debugStringify(iPv6Address)}`);
2199
- try {
2200
- gdcCluster.setNetworkInterfacesAttribute([
2201
- {
2202
- name: 'eth0',
2203
- isOperational: true,
2204
- offPremiseServicesReachableIPv4: null,
2205
- offPremiseServicesReachableIPv6: null,
2206
- hardwareAddress,
2207
- iPv4Addresses: [iPv4Address],
2208
- iPv6Addresses: [iPv6Address],
2209
- type: GeneralDiagnostics.InterfaceType.Ethernet,
2210
- },
2211
- ]);
2212
- } catch (error) {
2213
- this.log.error(`GeneralDiagnosticsCluster.setNetworkInterfacesAttribute for ${plg}${pluginName}${er} error:`, error);
2214
- }
2215
- } else this.log.warn(`*GeneralDiagnosticsCluster not found for ${plg}${pluginName}${wr}`);
2216
- */
2217
2165
  commissioningServer.addCommandHandler('testEventTrigger', async ({ request: { enableKey, eventTrigger } }) => this.log.info(`testEventTrigger called on GeneralDiagnostic cluster: ${enableKey} ${eventTrigger}`));
2218
2166
  return commissioningServer;
2219
2167
  }
@@ -2323,16 +2271,18 @@ export class Matterbridge extends EventEmitter {
2323
2271
  const networkInterfaces = os.networkInterfaces();
2324
2272
  this.systemInformation.ipv4Address = 'Not found';
2325
2273
  this.systemInformation.ipv6Address = 'Not found';
2326
- for (const interfaceDetails of Object.values(networkInterfaces)) {
2274
+ for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
2327
2275
  if (!interfaceDetails) {
2328
2276
  break;
2329
2277
  }
2330
2278
  for (const detail of interfaceDetails) {
2331
2279
  if (detail.family === 'IPv4' && !detail.internal && this.systemInformation.ipv4Address === 'Not found') {
2280
+ this.systemInformation.interfaceName = interfaceName;
2332
2281
  this.systemInformation.ipv4Address = detail.address;
2333
2282
  this.systemInformation.macAddress = detail.mac;
2334
2283
  }
2335
2284
  else if (detail.family === 'IPv6' && !detail.internal && this.systemInformation.ipv6Address === 'Not found') {
2285
+ this.systemInformation.interfaceName = interfaceName;
2336
2286
  this.systemInformation.ipv6Address = detail.address;
2337
2287
  this.systemInformation.macAddress = detail.mac;
2338
2288
  }
@@ -2361,6 +2311,7 @@ export class Matterbridge extends EventEmitter {
2361
2311
  this.log.debug('Host System Information:');
2362
2312
  this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
2363
2313
  this.log.debug(`- User: ${this.systemInformation.user}`);
2314
+ this.log.debug(`- Interface: ${this.systemInformation.interfaceName}`);
2364
2315
  this.log.debug(`- MAC Address: ${this.systemInformation.macAddress}`);
2365
2316
  this.log.debug(`- IPv4 Address: ${this.systemInformation.ipv4Address}`);
2366
2317
  this.log.debug(`- IPv6 Address: ${this.systemInformation.ipv6Address}`);
@@ -2532,9 +2483,10 @@ export class Matterbridge extends EventEmitter {
2532
2483
  type: plugin.type,
2533
2484
  name: plugin.name,
2534
2485
  version: plugin.version,
2535
- latestVersion: plugin.latestVersion,
2536
2486
  description: plugin.description,
2537
2487
  author: plugin.author,
2488
+ latestVersion: plugin.latestVersion,
2489
+ locked: plugin.locked,
2538
2490
  error: plugin.error,
2539
2491
  enabled: plugin.enabled,
2540
2492
  loaded: plugin.loaded,
@@ -2542,11 +2494,11 @@ export class Matterbridge extends EventEmitter {
2542
2494
  configured: plugin.configured,
2543
2495
  paired: plugin.paired,
2544
2496
  connected: plugin.connected,
2497
+ fabricInfo: plugin.fabricInfo,
2545
2498
  registeredDevices: plugin.registeredDevices,
2499
+ addedDevices: plugin.addedDevices,
2546
2500
  qrPairingCode: plugin.qrPairingCode,
2547
2501
  manualPairingCode: plugin.manualPairingCode,
2548
- // configJson: includeConfigSchema ? await this.loadPluginConfig(plugin) : {},
2549
- // schemaJson: includeConfigSchema ? await this.loadPluginSchema(plugin) : {},
2550
2502
  configJson: includeConfigSchema ? plugin.configJson : {},
2551
2503
  schemaJson: includeConfigSchema ? plugin.schemaJson : {},
2552
2504
  });
@@ -2662,6 +2614,7 @@ export class Matterbridge extends EventEmitter {
2662
2614
  const cleanMessage = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
2663
2615
  // Remove leading asterisks from the message
2664
2616
  const finalMessage = cleanMessage.replace(/^\*+/, '');
2617
+ // Send the message to all connected clients
2665
2618
  this.webSocketServer?.clients.forEach((client) => {
2666
2619
  if (client.readyState === WebSocket.OPEN) {
2667
2620
  client.send(JSON.stringify({ type, subType, message: finalMessage }));
@@ -2674,52 +2627,33 @@ export class Matterbridge extends EventEmitter {
2674
2627
  * @param port The port number to run the frontend server on. Default is 3000.
2675
2628
  */
2676
2629
  async initializeFrontend(port = 8283) {
2630
+ if (hasParameter('test_https')) {
2631
+ await this.initializeHttpsFrontend(8443);
2632
+ return;
2633
+ }
2677
2634
  this.log.debug(`Initializing the frontend on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
2635
+ // Create a WebSocket server
2678
2636
  const wssPort = 8284;
2679
- const useHttps = false;
2680
- // const wssHost = (useHttps ? 'wss://' : 'ws://') + `${os.hostname().toLowerCase()}:${wssPort}`;
2681
- const wssHost = (useHttps ? 'wss://' : 'ws://') + `${this.systemInformation.ipv4Address}:${wssPort}`;
2682
- if (!useHttps) {
2683
- // Create a WebSocket server no certificate required
2684
- this.webSocketServer = new WebSocketServer({ port: wssPort });
2685
- // this.log.info(`WebSocket server listening on ${UNDERLINE}${wssHost}${UNDERLINEOFF}${rs}`);
2686
- }
2687
- else {
2688
- // Define the options for HTTPS server
2689
- // openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout mykey.key -out mycert.pem -config openssl.cnf
2690
- // For wss connect the browser to https://laptop5_luca:8284/ https://192.168.1.189/log and accept the certificate
2691
- const serverOptions = {
2692
- cert: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/mycert.pem')), // Ensure the path is correct
2693
- key: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/mykey.key')), // Ensure the path is correct
2694
- // cert: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/laptop5_luca.pem')), // Ensure the path is correct
2695
- // key: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/laptop5_luca.key')), // Ensure the path is correct
2696
- };
2697
- // Create an HTTPS server
2698
- const httpsServer = https.createServer(serverOptions);
2699
- // Attach WebSocket server to HTTPS server
2700
- this.webSocketServer = new WebSocketServer({ server: httpsServer });
2701
- // Listen on a specific port
2702
- httpsServer.listen(wssPort, () => {
2703
- this.log.info(`WebSocket server listening on ${UNDERLINE}${wssHost}${UNDERLINEOFF}${rs}`);
2704
- });
2705
- }
2637
+ const wssHost = `ws://${this.systemInformation.ipv4Address}:${wssPort}`;
2638
+ this.webSocketServer = new WebSocketServer({ port: wssPort, host: this.systemInformation.ipv4Address });
2639
+ this.log.debug(`WebSocket server created on ${UNDERLINE}${wssHost}${UNDERLINEOFF}${rs}`);
2706
2640
  this.webSocketServer.on('connection', (ws) => {
2707
2641
  this.log.info('WebSocketServer client connected');
2708
2642
  this.log.setGlobalCallback(this.wssSendMessage.bind(this));
2709
- this.log.debug('WebSocketServer activated logger callback');
2643
+ this.log.debug('WebSocketServer logger callback added');
2710
2644
  this.wssSendMessage('Matterbridge', 'info', 'WebSocketServer client connected to Matterbridge');
2711
2645
  ws.on('message', (message) => {
2712
- this.log.info(`WebSocket received message => ${message}`);
2646
+ this.log.info(`WebSocket client sent a message => ${message}`);
2713
2647
  });
2714
2648
  ws.on('close', () => {
2715
2649
  this.log.info('WebSocket client disconnected');
2716
2650
  if (this.webSocketServer?.clients.size === 0) {
2717
2651
  this.log.setGlobalCallback(undefined);
2718
- this.log.debug('WebSocket deactivated logger callback');
2652
+ this.log.debug('All WebSocket client disconnected. WebSocketServer logger callback removed');
2719
2653
  }
2720
2654
  });
2721
2655
  ws.on('error', (error) => {
2722
- this.log.error(`WebSocket error: ${error}`);
2656
+ this.log.error(`WebSocket client error: ${error}`);
2723
2657
  });
2724
2658
  });
2725
2659
  this.webSocketServer.on('error', (ws, error) => {
@@ -2733,6 +2667,24 @@ export class Matterbridge extends EventEmitter {
2733
2667
  // Serve React build directory
2734
2668
  this.expressApp = express();
2735
2669
  this.expressApp.use(express.static(path.join(this.rootDirectory, 'frontend/build')));
2670
+ // Listen on HTTP
2671
+ this.expressServer = this.expressApp.listen(port, () => {
2672
+ this.log.info(`The frontend is listening on ${UNDERLINE}http://${this.systemInformation.ipv4Address}:${port}${UNDERLINEOFF}${rs}`);
2673
+ this.log.debug(`The frontend is listening on ${UNDERLINE}http://[${this.systemInformation.ipv6Address}]:${port}${UNDERLINEOFF}${rs}`);
2674
+ });
2675
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2676
+ this.expressServer.on('error', (error) => {
2677
+ this.log.error(`Frontend error listening on ${UNDERLINE}http://${this.systemInformation.ipv4Address}:${port}${UNDERLINEOFF}${rs}`);
2678
+ switch (error.code) {
2679
+ case 'EACCES':
2680
+ this.log.error(`Port ${port} requires elevated privileges`);
2681
+ break;
2682
+ case 'EADDRINUSE':
2683
+ this.log.error(`Port ${port} is already in use`);
2684
+ break;
2685
+ }
2686
+ process.exit(1);
2687
+ });
2736
2688
  // Endpoint to validate login code
2737
2689
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
2738
2690
  const { password } = req.body;
@@ -2783,13 +2735,15 @@ export class Matterbridge extends EventEmitter {
2783
2735
  this.matterbridgeInformation.matterbridgeConnected = this.matterbridgeConnected;
2784
2736
  // this.matterbridgeInformation.matterbridgeFabricInfo = this.matterbridgeFabricInfo;
2785
2737
  const response = { wssHost, qrPairingCode, manualPairingCode, systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
2786
- this.log.debug('Response:', debugStringify(response));
2738
+ // this.log.debug('Response:', debugStringify(response));
2787
2739
  res.json(response);
2788
2740
  });
2789
2741
  // Endpoint to provide plugins
2790
2742
  this.expressApp.get('/api/plugins', async (req, res) => {
2791
2743
  this.log.debug('The frontend sent /api/plugins');
2792
- res.json(await this.getBaseRegisteredPlugins(true));
2744
+ const response = await this.getBaseRegisteredPlugins(true);
2745
+ // this.log.debug('Response:', debugStringify(response));
2746
+ res.json(response);
2793
2747
  });
2794
2748
  // Endpoint to provide devices
2795
2749
  this.expressApp.get('/api/devices', (req, res) => {
@@ -2816,6 +2770,7 @@ export class Matterbridge extends EventEmitter {
2816
2770
  cluster: cluster,
2817
2771
  });
2818
2772
  });
2773
+ // this.log.debug('Response:', debugStringify(data));
2819
2774
  res.json(data);
2820
2775
  });
2821
2776
  // Endpoint to provide the cluster servers of the devices
@@ -2946,7 +2901,7 @@ export class Matterbridge extends EventEmitter {
2946
2901
  plugin.platform?.log.setLogDebug(this.debugEnabled);
2947
2902
  });
2948
2903
  }
2949
- // Handle the command reset from Settings
2904
+ // Handle the command unregister from Settings
2950
2905
  if (command === 'unregister') {
2951
2906
  await this.unregisterAndShutdownProcess();
2952
2907
  }
@@ -2958,7 +2913,7 @@ export class Matterbridge extends EventEmitter {
2958
2913
  if (command === 'factoryreset') {
2959
2914
  this.shutdownProcessAndFactoryReset(); // No await do it asyncronously
2960
2915
  }
2961
- // Handle the command restart from Header
2916
+ // Handle the command shutdown from Header
2962
2917
  if (command === 'shutdown') {
2963
2918
  this.shutdownProcess(); // No await do it asyncronously
2964
2919
  }
@@ -2980,7 +2935,7 @@ export class Matterbridge extends EventEmitter {
2980
2935
  }
2981
2936
  this.updateProcess();
2982
2937
  }
2983
- // Handle the command saveschema from Home
2938
+ // Handle the command saveconfig from Home
2984
2939
  if (command === 'saveconfig') {
2985
2940
  param = param.replace(/\*/g, '\\');
2986
2941
  this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
@@ -3030,6 +2985,7 @@ export class Matterbridge extends EventEmitter {
3030
2985
  this.registeredPlugins.push(plugin);
3031
2986
  await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
3032
2987
  this.log.info(`Plugin ${plg}${packageJsonPath}${nf} type ${plugin.type} added to matterbridge. Restart required.`);
2988
+ this.startPlugin(plugin, 'The plugin has been added to Matterbridge', true);
3033
2989
  }
3034
2990
  else {
3035
2991
  this.log.error(`Error adding plugin ${plg}${packageJsonPath}${er}`);
@@ -3047,8 +3003,10 @@ export class Matterbridge extends EventEmitter {
3047
3003
  if (index !== -1) {
3048
3004
  if (this.registeredPlugins[index].platform) {
3049
3005
  await this.registeredPlugins[index].platform?.onShutdown('The plugin has been removed.');
3050
- // await this.savePluginConfig(this.registeredPlugins[index]);
3051
3006
  }
3007
+ // Remove all devices from the plugin
3008
+ await this.removeAllBridgedDevices(this.registeredPlugins[index].name);
3009
+ // Remove the plugin from the registered plugins
3052
3010
  this.registeredPlugins.splice(index, 1);
3053
3011
  await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
3054
3012
  this.log.info(`Plugin ${plg}${param}${nf} removed from matterbridge`);
@@ -3065,6 +3023,7 @@ export class Matterbridge extends EventEmitter {
3065
3023
  const plugin = plugins.find((plugin) => plugin.name === param);
3066
3024
  if (plugin) {
3067
3025
  plugin.enabled = true;
3026
+ plugin.locked = undefined;
3068
3027
  plugin.error = undefined;
3069
3028
  plugin.loaded = undefined;
3070
3029
  plugin.started = undefined;
@@ -3072,6 +3031,7 @@ export class Matterbridge extends EventEmitter {
3072
3031
  plugin.connected = undefined;
3073
3032
  plugin.platform = undefined;
3074
3033
  plugin.registeredDevices = undefined;
3034
+ plugin.addedDevices = undefined;
3075
3035
  await this.nodeContext?.set('plugins', plugins);
3076
3036
  this.log.info(`Enabled plugin ${plg}${param}${nf}`);
3077
3037
  }
@@ -3096,10 +3056,13 @@ export class Matterbridge extends EventEmitter {
3096
3056
  const pluginToDisable = this.findPlugin(param);
3097
3057
  if (pluginToDisable) {
3098
3058
  if (pluginToDisable.platform) {
3099
- await pluginToDisable.platform.onShutdown('The plugin has been removed.');
3100
- // await this.savePluginConfig(pluginToDisable);
3059
+ await pluginToDisable.platform.onShutdown('The plugin has been disabled.');
3101
3060
  }
3061
+ // Remove all devices from the plugin
3062
+ this.log.info(`Unregistering devices for plugin ${plg}${pluginToDisable.name}${nf}...`);
3063
+ await this.removeAllBridgedDevices(pluginToDisable.name);
3102
3064
  pluginToDisable.enabled = false;
3065
+ pluginToDisable.locked = undefined;
3103
3066
  pluginToDisable.error = undefined;
3104
3067
  pluginToDisable.loaded = undefined;
3105
3068
  pluginToDisable.started = undefined;
@@ -3107,12 +3070,15 @@ export class Matterbridge extends EventEmitter {
3107
3070
  pluginToDisable.connected = undefined;
3108
3071
  pluginToDisable.platform = undefined;
3109
3072
  pluginToDisable.registeredDevices = undefined;
3073
+ pluginToDisable.addedDevices = undefined;
3074
+ // Save the plugins state in node storage
3110
3075
  const plugins = await this.nodeContext?.get('plugins');
3111
3076
  if (!plugins)
3112
3077
  return;
3113
3078
  const plugin = plugins.find((plugin) => plugin.name === param);
3114
3079
  if (plugin) {
3115
3080
  plugin.enabled = false;
3081
+ plugin.locked = undefined;
3116
3082
  plugin.error = undefined;
3117
3083
  plugin.loaded = undefined;
3118
3084
  plugin.started = undefined;
@@ -3120,6 +3086,7 @@ export class Matterbridge extends EventEmitter {
3120
3086
  plugin.connected = undefined;
3121
3087
  plugin.platform = undefined;
3122
3088
  plugin.registeredDevices = undefined;
3089
+ plugin.addedDevices = undefined;
3123
3090
  await this.nodeContext?.set('plugins', plugins);
3124
3091
  this.log.info(`Disabled plugin ${plg}${param}${nf}`);
3125
3092
  }
@@ -3127,47 +3094,67 @@ export class Matterbridge extends EventEmitter {
3127
3094
  }
3128
3095
  res.json({ message: 'Command received' });
3129
3096
  });
3130
- // Fallback for routing
3097
+ // Fallback for routing (must be the last route)
3131
3098
  this.expressApp.get('*', (req, res) => {
3132
- this.log.debug('The frontend sent *', req.url);
3099
+ this.log.debug('The frontend sent:', req.url);
3100
+ this.log.debug('Response send file:', path.join(this.rootDirectory, 'frontend/build/index.html'));
3133
3101
  res.sendFile(path.join(this.rootDirectory, 'frontend/build/index.html'));
3134
3102
  });
3135
- if (!useHttps) {
3136
- // Listen on HTTP
3137
- this.expressServer = this.expressApp.listen(port, () => {
3138
- this.log.info(`The frontend is listening on ${UNDERLINE}http://${this.systemInformation.ipv4Address}:${port}${UNDERLINEOFF}${rs}`);
3103
+ this.log.debug(`Frontend initialized on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
3104
+ }
3105
+ // Just for testing purposes. Use with matterbridge -test_https so no bridge is loaded
3106
+ async initializeHttpsFrontend(port = 8443) {
3107
+ this.log.debug(`Initializing the https frontend on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
3108
+ // Create the express app that serves the frontend
3109
+ this.expressApp = express();
3110
+ // Load the SSL certificate and private key
3111
+ // If we need also the CA certificate, we can add it to the serverOptions object
3112
+ // I created dev certificates with openssl using the following commands:
3113
+ // openssl genrsa -out key.pem 2048
3114
+ // openssl req -new -key key.pem -out csr.pem
3115
+ // openssl x509 -req -days 365 -in csr.pem -signkey key.pem -out cert.pem
3116
+ const serverOptions = {
3117
+ cert: await fs.readFile(path.join(this.matterbridgeDirectory, 'certs/cert.pem'), 'utf8'),
3118
+ key: await fs.readFile(path.join(this.matterbridgeDirectory, 'certs/key.pem'), 'utf8'),
3119
+ // ca: await fs.readFile(path.join(this.matterbridgeDirectory, 'certs/ca.pem'), 'utf8'),
3120
+ };
3121
+ // Create an HTTPS server with the SSL certificate and private key and attach the express app
3122
+ const httpsServer = https.createServer(serverOptions, this.expressApp);
3123
+ // Fallback for routing
3124
+ this.expressApp.get('/', (req, res) => {
3125
+ this.log.debug('The frontend sent:', req.url);
3126
+ res.send('Hello, HTTPS!');
3127
+ });
3128
+ // Listen on a specific port
3129
+ // Connect to the frontend with 'https://localhost:8443'
3130
+ httpsServer.listen(port, () => {
3131
+ this.log.info(`HTTPS server listening on ${UNDERLINE}'https://localhost:${port}${UNDERLINEOFF}${rs}`);
3132
+ });
3133
+ // Attach WebSocket server to HTTPS server
3134
+ this.webSocketServer = new WebSocketServer({ server: httpsServer });
3135
+ this.log.info(`WebSocketServer runnig on ${UNDERLINE}wss://localhost:${port}${UNDERLINEOFF}${rs}`);
3136
+ // Handle connections to the WebSocketServer
3137
+ // Connect to the WebSocket server using wss from the react frontend this way: const ws = new WebSocket('wss://localhost:8443');
3138
+ this.webSocketServer.on('connection', (ws) => {
3139
+ this.log.info('WebSocketServer: WebSocket client connected');
3140
+ ws.on('message', (message) => {
3141
+ this.log.info(`WebSocket client sent message => ${message}`);
3139
3142
  });
3140
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
3141
- this.expressServer.on('error', (error) => {
3142
- this.log.error(`Frontend error listening on ${UNDERLINE}http://${this.systemInformation.ipv4Address}:${port}${UNDERLINEOFF}${rs}`);
3143
- switch (error.code) {
3144
- case 'EACCES':
3145
- this.log.error(`Port ${port} requires elevated privileges`);
3146
- break;
3147
- case 'EADDRINUSE':
3148
- this.log.error(`Port ${port} is already in use`);
3149
- break;
3150
- default:
3151
- this.log.error(`Port ${port} requires elevated privileges`);
3152
- }
3153
- process.exit(1);
3143
+ ws.on('close', () => {
3144
+ this.log.info('WebSocket client disconnected');
3145
+ if (this.webSocketServer?.clients.size === 0) {
3146
+ this.log.info('All WebSocket clients disconnected');
3147
+ }
3154
3148
  });
3155
- }
3156
- else {
3157
- // SSL certificate and private key paths
3158
- const options = {
3159
- cert: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/laptop5_luca.pem')), // Ensure the path is correct
3160
- key: await fs.readFile(path.join(this.rootDirectory, 'frontend/certificates/laptop5_luca.key')), // Ensure the path is correct
3161
- };
3162
- // Create HTTPS server
3163
- const httpsServer = https.createServer(options, this.expressApp);
3164
- // Specify the port to listen on, for example 443 for default HTTPS
3165
- const PORT = 443;
3166
- httpsServer.listen(PORT, () => {
3167
- this.log.info(`The frontend is listening on ${UNDERLINE}https://${this.systemInformation.ipv4Address}:${PORT}${UNDERLINEOFF}${rs}`);
3149
+ ws.on('error', (error) => {
3150
+ this.log.error(`WebSocket client error: ${error}`);
3168
3151
  });
3169
- }
3170
- this.log.debug(`Frontend initialized on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
3152
+ });
3153
+ // Handle error from the WebSocketServer
3154
+ this.webSocketServer.on('error', (ws, error) => {
3155
+ this.log.error(`WebSocketServer error: ${error}`);
3156
+ return;
3157
+ });
3171
3158
  }
3172
3159
  /**
3173
3160
  * Retrieves the cluster text from a given device.
@@ -3228,125 +3215,4 @@ export class Matterbridge extends EventEmitter {
3228
3215
  return attributes;
3229
3216
  }
3230
3217
  }
3231
- /*
3232
- TO IMPLEMENT
3233
-
3234
- import { spawn } from 'child_process';
3235
-
3236
- function restartProcess() {
3237
- // Spawn a new process
3238
- const newProcess = spawn(process.argv[0], process.argv.slice(1), {
3239
- detached: true,
3240
- stdio: 'inherit',
3241
- });
3242
-
3243
- // Handle errors
3244
- newProcess.on('error', (err) => {
3245
- console.error('Failed to start new process:', err);
3246
- });
3247
-
3248
- // Unreference the new process so that the current process can exit
3249
- newProcess.unref();
3250
-
3251
- // Exit the current process
3252
- cleanup();
3253
- process.exit();
3254
- }
3255
-
3256
- import React from 'react';
3257
- import Form from "@rjsf/core";
3258
-
3259
- const schema = {
3260
- title: "Todo",
3261
- type: "object",
3262
- required: ["title"],
3263
- properties: {
3264
- title: {type: "string", title: "Title", default: "A new task"},
3265
- done: {type: "boolean", title: "Done?", default: false}
3266
- }
3267
- };
3268
-
3269
- const log = (type) => console.log.bind(console, type);
3270
-
3271
- function Todo() {
3272
- return (
3273
- <Form schema={schema}
3274
- onChange={log("changed")}
3275
- onSubmit={log("submitted")}
3276
- onError={log("errors")} />
3277
- );
3278
- }
3279
-
3280
- export default Todo;
3281
-
3282
- /*
3283
- How frontend was created
3284
- npx create-react-app matterbridge-frontend
3285
- cd matterbridge-frontend
3286
- npm install react-router-dom
3287
-
3288
- Success! Created frontend at C:\Users\lligu\OneDrive\GitHub\matterbridge\frontend
3289
- Inside that directory, you can run several commands:
3290
-
3291
- npm start
3292
- Starts the development server.
3293
-
3294
- npm run build
3295
- Bundles the app into static files for production.
3296
-
3297
- npm test
3298
- Starts the test runner.
3299
-
3300
- npm run eject
3301
- Removes this tool and copies build dependencies, configuration files
3302
- and scripts into the app directory. If you do this, you can’t go back!
3303
-
3304
- We suggest that you begin by typing:
3305
-
3306
- cd frontend
3307
- npm start
3308
-
3309
- Happy hacking!
3310
- PS C:\Users\lligu\OneDrive\GitHub\matterbridge> cd frontend
3311
- PS C:\Users\lligu\OneDrive\GitHub\matterbridge\frontend> npm run build
3312
-
3313
- > frontend@0.1.0 build
3314
- > react-scripts build
3315
-
3316
- Creating an optimized production build...
3317
- One of your dependencies, babel-preset-react-app, is importing the
3318
- "@babel/plugin-proposal-private-property-in-object" package without
3319
- declaring it in its dependencies. This is currently working because
3320
- "@babel/plugin-proposal-private-property-in-object" is already in your
3321
- node_modules folder for unrelated reasons, but it may break at any time.
3322
-
3323
- babel-preset-react-app is part of the create-react-app project, which
3324
- is not maintianed anymore. It is thus unlikely that this bug will
3325
- ever be fixed. Add "@babel/plugin-proposal-private-property-in-object" to
3326
- your devDependencies to work around this error. This will make this message
3327
- go away.
3328
-
3329
- Compiled successfully.
3330
-
3331
- File sizes after gzip:
3332
-
3333
- 46.65 kB build\static\js\main.9b7ec296.js
3334
- 1.77 kB build\static\js\453.8ab44547.chunk.js
3335
- 513 B build\static\css\main.f855e6bc.css
3336
-
3337
- The project was built assuming it is hosted at /.
3338
- You can control this with the homepage field in your package.json.
3339
-
3340
- The build folder is ready to be deployed.
3341
- You may serve it with a static server:
3342
-
3343
- npm install -g serve
3344
- serve -s build
3345
-
3346
- Find out more about deployment here:
3347
-
3348
- https://cra.link/deployment
3349
-
3350
- PS C:\Users\lligu\OneDrive\GitHub\matterbridge\frontend>
3351
- */
3352
3218
  //# sourceMappingURL=matterbridge.js.map