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