matterbridge 3.0.2-dev-20250517-4122c94 → 3.0.3-dev-20250517-bcc5d13

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.
package/CHANGELOG.md CHANGED
@@ -8,12 +8,29 @@ If you like this project and find it useful, please consider giving it a star on
8
8
  <img src="bmc-button.svg" alt="Buy me a coffee" width="120">
9
9
  </a>
10
10
 
11
- ## [3.0.2] - 2025-05-??
11
+ ## [3.0.3] - 2025-05-??
12
12
 
13
13
  ### Added
14
14
 
15
- - [virtual] Added virtual devices Restart Matterbridge and Update Matterbridge and full Jest tests.
16
- - [virtual] Added virtual devices Reboot Matterbridge for Shelly board and full Jest tests.
15
+ ### Changed
16
+
17
+ - [package]: Updated dependencies.
18
+ - [export]: Removed long deprecated Matter exports from matterbridge. Use matterbridge/matter.
19
+ - [matterbridge]: Refactored initialize() and cleanup() methods.
20
+ - [matterbridge]: Updated -help informations.
21
+
22
+ ### Fixed
23
+
24
+ <a href="https://www.buymeacoffee.com/luligugithub">
25
+ <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
26
+ </a>
27
+
28
+ ## [3.0.2] - 2025-05-14
29
+
30
+ ### Added
31
+
32
+ - [virtual] Added virtual devices Restart Matterbridge and Update Matterbridge and full Jest tests (can be disabled adding -novirtual to the command line).
33
+ - [virtual] Added virtual devices Reboot Matterbridge for Shelly board and full Jest tests (can be disabled adding -novirtual to the command line).
17
34
  - [shelly] Refactor shelly api and added full Jest test.
18
35
 
19
36
  ### Changed
package/dist/frontend.js CHANGED
@@ -8,7 +8,7 @@ import express from 'express';
8
8
  import WebSocket, { WebSocketServer } from 'ws';
9
9
  import multer from 'multer';
10
10
  import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from './logger/export.js';
11
- import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean } from './utils/export.js';
11
+ import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout } from './utils/export.js';
12
12
  import { plg } from './matterbridgeTypes.js';
13
13
  import { hasParameter } from './utils/export.js';
14
14
  import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
@@ -446,32 +446,7 @@ export class Frontend {
446
446
  this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
447
447
  }
448
448
  async stop() {
449
- if (this.httpServer) {
450
- this.httpServer.close((error) => {
451
- if (error) {
452
- this.log.error(`Error closing http server: ${error}`);
453
- }
454
- else {
455
- this.log.debug('Http server closed successfully');
456
- }
457
- });
458
- this.httpServer.removeAllListeners();
459
- this.httpServer = undefined;
460
- this.log.debug('Frontend http server closed successfully');
461
- }
462
- if (this.httpsServer) {
463
- this.httpsServer.close((error) => {
464
- if (error) {
465
- this.log.error(`Error closing https server: ${error}`);
466
- }
467
- else {
468
- this.log.debug('Https server closed successfully');
469
- }
470
- });
471
- this.httpsServer.removeAllListeners();
472
- this.httpsServer = undefined;
473
- this.log.debug('Frontend https server closed successfully');
474
- }
449
+ this.log.debug('Stopping the frontend...');
475
450
  if (this.expressApp) {
476
451
  this.expressApp.removeAllListeners();
477
452
  this.expressApp = undefined;
@@ -483,16 +458,53 @@ export class Frontend {
483
458
  client.close();
484
459
  }
485
460
  });
486
- this.webSocketServer.close((error) => {
487
- if (error) {
488
- this.log.error(`Error closing WebSocket server: ${error}`);
489
- }
490
- else {
491
- this.log.debug('WebSocket server closed successfully');
492
- }
493
- });
461
+ await withTimeout(new Promise((resolve) => {
462
+ this.webSocketServer?.close((error) => {
463
+ if (error) {
464
+ this.log.error(`Error closing WebSocket server: ${error}`);
465
+ }
466
+ else {
467
+ this.log.debug('WebSocket server closed successfully');
468
+ }
469
+ resolve();
470
+ });
471
+ }), 10000, false);
472
+ this.webSocketServer.removeAllListeners();
494
473
  this.webSocketServer = undefined;
495
474
  }
475
+ if (this.httpServer) {
476
+ await withTimeout(new Promise((resolve) => {
477
+ this.httpServer?.close((error) => {
478
+ if (error) {
479
+ this.log.error(`Error closing http server: ${error}`);
480
+ }
481
+ else {
482
+ this.log.debug('Http server closed successfully');
483
+ }
484
+ resolve();
485
+ });
486
+ }), 10000, false);
487
+ this.httpServer.removeAllListeners();
488
+ this.httpServer = undefined;
489
+ this.log.debug('Frontend http server closed successfully');
490
+ }
491
+ if (this.httpsServer) {
492
+ await withTimeout(new Promise((resolve) => {
493
+ this.httpsServer?.close((error) => {
494
+ if (error) {
495
+ this.log.error(`Error closing https server: ${error}`);
496
+ }
497
+ else {
498
+ this.log.debug('Https server closed successfully');
499
+ }
500
+ resolve();
501
+ });
502
+ }), 10000, false);
503
+ this.httpsServer.removeAllListeners();
504
+ this.httpsServer = undefined;
505
+ this.log.debug('Frontend https server closed successfully');
506
+ }
507
+ this.log.debug('Frontend stopped successfully');
496
508
  }
497
509
  formatMemoryUsage = (bytes) => {
498
510
  if (bytes >= 1024 ** 3) {
package/dist/index.js CHANGED
@@ -1,9 +1,6 @@
1
1
  import { Matterbridge } from './matterbridge.js';
2
2
  import { hasParameter } from './utils/export.js';
3
3
  import { AnsiLogger } from './logger/export.js';
4
- export { SemanticNamespace, ClosureTag, CompassDirectionTag, CompassLocationTag, DirectionTag, ElectricalMeasurementTag, LaundryTag, LevelTag, LocationTag, NumberTag, PositionTag, PowerSourceTag, RefrigeratorTag, RoomAirConditionerTag, SwitchesTag, } from '@matter/main';
5
- export * from '@matter/main/clusters';
6
- export * from '@matter/main/types';
7
4
  export * from './matterbridge.js';
8
5
  export * from './matterbridgeTypes.js';
9
6
  export * from './matterbridgeEndpoint.js';
@@ -14,6 +11,7 @@ export * from './matterbridgePlatform.js';
14
11
  export * from './matterbridgeAccessoryPlatform.js';
15
12
  export * from './matterbridgeDynamicPlatform.js';
16
13
  export * from './roboticVacuumCleaner.js';
14
+ export { addVirtualDevice } from './helpers.js';
17
15
  const log = new AnsiLogger({ logName: 'Main', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
18
16
  async function main() {
19
17
  log.debug('***Matterbridge.loadInstance() called');
@@ -212,12 +212,28 @@ export class Matterbridge extends EventEmitter {
212
212
  });
213
213
  }
214
214
  async initialize() {
215
+ this.emit('initialize_started');
215
216
  if (hasParameter('service'))
216
217
  this.restartMode = 'service';
217
218
  if (hasParameter('docker'))
218
219
  this.restartMode = 'docker';
219
220
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
221
+ this.matterbridgeInformation.homeDirectory = this.homeDirectory;
222
+ await this.createDirectory(this.homeDirectory, 'Matterbridge Home Directory');
220
223
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
224
+ this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
225
+ await this.createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory');
226
+ await this.createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory');
227
+ this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
228
+ this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
229
+ await this.createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory');
230
+ this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
231
+ this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
232
+ await this.createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory');
233
+ const { fileURLToPath } = await import('node:url');
234
+ const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
235
+ this.rootDirectory = path.resolve(currentFileDirectory, '../');
236
+ this.matterbridgeInformation.rootDirectory = this.rootDirectory;
221
237
  this.environment.vars.set('log.level', MatterLogLevel.INFO);
222
238
  this.environment.vars.set('log.format', MatterLogFormat.ANSI);
223
239
  this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
@@ -267,7 +283,7 @@ export class Matterbridge extends EventEmitter {
267
283
  this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
268
284
  this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
269
285
  this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
270
- const pairingFilePath = path.join(this.homeDirectory, '.mattercert', 'pairing.json');
286
+ const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
271
287
  try {
272
288
  await fs.access(pairingFilePath, fs.constants.R_OK);
273
289
  const pairingFileContent = await fs.readFile(pairingFilePath, 'utf8');
@@ -275,7 +291,7 @@ export class Matterbridge extends EventEmitter {
275
291
  if (pairingFileJson.passcode && pairingFileJson.discriminator) {
276
292
  this.passcode = pairingFileJson.passcode;
277
293
  this.discriminator = pairingFileJson.discriminator;
278
- this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf}`);
294
+ this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
279
295
  }
280
296
  if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
281
297
  const hexStringToUint8Array = (hexString) => {
@@ -297,7 +313,7 @@ export class Matterbridge extends EventEmitter {
297
313
  await this.nodeContext.set('matterport', this.port);
298
314
  await this.nodeContext.set('matterpasscode', this.passcode);
299
315
  await this.nodeContext.set('matterdiscriminator', this.discriminator);
300
- this.log.debug(`Initializing server node for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
316
+ this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
301
317
  if (hasParameter('logger')) {
302
318
  const level = getParameter('logger');
303
319
  if (level === 'debug') {
@@ -502,6 +518,7 @@ export class Matterbridge extends EventEmitter {
502
518
  throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
503
519
  }
504
520
  await this.parseCommandLine();
521
+ this.emit('initialize_completed');
505
522
  this.initialized = true;
506
523
  }
507
524
  async parseCommandLine() {
@@ -517,7 +534,9 @@ export class Matterbridge extends EventEmitter {
517
534
  - ipv6address [address]: set the ipv6 interface address to use for the matter listener (default all interfaces)
518
535
  - frontend [port]: start the frontend on the given port (default 8283)
519
536
  - logger: set the matterbridge logger level: debug | info | notice | warn | error | fatal (default info)
537
+ - filelogger enable the matterbridge file logger (matterbridge.log)
520
538
  - matterlogger: set the matter.js logger level: debug | info | notice | warn | error | fatal (default info)
539
+ - matterfilelogger enable the matter.js file logger (matter.log)
521
540
  - reset: remove the commissioning for Matterbridge (bridge mode). Shutdown Matterbridge before using it!
522
541
  - factoryreset: remove all commissioning information and reset all internal storages. Shutdown Matterbridge before using it!
523
542
  - list: list the registered plugins
@@ -526,13 +545,14 @@ export class Matterbridge extends EventEmitter {
526
545
  - sudo: force the use of sudo to install or update packages if the internal logic fails
527
546
  - nosudo: force not to use sudo to install or update packages if the internal logic fails
528
547
  - norestore: force not to automatically restore the matterbridge node storage and the matter storage from backup if it is corrupted
548
+ - novirtual: disable the creation of the virtual devices Restart, Update and Reboot Matterbridge
529
549
  - ssl: enable SSL for the frontend and WebSockerServer (certificates in .matterbridge/certs directory cert.pem, key.pem and ca.pem (optional))
530
550
  - vendorId: override the default vendorId 0xfff1
531
551
  - vendorName: override the default vendorName "Matterbridge"
532
552
  - productId: override the default productId 0x8000
533
553
  - productName: override the default productName "Matterbridge aggregator"
534
554
  - service: enable the service mode (used in the systemctl configuration file)
535
- - docker: enable the docker mode (used in the Dockerfile to build the docker image)
555
+ - docker: enable the docker mode (used in the docker image)
536
556
  - homedir: override the home directory (default: os.homedir())
537
557
  - add [plugin path]: register the plugin from the given absolute or relative path
538
558
  - add [plugin name]: register the globally installed plugin with the given name
@@ -644,11 +664,11 @@ export class Matterbridge extends EventEmitter {
644
664
  this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
645
665
  }
646
666
  else {
647
- await matterStorageManager?.createContext('events')?.clearAll();
648
- await matterStorageManager?.createContext('fabrics')?.clearAll();
649
- await matterStorageManager?.createContext('root')?.clearAll();
650
- await matterStorageManager?.createContext('sessions')?.clearAll();
651
- await matterStorageManager?.createContext('persist')?.clearAll();
667
+ await matterStorageManager.createContext('events')?.clearAll();
668
+ await matterStorageManager.createContext('fabrics')?.clearAll();
669
+ await matterStorageManager.createContext('root')?.clearAll();
670
+ await matterStorageManager.createContext('sessions')?.clearAll();
671
+ await matterStorageManager.createContext('persist')?.clearAll();
652
672
  this.log.info(`Reset commissionig for plugin ${plg}${plugin.name}${nf} done! Remove the device from the controller.`);
653
673
  }
654
674
  }
@@ -751,7 +771,7 @@ export class Matterbridge extends EventEmitter {
751
771
  };
752
772
  process.on('SIGTERM', this.sigtermHandler);
753
773
  }
754
- deregisterProcesslHandlers() {
774
+ deregisterProcessHandlers() {
755
775
  this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
756
776
  if (this.exceptionHandler)
757
777
  process.off('uncaughtException', this.exceptionHandler);
@@ -826,22 +846,18 @@ export class Matterbridge extends EventEmitter {
826
846
  this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
827
847
  this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
828
848
  this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
829
- this.homeDirectory = getParameter('homedir') ?? os.homedir();
830
- this.matterbridgeInformation.homeDirectory = this.homeDirectory;
831
- this.log.debug(`Home Directory: ${this.homeDirectory}`);
832
- const { fileURLToPath } = await import('node:url');
833
- const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
834
- this.rootDirectory = path.resolve(currentFileDirectory, '../');
835
- this.matterbridgeInformation.rootDirectory = this.rootDirectory;
836
849
  this.log.debug(`Root Directory: ${this.rootDirectory}`);
850
+ this.log.debug(`Home Directory: ${this.homeDirectory}`);
851
+ this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
852
+ this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
853
+ this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
837
854
  if (this.nodeContext)
838
855
  this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
839
856
  if (this.globalModulesDirectory === '') {
840
857
  try {
841
858
  this.execRunningCount++;
842
- this.globalModulesDirectory = await getGlobalNodeModules();
859
+ this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory = await getGlobalNodeModules();
843
860
  this.execRunningCount--;
844
- this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
845
861
  this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
846
862
  await this.nodeContext?.set('globalModulesDirectory', this.globalModulesDirectory);
847
863
  }
@@ -851,82 +867,16 @@ export class Matterbridge extends EventEmitter {
851
867
  }
852
868
  else
853
869
  this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
854
- this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
855
- this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
856
- try {
857
- await fs.access(this.matterbridgeDirectory);
858
- }
859
- catch (err) {
860
- if (err instanceof Error) {
861
- const nodeErr = err;
862
- if (nodeErr.code === 'ENOENT') {
863
- try {
864
- await fs.mkdir(this.matterbridgeDirectory, { recursive: true });
865
- this.log.info(`Created Matterbridge Directory: ${this.matterbridgeDirectory}`);
866
- }
867
- catch (err) {
868
- this.log.error(`Error creating directory: ${err}`);
869
- }
870
- }
871
- else {
872
- this.log.error(`Error accessing directory: ${err}`);
873
- }
874
- }
875
- }
876
- this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
877
- this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
878
- this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
879
- try {
880
- await fs.access(this.matterbridgePluginDirectory);
881
- }
882
- catch (err) {
883
- if (err instanceof Error) {
884
- const nodeErr = err;
885
- if (nodeErr.code === 'ENOENT') {
886
- try {
887
- await fs.mkdir(this.matterbridgePluginDirectory, { recursive: true });
888
- this.log.info(`Created Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
889
- }
890
- catch (err) {
891
- this.log.error(`Error creating directory: ${err}`);
892
- }
893
- }
894
- else {
895
- this.log.error(`Error accessing directory: ${err}`);
896
- }
897
- }
898
- }
899
- this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
900
- this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
901
- this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
902
- try {
903
- await fs.access(this.matterbridgeCertDirectory);
904
- }
905
- catch (err) {
906
- if (err instanceof Error) {
907
- const nodeErr = err;
908
- if (nodeErr.code === 'ENOENT') {
909
- try {
910
- await fs.mkdir(this.matterbridgeCertDirectory, { recursive: true });
911
- this.log.info(`Created .mattercert directory: ${this.matterbridgeCertDirectory}`);
912
- }
913
- catch (err) {
914
- this.log.error(`Error creating .mattercert directory: ${err}`);
915
- }
916
- }
917
- else {
918
- this.log.error(`Error accessing .mattercert directory: ${err}`);
919
- }
920
- }
921
- }
922
- this.log.debug(`Matterbridge Matter Cert Directory: ${this.matterbridgeCertDirectory}`);
923
870
  const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
924
- this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
925
- this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
871
+ this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
872
+ this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeDevVersion = packageJson.version;
926
873
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
927
874
  if (this.nodeContext)
928
- this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
875
+ this.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
929
876
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
877
+ if (this.nodeContext)
878
+ this.matterbridgeDevVersion = this.matterbridgeInformation.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
879
+ this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
930
880
  const currentDir = process.cwd();
931
881
  this.log.debug(`Current Working Directory: ${currentDir}`);
932
882
  const cmdArgs = process.argv.slice(2).join(' ');
@@ -934,11 +884,11 @@ export class Matterbridge extends EventEmitter {
934
884
  }
935
885
  createMatterLogger() {
936
886
  const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
937
- return (_level, formattedLog) => {
887
+ return (level, formattedLog) => {
938
888
  const logger = formattedLog.slice(44, 44 + 20).trim();
939
889
  const message = formattedLog.slice(65);
940
890
  matterLogger.logName = logger;
941
- switch (_level) {
891
+ switch (level) {
942
892
  case MatterLogLevel.DEBUG:
943
893
  matterLogger.log("debug", message);
944
894
  break;
@@ -973,7 +923,7 @@ export class Matterbridge extends EventEmitter {
973
923
  this.log.debug(`Error unlinking the log file ${CYAN}${filePath}${db}: ${error instanceof Error ? error.message : error}`);
974
924
  }
975
925
  }
976
- return async (_level, formattedLog) => {
926
+ return async (level, formattedLog) => {
977
927
  if (fileSize > 100000000)
978
928
  return;
979
929
  fileSize += formattedLog.length;
@@ -987,7 +937,7 @@ export class Matterbridge extends EventEmitter {
987
937
  const parts = message.split(' ');
988
938
  const logger = parts[1];
989
939
  const finalMessage = parts.slice(2).join(' ') + os.EOL;
990
- switch (_level) {
940
+ switch (level) {
991
941
  case MatterLogLevel.DEBUG:
992
942
  await fs.appendFile(filePath, `[${timestamp}] [${logger}] [debug] ${finalMessage}`);
993
943
  break;
@@ -1113,12 +1063,13 @@ export class Matterbridge extends EventEmitter {
1113
1063
  await this.matterbridgeContext?.clearAll();
1114
1064
  this.log.info('Matter storage reset done! Remove the bridge from the controller.');
1115
1065
  }
1116
- await this.stopMatterStorage();
1117
- await this.frontend.stop();
1066
+ await withTimeout(this.stopMatterStorage(), 10000, false);
1067
+ await withTimeout(this.frontend.stop(), 10000, false);
1118
1068
  try {
1119
1069
  Logger.removeLogger('matterfilelogger');
1120
1070
  }
1121
1071
  catch (error) {
1072
+ this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${db}: ${error instanceof Error ? error.message : String(error)}`);
1122
1073
  }
1123
1074
  if (this.nodeStorage && this.nodeContext) {
1124
1075
  this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
@@ -1136,53 +1087,40 @@ export class Matterbridge extends EventEmitter {
1136
1087
  this.nodeStorage = undefined;
1137
1088
  }
1138
1089
  else {
1139
- this.log.error('Error saving registered devices: nodeContext not found!');
1090
+ this.log.error('Error close the matterbridge node storage and context: nodeStorage or nodeContext not found!');
1140
1091
  }
1141
1092
  this.plugins.clear();
1142
1093
  this.devices.clear();
1143
1094
  if (message === 'shutting down with factory reset...') {
1144
1095
  try {
1145
- const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
1146
- this.log.info(`Unlinking old matter storage file: ${file}`);
1147
- await fs.unlink(file);
1148
- const backup = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.backup.json');
1149
- this.log.info(`Unlinking old matter storage backup file: ${backup}`);
1150
- await fs.unlink(backup);
1151
- }
1152
- catch (err) {
1153
- if (err instanceof Error && err.code !== 'ENOENT') {
1154
- this.log.error(`Error unlinking old matter storage file: ${err}`);
1155
- }
1156
- }
1157
- try {
1158
- const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
1159
- this.log.info(`Removing matter node storage directory: ${dir}`);
1096
+ const dir = path.join(this.matterbridgeDirectory, this.matterStorageName);
1097
+ this.log.info(`Removing matter storage directory: ${dir}`);
1160
1098
  await fs.rm(dir, { recursive: true });
1161
- const backup = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.backup');
1162
- this.log.info(`Removing matter node storage backup directory: ${backup}`);
1099
+ const backup = path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup');
1100
+ this.log.info(`Removing matter storage backup directory: ${backup}`);
1163
1101
  await fs.rm(backup, { recursive: true });
1164
1102
  }
1165
- catch (err) {
1166
- if (err instanceof Error && err.code !== 'ENOENT') {
1167
- this.log.error(`Error removing matter storage directory: ${err}`);
1103
+ catch (error) {
1104
+ if (error instanceof Error && error.code !== 'ENOENT') {
1105
+ this.log.error(`Error removing matter storage directory: ${error}`);
1168
1106
  }
1169
1107
  }
1170
1108
  try {
1171
- const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
1172
- this.log.info(`Removing storage directory: ${dir}`);
1109
+ const dir = path.join(this.matterbridgeDirectory, this.nodeStorageName);
1110
+ this.log.info(`Removing matterbridge storage directory: ${dir}`);
1173
1111
  await fs.rm(dir, { recursive: true });
1174
- const backup = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.backup');
1175
- this.log.info(`Removing storage backup directory: ${backup}`);
1112
+ const backup = path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup');
1113
+ this.log.info(`Removing matterbridge storage backup directory: ${backup}`);
1176
1114
  await fs.rm(backup, { recursive: true });
1177
1115
  }
1178
- catch (err) {
1179
- if (err instanceof Error && err.code !== 'ENOENT') {
1180
- this.log.error(`Error removing storage directory: ${err}`);
1116
+ catch (error) {
1117
+ if (error instanceof Error && error.code !== 'ENOENT') {
1118
+ this.log.error(`Error removing matterbridge storage directory: ${error}`);
1181
1119
  }
1182
1120
  }
1183
1121
  this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
1184
1122
  }
1185
- this.deregisterProcesslHandlers();
1123
+ this.deregisterProcessHandlers();
1186
1124
  if (restart) {
1187
1125
  if (message === 'updating...') {
1188
1126
  this.log.info('Cleanup completed. Updating...');
@@ -1692,7 +1630,7 @@ export class Matterbridge extends EventEmitter {
1692
1630
  }
1693
1631
  }
1694
1632
  async createAggregatorNode(storageContext) {
1695
- this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
1633
+ this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
1696
1634
  const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
1697
1635
  return aggregatorNode;
1698
1636
  }
@@ -1972,4 +1910,26 @@ export class Matterbridge extends EventEmitter {
1972
1910
  }
1973
1911
  });
1974
1912
  }
1913
+ async createDirectory(path, name) {
1914
+ try {
1915
+ await fs.access(path);
1916
+ }
1917
+ catch (err) {
1918
+ if (err instanceof Error) {
1919
+ const nodeErr = err;
1920
+ if (nodeErr.code === 'ENOENT') {
1921
+ try {
1922
+ await fs.mkdir(path, { recursive: true });
1923
+ this.log.info(`Created ${name}: ${path}`);
1924
+ }
1925
+ catch (err) {
1926
+ this.log.error(`Error creating ${name}: ${err}`);
1927
+ }
1928
+ }
1929
+ else {
1930
+ this.log.error(`Error accessing ${name}: ${err}`);
1931
+ }
1932
+ }
1933
+ }
1934
+ }
1975
1935
  }
@@ -43,9 +43,16 @@ export async function wait(timeout = 1000, name, debug = false) {
43
43
  }, timeout);
44
44
  });
45
45
  }
46
- export function withTimeout(promise, ms) {
46
+ export function withTimeout(promise, timeoutMillisecs = 10000, reThrow = true) {
47
47
  return new Promise((resolve, reject) => {
48
- const timer = setTimeout(() => reject(new Error('Operation timed out')), ms);
48
+ const timer = setTimeout(() => {
49
+ if (reThrow) {
50
+ reject(new Error('Operation timed out'));
51
+ }
52
+ else {
53
+ resolve(undefined);
54
+ }
55
+ }, timeoutMillisecs).unref();
49
56
  promise
50
57
  .then((result) => {
51
58
  clearTimeout(timer);
@@ -53,7 +60,12 @@ export function withTimeout(promise, ms) {
53
60
  })
54
61
  .catch((error) => {
55
62
  clearTimeout(timer);
56
- reject(error);
63
+ if (reThrow) {
64
+ reject(error);
65
+ }
66
+ else {
67
+ resolve(undefined);
68
+ }
57
69
  });
58
70
  });
59
71
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.0.2-dev-20250517-4122c94",
3
+ "version": "3.0.3-dev-20250517-bcc5d13",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge",
9
- "version": "3.0.2-dev-20250517-4122c94",
9
+ "version": "3.0.3-dev-20250517-bcc5d13",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@matter/main": "0.13.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.0.2-dev-20250517-4122c94",
3
+ "version": "3.0.3-dev-20250517-bcc5d13",
4
4
  "description": "Matterbridge plugin manager for Matter",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",