matterbridge 1.4.3 → 1.5.1

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 (40) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README-ADVANCED.md +197 -0
  3. package/README-DEV.md +167 -0
  4. package/README.md +26 -318
  5. package/dist/cluster/export.d.ts +23 -0
  6. package/dist/cluster/export.d.ts.map +1 -0
  7. package/dist/cluster/export.js +23 -0
  8. package/dist/cluster/export.js.map +1 -0
  9. package/dist/index.d.ts +0 -3
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +2 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/matterbridge.d.ts +4 -1
  14. package/dist/matterbridge.d.ts.map +1 -1
  15. package/dist/matterbridge.js +149 -100
  16. package/dist/matterbridge.js.map +1 -1
  17. package/dist/matterbridgeDevice.d.ts +190 -114
  18. package/dist/matterbridgeDevice.d.ts.map +1 -1
  19. package/dist/matterbridgeDevice.js +379 -189
  20. package/dist/matterbridgeDevice.js.map +1 -1
  21. package/dist/matterbridgeTypes.d.ts +2 -0
  22. package/dist/matterbridgeTypes.d.ts.map +1 -1
  23. package/dist/pluginManager.d.ts.map +1 -1
  24. package/dist/pluginManager.js +13 -3
  25. package/dist/pluginManager.js.map +1 -1
  26. package/dist/utils/utils.d.ts +22 -3
  27. package/dist/utils/utils.d.ts.map +1 -1
  28. package/dist/utils/utils.js +106 -42
  29. package/dist/utils/utils.js.map +1 -1
  30. package/frontend/build/asset-manifest.json +5 -5
  31. package/frontend/build/index.html +1 -1
  32. package/frontend/build/static/js/453.abd36b29.chunk.js +2 -0
  33. package/frontend/build/static/js/{453.d855a71b.chunk.js.map → 453.abd36b29.chunk.js.map} +1 -1
  34. package/frontend/build/static/js/main.4c5271fd.js +3 -0
  35. package/frontend/build/static/js/main.4c5271fd.js.map +1 -0
  36. package/package.json +16 -15
  37. package/frontend/build/static/js/453.d855a71b.chunk.js +0 -2
  38. package/frontend/build/static/js/main.e76dd8f6.js +0 -3
  39. package/frontend/build/static/js/main.e76dd8f6.js.map +0 -1
  40. /package/frontend/build/static/js/{main.e76dd8f6.js.LICENSE.txt → main.4c5271fd.js.LICENSE.txt} +0 -0
@@ -20,8 +20,7 @@
20
20
  * See the License for the specific language governing permissions and
21
21
  * limitations under the License. *
22
22
  */
23
- import { NodeStorageManager } from 'node-persist-manager';
24
- import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, hk, or, idn, BLUE, CYAN, nt } from 'node-ansi-logger';
23
+ // Node.js modules
25
24
  import { fileURLToPath } from 'url';
26
25
  import { promises as fs } from 'fs';
27
26
  import { exec, spawn } from 'child_process';
@@ -32,10 +31,13 @@ import express from 'express';
32
31
  import os from 'os';
33
32
  import path from 'path';
34
33
  import WebSocket, { WebSocketServer } from 'ws';
34
+ // NodeStorage and AnsiLogger modules
35
+ import { NodeStorageManager } from 'node-persist-manager';
36
+ import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, hk, or, idn, BLUE, CYAN, nt } from 'node-ansi-logger';
35
37
  // Matterbridge
36
38
  import { MatterbridgeDevice } from './matterbridgeDevice.js';
37
39
  import { BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster } from './cluster/BridgedDeviceBasicInformationCluster.js';
38
- import { logInterfaces, wait, waiter, zipDirectory } from './utils/utils.js';
40
+ import { logInterfaces, wait, waiter, createZip } from './utils/utils.js';
39
41
  // @project-chip/matter-node.js
40
42
  import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
41
43
  import { BasicInformationCluster, ClusterServer, FixedLabelCluster, GeneralCommissioning, PowerSourceCluster, SwitchCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById } from '@project-chip/matter-node.js/cluster';
@@ -44,7 +46,7 @@ import { Aggregator, DeviceTypes, NodeStateInformation } from '@project-chip/mat
44
46
  import { Format, Level, Logger } from '@project-chip/matter-node.js/log';
45
47
  import { ManualPairingCodeCodec, QrCodeSchema } from '@project-chip/matter-node.js/schema';
46
48
  import { StorageBackendDisk, StorageBackendJsonFile, StorageManager } from '@project-chip/matter-node.js/storage';
47
- import { requireMinNodeVersion, getParameter, getIntParameter, hasParameter } from '@project-chip/matter-node.js/util';
49
+ import { getParameter, getIntParameter, hasParameter } from '@project-chip/matter-node.js/util';
48
50
  import { CryptoNode } from '@project-chip/matter-node.js/crypto';
49
51
  import { PluginManager } from './pluginManager.js';
50
52
  import { DeviceManager } from './deviceManager.js';
@@ -80,6 +82,8 @@ export class Matterbridge extends EventEmitter {
80
82
  globalModulesDirectory: '',
81
83
  matterbridgeVersion: '',
82
84
  matterbridgeLatestVersion: '',
85
+ matterbridgeQrPairingCode: undefined,
86
+ matterbridgeManualPairingCode: undefined,
83
87
  matterbridgeFabricInformations: [],
84
88
  matterbridgeSessionInformations: [],
85
89
  matterbridgePaired: false,
@@ -98,14 +102,14 @@ export class Matterbridge extends EventEmitter {
98
102
  globalModulesDirectory = '';
99
103
  matterbridgeVersion = '';
100
104
  matterbridgeLatestVersion = '';
105
+ matterbridgeQrPairingCode = undefined;
106
+ matterbridgeManualPairingCode = undefined;
101
107
  matterbridgeFabricInformations = [];
102
108
  matterbridgeSessionInformations = [];
103
109
  matterbridgePaired = false;
104
110
  matterbridgeConnected = false;
105
111
  bridgeMode = '';
106
112
  restartMode = '';
107
- // public debugEnabled = false;
108
- // public loggerLevel: LogLevel = LogLevel.INFO;
109
113
  profile = getParameter('profile');
110
114
  log;
111
115
  matterbrideLoggerFile = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
@@ -121,6 +125,7 @@ export class Matterbridge extends EventEmitter {
121
125
  hasCleanupStarted = false;
122
126
  initialized = false;
123
127
  execRunningCount = 0;
128
+ startMatterInterval;
124
129
  cleanupTimeout1;
125
130
  cleanupTimeout2;
126
131
  checkUpdateInterval;
@@ -210,14 +215,19 @@ export class Matterbridge extends EventEmitter {
210
215
  // Set the matterbridge directory
211
216
  this.homeDirectory = os.homedir();
212
217
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
213
- // Create the file logger for matterbridge
214
- if (hasParameter('filelogger')) {
218
+ // Initialize nodeStorage and nodeContext
219
+ // this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
220
+ this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
221
+ // this.log.debug('Creating node storage context for matterbridge');
222
+ this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
223
+ // Create matterbridge logger
224
+ this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "info" /* LogLevel.INFO */ });
225
+ // Create the file logger for matterbridge (context: matterbridgeFileLog)
226
+ if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
215
227
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
216
228
  this.matterbridgeInformation.fileLogger = true;
217
229
  }
218
- // Create matterbridge logger
219
- this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "info" /* LogLevel.INFO */ });
220
- // Set matterbridge logger level
230
+ // Set matterbridge logger level (context: matterbridgeLogLevel)
221
231
  if (hasParameter('logger')) {
222
232
  const level = getParameter('logger');
223
233
  if (level === 'debug') {
@@ -244,11 +254,12 @@ export class Matterbridge extends EventEmitter {
244
254
  }
245
255
  }
246
256
  else {
247
- this.log.logLevel = "info" /* LogLevel.INFO */;
257
+ this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info" /* LogLevel.INFO */);
248
258
  }
249
259
  MatterbridgeDevice.logLevel = this.log.logLevel;
250
260
  this.log.debug('Matterbridge is starting...');
251
- // Set matter.js logger level, format and logger
261
+ this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
262
+ // Set matter.js logger level, format and logger (context: matterLogLevel)
252
263
  if (hasParameter('matterlogger')) {
253
264
  const level = getParameter('matterlogger');
254
265
  if (level === 'debug') {
@@ -275,30 +286,19 @@ export class Matterbridge extends EventEmitter {
275
286
  }
276
287
  }
277
288
  else {
278
- Logger.defaultLogLevel = Level.INFO;
289
+ Logger.defaultLogLevel = await this.nodeContext.get('matterLogLevel', Level.INFO);
279
290
  }
280
291
  Logger.format = Format.ANSI;
281
292
  Logger.setLogger('default', this.createMatterLogger());
282
- // Create the file logger for matter.js
283
- if (hasParameter('matterfilelogger')) {
293
+ // Create the file logger for matter.js (context: matterFileLog)
294
+ if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
284
295
  this.matterbridgeInformation.matterFileLogger = true;
285
- /*
286
- try {
287
- await fs.unlink(path.join(this.matterbridgeDirectory, this.matterLoggerFile));
288
- } catch (error) {
289
- this.log.debug(`Error unlinking the log file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
290
- }
291
- */
292
296
  Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
293
297
  defaultLogLevel: Level.DEBUG,
294
298
  logFormat: Format.PLAIN,
295
299
  });
296
300
  }
297
- // Initialize nodeStorage and nodeContext
298
- this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
299
- this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
300
- this.log.debug('Creating node storage context for matterbridge');
301
- this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
301
+ this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
302
302
  // Initialize PluginManager
303
303
  this.plugins = new PluginManager(this);
304
304
  await this.plugins.loadFromStorage();
@@ -307,8 +307,9 @@ export class Matterbridge extends EventEmitter {
307
307
  // Get the plugins from node storage and create the plugins node storage contexts
308
308
  for (const plugin of this.plugins) {
309
309
  const packageJson = await this.plugins.parse(plugin);
310
- if (packageJson === null) {
310
+ if (packageJson === null && !hasParameter('add')) {
311
311
  // Try to reinstall the plugin from npm (for Docker pull and external plugins)
312
+ // We don't do this when the add parameter is set because we shut down the process after adding the plugin
312
313
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
313
314
  try {
314
315
  await this.spawnCommand('npm', ['install', '-g', plugin.name]);
@@ -333,10 +334,20 @@ export class Matterbridge extends EventEmitter {
333
334
  }
334
335
  // Log system info and create .matterbridge directory
335
336
  await this.logNodeAndSystemInfo();
336
- this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ${hasParameter('bridge') ? 'mode bridge' : ''}${hasParameter('childbridge') ? 'mode childbridge' : ''}${hasParameter('controller') ? 'mode controller' : ''} ` +
337
- `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}running on ${this.systemInformation.osType} ${this.systemInformation.osRelease} ${this.systemInformation.osPlatform} ${this.systemInformation.osArch} `);
337
+ this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
338
+ `${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
339
+ `${hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge') ? 'mode childbridge ' : ''}` +
340
+ `${hasParameter('controller') ? 'mode controller ' : ''}` +
341
+ `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
342
+ `running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
338
343
  // Check node version and throw error
339
- requireMinNodeVersion(18);
344
+ const minNodeVersion = 18;
345
+ const nodeVersion = process.versions.node;
346
+ const versionMajor = parseInt(nodeVersion.split('.')[0]);
347
+ if (versionMajor < minNodeVersion) {
348
+ this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
349
+ throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
350
+ }
340
351
  // Register SIGINT SIGTERM signal handlers
341
352
  this.registerSignalHandlers();
342
353
  // Parse command line
@@ -357,6 +368,8 @@ export class Matterbridge extends EventEmitter {
357
368
  - childbridge: start Matterbridge in childbridge mode
358
369
  - port [port]: start the commissioning server on the given port (default 5540)
359
370
  - mdnsinterface [name]: set the interface to use for the matter server mdnsInterface (default all interfaces)
371
+ - ipv4address [address]: set the ipv4 interface address to use for the matter listener (default all interfaces)
372
+ - ipv6address [address]: set the ipv6 interface address to use for the matter listener (default all interfaces)
360
373
  - frontend [port]: start the frontend on the given port (default 8283)
361
374
  - logger: set the matterbridge logger level: debug | info | notice | warn | error | fatal (default info)
362
375
  - matterlogger: set the matter.js logger level: debug | info | notice | warn | error | fatal (default info)
@@ -517,8 +530,8 @@ export class Matterbridge extends EventEmitter {
517
530
  }
518
531
  }, 60 * 60 * 1000);
519
532
  if (hasParameter('test')) {
520
- this.bridgeMode = 'childbridge';
521
- MatterbridgeDevice.bridgeMode = 'childbridge';
533
+ this.bridgeMode = 'bridge';
534
+ MatterbridgeDevice.bridgeMode = 'bridge';
522
535
  await this.startTest();
523
536
  return;
524
537
  }
@@ -527,7 +540,12 @@ export class Matterbridge extends EventEmitter {
527
540
  await this.startController();
528
541
  return;
529
542
  }
530
- if (hasParameter('bridge')) {
543
+ // Check if the bridge mode is set and start matterbridge in bridge mode if not set
544
+ if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
545
+ this.log.info('Setting default matterbridge start mode to bridge');
546
+ await this.nodeContext?.set('bridgeMode', 'bridge');
547
+ }
548
+ if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
531
549
  this.bridgeMode = 'bridge';
532
550
  MatterbridgeDevice.bridgeMode = 'bridge';
533
551
  if (!this.storageManager)
@@ -568,12 +586,12 @@ export class Matterbridge extends EventEmitter {
568
586
  plugin.addedDevices = undefined;
569
587
  plugin.qrPairingCode = undefined;
570
588
  plugin.manualPairingCode = undefined;
571
- this.plugins.load(plugin, true, 'Matterbridge is starting'); // this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
589
+ this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
572
590
  }
573
591
  await this.startBridge();
574
592
  return;
575
593
  }
576
- if (hasParameter('childbridge')) {
594
+ if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
577
595
  this.bridgeMode = 'childbridge';
578
596
  MatterbridgeDevice.bridgeMode = 'childbridge';
579
597
  if (!this.storageManager)
@@ -604,9 +622,9 @@ export class Matterbridge extends EventEmitter {
604
622
  plugin.connected = false;
605
623
  plugin.registeredDevices = undefined;
606
624
  plugin.addedDevices = undefined;
607
- plugin.qrPairingCode = (await plugin.nodeContext?.get('qrPairingCode', undefined)) ?? undefined;
608
- plugin.manualPairingCode = (await plugin.nodeContext?.get('manualPairingCode', undefined)) ?? undefined;
609
- this.plugins.load(plugin, true, 'Matterbridge is starting'); // this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
625
+ plugin.qrPairingCode = undefined;
626
+ plugin.manualPairingCode = undefined;
627
+ this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
610
628
  }
611
629
  await this.startChildbridge();
612
630
  return;
@@ -853,7 +871,10 @@ export class Matterbridge extends EventEmitter {
853
871
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
854
872
  await this.nodeContext?.set('matterbridgeLatestVersion', this.matterbridgeLatestVersion);
855
873
  if (this.matterbridgeVersion !== this.matterbridgeLatestVersion) {
856
- this.log.warn(`Matterbridge is out of date. Current version: ${this.matterbridgeVersion}, Latest version: ${this.matterbridgeLatestVersion}`);
874
+ this.log.notice(`Matterbridge is out of date. Current version: ${this.matterbridgeVersion}. Latest version: ${this.matterbridgeLatestVersion}.`);
875
+ }
876
+ else {
877
+ this.log.debug(`Matterbridge is up to date. Current version: ${this.matterbridgeVersion}. Latest version: ${this.matterbridgeLatestVersion}.`);
857
878
  }
858
879
  })
859
880
  .catch((error) => {
@@ -876,12 +897,12 @@ export class Matterbridge extends EventEmitter {
876
897
  .then(async (latestVersion) => {
877
898
  plugin.latestVersion = latestVersion;
878
899
  if (plugin.version !== latestVersion)
879
- this.log.warn(`The plugin ${plg}${plugin.name}${wr} is out of date. Current version: ${plugin.version}, Latest version: ${latestVersion}`);
900
+ this.log.notice(`The plugin ${plg}${plugin.name}${nt} is out of date. Current version: ${plugin.version}. Latest version: ${latestVersion}.`);
880
901
  else
881
- this.log.info(`The plugin ${plg}${plugin.name}${nf} is up to date. Current version: ${plugin.version}, Latest version: ${latestVersion}`);
902
+ this.log.debug(`The plugin ${plg}${plugin.name}${db} is up to date. Current version: ${plugin.version}. Latest version: ${latestVersion}.`);
882
903
  })
883
904
  .catch((error) => {
884
- this.log.error(`Error getting ${plugin.name} latest version: ${error.message}`);
905
+ this.log.error(`Error getting ${plg}${plugin.name}${er} latest version: ${error.message}`);
885
906
  // error.stack && this.log.debug(error.stack);
886
907
  });
887
908
  }
@@ -936,12 +957,15 @@ export class Matterbridge extends EventEmitter {
936
957
  await fs.unlink(filePath);
937
958
  }
938
959
  catch (error) {
939
- this.log.debug(`Error unlinking the log file ${CYAN}${filePath}${er}: ${error instanceof Error ? error.message : error}`);
960
+ this.log.debug(`Error unlinking the log file ${CYAN}${filePath}${db}: ${error instanceof Error ? error.message : error}`);
940
961
  }
941
962
  }
942
963
  return async (_level, formattedLog) => {
964
+ if (fileSize > 100000000)
965
+ return;
943
966
  fileSize += formattedLog.length;
944
967
  if (fileSize > 100000000) {
968
+ await fs.appendFile(filePath, `Logging on file has been stoppped because the file size is greater then 100MB.` + os.EOL);
945
969
  return;
946
970
  }
947
971
  const now = new Date();
@@ -1027,11 +1051,18 @@ export class Matterbridge extends EventEmitter {
1027
1051
  this.log.info(message);
1028
1052
  // Deregisters the SIGINT and SIGTERM signal handlers
1029
1053
  this.deregisterSignalHandlers();
1054
+ // Clear the start matter interval
1055
+ if (this.startMatterInterval) {
1056
+ clearInterval(this.startMatterInterval);
1057
+ this.startMatterInterval = undefined;
1058
+ this.log.debug('Start matter interval cleared');
1059
+ }
1030
1060
  // Clear the check update interval
1031
- if (this.checkUpdateInterval)
1061
+ if (this.checkUpdateInterval) {
1032
1062
  clearInterval(this.checkUpdateInterval);
1033
- this.checkUpdateInterval = undefined;
1034
- this.log.debug('Check update interval cleared');
1063
+ this.checkUpdateInterval = undefined;
1064
+ this.log.debug('Check update interval cleared');
1065
+ }
1035
1066
  // Clear the configure timeout
1036
1067
  if (this.configureTimeout) {
1037
1068
  clearTimeout(this.configureTimeout);
@@ -1265,8 +1296,10 @@ export class Matterbridge extends EventEmitter {
1265
1296
  this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${dev}${device.name}${er}) for plugin ${plg}${pluginName}${er}: matterAggregator not found`);
1266
1297
  return;
1267
1298
  }
1268
- device.setBridgedDeviceReachability(false);
1269
- device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
1299
+ if (device.number !== undefined) {
1300
+ device.setBridgedDeviceReachability(false);
1301
+ device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
1302
+ }
1270
1303
  // device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerShutDownEvent({});
1271
1304
  // device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerLeaveEvent({});
1272
1305
  this.matterAggregator?.removeBridgedDevice(device);
@@ -1307,8 +1340,10 @@ export class Matterbridge extends EventEmitter {
1307
1340
  return;
1308
1341
  }
1309
1342
  });
1310
- device.setBridgedDeviceReachability(false);
1311
- device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
1343
+ if (device.number !== undefined) {
1344
+ device.setBridgedDeviceReachability(false);
1345
+ device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
1346
+ }
1312
1347
  plugin.aggregator.removeBridgedDevice(device);
1313
1348
  }
1314
1349
  this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
@@ -1357,13 +1392,14 @@ export class Matterbridge extends EventEmitter {
1357
1392
  // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1358
1393
  this.log.debug('***Starting startMatterInterval in bridge mode');
1359
1394
  let failCount = 0;
1360
- const startMatterInterval = setInterval(async () => {
1395
+ this.startMatterInterval = setInterval(async () => {
1361
1396
  for (const plugin of this.plugins) {
1362
1397
  // new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
1363
1398
  if (!plugin.enabled)
1364
1399
  continue;
1365
1400
  if (plugin.error) {
1366
- clearInterval(startMatterInterval);
1401
+ clearInterval(this.startMatterInterval);
1402
+ this.startMatterInterval = undefined;
1367
1403
  this.log.debug('***Cleared startMatterInterval interval for Matterbridge for plugin in error state');
1368
1404
  this.log.error(`The plugin ${plg}${plugin.name}${er} is in error state.`);
1369
1405
  this.log.error('The bridge will not start until the problem is solved to prevent the controllers from deleting all registered devices.');
@@ -1380,7 +1416,8 @@ export class Matterbridge extends EventEmitter {
1380
1416
  return;
1381
1417
  }
1382
1418
  }
1383
- clearInterval(startMatterInterval);
1419
+ clearInterval(this.startMatterInterval);
1420
+ this.startMatterInterval = undefined;
1384
1421
  this.log.debug('***Cleared startMatterInterval interval for Matterbridge');
1385
1422
  await this.startMatterServer();
1386
1423
  this.log.notice('Matter server started');
@@ -1421,14 +1458,15 @@ export class Matterbridge extends EventEmitter {
1421
1458
  // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1422
1459
  this.log.debug('***Starting start matter interval in childbridge mode...');
1423
1460
  let failCount = 0;
1424
- const startMatterInterval = setInterval(async () => {
1461
+ this.startMatterInterval = setInterval(async () => {
1425
1462
  let allStarted = true;
1426
1463
  for (const plugin of this.plugins) {
1427
1464
  // new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
1428
1465
  if (!plugin.enabled)
1429
1466
  continue;
1430
1467
  if (plugin.error) {
1431
- clearInterval(startMatterInterval);
1468
+ clearInterval(this.startMatterInterval);
1469
+ this.startMatterInterval = undefined;
1432
1470
  this.log.debug('***Cleared startMatterInterval interval for Matterbridge for plugin in error state');
1433
1471
  this.log.error(`The plugin ${plg}${plugin.name}${er} is in error state.`);
1434
1472
  this.log.error('The bridge will not start until the problem is solved to prevent the controllers from deleting all registered devices.');
@@ -1448,7 +1486,8 @@ export class Matterbridge extends EventEmitter {
1448
1486
  }
1449
1487
  if (!allStarted)
1450
1488
  return;
1451
- clearInterval(startMatterInterval);
1489
+ clearInterval(this.startMatterInterval);
1490
+ this.startMatterInterval = undefined;
1452
1491
  this.log.debug('***Cleared startMatterInterval interval in childbridge mode');
1453
1492
  await this.startMatterServer();
1454
1493
  this.log.notice('Matter server started');
@@ -1873,8 +1912,8 @@ export class Matterbridge extends EventEmitter {
1873
1912
  this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with nodeLabel '${productName}' port ${this.port} passcode ${this.passcode} discriminator ${this.discriminator}`);
1874
1913
  const commissioningServer = new CommissioningServer({
1875
1914
  port: this.port++,
1876
- // listeningAddressIpv4
1877
- // listeningAddressIpv6
1915
+ listeningAddressIpv4: getParameter('ipv4address'),
1916
+ listeningAddressIpv6: getParameter('ipv6address'),
1878
1917
  passcode: this.passcode,
1879
1918
  discriminator: this.discriminator,
1880
1919
  deviceName,
@@ -2101,16 +2140,15 @@ export class Matterbridge extends EventEmitter {
2101
2140
  }
2102
2141
  if (!commissioningServer.isCommissioned()) {
2103
2142
  const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
2104
- await storageContext.set('qrPairingCode', qrPairingCode);
2105
- await storageContext.set('manualPairingCode', manualPairingCode);
2106
- await nodeContext.set('qrPairingCode', qrPairingCode);
2107
- await nodeContext.set('manualPairingCode', manualPairingCode);
2108
2143
  const QrCode = new QrCodeSchema();
2109
2144
  this.log.info(`*The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code:\n\n`);
2110
2145
  // eslint-disable-next-line no-console
2111
- console.log(`${QrCode.encode(qrPairingCode)}\n`);
2112
- this.log.info(`${plg}${pluginName}${nf}\n\nqrPairingCode: ${qrPairingCode}\n\nManual pairing code: ${manualPairingCode}\n`);
2146
+ if (this.log.logLevel === "debug" /* LogLevel.DEBUG */ || this.log.logLevel === "info" /* LogLevel.INFO */)
2147
+ console.log(`${QrCode.encode(qrPairingCode)}\n`);
2148
+ this.log.info(`${plg}${pluginName}${nf} \n\nqrPairingCode: ${qrPairingCode} \n\nManual pairing code: ${manualPairingCode}\n`);
2113
2149
  if (pluginName === 'Matterbridge') {
2150
+ this.matterbridgeQrPairingCode = qrPairingCode;
2151
+ this.matterbridgeManualPairingCode = manualPairingCode;
2114
2152
  this.matterbridgeFabricInformations = [];
2115
2153
  this.matterbridgeSessionInformations = [];
2116
2154
  this.matterbridgePaired = false;
@@ -2595,40 +2633,21 @@ export class Matterbridge extends EventEmitter {
2595
2633
  // Endpoint to provide settings
2596
2634
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
2597
2635
  this.log.debug('The frontend sent /api/settings');
2598
- if (!this.matterbridgeContext) {
2599
- this.log.error('/api/settings matterbridgeContext not found');
2600
- res.json({});
2601
- return;
2602
- }
2603
- let qrPairingCode = '';
2604
- let manualPairingCode = '';
2605
- try {
2606
- qrPairingCode = await this.matterbridgeContext.get('qrPairingCode');
2607
- manualPairingCode = await this.matterbridgeContext.get('manualPairingCode');
2608
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2609
- }
2610
- catch (error) {
2611
- if (this.bridgeMode === 'bridge')
2612
- this.log.warn('pairingCodes for /api/settings not found');
2613
- }
2614
2636
  this.matterbridgeInformation.bridgeMode = this.bridgeMode;
2615
2637
  this.matterbridgeInformation.restartMode = this.restartMode;
2616
2638
  this.matterbridgeInformation.loggerLevel = this.log.logLevel;
2617
2639
  this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
2618
2640
  this.matterbridgeInformation.matterbridgePaired = this.matterbridgePaired;
2619
2641
  this.matterbridgeInformation.matterbridgeConnected = this.matterbridgeConnected;
2642
+ this.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridgeQrPairingCode;
2643
+ this.matterbridgeInformation.matterbridgeManualPairingCode = this.matterbridgeManualPairingCode;
2620
2644
  this.matterbridgeInformation.matterbridgeFabricInformations = this.matterbridgeFabricInformations;
2621
2645
  this.matterbridgeInformation.matterbridgeSessionInformations = this.matterbridgeSessionInformations;
2622
2646
  if (this.profile)
2623
2647
  this.matterbridgeInformation.profile = this.profile;
2624
- const response = { wssHost, ssl: hasParameter('ssl'), qrPairingCode, manualPairingCode, systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
2648
+ // const response = { wssHost, ssl: hasParameter('ssl'), qrPairingCode, manualPairingCode, systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
2649
+ const response = { wssHost, ssl: hasParameter('ssl'), systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
2625
2650
  // this.log.debug('Response:', debugStringify(response));
2626
- /*
2627
- if (this.webSocketServer && this.webSocketServer.clients.size > 0 && !AnsiLogger.getGlobalCallback()) {
2628
- AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), LogLevel.DEBUG);
2629
- this.log.debug('WebSocketServer logger global callback added');
2630
- }
2631
- */
2632
2651
  res.json(response);
2633
2652
  });
2634
2653
  // Endpoint to provide plugins
@@ -2801,8 +2820,8 @@ export class Matterbridge extends EventEmitter {
2801
2820
  // Endpoint to download the matterbridge storage directory
2802
2821
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
2803
2822
  this.log.debug('The frontend sent /api/download-mbstorage');
2804
- await zipDirectory(path.join(this.matterbridgeDirectory, `matterbridge.${this.nodeStorageName}.zip`), path.join(this.matterbridgeDirectory, this.nodeStorageName));
2805
- res.download(path.join(this.matterbridgeDirectory, `matterbridge.${this.nodeStorageName}.zip`), `matterbridge.${this.nodeStorageName}.zip`, (error) => {
2823
+ await createZip(path.join(os.tmpdir(), `matterbridge.${this.nodeStorageName}.zip`), path.join(this.matterbridgeDirectory, this.nodeStorageName));
2824
+ res.download(path.join(os.tmpdir(), `matterbridge.${this.nodeStorageName}.zip`), `matterbridge.${this.nodeStorageName}.zip`, (error) => {
2806
2825
  if (error) {
2807
2826
  this.log.error(`Error downloading file ${`matterbridge.${this.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
2808
2827
  res.status(500).send('Error downloading the matterbridge storage file');
@@ -2812,14 +2831,36 @@ export class Matterbridge extends EventEmitter {
2812
2831
  // Endpoint to download the matterbridge plugin directory
2813
2832
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
2814
2833
  this.log.debug('The frontend sent /api/download-pluginstorage');
2815
- await zipDirectory(path.join(this.matterbridgeDirectory, `matterbridge.pluginstorage.zip`), this.matterbridgePluginDirectory);
2816
- res.download(path.join(this.matterbridgeDirectory, `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
2834
+ await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridgePluginDirectory);
2835
+ res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
2817
2836
  if (error) {
2818
2837
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
2819
2838
  res.status(500).send('Error downloading the matterbridge plugin storage file');
2820
2839
  }
2821
2840
  });
2822
2841
  });
2842
+ // Endpoint to download the matterbridge plugin config files
2843
+ this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
2844
+ this.log.debug('The frontend sent /api/download-pluginconfig');
2845
+ await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridgeDirectory, '*.config.json')));
2846
+ // await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridgeDirectory, 'certs', '*.*')), path.relative(process.cwd(), path.join(this.matterbridgeDirectory, '*.config.json')));
2847
+ res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
2848
+ if (error) {
2849
+ this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
2850
+ res.status(500).send('Error downloading the matterbridge plugin storage file');
2851
+ }
2852
+ });
2853
+ });
2854
+ // Endpoint to download the matterbridge plugin config files
2855
+ this.expressApp.get('/api/download-backup', async (req, res) => {
2856
+ this.log.debug('The frontend sent /api/download-backup');
2857
+ res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
2858
+ if (error) {
2859
+ this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
2860
+ res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
2861
+ }
2862
+ });
2863
+ });
2823
2864
  // Endpoint to receive commands
2824
2865
  this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
2825
2866
  const command = req.params.command;
@@ -2838,6 +2879,21 @@ export class Matterbridge extends EventEmitter {
2838
2879
  res.json({ message: 'Command received' });
2839
2880
  return;
2840
2881
  }
2882
+ // Handle the command setbridgemode from Settings
2883
+ if (command === 'setbridgemode') {
2884
+ this.log.debug(`setbridgemode: ${param}`);
2885
+ await this.nodeContext?.set('bridgeMode', param);
2886
+ res.json({ message: 'Command received' });
2887
+ return;
2888
+ }
2889
+ // Handle the command backup from Settings
2890
+ if (command === 'backup') {
2891
+ this.log.notice(`Prepairing the backup...`);
2892
+ await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridgeDirectory), path.join(this.matterbridgePluginDirectory));
2893
+ this.log.notice(`Backup ready to be downloaded.`);
2894
+ res.json({ message: 'Command received' });
2895
+ return;
2896
+ }
2841
2897
  // Handle the command setmbloglevel from Settings
2842
2898
  if (command === 'setmbloglevel') {
2843
2899
  this.log.debug('Matterbridge log level:', param);
@@ -2915,13 +2971,6 @@ export class Matterbridge extends EventEmitter {
2915
2971
  this.matterbridgeInformation.matterFileLogger = param === 'true';
2916
2972
  await this.nodeContext?.set('matterFileLog', param === 'true');
2917
2973
  if (param === 'true') {
2918
- /*
2919
- try {
2920
- await fs.unlink(path.join(this.matterbridgeDirectory, this.matterLoggerFile));
2921
- } catch (error) {
2922
- this.log.debug(`Error unlinking the log file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
2923
- }
2924
- */
2925
2974
  try {
2926
2975
  Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
2927
2976
  defaultLogLevel: Level.DEBUG,