matterbridge 1.2.5 → 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.
@@ -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;
@@ -225,6 +234,10 @@ export class Matterbridge extends EventEmitter {
225
234
  if (process.platform === 'win32' && command === 'npm') {
226
235
  command = command + '.cmd';
227
236
  }
237
+ if (process.platform === 'linux' && command === 'npm') {
238
+ args.unshift(command);
239
+ command = 'sudo';
240
+ }
228
241
  return new Promise((resolve, reject) => {
229
242
  const childProcess = spawn(command, args, {
230
243
  stdio: 'inherit',
@@ -297,10 +310,33 @@ export class Matterbridge extends EventEmitter {
297
310
  this.emit('shutdown');
298
311
  process.exit(0);
299
312
  }
300
- // 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)
301
323
  await this.startStorage('json', path.join(this.matterbridgeDirectory, 'matterbridge.json'));
302
- this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
303
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
+ }
304
340
  // Initialize frontend
305
341
  await this.initializeFrontend(getIntParameter('frontend'));
306
342
  if (hasParameter('test')) {
@@ -311,9 +347,7 @@ export class Matterbridge extends EventEmitter {
311
347
  }
312
348
  if (hasParameter('controller')) {
313
349
  this.bridgeMode = 'controller';
314
- this.log.info('Creating mattercontrollerContext: mattercontrollerContext');
315
- this.mattercontrollerContext = this.storageManager?.createContext('mattercontrollerContext');
316
- await this.startMatterbridge();
350
+ await this.startMattercontroller();
317
351
  return;
318
352
  }
319
353
  if (hasParameter('bridge')) {
@@ -464,6 +498,28 @@ export class Matterbridge extends EventEmitter {
464
498
  this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
465
499
  }
466
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
+ }
467
523
  }
468
524
  catch (err) {
469
525
  this.log.error(`Failed to load plugin from ${plg}${packageJsonPath}${er}: ${err}`);
@@ -524,7 +580,7 @@ export class Matterbridge extends EventEmitter {
524
580
  await this.savePluginConfig(plugin);
525
581
  }
526
582
  else {
527
- this.log.warn(`Plugin ${plg}${plugin.name}${er} platform not found`);
583
+ this.log.warn(`Plugin ${plg}${plugin.name}${wr} platform not found`);
528
584
  }
529
585
  }
530
586
  // Set reachability to false
@@ -536,12 +592,34 @@ export class Matterbridge extends EventEmitter {
536
592
  this.log.error(`Plugin ${plg}${registeredDevice.plugin}${er} not found`);
537
593
  return;
538
594
  }
539
- this.log.debug(`*-- device: ${dev}${registeredDevice.device.name}${db} plugin ${plg}${registeredDevice.plugin}${db} type ${GREEN}${plugin.type}${db}`);
540
- if (this.bridgeMode === 'bridge') registeredDevice.device.setBridgedDeviceReachability(false);
541
- if (this.bridgeMode === 'childbridge') plugin.commissioningServer?.setReachability(false);
542
- if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform') this.setReachableAttribute(registeredDevice.device, false);
543
- 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
+ }
544
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
+ }
545
623
  */
546
624
  // Close the express server
547
625
  if (this.expressServer) {
@@ -717,8 +795,10 @@ export class Matterbridge extends EventEmitter {
717
795
  this.log.warn(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) plugin ${plg}${pluginName}${wr} not connected`);
718
796
  return;
719
797
  }
720
- // Register and add the device to matterbridge aggregator in bridge mode
798
+ // Remove the device from matterbridge aggregator in bridge mode
721
799
  if (this.bridgeMode === 'bridge') {
800
+ device.setBridgedDeviceReachability(false);
801
+ device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
722
802
  this.matterAggregator.removeBridgedDevice(device);
723
803
  this.registeredDevices.forEach((registeredDevice, index) => {
724
804
  if (registeredDevice.device === device) {
@@ -735,7 +815,7 @@ export class Matterbridge extends EventEmitter {
735
815
  // Only register the device in childbridge mode
736
816
  if (this.bridgeMode === 'childbridge') {
737
817
  if (plugin.type === 'AccessoryPlatform') {
738
- 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`);
739
819
  }
740
820
  else if (plugin.type === 'DynamicPlatform') {
741
821
  this.registeredDevices.forEach((registeredDevice, index) => {
@@ -744,6 +824,8 @@ export class Matterbridge extends EventEmitter {
744
824
  return;
745
825
  }
746
826
  });
827
+ device.setBridgedDeviceReachability(false);
828
+ device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
747
829
  plugin.aggregator.removeBridgedDevice(device);
748
830
  }
749
831
  this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
@@ -1158,20 +1240,23 @@ export class Matterbridge extends EventEmitter {
1158
1240
  }
1159
1241
  }
1160
1242
  /**
1161
- * Starts the Matterbridge based on the bridge mode.
1162
- * If the bridge mode is 'bridge', it creates a commissioning server, matter aggregator,
1163
- * and starts the matter server.
1164
- * If the bridge mode is 'childbridge', it starts the plugins, creates commissioning servers,
1165
- * and starts the matter server when all plugins are loaded and started.
1243
+ * Starts the Matterbridge controller.
1166
1244
  * @private
1167
1245
  * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1168
1246
  */
1169
- async startMatterbridge() {
1247
+ async startMattercontroller() {
1170
1248
  if (!this.storageManager) {
1171
1249
  this.log.error('No storage manager initialized');
1172
1250
  await this.cleanup('No storage manager initialized');
1173
1251
  return;
1174
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
+ }
1175
1260
  this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1176
1261
  this.createMatterServer(this.storageManager);
1177
1262
  if (!this.matterServer) {
@@ -1179,139 +1264,167 @@ export class Matterbridge extends EventEmitter {
1179
1264
  await this.cleanup('No matter server initialized');
1180
1265
  return;
1181
1266
  }
1182
- if (this.bridgeMode === 'controller') {
1183
- if (!this.mattercontrollerContext) {
1184
- this.log.error('No matter controller context initialized');
1185
- await this.cleanup('No matter controller context initialized');
1186
- 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)}`);
1187
1288
  }
1188
- this.log.info('Creating matter commissioning controller');
1189
- this.commissioningController = new CommissioningController({
1190
- autoConnect: false,
1191
- });
1192
- this.log.info('Adding matter commissioning controller to matter server');
1193
- await this.matterServer.addCommissioningController(this.commissioningController);
1194
- this.log.info('Starting matter server');
1195
- await this.matterServer.start();
1196
- this.log.info('Matter server started');
1197
- if (hasParameter('pairingcode')) {
1198
- const pairingCode = getParameter('pairingcode');
1199
- const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get('ip') : undefined;
1200
- const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get('port') : undefined;
1201
- let longDiscriminator, setupPin, shortDiscriminator;
1202
- if (pairingCode !== undefined) {
1203
- const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
1204
- shortDiscriminator = pairingCodeCodec.shortDiscriminator;
1205
- longDiscriminator = undefined;
1206
- setupPin = pairingCodeCodec.passcode;
1207
- this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
1208
- }
1209
- else {
1210
- longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
1211
- if (longDiscriminator > 4095)
1212
- throw new Error('Discriminator value must be less than 4096');
1213
- setupPin = this.mattercontrollerContext.get('pin', 20202021);
1214
- }
1215
- if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
1216
- throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
1217
- }
1218
- const commissioningOptions = {
1219
- regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
1220
- regulatoryCountryCode: 'XX',
1221
- };
1222
- const options = {
1223
- commissioning: commissioningOptions,
1224
- discovery: {
1225
- knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
1226
- identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
1227
- },
1228
- passcode: setupPin,
1229
- };
1230
- //this.log.info(`Commissioning ... ${JSON.stringify(options)}`);
1231
- this.log.info('Commissioning ...', options);
1232
- const nodeId = await this.commissioningController.commissionNode(options);
1233
- this.mattercontrollerContext.set('nodeId', nodeId.nodeId);
1234
- this.log.info(`Commissioning successfully done with nodeId: ${nodeId.nodeId}`);
1235
- // eslint-disable-next-line no-console
1236
- this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
1237
- } // (hasParameter('pairingcode'))
1238
- if (hasParameter('discover')) {
1239
- //const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1240
- //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');
1241
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...');
1242
1317
  const nodeIds = this.commissioningController.getCommissionedNodes();
1243
- this.log.info(`***Commissioning controller has ${nodeIds.length} nodes commisioned: ${this.commissioningController.isCommissioned()}`);
1244
1318
  for (const nodeId of nodeIds) {
1245
- this.log.info(`***Connecting to commissioned node: ${nodeId}`);
1246
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1247
- const node = await this.commissioningController.connectNode(nodeId, {
1248
- attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) => this.log.info(`***mattributeChangedCallback ${peerNodeId}: Attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
1249
- eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) => this.log.info(`***eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
1250
- stateInformationCallback: (peerNodeId, info) => {
1251
- switch (info) {
1252
- case NodeStateInformation.Connected:
1253
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
1254
- break;
1255
- case NodeStateInformation.Disconnected:
1256
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
1257
- break;
1258
- case NodeStateInformation.Reconnecting:
1259
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
1260
- break;
1261
- case NodeStateInformation.WaitingForDeviceDiscovery:
1262
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
1263
- break;
1264
- case NodeStateInformation.StructureChanged:
1265
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
1266
- break;
1267
- case NodeStateInformation.Decommissioned:
1268
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
1269
- break;
1270
- default:
1271
- this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
1272
- break;
1273
- }
1274
- },
1275
- });
1276
- node.logStructure();
1277
- // Get the interaction client
1278
- const interactionClient = await node.getInteractionClient();
1279
- let cluster;
1280
- let attributes;
1281
- // Log BasicInformationCluster
1282
- cluster = BasicInformationCluster;
1283
- attributes = await interactionClient.getMultipleAttributes({
1284
- attributes: [{ clusterId: cluster.id }],
1285
- });
1286
- this.log.warn(`Cluster: ${cluster.name} attributes:`);
1287
- attributes.forEach((attribute) => {
1288
- this.log.info(
1289
- // eslint-disable-next-line max-len
1290
- `- 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}`);
1291
- });
1292
- // Log PowerSourceCluster
1293
- cluster = PowerSourceCluster;
1294
- attributes = await interactionClient.getMultipleAttributes({
1295
- attributes: [{ clusterId: cluster.id }],
1296
- });
1297
- this.log.warn(`Cluster: ${cluster.name} attributes:`);
1298
- attributes.forEach((attribute) => {
1299
- this.log.info(
1300
- // eslint-disable-next-line max-len
1301
- `- 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}`);
1302
- });
1303
- // Log ThreadNetworkDiagnostics
1304
- cluster = ThreadNetworkDiagnosticsCluster;
1305
- attributes = await interactionClient.getMultipleAttributes({
1306
- attributes: [{ clusterId: cluster.id }],
1307
- });
1308
- this.log.warn(`Cluster: ${cluster.name} attributes:`);
1309
- attributes.forEach((attribute) => {
1310
- this.log.info(
1311
- // eslint-disable-next-line max-len
1312
- `- 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}`);
1313
- });
1319
+ this.log.info('***Commissioning controller unpairing node:', nodeId);
1320
+ await this.commissioningController.removeNode(nodeId);
1314
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;
1315
1428
  }
1316
1429
  if (this.bridgeMode === 'bridge') {
1317
1430
  // Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
@@ -1362,6 +1475,7 @@ export class Matterbridge extends EventEmitter {
1362
1475
  await this.startMatterServer();
1363
1476
  this.log.info('Matter server started');
1364
1477
  await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
1478
+ //if (hasParameter('advertise')) await this.commissioningServer.advertise();
1365
1479
  /*
1366
1480
  setInterval(() => {
1367
1481
  this.matterAggregator?.getBridgedDevices().forEach((device) => {
@@ -1445,6 +1559,7 @@ export class Matterbridge extends EventEmitter {
1445
1559
  });
1446
1560
  if (!allStarted)
1447
1561
  return;
1562
+ clearInterval(startMatterInterval);
1448
1563
  this.log.info('Starting matter server...');
1449
1564
  // Setting reachability to true
1450
1565
  this.registeredPlugins.forEach((plugin) => {
@@ -1481,7 +1596,7 @@ export class Matterbridge extends EventEmitter {
1481
1596
  await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
1482
1597
  }
1483
1598
  Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
1484
- clearInterval(startMatterInterval);
1599
+ //clearInterval(startMatterInterval);
1485
1600
  }, 1000);
1486
1601
  }
1487
1602
  }
@@ -1555,9 +1670,9 @@ export class Matterbridge extends EventEmitter {
1555
1670
  await storageContext.set('softwareVersionString', softwareVersionString ?? '1.0.0');
1556
1671
  await storageContext.set('hardwareVersion', hardwareVersion ?? 1);
1557
1672
  await storageContext.set('hardwareVersionString', hardwareVersionString ?? '1.0.0');
1558
- this.log.debug(`**Created commissioning server storage context for ${plg}${pluginName}${db}`);
1559
- this.log.debug(`**- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1560
- 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')}`);
1561
1676
  return storageContext;
1562
1677
  }
1563
1678
  /**
@@ -1572,7 +1687,7 @@ export class Matterbridge extends EventEmitter {
1572
1687
  if (!commissioningServer || !storageContext || !pluginName)
1573
1688
  return;
1574
1689
  if (!commissioningServer.isCommissioned()) {
1575
- 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 ...`);
1576
1691
  const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
1577
1692
  storageContext.set('qrPairingCode', qrPairingCode);
1578
1693
  storageContext.set('manualPairingCode', manualPairingCode);
@@ -1591,7 +1706,7 @@ export class Matterbridge extends EventEmitter {
1591
1706
  await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
1592
1707
  }
1593
1708
  else {
1594
- 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 ...`);
1595
1710
  if (pluginName !== 'Matterbridge') {
1596
1711
  const plugin = this.findPlugin(pluginName);
1597
1712
  if (plugin) {
@@ -1641,7 +1756,7 @@ export class Matterbridge extends EventEmitter {
1641
1756
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
1642
1757
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with hardwareVersion ${hardwareVersion} hardwareVersionString ${hardwareVersionString}`);
1643
1758
  const commissioningServer = new CommissioningServer({
1644
- port: undefined,
1759
+ port: this.port++,
1645
1760
  passcode: undefined,
1646
1761
  discriminator: undefined,
1647
1762
  deviceName,
@@ -1728,9 +1843,7 @@ export class Matterbridge extends EventEmitter {
1728
1843
  this.log.warn(`***Commissioning removed from fabric ${fabricIndex} for ${plg}${pluginName}${nf}. Resetting the commissioning server ...`);
1729
1844
  await commissioningServer.factoryReset();
1730
1845
  if (pluginName === 'Matterbridge') {
1731
- this.matterbridgeContext?.delete(`${pluginName}.EndpointStructure`);
1732
- this.matterbridgeContext?.delete(`${pluginName}.EventHandler`);
1733
- this.matterbridgeContext?.delete(`${pluginName}.SessionManager`);
1846
+ await this.matterbridgeContext?.clearAll();
1734
1847
  }
1735
1848
  else {
1736
1849
  for (const plugin of this.registeredPlugins) {
@@ -1738,9 +1851,7 @@ export class Matterbridge extends EventEmitter {
1738
1851
  await plugin.platform?.onShutdown('Commissioning removed by the controller');
1739
1852
  plugin.paired = false;
1740
1853
  plugin.connected = false;
1741
- plugin.storageContext?.delete(`${pluginName}.EndpointStructure`);
1742
- plugin.storageContext?.delete(`${pluginName}.EventHandler`);
1743
- plugin.storageContext?.delete(`${pluginName}.SessionManager`);
1854
+ await plugin.storageContext?.clearAll();
1744
1855
  }
1745
1856
  }
1746
1857
  }