matterbridge 2.1.1 → 2.1.2-dev.3
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 +17 -0
- package/dist/cli.js +0 -26
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +3 -29
- package/dist/frontend.js +51 -245
- package/dist/index.js +0 -28
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.js +1 -2
- package/dist/matter/types.js +0 -2
- package/dist/matterbridge.js +38 -752
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +1 -32
- package/dist/matterbridgeDeviceTypes.js +11 -112
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEndpoint.js +6 -690
- package/dist/matterbridgeEndpointHelpers.js +0 -96
- package/dist/matterbridgePlatform.js +9 -129
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +5 -243
- package/dist/storage/export.js +0 -1
- package/dist/utils/colorUtils.js +2 -205
- package/dist/utils/export.js +0 -1
- package/dist/utils/utils.js +7 -251
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- package/dist/cli.d.ts +0 -25
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cluster/export.d.ts +0 -2
- package/dist/cluster/export.d.ts.map +0 -1
- package/dist/cluster/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -27
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -46
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -109
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/index.d.ts +0 -35
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -4
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -409
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1056
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -177
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -834
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2262
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -164
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -165
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -238
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -61
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/export.d.ts +0 -3
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/utils.d.ts +0 -221
- package/dist/utils/utils.d.ts.map +0 -1
- package/dist/utils/utils.js.map +0 -1
package/dist/frontend.js
CHANGED
|
@@ -1,28 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Frontend.
|
|
3
|
-
*
|
|
4
|
-
* @file frontend.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @date 2025-01-13
|
|
7
|
-
* @version 1.0.1
|
|
8
|
-
*
|
|
9
|
-
* Copyright 2025, 2026, 2027 Luca Liguori.
|
|
10
|
-
*
|
|
11
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
-
* you may not use this file except in compliance with the License.
|
|
13
|
-
* You may obtain a copy of the License at
|
|
14
|
-
*
|
|
15
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
*
|
|
17
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
-
* See the License for the specific language governing permissions and
|
|
21
|
-
* limitations under the License. *
|
|
22
|
-
*/
|
|
23
|
-
// @matter
|
|
24
1
|
import { EndpointServer, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat } from '@matter/main';
|
|
25
|
-
// Node modules
|
|
26
2
|
import { createServer } from 'http';
|
|
27
3
|
import https from 'https';
|
|
28
4
|
import express from 'express';
|
|
@@ -30,32 +6,13 @@ import WebSocket, { WebSocketServer } from 'ws';
|
|
|
30
6
|
import os from 'os';
|
|
31
7
|
import path from 'path';
|
|
32
8
|
import { promises as fs } from 'fs';
|
|
33
|
-
// AnsiLogger module
|
|
34
9
|
import { AnsiLogger, CYAN, db, debugStringify, er, nf, rs, stringify, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';
|
|
35
|
-
// Matterbridge
|
|
36
10
|
import { createZip, getIntParameter, hasParameter, isValidNumber, isValidObject, isValidString } from './utils/utils.js';
|
|
37
11
|
import { plg } from './matterbridgeTypes.js';
|
|
38
12
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
39
|
-
/**
|
|
40
|
-
* Websocket message ID for logging.
|
|
41
|
-
* @constant {number}
|
|
42
|
-
*/
|
|
43
13
|
export const WS_ID_LOG = 0;
|
|
44
|
-
/**
|
|
45
|
-
* Websocket message ID indicating a refresh is needed.
|
|
46
|
-
* @constant {number}
|
|
47
|
-
*/
|
|
48
14
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
49
|
-
/**
|
|
50
|
-
* Websocket message ID indicating a restart is needed.
|
|
51
|
-
* @constant {number}
|
|
52
|
-
*/
|
|
53
15
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
54
|
-
/**
|
|
55
|
-
* Initializes the frontend of Matterbridge.
|
|
56
|
-
*
|
|
57
|
-
* @param port The port number to run the frontend server on. Default is 8283.
|
|
58
|
-
*/
|
|
59
16
|
export class Frontend {
|
|
60
17
|
matterbridge;
|
|
61
18
|
log;
|
|
@@ -65,12 +22,13 @@ export class Frontend {
|
|
|
65
22
|
httpServer;
|
|
66
23
|
httpsServer;
|
|
67
24
|
webSocketServer;
|
|
25
|
+
prevCpus = os.cpus();
|
|
68
26
|
memoryData = [];
|
|
69
27
|
memoryInterval;
|
|
70
28
|
memoryTimeout;
|
|
71
29
|
constructor(matterbridge) {
|
|
72
30
|
this.matterbridge = matterbridge;
|
|
73
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
31
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
74
32
|
}
|
|
75
33
|
set logLevel(logLevel) {
|
|
76
34
|
this.log.logLevel = logLevel;
|
|
@@ -78,21 +36,10 @@ export class Frontend {
|
|
|
78
36
|
async start(port = 8283) {
|
|
79
37
|
this.port = port;
|
|
80
38
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
81
|
-
// Create the express app that serves the frontend
|
|
82
39
|
this.expressApp = express();
|
|
83
|
-
// Log all requests to the server for debugging
|
|
84
|
-
/*
|
|
85
|
-
this.expressApp.use((req, res, next) => {
|
|
86
|
-
this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
|
|
87
|
-
next();
|
|
88
|
-
});
|
|
89
|
-
*/
|
|
90
|
-
// Serve static files from '/static' endpoint
|
|
91
40
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
92
41
|
if (!hasParameter('ssl')) {
|
|
93
|
-
// Create an HTTP server and attach the express app
|
|
94
42
|
this.httpServer = createServer(this.expressApp);
|
|
95
|
-
// Listen on the specified port
|
|
96
43
|
if (hasParameter('ingress')) {
|
|
97
44
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
98
45
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -106,7 +53,6 @@ export class Frontend {
|
|
|
106
53
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
107
54
|
});
|
|
108
55
|
}
|
|
109
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
110
56
|
this.httpServer.on('error', (error) => {
|
|
111
57
|
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
112
58
|
switch (error.code) {
|
|
@@ -122,7 +68,6 @@ export class Frontend {
|
|
|
122
68
|
});
|
|
123
69
|
}
|
|
124
70
|
else {
|
|
125
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
126
71
|
let cert;
|
|
127
72
|
try {
|
|
128
73
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -150,9 +95,7 @@ export class Frontend {
|
|
|
150
95
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
151
96
|
}
|
|
152
97
|
const serverOptions = { cert, key, ca };
|
|
153
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
154
98
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
155
|
-
// Listen on the specified port
|
|
156
99
|
if (hasParameter('ingress')) {
|
|
157
100
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
158
101
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -166,7 +109,6 @@ export class Frontend {
|
|
|
166
109
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
167
110
|
});
|
|
168
111
|
}
|
|
169
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
170
112
|
this.httpsServer.on('error', (error) => {
|
|
171
113
|
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
172
114
|
switch (error.code) {
|
|
@@ -183,13 +125,12 @@ export class Frontend {
|
|
|
183
125
|
}
|
|
184
126
|
if (this.initializeError)
|
|
185
127
|
return;
|
|
186
|
-
// Createe a WebSocket server and attach it to the http or https server
|
|
187
128
|
const wssPort = this.port;
|
|
188
129
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
189
130
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
190
131
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
191
132
|
const clientIp = request.socket.remoteAddress;
|
|
192
|
-
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug"
|
|
133
|
+
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug");
|
|
193
134
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
194
135
|
ws.on('message', (message) => {
|
|
195
136
|
this.wsMessageHandler(ws, message);
|
|
@@ -221,11 +162,9 @@ export class Frontend {
|
|
|
221
162
|
this.webSocketServer.on('error', (ws, error) => {
|
|
222
163
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
223
164
|
});
|
|
224
|
-
// Start the memory dump interval
|
|
225
165
|
if (hasParameter('memorydump')) {
|
|
226
|
-
this.
|
|
166
|
+
this.startCpuMemoryDump();
|
|
227
167
|
}
|
|
228
|
-
// Endpoint to validate login code
|
|
229
168
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
230
169
|
const { password } = req.body;
|
|
231
170
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -244,27 +183,23 @@ export class Frontend {
|
|
|
244
183
|
this.log.warn('/api/login error wrong password');
|
|
245
184
|
res.json({ valid: false });
|
|
246
185
|
}
|
|
247
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
248
186
|
}
|
|
249
187
|
catch (error) {
|
|
250
188
|
this.log.error('/api/login error getting password');
|
|
251
189
|
res.json({ valid: false });
|
|
252
190
|
}
|
|
253
191
|
});
|
|
254
|
-
// Endpoint to provide health check
|
|
255
192
|
this.expressApp.get('/health', (req, res) => {
|
|
256
193
|
this.log.debug('Express received /health');
|
|
257
194
|
const healthStatus = {
|
|
258
|
-
status: 'ok',
|
|
259
|
-
uptime: process.uptime(),
|
|
260
|
-
timestamp: new Date().toISOString(),
|
|
195
|
+
status: 'ok',
|
|
196
|
+
uptime: process.uptime(),
|
|
197
|
+
timestamp: new Date().toISOString(),
|
|
261
198
|
};
|
|
262
199
|
res.status(200).json(healthStatus);
|
|
263
200
|
});
|
|
264
|
-
// Endpoint to provide memory usage details
|
|
265
201
|
this.expressApp.get('/memory', async (req, res) => {
|
|
266
202
|
this.log.debug('Express received /memory');
|
|
267
|
-
// Memory usage from process
|
|
268
203
|
const memoryUsageRaw = process.memoryUsage();
|
|
269
204
|
const memoryUsage = {
|
|
270
205
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -273,13 +208,10 @@ export class Frontend {
|
|
|
273
208
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
274
209
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
275
210
|
};
|
|
276
|
-
// V8 heap statistics
|
|
277
211
|
const { default: v8 } = await import('node:v8');
|
|
278
212
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
279
213
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
280
|
-
// Format heapStats
|
|
281
214
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
282
|
-
// Format heapSpaces
|
|
283
215
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
284
216
|
...space,
|
|
285
217
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -289,23 +221,6 @@ export class Frontend {
|
|
|
289
221
|
}));
|
|
290
222
|
const { default: module } = await import('module');
|
|
291
223
|
const loadedModules = module._cache ? Object.keys(module._cache).sort() : [];
|
|
292
|
-
/*
|
|
293
|
-
if (req.query.heapdump === 'true') {
|
|
294
|
-
const { default: heapdump } = await import('heapdump');
|
|
295
|
-
const filename = `heapdump-${Date.now()}.heapsnapshot`;
|
|
296
|
-
|
|
297
|
-
heapdump.writeSnapshot(filename, (err, dumpFilename) => {
|
|
298
|
-
if (err) {
|
|
299
|
-
this.log.error(`Heap dump error: ${err.message}`);
|
|
300
|
-
return res.status(500).json({ error: 'Heap dump failed', details: err.message });
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
this.log.info(`Heap dump written to ${dumpFilename}`);
|
|
304
|
-
return res.status(200).json({ ...memoryReport, heapdump: dumpFilename });
|
|
305
|
-
});
|
|
306
|
-
return; // Exit early since heapdump response is handled inside callback
|
|
307
|
-
}
|
|
308
|
-
*/
|
|
309
224
|
const memoryReport = {
|
|
310
225
|
memoryUsage,
|
|
311
226
|
heapStats,
|
|
@@ -314,7 +229,6 @@ export class Frontend {
|
|
|
314
229
|
};
|
|
315
230
|
res.status(200).json(memoryReport);
|
|
316
231
|
});
|
|
317
|
-
// Endpoint to start advertising the server node
|
|
318
232
|
this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
|
|
319
233
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
320
234
|
if (pairingCodes) {
|
|
@@ -325,24 +239,19 @@ export class Frontend {
|
|
|
325
239
|
res.status(500).json({ error: 'Failed to generate pairing codes' });
|
|
326
240
|
}
|
|
327
241
|
});
|
|
328
|
-
// Endpoint to provide settings
|
|
329
242
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
330
243
|
this.log.debug('The frontend sent /api/settings');
|
|
331
244
|
res.json(await this.getApiSettings());
|
|
332
245
|
});
|
|
333
|
-
// Endpoint to provide plugins
|
|
334
246
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
335
247
|
this.log.debug('The frontend sent /api/plugins');
|
|
336
248
|
const response = this.getBaseRegisteredPlugins();
|
|
337
|
-
// this.log.debug('Response:', debugStringify(response));
|
|
338
249
|
res.json(response);
|
|
339
250
|
});
|
|
340
|
-
// Endpoint to provide devices
|
|
341
251
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
342
252
|
this.log.debug('The frontend sent /api/devices');
|
|
343
253
|
const devices = [];
|
|
344
254
|
this.matterbridge.devices.forEach(async (device) => {
|
|
345
|
-
// Check if the device has the required properties
|
|
346
255
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
|
|
347
256
|
return;
|
|
348
257
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -358,10 +267,8 @@ export class Frontend {
|
|
|
358
267
|
cluster: cluster,
|
|
359
268
|
});
|
|
360
269
|
});
|
|
361
|
-
// this.log.debug('Response:', debugStringify(data));
|
|
362
270
|
res.json(devices);
|
|
363
271
|
});
|
|
364
|
-
// Endpoint to provide the cluster servers of the devices
|
|
365
272
|
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
366
273
|
const selectedPluginName = req.params.selectedPluginName;
|
|
367
274
|
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
@@ -434,7 +341,6 @@ export class Frontend {
|
|
|
434
341
|
});
|
|
435
342
|
res.json(data);
|
|
436
343
|
});
|
|
437
|
-
// Endpoint to view the log
|
|
438
344
|
this.expressApp.get('/api/view-log', async (req, res) => {
|
|
439
345
|
this.log.debug('The frontend sent /api/log');
|
|
440
346
|
try {
|
|
@@ -447,12 +353,10 @@ export class Frontend {
|
|
|
447
353
|
res.status(500).send('Error reading log file');
|
|
448
354
|
}
|
|
449
355
|
});
|
|
450
|
-
// Endpoint to download the matterbridge log
|
|
451
356
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
452
357
|
this.log.debug('The frontend sent /api/download-mblog');
|
|
453
358
|
try {
|
|
454
359
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
|
|
455
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
456
360
|
}
|
|
457
361
|
catch (error) {
|
|
458
362
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -464,12 +368,10 @@ export class Frontend {
|
|
|
464
368
|
}
|
|
465
369
|
});
|
|
466
370
|
});
|
|
467
|
-
// Endpoint to download the matter log
|
|
468
371
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
469
372
|
this.log.debug('The frontend sent /api/download-mjlog');
|
|
470
373
|
try {
|
|
471
374
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
472
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
473
375
|
}
|
|
474
376
|
catch (error) {
|
|
475
377
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -481,7 +383,6 @@ export class Frontend {
|
|
|
481
383
|
}
|
|
482
384
|
});
|
|
483
385
|
});
|
|
484
|
-
// Endpoint to download the matter storage file
|
|
485
386
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
486
387
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
487
388
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
@@ -492,7 +393,6 @@ export class Frontend {
|
|
|
492
393
|
}
|
|
493
394
|
});
|
|
494
395
|
});
|
|
495
|
-
// Endpoint to download the matterbridge storage directory
|
|
496
396
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
497
397
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
498
398
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -503,7 +403,6 @@ export class Frontend {
|
|
|
503
403
|
}
|
|
504
404
|
});
|
|
505
405
|
});
|
|
506
|
-
// Endpoint to download the matterbridge plugin directory
|
|
507
406
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
508
407
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
509
408
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -514,11 +413,9 @@ export class Frontend {
|
|
|
514
413
|
}
|
|
515
414
|
});
|
|
516
415
|
});
|
|
517
|
-
// Endpoint to download the matterbridge plugin config files
|
|
518
416
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
519
417
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
520
418
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
521
|
-
// 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')));
|
|
522
419
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
523
420
|
if (error) {
|
|
524
421
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
@@ -526,7 +423,6 @@ export class Frontend {
|
|
|
526
423
|
}
|
|
527
424
|
});
|
|
528
425
|
});
|
|
529
|
-
// Endpoint to download the matterbridge plugin config files
|
|
530
426
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
531
427
|
this.log.debug('The frontend sent /api/download-backup');
|
|
532
428
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -536,7 +432,6 @@ export class Frontend {
|
|
|
536
432
|
}
|
|
537
433
|
});
|
|
538
434
|
});
|
|
539
|
-
// Endpoint to receive commands
|
|
540
435
|
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
541
436
|
const command = req.params.command;
|
|
542
437
|
let param = req.params.param;
|
|
@@ -546,15 +441,13 @@ export class Frontend {
|
|
|
546
441
|
return;
|
|
547
442
|
}
|
|
548
443
|
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
549
|
-
// Handle the command setpassword from Settings
|
|
550
444
|
if (command === 'setpassword') {
|
|
551
|
-
const password = param.slice(1, -1);
|
|
445
|
+
const password = param.slice(1, -1);
|
|
552
446
|
this.log.debug('setpassword', param, password);
|
|
553
447
|
await this.matterbridge.nodeContext?.set('password', password);
|
|
554
448
|
res.json({ message: 'Command received' });
|
|
555
449
|
return;
|
|
556
450
|
}
|
|
557
|
-
// Handle the command setbridgemode from Settings
|
|
558
451
|
if (command === 'setbridgemode') {
|
|
559
452
|
this.log.debug(`setbridgemode: ${param}`);
|
|
560
453
|
this.wssSendRestartRequired();
|
|
@@ -562,7 +455,6 @@ export class Frontend {
|
|
|
562
455
|
res.json({ message: 'Command received' });
|
|
563
456
|
return;
|
|
564
457
|
}
|
|
565
|
-
// Handle the command backup from Settings
|
|
566
458
|
if (command === 'backup') {
|
|
567
459
|
this.log.notice(`Prepairing the backup...`);
|
|
568
460
|
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
|
|
@@ -570,26 +462,25 @@ export class Frontend {
|
|
|
570
462
|
res.json({ message: 'Command received' });
|
|
571
463
|
return;
|
|
572
464
|
}
|
|
573
|
-
// Handle the command setmbloglevel from Settings
|
|
574
465
|
if (command === 'setmbloglevel') {
|
|
575
466
|
this.log.debug('Matterbridge log level:', param);
|
|
576
467
|
if (param === 'Debug') {
|
|
577
|
-
this.log.logLevel = "debug"
|
|
468
|
+
this.log.logLevel = "debug";
|
|
578
469
|
}
|
|
579
470
|
else if (param === 'Info') {
|
|
580
|
-
this.log.logLevel = "info"
|
|
471
|
+
this.log.logLevel = "info";
|
|
581
472
|
}
|
|
582
473
|
else if (param === 'Notice') {
|
|
583
|
-
this.log.logLevel = "notice"
|
|
474
|
+
this.log.logLevel = "notice";
|
|
584
475
|
}
|
|
585
476
|
else if (param === 'Warn') {
|
|
586
|
-
this.log.logLevel = "warn"
|
|
477
|
+
this.log.logLevel = "warn";
|
|
587
478
|
}
|
|
588
479
|
else if (param === 'Error') {
|
|
589
|
-
this.log.logLevel = "error"
|
|
480
|
+
this.log.logLevel = "error";
|
|
590
481
|
}
|
|
591
482
|
else if (param === 'Fatal') {
|
|
592
|
-
this.log.logLevel = "fatal"
|
|
483
|
+
this.log.logLevel = "fatal";
|
|
593
484
|
}
|
|
594
485
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
595
486
|
this.matterbridge.log.logLevel = this.log.logLevel;
|
|
@@ -599,13 +490,12 @@ export class Frontend {
|
|
|
599
490
|
for (const plugin of this.matterbridge.plugins) {
|
|
600
491
|
if (!plugin.platform || !plugin.platform.config)
|
|
601
492
|
continue;
|
|
602
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug"
|
|
603
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug"
|
|
493
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug" : this.log.logLevel;
|
|
494
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug" : this.log.logLevel);
|
|
604
495
|
}
|
|
605
496
|
res.json({ message: 'Command received' });
|
|
606
497
|
return;
|
|
607
498
|
}
|
|
608
|
-
// Handle the command setmbloglevel from Settings
|
|
609
499
|
if (command === 'setmjloglevel') {
|
|
610
500
|
this.log.debug('Matter.js log level:', param);
|
|
611
501
|
if (param === 'Debug') {
|
|
@@ -630,34 +520,30 @@ export class Frontend {
|
|
|
630
520
|
res.json({ message: 'Command received' });
|
|
631
521
|
return;
|
|
632
522
|
}
|
|
633
|
-
// Handle the command setmdnsinterface from Settings
|
|
634
523
|
if (command === 'setmdnsinterface') {
|
|
635
|
-
param = param.slice(1, -1);
|
|
524
|
+
param = param.slice(1, -1);
|
|
636
525
|
this.matterbridge.matterbridgeInformation.mattermdnsinterface = param;
|
|
637
526
|
this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
|
|
638
527
|
await this.matterbridge.nodeContext?.set('mattermdnsinterface', param);
|
|
639
528
|
res.json({ message: 'Command received' });
|
|
640
529
|
return;
|
|
641
530
|
}
|
|
642
|
-
// Handle the command setipv4address from Settings
|
|
643
531
|
if (command === 'setipv4address') {
|
|
644
|
-
param = param.slice(1, -1);
|
|
532
|
+
param = param.slice(1, -1);
|
|
645
533
|
this.matterbridge.matterbridgeInformation.matteripv4address = param;
|
|
646
534
|
this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
|
|
647
535
|
await this.matterbridge.nodeContext?.set('matteripv4address', param);
|
|
648
536
|
res.json({ message: 'Command received' });
|
|
649
537
|
return;
|
|
650
538
|
}
|
|
651
|
-
// Handle the command setipv6address from Settings
|
|
652
539
|
if (command === 'setipv6address') {
|
|
653
|
-
param = param.slice(1, -1);
|
|
540
|
+
param = param.slice(1, -1);
|
|
654
541
|
this.matterbridge.matterbridgeInformation.matteripv6address = param;
|
|
655
542
|
this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
|
|
656
543
|
await this.matterbridge.nodeContext?.set('matteripv6address', param);
|
|
657
544
|
res.json({ message: 'Command received' });
|
|
658
545
|
return;
|
|
659
546
|
}
|
|
660
|
-
// Handle the command setmatterport from Settings
|
|
661
547
|
if (command === 'setmatterport') {
|
|
662
548
|
const port = Math.min(Math.max(parseInt(param), 5540), 5560);
|
|
663
549
|
this.matterbridge.matterbridgeInformation.matterPort = port;
|
|
@@ -666,7 +552,6 @@ export class Frontend {
|
|
|
666
552
|
res.json({ message: 'Command received' });
|
|
667
553
|
return;
|
|
668
554
|
}
|
|
669
|
-
// Handle the command setmatterdiscriminator from Settings
|
|
670
555
|
if (command === 'setmatterdiscriminator') {
|
|
671
556
|
const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
|
|
672
557
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
|
|
@@ -675,7 +560,6 @@ export class Frontend {
|
|
|
675
560
|
res.json({ message: 'Command received' });
|
|
676
561
|
return;
|
|
677
562
|
}
|
|
678
|
-
// Handle the command setmatterpasscode from Settings
|
|
679
563
|
if (command === 'setmatterpasscode') {
|
|
680
564
|
const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
|
|
681
565
|
this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
|
|
@@ -684,20 +568,17 @@ export class Frontend {
|
|
|
684
568
|
res.json({ message: 'Command received' });
|
|
685
569
|
return;
|
|
686
570
|
}
|
|
687
|
-
// Handle the command setmbloglevel from Settings
|
|
688
571
|
if (command === 'setmblogfile') {
|
|
689
572
|
this.log.debug('Matterbridge file log:', param);
|
|
690
573
|
this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
|
|
691
574
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
|
|
692
|
-
// Create the file logger for matterbridge
|
|
693
575
|
if (param === 'true')
|
|
694
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug"
|
|
576
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
|
|
695
577
|
else
|
|
696
578
|
AnsiLogger.setGlobalLogfile(undefined);
|
|
697
579
|
res.json({ message: 'Command received' });
|
|
698
580
|
return;
|
|
699
581
|
}
|
|
700
|
-
// Handle the command setmbloglevel from Settings
|
|
701
582
|
if (command === 'setmjlogfile') {
|
|
702
583
|
this.log.debug('Matter file log:', param);
|
|
703
584
|
this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
|
|
@@ -724,57 +605,40 @@ export class Frontend {
|
|
|
724
605
|
res.json({ message: 'Command received' });
|
|
725
606
|
return;
|
|
726
607
|
}
|
|
727
|
-
// Handle the command unregister from Settings
|
|
728
608
|
if (command === 'unregister') {
|
|
729
609
|
await this.matterbridge.unregisterAndShutdownProcess();
|
|
730
610
|
res.json({ message: 'Command received' });
|
|
731
611
|
return;
|
|
732
612
|
}
|
|
733
|
-
// Handle the command reset from Settings
|
|
734
613
|
if (command === 'reset') {
|
|
735
614
|
await this.matterbridge.shutdownProcessAndReset();
|
|
736
615
|
res.json({ message: 'Command received' });
|
|
737
616
|
return;
|
|
738
617
|
}
|
|
739
|
-
// Handle the command factoryreset from Settings
|
|
740
618
|
if (command === 'factoryreset') {
|
|
741
619
|
await this.matterbridge.shutdownProcessAndFactoryReset();
|
|
742
620
|
res.json({ message: 'Command received' });
|
|
743
621
|
return;
|
|
744
622
|
}
|
|
745
|
-
// Handle the command shutdown from Header
|
|
746
623
|
if (command === 'shutdown') {
|
|
747
624
|
await this.matterbridge.shutdownProcess();
|
|
748
625
|
res.json({ message: 'Command received' });
|
|
749
626
|
return;
|
|
750
627
|
}
|
|
751
|
-
// Handle the command restart from Header
|
|
752
628
|
if (command === 'restart') {
|
|
753
629
|
await this.matterbridge.restartProcess();
|
|
754
630
|
res.json({ message: 'Command received' });
|
|
755
631
|
return;
|
|
756
632
|
}
|
|
757
|
-
// Handle the command update from Header
|
|
758
633
|
if (command === 'update') {
|
|
759
|
-
this.log.info('Updating matterbridge...');
|
|
760
|
-
try {
|
|
761
|
-
await this.matterbridge.spawnCommand('npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose']);
|
|
762
|
-
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
763
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
764
|
-
}
|
|
765
|
-
catch (error) {
|
|
766
|
-
this.log.error('Error updating matterbridge');
|
|
767
|
-
}
|
|
768
634
|
await this.matterbridge.updateProcess();
|
|
769
635
|
this.wssSendRestartRequired();
|
|
770
636
|
res.json({ message: 'Command received' });
|
|
771
637
|
return;
|
|
772
638
|
}
|
|
773
|
-
// Handle the command saveconfig from Home
|
|
774
639
|
if (command === 'saveconfig') {
|
|
775
640
|
param = param.replace(/\*/g, '\\');
|
|
776
641
|
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
777
|
-
// console.log('Req.body:', JSON.stringify(req.body, null, 2));
|
|
778
642
|
if (!this.matterbridge.plugins.has(param)) {
|
|
779
643
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
780
644
|
}
|
|
@@ -788,58 +652,49 @@ export class Frontend {
|
|
|
788
652
|
res.json({ message: 'Command received' });
|
|
789
653
|
return;
|
|
790
654
|
}
|
|
791
|
-
// Handle the command installplugin from Home
|
|
792
655
|
if (command === 'installplugin') {
|
|
793
656
|
param = param.replace(/\*/g, '\\');
|
|
794
657
|
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
795
658
|
try {
|
|
796
659
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
797
660
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
798
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
799
661
|
}
|
|
800
662
|
catch (error) {
|
|
801
663
|
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
802
664
|
}
|
|
803
665
|
this.wssSendRestartRequired();
|
|
804
666
|
param = param.split('@')[0];
|
|
805
|
-
// Also add the plugin to matterbridge so no return!
|
|
806
667
|
if (param === 'matterbridge') {
|
|
807
|
-
// 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
|
|
808
668
|
res.json({ message: 'Command received' });
|
|
809
669
|
return;
|
|
810
670
|
}
|
|
811
671
|
}
|
|
812
|
-
// Handle the command addplugin from Home
|
|
813
672
|
if (command === 'addplugin' || command === 'installplugin') {
|
|
814
673
|
param = param.replace(/\*/g, '\\');
|
|
815
674
|
const plugin = await this.matterbridge.plugins.add(param);
|
|
816
675
|
if (plugin) {
|
|
817
676
|
if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
818
|
-
// 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
|
|
819
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
820
677
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
821
678
|
}
|
|
822
|
-
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true);
|
|
679
|
+
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true);
|
|
823
680
|
}
|
|
824
681
|
res.json({ message: 'Command received' });
|
|
825
682
|
this.wssSendRefreshRequired();
|
|
826
683
|
return;
|
|
827
684
|
}
|
|
828
|
-
// Handle the command removeplugin from Home
|
|
829
685
|
if (command === 'removeplugin') {
|
|
830
686
|
if (!this.matterbridge.plugins.has(param)) {
|
|
831
687
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
832
688
|
}
|
|
833
689
|
else {
|
|
834
690
|
const plugin = this.matterbridge.plugins.get(param);
|
|
835
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
691
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
836
692
|
await this.matterbridge.plugins.remove(param);
|
|
837
693
|
}
|
|
838
694
|
res.json({ message: 'Command received' });
|
|
839
695
|
this.wssSendRefreshRequired();
|
|
840
696
|
return;
|
|
841
697
|
}
|
|
842
|
-
// Handle the command enableplugin from Home
|
|
843
698
|
if (command === 'enableplugin') {
|
|
844
699
|
if (!this.matterbridge.plugins.has(param)) {
|
|
845
700
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -857,17 +712,15 @@ export class Frontend {
|
|
|
857
712
|
plugin.addedDevices = undefined;
|
|
858
713
|
await this.matterbridge.plugins.enable(param);
|
|
859
714
|
if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
|
|
860
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
861
715
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
862
716
|
}
|
|
863
|
-
this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true);
|
|
717
|
+
this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true);
|
|
864
718
|
}
|
|
865
719
|
}
|
|
866
720
|
res.json({ message: 'Command received' });
|
|
867
721
|
this.wssSendRefreshRequired();
|
|
868
722
|
return;
|
|
869
723
|
}
|
|
870
|
-
// Handle the command disableplugin from Home
|
|
871
724
|
if (command === 'disableplugin') {
|
|
872
725
|
if (!this.matterbridge.plugins.has(param)) {
|
|
873
726
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -875,7 +728,7 @@ export class Frontend {
|
|
|
875
728
|
else {
|
|
876
729
|
const plugin = this.matterbridge.plugins.get(param);
|
|
877
730
|
if (plugin && plugin.enabled) {
|
|
878
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
731
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
879
732
|
await this.matterbridge.plugins.disable(param);
|
|
880
733
|
}
|
|
881
734
|
}
|
|
@@ -884,7 +737,6 @@ export class Frontend {
|
|
|
884
737
|
return;
|
|
885
738
|
}
|
|
886
739
|
});
|
|
887
|
-
// Fallback for routing (must be the last route)
|
|
888
740
|
this.expressApp.get('*', (req, res) => {
|
|
889
741
|
this.log.debug('The frontend sent:', req.url);
|
|
890
742
|
this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -893,29 +745,24 @@ export class Frontend {
|
|
|
893
745
|
this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
894
746
|
}
|
|
895
747
|
async stop() {
|
|
896
|
-
// Close the http server
|
|
897
748
|
if (this.httpServer) {
|
|
898
749
|
this.httpServer.close();
|
|
899
750
|
this.httpServer.removeAllListeners();
|
|
900
751
|
this.httpServer = undefined;
|
|
901
752
|
this.log.debug('Frontend http server closed successfully');
|
|
902
753
|
}
|
|
903
|
-
// Close the https server
|
|
904
754
|
if (this.httpsServer) {
|
|
905
755
|
this.httpsServer.close();
|
|
906
756
|
this.httpsServer.removeAllListeners();
|
|
907
757
|
this.httpsServer = undefined;
|
|
908
758
|
this.log.debug('Frontend https server closed successfully');
|
|
909
759
|
}
|
|
910
|
-
// Remove listeners from the express app
|
|
911
760
|
if (this.expressApp) {
|
|
912
761
|
this.expressApp.removeAllListeners();
|
|
913
762
|
this.expressApp = undefined;
|
|
914
763
|
this.log.debug('Frontend app closed successfully');
|
|
915
764
|
}
|
|
916
|
-
// Close the WebSocket server
|
|
917
765
|
if (this.webSocketServer) {
|
|
918
|
-
// Close all active connections
|
|
919
766
|
this.webSocketServer.clients.forEach((client) => {
|
|
920
767
|
if (client.readyState === WebSocket.OPEN) {
|
|
921
768
|
client.close();
|
|
@@ -931,23 +778,35 @@ export class Frontend {
|
|
|
931
778
|
});
|
|
932
779
|
this.webSocketServer = undefined;
|
|
933
780
|
}
|
|
934
|
-
// Stop the memory dump interval
|
|
935
781
|
if (hasParameter('memorydump')) {
|
|
936
|
-
this.
|
|
782
|
+
this.stopCpuMemoryDump();
|
|
937
783
|
}
|
|
938
784
|
}
|
|
939
|
-
// Function to format bytes to KB or MB
|
|
940
785
|
formatMemoryUsage = (bytes) => {
|
|
941
786
|
const kb = bytes / 1024;
|
|
942
787
|
const mb = kb / 1024;
|
|
943
788
|
return mb >= 1 ? `${mb.toFixed(2)} MB` : `${kb.toFixed(2)} KB`;
|
|
944
789
|
};
|
|
945
|
-
|
|
790
|
+
startCpuMemoryDump() {
|
|
946
791
|
clearInterval(this.memoryInterval);
|
|
947
792
|
clearTimeout(this.memoryTimeout);
|
|
948
793
|
const interval = () => {
|
|
794
|
+
const currCpus = os.cpus();
|
|
795
|
+
if (currCpus.length !== this.prevCpus.length) {
|
|
796
|
+
this.prevCpus = currCpus;
|
|
797
|
+
}
|
|
798
|
+
let totalIdle = 0, totalTick = 0;
|
|
799
|
+
this.prevCpus.forEach((prevCpu, i) => {
|
|
800
|
+
const currCpu = currCpus[i];
|
|
801
|
+
const idleDiff = currCpu.times.idle - prevCpu.times.idle;
|
|
802
|
+
const totalDiff = Object.keys(currCpu.times).reduce((acc, key) => acc + (currCpu.times[key] - prevCpu.times[key]), 0);
|
|
803
|
+
totalIdle += idleDiff;
|
|
804
|
+
totalTick += totalDiff;
|
|
805
|
+
});
|
|
806
|
+
const cpuUsage = (100 - (totalIdle / totalTick) * 100).toFixed(2);
|
|
807
|
+
this.prevCpus = currCpus;
|
|
949
808
|
const memoryUsageRaw = process.memoryUsage();
|
|
950
|
-
this.memoryData.push(memoryUsageRaw);
|
|
809
|
+
this.memoryData.push({ ...memoryUsageRaw, cpu: cpuUsage });
|
|
951
810
|
const memoryUsage = {
|
|
952
811
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
953
812
|
heapTotal: this.formatMemoryUsage(memoryUsageRaw.heapTotal),
|
|
@@ -955,17 +814,17 @@ export class Frontend {
|
|
|
955
814
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
956
815
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
957
816
|
};
|
|
958
|
-
this.log.debug(`***Memory usage rss ${CYAN}${memoryUsage.rss}${db} heapTotal ${CYAN}${memoryUsage.heapTotal}${db} heapUsed ${CYAN}${memoryUsage.heapUsed}${db} external ${memoryUsage.external} arrayBuffers ${memoryUsage.arrayBuffers}`);
|
|
817
|
+
this.log.debug(`***Cpu usage ${CYAN}${cpuUsage.padStart(6, ' ')} %${db} - Memory usage rss ${CYAN}${memoryUsage.rss}${db} heapTotal ${CYAN}${memoryUsage.heapTotal}${db} heapUsed ${CYAN}${memoryUsage.heapUsed}${db} external ${memoryUsage.external} arrayBuffers ${memoryUsage.arrayBuffers}`);
|
|
959
818
|
};
|
|
960
819
|
interval();
|
|
961
|
-
this.memoryInterval = setInterval(interval, getIntParameter('
|
|
820
|
+
this.memoryInterval = setInterval(interval, getIntParameter('memoryinterval') ?? 1000);
|
|
962
821
|
this.memoryInterval.unref();
|
|
963
822
|
this.memoryTimeout = setTimeout(() => {
|
|
964
|
-
this.
|
|
965
|
-
},
|
|
823
|
+
this.stopCpuMemoryDump();
|
|
824
|
+
}, getIntParameter('memorytimeout') ?? 600000);
|
|
966
825
|
this.memoryTimeout.unref();
|
|
967
826
|
}
|
|
968
|
-
|
|
827
|
+
stopCpuMemoryDump() {
|
|
969
828
|
clearInterval(this.memoryInterval);
|
|
970
829
|
this.memoryInterval = undefined;
|
|
971
830
|
clearTimeout(this.memoryTimeout);
|
|
@@ -978,15 +837,14 @@ export class Frontend {
|
|
|
978
837
|
external: this.formatMemoryUsage(memory.external),
|
|
979
838
|
arrayBuffers: this.formatMemoryUsage(memory.arrayBuffers),
|
|
980
839
|
};
|
|
981
|
-
|
|
982
|
-
console.log(`${YELLOW}Memory usage${db} rss ${CYAN}${memoryUsage.rss}${db} heapTotal ${CYAN}${memoryUsage.heapTotal}${db} heapUsed ${CYAN}${memoryUsage.heapUsed}${db} external ${memoryUsage.external} arrayBuffers ${memoryUsage.arrayBuffers}${rs}`);
|
|
840
|
+
console.log(`${YELLOW}Cpu usage${db} ${CYAN}${memory.cpu.padStart(6, ' ')} %${db} - ${YELLOW}Memory usage${db} rss ${CYAN}${memoryUsage.rss}${db} heapTotal ${CYAN}${memoryUsage.heapTotal}${db} heapUsed ${CYAN}${memoryUsage.heapUsed}${db} external ${memoryUsage.external} arrayBuffers ${memoryUsage.arrayBuffers}${rs}`);
|
|
983
841
|
}
|
|
842
|
+
this.memoryData = [];
|
|
843
|
+
this.prevCpus = [];
|
|
984
844
|
}
|
|
985
|
-
/**
|
|
986
|
-
* Retrieves the api settings.
|
|
987
|
-
* @returns {Promise<object>} A promise that resolve in the api settings object.
|
|
988
|
-
*/
|
|
989
845
|
async getApiSettings() {
|
|
846
|
+
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
847
|
+
this.matterbridge.systemInformation.heap = this.formatMemoryUsage(process.memoryUsage().heapUsed) + ' / ' + this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
990
848
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
991
849
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
992
850
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -1005,11 +863,6 @@ export class Frontend {
|
|
|
1005
863
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
1006
864
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
1007
865
|
}
|
|
1008
|
-
/**
|
|
1009
|
-
* Retrieves the cluster text description from a given device.
|
|
1010
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
1011
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
1012
|
-
*/
|
|
1013
866
|
getClusterTextFromDevice(device) {
|
|
1014
867
|
const getAttribute = (device, cluster, attribute) => {
|
|
1015
868
|
let value = undefined;
|
|
@@ -1048,7 +901,6 @@ export class Frontend {
|
|
|
1048
901
|
};
|
|
1049
902
|
let attributes = '';
|
|
1050
903
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1051
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
1052
904
|
if (typeof attributeValue === 'undefined')
|
|
1053
905
|
return;
|
|
1054
906
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1126,13 +978,8 @@ export class Frontend {
|
|
|
1126
978
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1127
979
|
attributes += `${getUserLabel(device)} `;
|
|
1128
980
|
});
|
|
1129
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1130
981
|
return attributes.trimStart().trimEnd();
|
|
1131
982
|
}
|
|
1132
|
-
/**
|
|
1133
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
1134
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
1135
|
-
*/
|
|
1136
983
|
getBaseRegisteredPlugins() {
|
|
1137
984
|
const baseRegisteredPlugins = [];
|
|
1138
985
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -1163,14 +1010,6 @@ export class Frontend {
|
|
|
1163
1010
|
}
|
|
1164
1011
|
return baseRegisteredPlugins;
|
|
1165
1012
|
}
|
|
1166
|
-
/**
|
|
1167
|
-
* Handles incoming websocket messages for the Matterbridge.
|
|
1168
|
-
*
|
|
1169
|
-
* @param {Matterbridge} this - The Matterbridge instance.
|
|
1170
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1171
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1172
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1173
|
-
*/
|
|
1174
1013
|
async wsMessageHandler(client, message) {
|
|
1175
1014
|
let data;
|
|
1176
1015
|
try {
|
|
@@ -1258,10 +1097,8 @@ export class Frontend {
|
|
|
1258
1097
|
else if (data.method === '/api/devices') {
|
|
1259
1098
|
const devices = [];
|
|
1260
1099
|
this.matterbridge.devices.forEach(async (device) => {
|
|
1261
|
-
// Filter by pluginName if provided
|
|
1262
1100
|
if (data.params.pluginName && data.params.pluginName !== device.plugin)
|
|
1263
1101
|
return;
|
|
1264
|
-
// Check if the device has the required properties
|
|
1265
1102
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
|
|
1266
1103
|
return;
|
|
1267
1104
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -1344,7 +1181,6 @@ export class Frontend {
|
|
|
1344
1181
|
});
|
|
1345
1182
|
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
1346
1183
|
deviceTypes = [];
|
|
1347
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1348
1184
|
const name = childEndpoint.endpoint?.id;
|
|
1349
1185
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
1350
1186
|
clusterServers.forEach((clusterServer) => {
|
|
@@ -1427,73 +1263,44 @@ export class Frontend {
|
|
|
1427
1263
|
return;
|
|
1428
1264
|
}
|
|
1429
1265
|
}
|
|
1430
|
-
/**
|
|
1431
|
-
* Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1432
|
-
*
|
|
1433
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1434
|
-
* @param {string} time - The time string of the message
|
|
1435
|
-
* @param {string} name - The logger name of the message
|
|
1436
|
-
* @param {string} message - The content of the message.
|
|
1437
|
-
*/
|
|
1438
1266
|
wssSendMessage(level, time, name, message) {
|
|
1439
1267
|
if (!level || !time || !name || !message)
|
|
1440
1268
|
return;
|
|
1441
|
-
// Remove ANSI escape codes from the message
|
|
1442
|
-
// eslint-disable-next-line no-control-regex
|
|
1443
1269
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1444
|
-
// Remove leading asterisks from the message
|
|
1445
1270
|
message = message.replace(/^\*+/, '');
|
|
1446
|
-
// Replace all occurrences of \t and \n
|
|
1447
1271
|
message = message.replace(/[\t\n]/g, '');
|
|
1448
|
-
// Remove non-printable characters
|
|
1449
|
-
// eslint-disable-next-line no-control-regex
|
|
1450
1272
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1451
|
-
// Replace all occurrences of \" with "
|
|
1452
1273
|
message = message.replace(/\\"/g, '"');
|
|
1453
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1454
1274
|
const maxContinuousLength = 100;
|
|
1455
1275
|
const keepStartLength = 20;
|
|
1456
1276
|
const keepEndLength = 20;
|
|
1457
|
-
// Split the message into words
|
|
1458
1277
|
message = message
|
|
1459
1278
|
.split(' ')
|
|
1460
1279
|
.map((word) => {
|
|
1461
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1462
1280
|
if (word.length > maxContinuousLength) {
|
|
1463
1281
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1464
1282
|
}
|
|
1465
1283
|
return word;
|
|
1466
1284
|
})
|
|
1467
1285
|
.join(' ');
|
|
1468
|
-
// Send the message to all connected clients
|
|
1469
1286
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1470
1287
|
if (client.readyState === WebSocket.OPEN) {
|
|
1471
1288
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1472
1289
|
}
|
|
1473
1290
|
});
|
|
1474
1291
|
}
|
|
1475
|
-
/**
|
|
1476
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1477
|
-
*
|
|
1478
|
-
*/
|
|
1479
1292
|
wssSendRefreshRequired() {
|
|
1480
1293
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1481
1294
|
this.matterbridge.matterbridgeInformation.refreshRequired = true;
|
|
1482
|
-
// Send the message to all connected clients
|
|
1483
1295
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1484
1296
|
if (client.readyState === WebSocket.OPEN) {
|
|
1485
1297
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: {} }));
|
|
1486
1298
|
}
|
|
1487
1299
|
});
|
|
1488
1300
|
}
|
|
1489
|
-
/**
|
|
1490
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1491
|
-
*
|
|
1492
|
-
*/
|
|
1493
1301
|
wssSendRestartRequired() {
|
|
1494
1302
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1495
1303
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1496
|
-
// Send the message to all connected clients
|
|
1497
1304
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1498
1305
|
if (client.readyState === WebSocket.OPEN) {
|
|
1499
1306
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
@@ -1501,4 +1308,3 @@ export class Frontend {
|
|
|
1501
1308
|
});
|
|
1502
1309
|
}
|
|
1503
1310
|
}
|
|
1504
|
-
//# sourceMappingURL=frontend.js.map
|