matterbridge 1.4.1 → 1.4.3

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 (42) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +1 -2
  3. package/dist/cluster/AirQualityCluster.d.ts.map +1 -1
  4. package/dist/cluster/AirQualityCluster.js +1 -0
  5. package/dist/cluster/AirQualityCluster.js.map +1 -1
  6. package/dist/cluster/BridgedDeviceBasicInformationCluster.d.ts.map +1 -1
  7. package/dist/cluster/BridgedDeviceBasicInformationCluster.js +1 -0
  8. package/dist/cluster/BridgedDeviceBasicInformationCluster.js.map +1 -1
  9. package/dist/cluster/PowerTopologyCluster.d.ts.map +1 -1
  10. package/dist/cluster/PowerTopologyCluster.js +1 -1
  11. package/dist/cluster/PowerTopologyCluster.js.map +1 -1
  12. package/dist/cluster/TvocCluster.d.ts.map +1 -1
  13. package/dist/cluster/TvocCluster.js +1 -0
  14. package/dist/cluster/TvocCluster.js.map +1 -1
  15. package/dist/matterbridge.d.ts +16 -0
  16. package/dist/matterbridge.d.ts.map +1 -1
  17. package/dist/matterbridge.js +299 -25
  18. package/dist/matterbridge.js.map +1 -1
  19. package/dist/matterbridgeDevice.d.ts +65 -157
  20. package/dist/matterbridgeDevice.d.ts.map +1 -1
  21. package/dist/matterbridgeDevice.js +224 -26
  22. package/dist/matterbridgeDevice.js.map +1 -1
  23. package/dist/matterbridgeTypes.d.ts +2 -0
  24. package/dist/matterbridgeTypes.d.ts.map +1 -1
  25. package/dist/pluginManager.d.ts.map +1 -1
  26. package/dist/pluginManager.js +1 -0
  27. package/dist/pluginManager.js.map +1 -1
  28. package/dist/utils/utils.d.ts +58 -0
  29. package/dist/utils/utils.d.ts.map +1 -1
  30. package/dist/utils/utils.js +133 -0
  31. package/dist/utils/utils.js.map +1 -1
  32. package/frontend/build/asset-manifest.json +6 -6
  33. package/frontend/build/index.html +1 -1
  34. package/frontend/build/static/css/main.ee3183e2.css +2 -0
  35. package/frontend/build/static/css/main.ee3183e2.css.map +1 -0
  36. package/frontend/build/static/js/{main.fd6f85a1.js → main.e76dd8f6.js} +3 -3
  37. package/frontend/build/static/js/main.e76dd8f6.js.map +1 -0
  38. package/package.json +26 -24
  39. package/frontend/build/static/css/main.5174e68c.css +0 -2
  40. package/frontend/build/static/css/main.5174e68c.css.map +0 -1
  41. package/frontend/build/static/js/main.fd6f85a1.js.map +0 -1
  42. /package/frontend/build/static/js/{main.fd6f85a1.js.LICENSE.txt → main.e76dd8f6.js.LICENSE.txt} +0 -0
@@ -35,7 +35,7 @@ import WebSocket, { WebSocketServer } from 'ws';
35
35
  // Matterbridge
36
36
  import { MatterbridgeDevice } from './matterbridgeDevice.js';
37
37
  import { BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster } from './cluster/BridgedDeviceBasicInformationCluster.js';
38
- import { logInterfaces, wait, waiter } from './utils/utils.js';
38
+ import { logInterfaces, wait, waiter, zipDirectory } from './utils/utils.js';
39
39
  // @project-chip/matter-node.js
40
40
  import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
41
41
  import { BasicInformationCluster, ClusterServer, FixedLabelCluster, GeneralCommissioning, PowerSourceCluster, SwitchCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById } from '@project-chip/matter-node.js/cluster';
@@ -87,7 +87,9 @@ export class Matterbridge extends EventEmitter {
87
87
  bridgeMode: '',
88
88
  restartMode: '',
89
89
  loggerLevel: "info" /* LogLevel.INFO */,
90
+ fileLogger: false,
90
91
  matterLoggerLevel: Level.INFO,
92
+ matterFileLogger: false,
91
93
  };
92
94
  homeDirectory = '';
93
95
  rootDirectory = '';
@@ -106,6 +108,8 @@ export class Matterbridge extends EventEmitter {
106
108
  // public loggerLevel: LogLevel = LogLevel.INFO;
107
109
  profile = getParameter('profile');
108
110
  log;
111
+ matterbrideLoggerFile = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
112
+ matterLoggerFile = 'matter' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
109
113
  plugins;
110
114
  devices;
111
115
  registeredDevices = [];
@@ -203,9 +207,17 @@ export class Matterbridge extends EventEmitter {
203
207
  this.restartMode = 'service';
204
208
  if (hasParameter('docker'))
205
209
  this.restartMode = 'docker';
206
- // Set Matterbridge logger
210
+ // Set the matterbridge directory
211
+ this.homeDirectory = os.homedir();
212
+ this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
213
+ // Create the file logger for matterbridge
214
+ if (hasParameter('filelogger')) {
215
+ AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
216
+ this.matterbridgeInformation.fileLogger = true;
217
+ }
218
+ // Create matterbridge logger
207
219
  this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "info" /* LogLevel.INFO */ });
208
- // Set matter.js logger level and format
220
+ // Set matterbridge logger level
209
221
  if (hasParameter('logger')) {
210
222
  const level = getParameter('logger');
211
223
  if (level === 'debug') {
@@ -236,7 +248,7 @@ export class Matterbridge extends EventEmitter {
236
248
  }
237
249
  MatterbridgeDevice.logLevel = this.log.logLevel;
238
250
  this.log.debug('Matterbridge is starting...');
239
- // Set matter.js logger level and format
251
+ // Set matter.js logger level, format and logger
240
252
  if (hasParameter('matterlogger')) {
241
253
  const level = getParameter('matterlogger');
242
254
  if (level === 'debug') {
@@ -266,9 +278,23 @@ export class Matterbridge extends EventEmitter {
266
278
  Logger.defaultLogLevel = Level.INFO;
267
279
  }
268
280
  Logger.format = Format.ANSI;
281
+ Logger.setLogger('default', this.createMatterLogger());
282
+ // Create the file logger for matter.js
283
+ if (hasParameter('matterfilelogger')) {
284
+ 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
+ Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
293
+ defaultLogLevel: Level.DEBUG,
294
+ logFormat: Format.PLAIN,
295
+ });
296
+ }
269
297
  // Initialize nodeStorage and nodeContext
270
- this.homeDirectory = os.homedir();
271
- this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
272
298
  this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
273
299
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
274
300
  this.log.debug('Creating node storage context for matterbridge');
@@ -288,6 +314,7 @@ export class Matterbridge extends EventEmitter {
288
314
  await this.spawnCommand('npm', ['install', '-g', plugin.name]);
289
315
  this.log.info(`Plugin ${plg}${plugin.name}${nf} reinstalled.`);
290
316
  plugin.error = false;
317
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
291
318
  }
292
319
  catch (error) {
293
320
  plugin.error = true;
@@ -605,9 +632,11 @@ export class Matterbridge extends EventEmitter {
605
632
  */
606
633
  deregisterSignalHandlers() {
607
634
  this.log.debug(`Deregistering SIGINT and SIGTERM signal handlers...`);
608
- this.sigintHandler && process.off('SIGINT', this.sigintHandler);
635
+ if (this.sigintHandler)
636
+ process.off('SIGINT', this.sigintHandler);
609
637
  this.sigintHandler = undefined;
610
- this.sigtermHandler && process.off('SIGTERM', this.sigtermHandler);
638
+ if (this.sigtermHandler)
639
+ process.off('SIGTERM', this.sigtermHandler);
611
640
  this.sigtermHandler = undefined;
612
641
  }
613
642
  /**
@@ -856,6 +885,96 @@ export class Matterbridge extends EventEmitter {
856
885
  // error.stack && this.log.debug(error.stack);
857
886
  });
858
887
  }
888
+ /**
889
+ * Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
890
+ *
891
+ * @returns {Function} The MatterLogger function.
892
+ */
893
+ createMatterLogger() {
894
+ const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
895
+ return (_level, formattedLog) => {
896
+ const logger = formattedLog.slice(44, 44 + 20).trim();
897
+ const message = formattedLog.slice(65);
898
+ matterLogger.logName = logger;
899
+ switch (_level) {
900
+ case Level.DEBUG:
901
+ matterLogger.log("debug" /* LogLevel.DEBUG */, message);
902
+ break;
903
+ case Level.INFO:
904
+ matterLogger.log("info" /* LogLevel.INFO */, message);
905
+ break;
906
+ case Level.NOTICE:
907
+ matterLogger.log("notice" /* LogLevel.NOTICE */, message);
908
+ break;
909
+ case Level.WARN:
910
+ matterLogger.log("warn" /* LogLevel.WARN */, message);
911
+ break;
912
+ case Level.ERROR:
913
+ matterLogger.log("error" /* LogLevel.ERROR */, message);
914
+ break;
915
+ case Level.FATAL:
916
+ matterLogger.log("fatal" /* LogLevel.FATAL */, message);
917
+ break;
918
+ default:
919
+ matterLogger.log("debug" /* LogLevel.DEBUG */, message);
920
+ break;
921
+ }
922
+ };
923
+ }
924
+ /**
925
+ * Creates a Matter File Logger.
926
+ *
927
+ * @param {string} filePath - The path to the log file.
928
+ * @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
929
+ * @returns {Function} - A function that logs formatted messages to the log file.
930
+ */
931
+ async createMatterFileLogger(filePath, unlink = false) {
932
+ // 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
933
+ let fileSize = 0;
934
+ if (unlink) {
935
+ try {
936
+ await fs.unlink(filePath);
937
+ }
938
+ catch (error) {
939
+ this.log.debug(`Error unlinking the log file ${CYAN}${filePath}${er}: ${error instanceof Error ? error.message : error}`);
940
+ }
941
+ }
942
+ return async (_level, formattedLog) => {
943
+ fileSize += formattedLog.length;
944
+ if (fileSize > 100000000) {
945
+ return;
946
+ }
947
+ const now = new Date();
948
+ const timestamp = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}.${now.getMilliseconds().toString().padStart(3, '0')}`;
949
+ const message = formattedLog.slice(24);
950
+ const parts = message.split(' ');
951
+ const logger = parts[1];
952
+ const finalMessage = parts.slice(2).join(' ') + os.EOL;
953
+ switch (_level) {
954
+ case Level.DEBUG:
955
+ await fs.appendFile(filePath, `[${timestamp}] [${logger}] [debug] ${finalMessage}`);
956
+ break;
957
+ case Level.INFO:
958
+ await fs.appendFile(filePath, `[${timestamp}] [${logger}] [info] ${finalMessage}`);
959
+ break;
960
+ case Level.NOTICE:
961
+ await fs.appendFile(filePath, `[${timestamp}] [${logger}] [notice] ${finalMessage}`);
962
+ break;
963
+ case Level.WARN:
964
+ await fs.appendFile(filePath, `[${timestamp}] [${logger}] [warn] ${finalMessage}`);
965
+ break;
966
+ case Level.ERROR:
967
+ await fs.appendFile(filePath, `[${timestamp}] [${logger}] [error] ${finalMessage}`);
968
+ break;
969
+ case Level.FATAL:
970
+ await fs.appendFile(filePath, `[${timestamp}] [${logger}] [fatal] ${finalMessage}`);
971
+ break;
972
+ default:
973
+ await fs.appendFile(filePath, `[${timestamp}] [${logger}] ${finalMessage}`);
974
+ break;
975
+ }
976
+ };
977
+ }
859
978
  /**
860
979
  * Update matterbridge and cleanup.
861
980
  */
@@ -979,6 +1098,14 @@ export class Matterbridge extends EventEmitter {
979
1098
  await this.stopMatterServer();
980
1099
  // Closing matter storage
981
1100
  await this.stopMatterStorage();
1101
+ // Remove the matterfilelogger
1102
+ try {
1103
+ Logger.removeLogger('matterfilelogger');
1104
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1105
+ }
1106
+ catch (error) {
1107
+ // this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
1108
+ }
982
1109
  // Serialize registeredDevices
983
1110
  if (this.nodeStorage && this.nodeContext) {
984
1111
  this.log.info('Saving registered devices...');
@@ -1580,6 +1707,7 @@ export class Matterbridge extends EventEmitter {
1580
1707
  if (storageType === 'json') {
1581
1708
  await this.backupJsonMatterStorage(storageName, storageName.replace('.json', '') + '.backup.json');
1582
1709
  }
1710
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1583
1711
  }
1584
1712
  catch (error) {
1585
1713
  this.log.error(`Storage initialize() error! The file .matterbridge/${storageName} may be corrupted.`);
@@ -2203,7 +2331,13 @@ export class Matterbridge extends EventEmitter {
2203
2331
  process.on('unhandledRejection', (reason, promise) => {
2204
2332
  this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
2205
2333
  });
2334
+
2335
+ spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
2336
+ spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
2337
+ debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
2338
+ debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
2206
2339
  */
2340
+ const cmdLine = command + ' ' + args.join(' ');
2207
2341
  if (process.platform === 'win32' && command === 'npm') {
2208
2342
  // Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
2209
2343
  const argstring = 'npm ' + args.join(' ');
@@ -2223,9 +2357,11 @@ export class Matterbridge extends EventEmitter {
2223
2357
  this.log.error(`Failed to start child process: ${err.message}`);
2224
2358
  reject(err); // Reject the promise on error
2225
2359
  });
2226
- childProcess.on('close', (code) => {
2360
+ childProcess.on('close', (code, signal) => {
2361
+ this.wssSendMessage('spawn', this.log.now(), 'Matterbridge:spawn', `child process closed with code ${code} and signal ${signal}`);
2227
2362
  if (code === 0) {
2228
- this.log.debug(`Child process stdio streams have closed with code ${code}`);
2363
+ if (cmdLine.startsWith('npm install -g'))
2364
+ this.log.notice(`${cmdLine.replace('npm install -g ', '')} installed correctly`);
2229
2365
  resolve();
2230
2366
  }
2231
2367
  else {
@@ -2234,8 +2370,8 @@ export class Matterbridge extends EventEmitter {
2234
2370
  }
2235
2371
  });
2236
2372
  childProcess.on('exit', (code, signal) => {
2373
+ this.wssSendMessage('spawn', this.log.now(), 'Matterbridge:spawn', `child process exited with code ${code} and signal ${signal}`);
2237
2374
  if (code === 0) {
2238
- this.log.debug(`Child process exited with code ${code} and signal ${signal}`);
2239
2375
  resolve();
2240
2376
  }
2241
2377
  else {
@@ -2250,14 +2386,12 @@ export class Matterbridge extends EventEmitter {
2250
2386
  if (childProcess.stdout) {
2251
2387
  childProcess.stdout.on('data', (data) => {
2252
2388
  const message = data.toString().trim();
2253
- // this.log.info('\n' + message);
2254
2389
  this.wssSendMessage('spawn', this.log.now(), 'Matterbridge:spawn', message);
2255
2390
  });
2256
2391
  }
2257
2392
  if (childProcess.stderr) {
2258
2393
  childProcess.stderr.on('data', (data) => {
2259
2394
  const message = data.toString().trim();
2260
- // this.log.debug('\n' + message);
2261
2395
  this.wssSendMessage('spawn', this.log.now(), 'Matterbridge:spawn', message);
2262
2396
  });
2263
2397
  }
@@ -2286,11 +2420,21 @@ export class Matterbridge extends EventEmitter {
2286
2420
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
2287
2421
  // Replace all occurrences of \" with "
2288
2422
  message = message.replace(/\\"/g, '"');
2289
- /*
2290
- if (message.length > 500) {
2291
- console.error(`${er}Message long: ${level} ${time} ${name} ${message} `);
2292
- }
2293
- */
2423
+ // Define the maximum allowed length for continuous characters without a space
2424
+ const maxContinuousLength = 100;
2425
+ const keepStartLength = 20;
2426
+ const keepEndLength = 20;
2427
+ // Split the message into words
2428
+ message = message
2429
+ .split(' ')
2430
+ .map((word) => {
2431
+ // If the word length exceeds the max continuous length, insert spaces and truncate
2432
+ if (word.length > maxContinuousLength) {
2433
+ return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
2434
+ }
2435
+ return word;
2436
+ })
2437
+ .join(' ');
2294
2438
  // Send the message to all connected clients
2295
2439
  this.webSocketServer?.clients.forEach((client) => {
2296
2440
  if (client.readyState === WebSocket.OPEN) {
@@ -2395,7 +2539,7 @@ export class Matterbridge extends EventEmitter {
2395
2539
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
2396
2540
  this.webSocketServer.on('connection', (ws, request) => {
2397
2541
  const clientIp = request.socket.remoteAddress;
2398
- this.log.setGlobalCallback(this.wssSendMessage.bind(this));
2542
+ AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug" /* LogLevel.DEBUG */);
2399
2543
  this.log.debug('WebSocketServer logger global callback added');
2400
2544
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
2401
2545
  // this.wssSendMessage('info', this.log.now(), 'Matterbridge', `WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -2405,7 +2549,7 @@ export class Matterbridge extends EventEmitter {
2405
2549
  ws.on('close', () => {
2406
2550
  this.log.info('WebSocket client disconnected');
2407
2551
  if (this.webSocketServer?.clients.size === 0) {
2408
- this.log.setGlobalCallback(undefined);
2552
+ AnsiLogger.setGlobalCallback(undefined);
2409
2553
  this.log.debug('All WebSocket clients disconnected. WebSocketServer logger global callback removed');
2410
2554
  }
2411
2555
  });
@@ -2441,6 +2585,7 @@ export class Matterbridge extends EventEmitter {
2441
2585
  this.log.warn('/api/login error wrong password');
2442
2586
  res.json({ valid: false });
2443
2587
  }
2588
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2444
2589
  }
2445
2590
  catch (error) {
2446
2591
  this.log.error('/api/login error getting password');
@@ -2460,6 +2605,7 @@ export class Matterbridge extends EventEmitter {
2460
2605
  try {
2461
2606
  qrPairingCode = await this.matterbridgeContext.get('qrPairingCode');
2462
2607
  manualPairingCode = await this.matterbridgeContext.get('manualPairingCode');
2608
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2463
2609
  }
2464
2610
  catch (error) {
2465
2611
  if (this.bridgeMode === 'bridge')
@@ -2477,12 +2623,12 @@ export class Matterbridge extends EventEmitter {
2477
2623
  this.matterbridgeInformation.profile = this.profile;
2478
2624
  const response = { wssHost, ssl: hasParameter('ssl'), qrPairingCode, manualPairingCode, systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
2479
2625
  // this.log.debug('Response:', debugStringify(response));
2480
- this.log.debug(`WebSocketServer logger local callback: ${this.log.getCallback() ? 'active' : 'inactive'}`);
2481
- this.log.debug(`WebSocketServer logger global callback: ${this.log.getGlobalCallback() ? 'active' : 'inactive'}`);
2482
- if (this.webSocketServer && this.webSocketServer.clients.size > 0 && !this.log.getGlobalCallback()) {
2483
- this.log.setGlobalCallback(this.wssSendMessage.bind(this));
2484
- this.log.debug('WebSocketServer logger global callback added');
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');
2485
2630
  }
2631
+ */
2486
2632
  res.json(response);
2487
2633
  });
2488
2634
  // Endpoint to provide plugins
@@ -2595,6 +2741,85 @@ export class Matterbridge extends EventEmitter {
2595
2741
  });
2596
2742
  res.json(data);
2597
2743
  });
2744
+ // Endpoint to send the log
2745
+ this.expressApp.get('/api/view-log', async (req, res) => {
2746
+ this.log.debug('The frontend sent /api/log');
2747
+ try {
2748
+ const data = await fs.readFile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), 'utf8');
2749
+ res.type('text/plain');
2750
+ res.send(data);
2751
+ }
2752
+ catch (error) {
2753
+ this.log.error(`Error reading log file ${this.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
2754
+ res.status(500).send('Error reading log file');
2755
+ }
2756
+ });
2757
+ // Endpoint to download the matterbridge log
2758
+ this.expressApp.get('/api/download-mblog', async (req, res) => {
2759
+ this.log.debug('The frontend sent /api/download-mblog');
2760
+ try {
2761
+ await fs.access(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), fs.constants.F_OK);
2762
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2763
+ }
2764
+ catch (error) {
2765
+ fs.appendFile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
2766
+ }
2767
+ res.download(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), 'matterbridge.log', (error) => {
2768
+ if (error) {
2769
+ this.log.error(`Error downloading log file ${this.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
2770
+ res.status(500).send('Error downloading the matterbridge log file');
2771
+ }
2772
+ });
2773
+ });
2774
+ // Endpoint to download the matter log
2775
+ this.expressApp.get('/api/download-mjlog', async (req, res) => {
2776
+ this.log.debug('The frontend sent /api/download-mjlog');
2777
+ try {
2778
+ await fs.access(path.join(this.matterbridgeDirectory, this.matterLoggerFile), fs.constants.F_OK);
2779
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2780
+ }
2781
+ catch (error) {
2782
+ fs.appendFile(path.join(this.matterbridgeDirectory, this.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
2783
+ }
2784
+ res.download(path.join(this.matterbridgeDirectory, this.matterLoggerFile), 'matter.log', (error) => {
2785
+ if (error) {
2786
+ this.log.error(`Error downloading log file ${this.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
2787
+ res.status(500).send('Error downloading the matter log file');
2788
+ }
2789
+ });
2790
+ });
2791
+ // Endpoint to download the matter storage file
2792
+ this.expressApp.get('/api/download-mjstorage', (req, res) => {
2793
+ this.log.debug('The frontend sent /api/download-mjstorage');
2794
+ res.download(path.join(this.matterbridgeDirectory, this.matterStorageName), 'matterbridge.json', (error) => {
2795
+ if (error) {
2796
+ this.log.error(`Error downloading log file ${this.matterStorageName}: ${error instanceof Error ? error.message : error}`);
2797
+ res.status(500).send('Error downloading the matter storage file');
2798
+ }
2799
+ });
2800
+ });
2801
+ // Endpoint to download the matterbridge storage directory
2802
+ this.expressApp.get('/api/download-mbstorage', async (req, res) => {
2803
+ 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) => {
2806
+ if (error) {
2807
+ this.log.error(`Error downloading file ${`matterbridge.${this.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
2808
+ res.status(500).send('Error downloading the matterbridge storage file');
2809
+ }
2810
+ });
2811
+ });
2812
+ // Endpoint to download the matterbridge plugin directory
2813
+ this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
2814
+ 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) => {
2817
+ if (error) {
2818
+ this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
2819
+ res.status(500).send('Error downloading the matterbridge plugin storage file');
2820
+ }
2821
+ });
2822
+ });
2598
2823
  // Endpoint to receive commands
2599
2824
  this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
2600
2825
  const command = req.params.command;
@@ -2671,6 +2896,53 @@ export class Matterbridge extends EventEmitter {
2671
2896
  res.json({ message: 'Command received' });
2672
2897
  return;
2673
2898
  }
2899
+ // Handle the command setmbloglevel from Settings
2900
+ if (command === 'setmblogfile') {
2901
+ this.log.debug('Matterbridge file log:', param);
2902
+ this.matterbridgeInformation.fileLogger = param === 'true';
2903
+ await this.nodeContext?.set('matterbridgeFileLog', param === 'true');
2904
+ // Create the file logger for matterbridge
2905
+ if (param === 'true')
2906
+ AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
2907
+ else
2908
+ AnsiLogger.setGlobalLogfile(undefined);
2909
+ res.json({ message: 'Command received' });
2910
+ return;
2911
+ }
2912
+ // Handle the command setmbloglevel from Settings
2913
+ if (command === 'setmjlogfile') {
2914
+ this.log.debug('Matter file log:', param);
2915
+ this.matterbridgeInformation.matterFileLogger = param === 'true';
2916
+ await this.nodeContext?.set('matterFileLog', param === 'true');
2917
+ 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
+ try {
2926
+ Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
2927
+ defaultLogLevel: Level.DEBUG,
2928
+ logFormat: Format.PLAIN,
2929
+ });
2930
+ }
2931
+ catch (error) {
2932
+ this.log.debug(`Error adding the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
2933
+ }
2934
+ }
2935
+ else {
2936
+ try {
2937
+ Logger.removeLogger('matterfilelogger');
2938
+ }
2939
+ catch (error) {
2940
+ this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
2941
+ }
2942
+ }
2943
+ res.json({ message: 'Command received' });
2944
+ return;
2945
+ }
2674
2946
  // Handle the command unregister from Settings
2675
2947
  if (command === 'unregister') {
2676
2948
  await this.unregisterAndShutdownProcess();
@@ -2707,6 +2979,7 @@ export class Matterbridge extends EventEmitter {
2707
2979
  try {
2708
2980
  await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
2709
2981
  this.log.info('Matterbridge has been updated. Full restart required.');
2982
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2710
2983
  }
2711
2984
  catch (error) {
2712
2985
  this.log.error('Error updating matterbridge');
@@ -2739,6 +3012,7 @@ export class Matterbridge extends EventEmitter {
2739
3012
  try {
2740
3013
  await this.spawnCommand('npm', ['install', '-g', param]);
2741
3014
  this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
3015
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2742
3016
  }
2743
3017
  catch (error) {
2744
3018
  this.log.error(`Error installing plugin ${plg}${param}${er}`);