matterbridge 1.2.4 → 1.2.6

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 +29 -0
  2. package/README.md +106 -8
  3. package/TODO.md +7 -0
  4. package/dist/AirQualityCluster.d.ts +2 -13
  5. package/dist/AirQualityCluster.d.ts.map +1 -1
  6. package/dist/AirQualityCluster.js +6 -6
  7. package/dist/AirQualityCluster.js.map +1 -1
  8. package/dist/EveHistoryCluster.d.ts +6 -26
  9. package/dist/EveHistoryCluster.d.ts.map +1 -1
  10. package/dist/EveHistoryCluster.js +6 -14
  11. package/dist/EveHistoryCluster.js.map +1 -1
  12. package/dist/TvocCluster.d.ts +2 -15
  13. package/dist/TvocCluster.d.ts.map +1 -1
  14. package/dist/TvocCluster.js +6 -6
  15. package/dist/TvocCluster.js.map +1 -1
  16. package/dist/cli.js +6 -1
  17. package/dist/cli.js.map +1 -1
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/matterbridge.d.ts +15 -0
  23. package/dist/matterbridge.d.ts.map +1 -1
  24. package/dist/matterbridge.js +367 -174
  25. package/dist/matterbridge.js.map +1 -1
  26. package/dist/matterbridgeController.d.ts.map +1 -1
  27. package/dist/matterbridgeController.js +9 -0
  28. package/dist/matterbridgeController.js.map +1 -1
  29. package/dist/matterbridgeDevice.d.ts +1241 -9
  30. package/dist/matterbridgeDevice.d.ts.map +1 -1
  31. package/dist/matterbridgeDevice.js +315 -92
  32. package/dist/matterbridgeDevice.js.map +1 -1
  33. package/frontend/build/asset-manifest.json +14 -14
  34. package/frontend/build/index.html +1 -1
  35. package/frontend/build/matterbridge 32x32.png +0 -0
  36. package/frontend/build/matterbridge 64x64.png +0 -0
  37. package/frontend/build/static/css/main.61f6cf42.css +1 -1
  38. package/frontend/build/static/css/main.61f6cf42.css.map +1 -1
  39. package/frontend/build/static/js/main.6b861489.js +3 -0
  40. package/frontend/build/static/js/{main.e3553a4d.js.LICENSE.txt → main.6b861489.js.LICENSE.txt} +99 -99
  41. package/frontend/build/static/js/main.6b861489.js.map +1 -0
  42. package/matterbridge.service +17 -0
  43. package/package.json +6 -5
  44. package/dist/matterbridgeComposed.d.ts +0 -43
  45. package/dist/matterbridgeComposed.d.ts.map +0 -1
  46. package/dist/matterbridgeComposed.js +0 -58
  47. package/dist/matterbridgeComposed.js.map +0 -1
  48. package/frontend/build/Matterbridge.jpg +0 -0
  49. package/frontend/build/static/js/main.e3553a4d.js +0 -3
  50. package/frontend/build/static/js/main.e3553a4d.js.map +0 -1
@@ -31,7 +31,9 @@ import express from 'express';
31
31
  import os from 'os';
32
32
  import path from 'path';
33
33
  import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
34
- import { BasicInformationCluster, BooleanStateCluster, BridgedDeviceBasicInformationCluster, ClusterServer, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster } from '@project-chip/matter-node.js/cluster';
34
+ import { BasicInformationCluster, BooleanStateCluster,
35
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
36
+ BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster, ClusterServer, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById, } from '@project-chip/matter-node.js/cluster';
35
37
  import { DeviceTypeId, VendorId } from '@project-chip/matter-node.js/datatype';
36
38
  import { Aggregator, DeviceTypes, NodeStateInformation } from '@project-chip/matter-node.js/device';
37
39
  import { Format, Level, Logger } from '@project-chip/matter-node.js/log';
@@ -79,6 +81,7 @@ export class Matterbridge extends EventEmitter {
79
81
  matterbridgeLatestVersion = '';
80
82
  bridgeMode = '';
81
83
  debugEnabled = false;
84
+ port = 5540;
82
85
  log;
83
86
  hasCleanupStarted = false;
84
87
  registeredPlugins = [];
@@ -137,6 +140,8 @@ export class Matterbridge extends EventEmitter {
137
140
  - childbridge: start Matterbridge in childbridge mode
138
141
  - frontend [port]: start the frontend on the given port (default 3000)
139
142
  - debug: enable debug mode (default false)
143
+ - reset: remove the commissioning for Matterbridge (bridge mode). Shutdown Matterbridge before using it!
144
+ - factoryreset: remove all commissioning information and reset all internal storages. Shutdown Matterbridge before using it!
140
145
  - list: list the registered plugins
141
146
  - add [plugin path]: register the plugin from the given absolute or relative path
142
147
  - add [plugin name]: register the globally installed plugin with the given name
@@ -145,9 +150,13 @@ export class Matterbridge extends EventEmitter {
145
150
  - enable [plugin path]: enable the plugin from the given absolute or relative path
146
151
  - enable [plugin name]: enable the globally installed plugin with the given name
147
152
  - disable [plugin path]: disable the plugin from the given absolute or relative path
148
- - disable [plugin name]: disable the globally installed plugin with the given name\n`);
153
+ - disable [plugin name]: disable the globally installed plugin with the given name
154
+ - reset [plugin path]: remove the commissioning for the plugin from the given absolute or relative path (childbridge mode). Shutdown Matterbridge before using it!
155
+ - reset [plugin name]: remove the commissioning for the globally installed plugin (childbridge mode). Shutdown Matterbridge before using it!\n`);
149
156
  process.exit(0);
150
157
  }
158
+ // Set the first port to use
159
+ this.port = getIntParameter('port') ?? 5540;
151
160
  // Set Matterbridge logger
152
161
  if (hasParameter('debug'))
153
162
  this.debugEnabled = true;
@@ -222,6 +231,13 @@ export class Matterbridge extends EventEmitter {
222
231
  /*
223
232
  npm > npm.cmd on windows
224
233
  */
234
+ if (process.platform === 'win32' && command === 'npm') {
235
+ command = command + '.cmd';
236
+ }
237
+ if (process.platform === 'linux' && command === 'npm') {
238
+ args.unshift(command);
239
+ command = 'sudo';
240
+ }
225
241
  return new Promise((resolve, reject) => {
226
242
  const childProcess = spawn(command, args, {
227
243
  stdio: 'inherit',
@@ -294,10 +310,33 @@ export class Matterbridge extends EventEmitter {
294
310
  this.emit('shutdown');
295
311
  process.exit(0);
296
312
  }
297
- // Start the storage (we need it now for frontend and later for matterbridge)
313
+ if (hasParameter('factoryreset')) {
314
+ // Delete matter storage file
315
+ await fs.unlink(path.join(this.matterbridgeDirectory, 'matterbridge.json'));
316
+ // Delete node storage directory with its subdirectories
317
+ await fs.rmdir(path.join(this.matterbridgeDirectory, 'storage'), { recursive: true });
318
+ this.log.info('Factory reset done! Remove all paired devices from the controllers.');
319
+ this.emit('shutdown');
320
+ process.exit(0);
321
+ }
322
+ // Start the storage and create matterbridgeContext (we need it now for frontend and later for matterbridge)
298
323
  await this.startStorage('json', path.join(this.matterbridgeDirectory, 'matterbridge.json'));
299
- this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
300
324
  this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
325
+ if (hasParameter('reset') && getParameter('reset') === undefined) {
326
+ this.log.info('Resetting Matterbridge commissioning information...');
327
+ await this.matterbridgeContext?.clearAll();
328
+ await this.stopStorage();
329
+ this.log.info('Reset done! Remove the device from the controller.');
330
+ this.emit('shutdown');
331
+ process.exit(0);
332
+ }
333
+ if (getParameter('reset') && getParameter('reset') !== undefined) {
334
+ this.log.debug(`Reset plugin ${getParameter('reset')}`);
335
+ await this.executeCommandLine(getParameter('reset'), 'reset');
336
+ await this.stopStorage();
337
+ this.emit('shutdown');
338
+ process.exit(0);
339
+ }
301
340
  // Initialize frontend
302
341
  await this.initializeFrontend(getIntParameter('frontend'));
303
342
  if (hasParameter('test')) {
@@ -308,9 +347,7 @@ export class Matterbridge extends EventEmitter {
308
347
  }
309
348
  if (hasParameter('controller')) {
310
349
  this.bridgeMode = 'controller';
311
- this.log.info('Creating mattercontrollerContext: mattercontrollerContext');
312
- this.mattercontrollerContext = this.storageManager?.createContext('mattercontrollerContext');
313
- await this.startMatterbridge();
350
+ await this.startMattercontroller();
314
351
  return;
315
352
  }
316
353
  if (hasParameter('bridge')) {
@@ -461,6 +498,28 @@ export class Matterbridge extends EventEmitter {
461
498
  this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
462
499
  }
463
500
  }
501
+ else if (mode === 'reset') {
502
+ const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
503
+ if (plugin) {
504
+ plugin.loaded = undefined;
505
+ plugin.started = undefined;
506
+ plugin.configured = undefined;
507
+ plugin.connected = undefined;
508
+ plugin.paired = undefined;
509
+ plugin.connected = undefined;
510
+ if (!this.storageManager)
511
+ this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
512
+ const context = this.storageManager?.createContext(plugin.name);
513
+ if (!context)
514
+ this.log.error(`Plugin ${plg}${plugin.name}${er} context not found`);
515
+ await context?.clearAll();
516
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
517
+ this.log.info(`Reset commissionig for plugin ${plg}${plugin.name}${nf} done! Remove the device from the controller.`);
518
+ }
519
+ else {
520
+ this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
521
+ }
522
+ }
464
523
  }
465
524
  catch (err) {
466
525
  this.log.error(`Failed to load plugin from ${plg}${packageJsonPath}${er}: ${err}`);
@@ -478,6 +537,13 @@ export class Matterbridge extends EventEmitter {
478
537
  await this.cleanup('SIGTERM received, cleaning up...');
479
538
  });
480
539
  }
540
+ /**
541
+ * Update matterbridge.
542
+ */
543
+ async updateProcess() {
544
+ await this.cleanup('updating...', false);
545
+ this.hasCleanupStarted = false;
546
+ }
481
547
  /**
482
548
  * Restarts the process by spawning a new process and exiting the current process.
483
549
  */
@@ -485,6 +551,13 @@ export class Matterbridge extends EventEmitter {
485
551
  await this.cleanup('restarting...', true);
486
552
  this.hasCleanupStarted = false;
487
553
  }
554
+ /**
555
+ * Shut down the process by exiting the current process.
556
+ */
557
+ async shutdownProcess() {
558
+ await this.cleanup('shutting down...', false);
559
+ this.hasCleanupStarted = false;
560
+ }
488
561
  /**
489
562
  * Cleans up the Matterbridge instance.
490
563
  * @param message - The cleanup message.
@@ -507,7 +580,7 @@ export class Matterbridge extends EventEmitter {
507
580
  await this.savePluginConfig(plugin);
508
581
  }
509
582
  else {
510
- this.log.warn(`Plugin ${plg}${plugin.name}${er} platform not found`);
583
+ this.log.warn(`Plugin ${plg}${plugin.name}${wr} platform not found`);
511
584
  }
512
585
  }
513
586
  // Set reachability to false
@@ -519,12 +592,34 @@ export class Matterbridge extends EventEmitter {
519
592
  this.log.error(`Plugin ${plg}${registeredDevice.plugin}${er} not found`);
520
593
  return;
521
594
  }
522
- this.log.debug(`*-- device: ${dev}${registeredDevice.device.name}${db} plugin ${plg}${registeredDevice.plugin}${db} type ${GREEN}${plugin.type}${db}`);
523
- if (this.bridgeMode === 'bridge') registeredDevice.device.setBridgedDeviceReachability(false);
524
- if (this.bridgeMode === 'childbridge') plugin.commissioningServer?.setReachability(false);
525
- if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform') this.setReachableAttribute(registeredDevice.device, false);
526
- if (this.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') registeredDevice.device.setBridgedDeviceReachability(false);
595
+ if (this.bridgeMode === 'bridge' && registeredDevice.device.number) {
596
+ this.log.debug(`*-- device: ${dev}${registeredDevice.device.name}${db} plugin ${plg}${registeredDevice.plugin}${db} type ${plugin.type}${db}`);
597
+ registeredDevice.device.setBridgedDeviceReachability(false);
598
+ registeredDevice.device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
599
+ }
600
+ if (this.bridgeMode === 'childbridge') {
601
+ if (plugin.type === 'DynamicPlatform' && registeredDevice.device.number) {
602
+ this.log.debug(`*-- device: ${dev}${registeredDevice.device.name}${db} plugin ${plg}${registeredDevice.plugin}${db} type ${plugin.type}${db}`);
603
+ registeredDevice.device.setBridgedDeviceReachability(false);
604
+ registeredDevice.device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
605
+ }
606
+ }
527
607
  });
608
+ if (this.bridgeMode === 'bridge') {
609
+ this.log.debug('*Changing reachability to false for Matterbridge');
610
+ this.matterAggregator?.getClusterServerById(BasicInformation.Cluster.id)?.setReachableAttribute(false);
611
+ this.matterAggregator?.getClusterServerById(BasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
612
+ this.commissioningServer?.setReachability(false);
613
+ }
614
+ if (this.bridgeMode === 'childbridge') {
615
+ for (const plugin of this.registeredPlugins) {
616
+ if (!plugin.enabled || plugin.error) continue;
617
+ this.log.debug(`*Changing reachability to false for plugin ${plg}${plugin.name}${db} type ${plugin.type}`);
618
+ plugin.aggregator?.getClusterServerById(BasicInformation.Cluster.id)?.setReachableAttribute(false);
619
+ plugin.aggregator?.getClusterServerById(BasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
620
+ plugin.commissioningServer?.setReachability(false);
621
+ }
622
+ }
528
623
  */
529
624
  // Close the express server
530
625
  if (this.expressServer) {
@@ -536,7 +631,7 @@ export class Matterbridge extends EventEmitter {
536
631
  this.expressApp.removeAllListeners();
537
632
  this.expressApp = undefined;
538
633
  }
539
- const cleanupTimeout1 = setTimeout(async () => {
634
+ /*const cleanupTimeout1 =*/ setTimeout(async () => {
540
635
  // Closing matter
541
636
  await this.stopMatter();
542
637
  // Closing storage
@@ -564,21 +659,28 @@ export class Matterbridge extends EventEmitter {
564
659
  this.registeredPlugins = [];
565
660
  this.registeredDevices = [];
566
661
  this.log.info('Waiting for matter to deliver last messages...');
567
- const cleanupTimeout2 = setTimeout(async () => {
662
+ /*const cleanupTimeout2 =*/ setTimeout(async () => {
568
663
  if (restart) {
569
- this.log.info('Cleanup completed. Restarting...');
570
- Matterbridge.instance = undefined;
571
- this.emit('restart');
664
+ if (message === 'updating...') {
665
+ this.log.info('Cleanup completed. Updating...');
666
+ Matterbridge.instance = undefined;
667
+ this.emit('update');
668
+ }
669
+ else if (message === 'restarting...') {
670
+ this.log.info('Cleanup completed. Restarting...');
671
+ Matterbridge.instance = undefined;
672
+ this.emit('restart');
673
+ }
572
674
  }
573
675
  else {
574
676
  this.log.info('Cleanup completed. Shutting down...');
575
677
  Matterbridge.instance = undefined;
576
678
  this.emit('shutdown');
577
679
  }
578
- }, 2 * 1000);
579
- cleanupTimeout2.unref();
680
+ }, 1 * 1000);
681
+ //cleanupTimeout2.unref();
580
682
  }, 3 * 1000);
581
- cleanupTimeout1.unref();
683
+ //cleanupTimeout1.unref();
582
684
  }
583
685
  }
584
686
  /**
@@ -693,8 +795,10 @@ export class Matterbridge extends EventEmitter {
693
795
  this.log.warn(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) plugin ${plg}${pluginName}${wr} not connected`);
694
796
  return;
695
797
  }
696
- // Register and add the device to matterbridge aggregator in bridge mode
798
+ // Remove the device from matterbridge aggregator in bridge mode
697
799
  if (this.bridgeMode === 'bridge') {
800
+ device.setBridgedDeviceReachability(false);
801
+ device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
698
802
  this.matterAggregator.removeBridgedDevice(device);
699
803
  this.registeredDevices.forEach((registeredDevice, index) => {
700
804
  if (registeredDevice.device === device) {
@@ -711,7 +815,7 @@ export class Matterbridge extends EventEmitter {
711
815
  // Only register the device in childbridge mode
712
816
  if (this.bridgeMode === 'childbridge') {
713
817
  if (plugin.type === 'AccessoryPlatform') {
714
- this.log.warn(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) for plugin ${plg}${pluginName}${wr} error: AccessoryPlatform not supported in childbridge mode`);
818
+ this.log.warn(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) for plugin ${plg}${pluginName}${wr}: AccessoryPlatform not supported in childbridge mode`);
715
819
  }
716
820
  else if (plugin.type === 'DynamicPlatform') {
717
821
  this.registeredDevices.forEach((registeredDevice, index) => {
@@ -720,6 +824,8 @@ export class Matterbridge extends EventEmitter {
720
824
  return;
721
825
  }
722
826
  });
827
+ device.setBridgedDeviceReachability(false);
828
+ device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
723
829
  plugin.aggregator.removeBridgedDevice(device);
724
830
  }
725
831
  this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
@@ -1134,20 +1240,23 @@ export class Matterbridge extends EventEmitter {
1134
1240
  }
1135
1241
  }
1136
1242
  /**
1137
- * Starts the Matterbridge based on the bridge mode.
1138
- * If the bridge mode is 'bridge', it creates a commissioning server, matter aggregator,
1139
- * and starts the matter server.
1140
- * If the bridge mode is 'childbridge', it starts the plugins, creates commissioning servers,
1141
- * and starts the matter server when all plugins are loaded and started.
1243
+ * Starts the Matterbridge controller.
1142
1244
  * @private
1143
1245
  * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1144
1246
  */
1145
- async startMatterbridge() {
1247
+ async startMattercontroller() {
1146
1248
  if (!this.storageManager) {
1147
1249
  this.log.error('No storage manager initialized');
1148
1250
  await this.cleanup('No storage manager initialized');
1149
1251
  return;
1150
1252
  }
1253
+ this.log.info('Creating context: mattercontrollerContext');
1254
+ this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
1255
+ if (!this.mattercontrollerContext) {
1256
+ this.log.error('No storage context mattercontrollerContext initialized');
1257
+ await this.cleanup('No storage context mattercontrollerContext initialized');
1258
+ return;
1259
+ }
1151
1260
  this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1152
1261
  this.createMatterServer(this.storageManager);
1153
1262
  if (!this.matterServer) {
@@ -1155,139 +1264,167 @@ export class Matterbridge extends EventEmitter {
1155
1264
  await this.cleanup('No matter server initialized');
1156
1265
  return;
1157
1266
  }
1158
- if (this.bridgeMode === 'controller') {
1159
- if (!this.mattercontrollerContext) {
1160
- this.log.error('No matter controller context initialized');
1161
- await this.cleanup('No matter controller context initialized');
1162
- return;
1267
+ this.log.info('Creating matter commissioning controller');
1268
+ this.commissioningController = new CommissioningController({
1269
+ autoConnect: false,
1270
+ });
1271
+ this.log.info('Adding matter commissioning controller to matter server');
1272
+ await this.matterServer.addCommissioningController(this.commissioningController);
1273
+ this.log.info('Starting matter server');
1274
+ await this.matterServer.start();
1275
+ this.log.info('Matter server started');
1276
+ if (hasParameter('pairingcode')) {
1277
+ this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
1278
+ const pairingCode = getParameter('pairingcode');
1279
+ const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get('ip') : undefined;
1280
+ const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get('port') : undefined;
1281
+ let longDiscriminator, setupPin, shortDiscriminator;
1282
+ if (pairingCode !== undefined) {
1283
+ const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
1284
+ shortDiscriminator = pairingCodeCodec.shortDiscriminator;
1285
+ longDiscriminator = undefined;
1286
+ setupPin = pairingCodeCodec.passcode;
1287
+ this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
1163
1288
  }
1164
- this.log.info('Creating matter commissioning controller');
1165
- this.commissioningController = new CommissioningController({
1166
- autoConnect: false,
1167
- });
1168
- this.log.info('Adding matter commissioning controller to matter server');
1169
- await this.matterServer.addCommissioningController(this.commissioningController);
1170
- this.log.info('Starting matter server');
1171
- await this.matterServer.start();
1172
- this.log.info('Matter server started');
1173
- if (hasParameter('pairingcode')) {
1174
- const pairingCode = getParameter('pairingcode');
1175
- const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get('ip') : undefined;
1176
- const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get('port') : undefined;
1177
- let longDiscriminator, setupPin, shortDiscriminator;
1178
- if (pairingCode !== undefined) {
1179
- const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
1180
- shortDiscriminator = pairingCodeCodec.shortDiscriminator;
1181
- longDiscriminator = undefined;
1182
- setupPin = pairingCodeCodec.passcode;
1183
- this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
1184
- }
1185
- else {
1186
- longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
1187
- if (longDiscriminator > 4095)
1188
- throw new Error('Discriminator value must be less than 4096');
1189
- setupPin = this.mattercontrollerContext.get('pin', 20202021);
1190
- }
1191
- if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
1192
- throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
1193
- }
1194
- const commissioningOptions = {
1195
- regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
1196
- regulatoryCountryCode: 'XX',
1197
- };
1198
- const options = {
1199
- commissioning: commissioningOptions,
1200
- discovery: {
1201
- knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
1202
- identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
1203
- },
1204
- passcode: setupPin,
1205
- };
1206
- //this.log.info(`Commissioning ... ${JSON.stringify(options)}`);
1207
- this.log.info('Commissioning ...', options);
1208
- const nodeId = await this.commissioningController.commissionNode(options);
1209
- this.mattercontrollerContext.set('nodeId', nodeId.nodeId);
1210
- this.log.info(`Commissioning successfully done with nodeId: ${nodeId.nodeId}`);
1211
- // eslint-disable-next-line no-console
1212
- this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
1213
- } // (hasParameter('pairingcode'))
1214
- if (hasParameter('discover')) {
1215
- //const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1216
- //console.log(discover);
1289
+ else {
1290
+ longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
1291
+ if (longDiscriminator > 4095)
1292
+ throw new Error('Discriminator value must be less than 4096');
1293
+ setupPin = this.mattercontrollerContext.get('pin', 20202021);
1294
+ }
1295
+ if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
1296
+ throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
1217
1297
  }
1298
+ const commissioningOptions = {
1299
+ regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
1300
+ regulatoryCountryCode: 'XX',
1301
+ };
1302
+ const options = {
1303
+ commissioning: commissioningOptions,
1304
+ discovery: {
1305
+ knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
1306
+ identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
1307
+ },
1308
+ passcode: setupPin,
1309
+ };
1310
+ this.log.info('Commissioning with options:', options);
1311
+ const nodeId = await this.commissioningController.commissionNode(options);
1312
+ this.log.info(`Commissioning successfully done with nodeId: ${nodeId.nodeId}`);
1313
+ this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
1314
+ } // (hasParameter('pairingcode'))
1315
+ if (hasParameter('unpairall')) {
1316
+ this.log.info('***Commissioning controller unpairing all nodes...');
1218
1317
  const nodeIds = this.commissioningController.getCommissionedNodes();
1219
- this.log.info(`***Commissioning controller has ${nodeIds.length} nodes commisioned: ${this.commissioningController.isCommissioned()}`);
1220
1318
  for (const nodeId of nodeIds) {
1221
- this.log.info(`***Connecting to commissioned node: ${nodeId}`);
1222
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1223
- const node = await this.commissioningController.connectNode(nodeId, {
1224
- attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) => this.log.info(`***mattributeChangedCallback ${peerNodeId}: Attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
1225
- eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) => this.log.info(`***eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
1226
- stateInformationCallback: (peerNodeId, info) => {
1227
- switch (info) {
1228
- case NodeStateInformation.Connected:
1229
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
1230
- break;
1231
- case NodeStateInformation.Disconnected:
1232
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
1233
- break;
1234
- case NodeStateInformation.Reconnecting:
1235
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
1236
- break;
1237
- case NodeStateInformation.WaitingForDeviceDiscovery:
1238
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
1239
- break;
1240
- case NodeStateInformation.StructureChanged:
1241
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
1242
- break;
1243
- case NodeStateInformation.Decommissioned:
1244
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
1245
- break;
1246
- default:
1247
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
1248
- break;
1249
- }
1250
- },
1251
- });
1252
- node.logStructure();
1253
- // Get the interaction client
1254
- const interactionClient = await node.getInteractionClient();
1255
- let cluster;
1256
- let attributes;
1257
- // Log BasicInformationCluster
1258
- cluster = BasicInformationCluster;
1259
- attributes = await interactionClient.getMultipleAttributes({
1260
- attributes: [{ clusterId: cluster.id }],
1261
- });
1262
- this.log.warn(`Cluster: ${cluster.name} attributes:`);
1263
- attributes.forEach((attribute) => {
1264
- this.log.info(
1265
- // eslint-disable-next-line max-len
1266
- `- endpoint: ${attribute.path.endpointId} clusterId: ${attribute.path.clusterId} id: ${attribute.path.attributeId} name: ${attribute.path.attributeName}: ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
1267
- });
1268
- // Log PowerSourceCluster
1269
- cluster = PowerSourceCluster;
1270
- attributes = await interactionClient.getMultipleAttributes({
1271
- attributes: [{ clusterId: cluster.id }],
1272
- });
1273
- this.log.warn(`Cluster: ${cluster.name} attributes:`);
1274
- attributes.forEach((attribute) => {
1275
- this.log.info(
1276
- // eslint-disable-next-line max-len
1277
- `- endpoint: ${attribute.path.endpointId} clusterId: ${attribute.path.clusterId} id: ${attribute.path.attributeId} name: ${attribute.path.attributeName}: ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
1278
- });
1279
- // Log ThreadNetworkDiagnostics
1280
- cluster = ThreadNetworkDiagnosticsCluster;
1281
- attributes = await interactionClient.getMultipleAttributes({
1282
- attributes: [{ clusterId: cluster.id }],
1283
- });
1284
- this.log.warn(`Cluster: ${cluster.name} attributes:`);
1285
- attributes.forEach((attribute) => {
1286
- this.log.info(
1287
- // eslint-disable-next-line max-len
1288
- `- endpoint: ${attribute.path.endpointId} clusterId: ${attribute.path.clusterId} id: ${attribute.path.attributeId} name: ${attribute.path.attributeName}: ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
1289
- });
1319
+ this.log.info('***Commissioning controller unpairing node:', nodeId);
1320
+ await this.commissioningController.removeNode(nodeId);
1290
1321
  }
1322
+ return;
1323
+ }
1324
+ if (hasParameter('discover')) {
1325
+ //const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1326
+ //console.log(discover);
1327
+ }
1328
+ if (!this.commissioningController.isCommissioned()) {
1329
+ this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
1330
+ return;
1331
+ }
1332
+ const nodeIds = this.commissioningController.getCommissionedNodes();
1333
+ this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
1334
+ for (const nodeId of nodeIds) {
1335
+ this.log.info(`***Connecting to commissioned node: ${nodeId}`);
1336
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1337
+ const node = await this.commissioningController.connectNode(nodeId, {
1338
+ 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)}`),
1339
+ 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)}`),
1340
+ stateInformationCallback: (peerNodeId, info) => {
1341
+ switch (info) {
1342
+ case NodeStateInformation.Connected:
1343
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
1344
+ break;
1345
+ case NodeStateInformation.Disconnected:
1346
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
1347
+ break;
1348
+ case NodeStateInformation.Reconnecting:
1349
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
1350
+ break;
1351
+ case NodeStateInformation.WaitingForDeviceDiscovery:
1352
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
1353
+ break;
1354
+ case NodeStateInformation.StructureChanged:
1355
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
1356
+ break;
1357
+ case NodeStateInformation.Decommissioned:
1358
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
1359
+ break;
1360
+ default:
1361
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
1362
+ break;
1363
+ }
1364
+ },
1365
+ });
1366
+ //node.logStructure();
1367
+ // Get the interaction client
1368
+ this.log.info('Getting the interaction client');
1369
+ const interactionClient = await node.getInteractionClient();
1370
+ let cluster;
1371
+ let attributes;
1372
+ // Log BasicInformationCluster
1373
+ cluster = BasicInformationCluster;
1374
+ attributes = await interactionClient.getMultipleAttributes({
1375
+ attributes: [{ clusterId: cluster.id }],
1376
+ });
1377
+ this.log.warn(`Cluster: ${cluster.name} attributes:`);
1378
+ attributes.forEach((attribute) => {
1379
+ this.log.info(
1380
+ // eslint-disable-next-line max-len
1381
+ `- 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}`);
1382
+ });
1383
+ // Log PowerSourceCluster
1384
+ cluster = PowerSourceCluster;
1385
+ attributes = await interactionClient.getMultipleAttributes({
1386
+ attributes: [{ clusterId: cluster.id }],
1387
+ });
1388
+ this.log.warn(`Cluster: ${cluster.name} attributes:`);
1389
+ attributes.forEach((attribute) => {
1390
+ this.log.info(
1391
+ // eslint-disable-next-line max-len
1392
+ `- 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}`);
1393
+ });
1394
+ // Log ThreadNetworkDiagnostics
1395
+ cluster = ThreadNetworkDiagnosticsCluster;
1396
+ attributes = await interactionClient.getMultipleAttributes({
1397
+ attributes: [{ clusterId: cluster.id }],
1398
+ });
1399
+ this.log.warn(`Cluster: ${cluster.name} attributes:`);
1400
+ attributes.forEach((attribute) => {
1401
+ this.log.info(
1402
+ // eslint-disable-next-line max-len
1403
+ `- 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}`);
1404
+ });
1405
+ }
1406
+ }
1407
+ /**
1408
+ * Starts the Matterbridge based on the bridge mode.
1409
+ * If the bridge mode is 'bridge', it creates a commissioning server, matter aggregator,
1410
+ * and starts the matter server.
1411
+ * If the bridge mode is 'childbridge', it starts the plugins, creates commissioning servers,
1412
+ * and starts the matter server when all plugins are loaded and started.
1413
+ * @private
1414
+ * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1415
+ */
1416
+ async startMatterbridge() {
1417
+ if (!this.storageManager) {
1418
+ this.log.error('No storage manager initialized');
1419
+ await this.cleanup('No storage manager initialized');
1420
+ return;
1421
+ }
1422
+ this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1423
+ this.createMatterServer(this.storageManager);
1424
+ if (!this.matterServer) {
1425
+ this.log.error('No matter server initialized');
1426
+ await this.cleanup('No matter server initialized');
1427
+ return;
1291
1428
  }
1292
1429
  if (this.bridgeMode === 'bridge') {
1293
1430
  // Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
@@ -1338,6 +1475,7 @@ export class Matterbridge extends EventEmitter {
1338
1475
  await this.startMatterServer();
1339
1476
  this.log.info('Matter server started');
1340
1477
  await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
1478
+ //if (hasParameter('advertise')) await this.commissioningServer.advertise();
1341
1479
  /*
1342
1480
  setInterval(() => {
1343
1481
  this.matterAggregator?.getBridgedDevices().forEach((device) => {
@@ -1421,6 +1559,7 @@ export class Matterbridge extends EventEmitter {
1421
1559
  });
1422
1560
  if (!allStarted)
1423
1561
  return;
1562
+ clearInterval(startMatterInterval);
1424
1563
  this.log.info('Starting matter server...');
1425
1564
  // Setting reachability to true
1426
1565
  this.registeredPlugins.forEach((plugin) => {
@@ -1457,7 +1596,7 @@ export class Matterbridge extends EventEmitter {
1457
1596
  await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
1458
1597
  }
1459
1598
  Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
1460
- clearInterval(startMatterInterval);
1599
+ //clearInterval(startMatterInterval);
1461
1600
  }, 1000);
1462
1601
  }
1463
1602
  }
@@ -1531,9 +1670,9 @@ export class Matterbridge extends EventEmitter {
1531
1670
  await storageContext.set('softwareVersionString', softwareVersionString ?? '1.0.0');
1532
1671
  await storageContext.set('hardwareVersion', hardwareVersion ?? 1);
1533
1672
  await storageContext.set('hardwareVersionString', hardwareVersionString ?? '1.0.0');
1534
- this.log.debug(`**Created commissioning server storage context for ${plg}${pluginName}${db}`);
1535
- this.log.debug(`**- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1536
- this.log.debug(`**- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1673
+ this.log.debug(`Created commissioning server storage context for ${plg}${pluginName}${db}`);
1674
+ this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1675
+ this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1537
1676
  return storageContext;
1538
1677
  }
1539
1678
  /**
@@ -1548,7 +1687,7 @@ export class Matterbridge extends EventEmitter {
1548
1687
  if (!commissioningServer || !storageContext || !pluginName)
1549
1688
  return;
1550
1689
  if (!commissioningServer.isCommissioned()) {
1551
- this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code ...`);
1690
+ this.log.info(`***The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code ...`);
1552
1691
  const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
1553
1692
  storageContext.set('qrPairingCode', qrPairingCode);
1554
1693
  storageContext.set('manualPairingCode', manualPairingCode);
@@ -1567,7 +1706,7 @@ export class Matterbridge extends EventEmitter {
1567
1706
  await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
1568
1707
  }
1569
1708
  else {
1570
- this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is already commissioned. Waiting for controllers to connect ...`);
1709
+ this.log.info(`***The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is already commissioned . Waiting for controllers to connect ...`);
1571
1710
  if (pluginName !== 'Matterbridge') {
1572
1711
  const plugin = this.findPlugin(pluginName);
1573
1712
  if (plugin) {
@@ -1617,7 +1756,7 @@ export class Matterbridge extends EventEmitter {
1617
1756
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
1618
1757
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with hardwareVersion ${hardwareVersion} hardwareVersionString ${hardwareVersionString}`);
1619
1758
  const commissioningServer = new CommissioningServer({
1620
- port: undefined,
1759
+ port: this.port++,
1621
1760
  passcode: undefined,
1622
1761
  discriminator: undefined,
1623
1762
  deviceName,
@@ -1704,9 +1843,7 @@ export class Matterbridge extends EventEmitter {
1704
1843
  this.log.warn(`***Commissioning removed from fabric ${fabricIndex} for ${plg}${pluginName}${nf}. Resetting the commissioning server ...`);
1705
1844
  await commissioningServer.factoryReset();
1706
1845
  if (pluginName === 'Matterbridge') {
1707
- this.matterbridgeContext?.delete(`${pluginName}.EndpointStructure`);
1708
- this.matterbridgeContext?.delete(`${pluginName}.EventHandler`);
1709
- this.matterbridgeContext?.delete(`${pluginName}.SessionManager`);
1846
+ await this.matterbridgeContext?.clearAll();
1710
1847
  }
1711
1848
  else {
1712
1849
  for (const plugin of this.registeredPlugins) {
@@ -1714,9 +1851,7 @@ export class Matterbridge extends EventEmitter {
1714
1851
  await plugin.platform?.onShutdown('Commissioning removed by the controller');
1715
1852
  plugin.paired = false;
1716
1853
  plugin.connected = false;
1717
- plugin.storageContext?.delete(`${pluginName}.EndpointStructure`);
1718
- plugin.storageContext?.delete(`${pluginName}.EventHandler`);
1719
- plugin.storageContext?.delete(`${pluginName}.SessionManager`);
1854
+ await plugin.storageContext?.clearAll();
1720
1855
  }
1721
1856
  }
1722
1857
  }
@@ -2038,6 +2173,26 @@ export class Matterbridge extends EventEmitter {
2038
2173
  this.expressApp = express();
2039
2174
  // Serve React build directory
2040
2175
  this.expressApp.use(express.static(path.join(this.rootDirectory, 'frontend/build')));
2176
+ // Endpoint to provide login code
2177
+ this.expressApp.post('/api/login', express.json(), async (req, res) => {
2178
+ const { password } = req.body;
2179
+ this.log.debug('The frontend sent /api/login', password);
2180
+ if (!this.nodeContext) {
2181
+ this.log.error('/api/login nodeContext not found');
2182
+ res.json({ valid: false });
2183
+ return;
2184
+ }
2185
+ try {
2186
+ const storedPassword = await this.nodeContext.get('password', '');
2187
+ if (storedPassword === '' || password === storedPassword)
2188
+ res.json({ valid: true });
2189
+ else
2190
+ res.json({ valid: false });
2191
+ }
2192
+ catch (error) {
2193
+ res.json({ valid: false });
2194
+ }
2195
+ });
2041
2196
  // Endpoint to provide QR pairing code
2042
2197
  this.expressApp.get('/api/qr-code', (req, res) => {
2043
2198
  this.log.debug('The frontend sent /api/qr-code');
@@ -2152,7 +2307,13 @@ export class Matterbridge extends EventEmitter {
2152
2307
  res.status(400).json({ error: 'No command provided' });
2153
2308
  return;
2154
2309
  }
2155
- this.log.info(`***Received command: ${command}:${param}`);
2310
+ this.log.debug(`*Received frontend command: ${command}:${param}`);
2311
+ // Handle the command setpassword from Settings
2312
+ if (command === 'setpassword') {
2313
+ const password = param.slice(1, -1); // Remove the first and last characters
2314
+ this.log.info('setpassword', param, password);
2315
+ await this.nodeContext?.set('password', password);
2316
+ }
2156
2317
  // Handle the command debugLevel from Settings
2157
2318
  if (command === 'setloglevel') {
2158
2319
  if (param === 'Debug') {
@@ -2174,15 +2335,47 @@ export class Matterbridge extends EventEmitter {
2174
2335
  plugin.platform?.log.setLogDebug(this.debugEnabled);
2175
2336
  });
2176
2337
  }
2177
- // Handle the command debugLevel from Header
2338
+ // Handle the command shutdown from Header
2339
+ if (command === 'shutdown') {
2340
+ this.shutdownProcess();
2341
+ }
2342
+ // Handle the command restart from Header
2178
2343
  if (command === 'restart') {
2179
2344
  this.restartProcess();
2180
2345
  }
2181
2346
  // Handle the command update from Header
2347
+ if (command === 'update') {
2348
+ this.log.warn(`***Updating matterbridge ${plg}${param}${db}`);
2349
+ try {
2350
+ await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
2351
+ this.log.info('Matterbridge has been updated. Full restart required.');
2352
+ }
2353
+ catch (error) {
2354
+ this.log.error('Error updating matterbridge');
2355
+ res.json({ message: 'Command received' });
2356
+ return;
2357
+ }
2358
+ this.updateProcess();
2359
+ }
2360
+ // Handle the command update from Header
2182
2361
  if (command === 'update') {
2183
2362
  this.log.warn(`The /api/command/${command} is not yet implemented`);
2184
2363
  }
2185
- // Handle the command addplugin from Home C:\Users\lligu\GitHub\matterbridge-example-accessory-platform
2364
+ // Handle the command installplugin from Home
2365
+ if (command === 'installplugin') {
2366
+ param = param.replace(/\*/g, '\\');
2367
+ this.log.warn(`***Installing plugin ${plg}${param}${db}`);
2368
+ try {
2369
+ await this.spawnCommand('npm', ['install', '-g', param]);
2370
+ this.log.info(`Plugin ${plg}${param}${nf} installed. Restart required.`);
2371
+ }
2372
+ catch (error) {
2373
+ this.log.error(`Error installing plugin ${plg}${param}${er}`);
2374
+ res.json({ message: 'Command received' });
2375
+ return;
2376
+ }
2377
+ }
2378
+ // Handle the command addplugin from Home
2186
2379
  if (command === 'addplugin') {
2187
2380
  param = param.replace(/\*/g, '\\');
2188
2381
  if (this.registeredPlugins.find((plugin) => plugin.name === param)) {
@@ -2297,7 +2490,7 @@ export class Matterbridge extends EventEmitter {
2297
2490
  });
2298
2491
  // Fallback for routing
2299
2492
  this.expressApp.get('*', (req, res) => {
2300
- this.log.warn('The frontend sent *', req.url);
2493
+ this.log.debug('The frontend sent *', req.url);
2301
2494
  res.sendFile(path.join(this.rootDirectory, 'frontend/build/index.html'));
2302
2495
  });
2303
2496
  this.expressServer = this.expressApp.listen(port, () => {