matterbridge 2.2.0-dev.9 → 2.2.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 +20 -1
- package/README.md +2 -2
- 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 +178 -0
- package/dist/frontend.d.ts.map +1 -0
- package/dist/frontend.js +356 -27
- 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 +411 -0
- package/dist/matterbridge.d.ts.map +1 -0
- package/dist/matterbridge.js +720 -48
- 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 +692 -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 +245 -20
- package/dist/matterbridgePlatform.js.map +1 -0
- package/dist/matterbridgeTypes.d.ts +177 -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 +232 -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 +122 -7
- 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.025c65d2.js +115 -0
- package/frontend/build/static/js/main.025c65d2.js.map +1 -0
- package/npm-shrinkwrap.json +44 -44
- package/package.json +3 -2
- 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.025c65d2.js.LICENSE.txt} +0 -0
package/dist/frontend.js
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
1
|
-
|
|
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
|
+
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 https from 'https';
|
|
4
28
|
import express from 'express';
|
|
@@ -6,19 +30,71 @@ import WebSocket, { WebSocketServer } from 'ws';
|
|
|
6
30
|
import os from 'node:os';
|
|
7
31
|
import path from 'node:path';
|
|
8
32
|
import { promises as fs } from 'node:fs';
|
|
33
|
+
// AnsiLogger module
|
|
9
34
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';
|
|
10
|
-
|
|
35
|
+
// Matterbridge
|
|
36
|
+
import { createZip, deepCopy, isValidArray, isValidNumber, isValidObject, isValidString } from './utils/export.js';
|
|
11
37
|
import { plg } from './matterbridgeTypes.js';
|
|
12
38
|
import { hasParameter } from './utils/export.js';
|
|
39
|
+
import { BridgedDeviceBasicInformation } from '@matter/main/clusters';
|
|
40
|
+
/**
|
|
41
|
+
* Websocket message ID for logging.
|
|
42
|
+
* @constant {number}
|
|
43
|
+
*/
|
|
13
44
|
export const WS_ID_LOG = 0;
|
|
45
|
+
/**
|
|
46
|
+
* Websocket message ID indicating a refresh is needed.
|
|
47
|
+
* @constant {number}
|
|
48
|
+
*/
|
|
14
49
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
50
|
+
/**
|
|
51
|
+
* Websocket message ID indicating a restart is needed.
|
|
52
|
+
* @constant {number}
|
|
53
|
+
*/
|
|
15
54
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
55
|
+
/**
|
|
56
|
+
* Websocket message ID indicating a cpu update.
|
|
57
|
+
* @constant {number}
|
|
58
|
+
*/
|
|
16
59
|
export const WS_ID_CPU_UPDATE = 3;
|
|
60
|
+
/**
|
|
61
|
+
* Websocket message ID indicating a memory update.
|
|
62
|
+
* @constant {number}
|
|
63
|
+
*/
|
|
17
64
|
export const WS_ID_MEMORY_UPDATE = 4;
|
|
65
|
+
/**
|
|
66
|
+
* Websocket message ID indicating an uptime update.
|
|
67
|
+
* @constant {number}
|
|
68
|
+
*/
|
|
18
69
|
export const WS_ID_UPTIME_UPDATE = 5;
|
|
70
|
+
/**
|
|
71
|
+
* Websocket message ID indicating a memory update.
|
|
72
|
+
* @constant {number}
|
|
73
|
+
*/
|
|
19
74
|
export const WS_ID_SNACKBAR = 6;
|
|
75
|
+
/**
|
|
76
|
+
* Websocket message ID indicating a shelly system update.
|
|
77
|
+
* check:
|
|
78
|
+
* curl -k http://127.0.0.1:8101/api/updates/sys/check
|
|
79
|
+
* perform:
|
|
80
|
+
* curl -k http://127.0.0.1:8101/api/updates/sys/perform
|
|
81
|
+
* @constant {number}
|
|
82
|
+
*/
|
|
20
83
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
84
|
+
/**
|
|
85
|
+
* Websocket message ID indicating a shelly main update.
|
|
86
|
+
* check:
|
|
87
|
+
* curl -k http://127.0.0.1:8101/api/updates/main/check
|
|
88
|
+
* perform:
|
|
89
|
+
* curl -k http://127.0.0.1:8101/api/updates/main/perform
|
|
90
|
+
* @constant {number}
|
|
91
|
+
*/
|
|
21
92
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
93
|
+
/**
|
|
94
|
+
* Initializes the frontend of Matterbridge.
|
|
95
|
+
*
|
|
96
|
+
* @param port The port number to run the frontend server on. Default is 8283.
|
|
97
|
+
*/
|
|
22
98
|
export class Frontend {
|
|
23
99
|
matterbridge;
|
|
24
100
|
log;
|
|
@@ -35,7 +111,7 @@ export class Frontend {
|
|
|
35
111
|
memoryTimeout;
|
|
36
112
|
constructor(matterbridge) {
|
|
37
113
|
this.matterbridge = matterbridge;
|
|
38
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
114
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
39
115
|
}
|
|
40
116
|
set logLevel(logLevel) {
|
|
41
117
|
this.log.logLevel = logLevel;
|
|
@@ -43,10 +119,21 @@ export class Frontend {
|
|
|
43
119
|
async start(port = 8283) {
|
|
44
120
|
this.port = port;
|
|
45
121
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
122
|
+
// Create the express app that serves the frontend
|
|
46
123
|
this.expressApp = express();
|
|
124
|
+
// Log all requests to the server for debugging
|
|
125
|
+
/*
|
|
126
|
+
this.expressApp.use((req, res, next) => {
|
|
127
|
+
this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
|
|
128
|
+
next();
|
|
129
|
+
});
|
|
130
|
+
*/
|
|
131
|
+
// Serve static files from '/static' endpoint
|
|
47
132
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
48
133
|
if (!hasParameter('ssl')) {
|
|
134
|
+
// Create an HTTP server and attach the express app
|
|
49
135
|
this.httpServer = createServer(this.expressApp);
|
|
136
|
+
// Listen on the specified port
|
|
50
137
|
if (hasParameter('ingress')) {
|
|
51
138
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
52
139
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -60,6 +147,7 @@ export class Frontend {
|
|
|
60
147
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
61
148
|
});
|
|
62
149
|
}
|
|
150
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63
151
|
this.httpServer.on('error', (error) => {
|
|
64
152
|
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
65
153
|
switch (error.code) {
|
|
@@ -75,6 +163,7 @@ export class Frontend {
|
|
|
75
163
|
});
|
|
76
164
|
}
|
|
77
165
|
else {
|
|
166
|
+
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
78
167
|
let cert;
|
|
79
168
|
try {
|
|
80
169
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -102,7 +191,9 @@ export class Frontend {
|
|
|
102
191
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
103
192
|
}
|
|
104
193
|
const serverOptions = { cert, key, ca };
|
|
194
|
+
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
105
195
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
196
|
+
// Listen on the specified port
|
|
106
197
|
if (hasParameter('ingress')) {
|
|
107
198
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
108
199
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -116,6 +207,7 @@ export class Frontend {
|
|
|
116
207
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
117
208
|
});
|
|
118
209
|
}
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
119
211
|
this.httpsServer.on('error', (error) => {
|
|
120
212
|
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
121
213
|
switch (error.code) {
|
|
@@ -132,16 +224,18 @@ export class Frontend {
|
|
|
132
224
|
}
|
|
133
225
|
if (this.initializeError)
|
|
134
226
|
return;
|
|
227
|
+
// Create a WebSocket server and attach it to the http or https server
|
|
135
228
|
const wssPort = this.port;
|
|
136
229
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
137
230
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
138
231
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
139
232
|
const clientIp = request.socket.remoteAddress;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
233
|
+
// Set the global logger callback for the WebSocketServer
|
|
234
|
+
let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
|
|
235
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
236
|
+
callbackLogLevel = "info" /* LogLevel.INFO */;
|
|
237
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
238
|
+
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
145
239
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
146
240
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
147
241
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -175,6 +269,7 @@ export class Frontend {
|
|
|
175
269
|
this.webSocketServer.on('error', (ws, error) => {
|
|
176
270
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
177
271
|
});
|
|
272
|
+
// Subscribe to cli events
|
|
178
273
|
const { cliEmitter } = await import('./cli.js');
|
|
179
274
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
180
275
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -185,6 +280,7 @@ export class Frontend {
|
|
|
185
280
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
186
281
|
this.wssSendCpuUpdate(cpuUsage);
|
|
187
282
|
});
|
|
283
|
+
// Endpoint to validate login code
|
|
188
284
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
189
285
|
const { password } = req.body;
|
|
190
286
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -203,23 +299,27 @@ export class Frontend {
|
|
|
203
299
|
this.log.warn('/api/login error wrong password');
|
|
204
300
|
res.json({ valid: false });
|
|
205
301
|
}
|
|
302
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
206
303
|
}
|
|
207
304
|
catch (error) {
|
|
208
305
|
this.log.error('/api/login error getting password');
|
|
209
306
|
res.json({ valid: false });
|
|
210
307
|
}
|
|
211
308
|
});
|
|
309
|
+
// Endpoint to provide health check
|
|
212
310
|
this.expressApp.get('/health', (req, res) => {
|
|
213
311
|
this.log.debug('Express received /health');
|
|
214
312
|
const healthStatus = {
|
|
215
|
-
status: 'ok',
|
|
216
|
-
uptime: process.uptime(),
|
|
217
|
-
timestamp: new Date().toISOString(),
|
|
313
|
+
status: 'ok', // Indicate service is healthy
|
|
314
|
+
uptime: process.uptime(), // Server uptime in seconds
|
|
315
|
+
timestamp: new Date().toISOString(), // Current timestamp
|
|
218
316
|
};
|
|
219
317
|
res.status(200).json(healthStatus);
|
|
220
318
|
});
|
|
319
|
+
// Endpoint to provide memory usage details
|
|
221
320
|
this.expressApp.get('/memory', async (req, res) => {
|
|
222
321
|
this.log.debug('Express received /memory');
|
|
322
|
+
// Memory usage from process
|
|
223
323
|
const memoryUsageRaw = process.memoryUsage();
|
|
224
324
|
const memoryUsage = {
|
|
225
325
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -228,10 +328,13 @@ export class Frontend {
|
|
|
228
328
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
229
329
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
230
330
|
};
|
|
331
|
+
// V8 heap statistics
|
|
231
332
|
const { default: v8 } = await import('node:v8');
|
|
232
333
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
233
334
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
335
|
+
// Format heapStats
|
|
234
336
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
337
|
+
// Format heapSpaces
|
|
235
338
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
236
339
|
...space,
|
|
237
340
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -249,6 +352,7 @@ export class Frontend {
|
|
|
249
352
|
};
|
|
250
353
|
res.status(200).json(memoryReport);
|
|
251
354
|
});
|
|
355
|
+
// Endpoint to start advertising the server node
|
|
252
356
|
this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
|
|
253
357
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
254
358
|
if (pairingCodes) {
|
|
@@ -259,18 +363,22 @@ export class Frontend {
|
|
|
259
363
|
res.status(500).json({ error: 'Failed to generate pairing codes' });
|
|
260
364
|
}
|
|
261
365
|
});
|
|
366
|
+
// Endpoint to provide settings
|
|
262
367
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
263
368
|
this.log.debug('The frontend sent /api/settings');
|
|
264
369
|
res.json(await this.getApiSettings());
|
|
265
370
|
});
|
|
371
|
+
// Endpoint to provide plugins
|
|
266
372
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
267
373
|
this.log.debug('The frontend sent /api/plugins');
|
|
268
374
|
res.json(this.getBaseRegisteredPlugins());
|
|
269
375
|
});
|
|
376
|
+
// Endpoint to provide devices
|
|
270
377
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
271
378
|
this.log.debug('The frontend sent /api/devices');
|
|
272
379
|
const devices = [];
|
|
273
380
|
this.matterbridge.devices.forEach(async (device) => {
|
|
381
|
+
// Check if the device has the required properties
|
|
274
382
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
275
383
|
return;
|
|
276
384
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -283,11 +391,13 @@ export class Frontend {
|
|
|
283
391
|
productUrl: device.productUrl,
|
|
284
392
|
configUrl: device.configUrl,
|
|
285
393
|
uniqueId: device.uniqueId,
|
|
394
|
+
reachable: this.getReachability(device),
|
|
286
395
|
cluster: cluster,
|
|
287
396
|
});
|
|
288
397
|
});
|
|
289
398
|
res.json(devices);
|
|
290
399
|
});
|
|
400
|
+
// Endpoint to provide the cluster servers of the devices
|
|
291
401
|
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
292
402
|
const selectedPluginName = req.params.selectedPluginName;
|
|
293
403
|
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
@@ -360,6 +470,7 @@ export class Frontend {
|
|
|
360
470
|
});
|
|
361
471
|
res.json(data);
|
|
362
472
|
});
|
|
473
|
+
// Endpoint to view the log
|
|
363
474
|
this.expressApp.get('/api/view-log', async (req, res) => {
|
|
364
475
|
this.log.debug('The frontend sent /api/log');
|
|
365
476
|
try {
|
|
@@ -372,10 +483,12 @@ export class Frontend {
|
|
|
372
483
|
res.status(500).send('Error reading log file');
|
|
373
484
|
}
|
|
374
485
|
});
|
|
486
|
+
// Endpoint to download the matterbridge log
|
|
375
487
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
376
488
|
this.log.debug('The frontend sent /api/download-mblog');
|
|
377
489
|
try {
|
|
378
490
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
|
|
491
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
379
492
|
}
|
|
380
493
|
catch (error) {
|
|
381
494
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -387,10 +500,12 @@ export class Frontend {
|
|
|
387
500
|
}
|
|
388
501
|
});
|
|
389
502
|
});
|
|
503
|
+
// Endpoint to download the matter log
|
|
390
504
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
391
505
|
this.log.debug('The frontend sent /api/download-mjlog');
|
|
392
506
|
try {
|
|
393
507
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
508
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
394
509
|
}
|
|
395
510
|
catch (error) {
|
|
396
511
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -402,10 +517,12 @@ export class Frontend {
|
|
|
402
517
|
}
|
|
403
518
|
});
|
|
404
519
|
});
|
|
520
|
+
// Endpoint to download the matter log
|
|
405
521
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
406
522
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
407
523
|
try {
|
|
408
524
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
|
|
525
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
409
526
|
}
|
|
410
527
|
catch (error) {
|
|
411
528
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'Create the Shelly system log before downloading it.');
|
|
@@ -417,6 +534,7 @@ export class Frontend {
|
|
|
417
534
|
}
|
|
418
535
|
});
|
|
419
536
|
});
|
|
537
|
+
// Endpoint to download the matter storage file
|
|
420
538
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
421
539
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
422
540
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
@@ -427,6 +545,7 @@ export class Frontend {
|
|
|
427
545
|
}
|
|
428
546
|
});
|
|
429
547
|
});
|
|
548
|
+
// Endpoint to download the matterbridge storage directory
|
|
430
549
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
431
550
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
432
551
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -437,6 +556,7 @@ export class Frontend {
|
|
|
437
556
|
}
|
|
438
557
|
});
|
|
439
558
|
});
|
|
559
|
+
// Endpoint to download the matterbridge plugin directory
|
|
440
560
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
441
561
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
442
562
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -447,9 +567,11 @@ export class Frontend {
|
|
|
447
567
|
}
|
|
448
568
|
});
|
|
449
569
|
});
|
|
570
|
+
// Endpoint to download the matterbridge plugin config files
|
|
450
571
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
451
572
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
452
573
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
574
|
+
// 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')));
|
|
453
575
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
454
576
|
if (error) {
|
|
455
577
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
@@ -457,6 +579,7 @@ export class Frontend {
|
|
|
457
579
|
}
|
|
458
580
|
});
|
|
459
581
|
});
|
|
582
|
+
// Endpoint to download the matterbridge plugin config files
|
|
460
583
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
461
584
|
this.log.debug('The frontend sent /api/download-backup');
|
|
462
585
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -466,6 +589,7 @@ export class Frontend {
|
|
|
466
589
|
}
|
|
467
590
|
});
|
|
468
591
|
});
|
|
592
|
+
// Endpoint to receive commands
|
|
469
593
|
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
470
594
|
const command = req.params.command;
|
|
471
595
|
let param = req.params.param;
|
|
@@ -475,13 +599,15 @@ export class Frontend {
|
|
|
475
599
|
return;
|
|
476
600
|
}
|
|
477
601
|
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
602
|
+
// Handle the command setpassword from Settings
|
|
478
603
|
if (command === 'setpassword') {
|
|
479
|
-
const password = param.slice(1, -1);
|
|
604
|
+
const password = param.slice(1, -1); // Remove the first and last characters
|
|
480
605
|
this.log.debug('setpassword', param, password);
|
|
481
606
|
await this.matterbridge.nodeContext?.set('password', password);
|
|
482
607
|
res.json({ message: 'Command received' });
|
|
483
608
|
return;
|
|
484
609
|
}
|
|
610
|
+
// Handle the command setbridgemode from Settings
|
|
485
611
|
if (command === 'setbridgemode') {
|
|
486
612
|
this.log.debug(`setbridgemode: ${param}`);
|
|
487
613
|
this.wssSendRestartRequired();
|
|
@@ -489,6 +615,7 @@ export class Frontend {
|
|
|
489
615
|
res.json({ message: 'Command received' });
|
|
490
616
|
return;
|
|
491
617
|
}
|
|
618
|
+
// Handle the command backup from Settings
|
|
492
619
|
if (command === 'backup') {
|
|
493
620
|
this.log.notice(`Prepairing the backup...`);
|
|
494
621
|
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
|
|
@@ -497,31 +624,33 @@ export class Frontend {
|
|
|
497
624
|
res.json({ message: 'Command received' });
|
|
498
625
|
return;
|
|
499
626
|
}
|
|
627
|
+
// Handle the command setmbloglevel from Settings
|
|
500
628
|
if (command === 'setmbloglevel') {
|
|
501
629
|
this.log.debug('Matterbridge log level:', param);
|
|
502
630
|
if (param === 'Debug') {
|
|
503
|
-
this.log.logLevel = "debug"
|
|
631
|
+
this.log.logLevel = "debug" /* LogLevel.DEBUG */;
|
|
504
632
|
}
|
|
505
633
|
else if (param === 'Info') {
|
|
506
|
-
this.log.logLevel = "info"
|
|
634
|
+
this.log.logLevel = "info" /* LogLevel.INFO */;
|
|
507
635
|
}
|
|
508
636
|
else if (param === 'Notice') {
|
|
509
|
-
this.log.logLevel = "notice"
|
|
637
|
+
this.log.logLevel = "notice" /* LogLevel.NOTICE */;
|
|
510
638
|
}
|
|
511
639
|
else if (param === 'Warn') {
|
|
512
|
-
this.log.logLevel = "warn"
|
|
640
|
+
this.log.logLevel = "warn" /* LogLevel.WARN */;
|
|
513
641
|
}
|
|
514
642
|
else if (param === 'Error') {
|
|
515
|
-
this.log.logLevel = "error"
|
|
643
|
+
this.log.logLevel = "error" /* LogLevel.ERROR */;
|
|
516
644
|
}
|
|
517
645
|
else if (param === 'Fatal') {
|
|
518
|
-
this.log.logLevel = "fatal"
|
|
646
|
+
this.log.logLevel = "fatal" /* LogLevel.FATAL */;
|
|
519
647
|
}
|
|
520
648
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
521
649
|
await this.matterbridge.setLogLevel(this.log.logLevel);
|
|
522
650
|
res.json({ message: 'Command received' });
|
|
523
651
|
return;
|
|
524
652
|
}
|
|
653
|
+
// Handle the command setmbloglevel from Settings
|
|
525
654
|
if (command === 'setmjloglevel') {
|
|
526
655
|
this.log.debug('Matter.js log level:', param);
|
|
527
656
|
if (param === 'Debug') {
|
|
@@ -546,30 +675,34 @@ export class Frontend {
|
|
|
546
675
|
res.json({ message: 'Command received' });
|
|
547
676
|
return;
|
|
548
677
|
}
|
|
678
|
+
// Handle the command setmdnsinterface from Settings
|
|
549
679
|
if (command === 'setmdnsinterface') {
|
|
550
|
-
param = param.slice(1, -1);
|
|
680
|
+
param = param.slice(1, -1); // Remove the first and last characters *mdns*
|
|
551
681
|
this.matterbridge.matterbridgeInformation.mattermdnsinterface = param;
|
|
552
682
|
this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
|
|
553
683
|
await this.matterbridge.nodeContext?.set('mattermdnsinterface', param);
|
|
554
684
|
res.json({ message: 'Command received' });
|
|
555
685
|
return;
|
|
556
686
|
}
|
|
687
|
+
// Handle the command setipv4address from Settings
|
|
557
688
|
if (command === 'setipv4address') {
|
|
558
|
-
param = param.slice(1, -1);
|
|
689
|
+
param = param.slice(1, -1); // Remove the first and last characters *ip*
|
|
559
690
|
this.matterbridge.matterbridgeInformation.matteripv4address = param;
|
|
560
691
|
this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
|
|
561
692
|
await this.matterbridge.nodeContext?.set('matteripv4address', param);
|
|
562
693
|
res.json({ message: 'Command received' });
|
|
563
694
|
return;
|
|
564
695
|
}
|
|
696
|
+
// Handle the command setipv6address from Settings
|
|
565
697
|
if (command === 'setipv6address') {
|
|
566
|
-
param = param.slice(1, -1);
|
|
698
|
+
param = param.slice(1, -1); // Remove the first and last characters *ip*
|
|
567
699
|
this.matterbridge.matterbridgeInformation.matteripv6address = param;
|
|
568
700
|
this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
|
|
569
701
|
await this.matterbridge.nodeContext?.set('matteripv6address', param);
|
|
570
702
|
res.json({ message: 'Command received' });
|
|
571
703
|
return;
|
|
572
704
|
}
|
|
705
|
+
// Handle the command setmatterport from Settings
|
|
573
706
|
if (command === 'setmatterport') {
|
|
574
707
|
const port = Math.min(Math.max(parseInt(param), 5540), 5560);
|
|
575
708
|
this.matterbridge.matterbridgeInformation.matterPort = port;
|
|
@@ -578,6 +711,7 @@ export class Frontend {
|
|
|
578
711
|
res.json({ message: 'Command received' });
|
|
579
712
|
return;
|
|
580
713
|
}
|
|
714
|
+
// Handle the command setmatterdiscriminator from Settings
|
|
581
715
|
if (command === 'setmatterdiscriminator') {
|
|
582
716
|
const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
|
|
583
717
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
|
|
@@ -586,6 +720,7 @@ export class Frontend {
|
|
|
586
720
|
res.json({ message: 'Command received' });
|
|
587
721
|
return;
|
|
588
722
|
}
|
|
723
|
+
// Handle the command setmatterpasscode from Settings
|
|
589
724
|
if (command === 'setmatterpasscode') {
|
|
590
725
|
const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
|
|
591
726
|
this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
|
|
@@ -594,17 +729,20 @@ export class Frontend {
|
|
|
594
729
|
res.json({ message: 'Command received' });
|
|
595
730
|
return;
|
|
596
731
|
}
|
|
732
|
+
// Handle the command setmbloglevel from Settings
|
|
597
733
|
if (command === 'setmblogfile') {
|
|
598
734
|
this.log.debug('Matterbridge file log:', param);
|
|
599
735
|
this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
|
|
600
736
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
|
|
737
|
+
// Create the file logger for matterbridge
|
|
601
738
|
if (param === 'true')
|
|
602
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug"
|
|
739
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
|
|
603
740
|
else
|
|
604
741
|
AnsiLogger.setGlobalLogfile(undefined);
|
|
605
742
|
res.json({ message: 'Command received' });
|
|
606
743
|
return;
|
|
607
744
|
}
|
|
745
|
+
// Handle the command setmbloglevel from Settings
|
|
608
746
|
if (command === 'setmjlogfile') {
|
|
609
747
|
this.log.debug('Matter file log:', param);
|
|
610
748
|
this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
|
|
@@ -631,40 +769,48 @@ export class Frontend {
|
|
|
631
769
|
res.json({ message: 'Command received' });
|
|
632
770
|
return;
|
|
633
771
|
}
|
|
772
|
+
// Handle the command unregister from Settings
|
|
634
773
|
if (command === 'unregister') {
|
|
635
774
|
await this.matterbridge.unregisterAndShutdownProcess();
|
|
636
775
|
res.json({ message: 'Command received' });
|
|
637
776
|
return;
|
|
638
777
|
}
|
|
778
|
+
// Handle the command reset from Settings
|
|
639
779
|
if (command === 'reset') {
|
|
640
780
|
await this.matterbridge.shutdownProcessAndReset();
|
|
641
781
|
res.json({ message: 'Command received' });
|
|
642
782
|
return;
|
|
643
783
|
}
|
|
784
|
+
// Handle the command factoryreset from Settings
|
|
644
785
|
if (command === 'factoryreset') {
|
|
645
786
|
await this.matterbridge.shutdownProcessAndFactoryReset();
|
|
646
787
|
res.json({ message: 'Command received' });
|
|
647
788
|
return;
|
|
648
789
|
}
|
|
790
|
+
// Handle the command shutdown from Header
|
|
649
791
|
if (command === 'shutdown') {
|
|
650
792
|
await this.matterbridge.shutdownProcess();
|
|
651
793
|
res.json({ message: 'Command received' });
|
|
652
794
|
return;
|
|
653
795
|
}
|
|
796
|
+
// Handle the command restart from Header
|
|
654
797
|
if (command === 'restart') {
|
|
655
798
|
await this.matterbridge.restartProcess();
|
|
656
799
|
res.json({ message: 'Command received' });
|
|
657
800
|
return;
|
|
658
801
|
}
|
|
802
|
+
// Handle the command update from Header
|
|
659
803
|
if (command === 'update') {
|
|
660
804
|
await this.matterbridge.updateProcess();
|
|
661
805
|
this.wssSendRestartRequired();
|
|
662
806
|
res.json({ message: 'Command received' });
|
|
663
807
|
return;
|
|
664
808
|
}
|
|
809
|
+
// Handle the command saveconfig from Home
|
|
665
810
|
if (command === 'saveconfig') {
|
|
666
811
|
param = param.replace(/\*/g, '\\');
|
|
667
812
|
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
813
|
+
// console.log('Req.body:', JSON.stringify(req.body, null, 2));
|
|
668
814
|
if (!this.matterbridge.plugins.has(param)) {
|
|
669
815
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
670
816
|
}
|
|
@@ -679,6 +825,7 @@ export class Frontend {
|
|
|
679
825
|
res.json({ message: 'Command received' });
|
|
680
826
|
return;
|
|
681
827
|
}
|
|
828
|
+
// Handle the command installplugin from Home
|
|
682
829
|
if (command === 'installplugin') {
|
|
683
830
|
param = param.replace(/\*/g, '\\');
|
|
684
831
|
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
@@ -687,24 +834,30 @@ export class Frontend {
|
|
|
687
834
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
688
835
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
689
836
|
this.wssSendSnackbarMessage(`Installed package ${param}`);
|
|
837
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
690
838
|
}
|
|
691
839
|
catch (error) {
|
|
692
840
|
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
693
841
|
this.wssSendSnackbarMessage(`Package ${param} not installed`);
|
|
694
842
|
}
|
|
695
|
-
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
696
843
|
this.wssSendRestartRequired();
|
|
697
844
|
param = param.split('@')[0];
|
|
845
|
+
// Also add the plugin to matterbridge so no return!
|
|
698
846
|
if (param === 'matterbridge') {
|
|
847
|
+
// 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
|
|
699
848
|
res.json({ message: 'Command received' });
|
|
700
849
|
return;
|
|
701
850
|
}
|
|
702
851
|
}
|
|
852
|
+
// Handle the command addplugin from Home
|
|
703
853
|
if (command === 'addplugin' || command === 'installplugin') {
|
|
704
854
|
param = param.replace(/\*/g, '\\');
|
|
705
855
|
const plugin = await this.matterbridge.plugins.add(param);
|
|
706
856
|
if (plugin) {
|
|
857
|
+
this.wssSendSnackbarMessage(`Added plugin ${param}`);
|
|
707
858
|
if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
859
|
+
// 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
|
|
860
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
708
861
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
709
862
|
}
|
|
710
863
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
@@ -714,19 +867,22 @@ export class Frontend {
|
|
|
714
867
|
res.json({ message: 'Command received' });
|
|
715
868
|
return;
|
|
716
869
|
}
|
|
870
|
+
// Handle the command removeplugin from Home
|
|
717
871
|
if (command === 'removeplugin') {
|
|
718
872
|
if (!this.matterbridge.plugins.has(param)) {
|
|
719
873
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
720
874
|
}
|
|
721
875
|
else {
|
|
722
876
|
const plugin = this.matterbridge.plugins.get(param);
|
|
723
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
877
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true); // This will also close the server node in childbridge mode
|
|
724
878
|
await this.matterbridge.plugins.remove(param);
|
|
879
|
+
this.wssSendSnackbarMessage(`Removed plugin ${param}`);
|
|
725
880
|
}
|
|
726
881
|
res.json({ message: 'Command received' });
|
|
727
882
|
this.wssSendRefreshRequired();
|
|
728
883
|
return;
|
|
729
884
|
}
|
|
885
|
+
// Handle the command enableplugin from Home
|
|
730
886
|
if (command === 'enableplugin') {
|
|
731
887
|
if (!this.matterbridge.plugins.has(param)) {
|
|
732
888
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -743,7 +899,9 @@ export class Frontend {
|
|
|
743
899
|
plugin.registeredDevices = undefined;
|
|
744
900
|
plugin.addedDevices = undefined;
|
|
745
901
|
await this.matterbridge.plugins.enable(param);
|
|
902
|
+
this.wssSendSnackbarMessage(`Enabled plugin ${param}`);
|
|
746
903
|
if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
|
|
904
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
747
905
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
748
906
|
}
|
|
749
907
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
|
|
@@ -755,6 +913,7 @@ export class Frontend {
|
|
|
755
913
|
this.wssSendRefreshRequired();
|
|
756
914
|
return;
|
|
757
915
|
}
|
|
916
|
+
// Handle the command disableplugin from Home
|
|
758
917
|
if (command === 'disableplugin') {
|
|
759
918
|
if (!this.matterbridge.plugins.has(param)) {
|
|
760
919
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -762,8 +921,9 @@ export class Frontend {
|
|
|
762
921
|
else {
|
|
763
922
|
const plugin = this.matterbridge.plugins.get(param);
|
|
764
923
|
if (plugin && plugin.enabled) {
|
|
765
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
924
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true); // This will also close the server node in childbridge mode
|
|
766
925
|
await this.matterbridge.plugins.disable(param);
|
|
926
|
+
this.wssSendSnackbarMessage(`Disabled plugin ${param}`);
|
|
767
927
|
}
|
|
768
928
|
}
|
|
769
929
|
res.json({ message: 'Command received' });
|
|
@@ -771,6 +931,7 @@ export class Frontend {
|
|
|
771
931
|
return;
|
|
772
932
|
}
|
|
773
933
|
});
|
|
934
|
+
// Fallback for routing (must be the last route)
|
|
774
935
|
this.expressApp.get('*', (req, res) => {
|
|
775
936
|
this.log.debug('The frontend sent:', req.url);
|
|
776
937
|
this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -779,24 +940,29 @@ export class Frontend {
|
|
|
779
940
|
this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
780
941
|
}
|
|
781
942
|
async stop() {
|
|
943
|
+
// Close the http server
|
|
782
944
|
if (this.httpServer) {
|
|
783
945
|
this.httpServer.close();
|
|
784
946
|
this.httpServer.removeAllListeners();
|
|
785
947
|
this.httpServer = undefined;
|
|
786
948
|
this.log.debug('Frontend http server closed successfully');
|
|
787
949
|
}
|
|
950
|
+
// Close the https server
|
|
788
951
|
if (this.httpsServer) {
|
|
789
952
|
this.httpsServer.close();
|
|
790
953
|
this.httpsServer.removeAllListeners();
|
|
791
954
|
this.httpsServer = undefined;
|
|
792
955
|
this.log.debug('Frontend https server closed successfully');
|
|
793
956
|
}
|
|
957
|
+
// Remove listeners from the express app
|
|
794
958
|
if (this.expressApp) {
|
|
795
959
|
this.expressApp.removeAllListeners();
|
|
796
960
|
this.expressApp = undefined;
|
|
797
961
|
this.log.debug('Frontend app closed successfully');
|
|
798
962
|
}
|
|
963
|
+
// Close the WebSocket server
|
|
799
964
|
if (this.webSocketServer) {
|
|
965
|
+
// Close all active connections
|
|
800
966
|
this.webSocketServer.clients.forEach((client) => {
|
|
801
967
|
if (client.readyState === WebSocket.OPEN) {
|
|
802
968
|
client.close();
|
|
@@ -813,6 +979,7 @@ export class Frontend {
|
|
|
813
979
|
this.webSocketServer = undefined;
|
|
814
980
|
}
|
|
815
981
|
}
|
|
982
|
+
// Function to format bytes to KB, MB, or GB
|
|
816
983
|
formatMemoryUsage = (bytes) => {
|
|
817
984
|
if (bytes >= 1024 ** 3) {
|
|
818
985
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -824,6 +991,7 @@ export class Frontend {
|
|
|
824
991
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
825
992
|
}
|
|
826
993
|
};
|
|
994
|
+
// Function to format system uptime with only the most significant unit
|
|
827
995
|
formatOsUpTime = (seconds) => {
|
|
828
996
|
if (seconds >= 86400) {
|
|
829
997
|
const days = Math.floor(seconds / 86400);
|
|
@@ -839,8 +1007,13 @@ export class Frontend {
|
|
|
839
1007
|
}
|
|
840
1008
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
841
1009
|
};
|
|
1010
|
+
/**
|
|
1011
|
+
* Retrieves the api settings data.
|
|
1012
|
+
* @returns {Promise<object>} A promise that resolve in the api settings object.
|
|
1013
|
+
*/
|
|
842
1014
|
async getApiSettings() {
|
|
843
1015
|
const { lastCpuUsage } = await import('./cli.js');
|
|
1016
|
+
// Update the system information
|
|
844
1017
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
845
1018
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
846
1019
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -849,6 +1022,7 @@ export class Frontend {
|
|
|
849
1022
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
850
1023
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
851
1024
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
1025
|
+
// Update the matterbridge information
|
|
852
1026
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
853
1027
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
854
1028
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -867,7 +1041,28 @@ export class Frontend {
|
|
|
867
1041
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
868
1042
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
869
1043
|
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Retrieves the reachable attribute.
|
|
1046
|
+
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
1047
|
+
* @returns {boolean} The reachable attribute.
|
|
1048
|
+
*/
|
|
1049
|
+
getReachability(device) {
|
|
1050
|
+
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1051
|
+
return false;
|
|
1052
|
+
if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
|
|
1053
|
+
return device.getAttribute(BridgedDeviceBasicInformation.Cluster.id, 'reachable');
|
|
1054
|
+
if (this.matterbridge.bridgeMode === 'childbridge')
|
|
1055
|
+
return true;
|
|
1056
|
+
return false;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Retrieves the cluster text description from a given device.
|
|
1060
|
+
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
1061
|
+
* @returns {string} The attributes description of the cluster servers in the device.
|
|
1062
|
+
*/
|
|
870
1063
|
getClusterTextFromDevice(device) {
|
|
1064
|
+
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1065
|
+
return '';
|
|
871
1066
|
const getAttribute = (device, cluster, attribute) => {
|
|
872
1067
|
let value = undefined;
|
|
873
1068
|
Object.entries(device.state)
|
|
@@ -905,6 +1100,7 @@ export class Frontend {
|
|
|
905
1100
|
};
|
|
906
1101
|
let attributes = '';
|
|
907
1102
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1103
|
+
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
908
1104
|
if (typeof attributeValue === 'undefined')
|
|
909
1105
|
return;
|
|
910
1106
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -982,8 +1178,13 @@ export class Frontend {
|
|
|
982
1178
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
983
1179
|
attributes += `${getUserLabel(device)} `;
|
|
984
1180
|
});
|
|
1181
|
+
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
985
1182
|
return attributes.trimStart().trimEnd();
|
|
986
1183
|
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Retrieves the base registered plugins sanitized for res.json().
|
|
1186
|
+
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
1187
|
+
*/
|
|
987
1188
|
getBaseRegisteredPlugins() {
|
|
988
1189
|
const baseRegisteredPlugins = [];
|
|
989
1190
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -1010,10 +1211,19 @@ export class Frontend {
|
|
|
1010
1211
|
manualPairingCode: plugin.manualPairingCode,
|
|
1011
1212
|
configJson: plugin.configJson,
|
|
1012
1213
|
schemaJson: plugin.schemaJson,
|
|
1214
|
+
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1215
|
+
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1013
1216
|
});
|
|
1014
1217
|
}
|
|
1015
1218
|
return baseRegisteredPlugins;
|
|
1016
1219
|
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1222
|
+
*
|
|
1223
|
+
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1224
|
+
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1225
|
+
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1226
|
+
*/
|
|
1017
1227
|
async wsMessageHandler(client, message) {
|
|
1018
1228
|
let data;
|
|
1019
1229
|
try {
|
|
@@ -1159,8 +1369,10 @@ export class Frontend {
|
|
|
1159
1369
|
else if (data.method === '/api/devices') {
|
|
1160
1370
|
const devices = [];
|
|
1161
1371
|
this.matterbridge.devices.forEach(async (device) => {
|
|
1372
|
+
// Filter by pluginName if provided
|
|
1162
1373
|
if (data.params.pluginName && data.params.pluginName !== device.plugin)
|
|
1163
1374
|
return;
|
|
1375
|
+
// Check if the device has the required properties
|
|
1164
1376
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1165
1377
|
return;
|
|
1166
1378
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -1173,6 +1385,7 @@ export class Frontend {
|
|
|
1173
1385
|
productUrl: device.productUrl,
|
|
1174
1386
|
configUrl: device.configUrl,
|
|
1175
1387
|
uniqueId: device.uniqueId,
|
|
1388
|
+
reachable: this.getReachability(device),
|
|
1176
1389
|
cluster: cluster,
|
|
1177
1390
|
});
|
|
1178
1391
|
});
|
|
@@ -1243,6 +1456,7 @@ export class Frontend {
|
|
|
1243
1456
|
});
|
|
1244
1457
|
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
1245
1458
|
deviceTypes = [];
|
|
1459
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1246
1460
|
const name = childEndpoint.endpoint?.id;
|
|
1247
1461
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
1248
1462
|
clusterServers.forEach((clusterServer) => {
|
|
@@ -1296,7 +1510,8 @@ export class Frontend {
|
|
|
1296
1510
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select' }));
|
|
1297
1511
|
return;
|
|
1298
1512
|
}
|
|
1299
|
-
const selectDeviceValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectDevice.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
|
|
1513
|
+
// const selectDeviceValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectDevice.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
|
|
1514
|
+
const selectDeviceValues = plugin.platform?.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1300
1515
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectDeviceValues }));
|
|
1301
1516
|
return;
|
|
1302
1517
|
}
|
|
@@ -1310,10 +1525,69 @@ export class Frontend {
|
|
|
1310
1525
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' }));
|
|
1311
1526
|
return;
|
|
1312
1527
|
}
|
|
1313
|
-
const selectEntityValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectEntity.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
|
|
1528
|
+
// const selectEntityValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectEntity.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
|
|
1529
|
+
const selectEntityValues = plugin.platform?.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1314
1530
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectEntityValues }));
|
|
1315
1531
|
return;
|
|
1316
1532
|
}
|
|
1533
|
+
else if (data.method === '/api/command') {
|
|
1534
|
+
if (!isValidString(data.params.command, 5)) {
|
|
1535
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter command in /api/command' }));
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1538
|
+
if (data.params.command === 'selectdevice' && isValidString(data.params.plugin, 10) && isValidString(data.params.serial, 1)) {
|
|
1539
|
+
const plugin = this.matterbridge.plugins.get(data.params.plugin);
|
|
1540
|
+
if (!plugin) {
|
|
1541
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/command' }));
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
const config = plugin.configJson;
|
|
1545
|
+
if (config) {
|
|
1546
|
+
if (config.postfix) {
|
|
1547
|
+
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1548
|
+
}
|
|
1549
|
+
// Add the serial to the whiteList if the whiteList exists and the serial is not already in it
|
|
1550
|
+
if (isValidArray(config.whiteList, 1)) {
|
|
1551
|
+
if (!config.whiteList.includes(data.params.serial)) {
|
|
1552
|
+
config.whiteList.push(data.params.serial);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
// Remove the serial from the blackList if the blackList exists and the serial is in it
|
|
1556
|
+
if (isValidArray(config.blackList, 1)) {
|
|
1557
|
+
if (config.blackList.includes(data.params.serial)) {
|
|
1558
|
+
config.blackList = config.blackList.filter((serial) => serial !== data.params.serial);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
if (plugin.platform)
|
|
1562
|
+
plugin.platform.config = config;
|
|
1563
|
+
await this.matterbridge.plugins.saveConfigFromPlugin(plugin);
|
|
1564
|
+
this.wssSendRestartRequired();
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
else if (data.params.command === 'unselectdevice' && isValidString(data.params.plugin, 10) && isValidString(data.params.serial, 1)) {
|
|
1568
|
+
const plugin = this.matterbridge.plugins.get(data.params.plugin);
|
|
1569
|
+
if (!plugin) {
|
|
1570
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/command' }));
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
const config = plugin.configJson;
|
|
1574
|
+
if (config) {
|
|
1575
|
+
if (config.postfix) {
|
|
1576
|
+
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1577
|
+
}
|
|
1578
|
+
// Add the serial to the blackList
|
|
1579
|
+
if (isValidArray(config.blackList)) {
|
|
1580
|
+
if (!config.blackList.includes(data.params.serial)) {
|
|
1581
|
+
config.blackList.push(data.params.serial);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
if (plugin.platform)
|
|
1585
|
+
plugin.platform.config = config;
|
|
1586
|
+
await this.matterbridge.plugins.saveConfigFromPlugin(plugin);
|
|
1587
|
+
this.wssSendRestartRequired();
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1317
1591
|
else {
|
|
1318
1592
|
this.log.error(`Invalid method from websocket client: ${debugStringify(data)}`);
|
|
1319
1593
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid method' }));
|
|
@@ -1325,85 +1599,139 @@ export class Frontend {
|
|
|
1325
1599
|
return;
|
|
1326
1600
|
}
|
|
1327
1601
|
}
|
|
1602
|
+
/**
|
|
1603
|
+
* Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1604
|
+
*
|
|
1605
|
+
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1606
|
+
* @param {string} time - The time string of the message
|
|
1607
|
+
* @param {string} name - The logger name of the message
|
|
1608
|
+
* @param {string} message - The content of the message.
|
|
1609
|
+
*/
|
|
1328
1610
|
wssSendMessage(level, time, name, message) {
|
|
1329
1611
|
if (!level || !time || !name || !message)
|
|
1330
1612
|
return;
|
|
1613
|
+
// Remove ANSI escape codes from the message
|
|
1614
|
+
// eslint-disable-next-line no-control-regex
|
|
1331
1615
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1616
|
+
// Remove leading asterisks from the message
|
|
1332
1617
|
message = message.replace(/^\*+/, '');
|
|
1618
|
+
// Replace all occurrences of \t and \n
|
|
1333
1619
|
message = message.replace(/[\t\n]/g, '');
|
|
1620
|
+
// Remove non-printable characters
|
|
1621
|
+
// eslint-disable-next-line no-control-regex
|
|
1334
1622
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1623
|
+
// Replace all occurrences of \" with "
|
|
1335
1624
|
message = message.replace(/\\"/g, '"');
|
|
1625
|
+
// Define the maximum allowed length for continuous characters without a space
|
|
1336
1626
|
const maxContinuousLength = 100;
|
|
1337
1627
|
const keepStartLength = 20;
|
|
1338
1628
|
const keepEndLength = 20;
|
|
1629
|
+
// Split the message into words
|
|
1339
1630
|
message = message
|
|
1340
1631
|
.split(' ')
|
|
1341
1632
|
.map((word) => {
|
|
1633
|
+
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1342
1634
|
if (word.length > maxContinuousLength) {
|
|
1343
1635
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1344
1636
|
}
|
|
1345
1637
|
return word;
|
|
1346
1638
|
})
|
|
1347
1639
|
.join(' ');
|
|
1640
|
+
// Send the message to all connected clients
|
|
1348
1641
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1349
1642
|
if (client.readyState === WebSocket.OPEN) {
|
|
1350
1643
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1351
1644
|
}
|
|
1352
1645
|
});
|
|
1353
1646
|
}
|
|
1647
|
+
/**
|
|
1648
|
+
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1649
|
+
*
|
|
1650
|
+
*/
|
|
1354
1651
|
wssSendRefreshRequired() {
|
|
1355
1652
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1356
1653
|
this.matterbridge.matterbridgeInformation.refreshRequired = true;
|
|
1654
|
+
// Send the message to all connected clients
|
|
1357
1655
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1358
1656
|
if (client.readyState === WebSocket.OPEN) {
|
|
1359
1657
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: {} }));
|
|
1360
1658
|
}
|
|
1361
1659
|
});
|
|
1362
1660
|
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Sends a need to restart WebSocket message to all connected clients.
|
|
1663
|
+
*
|
|
1664
|
+
*/
|
|
1363
1665
|
wssSendRestartRequired() {
|
|
1364
1666
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1365
1667
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1366
1668
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1669
|
+
// Send the message to all connected clients
|
|
1367
1670
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1368
1671
|
if (client.readyState === WebSocket.OPEN) {
|
|
1369
1672
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1370
1673
|
}
|
|
1371
1674
|
});
|
|
1372
1675
|
}
|
|
1676
|
+
/**
|
|
1677
|
+
* Sends a memory update message to all connected clients.
|
|
1678
|
+
*
|
|
1679
|
+
*/
|
|
1373
1680
|
wssSendCpuUpdate(cpuUsage) {
|
|
1374
1681
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1682
|
+
// Send the message to all connected clients
|
|
1375
1683
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1376
1684
|
if (client.readyState === WebSocket.OPEN) {
|
|
1377
1685
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
1378
1686
|
}
|
|
1379
1687
|
});
|
|
1380
1688
|
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Sends a cpu update message to all connected clients.
|
|
1691
|
+
*
|
|
1692
|
+
*/
|
|
1381
1693
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
1382
1694
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
1695
|
+
// Send the message to all connected clients
|
|
1383
1696
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1384
1697
|
if (client.readyState === WebSocket.OPEN) {
|
|
1385
1698
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
1386
1699
|
}
|
|
1387
1700
|
});
|
|
1388
1701
|
}
|
|
1702
|
+
/**
|
|
1703
|
+
* Sends a memory update message to all connected clients.
|
|
1704
|
+
*
|
|
1705
|
+
*/
|
|
1389
1706
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
1390
1707
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
1708
|
+
// Send the message to all connected clients
|
|
1391
1709
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1392
1710
|
if (client.readyState === WebSocket.OPEN) {
|
|
1393
1711
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
1394
1712
|
}
|
|
1395
1713
|
});
|
|
1396
1714
|
}
|
|
1715
|
+
/**
|
|
1716
|
+
* Sends a cpu update message to all connected clients.
|
|
1717
|
+
*
|
|
1718
|
+
*/
|
|
1397
1719
|
wssSendSnackbarMessage(message, timeout = 5) {
|
|
1398
1720
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
1721
|
+
// Send the message to all connected clients
|
|
1399
1722
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1400
1723
|
if (client.readyState === WebSocket.OPEN) {
|
|
1401
1724
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { message, timeout } }));
|
|
1402
1725
|
}
|
|
1403
1726
|
});
|
|
1404
1727
|
}
|
|
1728
|
+
/**
|
|
1729
|
+
* Sends a message to all connected clients.
|
|
1730
|
+
*
|
|
1731
|
+
*/
|
|
1405
1732
|
wssBroadcastMessage(id, method, params) {
|
|
1406
1733
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
1734
|
+
// Send the message to all connected clients
|
|
1407
1735
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1408
1736
|
if (client.readyState === WebSocket.OPEN) {
|
|
1409
1737
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -1411,3 +1739,4 @@ export class Frontend {
|
|
|
1411
1739
|
});
|
|
1412
1740
|
}
|
|
1413
1741
|
}
|
|
1742
|
+
//# sourceMappingURL=frontend.js.map
|