matterbridge 2.2.5 → 2.2.6-dev.2
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 +104 -65
- package/dist/cli.js +2 -37
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -94
- package/dist/frontend.js +37 -340
- package/dist/index.js +1 -28
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.js +0 -2
- package/dist/matter/types.js +0 -2
- package/dist/matterbridge.js +108 -746
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +1 -32
- package/dist/matterbridgeDeviceTypes.js +11 -112
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEndpoint.js +27 -711
- package/dist/matterbridgeEndpointHelpers.js +9 -118
- package/dist/matterbridgePlatform.js +11 -210
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +3 -229
- package/dist/shelly.js +6 -121
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -45
- package/dist/utils/colorUtils.js +2 -205
- package/dist/utils/copyDirectory.js +1 -37
- package/dist/utils/createZip.js +2 -42
- package/dist/utils/deepCopy.js +0 -40
- package/dist/utils/deepEqual.js +1 -65
- package/dist/utils/export.js +0 -1
- package/dist/utils/isvalid.js +0 -86
- package/dist/utils/network.js +5 -77
- package/dist/utils/parameter.js +0 -41
- package/dist/utils/wait.js +5 -48
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.66936fef.js → main.a45801ac.js} +3 -3
- package/frontend/build/static/js/{main.66936fef.js.map → main.a45801ac.js.map} +1 -1
- package/npm-shrinkwrap.json +51 -51
- package/package.json +3 -4
- package/dist/cli.d.ts +0 -29
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cluster/export.d.ts +0 -2
- package/dist/cluster/export.d.ts.map +0 -1
- package/dist/cluster/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -27
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -114
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -221
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/index.d.ts +0 -35
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -422
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1056
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -177
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -835
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2275
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -278
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -178
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -236
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -77
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts +0 -32
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -61
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -32
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -38
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -31
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -53
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/export.d.ts +0 -10
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -87
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/network.d.ts +0 -70
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/parameter.d.ts +0 -44
- package/dist/utils/parameter.d.ts.map +0 -1
- package/dist/utils/parameter.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -43
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
- /package/frontend/build/static/js/{main.66936fef.js.LICENSE.txt → main.a45801ac.js.LICENSE.txt} +0 -0
package/dist/frontend.js
CHANGED
|
@@ -1,28 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Frontend.
|
|
3
|
-
*
|
|
4
|
-
* @file frontend.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @date 2025-01-13
|
|
7
|
-
* @version 1.0.2
|
|
8
|
-
*
|
|
9
|
-
* Copyright 2025, 2026, 2027 Luca Liguori.
|
|
10
|
-
*
|
|
11
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
-
* you may not use this file except in compliance with the License.
|
|
13
|
-
* You may obtain a copy of the License at
|
|
14
|
-
*
|
|
15
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
*
|
|
17
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
-
* See the License for the specific language governing permissions and
|
|
21
|
-
* limitations under the License. *
|
|
22
|
-
*/
|
|
23
|
-
// @matter
|
|
24
1
|
import { EndpointServer, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
|
|
25
|
-
// Node modules
|
|
26
2
|
import { createServer } from 'node:http';
|
|
27
3
|
import os from 'node:os';
|
|
28
4
|
import path from 'node:path';
|
|
@@ -31,75 +7,21 @@ import https from 'https';
|
|
|
31
7
|
import express from 'express';
|
|
32
8
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
33
9
|
import multer from 'multer';
|
|
34
|
-
// AnsiLogger module
|
|
35
10
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from './logger/export.js';
|
|
36
|
-
// Matterbridge
|
|
37
11
|
import { createZip, deepCopy, isValidArray, isValidNumber, isValidObject, isValidString } from './utils/export.js';
|
|
38
12
|
import { plg } from './matterbridgeTypes.js';
|
|
39
13
|
import { hasParameter } from './utils/export.js';
|
|
40
14
|
import { BridgedDeviceBasicInformation } from '@matter/main/clusters';
|
|
41
|
-
/**
|
|
42
|
-
* Websocket message ID for logging.
|
|
43
|
-
* @constant {number}
|
|
44
|
-
*/
|
|
45
15
|
export const WS_ID_LOG = 0;
|
|
46
|
-
/**
|
|
47
|
-
* Websocket message ID indicating a refresh is needed.
|
|
48
|
-
* @constant {number}
|
|
49
|
-
*/
|
|
50
16
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
51
|
-
/**
|
|
52
|
-
* Websocket message ID indicating a restart is needed.
|
|
53
|
-
* @constant {number}
|
|
54
|
-
*/
|
|
55
17
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
56
|
-
/**
|
|
57
|
-
* Websocket message ID indicating a cpu update.
|
|
58
|
-
* @constant {number}
|
|
59
|
-
*/
|
|
60
18
|
export const WS_ID_CPU_UPDATE = 3;
|
|
61
|
-
/**
|
|
62
|
-
* Websocket message ID indicating a memory update.
|
|
63
|
-
* @constant {number}
|
|
64
|
-
*/
|
|
65
19
|
export const WS_ID_MEMORY_UPDATE = 4;
|
|
66
|
-
/**
|
|
67
|
-
* Websocket message ID indicating an uptime update.
|
|
68
|
-
* @constant {number}
|
|
69
|
-
*/
|
|
70
20
|
export const WS_ID_UPTIME_UPDATE = 5;
|
|
71
|
-
/**
|
|
72
|
-
* Websocket message ID indicating a snackbar message.
|
|
73
|
-
* @constant {number}
|
|
74
|
-
*/
|
|
75
21
|
export const WS_ID_SNACKBAR = 6;
|
|
76
|
-
/**
|
|
77
|
-
* Websocket message ID indicating matterbridge has un update available.
|
|
78
|
-
* @constant {number}
|
|
79
|
-
*/
|
|
80
22
|
export const WS_ID_UPDATE_NEEDED = 7;
|
|
81
|
-
/**
|
|
82
|
-
* Websocket message ID indicating a state update.
|
|
83
|
-
* @constant {number}
|
|
84
|
-
*/
|
|
85
23
|
export const WS_ID_STATEUPDATE = 8;
|
|
86
|
-
/**
|
|
87
|
-
* Websocket message ID indicating a shelly system update.
|
|
88
|
-
* check:
|
|
89
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/check
|
|
90
|
-
* perform:
|
|
91
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/perform
|
|
92
|
-
* @constant {number}
|
|
93
|
-
*/
|
|
94
24
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
95
|
-
/**
|
|
96
|
-
* Websocket message ID indicating a shelly main update.
|
|
97
|
-
* check:
|
|
98
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/check
|
|
99
|
-
* perform:
|
|
100
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/perform
|
|
101
|
-
* @constant {number}
|
|
102
|
-
*/
|
|
103
25
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
104
26
|
export class Frontend {
|
|
105
27
|
matterbridge;
|
|
@@ -117,7 +39,7 @@ export class Frontend {
|
|
|
117
39
|
memoryTimeout;
|
|
118
40
|
constructor(matterbridge) {
|
|
119
41
|
this.matterbridge = matterbridge;
|
|
120
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
42
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
121
43
|
}
|
|
122
44
|
set logLevel(logLevel) {
|
|
123
45
|
this.log.logLevel = logLevel;
|
|
@@ -125,25 +47,13 @@ export class Frontend {
|
|
|
125
47
|
async start(port = 8283) {
|
|
126
48
|
this.port = port;
|
|
127
49
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
128
|
-
// Initialize multer with the upload directory
|
|
129
50
|
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
130
51
|
await fs.mkdir(uploadDir, { recursive: true });
|
|
131
52
|
const upload = multer({ dest: uploadDir });
|
|
132
|
-
// Create the express app that serves the frontend
|
|
133
53
|
this.expressApp = express();
|
|
134
|
-
// Log all requests to the server for debugging
|
|
135
|
-
/*
|
|
136
|
-
this.expressApp.use((req, res, next) => {
|
|
137
|
-
this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
|
|
138
|
-
next();
|
|
139
|
-
});
|
|
140
|
-
*/
|
|
141
|
-
// Serve static files from '/static' endpoint
|
|
142
54
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
143
55
|
if (!hasParameter('ssl')) {
|
|
144
|
-
// Create an HTTP server and attach the express app
|
|
145
56
|
this.httpServer = createServer(this.expressApp);
|
|
146
|
-
// Listen on the specified port
|
|
147
57
|
if (hasParameter('ingress')) {
|
|
148
58
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
149
59
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -157,7 +67,6 @@ export class Frontend {
|
|
|
157
67
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
158
68
|
});
|
|
159
69
|
}
|
|
160
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
161
70
|
this.httpServer.on('error', (error) => {
|
|
162
71
|
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
163
72
|
switch (error.code) {
|
|
@@ -173,7 +82,6 @@ export class Frontend {
|
|
|
173
82
|
});
|
|
174
83
|
}
|
|
175
84
|
else {
|
|
176
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
177
85
|
let cert;
|
|
178
86
|
try {
|
|
179
87
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -201,9 +109,7 @@ export class Frontend {
|
|
|
201
109
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
202
110
|
}
|
|
203
111
|
const serverOptions = { cert, key, ca };
|
|
204
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
205
112
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
206
|
-
// Listen on the specified port
|
|
207
113
|
if (hasParameter('ingress')) {
|
|
208
114
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
209
115
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -217,7 +123,6 @@ export class Frontend {
|
|
|
217
123
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
218
124
|
});
|
|
219
125
|
}
|
|
220
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
221
126
|
this.httpsServer.on('error', (error) => {
|
|
222
127
|
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
223
128
|
switch (error.code) {
|
|
@@ -234,18 +139,16 @@ export class Frontend {
|
|
|
234
139
|
}
|
|
235
140
|
if (this.initializeError)
|
|
236
141
|
return;
|
|
237
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
238
142
|
const wssPort = this.port;
|
|
239
143
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
240
144
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
241
145
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
242
146
|
const clientIp = request.socket.remoteAddress;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
147
|
+
let callbackLogLevel = "notice";
|
|
148
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
149
|
+
callbackLogLevel = "info";
|
|
150
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
151
|
+
callbackLogLevel = "debug";
|
|
249
152
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
250
153
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
251
154
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -279,7 +182,6 @@ export class Frontend {
|
|
|
279
182
|
this.webSocketServer.on('error', (ws, error) => {
|
|
280
183
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
281
184
|
});
|
|
282
|
-
// Subscribe to cli events
|
|
283
185
|
const { cliEmitter } = await import('./cli.js');
|
|
284
186
|
cliEmitter.removeAllListeners();
|
|
285
187
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
@@ -291,7 +193,6 @@ export class Frontend {
|
|
|
291
193
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
292
194
|
this.wssSendCpuUpdate(cpuUsage);
|
|
293
195
|
});
|
|
294
|
-
// Endpoint to validate login code
|
|
295
196
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
296
197
|
const { password } = req.body;
|
|
297
198
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -310,27 +211,23 @@ export class Frontend {
|
|
|
310
211
|
this.log.warn('/api/login error wrong password');
|
|
311
212
|
res.json({ valid: false });
|
|
312
213
|
}
|
|
313
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
314
214
|
}
|
|
315
215
|
catch (error) {
|
|
316
216
|
this.log.error('/api/login error getting password');
|
|
317
217
|
res.json({ valid: false });
|
|
318
218
|
}
|
|
319
219
|
});
|
|
320
|
-
// Endpoint to provide health check
|
|
321
220
|
this.expressApp.get('/health', (req, res) => {
|
|
322
221
|
this.log.debug('Express received /health');
|
|
323
222
|
const healthStatus = {
|
|
324
|
-
status: 'ok',
|
|
325
|
-
uptime: process.uptime(),
|
|
326
|
-
timestamp: new Date().toISOString(),
|
|
223
|
+
status: 'ok',
|
|
224
|
+
uptime: process.uptime(),
|
|
225
|
+
timestamp: new Date().toISOString(),
|
|
327
226
|
};
|
|
328
227
|
res.status(200).json(healthStatus);
|
|
329
228
|
});
|
|
330
|
-
// Endpoint to provide memory usage details
|
|
331
229
|
this.expressApp.get('/memory', async (req, res) => {
|
|
332
230
|
this.log.debug('Express received /memory');
|
|
333
|
-
// Memory usage from process
|
|
334
231
|
const memoryUsageRaw = process.memoryUsage();
|
|
335
232
|
const memoryUsage = {
|
|
336
233
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -339,13 +236,10 @@ export class Frontend {
|
|
|
339
236
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
340
237
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
341
238
|
};
|
|
342
|
-
// V8 heap statistics
|
|
343
239
|
const { default: v8 } = await import('node:v8');
|
|
344
240
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
345
241
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
346
|
-
// Format heapStats
|
|
347
242
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
348
|
-
// Format heapSpaces
|
|
349
243
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
350
244
|
...space,
|
|
351
245
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -363,7 +257,6 @@ export class Frontend {
|
|
|
363
257
|
};
|
|
364
258
|
res.status(200).json(memoryReport);
|
|
365
259
|
});
|
|
366
|
-
// Endpoint to start advertising the server node
|
|
367
260
|
this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
|
|
368
261
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
369
262
|
if (pairingCodes) {
|
|
@@ -374,22 +267,18 @@ export class Frontend {
|
|
|
374
267
|
res.status(500).json({ error: 'Failed to generate pairing codes' });
|
|
375
268
|
}
|
|
376
269
|
});
|
|
377
|
-
// Endpoint to provide settings
|
|
378
270
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
379
271
|
this.log.debug('The frontend sent /api/settings');
|
|
380
272
|
res.json(await this.getApiSettings());
|
|
381
273
|
});
|
|
382
|
-
// Endpoint to provide plugins
|
|
383
274
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
384
275
|
this.log.debug('The frontend sent /api/plugins');
|
|
385
276
|
res.json(this.getBaseRegisteredPlugins());
|
|
386
277
|
});
|
|
387
|
-
// Endpoint to provide devices
|
|
388
278
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
389
279
|
this.log.debug('The frontend sent /api/devices');
|
|
390
280
|
const devices = [];
|
|
391
281
|
this.matterbridge.devices.forEach(async (device) => {
|
|
392
|
-
// Check if the device has the required properties
|
|
393
282
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
394
283
|
return;
|
|
395
284
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -408,7 +297,6 @@ export class Frontend {
|
|
|
408
297
|
});
|
|
409
298
|
res.json(devices);
|
|
410
299
|
});
|
|
411
|
-
// Endpoint to provide the cluster servers of the devices
|
|
412
300
|
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
413
301
|
const selectedPluginName = req.params.selectedPluginName;
|
|
414
302
|
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
@@ -481,7 +369,6 @@ export class Frontend {
|
|
|
481
369
|
});
|
|
482
370
|
res.json(data);
|
|
483
371
|
});
|
|
484
|
-
// Endpoint to view the log
|
|
485
372
|
this.expressApp.get('/api/view-log', async (req, res) => {
|
|
486
373
|
this.log.debug('The frontend sent /api/log');
|
|
487
374
|
try {
|
|
@@ -494,12 +381,10 @@ export class Frontend {
|
|
|
494
381
|
res.status(500).send('Error reading log file');
|
|
495
382
|
}
|
|
496
383
|
});
|
|
497
|
-
// Endpoint to download the matterbridge log
|
|
498
384
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
499
385
|
this.log.debug('The frontend sent /api/download-mblog');
|
|
500
386
|
try {
|
|
501
387
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
|
|
502
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
503
388
|
}
|
|
504
389
|
catch (error) {
|
|
505
390
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -511,12 +396,10 @@ export class Frontend {
|
|
|
511
396
|
}
|
|
512
397
|
});
|
|
513
398
|
});
|
|
514
|
-
// Endpoint to download the matter log
|
|
515
399
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
516
400
|
this.log.debug('The frontend sent /api/download-mjlog');
|
|
517
401
|
try {
|
|
518
402
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
519
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
520
403
|
}
|
|
521
404
|
catch (error) {
|
|
522
405
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -528,12 +411,10 @@ export class Frontend {
|
|
|
528
411
|
}
|
|
529
412
|
});
|
|
530
413
|
});
|
|
531
|
-
// Endpoint to download the matter log
|
|
532
414
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
533
415
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
534
416
|
try {
|
|
535
417
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
|
|
536
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
537
418
|
}
|
|
538
419
|
catch (error) {
|
|
539
420
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'Create the Shelly system log before downloading it.');
|
|
@@ -545,7 +426,6 @@ export class Frontend {
|
|
|
545
426
|
}
|
|
546
427
|
});
|
|
547
428
|
});
|
|
548
|
-
// Endpoint to download the matter storage file
|
|
549
429
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
550
430
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
551
431
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
@@ -556,7 +436,6 @@ export class Frontend {
|
|
|
556
436
|
}
|
|
557
437
|
});
|
|
558
438
|
});
|
|
559
|
-
// Endpoint to download the matterbridge storage directory
|
|
560
439
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
561
440
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
562
441
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -567,7 +446,6 @@ export class Frontend {
|
|
|
567
446
|
}
|
|
568
447
|
});
|
|
569
448
|
});
|
|
570
|
-
// Endpoint to download the matterbridge plugin directory
|
|
571
449
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
572
450
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
573
451
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -578,11 +456,9 @@ export class Frontend {
|
|
|
578
456
|
}
|
|
579
457
|
});
|
|
580
458
|
});
|
|
581
|
-
// Endpoint to download the matterbridge plugin config files
|
|
582
459
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
583
460
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
584
461
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
585
|
-
// 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')));
|
|
586
462
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
587
463
|
if (error) {
|
|
588
464
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
@@ -590,7 +466,6 @@ export class Frontend {
|
|
|
590
466
|
}
|
|
591
467
|
});
|
|
592
468
|
});
|
|
593
|
-
// Endpoint to download the matterbridge plugin config files
|
|
594
469
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
595
470
|
this.log.debug('The frontend sent /api/download-backup');
|
|
596
471
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -600,7 +475,6 @@ export class Frontend {
|
|
|
600
475
|
}
|
|
601
476
|
});
|
|
602
477
|
});
|
|
603
|
-
// Endpoint to receive commands
|
|
604
478
|
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
605
479
|
const command = req.params.command;
|
|
606
480
|
let param = req.params.param;
|
|
@@ -610,15 +484,13 @@ export class Frontend {
|
|
|
610
484
|
return;
|
|
611
485
|
}
|
|
612
486
|
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
613
|
-
// Handle the command setpassword from Settings
|
|
614
487
|
if (command === 'setpassword') {
|
|
615
|
-
const password = param.slice(1, -1);
|
|
488
|
+
const password = param.slice(1, -1);
|
|
616
489
|
this.log.debug('setpassword', param, password);
|
|
617
490
|
await this.matterbridge.nodeContext?.set('password', password);
|
|
618
491
|
res.json({ message: 'Command received' });
|
|
619
492
|
return;
|
|
620
493
|
}
|
|
621
|
-
// Handle the command setbridgemode from Settings
|
|
622
494
|
if (command === 'setbridgemode') {
|
|
623
495
|
this.log.debug(`setbridgemode: ${param}`);
|
|
624
496
|
this.wssSendRestartRequired();
|
|
@@ -626,7 +498,6 @@ export class Frontend {
|
|
|
626
498
|
res.json({ message: 'Command received' });
|
|
627
499
|
return;
|
|
628
500
|
}
|
|
629
|
-
// Handle the command backup from Settings
|
|
630
501
|
if (command === 'backup') {
|
|
631
502
|
this.log.notice(`Prepairing the backup...`);
|
|
632
503
|
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
|
|
@@ -635,33 +506,31 @@ export class Frontend {
|
|
|
635
506
|
res.json({ message: 'Command received' });
|
|
636
507
|
return;
|
|
637
508
|
}
|
|
638
|
-
// Handle the command setmbloglevel from Settings
|
|
639
509
|
if (command === 'setmbloglevel') {
|
|
640
510
|
this.log.debug('Matterbridge log level:', param);
|
|
641
511
|
if (param === 'Debug') {
|
|
642
|
-
this.log.logLevel = "debug"
|
|
512
|
+
this.log.logLevel = "debug";
|
|
643
513
|
}
|
|
644
514
|
else if (param === 'Info') {
|
|
645
|
-
this.log.logLevel = "info"
|
|
515
|
+
this.log.logLevel = "info";
|
|
646
516
|
}
|
|
647
517
|
else if (param === 'Notice') {
|
|
648
|
-
this.log.logLevel = "notice"
|
|
518
|
+
this.log.logLevel = "notice";
|
|
649
519
|
}
|
|
650
520
|
else if (param === 'Warn') {
|
|
651
|
-
this.log.logLevel = "warn"
|
|
521
|
+
this.log.logLevel = "warn";
|
|
652
522
|
}
|
|
653
523
|
else if (param === 'Error') {
|
|
654
|
-
this.log.logLevel = "error"
|
|
524
|
+
this.log.logLevel = "error";
|
|
655
525
|
}
|
|
656
526
|
else if (param === 'Fatal') {
|
|
657
|
-
this.log.logLevel = "fatal"
|
|
527
|
+
this.log.logLevel = "fatal";
|
|
658
528
|
}
|
|
659
529
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
660
530
|
await this.matterbridge.setLogLevel(this.log.logLevel);
|
|
661
531
|
res.json({ message: 'Command received' });
|
|
662
532
|
return;
|
|
663
533
|
}
|
|
664
|
-
// Handle the command setmbloglevel from Settings
|
|
665
534
|
if (command === 'setmjloglevel') {
|
|
666
535
|
this.log.debug('Matter.js log level:', param);
|
|
667
536
|
if (param === 'Debug') {
|
|
@@ -686,74 +555,68 @@ export class Frontend {
|
|
|
686
555
|
res.json({ message: 'Command received' });
|
|
687
556
|
return;
|
|
688
557
|
}
|
|
689
|
-
// Handle the command setmdnsinterface from Settings
|
|
690
558
|
if (command === 'setmdnsinterface') {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
559
|
+
if (param === 'json' && isValidString(req.body.value)) {
|
|
560
|
+
this.matterbridge.matterbridgeInformation.mattermdnsinterface = req.body.value;
|
|
561
|
+
this.log.debug(`Matter.js mdns interface: ${req.body.value === '' ? 'all interfaces' : req.body.value}`);
|
|
562
|
+
await this.matterbridge.nodeContext?.set('mattermdnsinterface', req.body.value);
|
|
563
|
+
}
|
|
695
564
|
res.json({ message: 'Command received' });
|
|
696
565
|
return;
|
|
697
566
|
}
|
|
698
|
-
// Handle the command setipv4address from Settings
|
|
699
567
|
if (command === 'setipv4address') {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
568
|
+
if (param === 'json' && isValidString(req.body.value)) {
|
|
569
|
+
this.log.debug(`Matter.js ipv4 address: ${req.body.value === '' ? 'all ipv4 addresses' : req.body.value}`);
|
|
570
|
+
this.matterbridge.matterbridgeInformation.matteripv4address = req.body.value;
|
|
571
|
+
await this.matterbridge.nodeContext?.set('matteripv4address', req.body.value);
|
|
572
|
+
}
|
|
704
573
|
res.json({ message: 'Command received' });
|
|
705
574
|
return;
|
|
706
575
|
}
|
|
707
|
-
// Handle the command setipv6address from Settings
|
|
708
576
|
if (command === 'setipv6address') {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
577
|
+
if (param === 'json' && isValidString(req.body.value)) {
|
|
578
|
+
this.log.debug(`Matter.js ipv6 address: ${req.body.value === '' ? 'all ipv6 addresses' : req.body.value}`);
|
|
579
|
+
this.matterbridge.matterbridgeInformation.matteripv6address = req.body.value;
|
|
580
|
+
await this.matterbridge.nodeContext?.set('matteripv6address', req.body.value);
|
|
581
|
+
}
|
|
713
582
|
res.json({ message: 'Command received' });
|
|
714
583
|
return;
|
|
715
584
|
}
|
|
716
|
-
// Handle the command setmatterport from Settings
|
|
717
585
|
if (command === 'setmatterport') {
|
|
718
|
-
const port = Math.min(Math.max(parseInt(
|
|
586
|
+
const port = Math.min(Math.max(parseInt(req.body.value), 5540), 5560);
|
|
719
587
|
this.matterbridge.matterbridgeInformation.matterPort = port;
|
|
720
588
|
this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
|
|
721
589
|
await this.matterbridge.nodeContext?.set('matterport', port);
|
|
722
590
|
res.json({ message: 'Command received' });
|
|
723
591
|
return;
|
|
724
592
|
}
|
|
725
|
-
// Handle the command setmatterdiscriminator from Settings
|
|
726
593
|
if (command === 'setmatterdiscriminator') {
|
|
727
|
-
const discriminator = Math.min(Math.max(parseInt(
|
|
594
|
+
const discriminator = Math.min(Math.max(parseInt(req.body.value), 1000), 4095);
|
|
728
595
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
|
|
729
596
|
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
|
|
730
597
|
await this.matterbridge.nodeContext?.set('matterdiscriminator', discriminator);
|
|
731
598
|
res.json({ message: 'Command received' });
|
|
732
599
|
return;
|
|
733
600
|
}
|
|
734
|
-
// Handle the command setmatterpasscode from Settings
|
|
735
601
|
if (command === 'setmatterpasscode') {
|
|
736
|
-
const passcode = Math.min(Math.max(parseInt(
|
|
602
|
+
const passcode = Math.min(Math.max(parseInt(req.body.value), 10000000), 90000000);
|
|
737
603
|
this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
|
|
738
604
|
this.log.debug(`Set matter commissioning passcode to ${CYAN}${passcode}${db}`);
|
|
739
605
|
await this.matterbridge.nodeContext?.set('matterpasscode', passcode);
|
|
740
606
|
res.json({ message: 'Command received' });
|
|
741
607
|
return;
|
|
742
608
|
}
|
|
743
|
-
// Handle the command setmbloglevel from Settings
|
|
744
609
|
if (command === 'setmblogfile') {
|
|
745
610
|
this.log.debug('Matterbridge file log:', param);
|
|
746
611
|
this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
|
|
747
612
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
|
|
748
|
-
// Create the file logger for matterbridge
|
|
749
613
|
if (param === 'true')
|
|
750
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug"
|
|
614
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
|
|
751
615
|
else
|
|
752
616
|
AnsiLogger.setGlobalLogfile(undefined);
|
|
753
617
|
res.json({ message: 'Command received' });
|
|
754
618
|
return;
|
|
755
619
|
}
|
|
756
|
-
// Handle the command setmbloglevel from Settings
|
|
757
620
|
if (command === 'setmjlogfile') {
|
|
758
621
|
this.log.debug('Matter file log:', param);
|
|
759
622
|
this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
|
|
@@ -780,48 +643,40 @@ export class Frontend {
|
|
|
780
643
|
res.json({ message: 'Command received' });
|
|
781
644
|
return;
|
|
782
645
|
}
|
|
783
|
-
// Handle the command unregister from Settings
|
|
784
646
|
if (command === 'unregister') {
|
|
785
647
|
await this.matterbridge.unregisterAndShutdownProcess();
|
|
786
648
|
res.json({ message: 'Command received' });
|
|
787
649
|
return;
|
|
788
650
|
}
|
|
789
|
-
// Handle the command reset from Settings
|
|
790
651
|
if (command === 'reset') {
|
|
791
652
|
await this.matterbridge.shutdownProcessAndReset();
|
|
792
653
|
res.json({ message: 'Command received' });
|
|
793
654
|
return;
|
|
794
655
|
}
|
|
795
|
-
// Handle the command factoryreset from Settings
|
|
796
656
|
if (command === 'factoryreset') {
|
|
797
657
|
await this.matterbridge.shutdownProcessAndFactoryReset();
|
|
798
658
|
res.json({ message: 'Command received' });
|
|
799
659
|
return;
|
|
800
660
|
}
|
|
801
|
-
// Handle the command shutdown from Header
|
|
802
661
|
if (command === 'shutdown') {
|
|
803
662
|
await this.matterbridge.shutdownProcess();
|
|
804
663
|
res.json({ message: 'Command received' });
|
|
805
664
|
return;
|
|
806
665
|
}
|
|
807
|
-
// Handle the command restart from Header
|
|
808
666
|
if (command === 'restart') {
|
|
809
667
|
await this.matterbridge.restartProcess();
|
|
810
668
|
res.json({ message: 'Command received' });
|
|
811
669
|
return;
|
|
812
670
|
}
|
|
813
|
-
// Handle the command update from Header
|
|
814
671
|
if (command === 'update') {
|
|
815
672
|
await this.matterbridge.updateProcess();
|
|
816
673
|
this.wssSendRestartRequired();
|
|
817
674
|
res.json({ message: 'Command received' });
|
|
818
675
|
return;
|
|
819
676
|
}
|
|
820
|
-
// Handle the command saveconfig from Home
|
|
821
677
|
if (command === 'saveconfig') {
|
|
822
678
|
param = param.replace(/\*/g, '\\');
|
|
823
679
|
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
824
|
-
// console.log('Req.body:', JSON.stringify(req.body, null, 2));
|
|
825
680
|
if (!this.matterbridge.plugins.has(param)) {
|
|
826
681
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
827
682
|
}
|
|
@@ -836,7 +691,6 @@ export class Frontend {
|
|
|
836
691
|
res.json({ message: 'Command received' });
|
|
837
692
|
return;
|
|
838
693
|
}
|
|
839
|
-
// Handle the command installplugin from Home
|
|
840
694
|
if (command === 'installplugin') {
|
|
841
695
|
param = param.replace(/\*/g, '\\');
|
|
842
696
|
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
@@ -845,7 +699,6 @@ export class Frontend {
|
|
|
845
699
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
846
700
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
847
701
|
this.wssSendSnackbarMessage(`Installed package ${param}`, 10, 'success');
|
|
848
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
849
702
|
}
|
|
850
703
|
catch (error) {
|
|
851
704
|
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
@@ -853,22 +706,17 @@ export class Frontend {
|
|
|
853
706
|
}
|
|
854
707
|
this.wssSendRestartRequired();
|
|
855
708
|
param = param.split('@')[0];
|
|
856
|
-
// Also add the plugin to matterbridge so no return!
|
|
857
709
|
if (param === 'matterbridge') {
|
|
858
|
-
// 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
|
|
859
710
|
res.json({ message: 'Command received' });
|
|
860
711
|
return;
|
|
861
712
|
}
|
|
862
713
|
}
|
|
863
|
-
// Handle the command addplugin from Home
|
|
864
714
|
if (command === 'addplugin' || command === 'installplugin') {
|
|
865
715
|
param = param.replace(/\*/g, '\\');
|
|
866
716
|
const plugin = await this.matterbridge.plugins.add(param);
|
|
867
717
|
if (plugin) {
|
|
868
718
|
this.wssSendSnackbarMessage(`Added plugin ${param}`);
|
|
869
719
|
if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
870
|
-
// 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
|
|
871
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
872
720
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
873
721
|
}
|
|
874
722
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
@@ -878,14 +726,13 @@ export class Frontend {
|
|
|
878
726
|
res.json({ message: 'Command received' });
|
|
879
727
|
return;
|
|
880
728
|
}
|
|
881
|
-
// Handle the command removeplugin from Home
|
|
882
729
|
if (command === 'removeplugin') {
|
|
883
730
|
if (!this.matterbridge.plugins.has(param)) {
|
|
884
731
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
885
732
|
}
|
|
886
733
|
else {
|
|
887
734
|
const plugin = this.matterbridge.plugins.get(param);
|
|
888
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
735
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
889
736
|
await this.matterbridge.plugins.remove(param);
|
|
890
737
|
this.wssSendSnackbarMessage(`Removed plugin ${param}`);
|
|
891
738
|
this.wssSendRefreshRequired('plugins');
|
|
@@ -893,7 +740,6 @@ export class Frontend {
|
|
|
893
740
|
res.json({ message: 'Command received' });
|
|
894
741
|
return;
|
|
895
742
|
}
|
|
896
|
-
// Handle the command enableplugin from Home
|
|
897
743
|
if (command === 'enableplugin') {
|
|
898
744
|
if (!this.matterbridge.plugins.has(param)) {
|
|
899
745
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -912,7 +758,6 @@ export class Frontend {
|
|
|
912
758
|
await this.matterbridge.plugins.enable(param);
|
|
913
759
|
this.wssSendSnackbarMessage(`Enabled plugin ${param}`);
|
|
914
760
|
if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
|
|
915
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
916
761
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
917
762
|
}
|
|
918
763
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
|
|
@@ -923,7 +768,6 @@ export class Frontend {
|
|
|
923
768
|
res.json({ message: 'Command received' });
|
|
924
769
|
return;
|
|
925
770
|
}
|
|
926
|
-
// Handle the command disableplugin from Home
|
|
927
771
|
if (command === 'disableplugin') {
|
|
928
772
|
if (!this.matterbridge.plugins.has(param)) {
|
|
929
773
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -931,7 +775,7 @@ export class Frontend {
|
|
|
931
775
|
else {
|
|
932
776
|
const plugin = this.matterbridge.plugins.get(param);
|
|
933
777
|
if (plugin && plugin.enabled) {
|
|
934
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
778
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
935
779
|
await this.matterbridge.plugins.disable(param);
|
|
936
780
|
this.wssSendSnackbarMessage(`Disabled plugin ${param}`);
|
|
937
781
|
this.wssSendRefreshRequired('plugins');
|
|
@@ -950,13 +794,10 @@ export class Frontend {
|
|
|
950
794
|
return;
|
|
951
795
|
}
|
|
952
796
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`);
|
|
953
|
-
// Define the path where the plugin file will be saved
|
|
954
797
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
955
798
|
try {
|
|
956
|
-
// Move the uploaded file to the specified path
|
|
957
799
|
await fs.rename(file.path, filePath);
|
|
958
800
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
959
|
-
// Install the plugin package
|
|
960
801
|
if (filename.endsWith('.tgz')) {
|
|
961
802
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
962
803
|
this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
|
|
@@ -973,7 +814,6 @@ export class Frontend {
|
|
|
973
814
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
974
815
|
}
|
|
975
816
|
});
|
|
976
|
-
// Fallback for routing (must be the last route)
|
|
977
817
|
this.expressApp.get('*', (req, res) => {
|
|
978
818
|
this.log.debug('The frontend sent:', req.url);
|
|
979
819
|
this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -982,31 +822,24 @@ export class Frontend {
|
|
|
982
822
|
this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
983
823
|
}
|
|
984
824
|
async stop() {
|
|
985
|
-
// Remove all listeners from the cliEmitter
|
|
986
|
-
// cliEmitter.removeAllListeners();
|
|
987
|
-
// Close the http server
|
|
988
825
|
if (this.httpServer) {
|
|
989
826
|
this.httpServer.close();
|
|
990
827
|
this.httpServer.removeAllListeners();
|
|
991
828
|
this.httpServer = undefined;
|
|
992
829
|
this.log.debug('Frontend http server closed successfully');
|
|
993
830
|
}
|
|
994
|
-
// Close the https server
|
|
995
831
|
if (this.httpsServer) {
|
|
996
832
|
this.httpsServer.close();
|
|
997
833
|
this.httpsServer.removeAllListeners();
|
|
998
834
|
this.httpsServer = undefined;
|
|
999
835
|
this.log.debug('Frontend https server closed successfully');
|
|
1000
836
|
}
|
|
1001
|
-
// Remove listeners from the express app
|
|
1002
837
|
if (this.expressApp) {
|
|
1003
838
|
this.expressApp.removeAllListeners();
|
|
1004
839
|
this.expressApp = undefined;
|
|
1005
840
|
this.log.debug('Frontend app closed successfully');
|
|
1006
841
|
}
|
|
1007
|
-
// Close the WebSocket server
|
|
1008
842
|
if (this.webSocketServer) {
|
|
1009
|
-
// Close all active connections
|
|
1010
843
|
this.webSocketServer.clients.forEach((client) => {
|
|
1011
844
|
if (client.readyState === WebSocket.OPEN) {
|
|
1012
845
|
client.close();
|
|
@@ -1023,7 +856,6 @@ export class Frontend {
|
|
|
1023
856
|
this.webSocketServer = undefined;
|
|
1024
857
|
}
|
|
1025
858
|
}
|
|
1026
|
-
// Function to format bytes to KB, MB, or GB
|
|
1027
859
|
formatMemoryUsage = (bytes) => {
|
|
1028
860
|
if (bytes >= 1024 ** 3) {
|
|
1029
861
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -1035,7 +867,6 @@ export class Frontend {
|
|
|
1035
867
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
1036
868
|
}
|
|
1037
869
|
};
|
|
1038
|
-
// Function to format system uptime with only the most significant unit
|
|
1039
870
|
formatOsUpTime = (seconds) => {
|
|
1040
871
|
if (seconds >= 86400) {
|
|
1041
872
|
const days = Math.floor(seconds / 86400);
|
|
@@ -1051,13 +882,8 @@ export class Frontend {
|
|
|
1051
882
|
}
|
|
1052
883
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
1053
884
|
};
|
|
1054
|
-
/**
|
|
1055
|
-
* Retrieves the api settings data.
|
|
1056
|
-
* @returns {Promise<object>} A promise that resolve in the api settings object.
|
|
1057
|
-
*/
|
|
1058
885
|
async getApiSettings() {
|
|
1059
886
|
const { lastCpuUsage } = await import('./cli.js');
|
|
1060
|
-
// Update the system information
|
|
1061
887
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
1062
888
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
1063
889
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -1066,7 +892,6 @@ export class Frontend {
|
|
|
1066
892
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
1067
893
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
1068
894
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
1069
|
-
// Update the matterbridge information
|
|
1070
895
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
1071
896
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
1072
897
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -1085,11 +910,6 @@ export class Frontend {
|
|
|
1085
910
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
1086
911
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
1087
912
|
}
|
|
1088
|
-
/**
|
|
1089
|
-
* Retrieves the reachable attribute.
|
|
1090
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
1091
|
-
* @returns {boolean} The reachable attribute.
|
|
1092
|
-
*/
|
|
1093
913
|
getReachability(device) {
|
|
1094
914
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1095
915
|
return false;
|
|
@@ -1099,11 +919,6 @@ export class Frontend {
|
|
|
1099
919
|
return true;
|
|
1100
920
|
return false;
|
|
1101
921
|
}
|
|
1102
|
-
/**
|
|
1103
|
-
* Retrieves the cluster text description from a given device.
|
|
1104
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
1105
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
1106
|
-
*/
|
|
1107
922
|
getClusterTextFromDevice(device) {
|
|
1108
923
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1109
924
|
return '';
|
|
@@ -1144,7 +959,6 @@ export class Frontend {
|
|
|
1144
959
|
};
|
|
1145
960
|
let attributes = '';
|
|
1146
961
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1147
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
1148
962
|
if (typeof attributeValue === 'undefined')
|
|
1149
963
|
return;
|
|
1150
964
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1222,13 +1036,8 @@ export class Frontend {
|
|
|
1222
1036
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1223
1037
|
attributes += `${getUserLabel(device)} `;
|
|
1224
1038
|
});
|
|
1225
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1226
1039
|
return attributes.trimStart().trimEnd();
|
|
1227
1040
|
}
|
|
1228
|
-
/**
|
|
1229
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
1230
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
1231
|
-
*/
|
|
1232
1041
|
getBaseRegisteredPlugins() {
|
|
1233
1042
|
const baseRegisteredPlugins = [];
|
|
1234
1043
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -1261,13 +1070,6 @@ export class Frontend {
|
|
|
1261
1070
|
}
|
|
1262
1071
|
return baseRegisteredPlugins;
|
|
1263
1072
|
}
|
|
1264
|
-
/**
|
|
1265
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1266
|
-
*
|
|
1267
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1268
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1269
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1270
|
-
*/
|
|
1271
1073
|
async wsMessageHandler(client, message) {
|
|
1272
1074
|
let data;
|
|
1273
1075
|
try {
|
|
@@ -1413,10 +1215,8 @@ export class Frontend {
|
|
|
1413
1215
|
else if (data.method === '/api/devices') {
|
|
1414
1216
|
const devices = [];
|
|
1415
1217
|
this.matterbridge.devices.forEach(async (device) => {
|
|
1416
|
-
// Filter by pluginName if provided
|
|
1417
1218
|
if (data.params.pluginName && data.params.pluginName !== device.plugin)
|
|
1418
1219
|
return;
|
|
1419
|
-
// Check if the device has the required properties
|
|
1420
1220
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1421
1221
|
return;
|
|
1422
1222
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -1500,7 +1300,6 @@ export class Frontend {
|
|
|
1500
1300
|
});
|
|
1501
1301
|
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
1502
1302
|
deviceTypes = [];
|
|
1503
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1504
1303
|
const name = childEndpoint.endpoint?.id;
|
|
1505
1304
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
1506
1305
|
clusterServers.forEach((clusterServer) => {
|
|
@@ -1554,7 +1353,6 @@ export class Frontend {
|
|
|
1554
1353
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select' }));
|
|
1555
1354
|
return;
|
|
1556
1355
|
}
|
|
1557
|
-
// const selectDeviceValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectDevice.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
|
|
1558
1356
|
const selectDeviceValues = plugin.platform?.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1559
1357
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectDeviceValues }));
|
|
1560
1358
|
return;
|
|
@@ -1569,7 +1367,6 @@ export class Frontend {
|
|
|
1569
1367
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' }));
|
|
1570
1368
|
return;
|
|
1571
1369
|
}
|
|
1572
|
-
// const selectEntityValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectEntity.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
|
|
1573
1370
|
const selectEntityValues = plugin.platform?.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1574
1371
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectEntityValues }));
|
|
1575
1372
|
return;
|
|
@@ -1601,7 +1398,6 @@ export class Frontend {
|
|
|
1601
1398
|
return;
|
|
1602
1399
|
}
|
|
1603
1400
|
const config = plugin.configJson;
|
|
1604
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1605
1401
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1606
1402
|
this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1607
1403
|
if (select === 'serial')
|
|
@@ -1609,11 +1405,9 @@ export class Frontend {
|
|
|
1609
1405
|
if (select === 'name')
|
|
1610
1406
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1611
1407
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1612
|
-
// Remove postfix from the serial if it exists
|
|
1613
1408
|
if (config.postfix) {
|
|
1614
1409
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1615
1410
|
}
|
|
1616
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1617
1411
|
if (isValidArray(config.whiteList, 1)) {
|
|
1618
1412
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1619
1413
|
config.whiteList.push(data.params.serial);
|
|
@@ -1622,7 +1416,6 @@ export class Frontend {
|
|
|
1622
1416
|
config.whiteList.push(data.params.name);
|
|
1623
1417
|
}
|
|
1624
1418
|
}
|
|
1625
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1626
1419
|
if (isValidArray(config.blackList, 1)) {
|
|
1627
1420
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1628
1421
|
config.blackList = config.blackList.filter((serial) => serial !== data.params.serial);
|
|
@@ -1644,7 +1437,6 @@ export class Frontend {
|
|
|
1644
1437
|
return;
|
|
1645
1438
|
}
|
|
1646
1439
|
const config = plugin.configJson;
|
|
1647
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1648
1440
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1649
1441
|
this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1650
1442
|
if (select === 'serial')
|
|
@@ -1655,7 +1447,6 @@ export class Frontend {
|
|
|
1655
1447
|
if (config.postfix) {
|
|
1656
1448
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1657
1449
|
}
|
|
1658
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1659
1450
|
if (isValidArray(config.whiteList, 1)) {
|
|
1660
1451
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1661
1452
|
config.whiteList = config.whiteList.filter((serial) => serial !== data.params.serial);
|
|
@@ -1664,7 +1455,6 @@ export class Frontend {
|
|
|
1664
1455
|
config.whiteList = config.whiteList.filter((name) => name !== data.params.name);
|
|
1665
1456
|
}
|
|
1666
1457
|
}
|
|
1667
|
-
// Add the serial to the blackList
|
|
1668
1458
|
if (isValidArray(config.blackList)) {
|
|
1669
1459
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1670
1460
|
config.blackList.push(data.params.serial);
|
|
@@ -1691,194 +1481,102 @@ export class Frontend {
|
|
|
1691
1481
|
return;
|
|
1692
1482
|
}
|
|
1693
1483
|
}
|
|
1694
|
-
/**
|
|
1695
|
-
* Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1696
|
-
*
|
|
1697
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1698
|
-
* @param {string} time - The time string of the message
|
|
1699
|
-
* @param {string} name - The logger name of the message
|
|
1700
|
-
* @param {string} message - The content of the message.
|
|
1701
|
-
*/
|
|
1702
1484
|
wssSendMessage(level, time, name, message) {
|
|
1703
1485
|
if (!level || !time || !name || !message)
|
|
1704
1486
|
return;
|
|
1705
|
-
// Remove ANSI escape codes from the message
|
|
1706
|
-
// eslint-disable-next-line no-control-regex
|
|
1707
1487
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1708
|
-
// Remove leading asterisks from the message
|
|
1709
1488
|
message = message.replace(/^\*+/, '');
|
|
1710
|
-
// Replace all occurrences of \t and \n
|
|
1711
1489
|
message = message.replace(/[\t\n]/g, '');
|
|
1712
|
-
// Remove non-printable characters
|
|
1713
|
-
// eslint-disable-next-line no-control-regex
|
|
1714
1490
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1715
|
-
// Replace all occurrences of \" with "
|
|
1716
1491
|
message = message.replace(/\\"/g, '"');
|
|
1717
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1718
1492
|
const maxContinuousLength = 100;
|
|
1719
1493
|
const keepStartLength = 20;
|
|
1720
1494
|
const keepEndLength = 20;
|
|
1721
|
-
// Split the message into words
|
|
1722
1495
|
message = message
|
|
1723
1496
|
.split(' ')
|
|
1724
1497
|
.map((word) => {
|
|
1725
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1726
1498
|
if (word.length > maxContinuousLength) {
|
|
1727
1499
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1728
1500
|
}
|
|
1729
1501
|
return word;
|
|
1730
1502
|
})
|
|
1731
1503
|
.join(' ');
|
|
1732
|
-
// Send the message to all connected clients
|
|
1733
1504
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1734
1505
|
if (client.readyState === WebSocket.OPEN) {
|
|
1735
1506
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1736
1507
|
}
|
|
1737
1508
|
});
|
|
1738
1509
|
}
|
|
1739
|
-
/**
|
|
1740
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1741
|
-
*
|
|
1742
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
1743
|
-
* possible values:
|
|
1744
|
-
* - 'matterbridgeLatestVersion'
|
|
1745
|
-
* - 'matterbridgeAdvertise'
|
|
1746
|
-
* - 'online'
|
|
1747
|
-
* - 'offline'
|
|
1748
|
-
* - 'reachability'
|
|
1749
|
-
* - 'settings'
|
|
1750
|
-
* - 'plugins'
|
|
1751
|
-
* - 'devices'
|
|
1752
|
-
* - 'fabrics'
|
|
1753
|
-
* - 'sessions'
|
|
1754
|
-
*/
|
|
1755
1510
|
wssSendRefreshRequired(changed = null) {
|
|
1756
1511
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1757
|
-
// Send the message to all connected clients
|
|
1758
1512
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1759
1513
|
if (client.readyState === WebSocket.OPEN) {
|
|
1760
1514
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1761
1515
|
}
|
|
1762
1516
|
});
|
|
1763
1517
|
}
|
|
1764
|
-
/**
|
|
1765
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1766
|
-
*
|
|
1767
|
-
*/
|
|
1768
1518
|
wssSendRestartRequired(snackbar = true) {
|
|
1769
1519
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1770
1520
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1771
1521
|
if (snackbar === true)
|
|
1772
1522
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1773
|
-
// Send the message to all connected clients
|
|
1774
1523
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1775
1524
|
if (client.readyState === WebSocket.OPEN) {
|
|
1776
1525
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1777
1526
|
}
|
|
1778
1527
|
});
|
|
1779
1528
|
}
|
|
1780
|
-
/**
|
|
1781
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
1782
|
-
*
|
|
1783
|
-
*/
|
|
1784
1529
|
wssSendUpdateRequired() {
|
|
1785
1530
|
this.log.debug('Sending an update required message to all connected clients');
|
|
1786
1531
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
1787
|
-
// Send the message to all connected clients
|
|
1788
1532
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1789
1533
|
if (client.readyState === WebSocket.OPEN) {
|
|
1790
1534
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
1791
1535
|
}
|
|
1792
1536
|
});
|
|
1793
1537
|
}
|
|
1794
|
-
/**
|
|
1795
|
-
* Sends a memory update message to all connected clients.
|
|
1796
|
-
*
|
|
1797
|
-
*/
|
|
1798
1538
|
wssSendCpuUpdate(cpuUsage) {
|
|
1799
1539
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1800
|
-
// Send the message to all connected clients
|
|
1801
1540
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1802
1541
|
if (client.readyState === WebSocket.OPEN) {
|
|
1803
1542
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
1804
1543
|
}
|
|
1805
1544
|
});
|
|
1806
1545
|
}
|
|
1807
|
-
/**
|
|
1808
|
-
* Sends a cpu update message to all connected clients.
|
|
1809
|
-
*
|
|
1810
|
-
*/
|
|
1811
1546
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
1812
1547
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
1813
|
-
// Send the message to all connected clients
|
|
1814
1548
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1815
1549
|
if (client.readyState === WebSocket.OPEN) {
|
|
1816
1550
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
1817
1551
|
}
|
|
1818
1552
|
});
|
|
1819
1553
|
}
|
|
1820
|
-
/**
|
|
1821
|
-
* Sends a memory update message to all connected clients.
|
|
1822
|
-
*
|
|
1823
|
-
*/
|
|
1824
1554
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
1825
1555
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
1826
|
-
// Send the message to all connected clients
|
|
1827
1556
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1828
1557
|
if (client.readyState === WebSocket.OPEN) {
|
|
1829
1558
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
1830
1559
|
}
|
|
1831
1560
|
});
|
|
1832
1561
|
}
|
|
1833
|
-
/**
|
|
1834
|
-
* Sends a cpu update message to all connected clients.
|
|
1835
|
-
* @param {string} message - The message to send.
|
|
1836
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
1837
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
1838
|
-
*
|
|
1839
|
-
*/
|
|
1840
1562
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
1841
1563
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
1842
|
-
// Send the message to all connected clients
|
|
1843
1564
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1844
1565
|
if (client.readyState === WebSocket.OPEN) {
|
|
1845
1566
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { message, timeout, severity } }));
|
|
1846
1567
|
}
|
|
1847
1568
|
});
|
|
1848
1569
|
}
|
|
1849
|
-
/**
|
|
1850
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
1851
|
-
*
|
|
1852
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
1853
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
1854
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
1855
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
1856
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
1857
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
1858
|
-
*
|
|
1859
|
-
* @remarks
|
|
1860
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
1861
|
-
* with the updated attribute information.
|
|
1862
|
-
*/
|
|
1863
1570
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
1864
1571
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
1865
|
-
// Send the message to all connected clients
|
|
1866
1572
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1867
1573
|
if (client.readyState === WebSocket.OPEN) {
|
|
1868
1574
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
1869
1575
|
}
|
|
1870
1576
|
});
|
|
1871
1577
|
}
|
|
1872
|
-
/**
|
|
1873
|
-
* Sends a message to all connected clients.
|
|
1874
|
-
* @param {number} id - The message id.
|
|
1875
|
-
* @param {string} method - The message method.
|
|
1876
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
1877
|
-
*
|
|
1878
|
-
*/
|
|
1879
1578
|
wssBroadcastMessage(id, method, params) {
|
|
1880
1579
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
1881
|
-
// Send the message to all connected clients
|
|
1882
1580
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1883
1581
|
if (client.readyState === WebSocket.OPEN) {
|
|
1884
1582
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -1886,4 +1584,3 @@ export class Frontend {
|
|
|
1886
1584
|
});
|
|
1887
1585
|
}
|
|
1888
1586
|
}
|
|
1889
|
-
//# sourceMappingURL=frontend.js.map
|