matterbridge 1.1.10 → 1.1.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.
Files changed (33) hide show
  1. package/CHANGELOG.md +19 -2
  2. package/README.md +58 -13
  3. package/dist/matterbridge.d.ts +13 -2
  4. package/dist/matterbridge.d.ts.map +1 -1
  5. package/dist/matterbridge.js +182 -86
  6. package/dist/matterbridge.js.map +1 -1
  7. package/dist/matterbridgeAccessoryPlatform.d.ts +2 -5
  8. package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -1
  9. package/dist/matterbridgeAccessoryPlatform.js +4 -7
  10. package/dist/matterbridgeAccessoryPlatform.js.map +1 -1
  11. package/dist/matterbridgeDevice.d.ts +8 -3
  12. package/dist/matterbridgeDevice.d.ts.map +1 -1
  13. package/dist/matterbridgeDevice.js +80 -20
  14. package/dist/matterbridgeDevice.js.map +1 -1
  15. package/dist/matterbridgeDynamicPlatform.d.ts +2 -5
  16. package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -1
  17. package/dist/matterbridgeDynamicPlatform.js +4 -7
  18. package/dist/matterbridgeDynamicPlatform.js.map +1 -1
  19. package/dist/matterbridgePlatform.d.ts +44 -0
  20. package/dist/matterbridgePlatform.d.ts.map +1 -0
  21. package/dist/matterbridgePlatform.js +93 -0
  22. package/dist/matterbridgePlatform.js.map +1 -0
  23. package/frontend/build/asset-manifest.json +6 -6
  24. package/frontend/build/index.html +1 -1
  25. package/frontend/build/static/css/{main.3804969f.css → main.70102d98.css} +2 -2
  26. package/frontend/build/static/css/main.70102d98.css.map +1 -0
  27. package/frontend/build/static/js/main.5d39b100.js +3 -0
  28. package/frontend/build/static/js/main.5d39b100.js.map +1 -0
  29. package/package.json +15 -13
  30. package/frontend/build/static/css/main.3804969f.css.map +0 -1
  31. package/frontend/build/static/js/main.82822a11.js +0 -3
  32. package/frontend/build/static/js/main.82822a11.js.map +0 -1
  33. /package/frontend/build/static/js/{main.82822a11.js.LICENSE.txt → main.5d39b100.js.LICENSE.txt} +0 -0
@@ -30,7 +30,7 @@ import EventEmitter from 'events';
30
30
  import express from 'express';
31
31
  import os from 'os';
32
32
  import path from 'path';
33
- import { CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
33
+ import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
34
34
  import { BasicInformationCluster, BridgedDeviceBasicInformationCluster, ClusterServer } from '@project-chip/matter-node.js/cluster';
35
35
  import { DeviceTypeId, VendorId } from '@project-chip/matter-node.js/datatype';
36
36
  import { Aggregator, DeviceTypes } from '@project-chip/matter-node.js/device';
@@ -59,13 +59,24 @@ export class Matterbridge extends EventEmitter {
59
59
  freeMemory: '',
60
60
  systemUptime: '',
61
61
  };
62
+ matterbridgeInformation = {
63
+ homeDirectory: '',
64
+ rootDirectory: '',
65
+ matterbridgeDirectory: '',
66
+ matterbridgePluginDirectory: '',
67
+ globalModulesDirectory: '',
68
+ matterbridgeVersion: '',
69
+ matterbridgeLatestVersion: '',
70
+ bridgeMode: '',
71
+ debugEnabled: false,
72
+ };
62
73
  homeDirectory = '';
63
74
  rootDirectory = '';
64
75
  matterbridgeDirectory = '';
65
76
  matterbridgePluginDirectory = '';
77
+ globalModulesDirectory = '';
66
78
  matterbridgeVersion = '';
67
79
  matterbridgeLatestVersion = '';
68
- globalModulesDir = '';
69
80
  bridgeMode = '';
70
81
  debugEnabled = false;
71
82
  log;
@@ -272,6 +283,12 @@ export class Matterbridge extends EventEmitter {
272
283
  MatterbridgeDevice.bridgeMode = 'childbridge';
273
284
  await this.testStartMatterBridge(); // No await do it asyncronously
274
285
  }
286
+ if (hasParameter('controller')) {
287
+ this.bridgeMode = 'controller';
288
+ this.log.info('Creating mattercontrollerContext: mattercontrollerContext');
289
+ this.mattercontrollerContext = this.storageManager?.createContext('mattercontrollerContext');
290
+ await this.startMatterBridge();
291
+ }
275
292
  if (hasParameter('bridge')) {
276
293
  this.bridgeMode = 'bridge';
277
294
  MatterbridgeDevice.bridgeMode = 'bridge';
@@ -326,8 +343,8 @@ export class Matterbridge extends EventEmitter {
326
343
  }
327
344
  if (!packageJsonExists) {
328
345
  this.log.debug(`Package.json not found at ${packageJsonPath}`);
329
- this.log.debug(`Trying at ${this.globalModulesDir}`);
330
- packageJsonPath = path.join(this.globalModulesDir, pluginPath);
346
+ this.log.debug(`Trying at ${this.globalModulesDirectory}`);
347
+ packageJsonPath = path.join(this.globalModulesDirectory, pluginPath);
331
348
  //this.log.debug(`Got ${packageJsonPath}`);
332
349
  }
333
350
  try {
@@ -449,10 +466,12 @@ export class Matterbridge extends EventEmitter {
449
466
  this.log.info(message);
450
467
  process.removeAllListeners('SIGINT');
451
468
  process.removeAllListeners('SIGTERM');
452
- // Callint the shutdown functions with a reason
469
+ // Calling the shutdown functions with a reason
453
470
  for (const plugin of this.registeredPlugins) {
454
471
  if (plugin.platform)
455
472
  await plugin.platform.onShutdown('Matterbridge is closing: ' + message);
473
+ if (plugin.platform)
474
+ await plugin.platform.saveConfig();
456
475
  }
457
476
  // Set reachability to false
458
477
  /*
@@ -487,17 +506,20 @@ export class Matterbridge extends EventEmitter {
487
506
  // Closing storage
488
507
  await this.stopStorage();
489
508
  // Serialize registeredDevices
490
- if (this.nodeContext) {
509
+ if (this.nodeStorage && this.nodeContext) {
491
510
  this.log.info('Saving registered devices...');
492
511
  const serializedRegisteredDevices = [];
493
512
  this.registeredDevices.forEach((registeredDevice) => {
494
513
  serializedRegisteredDevices.push(registeredDevice.device.serialize(registeredDevice.plugin));
495
514
  });
496
- //console.log('serializedRegisteredDevices:', serializedRegisteredDevices);
497
515
  await this.nodeContext.set('devices', serializedRegisteredDevices);
498
516
  this.log.info('Saved registered devices');
499
517
  // Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
518
+ this.log.debug('Closing node storage context...');
519
+ this.nodeContext.close();
500
520
  this.nodeContext = undefined;
521
+ this.log.debug('Closing node storage manager...');
522
+ this.nodeStorage.close();
501
523
  this.nodeStorage = undefined;
502
524
  }
503
525
  else {
@@ -505,6 +527,7 @@ export class Matterbridge extends EventEmitter {
505
527
  }
506
528
  this.registeredPlugins = [];
507
529
  this.registeredDevices = [];
530
+ this.log.info('Waiting for matter to deliver last messages...');
508
531
  const cleanupTimeout2 = setTimeout(async () => {
509
532
  if (restart) {
510
533
  this.log.info('Cleanup completed. Restarting...');
@@ -544,14 +567,14 @@ export class Matterbridge extends EventEmitter {
544
567
  */
545
568
  async addDevice(pluginName, device) {
546
569
  if (this.bridgeMode === 'bridge' && !this.matterAggregator) {
547
- this.log.error(`Adding device ${dev}${device.name}-${device.deviceName}${er} for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
570
+ this.log.error(`Adding device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
548
571
  return;
549
572
  }
550
- this.log.debug(`Adding device ${dev}${device.name}-${device.deviceName}${db} for plugin ${plg}${pluginName}${db}`);
573
+ this.log.debug(`Adding device ${dev}${device.deviceName}${db} (${dev}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
551
574
  // Check if the plugin is registered
552
575
  const plugin = this.registeredPlugins.find((plugin) => plugin.name === pluginName);
553
576
  if (!plugin) {
554
- this.log.error(`Error adding device ${dev}${device.name}-${device.deviceName}${er} plugin ${plg}${pluginName}${er} not found`);
577
+ this.log.error(`Error adding device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) plugin ${plg}${pluginName}${er} not found`);
555
578
  return;
556
579
  }
557
580
  // Register and add the device to matterbridge aggregator in bridge mode
@@ -562,14 +585,14 @@ export class Matterbridge extends EventEmitter {
562
585
  plugin.registeredDevices++;
563
586
  if (plugin.addedDevices !== undefined)
564
587
  plugin.addedDevices++;
565
- this.log.info(`Added and registered device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.name}-${device.deviceName}${nf} for plugin ${plg}${pluginName}${nf}`);
588
+ this.log.info(`Added and registered device (${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
566
589
  }
567
590
  // Only register the device in childbridge mode
568
591
  if (this.bridgeMode === 'childbridge') {
569
592
  this.registeredDevices.push({ plugin: pluginName, device, added: false });
570
593
  if (plugin.registeredDevices !== undefined)
571
594
  plugin.registeredDevices++;
572
- this.log.info(`Registered device ${dev}${device.name}-${device.deviceName}${nf} for plugin ${plg}${pluginName}${nf}`);
595
+ this.log.info(`Registered device ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
573
596
  }
574
597
  }
575
598
  /**
@@ -580,14 +603,14 @@ export class Matterbridge extends EventEmitter {
580
603
  */
581
604
  async addBridgedDevice(pluginName, device) {
582
605
  if (this.bridgeMode === 'bridge' && !this.matterAggregator) {
583
- this.log.error(`Adding bridged device ${dev}${device.name}-${device.deviceName}${er} for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
606
+ this.log.error(`Adding bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
584
607
  return;
585
608
  }
586
- this.log.debug(`Adding bridged device ${db}${device.name}-${device.deviceName}${nf} for plugin ${plg}${pluginName}${db}`);
609
+ this.log.debug(`Adding bridged device ${dev}${device.deviceName}${db} (${dev}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
587
610
  // Check if the plugin is registered
588
611
  const plugin = this.registeredPlugins.find((plugin) => plugin.name === pluginName);
589
612
  if (!plugin) {
590
- this.log.error(`Error adding bridged device ${dev}${device.name}-${device.deviceName}${er} plugin ${plg}${pluginName}${er} not found`);
613
+ this.log.error(`Error adding bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) plugin ${plg}${pluginName}${er} not found`);
591
614
  return;
592
615
  }
593
616
  // Register and add the device to matterbridge aggregator in bridge mode
@@ -598,36 +621,38 @@ export class Matterbridge extends EventEmitter {
598
621
  plugin.registeredDevices++;
599
622
  if (plugin.addedDevices !== undefined)
600
623
  plugin.addedDevices++;
601
- this.log.info(`Added and registered bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.name}-${device.deviceName}${nf} for plugin ${plg}${pluginName}${nf}`);
624
+ this.log.info(`Added and registered bridged device (${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
602
625
  }
603
626
  // Only register the device in childbridge mode
604
627
  if (this.bridgeMode === 'childbridge') {
605
628
  this.registeredDevices.push({ plugin: pluginName, device, added: false });
606
629
  if (plugin.registeredDevices !== undefined)
607
630
  plugin.registeredDevices++;
608
- this.log.info(`Registered bridged device ${dev}${device.name}-${device.deviceName}${nf} for plugin ${plg}${pluginName}${nf}`);
631
+ this.log.info(`Registered bridged device ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
609
632
  }
610
633
  }
611
634
  async removeBridgedDevice(pluginName, device) {
612
635
  if (this.bridgeMode === 'bridge' && !this.matterAggregator) {
613
- this.log.error(`Removing bridged device ${dev}${device.name}-${device.deviceName}${er} for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
636
+ this.log.error(`Removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
614
637
  return;
615
638
  }
616
- this.log.debug(`Removing bridged device ${db}${device.name}-${device.deviceName}${nf} for plugin ${plg}${pluginName}${db}`);
639
+ this.log.debug(`Removing bridged device ${dev}${device.deviceName}${db} (${dev}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
617
640
  // Check if the plugin is registered
618
641
  const plugin = this.findPlugin(pluginName);
619
642
  if (!plugin) {
620
- this.log.error(`Error removing bridged device ${dev}${device.name}-${device.deviceName}${er} plugin ${plg}${pluginName}${er} not found`);
643
+ this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) plugin ${plg}${pluginName}${er} not found`);
621
644
  return;
622
645
  }
623
646
  if (this.bridgeMode === 'childbridge' && !plugin.aggregator) {
624
- this.log.error(`Error removing bridged device ${dev}${device.name}-${device.deviceName}${er} plugin ${plg}${pluginName}${er} aggregator not found`);
647
+ this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) plugin ${plg}${pluginName}${er} aggregator not found`);
625
648
  return;
626
649
  }
650
+ /*
627
651
  if (this.bridgeMode === 'childbridge' && !plugin.connected) {
628
- this.log.error(`Error removing bridged device ${dev}${device.name}-${device.deviceName}${er} plugin ${plg}${pluginName}${er} not connected`);
629
- return;
652
+ this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) plugin ${plg}${pluginName}${er} not connected`);
653
+ return;
630
654
  }
655
+ */
631
656
  // Register and add the device to matterbridge aggregator in bridge mode
632
657
  if (this.bridgeMode === 'bridge') {
633
658
  this.matterAggregator.removeBridgedDevice(device);
@@ -641,12 +666,12 @@ export class Matterbridge extends EventEmitter {
641
666
  plugin.registeredDevices--;
642
667
  if (plugin.addedDevices !== undefined)
643
668
  plugin.addedDevices--;
644
- this.log.info(`Rmoved bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.name}-${device.deviceName}${nf} for plugin ${plg}${pluginName}${nf}`);
669
+ this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
645
670
  }
646
671
  // Only register the device in childbridge mode
647
672
  if (this.bridgeMode === 'childbridge') {
648
673
  if (plugin.type === 'AccessoryPlatform') {
649
- this.log.warn(`Removing bridged device ${dev}${device.name}-${device.deviceName}${wr} for plugin ${plg}${pluginName}${wr} error: AccessoryPlatform not supported in childbridge mode`);
674
+ 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`);
650
675
  }
651
676
  else if (plugin.type === 'DynamicPlatform') {
652
677
  this.registeredDevices.forEach((registeredDevice, index) => {
@@ -657,11 +682,11 @@ export class Matterbridge extends EventEmitter {
657
682
  });
658
683
  plugin.aggregator.removeBridgedDevice(device);
659
684
  }
685
+ this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
660
686
  if (plugin.registeredDevices !== undefined)
661
687
  plugin.registeredDevices--;
662
688
  if (plugin.addedDevices !== undefined)
663
689
  plugin.addedDevices--;
664
- this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.name}-${device.deviceName}${nf} for plugin ${plg}${pluginName}${nf}`);
665
690
  }
666
691
  }
667
692
  /**
@@ -897,6 +922,7 @@ export class Matterbridge extends EventEmitter {
897
922
  plugin.registeredDevices = 0;
898
923
  plugin.addedDevices = 0;
899
924
  await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
925
+ await platform.loadConfig();
900
926
  this.log.info(`Loaded plugin ${plg}${plugin.name}${db} type ${typ}${platform.type}${db} (entrypoint ${UNDERLINE}${pluginEntry}${UNDERLINEOFF})`);
901
927
  if (start)
902
928
  this.startPlugin(plugin, message); // No await do it asyncronously
@@ -934,15 +960,41 @@ export class Matterbridge extends EventEmitter {
934
960
  await this.cleanup('No matter server initialized');
935
961
  return;
936
962
  }
963
+ if (this.bridgeMode === 'controller') {
964
+ this.log.info('Creating matter commissioning controller');
965
+ this.commissioningController = new CommissioningController({
966
+ autoConnect: false,
967
+ });
968
+ this.log.info('Adding matter commissioning controller to matter server');
969
+ await this.matterServer.addCommissioningController(this.commissioningController);
970
+ this.log.info('Starting matter server');
971
+ await this.matterServer.start();
972
+ this.log.info('Matter server started');
973
+ if (hasParameter('discover')) {
974
+ //const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
975
+ //console.log(discover);
976
+ }
977
+ this.log.info(`Commissioning controller is already commisioned: ${this.commissioningController.isCommissioned()}`);
978
+ const nodes = this.commissioningController.getCommissionedNodes();
979
+ nodes.forEach(async (nodeId) => {
980
+ this.log.warn(`Connecting to commissioned node: ${nodeId}`);
981
+ });
982
+ }
937
983
  if (this.bridgeMode === 'bridge') {
938
984
  // Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
939
985
  // Plugins are started and configured by callback when Matterbridge is commissioned
940
986
  this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
941
- this.matterbridgeContext = this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
987
+ this.matterbridgeContext = this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Aggregator');
942
988
  if (!this.matterbridgeContext) {
943
- this.log.error(`Error creating storage context${er}`);
989
+ this.log.error(`Error creating storage context for ${plg}Matterbridge${er}`);
990
+ return;
991
+ }
992
+ if (!this.nodeContext) {
993
+ this.log.error(`Node storage context undefined for ${plg}Matterbridge${er}`);
944
994
  return;
945
995
  }
996
+ this.matterbridgeContext.set('softwareVersion', 1);
997
+ this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
946
998
  this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
947
999
  this.commissioningServer = this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
948
1000
  this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
@@ -951,9 +1003,10 @@ export class Matterbridge extends EventEmitter {
951
1003
  this.commissioningServer.addDevice(this.matterAggregator);
952
1004
  this.log.debug('Adding matterbridge commissioning server to matter server');
953
1005
  await this.matterServer.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
954
- this.log.debug('Starting matter server');
1006
+ this.log.debug('Starting matter server...');
955
1007
  await this.startMatterServer();
956
- this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, 'Matterbridge');
1008
+ this.log.info('Matter server started');
1009
+ await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
957
1010
  }
958
1011
  if (this.bridgeMode === 'childbridge') {
959
1012
  // Plugins are loaded and started by loadPlugin on startup
@@ -991,7 +1044,9 @@ export class Matterbridge extends EventEmitter {
991
1044
  await this.matterServer.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
992
1045
  }
993
1046
  if (plugin.type === 'DynamicPlatform') {
994
- plugin.storageContext = this.createCommissioningServerContext(plugin.name, 'Matterbridge Dynamic Platform', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Dynamic Platform');
1047
+ plugin.storageContext = this.createCommissioningServerContext(plugin.name, 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Dynamic Platform');
1048
+ plugin.storageContext.set('softwareVersion', 1);
1049
+ plugin.storageContext.set('softwareVersionString', this.matterbridgeVersion);
995
1050
  plugin.commissioningServer = this.createCommisioningServer(plugin.storageContext, plugin.name);
996
1051
  this.log.debug(`Creating aggregator for plugin ${plg}${plugin.name}${db}`);
997
1052
  plugin.aggregator = this.createMatterAggregator(plugin.storageContext); // Generate serialNumber and uniqueId
@@ -1018,7 +1073,7 @@ export class Matterbridge extends EventEmitter {
1018
1073
  });
1019
1074
  if (!allStarted)
1020
1075
  return;
1021
- this.log.info('Starting matter server');
1076
+ this.log.info('Starting matter server...');
1022
1077
  // Setting reachability to true
1023
1078
  this.registeredPlugins.forEach((plugin) => {
1024
1079
  if (!plugin.enabled)
@@ -1035,13 +1090,27 @@ export class Matterbridge extends EventEmitter {
1035
1090
  });
1036
1091
  });
1037
1092
  await this.startMatterServer();
1093
+ this.log.info('Matter server started');
1038
1094
  for (const plugin of this.registeredPlugins) {
1039
- this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.name);
1095
+ if (!plugin.enabled)
1096
+ continue;
1097
+ if (!plugin.commissioningServer) {
1098
+ this.log.error(`Commissioning server not found for plugin ${plg}${plugin.name}${er}`);
1099
+ continue;
1100
+ }
1101
+ if (!plugin.storageContext) {
1102
+ this.log.error(`Storage context not found for plugin ${plg}${plugin.name}${er}`);
1103
+ continue;
1104
+ }
1105
+ if (!plugin.nodeContext) {
1106
+ this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
1107
+ continue;
1108
+ }
1109
+ await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
1040
1110
  }
1041
1111
  Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
1042
1112
  clearInterval(startMatterInterval);
1043
1113
  }, 1000);
1044
- return;
1045
1114
  }
1046
1115
  }
1047
1116
  async startMatterServer() {
@@ -1107,9 +1176,9 @@ export class Matterbridge extends EventEmitter {
1107
1176
  storageContext.set('serialNumber', storageContext.get('serialNumber', random));
1108
1177
  storageContext.set('uniqueId', storageContext.get('uniqueId', random));
1109
1178
  storageContext.set('softwareVersion', softwareVersion ?? 1);
1110
- storageContext.set('softwareVersionString', softwareVersionString ?? '1.0.0');
1179
+ storageContext.set('softwareVersionString', softwareVersionString ?? '1.0');
1111
1180
  storageContext.set('hardwareVersion', hardwareVersion ?? 1);
1112
- storageContext.set('hardwareVersionString', hardwareVersionString ?? '1.0.0');
1181
+ storageContext.set('hardwareVersionString', hardwareVersionString ?? '1.0');
1113
1182
  return storageContext;
1114
1183
  }
1115
1184
  /**
@@ -1123,7 +1192,7 @@ export class Matterbridge extends EventEmitter {
1123
1192
  * @param storageContext - The storage context to store the pairing codes.
1124
1193
  * @param pluginName - The name of the commissioning server.
1125
1194
  */
1126
- async showCommissioningQRCode(commissioningServer, storageContext, pluginName) {
1195
+ async showCommissioningQRCode(commissioningServer, storageContext, nodeContext, pluginName) {
1127
1196
  if (!commissioningServer || !storageContext || !pluginName)
1128
1197
  return;
1129
1198
  if (!commissioningServer.isCommissioned()) {
@@ -1131,41 +1200,27 @@ export class Matterbridge extends EventEmitter {
1131
1200
  const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
1132
1201
  storageContext.set('qrPairingCode', qrPairingCode);
1133
1202
  storageContext.set('manualPairingCode', manualPairingCode);
1203
+ await nodeContext.set('qrPairingCode', qrPairingCode);
1204
+ await nodeContext.set('manualPairingCode', manualPairingCode);
1134
1205
  const QrCode = new QrCodeSchema();
1135
- this.log.info(`Pairing code:\n\n${QrCode.encode(qrPairingCode)}\nManual pairing code: ${manualPairingCode}\n`);
1136
- if (this.bridgeMode === 'bridge') {
1137
- await this.nodeContext?.set('qrPairingCode', qrPairingCode);
1138
- await this.nodeContext?.set('manualPairingCode', manualPairingCode);
1139
- }
1140
- if (this.bridgeMode === 'childbridge') {
1206
+ this.log.info(`Pairing code:\n\n${QrCode.encode(qrPairingCode)}\n${plg}${pluginName}${nf}\n\nqrPairingCode: ${qrPairingCode}\n\nManual pairing code: ${manualPairingCode}\n`);
1207
+ if (pluginName !== 'Matterbridge') {
1141
1208
  const plugin = this.findPlugin(pluginName);
1142
1209
  if (plugin) {
1143
- await plugin.nodeContext?.set('qrPairingCode', qrPairingCode);
1144
- await plugin.nodeContext?.set('manualPairingCode', manualPairingCode);
1145
- await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
1146
1210
  plugin.paired = false;
1147
1211
  }
1148
1212
  }
1213
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
1149
1214
  }
1150
1215
  else {
1151
1216
  this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is already commissioned. Waiting for controllers to connect ...`);
1152
- if (this.bridgeMode === 'bridge') {
1153
- const qrPairingCode = storageContext.get('qrPairingCode', '');
1154
- const manualPairingCode = storageContext.get('manualPairingCode', '');
1155
- await this.nodeContext?.set('qrPairingCode', qrPairingCode);
1156
- await this.nodeContext?.set('manualPairingCode', manualPairingCode);
1157
- }
1158
- if (this.bridgeMode === 'childbridge') {
1217
+ if (pluginName !== 'Matterbridge') {
1159
1218
  const plugin = this.findPlugin(pluginName);
1160
- if (plugin && plugin.storageContext && plugin.nodeContext) {
1161
- plugin.qrPairingCode = plugin.storageContext.get('qrPairingCode', '');
1162
- plugin.manualPairingCode = plugin.storageContext.get('manualPairingCode', '');
1163
- await plugin.nodeContext.set('qrPairingCode', plugin.qrPairingCode);
1164
- await plugin.nodeContext.set('manualPairingCode', plugin.manualPairingCode);
1165
- await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
1219
+ if (plugin) {
1166
1220
  plugin.paired = true;
1167
1221
  }
1168
1222
  }
1223
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
1169
1224
  }
1170
1225
  }
1171
1226
  /**
@@ -1186,11 +1241,11 @@ export class Matterbridge extends EventEmitter {
1186
1241
  * Creates a matter commissioning server.
1187
1242
  *
1188
1243
  * @param {StorageContext} context - The storage context.
1189
- * @param {string} name - The name of the commissioning server.
1244
+ * @param {string} pluginName - The name of the commissioning server.
1190
1245
  * @returns {CommissioningServer} The created commissioning server.
1191
1246
  */
1192
- createCommisioningServer(context, name) {
1193
- this.log.debug(`Creating matter commissioning server for plugin ${plg}${name}${db}`);
1247
+ createCommisioningServer(context, pluginName) {
1248
+ this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db}`);
1194
1249
  const deviceName = context.get('deviceName');
1195
1250
  const deviceType = context.get('deviceType');
1196
1251
  const vendorId = context.get('vendorId');
@@ -1199,9 +1254,14 @@ export class Matterbridge extends EventEmitter {
1199
1254
  const productName = context.get('productName'); // Home app = Model
1200
1255
  const serialNumber = context.get('serialNumber');
1201
1256
  const uniqueId = context.get('uniqueId');
1202
- this.log.debug(
1203
- // eslint-disable-next-line max-len
1204
- `Creating matter commissioning server for plugin ${plg}${name}${db} with deviceName ${deviceName} deviceType ${deviceType}(0x${deviceType.toString(16).padStart(4, '0')}) uniqueId ${uniqueId} serialNumber ${serialNumber}`);
1257
+ const softwareVersion = context.get('softwareVersion', 1);
1258
+ const softwareVersionString = context.get('softwareVersionString', '1.0.0'); // Home app = Firmware Revision
1259
+ const hardwareVersion = context.get('hardwareVersion', 1);
1260
+ const hardwareVersionString = context.get('hardwareVersionString', '1.0.0');
1261
+ this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with deviceName ${deviceName} deviceType ${deviceType}(0x${deviceType.toString(16).padStart(4, '0')})`);
1262
+ this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with uniqueId ${uniqueId} serialNumber ${serialNumber}`);
1263
+ this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
1264
+ this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with hardwareVersion ${hardwareVersion} hardwareVersionString ${hardwareVersionString}`);
1205
1265
  const commissioningServer = new CommissioningServer({
1206
1266
  port: undefined,
1207
1267
  passcode: undefined,
@@ -1215,10 +1275,10 @@ export class Matterbridge extends EventEmitter {
1215
1275
  productName,
1216
1276
  nodeLabel: productName,
1217
1277
  productLabel: productName,
1218
- softwareVersion: context.get('softwareVersion', 1),
1219
- softwareVersionString: context.get('softwareVersionString', '1.0.0'), // Home app = Firmware Revision
1220
- hardwareVersion: context.get('hardwareVersion', 1),
1221
- hardwareVersionString: context.get('hardwareVersionString', '1.0.0'),
1278
+ softwareVersion,
1279
+ softwareVersionString, // Home app = Firmware Revision
1280
+ hardwareVersion,
1281
+ hardwareVersionString,
1222
1282
  uniqueId,
1223
1283
  serialNumber,
1224
1284
  reachable: true,
@@ -1227,15 +1287,15 @@ export class Matterbridge extends EventEmitter {
1227
1287
  const info = commissioningServer.getActiveSessionInformation(fabricIndex);
1228
1288
  let connected = false;
1229
1289
  info.forEach((session) => {
1230
- this.log.debug(`***Active session changed on fabric ${fabricIndex} ${session.fabric?.rootVendorId}/${session.fabric?.label} for ${plg}${name}${nf}`, debugStringify(session));
1290
+ this.log.debug(`***Active session changed on fabric ${fabricIndex} ${session.fabric?.rootVendorId}/${session.fabric?.label} for ${plg}${pluginName}${nf}`, debugStringify(session));
1231
1291
  if (session.isPeerActive === true && session.secure === true && session.numberOfActiveSubscriptions >= 1) {
1232
- this.log.info(`***Controller ${session.fabric?.rootVendorId}/${session.fabric?.label} connected to ${plg}${name}${nf}`);
1292
+ this.log.info(`***Controller ${session.fabric?.rootVendorId}/${session.fabric?.label} connected to ${plg}${pluginName}${nf}`);
1233
1293
  connected = true;
1234
1294
  }
1235
1295
  });
1236
1296
  if (connected) {
1237
1297
  if (this.bridgeMode === 'childbridge') {
1238
- const plugin = this.findPlugin(name);
1298
+ const plugin = this.findPlugin(pluginName);
1239
1299
  if (plugin) {
1240
1300
  plugin.paired = true;
1241
1301
  plugin.connected = true;
@@ -1248,16 +1308,15 @@ export class Matterbridge extends EventEmitter {
1248
1308
  if (!plugin.enabled)
1249
1309
  continue;
1250
1310
  this.startPlugin(plugin, 'Matterbridge is commissioned and controllers are connected', true); // No await do it asyncronously with also configurePlugin
1251
- //this.configurePlugin(plugin); // No await do it asyncronously
1252
1311
  }
1253
1312
  Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
1254
1313
  }
1255
1314
  if (this.bridgeMode === 'childbridge') {
1256
1315
  //Logger.defaultLogLevel = Level.INFO;
1257
- const plugin = this.findPlugin(name);
1316
+ const plugin = this.findPlugin(pluginName);
1258
1317
  if (plugin && plugin.type === 'DynamicPlatform' && plugin.configured !== true) {
1259
1318
  for (const registeredDevice of this.registeredDevices) {
1260
- if (registeredDevice.plugin === name) {
1319
+ if (registeredDevice.plugin === pluginName) {
1261
1320
  this.log.info(`Adding bridged device ${dev}${registeredDevice.device.name}-${registeredDevice.device.deviceName}${nf} to aggregator for plugin ${plg}${plugin.name}${db}`);
1262
1321
  if (!plugin.aggregator) {
1263
1322
  this.log.error(`****Aggregator not found for plugin ${plg}${plugin.name}${er}`);
@@ -1274,7 +1333,7 @@ export class Matterbridge extends EventEmitter {
1274
1333
  }
1275
1334
  }
1276
1335
  for (const plugin of this.registeredPlugins) {
1277
- if (plugin.name === name && plugin.platform && plugin.configured !== true) {
1336
+ if (plugin.name === pluginName && plugin.platform && plugin.configured !== true) {
1278
1337
  this.configurePlugin(plugin); // No await do it asyncronously
1279
1338
  }
1280
1339
  }
@@ -1284,12 +1343,29 @@ export class Matterbridge extends EventEmitter {
1284
1343
  }, 2000);
1285
1344
  }
1286
1345
  },
1287
- commissioningChangedCallback: (fabricIndex) => {
1346
+ commissioningChangedCallback: async (fabricIndex) => {
1288
1347
  const info = commissioningServer.getCommissionedFabricInformation(fabricIndex);
1289
- this.log.debug(`***Commissioning changed on fabric ${fabricIndex} for ${plg}${name}${nf}`, debugStringify(info));
1348
+ this.log.debug(`***Commissioning changed on fabric ${fabricIndex} for ${plg}${pluginName}${nf}`, debugStringify(info));
1290
1349
  if (info.length === 0) {
1291
- this.log.warn(`***Commissioning removed from fabric ${fabricIndex} for ${plg}${name}${nf}. Resetting the commissioning server ...`);
1292
- commissioningServer.factoryReset(); // TODO delete from storage also "matterbridge-eve-weather.EndpointStructure"
1350
+ this.log.warn(`***Commissioning removed from fabric ${fabricIndex} for ${plg}${pluginName}${nf}. Resetting the commissioning server ...`);
1351
+ await commissioningServer.factoryReset();
1352
+ if (pluginName === 'Matterbridge') {
1353
+ this.matterbridgeContext?.delete(`${pluginName}.EndpointStructure`);
1354
+ this.matterbridgeContext?.delete(`${pluginName}.EventHandler`);
1355
+ this.matterbridgeContext?.delete(`${pluginName}.SessionManager`);
1356
+ }
1357
+ else {
1358
+ for (const plugin of this.registeredPlugins) {
1359
+ if (plugin.name === pluginName) {
1360
+ await plugin.platform?.onShutdown('Commissioning removed by the controller');
1361
+ plugin.paired = false;
1362
+ plugin.connected = false;
1363
+ plugin.storageContext?.delete(`${pluginName}.EndpointStructure`);
1364
+ plugin.storageContext?.delete(`${pluginName}.EventHandler`);
1365
+ plugin.storageContext?.delete(`${pluginName}.SessionManager`);
1366
+ }
1367
+ }
1368
+ }
1293
1369
  }
1294
1370
  },
1295
1371
  });
@@ -1444,16 +1520,20 @@ export class Matterbridge extends EventEmitter {
1444
1520
  this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
1445
1521
  // Home directory
1446
1522
  this.homeDirectory = os.homedir();
1523
+ this.matterbridgeInformation.homeDirectory = this.homeDirectory;
1447
1524
  this.log.debug(`Home Directory: ${this.homeDirectory}`);
1448
1525
  // Package root directory
1449
1526
  const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
1450
1527
  this.rootDirectory = path.resolve(currentFileDirectory, '../');
1528
+ this.matterbridgeInformation.rootDirectory = this.rootDirectory;
1451
1529
  this.log.debug(`Root Directory: ${this.rootDirectory}`);
1452
1530
  // Global node_modules directory
1453
- this.globalModulesDir = await this.getGlobalNodeModules();
1454
- this.log.debug(`Global node_modules Directory: ${this.globalModulesDir}`);
1531
+ this.globalModulesDirectory = await this.getGlobalNodeModules();
1532
+ this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
1533
+ this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
1455
1534
  // Create the data directory .matterbridge in the home directory
1456
1535
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
1536
+ this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
1457
1537
  try {
1458
1538
  await fs.access(this.matterbridgeDirectory);
1459
1539
  }
@@ -1477,6 +1557,7 @@ export class Matterbridge extends EventEmitter {
1477
1557
  this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
1478
1558
  // Create the data directory .matterbridge in the home directory
1479
1559
  this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
1560
+ this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
1480
1561
  try {
1481
1562
  await fs.access(this.matterbridgePluginDirectory);
1482
1563
  }
@@ -1501,8 +1582,10 @@ export class Matterbridge extends EventEmitter {
1501
1582
  // Matterbridge version
1502
1583
  const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
1503
1584
  this.matterbridgeVersion = packageJson.version;
1585
+ this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeVersion;
1504
1586
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
1505
1587
  this.matterbridgeLatestVersion = await this.getLatestVersion('matterbridge');
1588
+ this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeLatestVersion;
1506
1589
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
1507
1590
  if (this.matterbridgeVersion !== this.matterbridgeLatestVersion) {
1508
1591
  this.log.warn(`Matterbridge is out of date. Current version: ${this.matterbridgeVersion}, Latest version: ${this.matterbridgeLatestVersion}`);
@@ -1583,6 +1666,13 @@ export class Matterbridge extends EventEmitter {
1583
1666
  this.log.debug('The frontend sent /api/system-info');
1584
1667
  res.json(this.systemInformation);
1585
1668
  });
1669
+ // Endpoint to provide matterbridge information
1670
+ this.expressApp.get('/api/matterbridge-info', (req, res) => {
1671
+ this.log.debug('The frontend sent /api/matterbridge-info');
1672
+ this.matterbridgeInformation.bridgeMode = this.bridgeMode;
1673
+ this.matterbridgeInformation.debugEnabled = this.debugEnabled;
1674
+ res.json(this.matterbridgeInformation);
1675
+ });
1586
1676
  // Endpoint to provide plugins
1587
1677
  this.expressApp.get('/api/plugins', (req, res) => {
1588
1678
  this.log.debug('The frontend sent /api/plugins');
@@ -1703,27 +1793,33 @@ export class Matterbridge extends EventEmitter {
1703
1793
  }
1704
1794
  // Handle the command enableplugin from Home
1705
1795
  if (command === 'enableplugin') {
1706
- const plugin = this.findPlugin(param);
1796
+ const plugins = await this.nodeContext?.get('plugins');
1797
+ if (!plugins)
1798
+ return;
1799
+ const plugin = plugins.find((plugin) => plugin.name === param);
1707
1800
  if (plugin) {
1708
1801
  plugin.enabled = true;
1709
1802
  plugin.loaded = undefined;
1710
1803
  plugin.started = undefined;
1711
1804
  plugin.configured = undefined;
1712
1805
  plugin.connected = undefined;
1713
- await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
1806
+ await this.nodeContext?.set('plugins', plugins);
1714
1807
  this.log.info(`Enabled plugin ${plg}${param}${nf}`);
1715
1808
  }
1716
1809
  }
1717
1810
  // Handle the command disableplugin from Home
1718
1811
  if (command === 'disableplugin') {
1719
- const plugin = this.findPlugin(param);
1812
+ const plugins = await this.nodeContext?.get('plugins');
1813
+ if (!plugins)
1814
+ return;
1815
+ const plugin = plugins.find((plugin) => plugin.name === param);
1720
1816
  if (plugin) {
1721
1817
  plugin.enabled = false;
1722
1818
  plugin.loaded = undefined;
1723
1819
  plugin.started = undefined;
1724
1820
  plugin.configured = undefined;
1725
1821
  plugin.connected = undefined;
1726
- await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
1822
+ await this.nodeContext?.set('plugins', plugins);
1727
1823
  this.log.info(`Disabled plugin ${plg}${param}${nf}`);
1728
1824
  }
1729
1825
  }