matterbridge 1.2.10 → 1.2.12

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.
@@ -33,9 +33,7 @@ import os from 'os';
33
33
  import path from 'path';
34
34
  import WebSocket, { WebSocketServer } from 'ws';
35
35
  import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
36
- import { BasicInformationCluster, BooleanStateCluster,
37
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
38
- BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster, ClusterServer, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById, } from '@project-chip/matter-node.js/cluster';
36
+ import { BasicInformationCluster, BooleanStateCluster, BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster, ClusterServer, FixedLabelCluster, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById, } from '@project-chip/matter-node.js/cluster';
39
37
  import { DeviceTypeId, VendorId } from '@project-chip/matter-node.js/datatype';
40
38
  import { Aggregator, DeviceTypes, NodeStateInformation } from '@project-chip/matter-node.js/device';
41
39
  import { Format, Level, Logger } from '@project-chip/matter-node.js/log';
@@ -55,6 +53,7 @@ export class Matterbridge extends EventEmitter {
55
53
  ipv6Address: '',
56
54
  nodeVersion: '',
57
55
  hostname: '',
56
+ user: '',
58
57
  osType: '',
59
58
  osRelease: '',
60
59
  osPlatform: '',
@@ -72,6 +71,7 @@ export class Matterbridge extends EventEmitter {
72
71
  matterbridgeVersion: '',
73
72
  matterbridgeLatestVersion: '',
74
73
  bridgeMode: '',
74
+ restartMode: '',
75
75
  debugEnabled: false,
76
76
  };
77
77
  homeDirectory = '';
@@ -188,7 +188,7 @@ export class Matterbridge extends EventEmitter {
188
188
  await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
189
189
  // Set reachability to true and trigger event after 60 seconds
190
190
  setTimeout(() => {
191
- this.log.info(`*Setting reachability to true for ${plg}Matterbridge${db}`);
191
+ this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
192
192
  if (this.commissioningServer)
193
193
  this.setCommissioningServerReachability(this.commissioningServer, true);
194
194
  if (this.matterAggregator)
@@ -365,7 +365,7 @@ export class Matterbridge extends EventEmitter {
365
365
  // Delete matter storage file
366
366
  await fs.unlink(path.join(this.matterbridgeDirectory, 'matterbridge.json'));
367
367
  // Delete node storage directory with its subdirectories
368
- await fs.rmdir(path.join(this.matterbridgeDirectory, 'storage'), { recursive: true });
368
+ await fs.rm(path.join(this.matterbridgeDirectory, 'storage'), { recursive: true });
369
369
  this.log.info('Factory reset done! Remove all paired devices from the controllers.');
370
370
  this.emit('shutdown');
371
371
  process.exit(0);
@@ -609,6 +609,31 @@ export class Matterbridge extends EventEmitter {
609
609
  await this.cleanup('shutting down...', false);
610
610
  this.hasCleanupStarted = false;
611
611
  }
612
+ /**
613
+ * Shut down the process and reset.
614
+ */
615
+ async unregisterAndShutdownProcess() {
616
+ this.log.info('Unregistering all devices and shutting down...');
617
+ for (const plugin of this.registeredPlugins.filter((plugin) => plugin.enabled && !plugin.error)) {
618
+ await this.removeAllBridgedDevices(plugin.name);
619
+ }
620
+ await this.cleanup('unregistered all devices and shutting down...', false);
621
+ this.hasCleanupStarted = false;
622
+ }
623
+ /**
624
+ * Shut down the process and reset.
625
+ */
626
+ async shutdownProcessAndReset() {
627
+ await this.cleanup('shutting down with reset...', false);
628
+ this.hasCleanupStarted = false;
629
+ }
630
+ /**
631
+ * Shut down the process and factory reset.
632
+ */
633
+ async shutdownProcessAndFactoryReset() {
634
+ await this.cleanup('shutting down with factory reset...', false);
635
+ this.hasCleanupStarted = false;
636
+ }
612
637
  /**
613
638
  * Cleans up the Matterbridge instance.
614
639
  * @param message - The cleanup message.
@@ -624,12 +649,17 @@ export class Matterbridge extends EventEmitter {
624
649
  this.log.debug('All listeners removed');
625
650
  // Calling the shutdown functions with a reason
626
651
  for (const plugin of this.registeredPlugins) {
627
- if (!plugin.enabled)
652
+ if (!plugin.enabled || plugin.error)
628
653
  continue;
629
- this.log.info(`*Shutting down plugin ${plg}${plugin.name}${nf}`);
654
+ this.log.info(`Shutting down plugin ${plg}${plugin.name}${nf}`);
630
655
  if (plugin.platform) {
631
- await plugin.platform.onShutdown('Matterbridge is closing: ' + message);
632
- await this.savePluginConfig(plugin);
656
+ try {
657
+ await plugin.platform.onShutdown('Matterbridge is closing: ' + message);
658
+ await this.savePluginConfig(plugin);
659
+ }
660
+ catch (error) {
661
+ this.log.error(`Plugin ${plg}${plugin.name}${er} shutting down error: ${error}`);
662
+ }
633
663
  }
634
664
  else {
635
665
  this.log.warn(`Plugin ${plg}${plugin.name}${wr} platform not found`);
@@ -703,7 +733,7 @@ export class Matterbridge extends EventEmitter {
703
733
  });
704
734
  this.webSocketServer = undefined;
705
735
  }
706
- /*const cleanupTimeout1 =*/ setTimeout(async () => {
736
+ setTimeout(async () => {
707
737
  // Closing matter
708
738
  await this.stopMatter();
709
739
  // Closing storage
@@ -733,7 +763,7 @@ export class Matterbridge extends EventEmitter {
733
763
  this.registeredPlugins = [];
734
764
  this.registeredDevices = [];
735
765
  this.log.info('Waiting for matter to deliver last messages...');
736
- /*const cleanupTimeout2 =*/ setTimeout(async () => {
766
+ setTimeout(async () => {
737
767
  if (restart) {
738
768
  if (message === 'updating...') {
739
769
  this.log.info('Cleanup completed. Updating...');
@@ -747,14 +777,27 @@ export class Matterbridge extends EventEmitter {
747
777
  }
748
778
  }
749
779
  else {
780
+ if (message === 'shutting down with reset...') {
781
+ // Delete matter storage file
782
+ this.log.info('Resetting Matterbridge commissioning information...');
783
+ await fs.unlink(path.join(this.matterbridgeDirectory, 'matterbridge.json'));
784
+ this.log.info('Reset done! Remove all paired devices from the controllers.');
785
+ }
786
+ if (message === 'shutting down with factory reset...') {
787
+ // Delete matter storage file
788
+ this.log.info('Resetting Matterbridge commissioning information...');
789
+ await fs.unlink(path.join(this.matterbridgeDirectory, 'matterbridge.json'));
790
+ // Delete node storage directory with its subdirectories
791
+ this.log.info('Resetting Matterbridge storage...');
792
+ await fs.rm(path.join(this.matterbridgeDirectory, 'storage'), { recursive: true });
793
+ this.log.info('Factory reset done! Remove all paired devices from the controllers.');
794
+ }
750
795
  this.log.info('Cleanup completed. Shutting down...');
751
796
  Matterbridge.instance = undefined;
752
797
  this.emit('shutdown');
753
798
  }
754
799
  }, 2 * 1000);
755
- //cleanupTimeout2.unref();
756
800
  }, 3 * 1000);
757
- //cleanupTimeout1.unref();
758
801
  }
759
802
  }
760
803
  /**
@@ -904,7 +947,7 @@ export class Matterbridge extends EventEmitter {
904
947
  async removeAllBridgedDevices(pluginName) {
905
948
  const plugin = this.findPlugin(pluginName);
906
949
  if (this.bridgeMode === 'childbridge' && plugin?.type === 'AccessoryPlatform') {
907
- this.log.info(`Removing devices for plugin ${plg}${pluginName}${nf}: AccessoryPlatform not supported in childbridge mode`);
950
+ this.log.info(`Removing devices for plugin ${plg}${pluginName}${nf} type AccessoryPlatform is not supported in childbridge mode`);
908
951
  return;
909
952
  }
910
953
  const devicesToRemove = [];
@@ -1190,13 +1233,15 @@ export class Matterbridge extends EventEmitter {
1190
1233
  return Promise.resolve();
1191
1234
  })
1192
1235
  .catch((err) => {
1236
+ plugin.error = true;
1193
1237
  this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
1194
- return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
1238
+ // return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
1195
1239
  });
1196
1240
  }
1197
1241
  catch (err) {
1242
+ plugin.error = true;
1198
1243
  this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
1199
- return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
1244
+ // return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
1200
1245
  }
1201
1246
  }
1202
1247
  /**
@@ -1224,13 +1269,15 @@ export class Matterbridge extends EventEmitter {
1224
1269
  return Promise.resolve();
1225
1270
  })
1226
1271
  .catch((err) => {
1272
+ plugin.error = true;
1227
1273
  this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`);
1228
- return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
1274
+ // return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
1229
1275
  });
1230
1276
  }
1231
1277
  catch (err) {
1278
+ plugin.error = true;
1232
1279
  this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`);
1233
- return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
1280
+ // return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
1234
1281
  }
1235
1282
  }
1236
1283
  /**
@@ -1286,12 +1333,14 @@ export class Matterbridge extends EventEmitter {
1286
1333
  }
1287
1334
  else {
1288
1335
  this.log.error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`);
1336
+ plugin.error = true;
1289
1337
  return;
1290
1338
  //return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`));
1291
1339
  }
1292
1340
  }
1293
1341
  catch (err) {
1294
1342
  this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
1343
+ plugin.error = true;
1295
1344
  return;
1296
1345
  //return Promise.reject(new Error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`));
1297
1346
  }
@@ -1480,10 +1529,10 @@ export class Matterbridge extends EventEmitter {
1480
1529
  let failCount = 0;
1481
1530
  const startInterval = setInterval(async () => {
1482
1531
  for (const plugin of this.registeredPlugins) {
1483
- if (!plugin.enabled)
1532
+ if (!plugin.enabled || plugin.error)
1484
1533
  continue;
1485
1534
  if (!plugin.loaded) {
1486
- this.log.info(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded}...`);
1535
+ this.log.debug(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded}...`);
1487
1536
  failCount++;
1488
1537
  if (failCount > 30) {
1489
1538
  this.log.error(`***Failed to load plugin ${plg}${plugin.name}${er}`);
@@ -1521,7 +1570,7 @@ export class Matterbridge extends EventEmitter {
1521
1570
  await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
1522
1571
  //if (hasParameter('advertise')) await this.commissioningServer.advertise();
1523
1572
  setTimeout(() => {
1524
- this.log.info(`*Setting reachability to true for ${plg}Matterbridge${db}`);
1573
+ this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1525
1574
  if (this.commissioningServer)
1526
1575
  this.setCommissioningServerReachability(this.commissioningServer, true);
1527
1576
  if (this.matterAggregator)
@@ -1542,7 +1591,7 @@ export class Matterbridge extends EventEmitter {
1542
1591
  let failCount = 0;
1543
1592
  const startInterval = setInterval(async () => {
1544
1593
  if (!plugin.loaded || !plugin.started /* || !plugin.configured*/) {
1545
- this.log.info(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
1594
+ this.log.debug(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
1546
1595
  failCount++;
1547
1596
  if (failCount > 30) {
1548
1597
  this.log.error(`***Failed to load plugin ${plg}${plugin.name}${er}`);
@@ -1624,7 +1673,7 @@ export class Matterbridge extends EventEmitter {
1624
1673
  }
1625
1674
  await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
1626
1675
  // Setting reachability to true
1627
- this.log.info(`*Setting reachability to true for ${plg}${plugin.name}${db}`);
1676
+ this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
1628
1677
  if (plugin.commissioningServer)
1629
1678
  this.setCommissioningServerReachability(plugin.commissioningServer, true);
1630
1679
  if (plugin.type === 'AccessoryPlatform' && plugin.device)
@@ -1743,7 +1792,7 @@ export class Matterbridge extends EventEmitter {
1743
1792
  await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
1744
1793
  }
1745
1794
  else {
1746
- this.log.info(`***The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is already commissioned . Waiting for controllers to connect ...`);
1795
+ this.log.info(`*The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is already commissioned . Waiting for controllers to connect ...`);
1747
1796
  if (pluginName !== 'Matterbridge') {
1748
1797
  const plugin = this.findPlugin(pluginName);
1749
1798
  if (plugin) {
@@ -1792,7 +1841,7 @@ export class Matterbridge extends EventEmitter {
1792
1841
  if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent)
1793
1842
  basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
1794
1843
  matterAggregator.getBridgedDevices().forEach((device) => {
1795
- this.log.debug(`*Setting reachability to true for bridged device: ${dev}${device.name}${nf}`);
1844
+ this.log.debug(`Setting reachability to true for bridged device: ${dev}${device.name}${nf}`);
1796
1845
  device.getClusterServer(BridgedDeviceBasicInformationCluster)?.setReachableAttribute(reachable);
1797
1846
  device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: reachable });
1798
1847
  });
@@ -1818,6 +1867,35 @@ export class Matterbridge extends EventEmitter {
1818
1867
  * @returns {CommissioningServer} The created commissioning server.
1819
1868
  */
1820
1869
  async createCommisioningServer(context, pluginName) {
1870
+ const getVendorIdName = (vendorId) => {
1871
+ if (!vendorId)
1872
+ return '';
1873
+ let vendorName = '';
1874
+ switch (vendorId) {
1875
+ case 4937:
1876
+ vendorName = '(AppleHome)';
1877
+ break;
1878
+ case 4362:
1879
+ vendorName = '(SmartThings)';
1880
+ break;
1881
+ case 4939:
1882
+ vendorName = '(HomeAssistant)';
1883
+ break;
1884
+ case 24582:
1885
+ vendorName = '(GoogleHome)';
1886
+ break;
1887
+ case 4701:
1888
+ vendorName = '(Tuya)';
1889
+ break;
1890
+ case 4742:
1891
+ vendorName = '(eWeLink)';
1892
+ break;
1893
+ default:
1894
+ vendorName = '(unknown)';
1895
+ break;
1896
+ }
1897
+ return vendorName;
1898
+ };
1821
1899
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db}`);
1822
1900
  const deviceName = await context.get('deviceName');
1823
1901
  const deviceType = await context.get('deviceType');
@@ -1860,16 +1938,9 @@ export class Matterbridge extends EventEmitter {
1860
1938
  const info = commissioningServer.getActiveSessionInformation(fabricIndex);
1861
1939
  let connected = false;
1862
1940
  info.forEach((session) => {
1863
- this.log.info(`***Active session changed on fabric ${fabricIndex} ${session.fabric?.rootVendorId}/${session.fabric?.label} for ${plg}${pluginName}${nf}`, debugStringify(session));
1941
+ this.log.info(`*Active session changed on fabric ${fabricIndex} ${session.fabric?.rootVendorId}${getVendorIdName(session.fabric?.rootVendorId)}/${session.fabric?.label} for ${plg}${pluginName}${nf}`, debugStringify(session));
1864
1942
  if (session.isPeerActive === true && session.secure === true && session.numberOfActiveSubscriptions >= 1) {
1865
- let controllerName = '';
1866
- if (session.fabric?.rootVendorId === 4937)
1867
- controllerName = 'AppleHome';
1868
- if (session.fabric?.rootVendorId === 4362)
1869
- controllerName = 'SmartThings';
1870
- if (session.fabric?.rootVendorId === 4939)
1871
- controllerName = 'HomeAssistant';
1872
- this.log.info(`***Controller ${session.fabric?.rootVendorId}${controllerName !== '' ? '(' + controllerName + ')' : ''}/${session.fabric?.label} connected to ${plg}${pluginName}${nf} on session ${session.name}`);
1943
+ this.log.info(`*Controller ${session.fabric?.rootVendorId}${getVendorIdName(session.fabric?.rootVendorId)}/${session.fabric?.label} connected to ${plg}${pluginName}${nf} on session ${session.name}`);
1873
1944
  connected = true;
1874
1945
  }
1875
1946
  });
@@ -1885,9 +1956,15 @@ export class Matterbridge extends EventEmitter {
1885
1956
  if (this.bridgeMode === 'bridge') {
1886
1957
  //Logger.defaultLogLevel = Level.INFO;
1887
1958
  for (const plugin of this.registeredPlugins) {
1888
- if (!plugin.enabled)
1959
+ if (!plugin.enabled || !plugin.loaded || plugin.error)
1889
1960
  continue;
1890
- this.startPlugin(plugin, 'Matterbridge is commissioned and controllers are connected', true); // No await do it asyncronously with also configurePlugin
1961
+ try {
1962
+ this.startPlugin(plugin, 'Matterbridge is commissioned and controllers are connected', true); // No await do it asyncronously with also configurePlugin
1963
+ }
1964
+ catch (error) {
1965
+ plugin.error = true;
1966
+ this.log.error(`Error starting plugin ${plg}${plugin.name}${er}`, error);
1967
+ }
1891
1968
  }
1892
1969
  Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
1893
1970
  }
@@ -1925,9 +2002,9 @@ export class Matterbridge extends EventEmitter {
1925
2002
  },
1926
2003
  commissioningChangedCallback: async (fabricIndex) => {
1927
2004
  const info = commissioningServer.getCommissionedFabricInformation(fabricIndex);
1928
- this.log.debug(`***Commissioning changed on fabric ${fabricIndex} for ${plg}${pluginName}${nf}`, debugStringify(info));
2005
+ this.log.debug(`*Commissioning changed on fabric ${fabricIndex} for ${plg}${pluginName}${nf}`, debugStringify(info));
1929
2006
  if (info.length === 0) {
1930
- this.log.warn(`***Commissioning removed from fabric ${fabricIndex} for ${plg}${pluginName}${wr}. Resetting the commissioning server ...`);
2007
+ this.log.warn(`*Commissioning removed from fabric ${fabricIndex} for ${plg}${pluginName}${wr}. Resetting the commissioning server ...`);
1931
2008
  await commissioningServer.factoryReset();
1932
2009
  if (pluginName === 'Matterbridge') {
1933
2010
  await this.matterbridgeContext?.clearAll();
@@ -1942,7 +2019,7 @@ export class Matterbridge extends EventEmitter {
1942
2019
  }
1943
2020
  }
1944
2021
  }
1945
- this.log.warn(`***Restart to activate the pairing for ${plg}${pluginName}${wr}`);
2022
+ this.log.warn(`*Restart to activate the pairing for ${plg}${pluginName}${wr}`);
1946
2023
  }
1947
2024
  },
1948
2025
  });
@@ -2076,6 +2153,7 @@ export class Matterbridge extends EventEmitter {
2076
2153
  const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
2077
2154
  // Host system information
2078
2155
  this.systemInformation.hostname = os.hostname();
2156
+ this.systemInformation.user = os.userInfo().username;
2079
2157
  this.systemInformation.osType = os.type(); // "Windows_NT", "Darwin", etc.
2080
2158
  this.systemInformation.osRelease = os.release(); // Kernel version
2081
2159
  this.systemInformation.osPlatform = os.platform(); // "win32", "linux", "darwin", etc.
@@ -2086,6 +2164,7 @@ export class Matterbridge extends EventEmitter {
2086
2164
  // Log the system information
2087
2165
  this.log.debug('Host System Information:');
2088
2166
  this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
2167
+ this.log.debug(`- User: ${this.systemInformation.user}`);
2089
2168
  this.log.debug(`- IPv4 Address: ${this.systemInformation.ipv4Address}`);
2090
2169
  this.log.debug(`- IPv6 Address: ${this.systemInformation.ipv6Address}`);
2091
2170
  this.log.debug(`- Node.js: ${versionMajor}.${versionMinor}.${versionPatch}`);
@@ -2267,7 +2346,7 @@ export class Matterbridge extends EventEmitter {
2267
2346
  args.unshift(command);
2268
2347
  command = 'sudo';
2269
2348
  }
2270
- this.log.debug(`Spawning command ${command} with ${debugStringify(args)}`);
2349
+ this.log.debug(`Spawn command ${command} with ${debugStringify(args)}`);
2271
2350
  return new Promise((resolve, reject) => {
2272
2351
  const childProcess = spawn(command, args, {
2273
2352
  stdio: ['inherit', 'pipe', 'pipe'],
@@ -2431,6 +2510,7 @@ export class Matterbridge extends EventEmitter {
2431
2510
  res.json({});
2432
2511
  }
2433
2512
  this.matterbridgeInformation.bridgeMode = this.bridgeMode;
2513
+ this.matterbridgeInformation.restartMode = this.restartMode;
2434
2514
  this.matterbridgeInformation.debugEnabled = this.debugEnabled;
2435
2515
  const response = { wssHost, qrPairingCode, manualPairingCode, systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
2436
2516
  this.log.debug('The frontend sent /api/settings');
@@ -2500,6 +2580,7 @@ export class Matterbridge extends EventEmitter {
2500
2580
  //console.log(error);
2501
2581
  }
2502
2582
  data.push({
2583
+ endpoint: registeredDevice.device.number ? registeredDevice.device.number.toString() : '...',
2503
2584
  clusterName: clusterServer.name,
2504
2585
  clusterId: '0x' + clusterServer.id.toString(16).padStart(2, '0'),
2505
2586
  attributeName: key,
@@ -2508,6 +2589,37 @@ export class Matterbridge extends EventEmitter {
2508
2589
  });
2509
2590
  });
2510
2591
  });
2592
+ registeredDevice.device.getChildEndpoints().forEach((childEndpoint) => {
2593
+ const name = registeredDevice.device.getChildEndpointName(childEndpoint);
2594
+ const clusterServers = childEndpoint.getAllClusterServers();
2595
+ clusterServers.forEach((clusterServer) => {
2596
+ Object.entries(clusterServer.attributes).forEach(([key, value]) => {
2597
+ if (clusterServer.name === 'EveHistory')
2598
+ return;
2599
+ //this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
2600
+ let attributeValue;
2601
+ try {
2602
+ if (typeof value.getLocal() === 'object')
2603
+ attributeValue = stringify(value.getLocal());
2604
+ else
2605
+ attributeValue = value.getLocal().toString();
2606
+ }
2607
+ catch (error) {
2608
+ attributeValue = 'Unavailable';
2609
+ this.log.debug(`GetLocal error ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
2610
+ //console.log(error);
2611
+ }
2612
+ data.push({
2613
+ endpoint: (childEndpoint.number ? childEndpoint.number.toString() : '...') + (name ? ' (' + name + ')' : ''),
2614
+ clusterName: clusterServer.name,
2615
+ clusterId: '0x' + clusterServer.id.toString(16).padStart(2, '0'),
2616
+ attributeName: key,
2617
+ attributeId: '0x' + value.id.toString(16).padStart(2, '0'),
2618
+ attributeValue,
2619
+ });
2620
+ });
2621
+ });
2622
+ });
2511
2623
  }
2512
2624
  });
2513
2625
  res.json(data);
@@ -2549,13 +2661,25 @@ export class Matterbridge extends EventEmitter {
2549
2661
  plugin.platform?.log.setLogDebug(this.debugEnabled);
2550
2662
  });
2551
2663
  }
2552
- // Handle the command shutdown from Header
2664
+ // Handle the command reset from Settings
2665
+ if (command === 'unregister') {
2666
+ await this.unregisterAndShutdownProcess();
2667
+ }
2668
+ // Handle the command reset from Settings
2669
+ if (command === 'reset') {
2670
+ this.shutdownProcessAndReset(); // No await do it asyncronously
2671
+ }
2672
+ // Handle the command factoryreset from Settings
2673
+ if (command === 'factoryreset') {
2674
+ this.shutdownProcessAndFactoryReset(); // No await do it asyncronously
2675
+ }
2676
+ // Handle the command restart from Header
2553
2677
  if (command === 'shutdown') {
2554
- this.shutdownProcess();
2678
+ this.shutdownProcess(); // No await do it asyncronously
2555
2679
  }
2556
2680
  // Handle the command restart from Header
2557
2681
  if (command === 'restart') {
2558
- this.restartProcess();
2682
+ this.restartProcess(); // No await do it asyncronously
2559
2683
  }
2560
2684
  // Handle the command update from Header
2561
2685
  if (command === 'update') {
@@ -2643,10 +2767,13 @@ export class Matterbridge extends EventEmitter {
2643
2767
  const plugin = plugins.find((plugin) => plugin.name === param);
2644
2768
  if (plugin) {
2645
2769
  plugin.enabled = true;
2770
+ plugin.error = undefined;
2646
2771
  plugin.loaded = undefined;
2647
2772
  plugin.started = undefined;
2648
2773
  plugin.configured = undefined;
2649
2774
  plugin.connected = undefined;
2775
+ plugin.platform = undefined;
2776
+ plugin.registeredDevices = undefined;
2650
2777
  await this.nodeContext?.set('plugins', plugins);
2651
2778
  this.log.info(`Enabled plugin ${plg}${param}${nf}`);
2652
2779
  }
@@ -2657,7 +2784,6 @@ export class Matterbridge extends EventEmitter {
2657
2784
  pluginToEnable.platform = await this.loadPlugin(pluginToEnable);
2658
2785
  if (pluginToEnable.platform) {
2659
2786
  await this.startPlugin(pluginToEnable, 'The plugin has been enabled', true);
2660
- pluginToEnable.enabled = false;
2661
2787
  }
2662
2788
  else {
2663
2789
  pluginToEnable.enabled = false;
@@ -2676,21 +2802,26 @@ export class Matterbridge extends EventEmitter {
2676
2802
  await this.savePluginConfig(pluginToDisable);
2677
2803
  }
2678
2804
  pluginToDisable.enabled = false;
2805
+ pluginToDisable.error = undefined;
2679
2806
  pluginToDisable.loaded = undefined;
2680
2807
  pluginToDisable.started = undefined;
2681
2808
  pluginToDisable.configured = undefined;
2682
2809
  pluginToDisable.connected = undefined;
2683
2810
  pluginToDisable.platform = undefined;
2811
+ pluginToDisable.registeredDevices = undefined;
2684
2812
  const plugins = await this.nodeContext?.get('plugins');
2685
2813
  if (!plugins)
2686
2814
  return;
2687
2815
  const plugin = plugins.find((plugin) => plugin.name === param);
2688
2816
  if (plugin) {
2689
2817
  plugin.enabled = false;
2818
+ plugin.error = undefined;
2690
2819
  plugin.loaded = undefined;
2691
2820
  plugin.started = undefined;
2692
2821
  plugin.configured = undefined;
2693
2822
  plugin.connected = undefined;
2823
+ plugin.platform = undefined;
2824
+ plugin.registeredDevices = undefined;
2694
2825
  await this.nodeContext?.set('plugins', plugins);
2695
2826
  this.log.info(`Disabled plugin ${plg}${param}${nf}`);
2696
2827
  }
@@ -2706,7 +2837,7 @@ export class Matterbridge extends EventEmitter {
2706
2837
  if (!useHttps) {
2707
2838
  // Listen on HTTP
2708
2839
  this.expressServer = this.expressApp.listen(port, () => {
2709
- this.log.info(`The frontend is running on ${UNDERLINE}http://localhost:${port}${UNDERLINEOFF}${rs}`);
2840
+ this.log.info(`The frontend is listening on ${UNDERLINE}http://${this.systemInformation.ipv4Address}:${port}${UNDERLINEOFF}${rs}`);
2710
2841
  });
2711
2842
  }
2712
2843
  else {
@@ -2720,7 +2851,7 @@ export class Matterbridge extends EventEmitter {
2720
2851
  // Specify the port to listen on, for example 443 for default HTTPS
2721
2852
  const PORT = 443;
2722
2853
  httpsServer.listen(PORT, () => {
2723
- this.log.info(`The frontend is running on ${UNDERLINE}https://localhost:${PORT}${UNDERLINEOFF}${rs}`);
2854
+ this.log.info(`The frontend is listening on ${UNDERLINE}https://${this.systemInformation.ipv4Address}:${PORT}${UNDERLINEOFF}${rs}`);
2724
2855
  });
2725
2856
  }
2726
2857
  this.log.debug(`Frontend initialized on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
@@ -2731,6 +2862,16 @@ export class Matterbridge extends EventEmitter {
2731
2862
  * @returns The attributes of the cluster servers in the device.
2732
2863
  */
2733
2864
  getClusterTextFromDevice(device) {
2865
+ const stringifyFixedLabel = (endpoint) => {
2866
+ const labelList = endpoint.getClusterServer(FixedLabelCluster)?.getLabelListAttribute();
2867
+ if (!labelList)
2868
+ return;
2869
+ const composed = labelList.find((entry) => entry.label === 'composed');
2870
+ if (composed)
2871
+ return 'Composed: ' + composed.value;
2872
+ else
2873
+ return ''; //'FixedLabel: ' + labelList.map((entry) => entry.label + ': ' + entry.value).join(' ');
2874
+ };
2734
2875
  let attributes = '';
2735
2876
  //this.log.debug(`getClusterTextFromDevice: ${device.name}`);
2736
2877
  const clusterServers = device.getAllClusterServers();
@@ -2767,7 +2908,9 @@ export class Matterbridge extends EventEmitter {
2767
2908
  if (clusterServer.name === 'PressureMeasurement')
2768
2909
  attributes += `Pressure: ${clusterServer.getMeasuredValueAttribute()} `;
2769
2910
  if (clusterServer.name === 'FlowMeasurement')
2770
- attributes += `Pressure: ${clusterServer.getMeasuredValueAttribute()} `;
2911
+ attributes += `Flow: ${clusterServer.getMeasuredValueAttribute()} `;
2912
+ if (clusterServer.name === 'FixedLabel')
2913
+ attributes += `${stringifyFixedLabel(device)} `;
2771
2914
  });
2772
2915
  return attributes;
2773
2916
  }
@@ -2797,6 +2940,31 @@ function restartProcess() {
2797
2940
  process.exit();
2798
2941
  }
2799
2942
 
2943
+ import React from 'react';
2944
+ import Form from "@rjsf/core";
2945
+
2946
+ const schema = {
2947
+ title: "Todo",
2948
+ type: "object",
2949
+ required: ["title"],
2950
+ properties: {
2951
+ title: {type: "string", title: "Title", default: "A new task"},
2952
+ done: {type: "boolean", title: "Done?", default: false}
2953
+ }
2954
+ };
2955
+
2956
+ const log = (type) => console.log.bind(console, type);
2957
+
2958
+ function Todo() {
2959
+ return (
2960
+ <Form schema={schema}
2961
+ onChange={log("changed")}
2962
+ onSubmit={log("submitted")}
2963
+ onError={log("errors")} />
2964
+ );
2965
+ }
2966
+
2967
+ export default Todo;
2800
2968
 
2801
2969
  /*
2802
2970
  How frontend was created