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 +20 -3
- package/dist/frontend.js +47 -35
- package/dist/index.js +1 -3
- package/dist/matterbridge.js +87 -127
- package/dist/utils/wait.js +15 -3
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
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.
|
|
11
|
+
## [3.0.3] - 2025-05-??
|
|
12
12
|
|
|
13
13
|
### Added
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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');
|
package/dist/matterbridge.js
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
|
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
|
|
648
|
-
await matterStorageManager
|
|
649
|
-
await matterStorageManager
|
|
650
|
-
await matterStorageManager
|
|
651
|
-
await matterStorageManager
|
|
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
|
-
|
|
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.
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
|
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
|
|
1146
|
-
this.log.info(`
|
|
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,
|
|
1162
|
-
this.log.info(`Removing matter
|
|
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 (
|
|
1166
|
-
if (
|
|
1167
|
-
this.log.error(`Error removing matter storage directory: ${
|
|
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,
|
|
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,
|
|
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 (
|
|
1179
|
-
if (
|
|
1180
|
-
this.log.error(`Error removing storage directory: ${
|
|
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.
|
|
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
|
}
|
package/dist/utils/wait.js
CHANGED
|
@@ -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,
|
|
46
|
+
export function withTimeout(promise, timeoutMillisecs = 10000, reThrow = true) {
|
|
47
47
|
return new Promise((resolve, reject) => {
|
|
48
|
-
const timer = setTimeout(() =>
|
|
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
|
-
|
|
63
|
+
if (reThrow) {
|
|
64
|
+
reject(error);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
resolve(undefined);
|
|
68
|
+
}
|
|
57
69
|
});
|
|
58
70
|
});
|
|
59
71
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge",
|
|
3
|
-
"version": "3.0.
|
|
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.
|
|
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