matterbridge 1.2.5 → 1.2.7

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 (43) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +30 -5
  3. package/TODO.md +0 -4
  4. package/dist/AirQualityCluster.d.ts +2 -13
  5. package/dist/AirQualityCluster.d.ts.map +1 -1
  6. package/dist/AirQualityCluster.js +6 -6
  7. package/dist/AirQualityCluster.js.map +1 -1
  8. package/dist/EveHistoryCluster.d.ts +6 -26
  9. package/dist/EveHistoryCluster.d.ts.map +1 -1
  10. package/dist/EveHistoryCluster.js +6 -14
  11. package/dist/EveHistoryCluster.js.map +1 -1
  12. package/dist/TvocCluster.d.ts +2 -15
  13. package/dist/TvocCluster.d.ts.map +1 -1
  14. package/dist/TvocCluster.js +6 -6
  15. package/dist/TvocCluster.js.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/matterbridge.d.ts +7 -0
  21. package/dist/matterbridge.d.ts.map +1 -1
  22. package/dist/matterbridge.js +334 -198
  23. package/dist/matterbridge.js.map +1 -1
  24. package/dist/matterbridgeDevice.d.ts +37 -12
  25. package/dist/matterbridgeDevice.d.ts.map +1 -1
  26. package/dist/matterbridgeDevice.js +100 -18
  27. package/dist/matterbridgeDevice.js.map +1 -1
  28. package/dist/utils.d.ts +52 -1
  29. package/dist/utils.d.ts.map +1 -1
  30. package/dist/utils.js +121 -1
  31. package/dist/utils.js.map +1 -1
  32. package/frontend/build/asset-manifest.json +3 -3
  33. package/frontend/build/index.html +1 -1
  34. package/frontend/build/static/js/{main.6b861489.js → main.491fc08f.js} +3 -3
  35. package/frontend/build/static/js/main.491fc08f.js.map +1 -0
  36. package/matterbridge.service +5 -6
  37. package/package.json +2 -2
  38. package/dist/matterbridgeComposed.d.ts +0 -43
  39. package/dist/matterbridgeComposed.d.ts.map +0 -1
  40. package/dist/matterbridgeComposed.js +0 -58
  41. package/dist/matterbridgeComposed.js.map +0 -1
  42. package/frontend/build/static/js/main.6b861489.js.map +0 -1
  43. /package/frontend/build/static/js/{main.6b861489.js.LICENSE.txt → main.491fc08f.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 } from 'node-ansi-logger';
25
+ import { AnsiLogger, BRIGHT, RESET, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, er, nf, rs, wr, RED, GREEN } 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';
@@ -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;
@@ -190,25 +199,6 @@ export class Matterbridge extends EventEmitter {
190
199
  // Set matter.js logger level and format
191
200
  Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
192
201
  Logger.format = Format.ANSI;
193
- // Initialize NodeStorage
194
- /*
195
- this.log.debug('Creating node storage manager');
196
- this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'storage'), logging: false });
197
- this.log.debug('Creating node storage context for matterbridge');
198
- this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
199
- // Get the plugins from node storage
200
- this.registeredPlugins = await this.nodeContext.get<RegisteredPlugin[]>('plugins', []);
201
- for (const plugin of this.registeredPlugins) {
202
- this.log.debug(`Creating node storage context for plugin ${plugin.name}`);
203
- plugin.nodeContext = await this.nodeStorage.createStorage(plugin.name);
204
- await plugin.nodeContext.set<string>('name', plugin.name);
205
- await plugin.nodeContext.set<string>('type', plugin.type);
206
- await plugin.nodeContext.set<string>('path', plugin.path);
207
- await plugin.nodeContext.set<string>('version', plugin.version);
208
- await plugin.nodeContext.set<string>('description', plugin.description);
209
- await plugin.nodeContext.set<string>('author', plugin.author);
210
- }
211
- */
212
202
  // Parse command line
213
203
  this.parseCommandLine();
214
204
  }
@@ -225,9 +215,13 @@ export class Matterbridge extends EventEmitter {
225
215
  if (process.platform === 'win32' && command === 'npm') {
226
216
  command = command + '.cmd';
227
217
  }
218
+ if (process.platform === 'linux' && command === 'npm' && !hasParameter('docker')) {
219
+ args.unshift(command);
220
+ command = 'sudo';
221
+ }
228
222
  return new Promise((resolve, reject) => {
229
223
  const childProcess = spawn(command, args, {
230
- stdio: 'inherit',
224
+ stdio: ['inherit', 'pipe', 'pipe'],
231
225
  });
232
226
  childProcess.on('error', (err) => {
233
227
  this.log.error(`Failed to start child process: ${err.message}`);
@@ -250,6 +244,22 @@ export class Matterbridge extends EventEmitter {
250
244
  childProcess.on('disconnect', () => {
251
245
  this.log.info('Child process has been disconnected from the parent');
252
246
  });
247
+ if (childProcess.stdout) {
248
+ childProcess.stdout.on('data', (data) => {
249
+ // Convert the Buffer data to a string.
250
+ const message = data.toString();
251
+ this.log.info(message);
252
+ // TODO: Send this message to the frontend.
253
+ });
254
+ }
255
+ if (childProcess.stderr) {
256
+ childProcess.stderr.on('data', (data) => {
257
+ // Convert the Buffer data to a string.
258
+ const message = data.toString();
259
+ this.log.error(message);
260
+ // TODO: Handle the error message.
261
+ });
262
+ }
253
263
  });
254
264
  }
255
265
  /**
@@ -259,15 +269,27 @@ export class Matterbridge extends EventEmitter {
259
269
  */
260
270
  async parseCommandLine() {
261
271
  if (hasParameter('list')) {
262
- this.log.info('Registered plugins:');
272
+ this.log.info('Registered plugins');
263
273
  this.registeredPlugins.forEach((plugin, index) => {
264
- if (index === this.registeredPlugins.length - 1) {
265
- this.log.info(`└─┬─ ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${YELLOW}${plugin.enabled ? 'enabled' : 'disabled'}${nf}`);
266
- this.log.info(` └─ ${db}${plugin.path}${db}`);
274
+ if (index !== this.registeredPlugins.length - 1) {
275
+ this.log.info(`├─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${plugin.enabled ? GREEN : RED}enabled ${plugin.paired ? GREEN : RED}paired${nf}`);
276
+ this.log.info(`│ └─ entry ${UNDERLINE}${db}${plugin.path}${UNDERLINEOFF}${db}`);
267
277
  }
268
278
  else {
269
- this.log.info(`├─┬─ ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${YELLOW}${plugin.enabled ? 'enabled' : 'disabled'}${nf}`);
270
- this.log.info(`│ └─ ${db}${plugin.path}${db}`);
279
+ this.log.info(`└─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${plugin.enabled ? GREEN : RED}enabled ${plugin.paired ? GREEN : RED}paired${nf}`);
280
+ this.log.info(` └─ entry ${UNDERLINE}${db}${plugin.path}${UNDERLINEOFF}${db}`);
281
+ }
282
+ });
283
+ const serializedRegisteredDevices = await this.nodeContext?.get('devices', []);
284
+ this.log.info('│ Registered devices');
285
+ serializedRegisteredDevices?.forEach((device, index) => {
286
+ if (index !== serializedRegisteredDevices.length - 1) {
287
+ this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
288
+ this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
289
+ }
290
+ else {
291
+ this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
292
+ this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
271
293
  }
272
294
  });
273
295
  this.emit('shutdown');
@@ -297,10 +319,33 @@ export class Matterbridge extends EventEmitter {
297
319
  this.emit('shutdown');
298
320
  process.exit(0);
299
321
  }
300
- // Start the storage (we need it now for frontend and later for matterbridge)
322
+ if (hasParameter('factoryreset')) {
323
+ // Delete matter storage file
324
+ await fs.unlink(path.join(this.matterbridgeDirectory, 'matterbridge.json'));
325
+ // Delete node storage directory with its subdirectories
326
+ await fs.rmdir(path.join(this.matterbridgeDirectory, 'storage'), { recursive: true });
327
+ this.log.info('Factory reset done! Remove all paired devices from the controllers.');
328
+ this.emit('shutdown');
329
+ process.exit(0);
330
+ }
331
+ // Start the storage and create matterbridgeContext (we need it now for frontend and later for matterbridge)
301
332
  await this.startStorage('json', path.join(this.matterbridgeDirectory, 'matterbridge.json'));
302
- this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
303
333
  this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
334
+ if (hasParameter('reset') && getParameter('reset') === undefined) {
335
+ this.log.info('Resetting Matterbridge commissioning information...');
336
+ await this.matterbridgeContext?.clearAll();
337
+ await this.stopStorage();
338
+ this.log.info('Reset done! Remove the device from the controller.');
339
+ this.emit('shutdown');
340
+ process.exit(0);
341
+ }
342
+ if (getParameter('reset') && getParameter('reset') !== undefined) {
343
+ this.log.debug(`Reset plugin ${getParameter('reset')}`);
344
+ await this.executeCommandLine(getParameter('reset'), 'reset');
345
+ await this.stopStorage();
346
+ this.emit('shutdown');
347
+ process.exit(0);
348
+ }
304
349
  // Initialize frontend
305
350
  await this.initializeFrontend(getIntParameter('frontend'));
306
351
  if (hasParameter('test')) {
@@ -311,9 +356,7 @@ export class Matterbridge extends EventEmitter {
311
356
  }
312
357
  if (hasParameter('controller')) {
313
358
  this.bridgeMode = 'controller';
314
- this.log.info('Creating mattercontrollerContext: mattercontrollerContext');
315
- this.mattercontrollerContext = this.storageManager?.createContext('mattercontrollerContext');
316
- await this.startMatterbridge();
359
+ await this.startMattercontroller();
317
360
  return;
318
361
  }
319
362
  if (hasParameter('bridge')) {
@@ -464,6 +507,28 @@ export class Matterbridge extends EventEmitter {
464
507
  this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
465
508
  }
466
509
  }
510
+ else if (mode === 'reset') {
511
+ const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
512
+ if (plugin) {
513
+ plugin.loaded = undefined;
514
+ plugin.started = undefined;
515
+ plugin.configured = undefined;
516
+ plugin.connected = undefined;
517
+ plugin.paired = undefined;
518
+ plugin.connected = undefined;
519
+ if (!this.storageManager)
520
+ this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
521
+ const context = this.storageManager?.createContext(plugin.name);
522
+ if (!context)
523
+ this.log.error(`Plugin ${plg}${plugin.name}${er} context not found`);
524
+ await context?.clearAll();
525
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
526
+ this.log.info(`Reset commissionig for plugin ${plg}${plugin.name}${nf} done! Remove the device from the controller.`);
527
+ }
528
+ else {
529
+ this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
530
+ }
531
+ }
467
532
  }
468
533
  catch (err) {
469
534
  this.log.error(`Failed to load plugin from ${plg}${packageJsonPath}${er}: ${err}`);
@@ -524,7 +589,7 @@ export class Matterbridge extends EventEmitter {
524
589
  await this.savePluginConfig(plugin);
525
590
  }
526
591
  else {
527
- this.log.warn(`Plugin ${plg}${plugin.name}${er} platform not found`);
592
+ this.log.warn(`Plugin ${plg}${plugin.name}${wr} platform not found`);
528
593
  }
529
594
  }
530
595
  // Set reachability to false
@@ -536,12 +601,34 @@ export class Matterbridge extends EventEmitter {
536
601
  this.log.error(`Plugin ${plg}${registeredDevice.plugin}${er} not found`);
537
602
  return;
538
603
  }
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);
604
+ if (this.bridgeMode === 'bridge' && registeredDevice.device.number) {
605
+ this.log.debug(`*-- device: ${dev}${registeredDevice.device.name}${db} plugin ${plg}${registeredDevice.plugin}${db} type ${plugin.type}${db}`);
606
+ registeredDevice.device.setBridgedDeviceReachability(false);
607
+ registeredDevice.device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
608
+ }
609
+ if (this.bridgeMode === 'childbridge') {
610
+ if (plugin.type === 'DynamicPlatform' && registeredDevice.device.number) {
611
+ this.log.debug(`*-- device: ${dev}${registeredDevice.device.name}${db} plugin ${plg}${registeredDevice.plugin}${db} type ${plugin.type}${db}`);
612
+ registeredDevice.device.setBridgedDeviceReachability(false);
613
+ registeredDevice.device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
614
+ }
615
+ }
544
616
  });
617
+ if (this.bridgeMode === 'bridge') {
618
+ this.log.debug('*Changing reachability to false for Matterbridge');
619
+ this.matterAggregator?.getClusterServerById(BasicInformation.Cluster.id)?.setReachableAttribute(false);
620
+ this.matterAggregator?.getClusterServerById(BasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
621
+ this.commissioningServer?.setReachability(false);
622
+ }
623
+ if (this.bridgeMode === 'childbridge') {
624
+ for (const plugin of this.registeredPlugins) {
625
+ if (!plugin.enabled || plugin.error) continue;
626
+ this.log.debug(`*Changing reachability to false for plugin ${plg}${plugin.name}${db} type ${plugin.type}`);
627
+ plugin.aggregator?.getClusterServerById(BasicInformation.Cluster.id)?.setReachableAttribute(false);
628
+ plugin.aggregator?.getClusterServerById(BasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
629
+ plugin.commissioningServer?.setReachability(false);
630
+ }
631
+ }
545
632
  */
546
633
  // Close the express server
547
634
  if (this.expressServer) {
@@ -563,7 +650,9 @@ export class Matterbridge extends EventEmitter {
563
650
  this.log.info('Saving registered devices...');
564
651
  const serializedRegisteredDevices = [];
565
652
  this.registeredDevices.forEach((registeredDevice) => {
566
- serializedRegisteredDevices.push(registeredDevice.device.serialize(registeredDevice.plugin));
653
+ const serializedMatterbridgeDevice = registeredDevice.device.serialize(registeredDevice.plugin);
654
+ //this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
655
+ serializedRegisteredDevices.push(serializedMatterbridgeDevice);
567
656
  });
568
657
  await this.nodeContext.set('devices', serializedRegisteredDevices);
569
658
  this.log.info('Saved registered devices');
@@ -717,8 +806,10 @@ export class Matterbridge extends EventEmitter {
717
806
  this.log.warn(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) plugin ${plg}${pluginName}${wr} not connected`);
718
807
  return;
719
808
  }
720
- // Register and add the device to matterbridge aggregator in bridge mode
809
+ // Remove the device from matterbridge aggregator in bridge mode
721
810
  if (this.bridgeMode === 'bridge') {
811
+ device.setBridgedDeviceReachability(false);
812
+ device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
722
813
  this.matterAggregator.removeBridgedDevice(device);
723
814
  this.registeredDevices.forEach((registeredDevice, index) => {
724
815
  if (registeredDevice.device === device) {
@@ -732,10 +823,10 @@ export class Matterbridge extends EventEmitter {
732
823
  if (plugin.addedDevices !== undefined)
733
824
  plugin.addedDevices--;
734
825
  }
735
- // Only register the device in childbridge mode
826
+ // Remove the device in childbridge mode
736
827
  if (this.bridgeMode === 'childbridge') {
737
828
  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`);
829
+ this.log.info(`Removing bridged device ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}: AccessoryPlatform not supported in childbridge mode`);
739
830
  }
740
831
  else if (plugin.type === 'DynamicPlatform') {
741
832
  this.registeredDevices.forEach((registeredDevice, index) => {
@@ -744,6 +835,8 @@ export class Matterbridge extends EventEmitter {
744
835
  return;
745
836
  }
746
837
  });
838
+ device.setBridgedDeviceReachability(false);
839
+ device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
747
840
  plugin.aggregator.removeBridgedDevice(device);
748
841
  }
749
842
  this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
@@ -762,7 +855,7 @@ export class Matterbridge extends EventEmitter {
762
855
  async removeAllBridgedDevices(pluginName) {
763
856
  const plugin = this.findPlugin(pluginName);
764
857
  if (this.bridgeMode === 'childbridge' && plugin?.type === 'AccessoryPlatform') {
765
- this.log.warn(`Removing devices for plugin ${plg}${pluginName}${wr} error: AccessoryPlatform not supported in childbridge mode`);
858
+ this.log.info(`Removing devices for plugin ${plg}${pluginName}${nf}: AccessoryPlatform not supported in childbridge mode`);
766
859
  return;
767
860
  }
768
861
  const devicesToRemove = [];
@@ -1158,20 +1251,23 @@ export class Matterbridge extends EventEmitter {
1158
1251
  }
1159
1252
  }
1160
1253
  /**
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.
1254
+ * Starts the Matterbridge controller.
1166
1255
  * @private
1167
1256
  * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1168
1257
  */
1169
- async startMatterbridge() {
1258
+ async startMattercontroller() {
1170
1259
  if (!this.storageManager) {
1171
1260
  this.log.error('No storage manager initialized');
1172
1261
  await this.cleanup('No storage manager initialized');
1173
1262
  return;
1174
1263
  }
1264
+ this.log.info('Creating context: mattercontrollerContext');
1265
+ this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
1266
+ if (!this.mattercontrollerContext) {
1267
+ this.log.error('No storage context mattercontrollerContext initialized');
1268
+ await this.cleanup('No storage context mattercontrollerContext initialized');
1269
+ return;
1270
+ }
1175
1271
  this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1176
1272
  this.createMatterServer(this.storageManager);
1177
1273
  if (!this.matterServer) {
@@ -1179,139 +1275,167 @@ export class Matterbridge extends EventEmitter {
1179
1275
  await this.cleanup('No matter server initialized');
1180
1276
  return;
1181
1277
  }
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;
1278
+ this.log.info('Creating matter commissioning controller');
1279
+ this.commissioningController = new CommissioningController({
1280
+ autoConnect: false,
1281
+ });
1282
+ this.log.info('Adding matter commissioning controller to matter server');
1283
+ await this.matterServer.addCommissioningController(this.commissioningController);
1284
+ this.log.info('Starting matter server');
1285
+ await this.matterServer.start();
1286
+ this.log.info('Matter server started');
1287
+ if (hasParameter('pairingcode')) {
1288
+ this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
1289
+ const pairingCode = getParameter('pairingcode');
1290
+ const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get('ip') : undefined;
1291
+ const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get('port') : undefined;
1292
+ let longDiscriminator, setupPin, shortDiscriminator;
1293
+ if (pairingCode !== undefined) {
1294
+ const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
1295
+ shortDiscriminator = pairingCodeCodec.shortDiscriminator;
1296
+ longDiscriminator = undefined;
1297
+ setupPin = pairingCodeCodec.passcode;
1298
+ this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
1187
1299
  }
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);
1300
+ else {
1301
+ longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
1302
+ if (longDiscriminator > 4095)
1303
+ throw new Error('Discriminator value must be less than 4096');
1304
+ setupPin = this.mattercontrollerContext.get('pin', 20202021);
1305
+ }
1306
+ if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
1307
+ throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
1241
1308
  }
1309
+ const commissioningOptions = {
1310
+ regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
1311
+ regulatoryCountryCode: 'XX',
1312
+ };
1313
+ const options = {
1314
+ commissioning: commissioningOptions,
1315
+ discovery: {
1316
+ knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
1317
+ identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
1318
+ },
1319
+ passcode: setupPin,
1320
+ };
1321
+ this.log.info('Commissioning with options:', options);
1322
+ const nodeId = await this.commissioningController.commissionNode(options);
1323
+ this.log.info(`Commissioning successfully done with nodeId: ${nodeId.nodeId}`);
1324
+ this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
1325
+ } // (hasParameter('pairingcode'))
1326
+ if (hasParameter('unpairall')) {
1327
+ this.log.info('***Commissioning controller unpairing all nodes...');
1242
1328
  const nodeIds = this.commissioningController.getCommissionedNodes();
1243
- this.log.info(`***Commissioning controller has ${nodeIds.length} nodes commisioned: ${this.commissioningController.isCommissioned()}`);
1244
1329
  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
- });
1330
+ this.log.info('***Commissioning controller unpairing node:', nodeId);
1331
+ await this.commissioningController.removeNode(nodeId);
1314
1332
  }
1333
+ return;
1334
+ }
1335
+ if (hasParameter('discover')) {
1336
+ //const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1337
+ //console.log(discover);
1338
+ }
1339
+ if (!this.commissioningController.isCommissioned()) {
1340
+ this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
1341
+ return;
1342
+ }
1343
+ const nodeIds = this.commissioningController.getCommissionedNodes();
1344
+ this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
1345
+ for (const nodeId of nodeIds) {
1346
+ this.log.info(`***Connecting to commissioned node: ${nodeId}`);
1347
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1348
+ const node = await this.commissioningController.connectNode(nodeId, {
1349
+ 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)}`),
1350
+ 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)}`),
1351
+ stateInformationCallback: (peerNodeId, info) => {
1352
+ switch (info) {
1353
+ case NodeStateInformation.Connected:
1354
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
1355
+ break;
1356
+ case NodeStateInformation.Disconnected:
1357
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
1358
+ break;
1359
+ case NodeStateInformation.Reconnecting:
1360
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
1361
+ break;
1362
+ case NodeStateInformation.WaitingForDeviceDiscovery:
1363
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
1364
+ break;
1365
+ case NodeStateInformation.StructureChanged:
1366
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
1367
+ break;
1368
+ case NodeStateInformation.Decommissioned:
1369
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
1370
+ break;
1371
+ default:
1372
+ this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
1373
+ break;
1374
+ }
1375
+ },
1376
+ });
1377
+ //node.logStructure();
1378
+ // Get the interaction client
1379
+ this.log.info('Getting the interaction client');
1380
+ const interactionClient = await node.getInteractionClient();
1381
+ let cluster;
1382
+ let attributes;
1383
+ // Log BasicInformationCluster
1384
+ cluster = BasicInformationCluster;
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 PowerSourceCluster
1395
+ cluster = PowerSourceCluster;
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
+ // Log ThreadNetworkDiagnostics
1406
+ cluster = ThreadNetworkDiagnosticsCluster;
1407
+ attributes = await interactionClient.getMultipleAttributes({
1408
+ attributes: [{ clusterId: cluster.id }],
1409
+ });
1410
+ this.log.warn(`Cluster: ${cluster.name} attributes:`);
1411
+ attributes.forEach((attribute) => {
1412
+ this.log.info(
1413
+ // eslint-disable-next-line max-len
1414
+ `- 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}`);
1415
+ });
1416
+ }
1417
+ }
1418
+ /**
1419
+ * Starts the Matterbridge based on the bridge mode.
1420
+ * If the bridge mode is 'bridge', it creates a commissioning server, matter aggregator,
1421
+ * and starts the matter server.
1422
+ * If the bridge mode is 'childbridge', it starts the plugins, creates commissioning servers,
1423
+ * and starts the matter server when all plugins are loaded and started.
1424
+ * @private
1425
+ * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1426
+ */
1427
+ async startMatterbridge() {
1428
+ if (!this.storageManager) {
1429
+ this.log.error('No storage manager initialized');
1430
+ await this.cleanup('No storage manager initialized');
1431
+ return;
1432
+ }
1433
+ this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1434
+ this.createMatterServer(this.storageManager);
1435
+ if (!this.matterServer) {
1436
+ this.log.error('No matter server initialized');
1437
+ await this.cleanup('No matter server initialized');
1438
+ return;
1315
1439
  }
1316
1440
  if (this.bridgeMode === 'bridge') {
1317
1441
  // Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
@@ -1362,6 +1486,7 @@ export class Matterbridge extends EventEmitter {
1362
1486
  await this.startMatterServer();
1363
1487
  this.log.info('Matter server started');
1364
1488
  await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
1489
+ //if (hasParameter('advertise')) await this.commissioningServer.advertise();
1365
1490
  /*
1366
1491
  setInterval(() => {
1367
1492
  this.matterAggregator?.getBridgedDevices().forEach((device) => {
@@ -1445,6 +1570,7 @@ export class Matterbridge extends EventEmitter {
1445
1570
  });
1446
1571
  if (!allStarted)
1447
1572
  return;
1573
+ clearInterval(startMatterInterval);
1448
1574
  this.log.info('Starting matter server...');
1449
1575
  // Setting reachability to true
1450
1576
  this.registeredPlugins.forEach((plugin) => {
@@ -1481,7 +1607,7 @@ export class Matterbridge extends EventEmitter {
1481
1607
  await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
1482
1608
  }
1483
1609
  Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
1484
- clearInterval(startMatterInterval);
1610
+ //clearInterval(startMatterInterval);
1485
1611
  }, 1000);
1486
1612
  }
1487
1613
  }
@@ -1555,9 +1681,9 @@ export class Matterbridge extends EventEmitter {
1555
1681
  await storageContext.set('softwareVersionString', softwareVersionString ?? '1.0.0');
1556
1682
  await storageContext.set('hardwareVersion', hardwareVersion ?? 1);
1557
1683
  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')}`);
1684
+ this.log.debug(`Created commissioning server storage context for ${plg}${pluginName}${db}`);
1685
+ this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1686
+ this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1561
1687
  return storageContext;
1562
1688
  }
1563
1689
  /**
@@ -1572,7 +1698,7 @@ export class Matterbridge extends EventEmitter {
1572
1698
  if (!commissioningServer || !storageContext || !pluginName)
1573
1699
  return;
1574
1700
  if (!commissioningServer.isCommissioned()) {
1575
- this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code ...`);
1701
+ this.log.info(`***The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code ...`);
1576
1702
  const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
1577
1703
  storageContext.set('qrPairingCode', qrPairingCode);
1578
1704
  storageContext.set('manualPairingCode', manualPairingCode);
@@ -1591,7 +1717,7 @@ export class Matterbridge extends EventEmitter {
1591
1717
  await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
1592
1718
  }
1593
1719
  else {
1594
- this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is already commissioned. Waiting for controllers to connect ...`);
1720
+ this.log.info(`***The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is already commissioned . Waiting for controllers to connect ...`);
1595
1721
  if (pluginName !== 'Matterbridge') {
1596
1722
  const plugin = this.findPlugin(pluginName);
1597
1723
  if (plugin) {
@@ -1641,7 +1767,7 @@ export class Matterbridge extends EventEmitter {
1641
1767
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
1642
1768
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with hardwareVersion ${hardwareVersion} hardwareVersionString ${hardwareVersionString}`);
1643
1769
  const commissioningServer = new CommissioningServer({
1644
- port: undefined,
1770
+ port: this.port++,
1645
1771
  passcode: undefined,
1646
1772
  discriminator: undefined,
1647
1773
  deviceName,
@@ -1728,9 +1854,7 @@ export class Matterbridge extends EventEmitter {
1728
1854
  this.log.warn(`***Commissioning removed from fabric ${fabricIndex} for ${plg}${pluginName}${nf}. Resetting the commissioning server ...`);
1729
1855
  await commissioningServer.factoryReset();
1730
1856
  if (pluginName === 'Matterbridge') {
1731
- this.matterbridgeContext?.delete(`${pluginName}.EndpointStructure`);
1732
- this.matterbridgeContext?.delete(`${pluginName}.EventHandler`);
1733
- this.matterbridgeContext?.delete(`${pluginName}.SessionManager`);
1857
+ await this.matterbridgeContext?.clearAll();
1734
1858
  }
1735
1859
  else {
1736
1860
  for (const plugin of this.registeredPlugins) {
@@ -1738,9 +1862,7 @@ export class Matterbridge extends EventEmitter {
1738
1862
  await plugin.platform?.onShutdown('Commissioning removed by the controller');
1739
1863
  plugin.paired = false;
1740
1864
  plugin.connected = false;
1741
- plugin.storageContext?.delete(`${pluginName}.EndpointStructure`);
1742
- plugin.storageContext?.delete(`${pluginName}.EventHandler`);
1743
- plugin.storageContext?.delete(`${pluginName}.SessionManager`);
1865
+ await plugin.storageContext?.clearAll();
1744
1866
  }
1745
1867
  }
1746
1868
  }
@@ -2057,7 +2179,7 @@ export class Matterbridge extends EventEmitter {
2057
2179
  *
2058
2180
  * @param port The port number to run the frontend server on. Default is 3000.
2059
2181
  */
2060
- async initializeFrontend(port = 3000) {
2182
+ async initializeFrontend(port = 8283) {
2061
2183
  this.log.debug(`Initializing the frontend on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
2062
2184
  this.expressApp = express();
2063
2185
  // Serve React build directory
@@ -2082,6 +2204,24 @@ export class Matterbridge extends EventEmitter {
2082
2204
  res.json({ valid: false });
2083
2205
  }
2084
2206
  });
2207
+ // Endpoint to provide manual pairing code
2208
+ this.expressApp.get('/api/pairing-code', (req, res) => {
2209
+ this.log.debug('The frontend sent /api/pairing-code');
2210
+ if (!this.matterbridgeContext) {
2211
+ this.log.error('/api/pairing-code matterbridgeContext not found');
2212
+ res.json([]);
2213
+ return;
2214
+ }
2215
+ try {
2216
+ const qrData = { qrPairingCode: this.matterbridgeContext.get('qrPairingCode'), manualPairingCode: this.matterbridgeContext.get('manualPairingCode') };
2217
+ res.json(qrData);
2218
+ }
2219
+ catch (error) {
2220
+ if (this.bridgeMode === 'bridge')
2221
+ this.log.error('qrPairingCode for /api/qr-code not found');
2222
+ res.json({});
2223
+ }
2224
+ });
2085
2225
  // Endpoint to provide QR pairing code
2086
2226
  this.expressApp.get('/api/qr-code', (req, res) => {
2087
2227
  this.log.debug('The frontend sent /api/qr-code');
@@ -2196,7 +2336,7 @@ export class Matterbridge extends EventEmitter {
2196
2336
  res.status(400).json({ error: 'No command provided' });
2197
2337
  return;
2198
2338
  }
2199
- this.log.debug(`*Received frontend command: ${command}:${param}`);
2339
+ this.log.info(`Received frontend command: ${command}:${param}`);
2200
2340
  // Handle the command setpassword from Settings
2201
2341
  if (command === 'setpassword') {
2202
2342
  const password = param.slice(1, -1); // Remove the first and last characters
@@ -2246,10 +2386,6 @@ export class Matterbridge extends EventEmitter {
2246
2386
  }
2247
2387
  this.updateProcess();
2248
2388
  }
2249
- // Handle the command update from Header
2250
- if (command === 'update') {
2251
- this.log.warn(`The /api/command/${command} is not yet implemented`);
2252
- }
2253
2389
  // Handle the command installplugin from Home
2254
2390
  if (command === 'installplugin') {
2255
2391
  param = param.replace(/\*/g, '\\');