matterbridge 1.2.15 → 1.2.17

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 (37) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/AirQualityCluster.d.ts.map +1 -1
  3. package/dist/AirQualityCluster.js.map +1 -1
  4. package/dist/BridgedDeviceBasicInformationCluster.d.ts +1 -1
  5. package/dist/BridgedDeviceBasicInformationCluster.d.ts.map +1 -1
  6. package/dist/BridgedDeviceBasicInformationCluster.js +1 -1
  7. package/dist/BridgedDeviceBasicInformationCluster.js.map +1 -1
  8. package/dist/TvocCluster.d.ts.map +1 -1
  9. package/dist/TvocCluster.js.map +1 -1
  10. package/dist/cli.js +8 -5
  11. package/dist/cli.js.map +1 -1
  12. package/dist/defaultConfigSchema.d.ts +2 -0
  13. package/dist/defaultConfigSchema.d.ts.map +1 -1
  14. package/dist/defaultConfigSchema.js +74 -0
  15. package/dist/defaultConfigSchema.js.map +1 -1
  16. package/dist/matterbridge.d.ts +21 -6
  17. package/dist/matterbridge.d.ts.map +1 -1
  18. package/dist/matterbridge.js +187 -213
  19. package/dist/matterbridge.js.map +1 -1
  20. package/dist/matterbridgeController.js +1 -1
  21. package/dist/matterbridgeController.js.map +1 -1
  22. package/dist/matterbridgeDevice.d.ts +49 -75
  23. package/dist/matterbridgeDevice.d.ts.map +1 -1
  24. package/dist/matterbridgeDevice.js +103 -67
  25. package/dist/matterbridgeDevice.js.map +1 -1
  26. package/dist/utils.d.ts +19 -1
  27. package/dist/utils.d.ts.map +1 -1
  28. package/dist/utils.js +104 -35
  29. package/dist/utils.js.map +1 -1
  30. package/frontend/build/asset-manifest.json +3 -3
  31. package/frontend/build/index.html +1 -1
  32. package/frontend/build/static/js/main.eab44937.js +3 -0
  33. package/frontend/build/static/js/main.eab44937.js.map +1 -0
  34. package/package.json +11 -11
  35. package/frontend/build/static/js/main.5886e61b.js +0 -3
  36. package/frontend/build/static/js/main.5886e61b.js.map +0 -1
  37. /package/frontend/build/static/js/{main.5886e61b.js.LICENSE.txt → main.eab44937.js.LICENSE.txt} +0 -0
@@ -22,7 +22,7 @@
22
22
  */
23
23
  import { MatterbridgeDevice } from './matterbridgeDevice.js';
24
24
  import { NodeStorageManager } from 'node-persist-manager';
25
- import { AnsiLogger, BRIGHT, RESET, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, er, nf, rs, wr, RED, GREEN } from 'node-ansi-logger';
25
+ import { AnsiLogger, BRIGHT, RESET, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, er, nf, rs, wr, RED, GREEN, zb } from 'node-ansi-logger';
26
26
  import { fileURLToPath, pathToFileURL } from 'url';
27
27
  import { promises as fs } from 'fs';
28
28
  import { exec, spawn } from 'child_process';
@@ -34,7 +34,7 @@ import path from 'path';
34
34
  import WebSocket, { WebSocketServer } from 'ws';
35
35
  import { BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster } from './BridgedDeviceBasicInformationCluster.js';
36
36
  import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
37
- import { BasicInformationCluster, ClusterServer, FixedLabelCluster, GeneralCommissioning, GeneralDiagnostics, GeneralDiagnosticsCluster, PowerSourceCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById, } from '@project-chip/matter-node.js/cluster';
37
+ import { BasicInformationCluster, ClusterServer, FixedLabelCluster, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById } from '@project-chip/matter-node.js/cluster';
38
38
  import { DeviceTypeId, VendorId } from '@project-chip/matter-node.js/datatype';
39
39
  import { Aggregator, DeviceTypes, NodeStateInformation } from '@project-chip/matter-node.js/device';
40
40
  import { Format, Level, Logger } from '@project-chip/matter-node.js/log';
@@ -42,7 +42,7 @@ import { ManualPairingCodeCodec, QrCodeSchema } from '@project-chip/matter-node.
42
42
  import { StorageBackendDisk, StorageBackendJsonFile, StorageManager } from '@project-chip/matter-node.js/storage';
43
43
  import { requireMinNodeVersion, getParameter, getIntParameter, hasParameter } from '@project-chip/matter-node.js/util';
44
44
  import { CryptoNode } from '@project-chip/matter-node.js/crypto';
45
- import { somfytahoma_config, somfytahoma_schema, zigbee2mqtt_config, zigbee2mqtt_schema } from './defaultConfigSchema.js';
45
+ import { shelly_config, shelly_schema, somfytahoma_config, somfytahoma_schema, zigbee2mqtt_config, zigbee2mqtt_schema } from './defaultConfigSchema.js';
46
46
  const plg = '\u001B[38;5;33m';
47
47
  const dev = '\u001B[38;5;79m';
48
48
  const typ = '\u001B[38;5;207m';
@@ -84,6 +84,7 @@ export class Matterbridge extends EventEmitter {
84
84
  globalModulesDirectory = '';
85
85
  matterbridgeVersion = '';
86
86
  matterbridgeLatestVersion = '';
87
+ checkUpdateInterval; // = 24 * 60 * 60 * 1000; // 24 hours
87
88
  bridgeMode = '';
88
89
  restartMode = '';
89
90
  debugEnabled = false;
@@ -285,12 +286,6 @@ export class Matterbridge extends EventEmitter {
285
286
  await plugin.nodeContext.set('description', plugin.description);
286
287
  await plugin.nodeContext.set('author', plugin.author);
287
288
  }
288
- if (hasParameter('logstorage')) {
289
- await this.nodeContext.logStorage();
290
- for (const plugin of this.registeredPlugins) {
291
- await plugin.nodeContext?.logStorage();
292
- }
293
- }
294
289
  // Log system info and create .matterbridge directory
295
290
  await this.logNodeAndSystemInfo();
296
291
  this.log.info(
@@ -340,6 +335,16 @@ export class Matterbridge extends EventEmitter {
340
335
  this.emit('shutdown');
341
336
  process.exit(0);
342
337
  }
338
+ if (hasParameter('logstorage')) {
339
+ this.log.info(`${plg}matterbridge${nf} storage log`);
340
+ await this.nodeContext?.logStorage();
341
+ for (const plugin of this.registeredPlugins) {
342
+ this.log.info(`${plg}${plugin.name}${nf} storage log`);
343
+ await plugin.nodeContext?.logStorage();
344
+ }
345
+ this.emit('shutdown');
346
+ process.exit(0);
347
+ }
343
348
  if (getParameter('add')) {
344
349
  this.log.debug(`Registering plugin ${getParameter('add')}`);
345
350
  await this.executeCommandLine(getParameter('add'), 'add');
@@ -393,6 +398,12 @@ export class Matterbridge extends EventEmitter {
393
398
  }
394
399
  // Initialize frontend
395
400
  await this.initializeFrontend(getIntParameter('frontend'));
401
+ this.checkUpdateInterval = setInterval(() => {
402
+ this.getMatterbridgeLatestVersion();
403
+ this.registeredPlugins.forEach((plugin) => {
404
+ this.getPluginLatestVersion(plugin);
405
+ });
406
+ }, 60 * 60 * 1000);
396
407
  if (hasParameter('test')) {
397
408
  this.bridgeMode = 'childbridge';
398
409
  MatterbridgeDevice.bridgeMode = 'childbridge';
@@ -467,7 +478,7 @@ export class Matterbridge extends EventEmitter {
467
478
  this.log.debug(`Package.json not found at ${packageJsonPath}`);
468
479
  this.log.debug(`Trying at ${this.globalModulesDirectory}`);
469
480
  packageJsonPath = path.join(this.globalModulesDirectory, pluginPath);
470
- //this.log.debug(`Got ${packageJsonPath}`);
481
+ // this.log.debug(`Got ${packageJsonPath}`);
471
482
  }
472
483
  try {
473
484
  // Load the package.json of the plugin
@@ -650,6 +661,8 @@ export class Matterbridge extends EventEmitter {
650
661
  process.removeAllListeners('SIGINT');
651
662
  process.removeAllListeners('SIGTERM');
652
663
  this.log.debug('All listeners removed');
664
+ this.checkUpdateInterval && clearInterval(this.checkUpdateInterval);
665
+ this.checkUpdateInterval = undefined;
653
666
  // Calling the shutdown functions with a reason
654
667
  for (const plugin of this.registeredPlugins) {
655
668
  if (!plugin.enabled || plugin.error)
@@ -747,8 +760,9 @@ export class Matterbridge extends EventEmitter {
747
760
  const serializedRegisteredDevices = [];
748
761
  this.registeredDevices.forEach((registeredDevice) => {
749
762
  const serializedMatterbridgeDevice = registeredDevice.device.serialize(registeredDevice.plugin);
750
- //this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
751
- serializedRegisteredDevices.push(serializedMatterbridgeDevice);
763
+ // this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
764
+ if (serializedMatterbridgeDevice)
765
+ serializedRegisteredDevices.push(serializedMatterbridgeDevice);
752
766
  });
753
767
  await this.nodeContext.set('devices', serializedRegisteredDevices);
754
768
  this.log.info('Saved registered devices');
@@ -905,7 +919,7 @@ export class Matterbridge extends EventEmitter {
905
919
  if (this.bridgeMode === 'bridge') {
906
920
  device.setBridgedDeviceReachability(false);
907
921
  device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
908
- this.matterAggregator.removeBridgedDevice(device);
922
+ this.matterAggregator?.removeBridgedDevice(device);
909
923
  this.registeredDevices.forEach((registeredDevice, index) => {
910
924
  if (registeredDevice.device === device) {
911
925
  this.registeredDevices.splice(index, 1);
@@ -932,7 +946,7 @@ export class Matterbridge extends EventEmitter {
932
946
  });
933
947
  device.setBridgedDeviceReachability(false);
934
948
  device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
935
- plugin.aggregator.removeBridgedDevice(device);
949
+ plugin.aggregator?.removeBridgedDevice(device);
936
950
  }
937
951
  this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
938
952
  if (plugin.registeredDevices !== undefined)
@@ -1038,85 +1052,7 @@ export class Matterbridge extends EventEmitter {
1038
1052
  this.mattercontrollerContext = undefined;
1039
1053
  }
1040
1054
  async testStartMatterBridge() {
1041
- /*
1042
- if (!this.storageManager) {
1043
- this.log.error('No storage manager initialized');
1044
- await this.cleanup('No storage manager initialized');
1045
- return;
1046
- }
1047
- this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1048
- this.matterServer = this.createMatterServer(this.storageManager);
1049
-
1050
- this.log.debug('***Starting startMatterbridge interval for Matterbridge');
1051
- let failCount = 0;
1052
- const startInterval = setInterval(async () => {
1053
- for (const plugin of this.registeredPlugins) {
1054
- if (!plugin.enabled) continue;
1055
- if (!plugin.loaded) {
1056
- this.log.info(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded}...`);
1057
- failCount++;
1058
- if (failCount > 30) {
1059
- this.log.error(`***Failed to load plugin ${plg}${plugin.name}${er}`);
1060
- plugin.error = true;
1061
- } else {
1062
- return;
1063
- }
1064
- }
1065
- }
1066
- clearInterval(startInterval);
1067
- this.log.debug('***Cleared startMatterbridge interval for Matterbridge');
1068
-
1069
- this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
1070
- this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Aggregator');
1071
- if (!this.matterbridgeContext) {
1072
- this.log.error(`Error creating storage context for ${plg}Matterbridge${er}`);
1073
- return;
1074
- }
1075
- if (!this.nodeContext) {
1076
- this.log.error(`Node storage context undefined for ${plg}Matterbridge${er}`);
1077
- return;
1078
- }
1079
- await this.matterbridgeContext.set('softwareVersion', 1);
1080
- await this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
1081
- this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
1082
- this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
1083
-
1084
- this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
1085
- this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext, 'Matterbridge');
1086
- this.log.debug('Adding matterbridge aggregator to commissioning server');
1087
- this.commissioningServer.addDevice(this.matterAggregator);
1088
-
1089
- const device = new MatterbridgeDevice(DeviceTypes.CONTACT_SENSOR);
1090
- device.createDefaultIdentifyClusterServer();
1091
- device.createDefaultBridgedDeviceBasicInformationClusterServer('Boolean test', '0x89930475', 0x8000, 'Matterbridge', 'Boolean');
1092
- device.createDefaultBooleanStateClusterServer(true);
1093
- device.createDefaultPowerSourceReplaceableBatteryClusterServer(75);
1094
- device.createDefaultPowerSourceConfigurationClusterServer(1);
1095
-
1096
- this.log.debug('Adding matterbridge commissioning server to matter server');
1097
- await this.matterServer?.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
1098
- this.log.debug(`Setting reachability to true for ${plg}Matterbridge${db}`);
1099
- this.commissioningServer.setReachability(true);
1100
- this.log.debug('Starting matter server...');
1101
- await this.startMatterServer();
1102
- this.log.info('Matter server started');
1103
- await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
1104
-
1105
- setTimeout(() => {
1106
- this.matterAggregator?.addBridgedDevice(device);
1107
- this.log.info('Added device to aggregator');
1108
- }, 30 * 1000);
1109
-
1110
- setInterval(() => {
1111
- const cluster = device.getClusterServer(BooleanStateCluster);
1112
- if (!cluster) return;
1113
- const contact = cluster.getStateValueAttribute();
1114
- cluster.setStateValueAttribute(!contact);
1115
- if (cluster.isEventSupportedByName('stateChange')) cluster.triggerStateChangeEvent!({ stateValue: !contact });
1116
- this.log.info('Set attribute and event for BooleanStateCluster to', !contact);
1117
- }, 60 * 1000);
1118
- }, 1000);
1119
- */
1055
+ // Start the Matterbridge
1120
1056
  }
1121
1057
  /**
1122
1058
  * Loads the schema for a plugin.
@@ -1147,6 +1083,8 @@ export class Matterbridge extends EventEmitter {
1147
1083
  schema = zigbee2mqtt_schema;
1148
1084
  else if (plugin.name === 'matterbridge-somfy-tahoma')
1149
1085
  schema = somfytahoma_schema;
1086
+ else if (plugin.name === 'matterbridge-shelly')
1087
+ schema = shelly_schema;
1150
1088
  else
1151
1089
  schema = {
1152
1090
  title: plugin.description,
@@ -1239,6 +1177,8 @@ export class Matterbridge extends EventEmitter {
1239
1177
  config = zigbee2mqtt_config;
1240
1178
  else if (plugin.name === 'matterbridge-somfy-tahoma')
1241
1179
  config = somfytahoma_config;
1180
+ else if (plugin.name === 'matterbridge-shelly')
1181
+ config = shelly_config;
1242
1182
  else
1243
1183
  config = { name: plugin.name, type: plugin.type, unregisterOnShutdown: false };
1244
1184
  try {
@@ -1408,7 +1348,7 @@ export class Matterbridge extends EventEmitter {
1408
1348
  if (pluginInstance.default) {
1409
1349
  const config = await this.loadPluginConfig(plugin);
1410
1350
  const log = new AnsiLogger({ logName: plugin.description, logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logDebug: this.debugEnabled });
1411
- //log.setCallback(this.wssSendMessage.bind(this));
1351
+ // log.setCallback(this.wssSendMessage.bind(this));
1412
1352
  const platform = pluginInstance.default(this, log, config);
1413
1353
  platform.name = packageJson.name;
1414
1354
  platform.config = config;
@@ -1423,18 +1363,7 @@ export class Matterbridge extends EventEmitter {
1423
1363
  plugin.registeredDevices = 0;
1424
1364
  plugin.addedDevices = 0;
1425
1365
  await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
1426
- this.getLatestVersion(plugin.name)
1427
- .then(async (latestVersion) => {
1428
- plugin.latestVersion = latestVersion;
1429
- await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
1430
- if (plugin.version !== latestVersion)
1431
- this.log.warn(`The plugin ${plg}${plugin.name}${wr} is out of date. Current version: ${plugin.version}, Latest version: ${latestVersion}`);
1432
- else
1433
- this.log.info(`The plugin ${plg}${plugin.name}${nf} is up to date. Current version: ${plugin.version}, Latest version: ${latestVersion}`);
1434
- })
1435
- .catch((error) => {
1436
- this.log.error(`Error getting ${plugin.name} latest version: ${error}`);
1437
- });
1366
+ await this.getPluginLatestVersion(plugin);
1438
1367
  this.log.info(`Loaded plugin ${plg}${plugin.name}${nf} type ${typ}${platform.type} ${db}(entrypoint ${UNDERLINE}${pluginEntry}${UNDERLINEOFF})`);
1439
1368
  if (start)
1440
1369
  this.startPlugin(plugin, message); // No await do it asyncronously
@@ -1444,14 +1373,14 @@ export class Matterbridge extends EventEmitter {
1444
1373
  this.log.error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`);
1445
1374
  plugin.error = true;
1446
1375
  return;
1447
- //return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`));
1376
+ // return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`));
1448
1377
  }
1449
1378
  }
1450
1379
  catch (err) {
1451
1380
  this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
1452
1381
  plugin.error = true;
1453
1382
  return;
1454
- //return Promise.reject(new Error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`));
1383
+ // return Promise.reject(new Error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`));
1455
1384
  }
1456
1385
  }
1457
1386
  /**
@@ -1532,8 +1461,8 @@ export class Matterbridge extends EventEmitter {
1532
1461
  return;
1533
1462
  }
1534
1463
  if (hasParameter('discover')) {
1535
- //const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1536
- //console.log(discover);
1464
+ // const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1465
+ // console.log(discover);
1537
1466
  }
1538
1467
  if (!this.commissioningController.isCommissioned()) {
1539
1468
  this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
@@ -1573,7 +1502,7 @@ export class Matterbridge extends EventEmitter {
1573
1502
  }
1574
1503
  },
1575
1504
  });
1576
- //node.logStructure();
1505
+ // node.logStructure();
1577
1506
  // Get the interaction client
1578
1507
  this.log.info('Getting the interaction client');
1579
1508
  const interactionClient = await node.getInteractionClient();
@@ -1676,7 +1605,7 @@ export class Matterbridge extends EventEmitter {
1676
1605
  this.log.info('Matter server started');
1677
1606
  await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
1678
1607
  // logEndpoint(this.commissioningServer.getRootEndpoint());
1679
- //if (hasParameter('advertise')) await this.commissioningServer.advertise();
1608
+ // if (hasParameter('advertise')) await this.commissioningServer.advertise();
1680
1609
  setTimeout(() => {
1681
1610
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1682
1611
  if (this.commissioningServer)
@@ -1723,7 +1652,8 @@ export class Matterbridge extends EventEmitter {
1723
1652
  plugin.device = registeredDevice.device;
1724
1653
  }
1725
1654
  this.log.debug(`Adding commissioning server to matter server for plugin ${plg}${plugin.name}${db}`);
1726
- await this.matterServer?.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
1655
+ if (plugin.commissioningServer)
1656
+ await this.matterServer?.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
1727
1657
  }
1728
1658
  if (plugin.type === 'DynamicPlatform') {
1729
1659
  this.log.debug(`Creating commissioning server context for ${plg}${plugin.name}${db}`);
@@ -1788,7 +1718,7 @@ export class Matterbridge extends EventEmitter {
1788
1718
  this.setAggregatorReachability(plugin.aggregator, true);
1789
1719
  }
1790
1720
  Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
1791
- //clearInterval(startMatterInterval);
1721
+ // clearInterval(startMatterInterval);
1792
1722
  }, 1000);
1793
1723
  }
1794
1724
  }
@@ -1805,6 +1735,7 @@ export class Matterbridge extends EventEmitter {
1805
1735
  this.log.debug('Starting matter server...');
1806
1736
  await this.matterServer.start();
1807
1737
  this.log.debug('Started matter server');
1738
+ // this.commissioningServer?.getRootEndpoint() && logEndpoint(this.commissioningServer?.getRootEndpoint());
1808
1739
  }
1809
1740
  /**
1810
1741
  * Imports the commissioning server context for a specific plugin and device.
@@ -1925,7 +1856,13 @@ export class Matterbridge extends EventEmitter {
1925
1856
  await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
1926
1857
  }
1927
1858
  else {
1928
- this.log.info(`*The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is already commissioned . Waiting for controllers to connect ...`);
1859
+ this.log.info(`*The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is already commissioned. Waiting for controllers to connect ...`);
1860
+ const fabricInfo = commissioningServer.getCommissionedFabricInformation();
1861
+ if (fabricInfo.length > 0)
1862
+ this.log.info('Commissioned fabric information:');
1863
+ fabricInfo?.forEach((info) => {
1864
+ this.log.info(`- fabric index ${zb}${info.fabricIndex}${nf} id ${zb}${info.fabricId}${nf} vendor ${zb}${info.rootVendorId}${nf} ${this.getVendorIdName(info.rootVendorId)} ${info.label}`);
1865
+ });
1929
1866
  if (pluginName !== 'Matterbridge') {
1930
1867
  const plugin = this.findPlugin(pluginName);
1931
1868
  if (plugin) {
@@ -1992,6 +1929,41 @@ export class Matterbridge extends EventEmitter {
1992
1929
  if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent)
1993
1930
  basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
1994
1931
  }
1932
+ getVendorIdName = (vendorId) => {
1933
+ if (!vendorId)
1934
+ return '';
1935
+ let vendorName = '';
1936
+ switch (vendorId) {
1937
+ case 4937:
1938
+ vendorName = '(AppleHome)';
1939
+ break;
1940
+ case 4996:
1941
+ vendorName = '(AppleKeyChain)';
1942
+ break;
1943
+ case 4362:
1944
+ vendorName = '(SmartThings)';
1945
+ break;
1946
+ case 4939:
1947
+ vendorName = '(HomeAssistant)';
1948
+ break;
1949
+ case 24582:
1950
+ vendorName = '(GoogleHome)';
1951
+ break;
1952
+ case 4631:
1953
+ vendorName = '(Alexa)';
1954
+ break;
1955
+ case 4701:
1956
+ vendorName = '(Tuya)';
1957
+ break;
1958
+ case 4742:
1959
+ vendorName = '(eWeLink)';
1960
+ break;
1961
+ default:
1962
+ vendorName = '(unknown)';
1963
+ break;
1964
+ }
1965
+ return vendorName;
1966
+ };
1995
1967
  /**
1996
1968
  * Creates a matter commissioning server.
1997
1969
  *
@@ -2000,38 +1972,6 @@ export class Matterbridge extends EventEmitter {
2000
1972
  * @returns {CommissioningServer} The created commissioning server.
2001
1973
  */
2002
1974
  async createCommisioningServer(context, pluginName) {
2003
- const getVendorIdName = (vendorId) => {
2004
- if (!vendorId)
2005
- return '';
2006
- let vendorName = '';
2007
- switch (vendorId) {
2008
- case 4937:
2009
- vendorName = '(AppleHome)';
2010
- break;
2011
- case 4996:
2012
- vendorName = '(AppleKeyChain)';
2013
- break;
2014
- case 4362:
2015
- vendorName = '(SmartThings)';
2016
- break;
2017
- case 4939:
2018
- vendorName = '(HomeAssistant)';
2019
- break;
2020
- case 24582:
2021
- vendorName = '(GoogleHome)';
2022
- break;
2023
- case 4701:
2024
- vendorName = '(Tuya)';
2025
- break;
2026
- case 4742:
2027
- vendorName = '(eWeLink)';
2028
- break;
2029
- default:
2030
- vendorName = '(unknown)';
2031
- break;
2032
- }
2033
- return vendorName;
2034
- };
2035
1975
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db}`);
2036
1976
  const deviceName = await context.get('deviceName');
2037
1977
  const deviceType = await context.get('deviceType');
@@ -2074,9 +2014,9 @@ export class Matterbridge extends EventEmitter {
2074
2014
  const info = commissioningServer.getActiveSessionInformation(fabricIndex);
2075
2015
  let connected = false;
2076
2016
  info.forEach((session) => {
2077
- 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));
2017
+ this.log.info(`*Active session changed on fabric ${zb}${fabricIndex}${nf} vendor ${zb}${session.fabric?.rootVendorId}${nf} ${this.getVendorIdName(session.fabric?.rootVendorId)} ${session.fabric?.label} for ${plg}${pluginName}${nf}`, debugStringify(session));
2078
2018
  if (session.isPeerActive === true && session.secure === true && session.numberOfActiveSubscriptions >= 1) {
2079
- this.log.info(`*Controller ${session.fabric?.rootVendorId}${getVendorIdName(session.fabric?.rootVendorId)}/${session.fabric?.label} connected to ${plg}${pluginName}${nf} on session ${session.name}`);
2019
+ this.log.info(`*Controller ${zb}${session.fabric?.rootVendorId}${nf} ${this.getVendorIdName(session.fabric?.rootVendorId)} ${session.fabric?.label} connected to ${plg}${pluginName}${nf} on session ${session.name}`);
2080
2020
  connected = true;
2081
2021
  }
2082
2022
  });
@@ -2090,7 +2030,7 @@ export class Matterbridge extends EventEmitter {
2090
2030
  }
2091
2031
  setTimeout(() => {
2092
2032
  if (this.bridgeMode === 'bridge') {
2093
- //Logger.defaultLogLevel = Level.INFO;
2033
+ // Logger.defaultLogLevel = Level.INFO;
2094
2034
  for (const plugin of this.registeredPlugins) {
2095
2035
  if (!plugin.enabled || !plugin.loaded || plugin.error)
2096
2036
  continue;
@@ -2105,7 +2045,7 @@ export class Matterbridge extends EventEmitter {
2105
2045
  Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
2106
2046
  }
2107
2047
  if (this.bridgeMode === 'childbridge') {
2108
- //Logger.defaultLogLevel = Level.INFO;
2048
+ // Logger.defaultLogLevel = Level.INFO;
2109
2049
  const plugin = this.findPlugin(pluginName);
2110
2050
  if (plugin && plugin.type === 'DynamicPlatform' && plugin.configured !== true) {
2111
2051
  for (const registeredDevice of this.registeredDevices) {
@@ -2137,10 +2077,10 @@ export class Matterbridge extends EventEmitter {
2137
2077
  }
2138
2078
  },
2139
2079
  commissioningChangedCallback: async (fabricIndex) => {
2140
- const info = commissioningServer.getCommissionedFabricInformation(fabricIndex);
2141
- this.log.debug(`*Commissioning changed on fabric ${fabricIndex} for ${plg}${pluginName}${nf}`, debugStringify(info));
2142
- if (info.length === 0) {
2143
- this.log.warn(`*Commissioning removed from fabric ${fabricIndex} for ${plg}${pluginName}${wr}. Resetting the commissioning server ...`);
2080
+ const fabricInfo = commissioningServer.getCommissionedFabricInformation(fabricIndex);
2081
+ this.log.debug(`*Commissioning changed on fabric ${zb}${fabricIndex}${nf} for ${plg}${pluginName}${nf}`, debugStringify(fabricInfo));
2082
+ if (commissioningServer.getCommissionedFabricInformation().length === 0) {
2083
+ this.log.warn(`*Commissioning removed from fabric ${zb}${fabricIndex}${nf} for ${plg}${pluginName}${wr}. Resetting the commissioning server ...`);
2144
2084
  await commissioningServer.factoryReset();
2145
2085
  if (pluginName === 'Matterbridge') {
2146
2086
  await this.matterbridgeContext?.clearAll();
@@ -2159,48 +2099,48 @@ export class Matterbridge extends EventEmitter {
2159
2099
  }
2160
2100
  },
2161
2101
  });
2102
+ /*
2162
2103
  const gdcCluster = commissioningServer.getRootClusterServer(GeneralDiagnosticsCluster);
2163
2104
  if (gdcCluster) {
2164
- // We have like "30:f6:ef:69:2b:c5" in this.systemInformation.macAddress
2165
- const macArray = this.systemInformation.macAddress.split(':').map((hex) => parseInt(hex, 16));
2166
- let hardwareAddress = new Uint8Array(macArray);
2167
- if (hardwareAddress.length === 6)
2168
- hardwareAddress = Uint8Array.from([0, 0, ...hardwareAddress]);
2169
- // We have like "192.168.1.189" in this.systemInformation.ipv4Address
2170
- const ipv4Array = this.systemInformation.ipv4Address.split('.').map((num) => parseInt(num));
2171
- const iPv4Address = new Uint8Array(ipv4Array);
2172
- // We have like "fd78:cbf8:4939:746:d555:85a9:74f6:9c6" in this.systemInformation.ipv6Address
2173
- const ipv6Groups = this.systemInformation.ipv6Address.split(':');
2174
- const ipv6Array = [];
2175
- for (const group of ipv6Groups) {
2176
- const decimal = parseInt(group, 16);
2177
- ipv6Array.push(decimal >> 8); // High byte
2178
- ipv6Array.push(decimal & 0xff); // Low byte
2179
- }
2180
- const iPv6Address = new Uint8Array(ipv6Array);
2181
- this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} hardwareAddress ${this.systemInformation.macAddress} => ${debugStringify(hardwareAddress)}`);
2182
- this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} iPv4Address ${this.systemInformation.ipv4Address} => ${debugStringify(iPv4Address)}`);
2183
- this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} iPv6Address ${this.systemInformation.ipv6Address} => ${debugStringify(iPv6Address)}`);
2184
- try {
2185
- gdcCluster.setNetworkInterfacesAttribute([
2186
- {
2187
- name: 'eth0',
2188
- isOperational: true,
2189
- offPremiseServicesReachableIPv4: null,
2190
- offPremiseServicesReachableIPv6: null,
2191
- hardwareAddress,
2192
- iPv4Addresses: [iPv4Address],
2193
- iPv6Addresses: [iPv6Address],
2194
- type: GeneralDiagnostics.InterfaceType.Ethernet,
2195
- },
2196
- ]);
2197
- }
2198
- catch (error) {
2199
- this.log.error(`GeneralDiagnosticsCluster.setNetworkInterfacesAttribute for ${plg}${pluginName}${er} error:`, error);
2200
- }
2201
- }
2202
- else
2203
- this.log.warn(`*GeneralDiagnosticsCluster not found for ${plg}${pluginName}${wr}`);
2105
+ // console.log('GeneralDiagnosticsCluster found for', plg, pluginName, db);
2106
+ // console.log('GeneralDiagnosticsCluster', gdcCluster);
2107
+ // We have like "30:f6:ef:69:2b:c5" in this.systemInformation.macAddress
2108
+ const macArray = this.systemInformation.macAddress.split(':').map((hex) => parseInt(hex, 16));
2109
+ let hardwareAddress = new Uint8Array(macArray);
2110
+ if (hardwareAddress.length === 6) hardwareAddress = Uint8Array.from([0, 0, ...hardwareAddress]);
2111
+ // We have like "192.168.1.189" in this.systemInformation.ipv4Address
2112
+ const ipv4Array = this.systemInformation.ipv4Address.split('.').map((num) => parseInt(num));
2113
+ const iPv4Address = new Uint8Array(ipv4Array);
2114
+ // We have like "fd78:cbf8:4939:746:d555:85a9:74f6:9c6" in this.systemInformation.ipv6Address
2115
+ const ipv6Groups = this.systemInformation.ipv6Address.split(':');
2116
+ const ipv6Array = [];
2117
+ for (const group of ipv6Groups) {
2118
+ const decimal = parseInt(group, 16);
2119
+ ipv6Array.push(decimal >> 8); // High byte
2120
+ ipv6Array.push(decimal & 0xff); // Low byte
2121
+ }
2122
+ const iPv6Address = new Uint8Array(ipv6Array);
2123
+ this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} hardwareAddress ${this.systemInformation.macAddress} => ${debugStringify(hardwareAddress)}`);
2124
+ this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} iPv4Address ${this.systemInformation.ipv4Address} => ${debugStringify(iPv4Address)}`);
2125
+ this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} iPv6Address ${this.systemInformation.ipv6Address} => ${debugStringify(iPv6Address)}`);
2126
+ try {
2127
+ gdcCluster.setNetworkInterfacesAttribute([
2128
+ {
2129
+ name: 'eth0',
2130
+ isOperational: true,
2131
+ offPremiseServicesReachableIPv4: null,
2132
+ offPremiseServicesReachableIPv6: null,
2133
+ hardwareAddress,
2134
+ iPv4Addresses: [iPv4Address],
2135
+ iPv6Addresses: [iPv6Address],
2136
+ type: GeneralDiagnostics.InterfaceType.Ethernet,
2137
+ },
2138
+ ]);
2139
+ } catch (error) {
2140
+ this.log.error(`GeneralDiagnosticsCluster.setNetworkInterfacesAttribute for ${plg}${pluginName}${er} error:`, error);
2141
+ }
2142
+ } else this.log.warn(`*GeneralDiagnosticsCluster not found for ${plg}${pluginName}${wr}`);
2143
+ */
2204
2144
  commissioningServer.addCommandHandler('testEventTrigger', async ({ request: { enableKey, eventTrigger } }) => this.log.info(`testEventTrigger called on GeneralDiagnostic cluster: ${enableKey} ${eventTrigger}`));
2205
2145
  return commissioningServer;
2206
2146
  }
@@ -2439,6 +2379,20 @@ export class Matterbridge extends EventEmitter {
2439
2379
  if (this.nodeContext)
2440
2380
  this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', '');
2441
2381
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
2382
+ await this.getMatterbridgeLatestVersion();
2383
+ // Current working directory
2384
+ const currentDir = process.cwd();
2385
+ this.log.debug(`Current Working Directory: ${currentDir}`);
2386
+ // Command line arguments (excluding 'node' and the script name)
2387
+ const cmdArgs = process.argv.slice(2).join(' ');
2388
+ this.log.debug(`Command Line Arguments: ${cmdArgs}`);
2389
+ }
2390
+ /**
2391
+ * Retrieves the latest version of Matterbridge and performs necessary actions based on the version comparison.
2392
+ * @private
2393
+ * @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
2394
+ */
2395
+ async getMatterbridgeLatestVersion() {
2442
2396
  this.getLatestVersion('matterbridge')
2443
2397
  .then(async (matterbridgeLatestVersion) => {
2444
2398
  this.matterbridgeLatestVersion = matterbridgeLatestVersion;
@@ -2450,14 +2404,34 @@ export class Matterbridge extends EventEmitter {
2450
2404
  }
2451
2405
  })
2452
2406
  .catch((error) => {
2453
- this.log.error(`Error getting Matterbridge latest version: ${error}`);
2407
+ this.log.error(`Error getting Matterbridge latest version: ${error.message}`);
2408
+ error.stack && this.log.debug(error.stack);
2409
+ });
2410
+ }
2411
+ /**
2412
+ * Retrieves the latest version of a plugin and updates the plugin's latestVersion property.
2413
+ * If the plugin's version is different from the latest version, logs a warning message.
2414
+ * If the plugin's version is the same as the latest version, logs an info message.
2415
+ * If there is an error retrieving the latest version, logs an error message.
2416
+ *
2417
+ * @private
2418
+ * @param {RegisteredPlugin} plugin - The plugin for which to retrieve the latest version.
2419
+ * @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
2420
+ */
2421
+ async getPluginLatestVersion(plugin) {
2422
+ this.getLatestVersion(plugin.name)
2423
+ .then(async (latestVersion) => {
2424
+ plugin.latestVersion = latestVersion;
2425
+ await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
2426
+ if (plugin.version !== latestVersion)
2427
+ this.log.warn(`The plugin ${plg}${plugin.name}${wr} is out of date. Current version: ${plugin.version}, Latest version: ${latestVersion}`);
2428
+ else
2429
+ this.log.info(`The plugin ${plg}${plugin.name}${nf} is up to date. Current version: ${plugin.version}, Latest version: ${latestVersion}`);
2430
+ })
2431
+ .catch((error) => {
2432
+ this.log.error(`Error getting ${plugin.name} latest version: ${error.message}`);
2433
+ error.stack && this.log.debug(error.stack);
2454
2434
  });
2455
- // Current working directory
2456
- const currentDir = process.cwd();
2457
- this.log.debug(`Current Working Directory: ${currentDir}`);
2458
- // Command line arguments (excluding 'node' and the script name)
2459
- const cmdArgs = process.argv.slice(2).join(' ');
2460
- this.log.debug(`Command Line Arguments: ${cmdArgs}`);
2461
2435
  }
2462
2436
  /**
2463
2437
  * Retrieves an array of base registered plugins.
@@ -2572,14 +2546,14 @@ export class Matterbridge extends EventEmitter {
2572
2546
  if (childProcess.stdout) {
2573
2547
  childProcess.stdout.on('data', (data) => {
2574
2548
  const message = data.toString().trim();
2575
- //this.log.info('\n' + message);
2549
+ // this.log.info('\n' + message);
2576
2550
  this.wssSendMessage('Matterbridge:spawn', 'spawn', message);
2577
2551
  });
2578
2552
  }
2579
2553
  if (childProcess.stderr) {
2580
2554
  childProcess.stderr.on('data', (data) => {
2581
2555
  const message = data.toString().trim();
2582
- //this.log.debug('\n' + message);
2556
+ // this.log.debug('\n' + message);
2583
2557
  this.wssSendMessage('Matterbridge:spawn', 'spawn', message);
2584
2558
  });
2585
2559
  }
@@ -2613,7 +2587,7 @@ export class Matterbridge extends EventEmitter {
2613
2587
  this.log.debug(`Initializing the frontend on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
2614
2588
  const wssPort = 8284;
2615
2589
  const useHttps = false;
2616
- //const wssHost = (useHttps ? 'wss://' : 'ws://') + `${os.hostname().toLowerCase()}:${wssPort}`;
2590
+ // const wssHost = (useHttps ? 'wss://' : 'ws://') + `${os.hostname().toLowerCase()}:${wssPort}`;
2617
2591
  const wssHost = (useHttps ? 'wss://' : 'ws://') + `${this.systemInformation.ipv4Address}:${wssPort}`;
2618
2592
  if (!useHttps) {
2619
2593
  // Create a WebSocket server no certificate required
@@ -2756,7 +2730,7 @@ export class Matterbridge extends EventEmitter {
2756
2730
  Object.entries(clusterServer.attributes).forEach(([key, value]) => {
2757
2731
  if (clusterServer.name === 'EveHistory')
2758
2732
  return;
2759
- //this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
2733
+ // this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
2760
2734
  let attributeValue;
2761
2735
  try {
2762
2736
  if (typeof value.getLocal() === 'object')
@@ -2767,7 +2741,7 @@ export class Matterbridge extends EventEmitter {
2767
2741
  catch (error) {
2768
2742
  attributeValue = 'Unavailable';
2769
2743
  this.log.debug(`GetLocal value ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
2770
- //console.log(error);
2744
+ // console.log(error);
2771
2745
  }
2772
2746
  data.push({
2773
2747
  endpoint: registeredDevice.device.number ? registeredDevice.device.number.toString() : '...',
@@ -2786,7 +2760,7 @@ export class Matterbridge extends EventEmitter {
2786
2760
  Object.entries(clusterServer.attributes).forEach(([key, value]) => {
2787
2761
  if (clusterServer.name === 'EveHistory')
2788
2762
  return;
2789
- //this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
2763
+ // this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
2790
2764
  let attributeValue;
2791
2765
  try {
2792
2766
  if (typeof value.getLocal() === 'object')
@@ -2797,7 +2771,7 @@ export class Matterbridge extends EventEmitter {
2797
2771
  catch (error) {
2798
2772
  attributeValue = 'Unavailable';
2799
2773
  this.log.debug(`GetLocal error ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
2800
- //console.log(error);
2774
+ // console.log(error);
2801
2775
  }
2802
2776
  data.push({
2803
2777
  endpoint: (childEndpoint.number ? childEndpoint.number.toString() : '...') + (name ? ' (' + name + ')' : ''),
@@ -2895,7 +2869,7 @@ export class Matterbridge extends EventEmitter {
2895
2869
  if (command === 'saveconfig') {
2896
2870
  param = param.replace(/\*/g, '\\');
2897
2871
  this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
2898
- //console.log('Req.body:', JSON.stringify(req.body, null, 2));
2872
+ // console.log('Req.body:', JSON.stringify(req.body, null, 2));
2899
2873
  const plugins = await this.nodeContext?.get('plugins');
2900
2874
  if (!plugins)
2901
2875
  return;
@@ -3079,13 +3053,13 @@ export class Matterbridge extends EventEmitter {
3079
3053
  if (composed)
3080
3054
  return 'Composed: ' + composed.value;
3081
3055
  else
3082
- return ''; //'FixedLabel: ' + labelList.map((entry) => entry.label + ': ' + entry.value).join(' ');
3056
+ return ''; // 'FixedLabel: ' + labelList.map((entry) => entry.label + ': ' + entry.value).join(' ');
3083
3057
  };
3084
3058
  let attributes = '';
3085
- //this.log.debug(`getClusterTextFromDevice: ${device.name}`);
3059
+ // this.log.debug(`getClusterTextFromDevice: ${device.name}`);
3086
3060
  const clusterServers = device.getAllClusterServers();
3087
3061
  clusterServers.forEach((clusterServer) => {
3088
- //this.log.debug(`***--clusterServer: ${clusterServer.id} (${clusterServer.name})`);
3062
+ // this.log.debug(`***--clusterServer: ${clusterServer.id} (${clusterServer.name})`);
3089
3063
  if (clusterServer.name === 'OnOff')
3090
3064
  attributes += `OnOff: ${clusterServer.getOnOffAttribute()} `;
3091
3065
  if (clusterServer.name === 'Switch')