matterbridge 2.2.2-dev.3 → 2.2.3
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 +15 -1
- package/dist/cli.d.ts +29 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +37 -2
- package/dist/cli.js.map +1 -0
- package/dist/cluster/export.d.ts +2 -0
- package/dist/cluster/export.d.ts.map +1 -0
- package/dist/cluster/export.js +2 -0
- package/dist/cluster/export.js.map +1 -0
- package/dist/defaultConfigSchema.d.ts +27 -0
- package/dist/defaultConfigSchema.d.ts.map +1 -0
- package/dist/defaultConfigSchema.js +23 -0
- package/dist/defaultConfigSchema.js.map +1 -0
- package/dist/deviceManager.d.ts +114 -0
- package/dist/deviceManager.d.ts.map +1 -0
- package/dist/deviceManager.js +94 -1
- package/dist/deviceManager.js.map +1 -0
- package/dist/frontend.d.ts +201 -0
- package/dist/frontend.d.ts.map +1 -0
- package/dist/frontend.js +307 -24
- package/dist/frontend.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -1
- package/dist/index.js.map +1 -0
- package/dist/logger/export.d.ts +2 -0
- package/dist/logger/export.d.ts.map +1 -0
- package/dist/logger/export.js +1 -0
- package/dist/logger/export.js.map +1 -0
- package/dist/matter/behaviors.d.ts +2 -0
- package/dist/matter/behaviors.d.ts.map +1 -0
- package/dist/matter/behaviors.js +2 -0
- package/dist/matter/behaviors.js.map +1 -0
- package/dist/matter/clusters.d.ts +2 -0
- package/dist/matter/clusters.d.ts.map +1 -0
- package/dist/matter/clusters.js +2 -0
- package/dist/matter/clusters.js.map +1 -0
- package/dist/matter/devices.d.ts +2 -0
- package/dist/matter/devices.d.ts.map +1 -0
- package/dist/matter/devices.js +2 -0
- package/dist/matter/devices.js.map +1 -0
- package/dist/matter/endpoints.d.ts +2 -0
- package/dist/matter/endpoints.d.ts.map +1 -0
- package/dist/matter/endpoints.js +2 -0
- package/dist/matter/endpoints.js.map +1 -0
- package/dist/matter/export.d.ts +5 -0
- package/dist/matter/export.d.ts.map +1 -0
- package/dist/matter/export.js +2 -0
- package/dist/matter/export.js.map +1 -0
- package/dist/matter/types.d.ts +3 -0
- package/dist/matter/types.d.ts.map +1 -0
- package/dist/matter/types.js +2 -0
- package/dist/matter/types.js.map +1 -0
- package/dist/matterbridge.d.ts +412 -0
- package/dist/matterbridge.d.ts.map +1 -0
- package/dist/matterbridge.js +723 -49
- package/dist/matterbridge.js.map +1 -0
- package/dist/matterbridgeAccessoryPlatform.d.ts +39 -0
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -0
- package/dist/matterbridgeAccessoryPlatform.js +33 -0
- package/dist/matterbridgeAccessoryPlatform.js.map +1 -0
- package/dist/matterbridgeBehaviors.d.ts +1056 -0
- package/dist/matterbridgeBehaviors.d.ts.map +1 -0
- package/dist/matterbridgeBehaviors.js +32 -1
- package/dist/matterbridgeBehaviors.js.map +1 -0
- package/dist/matterbridgeDeviceTypes.d.ts +177 -0
- package/dist/matterbridgeDeviceTypes.d.ts.map +1 -0
- package/dist/matterbridgeDeviceTypes.js +112 -11
- package/dist/matterbridgeDeviceTypes.js.map +1 -0
- package/dist/matterbridgeDynamicPlatform.d.ts +39 -0
- package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -0
- package/dist/matterbridgeDynamicPlatform.js +33 -0
- package/dist/matterbridgeDynamicPlatform.js.map +1 -0
- package/dist/matterbridgeEndpoint.d.ts +835 -0
- package/dist/matterbridgeEndpoint.d.ts.map +1 -0
- package/dist/matterbridgeEndpoint.js +690 -6
- package/dist/matterbridgeEndpoint.js.map +1 -0
- package/dist/matterbridgeEndpointHelpers.d.ts +2275 -0
- package/dist/matterbridgeEndpointHelpers.d.ts.map +1 -0
- package/dist/matterbridgeEndpointHelpers.js +118 -9
- package/dist/matterbridgeEndpointHelpers.js.map +1 -0
- package/dist/matterbridgePlatform.d.ts +251 -0
- package/dist/matterbridgePlatform.d.ts.map +1 -0
- package/dist/matterbridgePlatform.js +185 -7
- package/dist/matterbridgePlatform.js.map +1 -0
- package/dist/matterbridgeTypes.d.ts +178 -0
- package/dist/matterbridgeTypes.d.ts.map +1 -0
- package/dist/matterbridgeTypes.js +24 -0
- package/dist/matterbridgeTypes.js.map +1 -0
- package/dist/pluginManager.d.ts +236 -0
- package/dist/pluginManager.d.ts.map +1 -0
- package/dist/pluginManager.js +229 -3
- package/dist/pluginManager.js.map +1 -0
- package/dist/shelly.d.ts +77 -0
- package/dist/shelly.d.ts.map +1 -0
- package/dist/shelly.js +121 -6
- package/dist/shelly.js.map +1 -0
- package/dist/storage/export.d.ts +2 -0
- package/dist/storage/export.d.ts.map +1 -0
- package/dist/storage/export.js +1 -0
- package/dist/storage/export.js.map +1 -0
- package/dist/update.d.ts +32 -0
- package/dist/update.d.ts.map +1 -0
- package/dist/update.js +45 -0
- package/dist/update.js.map +1 -0
- package/dist/utils/colorUtils.d.ts +61 -0
- package/dist/utils/colorUtils.d.ts.map +1 -0
- package/dist/utils/colorUtils.js +205 -2
- package/dist/utils/colorUtils.js.map +1 -0
- package/dist/utils/copyDirectory.d.ts +32 -0
- package/dist/utils/copyDirectory.d.ts.map +1 -0
- package/dist/utils/copyDirectory.js +37 -1
- package/dist/utils/copyDirectory.js.map +1 -0
- package/dist/utils/createZip.d.ts +38 -0
- package/dist/utils/createZip.d.ts.map +1 -0
- package/dist/utils/createZip.js +42 -2
- package/dist/utils/createZip.js.map +1 -0
- package/dist/utils/deepCopy.d.ts +31 -0
- package/dist/utils/deepCopy.d.ts.map +1 -0
- package/dist/utils/deepCopy.js +40 -0
- package/dist/utils/deepCopy.js.map +1 -0
- package/dist/utils/deepEqual.d.ts +53 -0
- package/dist/utils/deepEqual.d.ts.map +1 -0
- package/dist/utils/deepEqual.js +65 -1
- package/dist/utils/deepEqual.js.map +1 -0
- package/dist/utils/export.d.ts +10 -0
- package/dist/utils/export.d.ts.map +1 -0
- package/dist/utils/export.js +1 -0
- package/dist/utils/export.js.map +1 -0
- package/dist/utils/isvalid.d.ts +87 -0
- package/dist/utils/isvalid.d.ts.map +1 -0
- package/dist/utils/isvalid.js +86 -0
- package/dist/utils/isvalid.js.map +1 -0
- package/dist/utils/network.d.ts +70 -0
- package/dist/utils/network.d.ts.map +1 -0
- package/dist/utils/network.js +77 -5
- package/dist/utils/network.js.map +1 -0
- package/dist/utils/parameter.d.ts +44 -0
- package/dist/utils/parameter.d.ts.map +1 -0
- package/dist/utils/parameter.js +41 -0
- package/dist/utils/parameter.js.map +1 -0
- package/dist/utils/wait.d.ts +43 -0
- package/dist/utils/wait.d.ts.map +1 -0
- package/dist/utils/wait.js +48 -5
- package/dist/utils/wait.js.map +1 -0
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.a02de6aa.js → main.6ffd2c31.js} +6 -6
- package/frontend/build/static/js/{main.a02de6aa.js.map → main.6ffd2c31.js.map} +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -1
- /package/frontend/build/static/js/{main.a02de6aa.js.LICENSE.txt → main.6ffd2c31.js.LICENSE.txt} +0 -0
package/dist/frontend.js
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
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
|
|
1
24
|
import { EndpointServer, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
|
|
25
|
+
// Node modules
|
|
2
26
|
import { createServer } from 'node:http';
|
|
3
27
|
import os from 'node:os';
|
|
4
28
|
import path from 'node:path';
|
|
@@ -7,21 +31,70 @@ import https from 'https';
|
|
|
7
31
|
import express from 'express';
|
|
8
32
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
9
33
|
import multer from 'multer';
|
|
34
|
+
// AnsiLogger module
|
|
10
35
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';
|
|
36
|
+
// Matterbridge
|
|
11
37
|
import { createZip, deepCopy, isValidArray, isValidNumber, isValidObject, isValidString } from './utils/export.js';
|
|
12
38
|
import { plg } from './matterbridgeTypes.js';
|
|
13
39
|
import { hasParameter } from './utils/export.js';
|
|
14
40
|
import { BridgedDeviceBasicInformation } from '@matter/main/clusters';
|
|
15
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Websocket message ID for logging.
|
|
43
|
+
* @constant {number}
|
|
44
|
+
*/
|
|
16
45
|
export const WS_ID_LOG = 0;
|
|
46
|
+
/**
|
|
47
|
+
* Websocket message ID indicating a refresh is needed.
|
|
48
|
+
* @constant {number}
|
|
49
|
+
*/
|
|
17
50
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
51
|
+
/**
|
|
52
|
+
* Websocket message ID indicating a restart is needed.
|
|
53
|
+
* @constant {number}
|
|
54
|
+
*/
|
|
18
55
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
56
|
+
/**
|
|
57
|
+
* Websocket message ID indicating a cpu update.
|
|
58
|
+
* @constant {number}
|
|
59
|
+
*/
|
|
19
60
|
export const WS_ID_CPU_UPDATE = 3;
|
|
61
|
+
/**
|
|
62
|
+
* Websocket message ID indicating a memory update.
|
|
63
|
+
* @constant {number}
|
|
64
|
+
*/
|
|
20
65
|
export const WS_ID_MEMORY_UPDATE = 4;
|
|
66
|
+
/**
|
|
67
|
+
* Websocket message ID indicating an uptime update.
|
|
68
|
+
* @constant {number}
|
|
69
|
+
*/
|
|
21
70
|
export const WS_ID_UPTIME_UPDATE = 5;
|
|
71
|
+
/**
|
|
72
|
+
* Websocket message ID indicating a memory update.
|
|
73
|
+
* @constant {number}
|
|
74
|
+
*/
|
|
22
75
|
export const WS_ID_SNACKBAR = 6;
|
|
76
|
+
/**
|
|
77
|
+
* Websocket message ID indicating a memory update.
|
|
78
|
+
* @constant {number}
|
|
79
|
+
*/
|
|
23
80
|
export const WS_ID_UPDATE_NEEDED = 7;
|
|
81
|
+
/**
|
|
82
|
+
* Websocket message ID indicating a shelly system update.
|
|
83
|
+
* check:
|
|
84
|
+
* curl -k http://127.0.0.1:8101/api/updates/sys/check
|
|
85
|
+
* perform:
|
|
86
|
+
* curl -k http://127.0.0.1:8101/api/updates/sys/perform
|
|
87
|
+
* @constant {number}
|
|
88
|
+
*/
|
|
24
89
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
90
|
+
/**
|
|
91
|
+
* Websocket message ID indicating a shelly main update.
|
|
92
|
+
* check:
|
|
93
|
+
* curl -k http://127.0.0.1:8101/api/updates/main/check
|
|
94
|
+
* perform:
|
|
95
|
+
* curl -k http://127.0.0.1:8101/api/updates/main/perform
|
|
96
|
+
* @constant {number}
|
|
97
|
+
*/
|
|
25
98
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
26
99
|
export class Frontend {
|
|
27
100
|
matterbridge;
|
|
@@ -39,7 +112,7 @@ export class Frontend {
|
|
|
39
112
|
memoryTimeout;
|
|
40
113
|
constructor(matterbridge) {
|
|
41
114
|
this.matterbridge = matterbridge;
|
|
42
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
115
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
43
116
|
}
|
|
44
117
|
set logLevel(logLevel) {
|
|
45
118
|
this.log.logLevel = logLevel;
|
|
@@ -47,13 +120,25 @@ export class Frontend {
|
|
|
47
120
|
async start(port = 8283) {
|
|
48
121
|
this.port = port;
|
|
49
122
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
123
|
+
// Initialize multer with the upload directory
|
|
50
124
|
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
51
125
|
await fs.mkdir(uploadDir, { recursive: true });
|
|
52
126
|
const upload = multer({ dest: uploadDir });
|
|
127
|
+
// Create the express app that serves the frontend
|
|
53
128
|
this.expressApp = express();
|
|
129
|
+
// Log all requests to the server for debugging
|
|
130
|
+
/*
|
|
131
|
+
this.expressApp.use((req, res, next) => {
|
|
132
|
+
this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
|
|
133
|
+
next();
|
|
134
|
+
});
|
|
135
|
+
*/
|
|
136
|
+
// Serve static files from '/static' endpoint
|
|
54
137
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
55
138
|
if (!hasParameter('ssl')) {
|
|
139
|
+
// Create an HTTP server and attach the express app
|
|
56
140
|
this.httpServer = createServer(this.expressApp);
|
|
141
|
+
// Listen on the specified port
|
|
57
142
|
if (hasParameter('ingress')) {
|
|
58
143
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
59
144
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -67,6 +152,7 @@ export class Frontend {
|
|
|
67
152
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
68
153
|
});
|
|
69
154
|
}
|
|
155
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
156
|
this.httpServer.on('error', (error) => {
|
|
71
157
|
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
72
158
|
switch (error.code) {
|
|
@@ -82,6 +168,7 @@ export class Frontend {
|
|
|
82
168
|
});
|
|
83
169
|
}
|
|
84
170
|
else {
|
|
171
|
+
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
85
172
|
let cert;
|
|
86
173
|
try {
|
|
87
174
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -109,7 +196,9 @@ export class Frontend {
|
|
|
109
196
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
110
197
|
}
|
|
111
198
|
const serverOptions = { cert, key, ca };
|
|
199
|
+
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
112
200
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
201
|
+
// Listen on the specified port
|
|
113
202
|
if (hasParameter('ingress')) {
|
|
114
203
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
115
204
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -123,6 +212,7 @@ export class Frontend {
|
|
|
123
212
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
124
213
|
});
|
|
125
214
|
}
|
|
215
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
126
216
|
this.httpsServer.on('error', (error) => {
|
|
127
217
|
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
128
218
|
switch (error.code) {
|
|
@@ -139,16 +229,18 @@ export class Frontend {
|
|
|
139
229
|
}
|
|
140
230
|
if (this.initializeError)
|
|
141
231
|
return;
|
|
232
|
+
// Create a WebSocket server and attach it to the http or https server
|
|
142
233
|
const wssPort = this.port;
|
|
143
234
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
144
235
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
145
236
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
146
237
|
const clientIp = request.socket.remoteAddress;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
238
|
+
// Set the global logger callback for the WebSocketServer
|
|
239
|
+
let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
|
|
240
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
241
|
+
callbackLogLevel = "info" /* LogLevel.INFO */;
|
|
242
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
243
|
+
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
152
244
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
153
245
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
154
246
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -182,6 +274,8 @@ export class Frontend {
|
|
|
182
274
|
this.webSocketServer.on('error', (ws, error) => {
|
|
183
275
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
184
276
|
});
|
|
277
|
+
// Subscribe to cli events
|
|
278
|
+
const { cliEmitter } = await import('./cli.js');
|
|
185
279
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
186
280
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
187
281
|
});
|
|
@@ -191,6 +285,7 @@ export class Frontend {
|
|
|
191
285
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
192
286
|
this.wssSendCpuUpdate(cpuUsage);
|
|
193
287
|
});
|
|
288
|
+
// Endpoint to validate login code
|
|
194
289
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
195
290
|
const { password } = req.body;
|
|
196
291
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -209,23 +304,27 @@ export class Frontend {
|
|
|
209
304
|
this.log.warn('/api/login error wrong password');
|
|
210
305
|
res.json({ valid: false });
|
|
211
306
|
}
|
|
307
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
212
308
|
}
|
|
213
309
|
catch (error) {
|
|
214
310
|
this.log.error('/api/login error getting password');
|
|
215
311
|
res.json({ valid: false });
|
|
216
312
|
}
|
|
217
313
|
});
|
|
314
|
+
// Endpoint to provide health check
|
|
218
315
|
this.expressApp.get('/health', (req, res) => {
|
|
219
316
|
this.log.debug('Express received /health');
|
|
220
317
|
const healthStatus = {
|
|
221
|
-
status: 'ok',
|
|
222
|
-
uptime: process.uptime(),
|
|
223
|
-
timestamp: new Date().toISOString(),
|
|
318
|
+
status: 'ok', // Indicate service is healthy
|
|
319
|
+
uptime: process.uptime(), // Server uptime in seconds
|
|
320
|
+
timestamp: new Date().toISOString(), // Current timestamp
|
|
224
321
|
};
|
|
225
322
|
res.status(200).json(healthStatus);
|
|
226
323
|
});
|
|
324
|
+
// Endpoint to provide memory usage details
|
|
227
325
|
this.expressApp.get('/memory', async (req, res) => {
|
|
228
326
|
this.log.debug('Express received /memory');
|
|
327
|
+
// Memory usage from process
|
|
229
328
|
const memoryUsageRaw = process.memoryUsage();
|
|
230
329
|
const memoryUsage = {
|
|
231
330
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -234,10 +333,13 @@ export class Frontend {
|
|
|
234
333
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
235
334
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
236
335
|
};
|
|
336
|
+
// V8 heap statistics
|
|
237
337
|
const { default: v8 } = await import('node:v8');
|
|
238
338
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
239
339
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
340
|
+
// Format heapStats
|
|
240
341
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
342
|
+
// Format heapSpaces
|
|
241
343
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
242
344
|
...space,
|
|
243
345
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -255,6 +357,7 @@ export class Frontend {
|
|
|
255
357
|
};
|
|
256
358
|
res.status(200).json(memoryReport);
|
|
257
359
|
});
|
|
360
|
+
// Endpoint to start advertising the server node
|
|
258
361
|
this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
|
|
259
362
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
260
363
|
if (pairingCodes) {
|
|
@@ -265,18 +368,22 @@ export class Frontend {
|
|
|
265
368
|
res.status(500).json({ error: 'Failed to generate pairing codes' });
|
|
266
369
|
}
|
|
267
370
|
});
|
|
371
|
+
// Endpoint to provide settings
|
|
268
372
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
269
373
|
this.log.debug('The frontend sent /api/settings');
|
|
270
374
|
res.json(await this.getApiSettings());
|
|
271
375
|
});
|
|
376
|
+
// Endpoint to provide plugins
|
|
272
377
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
273
378
|
this.log.debug('The frontend sent /api/plugins');
|
|
274
379
|
res.json(this.getBaseRegisteredPlugins());
|
|
275
380
|
});
|
|
381
|
+
// Endpoint to provide devices
|
|
276
382
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
277
383
|
this.log.debug('The frontend sent /api/devices');
|
|
278
384
|
const devices = [];
|
|
279
385
|
this.matterbridge.devices.forEach(async (device) => {
|
|
386
|
+
// Check if the device has the required properties
|
|
280
387
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
281
388
|
return;
|
|
282
389
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -295,6 +402,7 @@ export class Frontend {
|
|
|
295
402
|
});
|
|
296
403
|
res.json(devices);
|
|
297
404
|
});
|
|
405
|
+
// Endpoint to provide the cluster servers of the devices
|
|
298
406
|
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
299
407
|
const selectedPluginName = req.params.selectedPluginName;
|
|
300
408
|
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
@@ -367,6 +475,7 @@ export class Frontend {
|
|
|
367
475
|
});
|
|
368
476
|
res.json(data);
|
|
369
477
|
});
|
|
478
|
+
// Endpoint to view the log
|
|
370
479
|
this.expressApp.get('/api/view-log', async (req, res) => {
|
|
371
480
|
this.log.debug('The frontend sent /api/log');
|
|
372
481
|
try {
|
|
@@ -379,10 +488,12 @@ export class Frontend {
|
|
|
379
488
|
res.status(500).send('Error reading log file');
|
|
380
489
|
}
|
|
381
490
|
});
|
|
491
|
+
// Endpoint to download the matterbridge log
|
|
382
492
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
383
493
|
this.log.debug('The frontend sent /api/download-mblog');
|
|
384
494
|
try {
|
|
385
495
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
|
|
496
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
386
497
|
}
|
|
387
498
|
catch (error) {
|
|
388
499
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -394,10 +505,12 @@ export class Frontend {
|
|
|
394
505
|
}
|
|
395
506
|
});
|
|
396
507
|
});
|
|
508
|
+
// Endpoint to download the matter log
|
|
397
509
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
398
510
|
this.log.debug('The frontend sent /api/download-mjlog');
|
|
399
511
|
try {
|
|
400
512
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
513
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
401
514
|
}
|
|
402
515
|
catch (error) {
|
|
403
516
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -409,10 +522,12 @@ export class Frontend {
|
|
|
409
522
|
}
|
|
410
523
|
});
|
|
411
524
|
});
|
|
525
|
+
// Endpoint to download the matter log
|
|
412
526
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
413
527
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
414
528
|
try {
|
|
415
529
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
|
|
530
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
416
531
|
}
|
|
417
532
|
catch (error) {
|
|
418
533
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'Create the Shelly system log before downloading it.');
|
|
@@ -424,6 +539,7 @@ export class Frontend {
|
|
|
424
539
|
}
|
|
425
540
|
});
|
|
426
541
|
});
|
|
542
|
+
// Endpoint to download the matter storage file
|
|
427
543
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
428
544
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
429
545
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
@@ -434,6 +550,7 @@ export class Frontend {
|
|
|
434
550
|
}
|
|
435
551
|
});
|
|
436
552
|
});
|
|
553
|
+
// Endpoint to download the matterbridge storage directory
|
|
437
554
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
438
555
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
439
556
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -444,6 +561,7 @@ export class Frontend {
|
|
|
444
561
|
}
|
|
445
562
|
});
|
|
446
563
|
});
|
|
564
|
+
// Endpoint to download the matterbridge plugin directory
|
|
447
565
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
448
566
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
449
567
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -454,9 +572,11 @@ export class Frontend {
|
|
|
454
572
|
}
|
|
455
573
|
});
|
|
456
574
|
});
|
|
575
|
+
// Endpoint to download the matterbridge plugin config files
|
|
457
576
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
458
577
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
459
578
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
579
|
+
// 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')));
|
|
460
580
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
461
581
|
if (error) {
|
|
462
582
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
@@ -464,6 +584,7 @@ export class Frontend {
|
|
|
464
584
|
}
|
|
465
585
|
});
|
|
466
586
|
});
|
|
587
|
+
// Endpoint to download the matterbridge plugin config files
|
|
467
588
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
468
589
|
this.log.debug('The frontend sent /api/download-backup');
|
|
469
590
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -473,6 +594,7 @@ export class Frontend {
|
|
|
473
594
|
}
|
|
474
595
|
});
|
|
475
596
|
});
|
|
597
|
+
// Endpoint to receive commands
|
|
476
598
|
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
477
599
|
const command = req.params.command;
|
|
478
600
|
let param = req.params.param;
|
|
@@ -482,13 +604,15 @@ export class Frontend {
|
|
|
482
604
|
return;
|
|
483
605
|
}
|
|
484
606
|
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
607
|
+
// Handle the command setpassword from Settings
|
|
485
608
|
if (command === 'setpassword') {
|
|
486
|
-
const password = param.slice(1, -1);
|
|
609
|
+
const password = param.slice(1, -1); // Remove the first and last characters
|
|
487
610
|
this.log.debug('setpassword', param, password);
|
|
488
611
|
await this.matterbridge.nodeContext?.set('password', password);
|
|
489
612
|
res.json({ message: 'Command received' });
|
|
490
613
|
return;
|
|
491
614
|
}
|
|
615
|
+
// Handle the command setbridgemode from Settings
|
|
492
616
|
if (command === 'setbridgemode') {
|
|
493
617
|
this.log.debug(`setbridgemode: ${param}`);
|
|
494
618
|
this.wssSendRestartRequired();
|
|
@@ -496,6 +620,7 @@ export class Frontend {
|
|
|
496
620
|
res.json({ message: 'Command received' });
|
|
497
621
|
return;
|
|
498
622
|
}
|
|
623
|
+
// Handle the command backup from Settings
|
|
499
624
|
if (command === 'backup') {
|
|
500
625
|
this.log.notice(`Prepairing the backup...`);
|
|
501
626
|
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
|
|
@@ -504,31 +629,33 @@ export class Frontend {
|
|
|
504
629
|
res.json({ message: 'Command received' });
|
|
505
630
|
return;
|
|
506
631
|
}
|
|
632
|
+
// Handle the command setmbloglevel from Settings
|
|
507
633
|
if (command === 'setmbloglevel') {
|
|
508
634
|
this.log.debug('Matterbridge log level:', param);
|
|
509
635
|
if (param === 'Debug') {
|
|
510
|
-
this.log.logLevel = "debug"
|
|
636
|
+
this.log.logLevel = "debug" /* LogLevel.DEBUG */;
|
|
511
637
|
}
|
|
512
638
|
else if (param === 'Info') {
|
|
513
|
-
this.log.logLevel = "info"
|
|
639
|
+
this.log.logLevel = "info" /* LogLevel.INFO */;
|
|
514
640
|
}
|
|
515
641
|
else if (param === 'Notice') {
|
|
516
|
-
this.log.logLevel = "notice"
|
|
642
|
+
this.log.logLevel = "notice" /* LogLevel.NOTICE */;
|
|
517
643
|
}
|
|
518
644
|
else if (param === 'Warn') {
|
|
519
|
-
this.log.logLevel = "warn"
|
|
645
|
+
this.log.logLevel = "warn" /* LogLevel.WARN */;
|
|
520
646
|
}
|
|
521
647
|
else if (param === 'Error') {
|
|
522
|
-
this.log.logLevel = "error"
|
|
648
|
+
this.log.logLevel = "error" /* LogLevel.ERROR */;
|
|
523
649
|
}
|
|
524
650
|
else if (param === 'Fatal') {
|
|
525
|
-
this.log.logLevel = "fatal"
|
|
651
|
+
this.log.logLevel = "fatal" /* LogLevel.FATAL */;
|
|
526
652
|
}
|
|
527
653
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
528
654
|
await this.matterbridge.setLogLevel(this.log.logLevel);
|
|
529
655
|
res.json({ message: 'Command received' });
|
|
530
656
|
return;
|
|
531
657
|
}
|
|
658
|
+
// Handle the command setmbloglevel from Settings
|
|
532
659
|
if (command === 'setmjloglevel') {
|
|
533
660
|
this.log.debug('Matter.js log level:', param);
|
|
534
661
|
if (param === 'Debug') {
|
|
@@ -553,30 +680,34 @@ export class Frontend {
|
|
|
553
680
|
res.json({ message: 'Command received' });
|
|
554
681
|
return;
|
|
555
682
|
}
|
|
683
|
+
// Handle the command setmdnsinterface from Settings
|
|
556
684
|
if (command === 'setmdnsinterface') {
|
|
557
|
-
param = param.slice(1, -1);
|
|
685
|
+
param = param.slice(1, -1); // Remove the first and last characters *mdns*
|
|
558
686
|
this.matterbridge.matterbridgeInformation.mattermdnsinterface = param;
|
|
559
687
|
this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
|
|
560
688
|
await this.matterbridge.nodeContext?.set('mattermdnsinterface', param);
|
|
561
689
|
res.json({ message: 'Command received' });
|
|
562
690
|
return;
|
|
563
691
|
}
|
|
692
|
+
// Handle the command setipv4address from Settings
|
|
564
693
|
if (command === 'setipv4address') {
|
|
565
|
-
param = param.slice(1, -1);
|
|
694
|
+
param = param.slice(1, -1); // Remove the first and last characters *ip*
|
|
566
695
|
this.matterbridge.matterbridgeInformation.matteripv4address = param;
|
|
567
696
|
this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
|
|
568
697
|
await this.matterbridge.nodeContext?.set('matteripv4address', param);
|
|
569
698
|
res.json({ message: 'Command received' });
|
|
570
699
|
return;
|
|
571
700
|
}
|
|
701
|
+
// Handle the command setipv6address from Settings
|
|
572
702
|
if (command === 'setipv6address') {
|
|
573
|
-
param = param.slice(1, -1);
|
|
703
|
+
param = param.slice(1, -1); // Remove the first and last characters *ip*
|
|
574
704
|
this.matterbridge.matterbridgeInformation.matteripv6address = param;
|
|
575
705
|
this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
|
|
576
706
|
await this.matterbridge.nodeContext?.set('matteripv6address', param);
|
|
577
707
|
res.json({ message: 'Command received' });
|
|
578
708
|
return;
|
|
579
709
|
}
|
|
710
|
+
// Handle the command setmatterport from Settings
|
|
580
711
|
if (command === 'setmatterport') {
|
|
581
712
|
const port = Math.min(Math.max(parseInt(param), 5540), 5560);
|
|
582
713
|
this.matterbridge.matterbridgeInformation.matterPort = port;
|
|
@@ -585,6 +716,7 @@ export class Frontend {
|
|
|
585
716
|
res.json({ message: 'Command received' });
|
|
586
717
|
return;
|
|
587
718
|
}
|
|
719
|
+
// Handle the command setmatterdiscriminator from Settings
|
|
588
720
|
if (command === 'setmatterdiscriminator') {
|
|
589
721
|
const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
|
|
590
722
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
|
|
@@ -593,6 +725,7 @@ export class Frontend {
|
|
|
593
725
|
res.json({ message: 'Command received' });
|
|
594
726
|
return;
|
|
595
727
|
}
|
|
728
|
+
// Handle the command setmatterpasscode from Settings
|
|
596
729
|
if (command === 'setmatterpasscode') {
|
|
597
730
|
const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
|
|
598
731
|
this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
|
|
@@ -601,17 +734,20 @@ export class Frontend {
|
|
|
601
734
|
res.json({ message: 'Command received' });
|
|
602
735
|
return;
|
|
603
736
|
}
|
|
737
|
+
// Handle the command setmbloglevel from Settings
|
|
604
738
|
if (command === 'setmblogfile') {
|
|
605
739
|
this.log.debug('Matterbridge file log:', param);
|
|
606
740
|
this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
|
|
607
741
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
|
|
742
|
+
// Create the file logger for matterbridge
|
|
608
743
|
if (param === 'true')
|
|
609
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug"
|
|
744
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
|
|
610
745
|
else
|
|
611
746
|
AnsiLogger.setGlobalLogfile(undefined);
|
|
612
747
|
res.json({ message: 'Command received' });
|
|
613
748
|
return;
|
|
614
749
|
}
|
|
750
|
+
// Handle the command setmbloglevel from Settings
|
|
615
751
|
if (command === 'setmjlogfile') {
|
|
616
752
|
this.log.debug('Matter file log:', param);
|
|
617
753
|
this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
|
|
@@ -638,40 +774,48 @@ export class Frontend {
|
|
|
638
774
|
res.json({ message: 'Command received' });
|
|
639
775
|
return;
|
|
640
776
|
}
|
|
777
|
+
// Handle the command unregister from Settings
|
|
641
778
|
if (command === 'unregister') {
|
|
642
779
|
await this.matterbridge.unregisterAndShutdownProcess();
|
|
643
780
|
res.json({ message: 'Command received' });
|
|
644
781
|
return;
|
|
645
782
|
}
|
|
783
|
+
// Handle the command reset from Settings
|
|
646
784
|
if (command === 'reset') {
|
|
647
785
|
await this.matterbridge.shutdownProcessAndReset();
|
|
648
786
|
res.json({ message: 'Command received' });
|
|
649
787
|
return;
|
|
650
788
|
}
|
|
789
|
+
// Handle the command factoryreset from Settings
|
|
651
790
|
if (command === 'factoryreset') {
|
|
652
791
|
await this.matterbridge.shutdownProcessAndFactoryReset();
|
|
653
792
|
res.json({ message: 'Command received' });
|
|
654
793
|
return;
|
|
655
794
|
}
|
|
795
|
+
// Handle the command shutdown from Header
|
|
656
796
|
if (command === 'shutdown') {
|
|
657
797
|
await this.matterbridge.shutdownProcess();
|
|
658
798
|
res.json({ message: 'Command received' });
|
|
659
799
|
return;
|
|
660
800
|
}
|
|
801
|
+
// Handle the command restart from Header
|
|
661
802
|
if (command === 'restart') {
|
|
662
803
|
await this.matterbridge.restartProcess();
|
|
663
804
|
res.json({ message: 'Command received' });
|
|
664
805
|
return;
|
|
665
806
|
}
|
|
807
|
+
// Handle the command update from Header
|
|
666
808
|
if (command === 'update') {
|
|
667
809
|
await this.matterbridge.updateProcess();
|
|
668
810
|
this.wssSendRestartRequired();
|
|
669
811
|
res.json({ message: 'Command received' });
|
|
670
812
|
return;
|
|
671
813
|
}
|
|
814
|
+
// Handle the command saveconfig from Home
|
|
672
815
|
if (command === 'saveconfig') {
|
|
673
816
|
param = param.replace(/\*/g, '\\');
|
|
674
817
|
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
818
|
+
// console.log('Req.body:', JSON.stringify(req.body, null, 2));
|
|
675
819
|
if (!this.matterbridge.plugins.has(param)) {
|
|
676
820
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
677
821
|
}
|
|
@@ -686,6 +830,7 @@ export class Frontend {
|
|
|
686
830
|
res.json({ message: 'Command received' });
|
|
687
831
|
return;
|
|
688
832
|
}
|
|
833
|
+
// Handle the command installplugin from Home
|
|
689
834
|
if (command === 'installplugin') {
|
|
690
835
|
param = param.replace(/\*/g, '\\');
|
|
691
836
|
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
@@ -694,6 +839,7 @@ export class Frontend {
|
|
|
694
839
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
695
840
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
696
841
|
this.wssSendSnackbarMessage(`Installed package ${param}`);
|
|
842
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
697
843
|
}
|
|
698
844
|
catch (error) {
|
|
699
845
|
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
@@ -701,17 +847,22 @@ export class Frontend {
|
|
|
701
847
|
}
|
|
702
848
|
this.wssSendRestartRequired();
|
|
703
849
|
param = param.split('@')[0];
|
|
850
|
+
// Also add the plugin to matterbridge so no return!
|
|
704
851
|
if (param === 'matterbridge') {
|
|
852
|
+
// 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
|
|
705
853
|
res.json({ message: 'Command received' });
|
|
706
854
|
return;
|
|
707
855
|
}
|
|
708
856
|
}
|
|
857
|
+
// Handle the command addplugin from Home
|
|
709
858
|
if (command === 'addplugin' || command === 'installplugin') {
|
|
710
859
|
param = param.replace(/\*/g, '\\');
|
|
711
860
|
const plugin = await this.matterbridge.plugins.add(param);
|
|
712
861
|
if (plugin) {
|
|
713
862
|
this.wssSendSnackbarMessage(`Added plugin ${param}`);
|
|
714
863
|
if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
864
|
+
// 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
|
|
865
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
715
866
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
716
867
|
}
|
|
717
868
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
@@ -721,13 +872,14 @@ export class Frontend {
|
|
|
721
872
|
res.json({ message: 'Command received' });
|
|
722
873
|
return;
|
|
723
874
|
}
|
|
875
|
+
// Handle the command removeplugin from Home
|
|
724
876
|
if (command === 'removeplugin') {
|
|
725
877
|
if (!this.matterbridge.plugins.has(param)) {
|
|
726
878
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
727
879
|
}
|
|
728
880
|
else {
|
|
729
881
|
const plugin = this.matterbridge.plugins.get(param);
|
|
730
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
882
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true); // This will also close the server node in childbridge mode
|
|
731
883
|
await this.matterbridge.plugins.remove(param);
|
|
732
884
|
this.wssSendSnackbarMessage(`Removed plugin ${param}`);
|
|
733
885
|
this.wssSendRefreshRequired('plugins');
|
|
@@ -735,6 +887,7 @@ export class Frontend {
|
|
|
735
887
|
res.json({ message: 'Command received' });
|
|
736
888
|
return;
|
|
737
889
|
}
|
|
890
|
+
// Handle the command enableplugin from Home
|
|
738
891
|
if (command === 'enableplugin') {
|
|
739
892
|
if (!this.matterbridge.plugins.has(param)) {
|
|
740
893
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -753,6 +906,7 @@ export class Frontend {
|
|
|
753
906
|
await this.matterbridge.plugins.enable(param);
|
|
754
907
|
this.wssSendSnackbarMessage(`Enabled plugin ${param}`);
|
|
755
908
|
if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
|
|
909
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
756
910
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
757
911
|
}
|
|
758
912
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
|
|
@@ -763,6 +917,7 @@ export class Frontend {
|
|
|
763
917
|
res.json({ message: 'Command received' });
|
|
764
918
|
return;
|
|
765
919
|
}
|
|
920
|
+
// Handle the command disableplugin from Home
|
|
766
921
|
if (command === 'disableplugin') {
|
|
767
922
|
if (!this.matterbridge.plugins.has(param)) {
|
|
768
923
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -770,7 +925,7 @@ export class Frontend {
|
|
|
770
925
|
else {
|
|
771
926
|
const plugin = this.matterbridge.plugins.get(param);
|
|
772
927
|
if (plugin && plugin.enabled) {
|
|
773
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
928
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true); // This will also close the server node in childbridge mode
|
|
774
929
|
await this.matterbridge.plugins.disable(param);
|
|
775
930
|
this.wssSendSnackbarMessage(`Disabled plugin ${param}`);
|
|
776
931
|
this.wssSendRefreshRequired('plugins');
|
|
@@ -788,10 +943,13 @@ export class Frontend {
|
|
|
788
943
|
res.status(400).send('Invalid request: file and filename are required');
|
|
789
944
|
return;
|
|
790
945
|
}
|
|
946
|
+
// Define the path where the plugin file will be saved
|
|
791
947
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
792
948
|
try {
|
|
949
|
+
// Move the uploaded file to the specified path
|
|
793
950
|
await fs.rename(file.path, filePath);
|
|
794
951
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
952
|
+
// Install the plugin package
|
|
795
953
|
if (filename.endsWith('.tgz')) {
|
|
796
954
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
797
955
|
this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
|
|
@@ -807,6 +965,7 @@ export class Frontend {
|
|
|
807
965
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
808
966
|
}
|
|
809
967
|
});
|
|
968
|
+
// Fallback for routing (must be the last route)
|
|
810
969
|
this.expressApp.get('*', (req, res) => {
|
|
811
970
|
this.log.debug('The frontend sent:', req.url);
|
|
812
971
|
this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -815,25 +974,31 @@ export class Frontend {
|
|
|
815
974
|
this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
816
975
|
}
|
|
817
976
|
async stop() {
|
|
818
|
-
cliEmitter
|
|
977
|
+
// Remove all listeners from the cliEmitter
|
|
978
|
+
// cliEmitter.removeAllListeners();
|
|
979
|
+
// Close the http server
|
|
819
980
|
if (this.httpServer) {
|
|
820
981
|
this.httpServer.close();
|
|
821
982
|
this.httpServer.removeAllListeners();
|
|
822
983
|
this.httpServer = undefined;
|
|
823
984
|
this.log.debug('Frontend http server closed successfully');
|
|
824
985
|
}
|
|
986
|
+
// Close the https server
|
|
825
987
|
if (this.httpsServer) {
|
|
826
988
|
this.httpsServer.close();
|
|
827
989
|
this.httpsServer.removeAllListeners();
|
|
828
990
|
this.httpsServer = undefined;
|
|
829
991
|
this.log.debug('Frontend https server closed successfully');
|
|
830
992
|
}
|
|
993
|
+
// Remove listeners from the express app
|
|
831
994
|
if (this.expressApp) {
|
|
832
995
|
this.expressApp.removeAllListeners();
|
|
833
996
|
this.expressApp = undefined;
|
|
834
997
|
this.log.debug('Frontend app closed successfully');
|
|
835
998
|
}
|
|
999
|
+
// Close the WebSocket server
|
|
836
1000
|
if (this.webSocketServer) {
|
|
1001
|
+
// Close all active connections
|
|
837
1002
|
this.webSocketServer.clients.forEach((client) => {
|
|
838
1003
|
if (client.readyState === WebSocket.OPEN) {
|
|
839
1004
|
client.close();
|
|
@@ -850,6 +1015,7 @@ export class Frontend {
|
|
|
850
1015
|
this.webSocketServer = undefined;
|
|
851
1016
|
}
|
|
852
1017
|
}
|
|
1018
|
+
// Function to format bytes to KB, MB, or GB
|
|
853
1019
|
formatMemoryUsage = (bytes) => {
|
|
854
1020
|
if (bytes >= 1024 ** 3) {
|
|
855
1021
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -861,6 +1027,7 @@ export class Frontend {
|
|
|
861
1027
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
862
1028
|
}
|
|
863
1029
|
};
|
|
1030
|
+
// Function to format system uptime with only the most significant unit
|
|
864
1031
|
formatOsUpTime = (seconds) => {
|
|
865
1032
|
if (seconds >= 86400) {
|
|
866
1033
|
const days = Math.floor(seconds / 86400);
|
|
@@ -876,8 +1043,13 @@ export class Frontend {
|
|
|
876
1043
|
}
|
|
877
1044
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
878
1045
|
};
|
|
1046
|
+
/**
|
|
1047
|
+
* Retrieves the api settings data.
|
|
1048
|
+
* @returns {Promise<object>} A promise that resolve in the api settings object.
|
|
1049
|
+
*/
|
|
879
1050
|
async getApiSettings() {
|
|
880
1051
|
const { lastCpuUsage } = await import('./cli.js');
|
|
1052
|
+
// Update the system information
|
|
881
1053
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
882
1054
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
883
1055
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -886,6 +1058,7 @@ export class Frontend {
|
|
|
886
1058
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
887
1059
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
888
1060
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
1061
|
+
// Update the matterbridge information
|
|
889
1062
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
890
1063
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
891
1064
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -904,6 +1077,11 @@ export class Frontend {
|
|
|
904
1077
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
905
1078
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
906
1079
|
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Retrieves the reachable attribute.
|
|
1082
|
+
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
1083
|
+
* @returns {boolean} The reachable attribute.
|
|
1084
|
+
*/
|
|
907
1085
|
getReachability(device) {
|
|
908
1086
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
909
1087
|
return false;
|
|
@@ -913,6 +1091,11 @@ export class Frontend {
|
|
|
913
1091
|
return true;
|
|
914
1092
|
return false;
|
|
915
1093
|
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Retrieves the cluster text description from a given device.
|
|
1096
|
+
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
1097
|
+
* @returns {string} The attributes description of the cluster servers in the device.
|
|
1098
|
+
*/
|
|
916
1099
|
getClusterTextFromDevice(device) {
|
|
917
1100
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
918
1101
|
return '';
|
|
@@ -953,6 +1136,7 @@ export class Frontend {
|
|
|
953
1136
|
};
|
|
954
1137
|
let attributes = '';
|
|
955
1138
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1139
|
+
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
956
1140
|
if (typeof attributeValue === 'undefined')
|
|
957
1141
|
return;
|
|
958
1142
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1030,8 +1214,13 @@ export class Frontend {
|
|
|
1030
1214
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1031
1215
|
attributes += `${getUserLabel(device)} `;
|
|
1032
1216
|
});
|
|
1217
|
+
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1033
1218
|
return attributes.trimStart().trimEnd();
|
|
1034
1219
|
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Retrieves the base registered plugins sanitized for res.json().
|
|
1222
|
+
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
1223
|
+
*/
|
|
1035
1224
|
getBaseRegisteredPlugins() {
|
|
1036
1225
|
const baseRegisteredPlugins = [];
|
|
1037
1226
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -1064,6 +1253,13 @@ export class Frontend {
|
|
|
1064
1253
|
}
|
|
1065
1254
|
return baseRegisteredPlugins;
|
|
1066
1255
|
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1258
|
+
*
|
|
1259
|
+
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1260
|
+
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1261
|
+
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1262
|
+
*/
|
|
1067
1263
|
async wsMessageHandler(client, message) {
|
|
1068
1264
|
let data;
|
|
1069
1265
|
try {
|
|
@@ -1209,8 +1405,10 @@ export class Frontend {
|
|
|
1209
1405
|
else if (data.method === '/api/devices') {
|
|
1210
1406
|
const devices = [];
|
|
1211
1407
|
this.matterbridge.devices.forEach(async (device) => {
|
|
1408
|
+
// Filter by pluginName if provided
|
|
1212
1409
|
if (data.params.pluginName && data.params.pluginName !== device.plugin)
|
|
1213
1410
|
return;
|
|
1411
|
+
// Check if the device has the required properties
|
|
1214
1412
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1215
1413
|
return;
|
|
1216
1414
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -1294,6 +1492,7 @@ export class Frontend {
|
|
|
1294
1492
|
});
|
|
1295
1493
|
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
1296
1494
|
deviceTypes = [];
|
|
1495
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1297
1496
|
const name = childEndpoint.endpoint?.id;
|
|
1298
1497
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
1299
1498
|
clusterServers.forEach((clusterServer) => {
|
|
@@ -1347,6 +1546,7 @@ export class Frontend {
|
|
|
1347
1546
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select' }));
|
|
1348
1547
|
return;
|
|
1349
1548
|
}
|
|
1549
|
+
// const selectDeviceValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectDevice.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
|
|
1350
1550
|
const selectDeviceValues = plugin.platform?.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1351
1551
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectDeviceValues }));
|
|
1352
1552
|
return;
|
|
@@ -1361,6 +1561,7 @@ export class Frontend {
|
|
|
1361
1561
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' }));
|
|
1362
1562
|
return;
|
|
1363
1563
|
}
|
|
1564
|
+
// const selectEntityValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectEntity.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
|
|
1364
1565
|
const selectEntityValues = plugin.platform?.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1365
1566
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectEntityValues }));
|
|
1366
1567
|
return;
|
|
@@ -1381,11 +1582,13 @@ export class Frontend {
|
|
|
1381
1582
|
if (config.postfix) {
|
|
1382
1583
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1383
1584
|
}
|
|
1585
|
+
// Add the serial to the whiteList if the whiteList exists and the serial is not already in it
|
|
1384
1586
|
if (isValidArray(config.whiteList, 1)) {
|
|
1385
1587
|
if (!config.whiteList.includes(data.params.serial)) {
|
|
1386
1588
|
config.whiteList.push(data.params.serial);
|
|
1387
1589
|
}
|
|
1388
1590
|
}
|
|
1591
|
+
// Remove the serial from the blackList if the blackList exists and the serial is in it
|
|
1389
1592
|
if (isValidArray(config.blackList, 1)) {
|
|
1390
1593
|
if (config.blackList.includes(data.params.serial)) {
|
|
1391
1594
|
config.blackList = config.blackList.filter((serial) => serial !== data.params.serial);
|
|
@@ -1408,11 +1611,13 @@ export class Frontend {
|
|
|
1408
1611
|
if (config.postfix) {
|
|
1409
1612
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1410
1613
|
}
|
|
1614
|
+
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1411
1615
|
if (isValidArray(config.whiteList, 1)) {
|
|
1412
1616
|
if (config.whiteList.includes(data.params.serial)) {
|
|
1413
1617
|
config.whiteList = config.whiteList.filter((serial) => serial !== data.params.serial);
|
|
1414
1618
|
}
|
|
1415
1619
|
}
|
|
1620
|
+
// Add the serial to the blackList
|
|
1416
1621
|
if (isValidArray(config.blackList)) {
|
|
1417
1622
|
if (!config.blackList.includes(data.params.serial)) {
|
|
1418
1623
|
config.blackList.push(data.params.serial);
|
|
@@ -1436,94 +1641,171 @@ export class Frontend {
|
|
|
1436
1641
|
return;
|
|
1437
1642
|
}
|
|
1438
1643
|
}
|
|
1644
|
+
/**
|
|
1645
|
+
* Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1646
|
+
*
|
|
1647
|
+
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1648
|
+
* @param {string} time - The time string of the message
|
|
1649
|
+
* @param {string} name - The logger name of the message
|
|
1650
|
+
* @param {string} message - The content of the message.
|
|
1651
|
+
*/
|
|
1439
1652
|
wssSendMessage(level, time, name, message) {
|
|
1440
1653
|
if (!level || !time || !name || !message)
|
|
1441
1654
|
return;
|
|
1655
|
+
// Remove ANSI escape codes from the message
|
|
1656
|
+
// eslint-disable-next-line no-control-regex
|
|
1442
1657
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1658
|
+
// Remove leading asterisks from the message
|
|
1443
1659
|
message = message.replace(/^\*+/, '');
|
|
1660
|
+
// Replace all occurrences of \t and \n
|
|
1444
1661
|
message = message.replace(/[\t\n]/g, '');
|
|
1662
|
+
// Remove non-printable characters
|
|
1663
|
+
// eslint-disable-next-line no-control-regex
|
|
1445
1664
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1665
|
+
// Replace all occurrences of \" with "
|
|
1446
1666
|
message = message.replace(/\\"/g, '"');
|
|
1667
|
+
// Define the maximum allowed length for continuous characters without a space
|
|
1447
1668
|
const maxContinuousLength = 100;
|
|
1448
1669
|
const keepStartLength = 20;
|
|
1449
1670
|
const keepEndLength = 20;
|
|
1671
|
+
// Split the message into words
|
|
1450
1672
|
message = message
|
|
1451
1673
|
.split(' ')
|
|
1452
1674
|
.map((word) => {
|
|
1675
|
+
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1453
1676
|
if (word.length > maxContinuousLength) {
|
|
1454
1677
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1455
1678
|
}
|
|
1456
1679
|
return word;
|
|
1457
1680
|
})
|
|
1458
1681
|
.join(' ');
|
|
1682
|
+
// Send the message to all connected clients
|
|
1459
1683
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1460
1684
|
if (client.readyState === WebSocket.OPEN) {
|
|
1461
1685
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1462
1686
|
}
|
|
1463
1687
|
});
|
|
1464
1688
|
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1691
|
+
*
|
|
1692
|
+
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
1693
|
+
* possible values:
|
|
1694
|
+
* - 'matterbridgeLatestVersion'
|
|
1695
|
+
* - 'matterbridgeAdvertise'
|
|
1696
|
+
* - 'online'
|
|
1697
|
+
* - 'offline'
|
|
1698
|
+
* - 'reachability'
|
|
1699
|
+
* - 'settings'
|
|
1700
|
+
* - 'plugins'
|
|
1701
|
+
* - 'devices'
|
|
1702
|
+
* - 'fabrics'
|
|
1703
|
+
* - 'sessions'
|
|
1704
|
+
*/
|
|
1465
1705
|
wssSendRefreshRequired(changed = null) {
|
|
1466
1706
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1707
|
+
// Send the message to all connected clients
|
|
1467
1708
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1468
1709
|
if (client.readyState === WebSocket.OPEN) {
|
|
1469
1710
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1470
1711
|
}
|
|
1471
1712
|
});
|
|
1472
1713
|
}
|
|
1714
|
+
/**
|
|
1715
|
+
* Sends a need to restart WebSocket message to all connected clients.
|
|
1716
|
+
*
|
|
1717
|
+
*/
|
|
1473
1718
|
wssSendRestartRequired(snackbar = true) {
|
|
1474
1719
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1475
1720
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1476
1721
|
if (snackbar === true)
|
|
1477
1722
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1723
|
+
// Send the message to all connected clients
|
|
1478
1724
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1479
1725
|
if (client.readyState === WebSocket.OPEN) {
|
|
1480
1726
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1481
1727
|
}
|
|
1482
1728
|
});
|
|
1483
1729
|
}
|
|
1730
|
+
/**
|
|
1731
|
+
* Sends a need to update WebSocket message to all connected clients.
|
|
1732
|
+
*
|
|
1733
|
+
*/
|
|
1484
1734
|
wssSendUpdateRequired() {
|
|
1485
1735
|
this.log.debug('Sending an update required message to all connected clients');
|
|
1486
1736
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
1737
|
+
// Send the message to all connected clients
|
|
1487
1738
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1488
1739
|
if (client.readyState === WebSocket.OPEN) {
|
|
1489
1740
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
1490
1741
|
}
|
|
1491
1742
|
});
|
|
1492
1743
|
}
|
|
1744
|
+
/**
|
|
1745
|
+
* Sends a memory update message to all connected clients.
|
|
1746
|
+
*
|
|
1747
|
+
*/
|
|
1493
1748
|
wssSendCpuUpdate(cpuUsage) {
|
|
1494
1749
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1750
|
+
// Send the message to all connected clients
|
|
1495
1751
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1496
1752
|
if (client.readyState === WebSocket.OPEN) {
|
|
1497
1753
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
1498
1754
|
}
|
|
1499
1755
|
});
|
|
1500
1756
|
}
|
|
1757
|
+
/**
|
|
1758
|
+
* Sends a cpu update message to all connected clients.
|
|
1759
|
+
*
|
|
1760
|
+
*/
|
|
1501
1761
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
1502
1762
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
1763
|
+
// Send the message to all connected clients
|
|
1503
1764
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1504
1765
|
if (client.readyState === WebSocket.OPEN) {
|
|
1505
1766
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
1506
1767
|
}
|
|
1507
1768
|
});
|
|
1508
1769
|
}
|
|
1770
|
+
/**
|
|
1771
|
+
* Sends a memory update message to all connected clients.
|
|
1772
|
+
*
|
|
1773
|
+
*/
|
|
1509
1774
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
1510
1775
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
1776
|
+
// Send the message to all connected clients
|
|
1511
1777
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1512
1778
|
if (client.readyState === WebSocket.OPEN) {
|
|
1513
1779
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
1514
1780
|
}
|
|
1515
1781
|
});
|
|
1516
1782
|
}
|
|
1783
|
+
/**
|
|
1784
|
+
* Sends a cpu update message to all connected clients.
|
|
1785
|
+
* @param {string} message - The message to send.
|
|
1786
|
+
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
1787
|
+
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
1788
|
+
*
|
|
1789
|
+
*/
|
|
1517
1790
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
1518
1791
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
1792
|
+
// Send the message to all connected clients
|
|
1519
1793
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1520
1794
|
if (client.readyState === WebSocket.OPEN) {
|
|
1521
1795
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { message, timeout, severity } }));
|
|
1522
1796
|
}
|
|
1523
1797
|
});
|
|
1524
1798
|
}
|
|
1799
|
+
/**
|
|
1800
|
+
* Sends a message to all connected clients.
|
|
1801
|
+
* @param {number} id - The message id.
|
|
1802
|
+
* @param {string} method - The message method.
|
|
1803
|
+
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
1804
|
+
*
|
|
1805
|
+
*/
|
|
1525
1806
|
wssBroadcastMessage(id, method, params) {
|
|
1526
1807
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
1808
|
+
// Send the message to all connected clients
|
|
1527
1809
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1528
1810
|
if (client.readyState === WebSocket.OPEN) {
|
|
1529
1811
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -1531,3 +1813,4 @@ export class Frontend {
|
|
|
1531
1813
|
});
|
|
1532
1814
|
}
|
|
1533
1815
|
}
|
|
1816
|
+
//# sourceMappingURL=frontend.js.map
|