matterbridge 3.1.1-dev-20250701-9281629 → 3.1.1-dev-20250704-aff5fcb
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 +5 -3
- package/dist/frontend.js +35 -13
- package/dist/matterbridge.js +19 -20
- package/dist/utils/spawn.js +61 -63
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.42525fab.js → main.1d25e0d8.js} +5 -5
- package/frontend/build/static/js/{main.42525fab.js.map → main.1d25e0d8.js.map} +1 -1
- package/frontend/package.json +1 -1
- package/npm-shrinkwrap.json +6 -6
- package/package.json +2 -2
- /package/frontend/build/static/js/{main.42525fab.js.LICENSE.txt → main.1d25e0d8.js.LICENSE.txt} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -8,11 +8,12 @@ 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.1.1] - 2025-07-
|
|
11
|
+
## [3.1.1] - 2025-07-08
|
|
12
12
|
|
|
13
13
|
### Development Breaking Changes
|
|
14
14
|
|
|
15
|
-
- [
|
|
15
|
+
- [exports]: The single devices (i.e. Rvc, Evse etc...) are only exported from `matterbridge/devices`. Please update your imports to use the new export path. Refer to the [documentation](README-DEV.md) for details on imports.
|
|
16
|
+
- [MatterbridgeEndpoint]: Added the mode property: `server` will make the device indipendent from its plugin. It has its own server node: QRCode, Fabrics and Sessions are visible in the Devices section of the Home page. This is a workaround for the Rvc Apple issue. With mode=server the Rvc (like any other device) can be paired directly to the controller like a native not bridged Matter device.
|
|
16
17
|
|
|
17
18
|
### Added
|
|
18
19
|
|
|
@@ -26,8 +27,9 @@ If you like this project and find it useful, please consider giving it a star on
|
|
|
26
27
|
|
|
27
28
|
- [package]: Updated dependencies.
|
|
28
29
|
- [frontend]: Added all esa devices.
|
|
29
|
-
- [frontend]: New default values:
|
|
30
|
+
- [frontend]: New default values: devices on the home page and icon view on the devices page.
|
|
30
31
|
- [matter.js]: Bumped `matter.js` to 0.15.1 (https://github.com/project-chip/matter.js/discussions/2220). Great job matter.js!
|
|
32
|
+
- [imports]: Added more dynamic imports to Matterbridge and Frontend classes.
|
|
31
33
|
|
|
32
34
|
<a href="https://www.buymeacoffee.com/luligugithub">
|
|
33
35
|
<img src="bmc-button.svg" alt="Buy me a coffee" width="80">
|
package/dist/frontend.js
CHANGED
|
@@ -3,6 +3,7 @@ import https from 'node:https';
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { promises as fs } from 'node:fs';
|
|
6
|
+
import EventEmitter from 'node:events';
|
|
6
7
|
import express from 'express';
|
|
7
8
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
8
9
|
import multer from 'multer';
|
|
@@ -12,7 +13,6 @@ import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/cluster
|
|
|
12
13
|
import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout, hasParameter } from './utils/export.js';
|
|
13
14
|
import { plg } from './matterbridgeTypes.js';
|
|
14
15
|
import { capitalizeFirstLetter } from './matterbridgeEndpointHelpers.js';
|
|
15
|
-
import spawn from './utils/spawn.js';
|
|
16
16
|
export const WS_ID_LOG = 0;
|
|
17
17
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
18
18
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
@@ -25,7 +25,7 @@ export const WS_ID_STATEUPDATE = 8;
|
|
|
25
25
|
export const WS_ID_CLOSE_SNACKBAR = 9;
|
|
26
26
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
27
27
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
28
|
-
export class Frontend {
|
|
28
|
+
export class Frontend extends EventEmitter {
|
|
29
29
|
matterbridge;
|
|
30
30
|
log;
|
|
31
31
|
port = 8283;
|
|
@@ -35,6 +35,7 @@ export class Frontend {
|
|
|
35
35
|
httpsServer;
|
|
36
36
|
webSocketServer;
|
|
37
37
|
constructor(matterbridge) {
|
|
38
|
+
super();
|
|
38
39
|
this.matterbridge = matterbridge;
|
|
39
40
|
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
40
41
|
}
|
|
@@ -54,6 +55,7 @@ export class Frontend {
|
|
|
54
55
|
if (hasParameter('ingress')) {
|
|
55
56
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
56
57
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
58
|
+
this.emit('server_listening', 'http', this.port, '0.0.0.0');
|
|
57
59
|
});
|
|
58
60
|
}
|
|
59
61
|
else {
|
|
@@ -62,6 +64,7 @@ export class Frontend {
|
|
|
62
64
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
|
|
63
65
|
if (this.matterbridge.systemInformation.ipv6Address !== '')
|
|
64
66
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
67
|
+
this.emit('server_listening', 'http', this.port);
|
|
65
68
|
});
|
|
66
69
|
}
|
|
67
70
|
this.httpServer.on('error', (error) => {
|
|
@@ -75,6 +78,7 @@ export class Frontend {
|
|
|
75
78
|
break;
|
|
76
79
|
}
|
|
77
80
|
this.initializeError = true;
|
|
81
|
+
this.emit('server_error', error);
|
|
78
82
|
return;
|
|
79
83
|
});
|
|
80
84
|
}
|
|
@@ -110,6 +114,7 @@ export class Frontend {
|
|
|
110
114
|
if (hasParameter('ingress')) {
|
|
111
115
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
112
116
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
117
|
+
this.emit('server_listening', 'https', this.port, '0.0.0.0');
|
|
113
118
|
});
|
|
114
119
|
}
|
|
115
120
|
else {
|
|
@@ -118,6 +123,7 @@ export class Frontend {
|
|
|
118
123
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
|
|
119
124
|
if (this.matterbridge.systemInformation.ipv6Address !== '')
|
|
120
125
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
126
|
+
this.emit('server_listening', 'https', this.port);
|
|
121
127
|
});
|
|
122
128
|
}
|
|
123
129
|
this.httpsServer.on('error', (error) => {
|
|
@@ -131,6 +137,7 @@ export class Frontend {
|
|
|
131
137
|
break;
|
|
132
138
|
}
|
|
133
139
|
this.initializeError = true;
|
|
140
|
+
this.emit('server_error', error);
|
|
134
141
|
return;
|
|
135
142
|
});
|
|
136
143
|
}
|
|
@@ -175,6 +182,7 @@ export class Frontend {
|
|
|
175
182
|
});
|
|
176
183
|
this.webSocketServer.on('listening', () => {
|
|
177
184
|
this.log.info(`The WebSocketServer is listening on ${UNDERLINE}${wssHost}${UNDERLINEOFF}${rs}`);
|
|
185
|
+
this.emit('websocket_server_listening', wssHost);
|
|
178
186
|
});
|
|
179
187
|
this.webSocketServer.on('error', (ws, error) => {
|
|
180
188
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
@@ -423,7 +431,8 @@ export class Frontend {
|
|
|
423
431
|
await fs.rename(file.path, filePath);
|
|
424
432
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
425
433
|
if (filename.endsWith('.tgz')) {
|
|
426
|
-
|
|
434
|
+
const { spawnCommand } = await import('./utils/spawn.js');
|
|
435
|
+
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
427
436
|
this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
|
|
428
437
|
this.wssSendCloseSnackbarMessage(`Installing package ${filename}. Please wait...`);
|
|
429
438
|
this.wssSendSnackbarMessage(`Installed package ${filename}`, 10, 'success');
|
|
@@ -566,6 +575,8 @@ export class Frontend {
|
|
|
566
575
|
return false;
|
|
567
576
|
if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
|
|
568
577
|
return device.getAttribute(BridgedDeviceBasicInformation.Cluster.id, 'reachable');
|
|
578
|
+
if (device.mode === 'server' && device.serverNode && device.serverNode.state.basicInformation.reachable !== undefined)
|
|
579
|
+
return device.serverNode.state.basicInformation.reachable;
|
|
569
580
|
if (this.matterbridge.bridgeMode === 'childbridge')
|
|
570
581
|
return true;
|
|
571
582
|
return false;
|
|
@@ -592,6 +603,17 @@ export class Frontend {
|
|
|
592
603
|
return powerSource(child);
|
|
593
604
|
}
|
|
594
605
|
}
|
|
606
|
+
getMatterDataFromDevice(device) {
|
|
607
|
+
if (device.mode === 'server' && device.serverNode && device.serverContext) {
|
|
608
|
+
return {
|
|
609
|
+
commissioned: device.serverNode.state.commissioning.commissioned,
|
|
610
|
+
qrPairingCode: device.serverNode.state.commissioning.pairingCodes.qrPairingCode,
|
|
611
|
+
manualPairingCode: device.serverNode.state.commissioning.pairingCodes.manualPairingCode,
|
|
612
|
+
fabricInformations: this.matterbridge.sanitizeFabricInformations(Object.values(device.serverNode.state.commissioning.fabrics)),
|
|
613
|
+
sessionInformations: this.matterbridge.sanitizeSessionInformation(Object.values(device.serverNode.state.sessions.sessions)),
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
}
|
|
595
617
|
getClusterTextFromDevice(device) {
|
|
596
618
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
597
619
|
return '';
|
|
@@ -766,12 +788,11 @@ export class Frontend {
|
|
|
766
788
|
}
|
|
767
789
|
async getDevices(pluginName) {
|
|
768
790
|
const devices = [];
|
|
769
|
-
this.matterbridge.devices.
|
|
791
|
+
for (const device of this.matterbridge.devices.array()) {
|
|
770
792
|
if (pluginName && pluginName !== device.plugin)
|
|
771
|
-
|
|
793
|
+
continue;
|
|
772
794
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
773
|
-
|
|
774
|
-
const cluster = this.getClusterTextFromDevice(device);
|
|
795
|
+
continue;
|
|
775
796
|
devices.push({
|
|
776
797
|
pluginName: device.plugin,
|
|
777
798
|
type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
|
|
@@ -783,9 +804,10 @@ export class Frontend {
|
|
|
783
804
|
uniqueId: device.uniqueId,
|
|
784
805
|
reachable: this.getReachability(device),
|
|
785
806
|
powerSource: this.getPowerSource(device),
|
|
786
|
-
|
|
807
|
+
matter: this.getMatterDataFromDevice(device),
|
|
808
|
+
cluster: this.getClusterTextFromDevice(device),
|
|
787
809
|
});
|
|
788
|
-
}
|
|
810
|
+
}
|
|
789
811
|
return devices;
|
|
790
812
|
}
|
|
791
813
|
getClusters(pluginName, endpointNumber) {
|
|
@@ -884,8 +906,8 @@ export class Frontend {
|
|
|
884
906
|
return;
|
|
885
907
|
}
|
|
886
908
|
this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}...`, 0);
|
|
887
|
-
spawn
|
|
888
|
-
|
|
909
|
+
const { spawnCommand } = await import('./utils/spawn.js');
|
|
910
|
+
spawnCommand(this.matterbridge, 'npm', ['install', '-g', data.params.packageName, '--omit=dev', '--verbose'])
|
|
889
911
|
.then((response) => {
|
|
890
912
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
|
|
891
913
|
this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
|
|
@@ -949,8 +971,8 @@ export class Frontend {
|
|
|
949
971
|
this.wssSendRefreshRequired('devices');
|
|
950
972
|
}
|
|
951
973
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
952
|
-
spawn
|
|
953
|
-
|
|
974
|
+
const { spawnCommand } = await import('./utils/spawn.js');
|
|
975
|
+
spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
954
976
|
.then((response) => {
|
|
955
977
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
|
|
956
978
|
this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
|
package/dist/matterbridge.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { promises as fs } from 'node:fs';
|
|
4
4
|
import EventEmitter from 'node:events';
|
|
5
5
|
import { inspect } from 'node:util';
|
|
6
|
-
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from 'node-ansi-logger';
|
|
6
|
+
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt } from 'node-ansi-logger';
|
|
7
7
|
import { NodeStorageManager } from 'node-persist-manager';
|
|
8
8
|
import { DeviceTypeId, Endpoint, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode, UINT32_MAX, UINT16_MAX, Crypto, } from '@matter/main';
|
|
9
9
|
import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
|
|
@@ -11,7 +11,6 @@ import { AggregatorEndpoint } from '@matter/main/endpoints';
|
|
|
11
11
|
import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
|
|
12
12
|
import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
|
|
13
13
|
import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout, waiter, isValidString, parseVersionString, isValidNumber, createDirectory } from './utils/export.js';
|
|
14
|
-
import { logInterfaces, getGlobalNodeModules } from './utils/network.js';
|
|
15
14
|
import { dev, plg, typ } from './matterbridgeTypes.js';
|
|
16
15
|
import { PluginManager } from './pluginManager.js';
|
|
17
16
|
import { DeviceManager } from './deviceManager.js';
|
|
@@ -19,7 +18,6 @@ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
|
19
18
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
20
19
|
import { Frontend } from './frontend.js';
|
|
21
20
|
import { addVirtualDevices } from './helpers.js';
|
|
22
|
-
import spawn from './utils/spawn.js';
|
|
23
21
|
export class Matterbridge extends EventEmitter {
|
|
24
22
|
systemInformation = {
|
|
25
23
|
interfaceName: '',
|
|
@@ -385,15 +383,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
385
383
|
}
|
|
386
384
|
Logger.format = MatterLogFormat.ANSI;
|
|
387
385
|
Logger.setLogger('default', this.createMatterLogger());
|
|
388
|
-
this.matterbridgeInformation.matterLoggerLevel = Logger.
|
|
386
|
+
this.matterbridgeInformation.matterLoggerLevel = Logger.level;
|
|
389
387
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
390
388
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
391
389
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
392
|
-
defaultLogLevel: Logger.
|
|
390
|
+
defaultLogLevel: Logger.level,
|
|
393
391
|
logFormat: MatterLogFormat.PLAIN,
|
|
394
392
|
});
|
|
395
393
|
}
|
|
396
|
-
this.log.debug(`Matter logLevel: ${Logger.
|
|
394
|
+
this.log.debug(`Matter logLevel: ${Logger.level} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
397
395
|
const networkInterfaces = os.networkInterfaces();
|
|
398
396
|
const availableAddresses = Object.entries(networkInterfaces);
|
|
399
397
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -494,7 +492,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
494
492
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
495
493
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
496
494
|
try {
|
|
497
|
-
|
|
495
|
+
const { spawnCommand } = await import('./utils/spawn.js');
|
|
496
|
+
await spawnCommand(this, 'npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
498
497
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} reinstalled.`);
|
|
499
498
|
plugin.error = false;
|
|
500
499
|
}
|
|
@@ -605,6 +604,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
605
604
|
return;
|
|
606
605
|
}
|
|
607
606
|
if (hasParameter('loginterfaces')) {
|
|
607
|
+
const { logInterfaces } = await import('./utils/network.js');
|
|
608
608
|
this.log.info(`${plg}Matterbridge${nf} network interfaces log`);
|
|
609
609
|
logInterfaces();
|
|
610
610
|
this.shutdown = true;
|
|
@@ -667,7 +667,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
667
667
|
await matterStorageManager.createContext('root')?.clearAll();
|
|
668
668
|
await matterStorageManager.createContext('sessions')?.clearAll();
|
|
669
669
|
await matterStorageManager.createContext('persist')?.clearAll();
|
|
670
|
-
this.log.
|
|
670
|
+
this.log.notice(`Reset commissioning for plugin ${plg}${plugin.name}${nt} done! Remove the device from the controller.`);
|
|
671
671
|
}
|
|
672
672
|
}
|
|
673
673
|
else {
|
|
@@ -679,10 +679,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
679
679
|
}
|
|
680
680
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
681
681
|
await this.frontend.start(getIntParameter('frontend'));
|
|
682
|
+
clearTimeout(this.checkUpdateTimeout);
|
|
682
683
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
683
684
|
const { checkUpdates } = await import('./update.js');
|
|
684
685
|
checkUpdates(this);
|
|
685
686
|
}, 30 * 1000).unref();
|
|
687
|
+
clearInterval(this.checkUpdateInterval);
|
|
686
688
|
this.checkUpdateInterval = setInterval(async () => {
|
|
687
689
|
const { checkUpdates } = await import('./update.js');
|
|
688
690
|
checkUpdates(this);
|
|
@@ -853,6 +855,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
853
855
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
854
856
|
if (this.globalModulesDirectory === '') {
|
|
855
857
|
try {
|
|
858
|
+
const { getGlobalNodeModules } = await import('./utils/network.js');
|
|
856
859
|
this.execRunningCount++;
|
|
857
860
|
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory = await getGlobalNodeModules();
|
|
858
861
|
this.execRunningCount--;
|
|
@@ -905,9 +908,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
905
908
|
case MatterLogLevel.FATAL:
|
|
906
909
|
matterLogger.log("fatal", message);
|
|
907
910
|
break;
|
|
908
|
-
default:
|
|
909
|
-
matterLogger.log("debug", message);
|
|
910
|
-
break;
|
|
911
911
|
}
|
|
912
912
|
};
|
|
913
913
|
}
|
|
@@ -922,11 +922,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
922
922
|
}
|
|
923
923
|
}
|
|
924
924
|
return async (level, formattedLog) => {
|
|
925
|
-
if (fileSize > 100000000)
|
|
925
|
+
if (fileSize > 100000000) {
|
|
926
926
|
return;
|
|
927
|
+
}
|
|
927
928
|
fileSize += formattedLog.length;
|
|
928
929
|
if (fileSize > 100000000) {
|
|
929
|
-
await fs.appendFile(filePath, `Logging on file has been
|
|
930
|
+
await fs.appendFile(filePath, `Logging on file has been stopped because the file size is greater than 100MB.` + os.EOL);
|
|
930
931
|
return;
|
|
931
932
|
}
|
|
932
933
|
const now = new Date();
|
|
@@ -954,9 +955,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
954
955
|
case MatterLogLevel.FATAL:
|
|
955
956
|
await fs.appendFile(filePath, `[${timestamp}] [${logger}] [fatal] ${finalMessage}`);
|
|
956
957
|
break;
|
|
957
|
-
default:
|
|
958
|
-
await fs.appendFile(filePath, `[${timestamp}] [${logger}] ${finalMessage}`);
|
|
959
|
-
break;
|
|
960
958
|
}
|
|
961
959
|
};
|
|
962
960
|
}
|
|
@@ -969,11 +967,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
969
967
|
async updateProcess() {
|
|
970
968
|
this.log.info('Updating matterbridge...');
|
|
971
969
|
try {
|
|
972
|
-
|
|
970
|
+
const { spawnCommand } = await import('./utils/spawn.js');
|
|
971
|
+
await spawnCommand(this, 'npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose']);
|
|
973
972
|
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
974
973
|
}
|
|
975
974
|
catch (error) {
|
|
976
|
-
this.log.error(
|
|
975
|
+
this.log.error(`Error updating matterbridge: ${error instanceof Error ? error.message : error}`);
|
|
977
976
|
}
|
|
978
977
|
this.frontend.wssSendRestartRequired();
|
|
979
978
|
await this.cleanup('updating...', false);
|
|
@@ -1849,8 +1848,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1849
1848
|
};
|
|
1850
1849
|
});
|
|
1851
1850
|
}
|
|
1852
|
-
sanitizeSessionInformation(
|
|
1853
|
-
return
|
|
1851
|
+
sanitizeSessionInformation(sessions) {
|
|
1852
|
+
return sessions
|
|
1854
1853
|
.filter((session) => session.isPeerActive)
|
|
1855
1854
|
.map((session) => {
|
|
1856
1855
|
return {
|
package/dist/utils/spawn.js
CHANGED
|
@@ -1,68 +1,66 @@
|
|
|
1
1
|
import { hasParameter } from './commandLine.js';
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
matterbridge.log.debug(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`);
|
|
30
|
-
resolve(true);
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
matterbridge.log.error(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`);
|
|
34
|
-
reject(new Error(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`));
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
childProcess.on('exit', (code, signal) => {
|
|
38
|
-
matterbridge.frontend.wssSendMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn', `child process exited with code ${code} and signal ${signal}`);
|
|
39
|
-
if (code === 0) {
|
|
40
|
-
matterbridge.log.debug(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`);
|
|
41
|
-
resolve(true);
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
matterbridge.log.error(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`);
|
|
45
|
-
reject(new Error(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`));
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
childProcess.on('disconnect', () => {
|
|
49
|
-
matterbridge.log.debug(`Child process "${cmdLine}" has been disconnected from the parent`);
|
|
2
|
+
export async function spawnCommand(matterbridge, command, args) {
|
|
3
|
+
const { spawn } = await import('node:child_process');
|
|
4
|
+
const cmdLine = command + ' ' + args.join(' ');
|
|
5
|
+
if (process.platform === 'win32' && command === 'npm') {
|
|
6
|
+
const argstring = 'npm ' + args.join(' ');
|
|
7
|
+
args.splice(0, args.length, '/c', argstring);
|
|
8
|
+
command = 'cmd.exe';
|
|
9
|
+
}
|
|
10
|
+
if (hasParameter('sudo') || (process.platform !== 'win32' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
11
|
+
args.unshift(command);
|
|
12
|
+
command = 'sudo';
|
|
13
|
+
}
|
|
14
|
+
matterbridge.log.debug(`Spawn command ${command} with ${args.join(' ')}`);
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const childProcess = spawn(command, args, {
|
|
17
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
18
|
+
});
|
|
19
|
+
childProcess.on('error', (err) => {
|
|
20
|
+
matterbridge.log.error(`Failed to start child process "${cmdLine}": ${err.message}`);
|
|
21
|
+
reject(err);
|
|
22
|
+
});
|
|
23
|
+
childProcess.on('close', (code, signal) => {
|
|
24
|
+
matterbridge.frontend.wssSendMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn', `child process closed with code ${code} and signal ${signal}`);
|
|
25
|
+
if (code === 0) {
|
|
26
|
+
if (cmdLine.startsWith('npm install -g'))
|
|
27
|
+
matterbridge.log.notice(`Package ${cmdLine.replace('npm install -g ', '').replace('--verbose', '').replace('--omit=dev', '')} installed correctly`);
|
|
28
|
+
matterbridge.log.debug(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`);
|
|
50
29
|
resolve(true);
|
|
51
|
-
});
|
|
52
|
-
if (childProcess.stdout) {
|
|
53
|
-
childProcess.stdout.on('data', (data) => {
|
|
54
|
-
const message = data.toString().trim();
|
|
55
|
-
matterbridge.log.debug(`Spawn output (stdout): ${message}`);
|
|
56
|
-
matterbridge.frontend.wssSendMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn', message);
|
|
57
|
-
});
|
|
58
30
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
matterbridge.log.debug(`Spawn verbose (stderr): ${message}`);
|
|
63
|
-
matterbridge.frontend.wssSendMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn', message);
|
|
64
|
-
});
|
|
31
|
+
else {
|
|
32
|
+
matterbridge.log.error(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`);
|
|
33
|
+
reject(new Error(`Child process "${cmdLine}" closed with code ${code} and signal ${signal}`));
|
|
65
34
|
}
|
|
66
35
|
});
|
|
67
|
-
|
|
68
|
-
};
|
|
36
|
+
childProcess.on('exit', (code, signal) => {
|
|
37
|
+
matterbridge.frontend.wssSendMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn', `child process exited with code ${code} and signal ${signal}`);
|
|
38
|
+
if (code === 0) {
|
|
39
|
+
matterbridge.log.debug(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`);
|
|
40
|
+
resolve(true);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
matterbridge.log.error(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`);
|
|
44
|
+
reject(new Error(`Child process "${cmdLine}" exited with code ${code} and signal ${signal}`));
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
childProcess.on('disconnect', () => {
|
|
48
|
+
matterbridge.log.debug(`Child process "${cmdLine}" has been disconnected from the parent`);
|
|
49
|
+
resolve(true);
|
|
50
|
+
});
|
|
51
|
+
if (childProcess.stdout) {
|
|
52
|
+
childProcess.stdout.on('data', (data) => {
|
|
53
|
+
const message = data.toString().trim();
|
|
54
|
+
matterbridge.log.debug(`Spawn output (stdout): ${message}`);
|
|
55
|
+
matterbridge.frontend.wssSendMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn', message);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (childProcess.stderr) {
|
|
59
|
+
childProcess.stderr.on('data', (data) => {
|
|
60
|
+
const message = data.toString().trim();
|
|
61
|
+
matterbridge.log.debug(`Spawn verbose (stderr): ${message}`);
|
|
62
|
+
matterbridge.frontend.wssSendMessage('spawn', matterbridge.log.now(), 'Matterbridge:spawn', message);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": {
|
|
3
3
|
"main.css": "./static/css/main.944b63c3.css",
|
|
4
|
-
"main.js": "./static/js/main.
|
|
4
|
+
"main.js": "./static/js/main.1d25e0d8.js",
|
|
5
5
|
"static/js/453.d855a71b.chunk.js": "./static/js/453.d855a71b.chunk.js",
|
|
6
6
|
"static/media/roboto-latin-700-normal.woff2": "./static/media/roboto-latin-700-normal.c4d6cab43bec89049809.woff2",
|
|
7
7
|
"static/media/roboto-latin-500-normal.woff2": "./static/media/roboto-latin-500-normal.599f66a60bdf974e578e.woff2",
|
|
@@ -77,11 +77,11 @@
|
|
|
77
77
|
"static/media/roboto-greek-ext-300-normal.woff": "./static/media/roboto-greek-ext-300-normal.60729cafbded24073dfb.woff",
|
|
78
78
|
"index.html": "./index.html",
|
|
79
79
|
"main.944b63c3.css.map": "./static/css/main.944b63c3.css.map",
|
|
80
|
-
"main.
|
|
80
|
+
"main.1d25e0d8.js.map": "./static/js/main.1d25e0d8.js.map",
|
|
81
81
|
"453.d855a71b.chunk.js.map": "./static/js/453.d855a71b.chunk.js.map"
|
|
82
82
|
},
|
|
83
83
|
"entrypoints": [
|
|
84
84
|
"static/css/main.944b63c3.css",
|
|
85
|
-
"static/js/main.
|
|
85
|
+
"static/js/main.1d25e0d8.js"
|
|
86
86
|
]
|
|
87
87
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="./"><link rel="icon" href="./matterbridge 32x32.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>Matterbridge</title><link rel="manifest" href="./manifest.json"/><script defer="defer" src="./static/js/main.
|
|
1
|
+
<!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="./"><link rel="icon" href="./matterbridge 32x32.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>Matterbridge</title><link rel="manifest" href="./manifest.json"/><script defer="defer" src="./static/js/main.1d25e0d8.js"></script><link href="./static/css/main.944b63c3.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|