matterbridge 3.0.1-dev-20250502-6d36575 → 3.0.1-dev-20250502-f374923
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 +6 -4
- package/README-DOCKER.md +2 -0
- package/dist/frontend.js +204 -207
- package/dist/matterbridge.js +8 -5
- package/dist/utils/isvalid.js +16 -0
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.2093c348.js → main.53d64feb.js} +3 -3
- package/frontend/build/static/js/{main.2093c348.js.map → main.53d64feb.js.map} +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- /package/frontend/build/static/js/{main.2093c348.js.LICENSE.txt → main.53d64feb.js.LICENSE.txt} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -17,15 +17,17 @@ It is also available the official Matterbridge Home Assistant plugin https://git
|
|
|
17
17
|
|
|
18
18
|
### Added
|
|
19
19
|
|
|
20
|
-
- [docker]: The builder for the **docker** image with tag **latest** will run each day at 00:00 UTC. Inside the image matterbridge and all plugins with the latest release (as published on npm) are already loaded. You can just pull the new image and matterbridge with all plugins will be the latest.
|
|
21
|
-
- [docker]: The builder for the **docker** image with tag **dev** will run each day at 00:00 UTC. Inside the image matterbridge and all plugins with the dev release (as pushed on GitHub) are already loaded. You can just pull the new image and matterbridge with all plugins will be the latest dev
|
|
22
|
-
- [npm]: The dev of matterbridge is published with tag **dev** on **npm** each day at 00:00 UTC if there is a new commit. It is possible that the dev is outdated by published latest.
|
|
20
|
+
- [docker]: The builder for the **docker** image with tag **latest** will run each day at 00:00 UTC if there are new releases. Inside the image matterbridge and all plugins with the latest release (as published on npm) are already loaded. You can just pull the new image and matterbridge with all plugins will be the latest.
|
|
21
|
+
- [docker]: The builder for the **docker** image with tag **dev** will run each day at 00:00 UTC if there are new commits. Inside the image matterbridge and all plugins with the dev release (as pushed on GitHub) are already loaded. You can just pull the new image and matterbridge with all plugins will be the latest dev. It is possible that the devs are outdated by some published latests.
|
|
22
|
+
- [npm]: The dev of matterbridge is published with tag **dev** on **npm** each day at 00:00 UTC if there is a new commit. It is possible that the dev is outdated by a published latest.
|
|
23
23
|
- [frontend]: Added closeSnackbarMessage() to remove the notification with timeout = 0.
|
|
24
|
-
- [frontend]: Moved plugin actions from express to web socket.
|
|
24
|
+
- [frontend]: Moved all plugin actions from express to web socket.
|
|
25
|
+
- [frontend]: Moved all config from express to web socket.
|
|
25
26
|
|
|
26
27
|
### Changed
|
|
27
28
|
|
|
28
29
|
- [docker]: Updated the [Docker configurations](README-DOCKER.md).
|
|
30
|
+
- [frontend]: Changing configuration for a plugin now only lock configuration on that plugin.
|
|
29
31
|
|
|
30
32
|
## [3.0.0] - 2025-04-29
|
|
31
33
|
|
package/README-DOCKER.md
CHANGED
|
@@ -22,6 +22,8 @@ The image (tag **latest**) includes matterbridge and all plugins with the latest
|
|
|
22
22
|
|
|
23
23
|
The image (tag **dev**) includes matterbridge and all plugins with the dev release (as pushed on GitHub). You can just pull the new image and matterbridge with all plugins will be the latest release pushed on GitHub. It is possible that the devs are outdated by published latests.
|
|
24
24
|
|
|
25
|
+
You can directly select and add a plugin without installing it.
|
|
26
|
+
|
|
25
27
|
It is based on node:22-bookworm-slim and integrates the health check.
|
|
26
28
|
|
|
27
29
|
How Health Checks Work in Different Scenarios
|
package/dist/frontend.js
CHANGED
|
@@ -413,192 +413,6 @@ export class Frontend {
|
|
|
413
413
|
}
|
|
414
414
|
});
|
|
415
415
|
});
|
|
416
|
-
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
417
|
-
const command = req.params.command;
|
|
418
|
-
let param = req.params.param;
|
|
419
|
-
this.log.debug(`The frontend sent /api/command/${command}/${param}`);
|
|
420
|
-
if (!command) {
|
|
421
|
-
res.status(400).json({ error: 'No command provided' });
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
425
|
-
if (command === 'setpassword') {
|
|
426
|
-
const password = param.slice(1, -1);
|
|
427
|
-
this.log.debug('setpassword', param, password);
|
|
428
|
-
await this.matterbridge.nodeContext?.set('password', password);
|
|
429
|
-
res.json({ message: 'Command received' });
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
if (command === 'setbridgemode') {
|
|
433
|
-
this.log.debug(`setbridgemode: ${param}`);
|
|
434
|
-
this.wssSendRestartRequired();
|
|
435
|
-
await this.matterbridge.nodeContext?.set('bridgeMode', param);
|
|
436
|
-
res.json({ message: 'Command received' });
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
if (command === 'backup') {
|
|
440
|
-
this.log.notice(`Prepairing the backup...`);
|
|
441
|
-
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
|
|
442
|
-
this.log.notice(`Backup ready to be downloaded.`);
|
|
443
|
-
this.wssSendSnackbarMessage('Backup ready to be downloaded', 10);
|
|
444
|
-
res.json({ message: 'Command received' });
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
if (command === 'setmbloglevel') {
|
|
448
|
-
this.log.debug('Matterbridge log level:', param);
|
|
449
|
-
if (param === 'Debug') {
|
|
450
|
-
this.log.logLevel = "debug";
|
|
451
|
-
}
|
|
452
|
-
else if (param === 'Info') {
|
|
453
|
-
this.log.logLevel = "info";
|
|
454
|
-
}
|
|
455
|
-
else if (param === 'Notice') {
|
|
456
|
-
this.log.logLevel = "notice";
|
|
457
|
-
}
|
|
458
|
-
else if (param === 'Warn') {
|
|
459
|
-
this.log.logLevel = "warn";
|
|
460
|
-
}
|
|
461
|
-
else if (param === 'Error') {
|
|
462
|
-
this.log.logLevel = "error";
|
|
463
|
-
}
|
|
464
|
-
else if (param === 'Fatal') {
|
|
465
|
-
this.log.logLevel = "fatal";
|
|
466
|
-
}
|
|
467
|
-
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
468
|
-
await this.matterbridge.setLogLevel(this.log.logLevel);
|
|
469
|
-
res.json({ message: 'Command received' });
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
472
|
-
if (command === 'setmjloglevel') {
|
|
473
|
-
this.log.debug('Matter.js log level:', param);
|
|
474
|
-
if (param === 'Debug') {
|
|
475
|
-
Logger.defaultLogLevel = MatterLogLevel.DEBUG;
|
|
476
|
-
}
|
|
477
|
-
else if (param === 'Info') {
|
|
478
|
-
Logger.defaultLogLevel = MatterLogLevel.INFO;
|
|
479
|
-
}
|
|
480
|
-
else if (param === 'Notice') {
|
|
481
|
-
Logger.defaultLogLevel = MatterLogLevel.NOTICE;
|
|
482
|
-
}
|
|
483
|
-
else if (param === 'Warn') {
|
|
484
|
-
Logger.defaultLogLevel = MatterLogLevel.WARN;
|
|
485
|
-
}
|
|
486
|
-
else if (param === 'Error') {
|
|
487
|
-
Logger.defaultLogLevel = MatterLogLevel.ERROR;
|
|
488
|
-
}
|
|
489
|
-
else if (param === 'Fatal') {
|
|
490
|
-
Logger.defaultLogLevel = MatterLogLevel.FATAL;
|
|
491
|
-
}
|
|
492
|
-
await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.defaultLogLevel);
|
|
493
|
-
res.json({ message: 'Command received' });
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
if (command === 'setmdnsinterface') {
|
|
497
|
-
if (param === 'json' && isValidString(req.body.value)) {
|
|
498
|
-
this.matterbridge.matterbridgeInformation.mattermdnsinterface = req.body.value;
|
|
499
|
-
this.log.debug(`Matter.js mdns interface: ${req.body.value === '' ? 'all interfaces' : req.body.value}`);
|
|
500
|
-
await this.matterbridge.nodeContext?.set('mattermdnsinterface', req.body.value);
|
|
501
|
-
}
|
|
502
|
-
res.json({ message: 'Command received' });
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
if (command === 'setipv4address') {
|
|
506
|
-
if (param === 'json' && isValidString(req.body.value)) {
|
|
507
|
-
this.log.debug(`Matter.js ipv4 address: ${req.body.value === '' ? 'all ipv4 addresses' : req.body.value}`);
|
|
508
|
-
this.matterbridge.matterbridgeInformation.matteripv4address = req.body.value;
|
|
509
|
-
await this.matterbridge.nodeContext?.set('matteripv4address', req.body.value);
|
|
510
|
-
}
|
|
511
|
-
res.json({ message: 'Command received' });
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
if (command === 'setipv6address') {
|
|
515
|
-
if (param === 'json' && isValidString(req.body.value)) {
|
|
516
|
-
this.log.debug(`Matter.js ipv6 address: ${req.body.value === '' ? 'all ipv6 addresses' : req.body.value}`);
|
|
517
|
-
this.matterbridge.matterbridgeInformation.matteripv6address = req.body.value;
|
|
518
|
-
await this.matterbridge.nodeContext?.set('matteripv6address', req.body.value);
|
|
519
|
-
}
|
|
520
|
-
res.json({ message: 'Command received' });
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
if (command === 'setmatterport') {
|
|
524
|
-
const port = Math.min(Math.max(parseInt(req.body.value), 5540), 5560);
|
|
525
|
-
this.matterbridge.matterbridgeInformation.matterPort = port;
|
|
526
|
-
this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
|
|
527
|
-
await this.matterbridge.nodeContext?.set('matterport', port);
|
|
528
|
-
res.json({ message: 'Command received' });
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
if (command === 'setmatterdiscriminator') {
|
|
532
|
-
const discriminator = Math.min(Math.max(parseInt(req.body.value), 1000), 4095);
|
|
533
|
-
this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
|
|
534
|
-
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
|
|
535
|
-
await this.matterbridge.nodeContext?.set('matterdiscriminator', discriminator);
|
|
536
|
-
res.json({ message: 'Command received' });
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
if (command === 'setmatterpasscode') {
|
|
540
|
-
const passcode = Math.min(Math.max(parseInt(req.body.value), 10000000), 90000000);
|
|
541
|
-
this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
|
|
542
|
-
this.log.debug(`Set matter commissioning passcode to ${CYAN}${passcode}${db}`);
|
|
543
|
-
await this.matterbridge.nodeContext?.set('matterpasscode', passcode);
|
|
544
|
-
res.json({ message: 'Command received' });
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
if (command === 'setmblogfile') {
|
|
548
|
-
this.log.debug('Matterbridge file log:', param);
|
|
549
|
-
this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
|
|
550
|
-
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
|
|
551
|
-
if (param === 'true')
|
|
552
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
|
|
553
|
-
else
|
|
554
|
-
AnsiLogger.setGlobalLogfile(undefined);
|
|
555
|
-
res.json({ message: 'Command received' });
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
if (command === 'setmjlogfile') {
|
|
559
|
-
this.log.debug('Matter file log:', param);
|
|
560
|
-
this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
|
|
561
|
-
await this.matterbridge.nodeContext?.set('matterFileLog', param === 'true');
|
|
562
|
-
if (param === 'true') {
|
|
563
|
-
try {
|
|
564
|
-
Logger.addLogger('matterfilelogger', await this.matterbridge.createMatterFileLogger(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), true), {
|
|
565
|
-
defaultLogLevel: MatterLogLevel.DEBUG,
|
|
566
|
-
logFormat: MatterLogFormat.PLAIN,
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
catch (error) {
|
|
570
|
-
this.log.debug(`Error adding the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
else {
|
|
574
|
-
try {
|
|
575
|
-
Logger.removeLogger('matterfilelogger');
|
|
576
|
-
}
|
|
577
|
-
catch (error) {
|
|
578
|
-
this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
res.json({ message: 'Command received' });
|
|
582
|
-
return;
|
|
583
|
-
}
|
|
584
|
-
if (command === 'saveconfig') {
|
|
585
|
-
param = param.replace(/\*/g, '\\');
|
|
586
|
-
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
587
|
-
if (!this.matterbridge.plugins.has(param)) {
|
|
588
|
-
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
589
|
-
}
|
|
590
|
-
else {
|
|
591
|
-
const plugin = this.matterbridge.plugins.get(param);
|
|
592
|
-
if (!plugin)
|
|
593
|
-
return;
|
|
594
|
-
this.matterbridge.plugins.saveConfigFromJson(plugin, req.body);
|
|
595
|
-
this.wssSendSnackbarMessage(`Saved config for plugin ${param}`);
|
|
596
|
-
this.wssSendRestartRequired();
|
|
597
|
-
}
|
|
598
|
-
res.json({ message: 'Command received' });
|
|
599
|
-
return;
|
|
600
|
-
}
|
|
601
|
-
});
|
|
602
416
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
603
417
|
const { filename } = req.body;
|
|
604
418
|
const file = req.file;
|
|
@@ -607,7 +421,7 @@ export class Frontend {
|
|
|
607
421
|
res.status(400).send('Invalid request: file and filename are required');
|
|
608
422
|
return;
|
|
609
423
|
}
|
|
610
|
-
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait
|
|
424
|
+
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
611
425
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
612
426
|
try {
|
|
613
427
|
await fs.rename(file.path, filePath);
|
|
@@ -615,6 +429,7 @@ export class Frontend {
|
|
|
615
429
|
if (filename.endsWith('.tgz')) {
|
|
616
430
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
617
431
|
this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
|
|
432
|
+
this.wssSendCloseSnackbarMessage(`Installing package ${filename}. Please wait...`);
|
|
618
433
|
this.wssSendSnackbarMessage(`Installed package ${filename}`, 10, 'success');
|
|
619
434
|
this.wssSendRestartRequired();
|
|
620
435
|
res.send(`Plugin package ${filename} uploaded and installed successfully`);
|
|
@@ -624,6 +439,7 @@ export class Frontend {
|
|
|
624
439
|
}
|
|
625
440
|
catch (err) {
|
|
626
441
|
this.log.error(`Error uploading or installing plugin package file ${plg}${filename}${er}:`, err);
|
|
442
|
+
this.wssSendCloseSnackbarMessage(`Installing package ${filename}. Please wait...`);
|
|
627
443
|
this.wssSendSnackbarMessage(`Error uploading or installing plugin package ${filename}`, 10, 'error');
|
|
628
444
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
629
445
|
}
|
|
@@ -911,6 +727,7 @@ export class Frontend {
|
|
|
911
727
|
started: plugin.started,
|
|
912
728
|
configured: plugin.configured,
|
|
913
729
|
paired: plugin.paired,
|
|
730
|
+
restartRequired: plugin.restartRequired,
|
|
914
731
|
fabricInformations: plugin.fabricInformations,
|
|
915
732
|
sessionInformations: plugin.sessionInformations,
|
|
916
733
|
registeredDevices: plugin.registeredDevices,
|
|
@@ -1026,7 +843,6 @@ export class Frontend {
|
|
|
1026
843
|
this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
|
|
1027
844
|
this.wssSendSnackbarMessage(`Package ${data.params.packageName} not installed`, 10, 'error');
|
|
1028
845
|
});
|
|
1029
|
-
return;
|
|
1030
846
|
}
|
|
1031
847
|
else if (data.method === '/api/uninstall') {
|
|
1032
848
|
if (!isValidString(data.params.packageName, 10)) {
|
|
@@ -1130,51 +946,63 @@ export class Frontend {
|
|
|
1130
946
|
this.wssSendRefreshRequired('plugins');
|
|
1131
947
|
this.wssSendRefreshRequired('devices');
|
|
1132
948
|
}
|
|
949
|
+
else if (data.method === '/api/savepluginconfig') {
|
|
950
|
+
if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
|
|
951
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/savepluginconfig' }));
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
if (!isValidObject(data.params.formData, 5)) {
|
|
955
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter formData in /api/savepluginconfig' }));
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
this.log.info(`Saving config for plugin ${plg}${data.params.pluginName}${nf}...`);
|
|
959
|
+
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
960
|
+
if (!plugin) {
|
|
961
|
+
this.log.warn(`Plugin ${plg}${data.params.pluginName}${wr} not found in matterbridge`);
|
|
962
|
+
}
|
|
963
|
+
else {
|
|
964
|
+
this.matterbridge.plugins.saveConfigFromJson(plugin, data.params.formData);
|
|
965
|
+
this.wssSendSnackbarMessage(`Saved config for plugin ${data.params.pluginName}`);
|
|
966
|
+
this.wssSendRefreshRequired('plugins');
|
|
967
|
+
this.wssSendRestartRequired();
|
|
968
|
+
}
|
|
969
|
+
}
|
|
1133
970
|
else if (data.method === '/api/shellysysupdate') {
|
|
1134
971
|
const { triggerShellySysUpdate } = await import('./shelly.js');
|
|
1135
972
|
triggerShellySysUpdate(this.matterbridge);
|
|
1136
|
-
return;
|
|
1137
973
|
}
|
|
1138
974
|
else if (data.method === '/api/shellymainupdate') {
|
|
1139
975
|
const { triggerShellyMainUpdate } = await import('./shelly.js');
|
|
1140
976
|
triggerShellyMainUpdate(this.matterbridge);
|
|
1141
|
-
return;
|
|
1142
977
|
}
|
|
1143
978
|
else if (data.method === '/api/shellycreatesystemlog') {
|
|
1144
979
|
const { createShellySystemLog } = await import('./shelly.js');
|
|
1145
980
|
createShellySystemLog(this.matterbridge);
|
|
1146
|
-
return;
|
|
1147
981
|
}
|
|
1148
982
|
else if (data.method === '/api/shellynetconfig') {
|
|
1149
983
|
this.log.debug('/api/shellynetconfig:', data.params);
|
|
1150
984
|
const { triggerShellyChangeIp: triggerShellyChangeNet } = await import('./shelly.js');
|
|
1151
985
|
triggerShellyChangeNet(this.matterbridge, data.params);
|
|
1152
|
-
return;
|
|
1153
986
|
}
|
|
1154
987
|
else if (data.method === '/api/softreset') {
|
|
1155
988
|
const { triggerShellySoftReset } = await import('./shelly.js');
|
|
1156
989
|
triggerShellySoftReset(this.matterbridge);
|
|
1157
|
-
return;
|
|
1158
990
|
}
|
|
1159
991
|
else if (data.method === '/api/hardreset') {
|
|
1160
992
|
const { triggerShellyHardReset } = await import('./shelly.js');
|
|
1161
993
|
triggerShellyHardReset(this.matterbridge);
|
|
1162
|
-
return;
|
|
1163
994
|
}
|
|
1164
995
|
else if (data.method === '/api/reboot') {
|
|
1165
996
|
const { triggerShellyReboot } = await import('./shelly.js');
|
|
1166
997
|
triggerShellyReboot(this.matterbridge);
|
|
1167
|
-
return;
|
|
1168
998
|
}
|
|
1169
999
|
else if (data.method === '/api/restart') {
|
|
1170
1000
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1171
1001
|
await this.matterbridge.restartProcess();
|
|
1172
|
-
return;
|
|
1173
1002
|
}
|
|
1174
1003
|
else if (data.method === '/api/shutdown') {
|
|
1175
1004
|
this.wssSendSnackbarMessage(`Shutting down matterbridge...`, 0);
|
|
1176
1005
|
await this.matterbridge.shutdownProcess();
|
|
1177
|
-
return;
|
|
1178
1006
|
}
|
|
1179
1007
|
else if (data.method === '/api/create-backup') {
|
|
1180
1008
|
this.wssSendSnackbarMessage('Creating backup...', 0);
|
|
@@ -1204,7 +1032,6 @@ export class Frontend {
|
|
|
1204
1032
|
this.wssSendRefreshRequired('matterbridgeAdvertise');
|
|
1205
1033
|
this.wssSendSnackbarMessage(`Started fabrics share`, 0);
|
|
1206
1034
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes }));
|
|
1207
|
-
return;
|
|
1208
1035
|
}
|
|
1209
1036
|
else if (data.method === '/api/stopadvertise') {
|
|
1210
1037
|
await this.matterbridge.stopAdvertiseServerNode(this.matterbridge.serverNode);
|
|
@@ -1212,21 +1039,17 @@ export class Frontend {
|
|
|
1212
1039
|
this.wssSendRefreshRequired('matterbridgeAdvertise');
|
|
1213
1040
|
this.wssSendSnackbarMessage(`Stopped fabrics share`, 0);
|
|
1214
1041
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src }));
|
|
1215
|
-
return;
|
|
1216
1042
|
}
|
|
1217
1043
|
else if (data.method === '/api/settings') {
|
|
1218
1044
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: await this.getApiSettings() }));
|
|
1219
|
-
return;
|
|
1220
1045
|
}
|
|
1221
1046
|
else if (data.method === '/api/plugins') {
|
|
1222
1047
|
const response = this.getBaseRegisteredPlugins();
|
|
1223
1048
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
|
|
1224
|
-
return;
|
|
1225
1049
|
}
|
|
1226
1050
|
else if (data.method === '/api/devices') {
|
|
1227
1051
|
const devices = await this.getDevices(isValidString(data.params.pluginName) ? data.params.pluginName : undefined);
|
|
1228
1052
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: devices }));
|
|
1229
|
-
return;
|
|
1230
1053
|
}
|
|
1231
1054
|
else if (data.method === '/api/clusters') {
|
|
1232
1055
|
if (!isValidString(data.params.plugin, 10)) {
|
|
@@ -1333,7 +1156,6 @@ export class Frontend {
|
|
|
1333
1156
|
});
|
|
1334
1157
|
});
|
|
1335
1158
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, deviceName, serialNumber, endpoint: data.params.endpoint, deviceTypes, response: clusters }));
|
|
1336
|
-
return;
|
|
1337
1159
|
}
|
|
1338
1160
|
else if (data.method === '/api/select' || data.method === '/api/select/devices') {
|
|
1339
1161
|
if (!isValidString(data.params.plugin, 10)) {
|
|
@@ -1347,7 +1169,6 @@ export class Frontend {
|
|
|
1347
1169
|
}
|
|
1348
1170
|
const selectDeviceValues = plugin.platform?.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1349
1171
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectDeviceValues }));
|
|
1350
|
-
return;
|
|
1351
1172
|
}
|
|
1352
1173
|
else if (data.method === '/api/select/entities') {
|
|
1353
1174
|
if (!isValidString(data.params.plugin, 10)) {
|
|
@@ -1361,7 +1182,6 @@ export class Frontend {
|
|
|
1361
1182
|
}
|
|
1362
1183
|
const selectEntityValues = plugin.platform?.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1363
1184
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectEntityValues }));
|
|
1364
|
-
return;
|
|
1365
1185
|
}
|
|
1366
1186
|
else if (data.method === '/api/action') {
|
|
1367
1187
|
if (!isValidString(data.params.plugin, 5) || !isValidString(data.params.action, 1)) {
|
|
@@ -1378,6 +1198,185 @@ export class Frontend {
|
|
|
1378
1198
|
this.log.error(`Error in plugin ${plugin.name} action ${data.params.action}: ${error}`);
|
|
1379
1199
|
});
|
|
1380
1200
|
}
|
|
1201
|
+
else if (data.method === '/api/config') {
|
|
1202
|
+
if (!isValidString(data.params.name, 5) || data.params.value === undefined) {
|
|
1203
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/config' }));
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
this.log.debug(`Received /api/config name ${CYAN}${data.params.name}${db} value ${CYAN}${data.params.value}${db}`);
|
|
1207
|
+
this.log.fatal(`Received /api/config name ${CYAN}${data.params.name}${db} value(${typeof data.params.value}) ${CYAN}${data.params.value}${db}`);
|
|
1208
|
+
switch (data.params.name) {
|
|
1209
|
+
case 'setpassword':
|
|
1210
|
+
if (isValidString(data.params.value)) {
|
|
1211
|
+
await this.matterbridge.nodeContext?.set('password', data.params.value);
|
|
1212
|
+
}
|
|
1213
|
+
break;
|
|
1214
|
+
case 'setbridgemode':
|
|
1215
|
+
if (isValidString(data.params.value, 5)) {
|
|
1216
|
+
await this.matterbridge.nodeContext?.set('bridgeMode', data.params.value);
|
|
1217
|
+
this.wssSendRestartRequired();
|
|
1218
|
+
}
|
|
1219
|
+
break;
|
|
1220
|
+
case 'setmbloglevel':
|
|
1221
|
+
if (isValidString(data.params.value, 4)) {
|
|
1222
|
+
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1223
|
+
if (data.params.value === 'Debug') {
|
|
1224
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1225
|
+
}
|
|
1226
|
+
else if (data.params.value === 'Info') {
|
|
1227
|
+
await this.matterbridge.setLogLevel("info");
|
|
1228
|
+
}
|
|
1229
|
+
else if (data.params.value === 'Notice') {
|
|
1230
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1231
|
+
}
|
|
1232
|
+
else if (data.params.value === 'Warn') {
|
|
1233
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1234
|
+
}
|
|
1235
|
+
else if (data.params.value === 'Error') {
|
|
1236
|
+
await this.matterbridge.setLogLevel("error");
|
|
1237
|
+
}
|
|
1238
|
+
else if (data.params.value === 'Fatal') {
|
|
1239
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1240
|
+
}
|
|
1241
|
+
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1242
|
+
}
|
|
1243
|
+
break;
|
|
1244
|
+
case 'setmblogfile':
|
|
1245
|
+
if (isValidBoolean(data.params.value)) {
|
|
1246
|
+
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1247
|
+
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1248
|
+
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1249
|
+
if (data.params.value)
|
|
1250
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1251
|
+
else
|
|
1252
|
+
AnsiLogger.setGlobalLogfile(undefined);
|
|
1253
|
+
}
|
|
1254
|
+
break;
|
|
1255
|
+
case 'setmjloglevel':
|
|
1256
|
+
if (isValidString(data.params.value, 4)) {
|
|
1257
|
+
this.log.debug('Matter logger level:', data.params.value);
|
|
1258
|
+
if (data.params.value === 'Debug') {
|
|
1259
|
+
Logger.level = MatterLogLevel.DEBUG;
|
|
1260
|
+
}
|
|
1261
|
+
else if (data.params.value === 'Info') {
|
|
1262
|
+
Logger.level = MatterLogLevel.INFO;
|
|
1263
|
+
}
|
|
1264
|
+
else if (data.params.value === 'Notice') {
|
|
1265
|
+
Logger.level = MatterLogLevel.NOTICE;
|
|
1266
|
+
}
|
|
1267
|
+
else if (data.params.value === 'Warn') {
|
|
1268
|
+
Logger.level = MatterLogLevel.WARN;
|
|
1269
|
+
}
|
|
1270
|
+
else if (data.params.value === 'Error') {
|
|
1271
|
+
Logger.level = MatterLogLevel.ERROR;
|
|
1272
|
+
}
|
|
1273
|
+
else if (data.params.value === 'Fatal') {
|
|
1274
|
+
Logger.level = MatterLogLevel.FATAL;
|
|
1275
|
+
}
|
|
1276
|
+
this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.level;
|
|
1277
|
+
await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.level);
|
|
1278
|
+
}
|
|
1279
|
+
break;
|
|
1280
|
+
case 'setmjlogfile':
|
|
1281
|
+
if (isValidBoolean(data.params.value)) {
|
|
1282
|
+
this.log.debug('Matter file log:', data.params.value);
|
|
1283
|
+
this.matterbridge.matterbridgeInformation.matterFileLogger = data.params.value;
|
|
1284
|
+
await this.matterbridge.nodeContext?.set('matterFileLog', data.params.value);
|
|
1285
|
+
if (data.params.value) {
|
|
1286
|
+
try {
|
|
1287
|
+
Logger.addLogger('matterfilelogger', await this.matterbridge.createMatterFileLogger(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), true), {
|
|
1288
|
+
defaultLogLevel: this.matterbridge.matterbridgeInformation.matterLoggerLevel,
|
|
1289
|
+
logFormat: MatterLogFormat.PLAIN,
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
catch (error) {
|
|
1293
|
+
this.log.debug(`Error adding the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
else {
|
|
1297
|
+
try {
|
|
1298
|
+
Logger.removeLogger('matterfilelogger');
|
|
1299
|
+
}
|
|
1300
|
+
catch (error) {
|
|
1301
|
+
this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
break;
|
|
1306
|
+
case 'setmdnsinterface':
|
|
1307
|
+
if (isValidString(data.params.value)) {
|
|
1308
|
+
this.log.debug(`Matter.js mdns interface: ${data.params.value === '' ? 'all interfaces' : data.params.value}`);
|
|
1309
|
+
this.matterbridge.mdnsInterface = data.params.value === '' ? undefined : data.params.value;
|
|
1310
|
+
this.matterbridge.matterbridgeInformation.mattermdnsinterface = this.matterbridge.mdnsInterface;
|
|
1311
|
+
await this.matterbridge.nodeContext?.set('mattermdnsinterface', data.params.value);
|
|
1312
|
+
this.wssSendRestartRequired();
|
|
1313
|
+
}
|
|
1314
|
+
break;
|
|
1315
|
+
case 'setipv4address':
|
|
1316
|
+
if (isValidString(data.params.value)) {
|
|
1317
|
+
this.log.debug(`Matter.js ipv4 address: ${data.params.value === '' ? 'all ipv4 addresses' : data.params.value}`);
|
|
1318
|
+
this.matterbridge.ipv4address = data.params.value === '' ? undefined : data.params.value;
|
|
1319
|
+
this.matterbridge.matterbridgeInformation.matteripv4address = this.matterbridge.ipv4address;
|
|
1320
|
+
await this.matterbridge.nodeContext?.set('matteripv4address', data.params.value);
|
|
1321
|
+
this.wssSendRestartRequired();
|
|
1322
|
+
}
|
|
1323
|
+
break;
|
|
1324
|
+
case 'setipv6address':
|
|
1325
|
+
if (isValidString(data.params.value)) {
|
|
1326
|
+
this.log.debug(`Matter.js ipv6 address: ${data.params.value === '' ? 'all ipv6 addresses' : data.params.value}`);
|
|
1327
|
+
this.matterbridge.ipv6address = data.params.value === '' ? undefined : data.params.value;
|
|
1328
|
+
this.matterbridge.matterbridgeInformation.matteripv6address = this.matterbridge.ipv6address;
|
|
1329
|
+
await this.matterbridge.nodeContext?.set('matteripv6address', data.params.value);
|
|
1330
|
+
this.wssSendRestartRequired();
|
|
1331
|
+
}
|
|
1332
|
+
break;
|
|
1333
|
+
case 'setmatterport':
|
|
1334
|
+
data.params.value = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1335
|
+
if (isValidNumber(data.params.value, 5540, 5580)) {
|
|
1336
|
+
this.log.debug(`Set matter commissioning port to ${CYAN}${data.params.value}${db}`);
|
|
1337
|
+
this.matterbridge.matterbridgeInformation.matterPort = data.params.value;
|
|
1338
|
+
await this.matterbridge.nodeContext?.set('matterport', data.params.value);
|
|
1339
|
+
this.wssSendRestartRequired();
|
|
1340
|
+
}
|
|
1341
|
+
else {
|
|
1342
|
+
this.log.debug(`Reset matter commissioning port to ${CYAN}5040${db}`);
|
|
1343
|
+
this.matterbridge.matterbridgeInformation.matterPort = 5040;
|
|
1344
|
+
await this.matterbridge.nodeContext?.set('matterport', 5040);
|
|
1345
|
+
this.wssSendRestartRequired();
|
|
1346
|
+
}
|
|
1347
|
+
break;
|
|
1348
|
+
case 'setmatterdiscriminator':
|
|
1349
|
+
data.params.value = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1350
|
+
if (isValidNumber(data.params.value, 1000, 4095)) {
|
|
1351
|
+
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${data.params.value}${db}`);
|
|
1352
|
+
this.matterbridge.matterbridgeInformation.matterDiscriminator = data.params.value;
|
|
1353
|
+
await this.matterbridge.nodeContext?.set('matterdiscriminator', data.params.value);
|
|
1354
|
+
this.wssSendRestartRequired();
|
|
1355
|
+
}
|
|
1356
|
+
else {
|
|
1357
|
+
this.log.debug(`Reset matter commissioning discriminator to ${CYAN}undefined${db}`);
|
|
1358
|
+
this.matterbridge.matterbridgeInformation.matterDiscriminator = undefined;
|
|
1359
|
+
await this.matterbridge.nodeContext?.remove('matterdiscriminator');
|
|
1360
|
+
this.wssSendRestartRequired();
|
|
1361
|
+
}
|
|
1362
|
+
break;
|
|
1363
|
+
case 'setmatterpasscode':
|
|
1364
|
+
data.params.value = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1365
|
+
if (isValidNumber(data.params.value, 10000000, 90000000)) {
|
|
1366
|
+
this.matterbridge.matterbridgeInformation.matterPasscode = data.params.value;
|
|
1367
|
+
this.log.debug(`Set matter commissioning passcode to ${CYAN}${data.params.value}${db}`);
|
|
1368
|
+
await this.matterbridge.nodeContext?.set('matterpasscode', data.params.value);
|
|
1369
|
+
this.wssSendRestartRequired();
|
|
1370
|
+
}
|
|
1371
|
+
else {
|
|
1372
|
+
this.log.debug(`Reset matter commissioning passcode to ${CYAN}undefined${db}`);
|
|
1373
|
+
this.matterbridge.matterbridgeInformation.matterPasscode = undefined;
|
|
1374
|
+
await this.matterbridge.nodeContext?.remove('matterpasscode');
|
|
1375
|
+
this.wssSendRestartRequired();
|
|
1376
|
+
}
|
|
1377
|
+
break;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1381
1380
|
else if (data.method === '/api/command') {
|
|
1382
1381
|
if (!isValidString(data.params.command, 5)) {
|
|
1383
1382
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter command in /api/command' }));
|
|
@@ -1465,12 +1464,10 @@ export class Frontend {
|
|
|
1465
1464
|
else {
|
|
1466
1465
|
this.log.error(`Invalid method from websocket client: ${debugStringify(data)}`);
|
|
1467
1466
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid method' }));
|
|
1468
|
-
return;
|
|
1469
1467
|
}
|
|
1470
1468
|
}
|
|
1471
1469
|
catch (error) {
|
|
1472
1470
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1473
|
-
return;
|
|
1474
1471
|
}
|
|
1475
1472
|
}
|
|
1476
1473
|
wssSendMessage(level, time, name, message) {
|
package/dist/matterbridge.js
CHANGED
|
@@ -5,7 +5,7 @@ import EventEmitter from 'node:events';
|
|
|
5
5
|
import { inspect } from 'node:util';
|
|
6
6
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from './logger/export.js';
|
|
7
7
|
import { NodeStorageManager } from './storage/export.js';
|
|
8
|
-
import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout, waiter } from './utils/export.js';
|
|
8
|
+
import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout, waiter, isValidString, parseVersionString } from './utils/export.js';
|
|
9
9
|
import { logInterfaces, getGlobalNodeModules } from './utils/network.js';
|
|
10
10
|
import { PluginManager } from './pluginManager.js';
|
|
11
11
|
import { DeviceManager } from './deviceManager.js';
|
|
@@ -369,6 +369,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
369
369
|
if (!availableInterfaces.includes(this.mdnsInterface)) {
|
|
370
370
|
this.log.error(`Invalid mdnsInterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
|
|
371
371
|
this.mdnsInterface = undefined;
|
|
372
|
+
await this.nodeContext.remove('mattermdnsinterface');
|
|
372
373
|
}
|
|
373
374
|
else {
|
|
374
375
|
this.log.info(`Using mdnsInterface ${CYAN}${this.mdnsInterface}${nf} for the Matter MdnsBroadcaster.`);
|
|
@@ -396,6 +397,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
396
397
|
if (!isValid) {
|
|
397
398
|
this.log.error(`Invalid ipv4address: ${this.ipv4address}. Using all available addresses.`);
|
|
398
399
|
this.ipv4address = undefined;
|
|
400
|
+
await this.nodeContext.remove('matteripv4address');
|
|
399
401
|
}
|
|
400
402
|
}
|
|
401
403
|
if (hasParameter('ipv6address')) {
|
|
@@ -423,6 +425,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
423
425
|
if (!isValid) {
|
|
424
426
|
this.log.error(`Invalid ipv6address: ${this.ipv6address}. Using all available addresses.`);
|
|
425
427
|
this.ipv6address = undefined;
|
|
428
|
+
await this.nodeContext.remove('matteripv6address');
|
|
426
429
|
}
|
|
427
430
|
}
|
|
428
431
|
this.plugins = new PluginManager(this);
|
|
@@ -1403,10 +1406,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1403
1406
|
await storageContext.set('productLabel', productName.slice(0, 32));
|
|
1404
1407
|
await storageContext.set('serialNumber', await storageContext.get('serialNumber', serialNumber ? serialNumber.slice(0, 32) : 'SN' + random));
|
|
1405
1408
|
await storageContext.set('uniqueId', await storageContext.get('uniqueId', 'UI' + random));
|
|
1406
|
-
await storageContext.set('softwareVersion',
|
|
1407
|
-
await storageContext.set('softwareVersionString', this.matterbridgeVersion
|
|
1408
|
-
await storageContext.set('hardwareVersion',
|
|
1409
|
-
await storageContext.set('hardwareVersionString', this.systemInformation.osRelease
|
|
1409
|
+
await storageContext.set('softwareVersion', parseVersionString(this.matterbridgeVersion) || 1);
|
|
1410
|
+
await storageContext.set('softwareVersionString', isValidString(this.matterbridgeVersion, 5) ? this.matterbridgeVersion : '1.0.0');
|
|
1411
|
+
await storageContext.set('hardwareVersion', parseVersionString(this.systemInformation.osRelease) || 1);
|
|
1412
|
+
await storageContext.set('hardwareVersionString', isValidString(this.systemInformation.osRelease, 5) ? this.systemInformation.osRelease : '1.0.0');
|
|
1410
1413
|
this.log.debug(`Created server node storage context "${pluginName}.persist" for ${pluginName}:`);
|
|
1411
1414
|
this.log.debug(`- storeId: ${await storageContext.get('storeId')}`);
|
|
1412
1415
|
this.log.debug(`- deviceName: ${await storageContext.get('deviceName')}`);
|