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