matterbridge 2.2.0 → 2.2.2-dev.1
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 +31 -0
- package/README.md +2 -2
- package/dist/cli.js +2 -37
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -94
- package/dist/frontend.js +124 -283
- package/dist/index.js +1 -28
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.js +0 -2
- package/dist/matter/types.js +0 -2
- package/dist/matterbridge.js +63 -732
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +1 -32
- package/dist/matterbridgeDeviceTypes.js +11 -112
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEndpoint.js +6 -690
- package/dist/matterbridgeEndpointHelpers.js +9 -118
- package/dist/matterbridgePlatform.js +67 -153
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +6 -229
- package/dist/shelly.js +7 -122
- package/dist/storage/export.js +0 -1
- package/dist/update.js +3 -47
- package/dist/utils/colorUtils.js +2 -205
- package/dist/utils/copyDirectory.js +1 -37
- package/dist/utils/createZip.js +2 -42
- package/dist/utils/deepCopy.js +0 -40
- package/dist/utils/deepEqual.js +1 -65
- package/dist/utils/export.js +0 -1
- package/dist/utils/isvalid.js +0 -86
- package/dist/utils/network.js +5 -77
- package/dist/utils/parameter.js +0 -41
- package/dist/utils/wait.js +5 -48
- package/frontend/build/asset-manifest.json +6 -6
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/css/{main.cf25d33e.css → main.b9449869.css} +2 -2
- package/frontend/build/static/css/{main.cf25d33e.css.map → main.b9449869.css.map} +1 -1
- package/frontend/build/static/js/main.92802eb1.js +115 -0
- package/frontend/build/static/js/main.92802eb1.js.map +1 -0
- package/npm-shrinkwrap.json +49 -49
- package/package.json +2 -3
- package/dist/cli.d.ts +0 -29
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cluster/export.d.ts +0 -2
- package/dist/cluster/export.d.ts.map +0 -1
- package/dist/cluster/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -27
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -114
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -172
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/index.d.ts +0 -35
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -411
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1056
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -177
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -835
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2275
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -181
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -174
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -236
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -77
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts +0 -32
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -61
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -32
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -38
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -31
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -53
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/export.d.ts +0 -10
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -87
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/network.d.ts +0 -70
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/parameter.d.ts +0 -44
- package/dist/utils/parameter.d.ts.map +0 -1
- package/dist/utils/parameter.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -43
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
- package/frontend/build/static/js/main.8240902c.js +0 -115
- package/frontend/build/static/js/main.8240902c.js.map +0 -1
- /package/frontend/build/static/js/{main.8240902c.js.LICENSE.txt → main.92802eb1.js.LICENSE.txt} +0 -0
package/dist/frontend.js
CHANGED
|
@@ -1,28 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Frontend.
|
|
3
|
-
*
|
|
4
|
-
* @file frontend.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @date 2025-01-13
|
|
7
|
-
* @version 1.0.2
|
|
8
|
-
*
|
|
9
|
-
* Copyright 2025, 2026, 2027 Luca Liguori.
|
|
10
|
-
*
|
|
11
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
-
* you may not use this file except in compliance with the License.
|
|
13
|
-
* You may obtain a copy of the License at
|
|
14
|
-
*
|
|
15
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
*
|
|
17
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
-
* See the License for the specific language governing permissions and
|
|
21
|
-
* limitations under the License. *
|
|
22
|
-
*/
|
|
23
|
-
// @matter
|
|
24
1
|
import { EndpointServer, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
|
|
25
|
-
// Node modules
|
|
26
2
|
import { createServer } from 'node:http';
|
|
27
3
|
import https from 'https';
|
|
28
4
|
import express from 'express';
|
|
@@ -30,70 +6,21 @@ import WebSocket, { WebSocketServer } from 'ws';
|
|
|
30
6
|
import os from 'node:os';
|
|
31
7
|
import path from 'node:path';
|
|
32
8
|
import { promises as fs } from 'node:fs';
|
|
33
|
-
// AnsiLogger module
|
|
34
9
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';
|
|
35
|
-
|
|
36
|
-
import { createZip, deepCopy, isValidNumber, isValidObject, isValidString } from './utils/export.js';
|
|
10
|
+
import { createZip, deepCopy, isValidArray, isValidNumber, isValidObject, isValidString } from './utils/export.js';
|
|
37
11
|
import { plg } from './matterbridgeTypes.js';
|
|
38
12
|
import { hasParameter } from './utils/export.js';
|
|
39
|
-
|
|
40
|
-
* Websocket message ID for logging.
|
|
41
|
-
* @constant {number}
|
|
42
|
-
*/
|
|
13
|
+
import { BridgedDeviceBasicInformation } from '@matter/main/clusters';
|
|
43
14
|
export const WS_ID_LOG = 0;
|
|
44
|
-
/**
|
|
45
|
-
* Websocket message ID indicating a refresh is needed.
|
|
46
|
-
* @constant {number}
|
|
47
|
-
*/
|
|
48
15
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
49
|
-
/**
|
|
50
|
-
* Websocket message ID indicating a restart is needed.
|
|
51
|
-
* @constant {number}
|
|
52
|
-
*/
|
|
53
16
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
54
|
-
/**
|
|
55
|
-
* Websocket message ID indicating a cpu update.
|
|
56
|
-
* @constant {number}
|
|
57
|
-
*/
|
|
58
17
|
export const WS_ID_CPU_UPDATE = 3;
|
|
59
|
-
/**
|
|
60
|
-
* Websocket message ID indicating a memory update.
|
|
61
|
-
* @constant {number}
|
|
62
|
-
*/
|
|
63
18
|
export const WS_ID_MEMORY_UPDATE = 4;
|
|
64
|
-
/**
|
|
65
|
-
* Websocket message ID indicating an uptime update.
|
|
66
|
-
* @constant {number}
|
|
67
|
-
*/
|
|
68
19
|
export const WS_ID_UPTIME_UPDATE = 5;
|
|
69
|
-
/**
|
|
70
|
-
* Websocket message ID indicating a memory update.
|
|
71
|
-
* @constant {number}
|
|
72
|
-
*/
|
|
73
20
|
export const WS_ID_SNACKBAR = 6;
|
|
74
|
-
|
|
75
|
-
* Websocket message ID indicating a shelly system update.
|
|
76
|
-
* check:
|
|
77
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/check
|
|
78
|
-
* perform:
|
|
79
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/perform
|
|
80
|
-
* @constant {number}
|
|
81
|
-
*/
|
|
21
|
+
export const WS_ID_UPDATE_NEEDED = 7;
|
|
82
22
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
83
|
-
/**
|
|
84
|
-
* Websocket message ID indicating a shelly main update.
|
|
85
|
-
* check:
|
|
86
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/check
|
|
87
|
-
* perform:
|
|
88
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/perform
|
|
89
|
-
* @constant {number}
|
|
90
|
-
*/
|
|
91
23
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
92
|
-
/**
|
|
93
|
-
* Initializes the frontend of Matterbridge.
|
|
94
|
-
*
|
|
95
|
-
* @param port The port number to run the frontend server on. Default is 8283.
|
|
96
|
-
*/
|
|
97
24
|
export class Frontend {
|
|
98
25
|
matterbridge;
|
|
99
26
|
log;
|
|
@@ -110,7 +37,7 @@ export class Frontend {
|
|
|
110
37
|
memoryTimeout;
|
|
111
38
|
constructor(matterbridge) {
|
|
112
39
|
this.matterbridge = matterbridge;
|
|
113
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
40
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
114
41
|
}
|
|
115
42
|
set logLevel(logLevel) {
|
|
116
43
|
this.log.logLevel = logLevel;
|
|
@@ -118,21 +45,10 @@ export class Frontend {
|
|
|
118
45
|
async start(port = 8283) {
|
|
119
46
|
this.port = port;
|
|
120
47
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
121
|
-
// Create the express app that serves the frontend
|
|
122
48
|
this.expressApp = express();
|
|
123
|
-
// Log all requests to the server for debugging
|
|
124
|
-
/*
|
|
125
|
-
this.expressApp.use((req, res, next) => {
|
|
126
|
-
this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
|
|
127
|
-
next();
|
|
128
|
-
});
|
|
129
|
-
*/
|
|
130
|
-
// Serve static files from '/static' endpoint
|
|
131
49
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
132
50
|
if (!hasParameter('ssl')) {
|
|
133
|
-
// Create an HTTP server and attach the express app
|
|
134
51
|
this.httpServer = createServer(this.expressApp);
|
|
135
|
-
// Listen on the specified port
|
|
136
52
|
if (hasParameter('ingress')) {
|
|
137
53
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
138
54
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -146,7 +62,6 @@ export class Frontend {
|
|
|
146
62
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
147
63
|
});
|
|
148
64
|
}
|
|
149
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
150
65
|
this.httpServer.on('error', (error) => {
|
|
151
66
|
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
152
67
|
switch (error.code) {
|
|
@@ -162,7 +77,6 @@ export class Frontend {
|
|
|
162
77
|
});
|
|
163
78
|
}
|
|
164
79
|
else {
|
|
165
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
166
80
|
let cert;
|
|
167
81
|
try {
|
|
168
82
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -190,9 +104,7 @@ export class Frontend {
|
|
|
190
104
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
191
105
|
}
|
|
192
106
|
const serverOptions = { cert, key, ca };
|
|
193
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
194
107
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
195
|
-
// Listen on the specified port
|
|
196
108
|
if (hasParameter('ingress')) {
|
|
197
109
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
198
110
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -206,7 +118,6 @@ export class Frontend {
|
|
|
206
118
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
207
119
|
});
|
|
208
120
|
}
|
|
209
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
210
121
|
this.httpsServer.on('error', (error) => {
|
|
211
122
|
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
212
123
|
switch (error.code) {
|
|
@@ -223,18 +134,16 @@ export class Frontend {
|
|
|
223
134
|
}
|
|
224
135
|
if (this.initializeError)
|
|
225
136
|
return;
|
|
226
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
227
137
|
const wssPort = this.port;
|
|
228
138
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
229
139
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
230
140
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
231
141
|
const clientIp = request.socket.remoteAddress;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
142
|
+
let callbackLogLevel = "notice";
|
|
143
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
144
|
+
callbackLogLevel = "info";
|
|
145
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
146
|
+
callbackLogLevel = "debug";
|
|
238
147
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
239
148
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
240
149
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -268,7 +177,6 @@ export class Frontend {
|
|
|
268
177
|
this.webSocketServer.on('error', (ws, error) => {
|
|
269
178
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
270
179
|
});
|
|
271
|
-
// Subscribe to cli events
|
|
272
180
|
const { cliEmitter } = await import('./cli.js');
|
|
273
181
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
274
182
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -279,7 +187,6 @@ export class Frontend {
|
|
|
279
187
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
280
188
|
this.wssSendCpuUpdate(cpuUsage);
|
|
281
189
|
});
|
|
282
|
-
// Endpoint to validate login code
|
|
283
190
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
284
191
|
const { password } = req.body;
|
|
285
192
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -298,27 +205,23 @@ export class Frontend {
|
|
|
298
205
|
this.log.warn('/api/login error wrong password');
|
|
299
206
|
res.json({ valid: false });
|
|
300
207
|
}
|
|
301
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
302
208
|
}
|
|
303
209
|
catch (error) {
|
|
304
210
|
this.log.error('/api/login error getting password');
|
|
305
211
|
res.json({ valid: false });
|
|
306
212
|
}
|
|
307
213
|
});
|
|
308
|
-
// Endpoint to provide health check
|
|
309
214
|
this.expressApp.get('/health', (req, res) => {
|
|
310
215
|
this.log.debug('Express received /health');
|
|
311
216
|
const healthStatus = {
|
|
312
|
-
status: 'ok',
|
|
313
|
-
uptime: process.uptime(),
|
|
314
|
-
timestamp: new Date().toISOString(),
|
|
217
|
+
status: 'ok',
|
|
218
|
+
uptime: process.uptime(),
|
|
219
|
+
timestamp: new Date().toISOString(),
|
|
315
220
|
};
|
|
316
221
|
res.status(200).json(healthStatus);
|
|
317
222
|
});
|
|
318
|
-
// Endpoint to provide memory usage details
|
|
319
223
|
this.expressApp.get('/memory', async (req, res) => {
|
|
320
224
|
this.log.debug('Express received /memory');
|
|
321
|
-
// Memory usage from process
|
|
322
225
|
const memoryUsageRaw = process.memoryUsage();
|
|
323
226
|
const memoryUsage = {
|
|
324
227
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -327,13 +230,10 @@ export class Frontend {
|
|
|
327
230
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
328
231
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
329
232
|
};
|
|
330
|
-
// V8 heap statistics
|
|
331
233
|
const { default: v8 } = await import('node:v8');
|
|
332
234
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
333
235
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
334
|
-
// Format heapStats
|
|
335
236
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
336
|
-
// Format heapSpaces
|
|
337
237
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
338
238
|
...space,
|
|
339
239
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -351,7 +251,6 @@ export class Frontend {
|
|
|
351
251
|
};
|
|
352
252
|
res.status(200).json(memoryReport);
|
|
353
253
|
});
|
|
354
|
-
// Endpoint to start advertising the server node
|
|
355
254
|
this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
|
|
356
255
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
357
256
|
if (pairingCodes) {
|
|
@@ -362,22 +261,18 @@ export class Frontend {
|
|
|
362
261
|
res.status(500).json({ error: 'Failed to generate pairing codes' });
|
|
363
262
|
}
|
|
364
263
|
});
|
|
365
|
-
// Endpoint to provide settings
|
|
366
264
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
367
265
|
this.log.debug('The frontend sent /api/settings');
|
|
368
266
|
res.json(await this.getApiSettings());
|
|
369
267
|
});
|
|
370
|
-
// Endpoint to provide plugins
|
|
371
268
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
372
269
|
this.log.debug('The frontend sent /api/plugins');
|
|
373
270
|
res.json(this.getBaseRegisteredPlugins());
|
|
374
271
|
});
|
|
375
|
-
// Endpoint to provide devices
|
|
376
272
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
377
273
|
this.log.debug('The frontend sent /api/devices');
|
|
378
274
|
const devices = [];
|
|
379
275
|
this.matterbridge.devices.forEach(async (device) => {
|
|
380
|
-
// Check if the device has the required properties
|
|
381
276
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
382
277
|
return;
|
|
383
278
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -390,12 +285,12 @@ export class Frontend {
|
|
|
390
285
|
productUrl: device.productUrl,
|
|
391
286
|
configUrl: device.configUrl,
|
|
392
287
|
uniqueId: device.uniqueId,
|
|
288
|
+
reachable: this.getReachability(device),
|
|
393
289
|
cluster: cluster,
|
|
394
290
|
});
|
|
395
291
|
});
|
|
396
292
|
res.json(devices);
|
|
397
293
|
});
|
|
398
|
-
// Endpoint to provide the cluster servers of the devices
|
|
399
294
|
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
400
295
|
const selectedPluginName = req.params.selectedPluginName;
|
|
401
296
|
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
@@ -468,7 +363,6 @@ export class Frontend {
|
|
|
468
363
|
});
|
|
469
364
|
res.json(data);
|
|
470
365
|
});
|
|
471
|
-
// Endpoint to view the log
|
|
472
366
|
this.expressApp.get('/api/view-log', async (req, res) => {
|
|
473
367
|
this.log.debug('The frontend sent /api/log');
|
|
474
368
|
try {
|
|
@@ -481,12 +375,10 @@ export class Frontend {
|
|
|
481
375
|
res.status(500).send('Error reading log file');
|
|
482
376
|
}
|
|
483
377
|
});
|
|
484
|
-
// Endpoint to download the matterbridge log
|
|
485
378
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
486
379
|
this.log.debug('The frontend sent /api/download-mblog');
|
|
487
380
|
try {
|
|
488
381
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
|
|
489
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
490
382
|
}
|
|
491
383
|
catch (error) {
|
|
492
384
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -498,12 +390,10 @@ export class Frontend {
|
|
|
498
390
|
}
|
|
499
391
|
});
|
|
500
392
|
});
|
|
501
|
-
// Endpoint to download the matter log
|
|
502
393
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
503
394
|
this.log.debug('The frontend sent /api/download-mjlog');
|
|
504
395
|
try {
|
|
505
396
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
506
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
507
397
|
}
|
|
508
398
|
catch (error) {
|
|
509
399
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -515,12 +405,10 @@ export class Frontend {
|
|
|
515
405
|
}
|
|
516
406
|
});
|
|
517
407
|
});
|
|
518
|
-
// Endpoint to download the matter log
|
|
519
408
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
520
409
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
521
410
|
try {
|
|
522
411
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
|
|
523
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
524
412
|
}
|
|
525
413
|
catch (error) {
|
|
526
414
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'Create the Shelly system log before downloading it.');
|
|
@@ -532,7 +420,6 @@ export class Frontend {
|
|
|
532
420
|
}
|
|
533
421
|
});
|
|
534
422
|
});
|
|
535
|
-
// Endpoint to download the matter storage file
|
|
536
423
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
537
424
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
538
425
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
@@ -543,7 +430,6 @@ export class Frontend {
|
|
|
543
430
|
}
|
|
544
431
|
});
|
|
545
432
|
});
|
|
546
|
-
// Endpoint to download the matterbridge storage directory
|
|
547
433
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
548
434
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
549
435
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -554,7 +440,6 @@ export class Frontend {
|
|
|
554
440
|
}
|
|
555
441
|
});
|
|
556
442
|
});
|
|
557
|
-
// Endpoint to download the matterbridge plugin directory
|
|
558
443
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
559
444
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
560
445
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -565,11 +450,9 @@ export class Frontend {
|
|
|
565
450
|
}
|
|
566
451
|
});
|
|
567
452
|
});
|
|
568
|
-
// Endpoint to download the matterbridge plugin config files
|
|
569
453
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
570
454
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
571
455
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
572
|
-
// await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, 'certs', '*.*')), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
573
456
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
574
457
|
if (error) {
|
|
575
458
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
@@ -577,7 +460,6 @@ export class Frontend {
|
|
|
577
460
|
}
|
|
578
461
|
});
|
|
579
462
|
});
|
|
580
|
-
// Endpoint to download the matterbridge plugin config files
|
|
581
463
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
582
464
|
this.log.debug('The frontend sent /api/download-backup');
|
|
583
465
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -587,7 +469,6 @@ export class Frontend {
|
|
|
587
469
|
}
|
|
588
470
|
});
|
|
589
471
|
});
|
|
590
|
-
// Endpoint to receive commands
|
|
591
472
|
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
592
473
|
const command = req.params.command;
|
|
593
474
|
let param = req.params.param;
|
|
@@ -597,15 +478,13 @@ export class Frontend {
|
|
|
597
478
|
return;
|
|
598
479
|
}
|
|
599
480
|
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
600
|
-
// Handle the command setpassword from Settings
|
|
601
481
|
if (command === 'setpassword') {
|
|
602
|
-
const password = param.slice(1, -1);
|
|
482
|
+
const password = param.slice(1, -1);
|
|
603
483
|
this.log.debug('setpassword', param, password);
|
|
604
484
|
await this.matterbridge.nodeContext?.set('password', password);
|
|
605
485
|
res.json({ message: 'Command received' });
|
|
606
486
|
return;
|
|
607
487
|
}
|
|
608
|
-
// Handle the command setbridgemode from Settings
|
|
609
488
|
if (command === 'setbridgemode') {
|
|
610
489
|
this.log.debug(`setbridgemode: ${param}`);
|
|
611
490
|
this.wssSendRestartRequired();
|
|
@@ -613,7 +492,6 @@ export class Frontend {
|
|
|
613
492
|
res.json({ message: 'Command received' });
|
|
614
493
|
return;
|
|
615
494
|
}
|
|
616
|
-
// Handle the command backup from Settings
|
|
617
495
|
if (command === 'backup') {
|
|
618
496
|
this.log.notice(`Prepairing the backup...`);
|
|
619
497
|
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
|
|
@@ -622,33 +500,31 @@ export class Frontend {
|
|
|
622
500
|
res.json({ message: 'Command received' });
|
|
623
501
|
return;
|
|
624
502
|
}
|
|
625
|
-
// Handle the command setmbloglevel from Settings
|
|
626
503
|
if (command === 'setmbloglevel') {
|
|
627
504
|
this.log.debug('Matterbridge log level:', param);
|
|
628
505
|
if (param === 'Debug') {
|
|
629
|
-
this.log.logLevel = "debug"
|
|
506
|
+
this.log.logLevel = "debug";
|
|
630
507
|
}
|
|
631
508
|
else if (param === 'Info') {
|
|
632
|
-
this.log.logLevel = "info"
|
|
509
|
+
this.log.logLevel = "info";
|
|
633
510
|
}
|
|
634
511
|
else if (param === 'Notice') {
|
|
635
|
-
this.log.logLevel = "notice"
|
|
512
|
+
this.log.logLevel = "notice";
|
|
636
513
|
}
|
|
637
514
|
else if (param === 'Warn') {
|
|
638
|
-
this.log.logLevel = "warn"
|
|
515
|
+
this.log.logLevel = "warn";
|
|
639
516
|
}
|
|
640
517
|
else if (param === 'Error') {
|
|
641
|
-
this.log.logLevel = "error"
|
|
518
|
+
this.log.logLevel = "error";
|
|
642
519
|
}
|
|
643
520
|
else if (param === 'Fatal') {
|
|
644
|
-
this.log.logLevel = "fatal"
|
|
521
|
+
this.log.logLevel = "fatal";
|
|
645
522
|
}
|
|
646
523
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
647
524
|
await this.matterbridge.setLogLevel(this.log.logLevel);
|
|
648
525
|
res.json({ message: 'Command received' });
|
|
649
526
|
return;
|
|
650
527
|
}
|
|
651
|
-
// Handle the command setmbloglevel from Settings
|
|
652
528
|
if (command === 'setmjloglevel') {
|
|
653
529
|
this.log.debug('Matter.js log level:', param);
|
|
654
530
|
if (param === 'Debug') {
|
|
@@ -673,34 +549,30 @@ export class Frontend {
|
|
|
673
549
|
res.json({ message: 'Command received' });
|
|
674
550
|
return;
|
|
675
551
|
}
|
|
676
|
-
// Handle the command setmdnsinterface from Settings
|
|
677
552
|
if (command === 'setmdnsinterface') {
|
|
678
|
-
param = param.slice(1, -1);
|
|
553
|
+
param = param.slice(1, -1);
|
|
679
554
|
this.matterbridge.matterbridgeInformation.mattermdnsinterface = param;
|
|
680
555
|
this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
|
|
681
556
|
await this.matterbridge.nodeContext?.set('mattermdnsinterface', param);
|
|
682
557
|
res.json({ message: 'Command received' });
|
|
683
558
|
return;
|
|
684
559
|
}
|
|
685
|
-
// Handle the command setipv4address from Settings
|
|
686
560
|
if (command === 'setipv4address') {
|
|
687
|
-
param = param.slice(1, -1);
|
|
561
|
+
param = param.slice(1, -1);
|
|
688
562
|
this.matterbridge.matterbridgeInformation.matteripv4address = param;
|
|
689
563
|
this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
|
|
690
564
|
await this.matterbridge.nodeContext?.set('matteripv4address', param);
|
|
691
565
|
res.json({ message: 'Command received' });
|
|
692
566
|
return;
|
|
693
567
|
}
|
|
694
|
-
// Handle the command setipv6address from Settings
|
|
695
568
|
if (command === 'setipv6address') {
|
|
696
|
-
param = param.slice(1, -1);
|
|
569
|
+
param = param.slice(1, -1);
|
|
697
570
|
this.matterbridge.matterbridgeInformation.matteripv6address = param;
|
|
698
571
|
this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
|
|
699
572
|
await this.matterbridge.nodeContext?.set('matteripv6address', param);
|
|
700
573
|
res.json({ message: 'Command received' });
|
|
701
574
|
return;
|
|
702
575
|
}
|
|
703
|
-
// Handle the command setmatterport from Settings
|
|
704
576
|
if (command === 'setmatterport') {
|
|
705
577
|
const port = Math.min(Math.max(parseInt(param), 5540), 5560);
|
|
706
578
|
this.matterbridge.matterbridgeInformation.matterPort = port;
|
|
@@ -709,7 +581,6 @@ export class Frontend {
|
|
|
709
581
|
res.json({ message: 'Command received' });
|
|
710
582
|
return;
|
|
711
583
|
}
|
|
712
|
-
// Handle the command setmatterdiscriminator from Settings
|
|
713
584
|
if (command === 'setmatterdiscriminator') {
|
|
714
585
|
const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
|
|
715
586
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
|
|
@@ -718,7 +589,6 @@ export class Frontend {
|
|
|
718
589
|
res.json({ message: 'Command received' });
|
|
719
590
|
return;
|
|
720
591
|
}
|
|
721
|
-
// Handle the command setmatterpasscode from Settings
|
|
722
592
|
if (command === 'setmatterpasscode') {
|
|
723
593
|
const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
|
|
724
594
|
this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
|
|
@@ -727,20 +597,17 @@ export class Frontend {
|
|
|
727
597
|
res.json({ message: 'Command received' });
|
|
728
598
|
return;
|
|
729
599
|
}
|
|
730
|
-
// Handle the command setmbloglevel from Settings
|
|
731
600
|
if (command === 'setmblogfile') {
|
|
732
601
|
this.log.debug('Matterbridge file log:', param);
|
|
733
602
|
this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
|
|
734
603
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
|
|
735
|
-
// Create the file logger for matterbridge
|
|
736
604
|
if (param === 'true')
|
|
737
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug"
|
|
605
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
|
|
738
606
|
else
|
|
739
607
|
AnsiLogger.setGlobalLogfile(undefined);
|
|
740
608
|
res.json({ message: 'Command received' });
|
|
741
609
|
return;
|
|
742
610
|
}
|
|
743
|
-
// Handle the command setmbloglevel from Settings
|
|
744
611
|
if (command === 'setmjlogfile') {
|
|
745
612
|
this.log.debug('Matter file log:', param);
|
|
746
613
|
this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
|
|
@@ -767,48 +634,40 @@ export class Frontend {
|
|
|
767
634
|
res.json({ message: 'Command received' });
|
|
768
635
|
return;
|
|
769
636
|
}
|
|
770
|
-
// Handle the command unregister from Settings
|
|
771
637
|
if (command === 'unregister') {
|
|
772
638
|
await this.matterbridge.unregisterAndShutdownProcess();
|
|
773
639
|
res.json({ message: 'Command received' });
|
|
774
640
|
return;
|
|
775
641
|
}
|
|
776
|
-
// Handle the command reset from Settings
|
|
777
642
|
if (command === 'reset') {
|
|
778
643
|
await this.matterbridge.shutdownProcessAndReset();
|
|
779
644
|
res.json({ message: 'Command received' });
|
|
780
645
|
return;
|
|
781
646
|
}
|
|
782
|
-
// Handle the command factoryreset from Settings
|
|
783
647
|
if (command === 'factoryreset') {
|
|
784
648
|
await this.matterbridge.shutdownProcessAndFactoryReset();
|
|
785
649
|
res.json({ message: 'Command received' });
|
|
786
650
|
return;
|
|
787
651
|
}
|
|
788
|
-
// Handle the command shutdown from Header
|
|
789
652
|
if (command === 'shutdown') {
|
|
790
653
|
await this.matterbridge.shutdownProcess();
|
|
791
654
|
res.json({ message: 'Command received' });
|
|
792
655
|
return;
|
|
793
656
|
}
|
|
794
|
-
// Handle the command restart from Header
|
|
795
657
|
if (command === 'restart') {
|
|
796
658
|
await this.matterbridge.restartProcess();
|
|
797
659
|
res.json({ message: 'Command received' });
|
|
798
660
|
return;
|
|
799
661
|
}
|
|
800
|
-
// Handle the command update from Header
|
|
801
662
|
if (command === 'update') {
|
|
802
663
|
await this.matterbridge.updateProcess();
|
|
803
664
|
this.wssSendRestartRequired();
|
|
804
665
|
res.json({ message: 'Command received' });
|
|
805
666
|
return;
|
|
806
667
|
}
|
|
807
|
-
// Handle the command saveconfig from Home
|
|
808
668
|
if (command === 'saveconfig') {
|
|
809
669
|
param = param.replace(/\*/g, '\\');
|
|
810
670
|
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
811
|
-
// console.log('Req.body:', JSON.stringify(req.body, null, 2));
|
|
812
671
|
if (!this.matterbridge.plugins.has(param)) {
|
|
813
672
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
814
673
|
}
|
|
@@ -823,7 +682,6 @@ export class Frontend {
|
|
|
823
682
|
res.json({ message: 'Command received' });
|
|
824
683
|
return;
|
|
825
684
|
}
|
|
826
|
-
// Handle the command installplugin from Home
|
|
827
685
|
if (command === 'installplugin') {
|
|
828
686
|
param = param.replace(/\*/g, '\\');
|
|
829
687
|
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
@@ -832,54 +690,47 @@ export class Frontend {
|
|
|
832
690
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
833
691
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
834
692
|
this.wssSendSnackbarMessage(`Installed package ${param}`);
|
|
835
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
836
693
|
}
|
|
837
694
|
catch (error) {
|
|
838
695
|
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
839
696
|
this.wssSendSnackbarMessage(`Package ${param} not installed`);
|
|
840
697
|
}
|
|
841
|
-
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
842
698
|
this.wssSendRestartRequired();
|
|
843
699
|
param = param.split('@')[0];
|
|
844
|
-
// Also add the plugin to matterbridge so no return!
|
|
845
700
|
if (param === 'matterbridge') {
|
|
846
|
-
// If we used the command installplugin to install a dev or a specific version of matterbridge we don't want to add it to matterbridge
|
|
847
701
|
res.json({ message: 'Command received' });
|
|
848
702
|
return;
|
|
849
703
|
}
|
|
850
704
|
}
|
|
851
|
-
// Handle the command addplugin from Home
|
|
852
705
|
if (command === 'addplugin' || command === 'installplugin') {
|
|
853
706
|
param = param.replace(/\*/g, '\\');
|
|
854
707
|
const plugin = await this.matterbridge.plugins.add(param);
|
|
855
708
|
if (plugin) {
|
|
709
|
+
this.wssSendSnackbarMessage(`Added plugin ${param}`);
|
|
856
710
|
if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
857
|
-
// We don't know now if the plugin is a dynamic platform or an accessory platform so we create the server node and the aggregator node
|
|
858
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
859
711
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
860
712
|
}
|
|
861
713
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
862
|
-
this.wssSendRefreshRequired();
|
|
714
|
+
this.wssSendRefreshRequired('plugins');
|
|
863
715
|
});
|
|
864
716
|
}
|
|
865
717
|
res.json({ message: 'Command received' });
|
|
866
718
|
return;
|
|
867
719
|
}
|
|
868
|
-
// Handle the command removeplugin from Home
|
|
869
720
|
if (command === 'removeplugin') {
|
|
870
721
|
if (!this.matterbridge.plugins.has(param)) {
|
|
871
722
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
872
723
|
}
|
|
873
724
|
else {
|
|
874
725
|
const plugin = this.matterbridge.plugins.get(param);
|
|
875
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
726
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
876
727
|
await this.matterbridge.plugins.remove(param);
|
|
728
|
+
this.wssSendSnackbarMessage(`Removed plugin ${param}`);
|
|
729
|
+
this.wssSendRefreshRequired('plugins');
|
|
877
730
|
}
|
|
878
731
|
res.json({ message: 'Command received' });
|
|
879
|
-
this.wssSendRefreshRequired();
|
|
880
732
|
return;
|
|
881
733
|
}
|
|
882
|
-
// Handle the command enableplugin from Home
|
|
883
734
|
if (command === 'enableplugin') {
|
|
884
735
|
if (!this.matterbridge.plugins.has(param)) {
|
|
885
736
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -896,20 +747,18 @@ export class Frontend {
|
|
|
896
747
|
plugin.registeredDevices = undefined;
|
|
897
748
|
plugin.addedDevices = undefined;
|
|
898
749
|
await this.matterbridge.plugins.enable(param);
|
|
750
|
+
this.wssSendSnackbarMessage(`Enabled plugin ${param}`);
|
|
899
751
|
if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
|
|
900
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
901
752
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
902
753
|
}
|
|
903
754
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
|
|
904
|
-
this.wssSendRefreshRequired();
|
|
755
|
+
this.wssSendRefreshRequired('plugins');
|
|
905
756
|
});
|
|
906
757
|
}
|
|
907
758
|
}
|
|
908
759
|
res.json({ message: 'Command received' });
|
|
909
|
-
this.wssSendRefreshRequired();
|
|
910
760
|
return;
|
|
911
761
|
}
|
|
912
|
-
// Handle the command disableplugin from Home
|
|
913
762
|
if (command === 'disableplugin') {
|
|
914
763
|
if (!this.matterbridge.plugins.has(param)) {
|
|
915
764
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -917,16 +766,16 @@ export class Frontend {
|
|
|
917
766
|
else {
|
|
918
767
|
const plugin = this.matterbridge.plugins.get(param);
|
|
919
768
|
if (plugin && plugin.enabled) {
|
|
920
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
769
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
921
770
|
await this.matterbridge.plugins.disable(param);
|
|
771
|
+
this.wssSendSnackbarMessage(`Disabled plugin ${param}`);
|
|
772
|
+
this.wssSendRefreshRequired('plugins');
|
|
922
773
|
}
|
|
923
774
|
}
|
|
924
775
|
res.json({ message: 'Command received' });
|
|
925
|
-
this.wssSendRefreshRequired();
|
|
926
776
|
return;
|
|
927
777
|
}
|
|
928
778
|
});
|
|
929
|
-
// Fallback for routing (must be the last route)
|
|
930
779
|
this.expressApp.get('*', (req, res) => {
|
|
931
780
|
this.log.debug('The frontend sent:', req.url);
|
|
932
781
|
this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -935,29 +784,24 @@ export class Frontend {
|
|
|
935
784
|
this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
936
785
|
}
|
|
937
786
|
async stop() {
|
|
938
|
-
// Close the http server
|
|
939
787
|
if (this.httpServer) {
|
|
940
788
|
this.httpServer.close();
|
|
941
789
|
this.httpServer.removeAllListeners();
|
|
942
790
|
this.httpServer = undefined;
|
|
943
791
|
this.log.debug('Frontend http server closed successfully');
|
|
944
792
|
}
|
|
945
|
-
// Close the https server
|
|
946
793
|
if (this.httpsServer) {
|
|
947
794
|
this.httpsServer.close();
|
|
948
795
|
this.httpsServer.removeAllListeners();
|
|
949
796
|
this.httpsServer = undefined;
|
|
950
797
|
this.log.debug('Frontend https server closed successfully');
|
|
951
798
|
}
|
|
952
|
-
// Remove listeners from the express app
|
|
953
799
|
if (this.expressApp) {
|
|
954
800
|
this.expressApp.removeAllListeners();
|
|
955
801
|
this.expressApp = undefined;
|
|
956
802
|
this.log.debug('Frontend app closed successfully');
|
|
957
803
|
}
|
|
958
|
-
// Close the WebSocket server
|
|
959
804
|
if (this.webSocketServer) {
|
|
960
|
-
// Close all active connections
|
|
961
805
|
this.webSocketServer.clients.forEach((client) => {
|
|
962
806
|
if (client.readyState === WebSocket.OPEN) {
|
|
963
807
|
client.close();
|
|
@@ -974,7 +818,6 @@ export class Frontend {
|
|
|
974
818
|
this.webSocketServer = undefined;
|
|
975
819
|
}
|
|
976
820
|
}
|
|
977
|
-
// Function to format bytes to KB, MB, or GB
|
|
978
821
|
formatMemoryUsage = (bytes) => {
|
|
979
822
|
if (bytes >= 1024 ** 3) {
|
|
980
823
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -986,7 +829,6 @@ export class Frontend {
|
|
|
986
829
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
987
830
|
}
|
|
988
831
|
};
|
|
989
|
-
// Function to format system uptime with only the most significant unit
|
|
990
832
|
formatOsUpTime = (seconds) => {
|
|
991
833
|
if (seconds >= 86400) {
|
|
992
834
|
const days = Math.floor(seconds / 86400);
|
|
@@ -1002,13 +844,8 @@ export class Frontend {
|
|
|
1002
844
|
}
|
|
1003
845
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
1004
846
|
};
|
|
1005
|
-
/**
|
|
1006
|
-
* Retrieves the api settings data.
|
|
1007
|
-
* @returns {Promise<object>} A promise that resolve in the api settings object.
|
|
1008
|
-
*/
|
|
1009
847
|
async getApiSettings() {
|
|
1010
848
|
const { lastCpuUsage } = await import('./cli.js');
|
|
1011
|
-
// Update the system information
|
|
1012
849
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
1013
850
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
1014
851
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -1017,7 +854,6 @@ export class Frontend {
|
|
|
1017
854
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
1018
855
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
1019
856
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
1020
|
-
// Update the matterbridge information
|
|
1021
857
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
1022
858
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
1023
859
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -1036,11 +872,15 @@ export class Frontend {
|
|
|
1036
872
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
1037
873
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
1038
874
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
875
|
+
getReachability(device) {
|
|
876
|
+
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
877
|
+
return false;
|
|
878
|
+
if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
|
|
879
|
+
return device.getAttribute(BridgedDeviceBasicInformation.Cluster.id, 'reachable');
|
|
880
|
+
if (this.matterbridge.bridgeMode === 'childbridge')
|
|
881
|
+
return true;
|
|
882
|
+
return false;
|
|
883
|
+
}
|
|
1044
884
|
getClusterTextFromDevice(device) {
|
|
1045
885
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1046
886
|
return '';
|
|
@@ -1081,7 +921,6 @@ export class Frontend {
|
|
|
1081
921
|
};
|
|
1082
922
|
let attributes = '';
|
|
1083
923
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1084
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
1085
924
|
if (typeof attributeValue === 'undefined')
|
|
1086
925
|
return;
|
|
1087
926
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1159,13 +998,8 @@ export class Frontend {
|
|
|
1159
998
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1160
999
|
attributes += `${getUserLabel(device)} `;
|
|
1161
1000
|
});
|
|
1162
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1163
1001
|
return attributes.trimStart().trimEnd();
|
|
1164
1002
|
}
|
|
1165
|
-
/**
|
|
1166
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
1167
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
1168
|
-
*/
|
|
1169
1003
|
getBaseRegisteredPlugins() {
|
|
1170
1004
|
const baseRegisteredPlugins = [];
|
|
1171
1005
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -1192,17 +1026,12 @@ export class Frontend {
|
|
|
1192
1026
|
manualPairingCode: plugin.manualPairingCode,
|
|
1193
1027
|
configJson: plugin.configJson,
|
|
1194
1028
|
schemaJson: plugin.schemaJson,
|
|
1029
|
+
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1030
|
+
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1195
1031
|
});
|
|
1196
1032
|
}
|
|
1197
1033
|
return baseRegisteredPlugins;
|
|
1198
1034
|
}
|
|
1199
|
-
/**
|
|
1200
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1201
|
-
*
|
|
1202
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1203
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1204
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1205
|
-
*/
|
|
1206
1035
|
async wsMessageHandler(client, message) {
|
|
1207
1036
|
let data;
|
|
1208
1037
|
try {
|
|
@@ -1323,7 +1152,7 @@ export class Frontend {
|
|
|
1323
1152
|
this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
|
|
1324
1153
|
this.matterbridge.matterbridgeQrPairingCode = pairingCodes?.qrPairingCode;
|
|
1325
1154
|
this.matterbridge.matterbridgeManualPairingCode = pairingCodes?.manualPairingCode;
|
|
1326
|
-
this.wssSendRefreshRequired();
|
|
1155
|
+
this.wssSendRefreshRequired('matterbridgeAdvertise');
|
|
1327
1156
|
this.wssSendSnackbarMessage(`Started fabrics share`, 0);
|
|
1328
1157
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes }));
|
|
1329
1158
|
return;
|
|
@@ -1331,7 +1160,7 @@ export class Frontend {
|
|
|
1331
1160
|
else if (data.method === '/api/stopadvertise') {
|
|
1332
1161
|
await this.matterbridge.stopAdvertiseServerNode(this.matterbridge.serverNode);
|
|
1333
1162
|
this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = false;
|
|
1334
|
-
this.wssSendRefreshRequired();
|
|
1163
|
+
this.wssSendRefreshRequired('matterbridgeAdvertise');
|
|
1335
1164
|
this.wssSendSnackbarMessage(`Stopped fabrics share`, 0);
|
|
1336
1165
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src }));
|
|
1337
1166
|
return;
|
|
@@ -1348,10 +1177,8 @@ export class Frontend {
|
|
|
1348
1177
|
else if (data.method === '/api/devices') {
|
|
1349
1178
|
const devices = [];
|
|
1350
1179
|
this.matterbridge.devices.forEach(async (device) => {
|
|
1351
|
-
// Filter by pluginName if provided
|
|
1352
1180
|
if (data.params.pluginName && data.params.pluginName !== device.plugin)
|
|
1353
1181
|
return;
|
|
1354
|
-
// Check if the device has the required properties
|
|
1355
1182
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1356
1183
|
return;
|
|
1357
1184
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -1364,6 +1191,7 @@ export class Frontend {
|
|
|
1364
1191
|
productUrl: device.productUrl,
|
|
1365
1192
|
configUrl: device.configUrl,
|
|
1366
1193
|
uniqueId: device.uniqueId,
|
|
1194
|
+
reachable: this.getReachability(device),
|
|
1367
1195
|
cluster: cluster,
|
|
1368
1196
|
});
|
|
1369
1197
|
});
|
|
@@ -1434,7 +1262,6 @@ export class Frontend {
|
|
|
1434
1262
|
});
|
|
1435
1263
|
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
1436
1264
|
deviceTypes = [];
|
|
1437
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1438
1265
|
const name = childEndpoint.endpoint?.id;
|
|
1439
1266
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
1440
1267
|
clusterServers.forEach((clusterServer) => {
|
|
@@ -1488,7 +1315,7 @@ export class Frontend {
|
|
|
1488
1315
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select' }));
|
|
1489
1316
|
return;
|
|
1490
1317
|
}
|
|
1491
|
-
const selectDeviceValues = plugin.platform?.
|
|
1318
|
+
const selectDeviceValues = plugin.platform?.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1492
1319
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectDeviceValues }));
|
|
1493
1320
|
return;
|
|
1494
1321
|
}
|
|
@@ -1502,10 +1329,70 @@ export class Frontend {
|
|
|
1502
1329
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' }));
|
|
1503
1330
|
return;
|
|
1504
1331
|
}
|
|
1505
|
-
const selectEntityValues = plugin.platform?.
|
|
1332
|
+
const selectEntityValues = plugin.platform?.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1506
1333
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectEntityValues }));
|
|
1507
1334
|
return;
|
|
1508
1335
|
}
|
|
1336
|
+
else if (data.method === '/api/command') {
|
|
1337
|
+
if (!isValidString(data.params.command, 5)) {
|
|
1338
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter command in /api/command' }));
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
if (data.params.command === 'selectdevice' && isValidString(data.params.plugin, 10) && isValidString(data.params.serial, 1)) {
|
|
1342
|
+
const plugin = this.matterbridge.plugins.get(data.params.plugin);
|
|
1343
|
+
if (!plugin) {
|
|
1344
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/command' }));
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
const config = plugin.configJson;
|
|
1348
|
+
if (config) {
|
|
1349
|
+
if (config.postfix) {
|
|
1350
|
+
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1351
|
+
}
|
|
1352
|
+
if (isValidArray(config.whiteList, 1)) {
|
|
1353
|
+
if (!config.whiteList.includes(data.params.serial)) {
|
|
1354
|
+
config.whiteList.push(data.params.serial);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
if (isValidArray(config.blackList, 1)) {
|
|
1358
|
+
if (config.blackList.includes(data.params.serial)) {
|
|
1359
|
+
config.blackList = config.blackList.filter((serial) => serial !== data.params.serial);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
if (plugin.platform)
|
|
1363
|
+
plugin.platform.config = config;
|
|
1364
|
+
await this.matterbridge.plugins.saveConfigFromPlugin(plugin);
|
|
1365
|
+
this.wssSendRestartRequired(false);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
else if (data.params.command === 'unselectdevice' && isValidString(data.params.plugin, 10) && isValidString(data.params.serial, 1)) {
|
|
1369
|
+
const plugin = this.matterbridge.plugins.get(data.params.plugin);
|
|
1370
|
+
if (!plugin) {
|
|
1371
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/command' }));
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
const config = plugin.configJson;
|
|
1375
|
+
if (config) {
|
|
1376
|
+
if (config.postfix) {
|
|
1377
|
+
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1378
|
+
}
|
|
1379
|
+
if (isValidArray(config.whiteList, 1)) {
|
|
1380
|
+
if (config.whiteList.includes(data.params.serial)) {
|
|
1381
|
+
config.whiteList = config.whiteList.filter((serial) => serial !== data.params.serial);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
if (isValidArray(config.blackList)) {
|
|
1385
|
+
if (!config.blackList.includes(data.params.serial)) {
|
|
1386
|
+
config.blackList.push(data.params.serial);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
if (plugin.platform)
|
|
1390
|
+
plugin.platform.config = config;
|
|
1391
|
+
await this.matterbridge.plugins.saveConfigFromPlugin(plugin);
|
|
1392
|
+
this.wssSendRestartRequired(false);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1509
1396
|
else {
|
|
1510
1397
|
this.log.error(`Invalid method from websocket client: ${debugStringify(data)}`);
|
|
1511
1398
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid method' }));
|
|
@@ -1517,139 +1404,94 @@ export class Frontend {
|
|
|
1517
1404
|
return;
|
|
1518
1405
|
}
|
|
1519
1406
|
}
|
|
1520
|
-
/**
|
|
1521
|
-
* Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1522
|
-
*
|
|
1523
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1524
|
-
* @param {string} time - The time string of the message
|
|
1525
|
-
* @param {string} name - The logger name of the message
|
|
1526
|
-
* @param {string} message - The content of the message.
|
|
1527
|
-
*/
|
|
1528
1407
|
wssSendMessage(level, time, name, message) {
|
|
1529
1408
|
if (!level || !time || !name || !message)
|
|
1530
1409
|
return;
|
|
1531
|
-
// Remove ANSI escape codes from the message
|
|
1532
|
-
// eslint-disable-next-line no-control-regex
|
|
1533
1410
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1534
|
-
// Remove leading asterisks from the message
|
|
1535
1411
|
message = message.replace(/^\*+/, '');
|
|
1536
|
-
// Replace all occurrences of \t and \n
|
|
1537
1412
|
message = message.replace(/[\t\n]/g, '');
|
|
1538
|
-
// Remove non-printable characters
|
|
1539
|
-
// eslint-disable-next-line no-control-regex
|
|
1540
1413
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1541
|
-
// Replace all occurrences of \" with "
|
|
1542
1414
|
message = message.replace(/\\"/g, '"');
|
|
1543
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1544
1415
|
const maxContinuousLength = 100;
|
|
1545
1416
|
const keepStartLength = 20;
|
|
1546
1417
|
const keepEndLength = 20;
|
|
1547
|
-
// Split the message into words
|
|
1548
1418
|
message = message
|
|
1549
1419
|
.split(' ')
|
|
1550
1420
|
.map((word) => {
|
|
1551
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1552
1421
|
if (word.length > maxContinuousLength) {
|
|
1553
1422
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1554
1423
|
}
|
|
1555
1424
|
return word;
|
|
1556
1425
|
})
|
|
1557
1426
|
.join(' ');
|
|
1558
|
-
// Send the message to all connected clients
|
|
1559
1427
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1560
1428
|
if (client.readyState === WebSocket.OPEN) {
|
|
1561
1429
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1562
1430
|
}
|
|
1563
1431
|
});
|
|
1564
1432
|
}
|
|
1565
|
-
|
|
1566
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1567
|
-
*
|
|
1568
|
-
*/
|
|
1569
|
-
wssSendRefreshRequired() {
|
|
1433
|
+
wssSendRefreshRequired(changed = null) {
|
|
1570
1434
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1571
|
-
this.matterbridge.matterbridgeInformation.refreshRequired = true;
|
|
1572
|
-
// Send the message to all connected clients
|
|
1573
1435
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1574
1436
|
if (client.readyState === WebSocket.OPEN) {
|
|
1575
|
-
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: {} }));
|
|
1437
|
+
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1576
1438
|
}
|
|
1577
1439
|
});
|
|
1578
1440
|
}
|
|
1579
|
-
|
|
1580
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1581
|
-
*
|
|
1582
|
-
*/
|
|
1583
|
-
wssSendRestartRequired() {
|
|
1441
|
+
wssSendRestartRequired(snackbar = true) {
|
|
1584
1442
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1585
1443
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1586
|
-
|
|
1587
|
-
|
|
1444
|
+
if (snackbar === true)
|
|
1445
|
+
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1588
1446
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1589
1447
|
if (client.readyState === WebSocket.OPEN) {
|
|
1590
1448
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1591
1449
|
}
|
|
1592
1450
|
});
|
|
1593
1451
|
}
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1452
|
+
wssSendUpdateRequired() {
|
|
1453
|
+
this.log.debug('Sending an update required message to all connected clients');
|
|
1454
|
+
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
1455
|
+
this.webSocketServer?.clients.forEach((client) => {
|
|
1456
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
1457
|
+
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
1458
|
+
}
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1598
1461
|
wssSendCpuUpdate(cpuUsage) {
|
|
1599
1462
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1600
|
-
// Send the message to all connected clients
|
|
1601
1463
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1602
1464
|
if (client.readyState === WebSocket.OPEN) {
|
|
1603
1465
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
1604
1466
|
}
|
|
1605
1467
|
});
|
|
1606
1468
|
}
|
|
1607
|
-
/**
|
|
1608
|
-
* Sends a cpu update message to all connected clients.
|
|
1609
|
-
*
|
|
1610
|
-
*/
|
|
1611
1469
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
1612
1470
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
1613
|
-
// Send the message to all connected clients
|
|
1614
1471
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1615
1472
|
if (client.readyState === WebSocket.OPEN) {
|
|
1616
1473
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
1617
1474
|
}
|
|
1618
1475
|
});
|
|
1619
1476
|
}
|
|
1620
|
-
/**
|
|
1621
|
-
* Sends a memory update message to all connected clients.
|
|
1622
|
-
*
|
|
1623
|
-
*/
|
|
1624
1477
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
1625
1478
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
1626
|
-
// Send the message to all connected clients
|
|
1627
1479
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1628
1480
|
if (client.readyState === WebSocket.OPEN) {
|
|
1629
1481
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
1630
1482
|
}
|
|
1631
1483
|
});
|
|
1632
1484
|
}
|
|
1633
|
-
/**
|
|
1634
|
-
* Sends a cpu update message to all connected clients.
|
|
1635
|
-
*
|
|
1636
|
-
*/
|
|
1637
1485
|
wssSendSnackbarMessage(message, timeout = 5) {
|
|
1638
1486
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
1639
|
-
// Send the message to all connected clients
|
|
1640
1487
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1641
1488
|
if (client.readyState === WebSocket.OPEN) {
|
|
1642
1489
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { message, timeout } }));
|
|
1643
1490
|
}
|
|
1644
1491
|
});
|
|
1645
1492
|
}
|
|
1646
|
-
/**
|
|
1647
|
-
* Sends a message to all connected clients.
|
|
1648
|
-
*
|
|
1649
|
-
*/
|
|
1650
1493
|
wssBroadcastMessage(id, method, params) {
|
|
1651
1494
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
1652
|
-
// Send the message to all connected clients
|
|
1653
1495
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1654
1496
|
if (client.readyState === WebSocket.OPEN) {
|
|
1655
1497
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -1657,4 +1499,3 @@ export class Frontend {
|
|
|
1657
1499
|
});
|
|
1658
1500
|
}
|
|
1659
1501
|
}
|
|
1660
|
-
//# sourceMappingURL=frontend.js.map
|