matterbridge 2.2.4-dev.2 → 2.2.4
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 +5 -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 +330 -26
- 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 +251 -0
- package/dist/matterbridgePlatform.d.ts.map +1 -0
- package/dist/matterbridgePlatform.js +185 -7
- package/dist/matterbridgePlatform.js.map +1 -0
- package/dist/matterbridgeTypes.d.ts +178 -0
- package/dist/matterbridgeTypes.d.ts.map +1 -0
- package/dist/matterbridgeTypes.js +24 -0
- package/dist/matterbridgeTypes.js.map +1 -0
- package/dist/pluginManager.d.ts +236 -0
- package/dist/pluginManager.d.ts.map +1 -0
- package/dist/pluginManager.js +229 -3
- package/dist/pluginManager.js.map +1 -0
- package/dist/shelly.d.ts +77 -0
- package/dist/shelly.d.ts.map +1 -0
- package/dist/shelly.js +121 -6
- package/dist/shelly.js.map +1 -0
- package/dist/storage/export.d.ts +2 -0
- package/dist/storage/export.d.ts.map +1 -0
- package/dist/storage/export.js +1 -0
- package/dist/storage/export.js.map +1 -0
- package/dist/update.d.ts +32 -0
- package/dist/update.d.ts.map +1 -0
- package/dist/update.js +45 -0
- package/dist/update.js.map +1 -0
- package/dist/utils/colorUtils.d.ts +61 -0
- package/dist/utils/colorUtils.d.ts.map +1 -0
- package/dist/utils/colorUtils.js +205 -2
- package/dist/utils/colorUtils.js.map +1 -0
- package/dist/utils/copyDirectory.d.ts +32 -0
- package/dist/utils/copyDirectory.d.ts.map +1 -0
- package/dist/utils/copyDirectory.js +37 -1
- package/dist/utils/copyDirectory.js.map +1 -0
- package/dist/utils/createZip.d.ts +38 -0
- package/dist/utils/createZip.d.ts.map +1 -0
- package/dist/utils/createZip.js +42 -2
- package/dist/utils/createZip.js.map +1 -0
- package/dist/utils/deepCopy.d.ts +31 -0
- package/dist/utils/deepCopy.d.ts.map +1 -0
- package/dist/utils/deepCopy.js +40 -0
- package/dist/utils/deepCopy.js.map +1 -0
- package/dist/utils/deepEqual.d.ts +53 -0
- package/dist/utils/deepEqual.d.ts.map +1 -0
- package/dist/utils/deepEqual.js +65 -1
- package/dist/utils/deepEqual.js.map +1 -0
- package/dist/utils/export.d.ts +10 -0
- package/dist/utils/export.d.ts.map +1 -0
- package/dist/utils/export.js +1 -0
- package/dist/utils/export.js.map +1 -0
- package/dist/utils/isvalid.d.ts +87 -0
- package/dist/utils/isvalid.d.ts.map +1 -0
- package/dist/utils/isvalid.js +86 -0
- package/dist/utils/isvalid.js.map +1 -0
- package/dist/utils/network.d.ts +70 -0
- package/dist/utils/network.d.ts.map +1 -0
- package/dist/utils/network.js +77 -5
- package/dist/utils/network.js.map +1 -0
- package/dist/utils/parameter.d.ts +44 -0
- package/dist/utils/parameter.d.ts.map +1 -0
- package/dist/utils/parameter.js +41 -0
- package/dist/utils/parameter.js.map +1 -0
- package/dist/utils/wait.d.ts +43 -0
- package/dist/utils/wait.d.ts.map +1 -0
- package/dist/utils/wait.js +48 -5
- package/dist/utils/wait.js.map +1 -0
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.819c0908.js → main.4a12038d.js} +4 -4
- package/frontend/build/static/js/{main.819c0908.js.map → main.4a12038d.js.map} +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -1
- /package/frontend/build/static/js/{main.819c0908.js.LICENSE.txt → main.4a12038d.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 } 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,32 +836,39 @@ 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}...`);
|
|
694
|
-
this.wssSendSnackbarMessage(`Installing package ${param}
|
|
843
|
+
this.wssSendSnackbarMessage(`Installing package ${param}. Please wait...`);
|
|
695
844
|
try {
|
|
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
|
-
this.wssSendSnackbarMessage(`Installed package ${param}
|
|
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}`);
|
|
702
|
-
this.wssSendSnackbarMessage(`Package ${param} not installed
|
|
852
|
+
this.wssSendSnackbarMessage(`Package ${param} not installed`, 10, 'error');
|
|
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');
|
|
@@ -790,11 +949,14 @@ export class Frontend {
|
|
|
790
949
|
res.status(400).send('Invalid request: file and filename are required');
|
|
791
950
|
return;
|
|
792
951
|
}
|
|
793
|
-
this.wssSendSnackbarMessage(`Installing package ${filename}...`);
|
|
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.`);
|
|
@@ -807,9 +969,11 @@ export class Frontend {
|
|
|
807
969
|
}
|
|
808
970
|
catch (err) {
|
|
809
971
|
this.log.error(`Error uploading or installing plugin package file ${plg}${filename}${er}:`, err);
|
|
972
|
+
this.wssSendSnackbarMessage(`Error uploading or installing plugin package ${filename}`, 10, 'error');
|
|
810
973
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
811
974
|
}
|
|
812
975
|
});
|
|
976
|
+
// Fallback for routing (must be the last route)
|
|
813
977
|
this.expressApp.get('*', (req, res) => {
|
|
814
978
|
this.log.debug('The frontend sent:', req.url);
|
|
815
979
|
this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -818,24 +982,31 @@ export class Frontend {
|
|
|
818
982
|
this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
819
983
|
}
|
|
820
984
|
async stop() {
|
|
985
|
+
// Remove all listeners from the cliEmitter
|
|
986
|
+
// cliEmitter.removeAllListeners();
|
|
987
|
+
// Close the http server
|
|
821
988
|
if (this.httpServer) {
|
|
822
989
|
this.httpServer.close();
|
|
823
990
|
this.httpServer.removeAllListeners();
|
|
824
991
|
this.httpServer = undefined;
|
|
825
992
|
this.log.debug('Frontend http server closed successfully');
|
|
826
993
|
}
|
|
994
|
+
// Close the https server
|
|
827
995
|
if (this.httpsServer) {
|
|
828
996
|
this.httpsServer.close();
|
|
829
997
|
this.httpsServer.removeAllListeners();
|
|
830
998
|
this.httpsServer = undefined;
|
|
831
999
|
this.log.debug('Frontend https server closed successfully');
|
|
832
1000
|
}
|
|
1001
|
+
// Remove listeners from the express app
|
|
833
1002
|
if (this.expressApp) {
|
|
834
1003
|
this.expressApp.removeAllListeners();
|
|
835
1004
|
this.expressApp = undefined;
|
|
836
1005
|
this.log.debug('Frontend app closed successfully');
|
|
837
1006
|
}
|
|
1007
|
+
// Close the WebSocket server
|
|
838
1008
|
if (this.webSocketServer) {
|
|
1009
|
+
// Close all active connections
|
|
839
1010
|
this.webSocketServer.clients.forEach((client) => {
|
|
840
1011
|
if (client.readyState === WebSocket.OPEN) {
|
|
841
1012
|
client.close();
|
|
@@ -852,6 +1023,7 @@ export class Frontend {
|
|
|
852
1023
|
this.webSocketServer = undefined;
|
|
853
1024
|
}
|
|
854
1025
|
}
|
|
1026
|
+
// Function to format bytes to KB, MB, or GB
|
|
855
1027
|
formatMemoryUsage = (bytes) => {
|
|
856
1028
|
if (bytes >= 1024 ** 3) {
|
|
857
1029
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -863,6 +1035,7 @@ export class Frontend {
|
|
|
863
1035
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
864
1036
|
}
|
|
865
1037
|
};
|
|
1038
|
+
// Function to format system uptime with only the most significant unit
|
|
866
1039
|
formatOsUpTime = (seconds) => {
|
|
867
1040
|
if (seconds >= 86400) {
|
|
868
1041
|
const days = Math.floor(seconds / 86400);
|
|
@@ -878,8 +1051,13 @@ export class Frontend {
|
|
|
878
1051
|
}
|
|
879
1052
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
880
1053
|
};
|
|
1054
|
+
/**
|
|
1055
|
+
* Retrieves the api settings data.
|
|
1056
|
+
* @returns {Promise<object>} A promise that resolve in the api settings object.
|
|
1057
|
+
*/
|
|
881
1058
|
async getApiSettings() {
|
|
882
1059
|
const { lastCpuUsage } = await import('./cli.js');
|
|
1060
|
+
// Update the system information
|
|
883
1061
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
884
1062
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
885
1063
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -888,6 +1066,7 @@ export class Frontend {
|
|
|
888
1066
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
889
1067
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
890
1068
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
1069
|
+
// Update the matterbridge information
|
|
891
1070
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
892
1071
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
893
1072
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -906,6 +1085,11 @@ export class Frontend {
|
|
|
906
1085
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
907
1086
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
908
1087
|
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Retrieves the reachable attribute.
|
|
1090
|
+
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
1091
|
+
* @returns {boolean} The reachable attribute.
|
|
1092
|
+
*/
|
|
909
1093
|
getReachability(device) {
|
|
910
1094
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
911
1095
|
return false;
|
|
@@ -915,6 +1099,11 @@ export class Frontend {
|
|
|
915
1099
|
return true;
|
|
916
1100
|
return false;
|
|
917
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
|
+
*/
|
|
918
1107
|
getClusterTextFromDevice(device) {
|
|
919
1108
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
920
1109
|
return '';
|
|
@@ -955,6 +1144,7 @@ export class Frontend {
|
|
|
955
1144
|
};
|
|
956
1145
|
let attributes = '';
|
|
957
1146
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1147
|
+
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
958
1148
|
if (typeof attributeValue === 'undefined')
|
|
959
1149
|
return;
|
|
960
1150
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1032,8 +1222,13 @@ export class Frontend {
|
|
|
1032
1222
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1033
1223
|
attributes += `${getUserLabel(device)} `;
|
|
1034
1224
|
});
|
|
1225
|
+
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1035
1226
|
return attributes.trimStart().trimEnd();
|
|
1036
1227
|
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Retrieves the base registered plugins sanitized for res.json().
|
|
1230
|
+
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
1231
|
+
*/
|
|
1037
1232
|
getBaseRegisteredPlugins() {
|
|
1038
1233
|
const baseRegisteredPlugins = [];
|
|
1039
1234
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -1066,6 +1261,13 @@ export class Frontend {
|
|
|
1066
1261
|
}
|
|
1067
1262
|
return baseRegisteredPlugins;
|
|
1068
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
|
+
*/
|
|
1069
1271
|
async wsMessageHandler(client, message) {
|
|
1070
1272
|
let data;
|
|
1071
1273
|
try {
|
|
@@ -1211,8 +1413,10 @@ export class Frontend {
|
|
|
1211
1413
|
else if (data.method === '/api/devices') {
|
|
1212
1414
|
const devices = [];
|
|
1213
1415
|
this.matterbridge.devices.forEach(async (device) => {
|
|
1416
|
+
// Filter by pluginName if provided
|
|
1214
1417
|
if (data.params.pluginName && data.params.pluginName !== device.plugin)
|
|
1215
1418
|
return;
|
|
1419
|
+
// Check if the device has the required properties
|
|
1216
1420
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1217
1421
|
return;
|
|
1218
1422
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -1296,6 +1500,7 @@ export class Frontend {
|
|
|
1296
1500
|
});
|
|
1297
1501
|
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
1298
1502
|
deviceTypes = [];
|
|
1503
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1299
1504
|
const name = childEndpoint.endpoint?.id;
|
|
1300
1505
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
1301
1506
|
clusterServers.forEach((clusterServer) => {
|
|
@@ -1349,6 +1554,7 @@ export class Frontend {
|
|
|
1349
1554
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select' }));
|
|
1350
1555
|
return;
|
|
1351
1556
|
}
|
|
1557
|
+
// const selectDeviceValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectDevice.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
|
|
1352
1558
|
const selectDeviceValues = plugin.platform?.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1353
1559
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectDeviceValues }));
|
|
1354
1560
|
return;
|
|
@@ -1363,6 +1569,7 @@ export class Frontend {
|
|
|
1363
1569
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' }));
|
|
1364
1570
|
return;
|
|
1365
1571
|
}
|
|
1572
|
+
// const selectEntityValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectEntity.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
|
|
1366
1573
|
const selectEntityValues = plugin.platform?.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1367
1574
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectEntityValues }));
|
|
1368
1575
|
return;
|
|
@@ -1383,11 +1590,13 @@ export class Frontend {
|
|
|
1383
1590
|
if (config.postfix) {
|
|
1384
1591
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1385
1592
|
}
|
|
1593
|
+
// Add the serial to the whiteList if the whiteList exists and the serial is not already in it
|
|
1386
1594
|
if (isValidArray(config.whiteList, 1)) {
|
|
1387
1595
|
if (!config.whiteList.includes(data.params.serial)) {
|
|
1388
1596
|
config.whiteList.push(data.params.serial);
|
|
1389
1597
|
}
|
|
1390
1598
|
}
|
|
1599
|
+
// Remove the serial from the blackList if the blackList exists and the serial is in it
|
|
1391
1600
|
if (isValidArray(config.blackList, 1)) {
|
|
1392
1601
|
if (config.blackList.includes(data.params.serial)) {
|
|
1393
1602
|
config.blackList = config.blackList.filter((serial) => serial !== data.params.serial);
|
|
@@ -1410,11 +1619,13 @@ export class Frontend {
|
|
|
1410
1619
|
if (config.postfix) {
|
|
1411
1620
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1412
1621
|
}
|
|
1622
|
+
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1413
1623
|
if (isValidArray(config.whiteList, 1)) {
|
|
1414
1624
|
if (config.whiteList.includes(data.params.serial)) {
|
|
1415
1625
|
config.whiteList = config.whiteList.filter((serial) => serial !== data.params.serial);
|
|
1416
1626
|
}
|
|
1417
1627
|
}
|
|
1628
|
+
// Add the serial to the blackList
|
|
1418
1629
|
if (isValidArray(config.blackList)) {
|
|
1419
1630
|
if (!config.blackList.includes(data.params.serial)) {
|
|
1420
1631
|
config.blackList.push(data.params.serial);
|
|
@@ -1438,102 +1649,194 @@ export class Frontend {
|
|
|
1438
1649
|
return;
|
|
1439
1650
|
}
|
|
1440
1651
|
}
|
|
1652
|
+
/**
|
|
1653
|
+
* Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1654
|
+
*
|
|
1655
|
+
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1656
|
+
* @param {string} time - The time string of the message
|
|
1657
|
+
* @param {string} name - The logger name of the message
|
|
1658
|
+
* @param {string} message - The content of the message.
|
|
1659
|
+
*/
|
|
1441
1660
|
wssSendMessage(level, time, name, message) {
|
|
1442
1661
|
if (!level || !time || !name || !message)
|
|
1443
1662
|
return;
|
|
1663
|
+
// Remove ANSI escape codes from the message
|
|
1664
|
+
// eslint-disable-next-line no-control-regex
|
|
1444
1665
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1666
|
+
// Remove leading asterisks from the message
|
|
1445
1667
|
message = message.replace(/^\*+/, '');
|
|
1668
|
+
// Replace all occurrences of \t and \n
|
|
1446
1669
|
message = message.replace(/[\t\n]/g, '');
|
|
1670
|
+
// Remove non-printable characters
|
|
1671
|
+
// eslint-disable-next-line no-control-regex
|
|
1447
1672
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1673
|
+
// Replace all occurrences of \" with "
|
|
1448
1674
|
message = message.replace(/\\"/g, '"');
|
|
1675
|
+
// Define the maximum allowed length for continuous characters without a space
|
|
1449
1676
|
const maxContinuousLength = 100;
|
|
1450
1677
|
const keepStartLength = 20;
|
|
1451
1678
|
const keepEndLength = 20;
|
|
1679
|
+
// Split the message into words
|
|
1452
1680
|
message = message
|
|
1453
1681
|
.split(' ')
|
|
1454
1682
|
.map((word) => {
|
|
1683
|
+
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1455
1684
|
if (word.length > maxContinuousLength) {
|
|
1456
1685
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1457
1686
|
}
|
|
1458
1687
|
return word;
|
|
1459
1688
|
})
|
|
1460
1689
|
.join(' ');
|
|
1690
|
+
// Send the message to all connected clients
|
|
1461
1691
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1462
1692
|
if (client.readyState === WebSocket.OPEN) {
|
|
1463
1693
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1464
1694
|
}
|
|
1465
1695
|
});
|
|
1466
1696
|
}
|
|
1697
|
+
/**
|
|
1698
|
+
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1699
|
+
*
|
|
1700
|
+
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
1701
|
+
* possible values:
|
|
1702
|
+
* - 'matterbridgeLatestVersion'
|
|
1703
|
+
* - 'matterbridgeAdvertise'
|
|
1704
|
+
* - 'online'
|
|
1705
|
+
* - 'offline'
|
|
1706
|
+
* - 'reachability'
|
|
1707
|
+
* - 'settings'
|
|
1708
|
+
* - 'plugins'
|
|
1709
|
+
* - 'devices'
|
|
1710
|
+
* - 'fabrics'
|
|
1711
|
+
* - 'sessions'
|
|
1712
|
+
*/
|
|
1467
1713
|
wssSendRefreshRequired(changed = null) {
|
|
1468
1714
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1715
|
+
// Send the message to all connected clients
|
|
1469
1716
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1470
1717
|
if (client.readyState === WebSocket.OPEN) {
|
|
1471
1718
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1472
1719
|
}
|
|
1473
1720
|
});
|
|
1474
1721
|
}
|
|
1722
|
+
/**
|
|
1723
|
+
* Sends a need to restart WebSocket message to all connected clients.
|
|
1724
|
+
*
|
|
1725
|
+
*/
|
|
1475
1726
|
wssSendRestartRequired(snackbar = true) {
|
|
1476
1727
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1477
1728
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1478
1729
|
if (snackbar === true)
|
|
1479
1730
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1731
|
+
// Send the message to all connected clients
|
|
1480
1732
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1481
1733
|
if (client.readyState === WebSocket.OPEN) {
|
|
1482
1734
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1483
1735
|
}
|
|
1484
1736
|
});
|
|
1485
1737
|
}
|
|
1738
|
+
/**
|
|
1739
|
+
* Sends a need to update WebSocket message to all connected clients.
|
|
1740
|
+
*
|
|
1741
|
+
*/
|
|
1486
1742
|
wssSendUpdateRequired() {
|
|
1487
1743
|
this.log.debug('Sending an update required message to all connected clients');
|
|
1488
1744
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
1745
|
+
// Send the message to all connected clients
|
|
1489
1746
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1490
1747
|
if (client.readyState === WebSocket.OPEN) {
|
|
1491
1748
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
1492
1749
|
}
|
|
1493
1750
|
});
|
|
1494
1751
|
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Sends a memory update message to all connected clients.
|
|
1754
|
+
*
|
|
1755
|
+
*/
|
|
1495
1756
|
wssSendCpuUpdate(cpuUsage) {
|
|
1496
1757
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1758
|
+
// Send the message to all connected clients
|
|
1497
1759
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1498
1760
|
if (client.readyState === WebSocket.OPEN) {
|
|
1499
1761
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
1500
1762
|
}
|
|
1501
1763
|
});
|
|
1502
1764
|
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Sends a cpu update message to all connected clients.
|
|
1767
|
+
*
|
|
1768
|
+
*/
|
|
1503
1769
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
1504
1770
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
1771
|
+
// Send the message to all connected clients
|
|
1505
1772
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1506
1773
|
if (client.readyState === WebSocket.OPEN) {
|
|
1507
1774
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
1508
1775
|
}
|
|
1509
1776
|
});
|
|
1510
1777
|
}
|
|
1778
|
+
/**
|
|
1779
|
+
* Sends a memory update message to all connected clients.
|
|
1780
|
+
*
|
|
1781
|
+
*/
|
|
1511
1782
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
1512
1783
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
1784
|
+
// Send the message to all connected clients
|
|
1513
1785
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1514
1786
|
if (client.readyState === WebSocket.OPEN) {
|
|
1515
1787
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
1516
1788
|
}
|
|
1517
1789
|
});
|
|
1518
1790
|
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Sends a cpu update message to all connected clients.
|
|
1793
|
+
* @param {string} message - The message to send.
|
|
1794
|
+
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
1795
|
+
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
1796
|
+
*
|
|
1797
|
+
*/
|
|
1519
1798
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
1520
1799
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
1800
|
+
// Send the message to all connected clients
|
|
1521
1801
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1522
1802
|
if (client.readyState === WebSocket.OPEN) {
|
|
1523
1803
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { message, timeout, severity } }));
|
|
1524
1804
|
}
|
|
1525
1805
|
});
|
|
1526
1806
|
}
|
|
1807
|
+
/**
|
|
1808
|
+
* Sends an attribute update message to all connected WebSocket clients.
|
|
1809
|
+
*
|
|
1810
|
+
* @param {string | undefined} plugin - The name of the plugin.
|
|
1811
|
+
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
1812
|
+
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
1813
|
+
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
1814
|
+
* @param {string} attribute - The name of the attribute that changed.
|
|
1815
|
+
* @param {number | string | boolean} value - The new value of the attribute.
|
|
1816
|
+
*
|
|
1817
|
+
* @remarks
|
|
1818
|
+
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
1819
|
+
* with the updated attribute information.
|
|
1820
|
+
*/
|
|
1527
1821
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
1528
1822
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
1823
|
+
// Send the message to all connected clients
|
|
1529
1824
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1530
1825
|
if (client.readyState === WebSocket.OPEN) {
|
|
1531
1826
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
1532
1827
|
}
|
|
1533
1828
|
});
|
|
1534
1829
|
}
|
|
1830
|
+
/**
|
|
1831
|
+
* Sends a message to all connected clients.
|
|
1832
|
+
* @param {number} id - The message id.
|
|
1833
|
+
* @param {string} method - The message method.
|
|
1834
|
+
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
1835
|
+
*
|
|
1836
|
+
*/
|
|
1535
1837
|
wssBroadcastMessage(id, method, params) {
|
|
1536
1838
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
1839
|
+
// Send the message to all connected clients
|
|
1537
1840
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1538
1841
|
if (client.readyState === WebSocket.OPEN) {
|
|
1539
1842
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -1541,3 +1844,4 @@ export class Frontend {
|
|
|
1541
1844
|
});
|
|
1542
1845
|
}
|
|
1543
1846
|
}
|
|
1847
|
+
//# sourceMappingURL=frontend.js.map
|