matterbridge 2.1.4 → 2.1.5-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/cli.js +0 -26
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -94
- package/dist/frontend.js +23 -232
- 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 +0 -2
- package/dist/matter/types.js +0 -2
- package/dist/matterbridge.js +55 -761
- 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 +10 -710
- package/dist/matterbridgeEndpointHelpers.js +29 -105
- package/dist/matterbridgePlatform.js +5 -121
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +3 -230
- 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 +13 -267
- 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 -114
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -110
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/index.d.ts +0 -35
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -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 -2264
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -159
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -167
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -236
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/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 -231
- 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;
|
|
@@ -71,7 +28,7 @@ export class Frontend {
|
|
|
71
28
|
memoryTimeout;
|
|
72
29
|
constructor(matterbridge) {
|
|
73
30
|
this.matterbridge = matterbridge;
|
|
74
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
31
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
75
32
|
}
|
|
76
33
|
set logLevel(logLevel) {
|
|
77
34
|
this.log.logLevel = logLevel;
|
|
@@ -79,21 +36,10 @@ export class Frontend {
|
|
|
79
36
|
async start(port = 8283) {
|
|
80
37
|
this.port = port;
|
|
81
38
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
82
|
-
// Create the express app that serves the frontend
|
|
83
39
|
this.expressApp = express();
|
|
84
|
-
// Log all requests to the server for debugging
|
|
85
|
-
/*
|
|
86
|
-
this.expressApp.use((req, res, next) => {
|
|
87
|
-
this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
|
|
88
|
-
next();
|
|
89
|
-
});
|
|
90
|
-
*/
|
|
91
|
-
// Serve static files from '/static' endpoint
|
|
92
40
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
93
41
|
if (!hasParameter('ssl')) {
|
|
94
|
-
// Create an HTTP server and attach the express app
|
|
95
42
|
this.httpServer = createServer(this.expressApp);
|
|
96
|
-
// Listen on the specified port
|
|
97
43
|
if (hasParameter('ingress')) {
|
|
98
44
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
99
45
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -107,7 +53,6 @@ export class Frontend {
|
|
|
107
53
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
108
54
|
});
|
|
109
55
|
}
|
|
110
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
56
|
this.httpServer.on('error', (error) => {
|
|
112
57
|
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
113
58
|
switch (error.code) {
|
|
@@ -123,7 +68,6 @@ export class Frontend {
|
|
|
123
68
|
});
|
|
124
69
|
}
|
|
125
70
|
else {
|
|
126
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
127
71
|
let cert;
|
|
128
72
|
try {
|
|
129
73
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -151,9 +95,7 @@ export class Frontend {
|
|
|
151
95
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
152
96
|
}
|
|
153
97
|
const serverOptions = { cert, key, ca };
|
|
154
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
155
98
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
156
|
-
// Listen on the specified port
|
|
157
99
|
if (hasParameter('ingress')) {
|
|
158
100
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
159
101
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -167,7 +109,6 @@ export class Frontend {
|
|
|
167
109
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
168
110
|
});
|
|
169
111
|
}
|
|
170
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
171
112
|
this.httpsServer.on('error', (error) => {
|
|
172
113
|
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
173
114
|
switch (error.code) {
|
|
@@ -184,13 +125,12 @@ export class Frontend {
|
|
|
184
125
|
}
|
|
185
126
|
if (this.initializeError)
|
|
186
127
|
return;
|
|
187
|
-
// Createe a WebSocket server and attach it to the http or https server
|
|
188
128
|
const wssPort = this.port;
|
|
189
129
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
190
130
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
191
131
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
192
132
|
const clientIp = request.socket.remoteAddress;
|
|
193
|
-
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug"
|
|
133
|
+
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug");
|
|
194
134
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
195
135
|
ws.on('message', (message) => {
|
|
196
136
|
this.wsMessageHandler(ws, message);
|
|
@@ -222,11 +162,9 @@ export class Frontend {
|
|
|
222
162
|
this.webSocketServer.on('error', (ws, error) => {
|
|
223
163
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
224
164
|
});
|
|
225
|
-
// Start the memory dump interval
|
|
226
165
|
if (hasParameter('memorydump')) {
|
|
227
166
|
this.startCpuMemoryDump();
|
|
228
167
|
}
|
|
229
|
-
// Endpoint to validate login code
|
|
230
168
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
231
169
|
const { password } = req.body;
|
|
232
170
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -245,27 +183,23 @@ export class Frontend {
|
|
|
245
183
|
this.log.warn('/api/login error wrong password');
|
|
246
184
|
res.json({ valid: false });
|
|
247
185
|
}
|
|
248
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
249
186
|
}
|
|
250
187
|
catch (error) {
|
|
251
188
|
this.log.error('/api/login error getting password');
|
|
252
189
|
res.json({ valid: false });
|
|
253
190
|
}
|
|
254
191
|
});
|
|
255
|
-
// Endpoint to provide health check
|
|
256
192
|
this.expressApp.get('/health', (req, res) => {
|
|
257
193
|
this.log.debug('Express received /health');
|
|
258
194
|
const healthStatus = {
|
|
259
|
-
status: 'ok',
|
|
260
|
-
uptime: process.uptime(),
|
|
261
|
-
timestamp: new Date().toISOString(),
|
|
195
|
+
status: 'ok',
|
|
196
|
+
uptime: process.uptime(),
|
|
197
|
+
timestamp: new Date().toISOString(),
|
|
262
198
|
};
|
|
263
199
|
res.status(200).json(healthStatus);
|
|
264
200
|
});
|
|
265
|
-
// Endpoint to provide memory usage details
|
|
266
201
|
this.expressApp.get('/memory', async (req, res) => {
|
|
267
202
|
this.log.debug('Express received /memory');
|
|
268
|
-
// Memory usage from process
|
|
269
203
|
const memoryUsageRaw = process.memoryUsage();
|
|
270
204
|
const memoryUsage = {
|
|
271
205
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -274,13 +208,10 @@ export class Frontend {
|
|
|
274
208
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
275
209
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
276
210
|
};
|
|
277
|
-
// V8 heap statistics
|
|
278
211
|
const { default: v8 } = await import('node:v8');
|
|
279
212
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
280
213
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
281
|
-
// Format heapStats
|
|
282
214
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
283
|
-
// Format heapSpaces
|
|
284
215
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
285
216
|
...space,
|
|
286
217
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -290,23 +221,6 @@ export class Frontend {
|
|
|
290
221
|
}));
|
|
291
222
|
const { default: module } = await import('module');
|
|
292
223
|
const loadedModules = module._cache ? Object.keys(module._cache).sort() : [];
|
|
293
|
-
/*
|
|
294
|
-
if (req.query.heapdump === 'true') {
|
|
295
|
-
const { default: heapdump } = await import('heapdump');
|
|
296
|
-
const filename = `heapdump-${Date.now()}.heapsnapshot`;
|
|
297
|
-
|
|
298
|
-
heapdump.writeSnapshot(filename, (err, dumpFilename) => {
|
|
299
|
-
if (err) {
|
|
300
|
-
this.log.error(`Heap dump error: ${err.message}`);
|
|
301
|
-
return res.status(500).json({ error: 'Heap dump failed', details: err.message });
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
this.log.info(`Heap dump written to ${dumpFilename}`);
|
|
305
|
-
return res.status(200).json({ ...memoryReport, heapdump: dumpFilename });
|
|
306
|
-
});
|
|
307
|
-
return; // Exit early since heapdump response is handled inside callback
|
|
308
|
-
}
|
|
309
|
-
*/
|
|
310
224
|
const memoryReport = {
|
|
311
225
|
memoryUsage,
|
|
312
226
|
heapStats,
|
|
@@ -315,7 +229,6 @@ export class Frontend {
|
|
|
315
229
|
};
|
|
316
230
|
res.status(200).json(memoryReport);
|
|
317
231
|
});
|
|
318
|
-
// Endpoint to start advertising the server node
|
|
319
232
|
this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
|
|
320
233
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
321
234
|
if (pairingCodes) {
|
|
@@ -326,24 +239,19 @@ export class Frontend {
|
|
|
326
239
|
res.status(500).json({ error: 'Failed to generate pairing codes' });
|
|
327
240
|
}
|
|
328
241
|
});
|
|
329
|
-
// Endpoint to provide settings
|
|
330
242
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
331
243
|
this.log.debug('The frontend sent /api/settings');
|
|
332
244
|
res.json(await this.getApiSettings());
|
|
333
245
|
});
|
|
334
|
-
// Endpoint to provide plugins
|
|
335
246
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
336
247
|
this.log.debug('The frontend sent /api/plugins');
|
|
337
248
|
const response = this.getBaseRegisteredPlugins();
|
|
338
|
-
// this.log.debug('Response:', debugStringify(response));
|
|
339
249
|
res.json(response);
|
|
340
250
|
});
|
|
341
|
-
// Endpoint to provide devices
|
|
342
251
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
343
252
|
this.log.debug('The frontend sent /api/devices');
|
|
344
253
|
const devices = [];
|
|
345
254
|
this.matterbridge.devices.forEach(async (device) => {
|
|
346
|
-
// Check if the device has the required properties
|
|
347
255
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
|
|
348
256
|
return;
|
|
349
257
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -359,10 +267,8 @@ export class Frontend {
|
|
|
359
267
|
cluster: cluster,
|
|
360
268
|
});
|
|
361
269
|
});
|
|
362
|
-
// this.log.debug('Response:', debugStringify(data));
|
|
363
270
|
res.json(devices);
|
|
364
271
|
});
|
|
365
|
-
// Endpoint to provide the cluster servers of the devices
|
|
366
272
|
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
367
273
|
const selectedPluginName = req.params.selectedPluginName;
|
|
368
274
|
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
@@ -435,7 +341,6 @@ export class Frontend {
|
|
|
435
341
|
});
|
|
436
342
|
res.json(data);
|
|
437
343
|
});
|
|
438
|
-
// Endpoint to view the log
|
|
439
344
|
this.expressApp.get('/api/view-log', async (req, res) => {
|
|
440
345
|
this.log.debug('The frontend sent /api/log');
|
|
441
346
|
try {
|
|
@@ -448,12 +353,10 @@ export class Frontend {
|
|
|
448
353
|
res.status(500).send('Error reading log file');
|
|
449
354
|
}
|
|
450
355
|
});
|
|
451
|
-
// Endpoint to download the matterbridge log
|
|
452
356
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
453
357
|
this.log.debug('The frontend sent /api/download-mblog');
|
|
454
358
|
try {
|
|
455
359
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
|
|
456
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
457
360
|
}
|
|
458
361
|
catch (error) {
|
|
459
362
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -465,12 +368,10 @@ export class Frontend {
|
|
|
465
368
|
}
|
|
466
369
|
});
|
|
467
370
|
});
|
|
468
|
-
// Endpoint to download the matter log
|
|
469
371
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
470
372
|
this.log.debug('The frontend sent /api/download-mjlog');
|
|
471
373
|
try {
|
|
472
374
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
473
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
474
375
|
}
|
|
475
376
|
catch (error) {
|
|
476
377
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -482,7 +383,6 @@ export class Frontend {
|
|
|
482
383
|
}
|
|
483
384
|
});
|
|
484
385
|
});
|
|
485
|
-
// Endpoint to download the matter storage file
|
|
486
386
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
487
387
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
488
388
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
@@ -493,7 +393,6 @@ export class Frontend {
|
|
|
493
393
|
}
|
|
494
394
|
});
|
|
495
395
|
});
|
|
496
|
-
// Endpoint to download the matterbridge storage directory
|
|
497
396
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
498
397
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
499
398
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -504,7 +403,6 @@ export class Frontend {
|
|
|
504
403
|
}
|
|
505
404
|
});
|
|
506
405
|
});
|
|
507
|
-
// Endpoint to download the matterbridge plugin directory
|
|
508
406
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
509
407
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
510
408
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -515,11 +413,9 @@ export class Frontend {
|
|
|
515
413
|
}
|
|
516
414
|
});
|
|
517
415
|
});
|
|
518
|
-
// Endpoint to download the matterbridge plugin config files
|
|
519
416
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
520
417
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
521
418
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
522
|
-
// 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')));
|
|
523
419
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
524
420
|
if (error) {
|
|
525
421
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
@@ -527,7 +423,6 @@ export class Frontend {
|
|
|
527
423
|
}
|
|
528
424
|
});
|
|
529
425
|
});
|
|
530
|
-
// Endpoint to download the matterbridge plugin config files
|
|
531
426
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
532
427
|
this.log.debug('The frontend sent /api/download-backup');
|
|
533
428
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -537,7 +432,6 @@ export class Frontend {
|
|
|
537
432
|
}
|
|
538
433
|
});
|
|
539
434
|
});
|
|
540
|
-
// Endpoint to receive commands
|
|
541
435
|
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
542
436
|
const command = req.params.command;
|
|
543
437
|
let param = req.params.param;
|
|
@@ -547,15 +441,13 @@ export class Frontend {
|
|
|
547
441
|
return;
|
|
548
442
|
}
|
|
549
443
|
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
550
|
-
// Handle the command setpassword from Settings
|
|
551
444
|
if (command === 'setpassword') {
|
|
552
|
-
const password = param.slice(1, -1);
|
|
445
|
+
const password = param.slice(1, -1);
|
|
553
446
|
this.log.debug('setpassword', param, password);
|
|
554
447
|
await this.matterbridge.nodeContext?.set('password', password);
|
|
555
448
|
res.json({ message: 'Command received' });
|
|
556
449
|
return;
|
|
557
450
|
}
|
|
558
|
-
// Handle the command setbridgemode from Settings
|
|
559
451
|
if (command === 'setbridgemode') {
|
|
560
452
|
this.log.debug(`setbridgemode: ${param}`);
|
|
561
453
|
this.wssSendRestartRequired();
|
|
@@ -563,7 +455,6 @@ export class Frontend {
|
|
|
563
455
|
res.json({ message: 'Command received' });
|
|
564
456
|
return;
|
|
565
457
|
}
|
|
566
|
-
// Handle the command backup from Settings
|
|
567
458
|
if (command === 'backup') {
|
|
568
459
|
this.log.notice(`Prepairing the backup...`);
|
|
569
460
|
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
|
|
@@ -571,26 +462,25 @@ export class Frontend {
|
|
|
571
462
|
res.json({ message: 'Command received' });
|
|
572
463
|
return;
|
|
573
464
|
}
|
|
574
|
-
// Handle the command setmbloglevel from Settings
|
|
575
465
|
if (command === 'setmbloglevel') {
|
|
576
466
|
this.log.debug('Matterbridge log level:', param);
|
|
577
467
|
if (param === 'Debug') {
|
|
578
|
-
this.log.logLevel = "debug"
|
|
468
|
+
this.log.logLevel = "debug";
|
|
579
469
|
}
|
|
580
470
|
else if (param === 'Info') {
|
|
581
|
-
this.log.logLevel = "info"
|
|
471
|
+
this.log.logLevel = "info";
|
|
582
472
|
}
|
|
583
473
|
else if (param === 'Notice') {
|
|
584
|
-
this.log.logLevel = "notice"
|
|
474
|
+
this.log.logLevel = "notice";
|
|
585
475
|
}
|
|
586
476
|
else if (param === 'Warn') {
|
|
587
|
-
this.log.logLevel = "warn"
|
|
477
|
+
this.log.logLevel = "warn";
|
|
588
478
|
}
|
|
589
479
|
else if (param === 'Error') {
|
|
590
|
-
this.log.logLevel = "error"
|
|
480
|
+
this.log.logLevel = "error";
|
|
591
481
|
}
|
|
592
482
|
else if (param === 'Fatal') {
|
|
593
|
-
this.log.logLevel = "fatal"
|
|
483
|
+
this.log.logLevel = "fatal";
|
|
594
484
|
}
|
|
595
485
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
596
486
|
this.matterbridge.log.logLevel = this.log.logLevel;
|
|
@@ -600,13 +490,12 @@ export class Frontend {
|
|
|
600
490
|
for (const plugin of this.matterbridge.plugins) {
|
|
601
491
|
if (!plugin.platform || !plugin.platform.config)
|
|
602
492
|
continue;
|
|
603
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug"
|
|
604
|
-
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);
|
|
605
495
|
}
|
|
606
496
|
res.json({ message: 'Command received' });
|
|
607
497
|
return;
|
|
608
498
|
}
|
|
609
|
-
// Handle the command setmbloglevel from Settings
|
|
610
499
|
if (command === 'setmjloglevel') {
|
|
611
500
|
this.log.debug('Matter.js log level:', param);
|
|
612
501
|
if (param === 'Debug') {
|
|
@@ -631,34 +520,30 @@ export class Frontend {
|
|
|
631
520
|
res.json({ message: 'Command received' });
|
|
632
521
|
return;
|
|
633
522
|
}
|
|
634
|
-
// Handle the command setmdnsinterface from Settings
|
|
635
523
|
if (command === 'setmdnsinterface') {
|
|
636
|
-
param = param.slice(1, -1);
|
|
524
|
+
param = param.slice(1, -1);
|
|
637
525
|
this.matterbridge.matterbridgeInformation.mattermdnsinterface = param;
|
|
638
526
|
this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
|
|
639
527
|
await this.matterbridge.nodeContext?.set('mattermdnsinterface', param);
|
|
640
528
|
res.json({ message: 'Command received' });
|
|
641
529
|
return;
|
|
642
530
|
}
|
|
643
|
-
// Handle the command setipv4address from Settings
|
|
644
531
|
if (command === 'setipv4address') {
|
|
645
|
-
param = param.slice(1, -1);
|
|
532
|
+
param = param.slice(1, -1);
|
|
646
533
|
this.matterbridge.matterbridgeInformation.matteripv4address = param;
|
|
647
534
|
this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
|
|
648
535
|
await this.matterbridge.nodeContext?.set('matteripv4address', param);
|
|
649
536
|
res.json({ message: 'Command received' });
|
|
650
537
|
return;
|
|
651
538
|
}
|
|
652
|
-
// Handle the command setipv6address from Settings
|
|
653
539
|
if (command === 'setipv6address') {
|
|
654
|
-
param = param.slice(1, -1);
|
|
540
|
+
param = param.slice(1, -1);
|
|
655
541
|
this.matterbridge.matterbridgeInformation.matteripv6address = param;
|
|
656
542
|
this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
|
|
657
543
|
await this.matterbridge.nodeContext?.set('matteripv6address', param);
|
|
658
544
|
res.json({ message: 'Command received' });
|
|
659
545
|
return;
|
|
660
546
|
}
|
|
661
|
-
// Handle the command setmatterport from Settings
|
|
662
547
|
if (command === 'setmatterport') {
|
|
663
548
|
const port = Math.min(Math.max(parseInt(param), 5540), 5560);
|
|
664
549
|
this.matterbridge.matterbridgeInformation.matterPort = port;
|
|
@@ -667,7 +552,6 @@ export class Frontend {
|
|
|
667
552
|
res.json({ message: 'Command received' });
|
|
668
553
|
return;
|
|
669
554
|
}
|
|
670
|
-
// Handle the command setmatterdiscriminator from Settings
|
|
671
555
|
if (command === 'setmatterdiscriminator') {
|
|
672
556
|
const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
|
|
673
557
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
|
|
@@ -676,7 +560,6 @@ export class Frontend {
|
|
|
676
560
|
res.json({ message: 'Command received' });
|
|
677
561
|
return;
|
|
678
562
|
}
|
|
679
|
-
// Handle the command setmatterpasscode from Settings
|
|
680
563
|
if (command === 'setmatterpasscode') {
|
|
681
564
|
const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
|
|
682
565
|
this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
|
|
@@ -685,20 +568,17 @@ export class Frontend {
|
|
|
685
568
|
res.json({ message: 'Command received' });
|
|
686
569
|
return;
|
|
687
570
|
}
|
|
688
|
-
// Handle the command setmbloglevel from Settings
|
|
689
571
|
if (command === 'setmblogfile') {
|
|
690
572
|
this.log.debug('Matterbridge file log:', param);
|
|
691
573
|
this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
|
|
692
574
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
|
|
693
|
-
// Create the file logger for matterbridge
|
|
694
575
|
if (param === 'true')
|
|
695
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug"
|
|
576
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
|
|
696
577
|
else
|
|
697
578
|
AnsiLogger.setGlobalLogfile(undefined);
|
|
698
579
|
res.json({ message: 'Command received' });
|
|
699
580
|
return;
|
|
700
581
|
}
|
|
701
|
-
// Handle the command setmbloglevel from Settings
|
|
702
582
|
if (command === 'setmjlogfile') {
|
|
703
583
|
this.log.debug('Matter file log:', param);
|
|
704
584
|
this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
|
|
@@ -725,48 +605,40 @@ export class Frontend {
|
|
|
725
605
|
res.json({ message: 'Command received' });
|
|
726
606
|
return;
|
|
727
607
|
}
|
|
728
|
-
// Handle the command unregister from Settings
|
|
729
608
|
if (command === 'unregister') {
|
|
730
609
|
await this.matterbridge.unregisterAndShutdownProcess();
|
|
731
610
|
res.json({ message: 'Command received' });
|
|
732
611
|
return;
|
|
733
612
|
}
|
|
734
|
-
// Handle the command reset from Settings
|
|
735
613
|
if (command === 'reset') {
|
|
736
614
|
await this.matterbridge.shutdownProcessAndReset();
|
|
737
615
|
res.json({ message: 'Command received' });
|
|
738
616
|
return;
|
|
739
617
|
}
|
|
740
|
-
// Handle the command factoryreset from Settings
|
|
741
618
|
if (command === 'factoryreset') {
|
|
742
619
|
await this.matterbridge.shutdownProcessAndFactoryReset();
|
|
743
620
|
res.json({ message: 'Command received' });
|
|
744
621
|
return;
|
|
745
622
|
}
|
|
746
|
-
// Handle the command shutdown from Header
|
|
747
623
|
if (command === 'shutdown') {
|
|
748
624
|
await this.matterbridge.shutdownProcess();
|
|
749
625
|
res.json({ message: 'Command received' });
|
|
750
626
|
return;
|
|
751
627
|
}
|
|
752
|
-
// Handle the command restart from Header
|
|
753
628
|
if (command === 'restart') {
|
|
754
629
|
await this.matterbridge.restartProcess();
|
|
755
630
|
res.json({ message: 'Command received' });
|
|
756
631
|
return;
|
|
757
632
|
}
|
|
758
|
-
// Handle the command update from Header
|
|
759
633
|
if (command === 'update') {
|
|
760
634
|
await this.matterbridge.updateProcess();
|
|
761
635
|
this.wssSendRestartRequired();
|
|
762
636
|
res.json({ message: 'Command received' });
|
|
763
637
|
return;
|
|
764
638
|
}
|
|
765
|
-
// Handle the command saveconfig from Home
|
|
766
639
|
if (command === 'saveconfig') {
|
|
767
640
|
param = param.replace(/\*/g, '\\');
|
|
768
641
|
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
769
|
-
// console.log('Req.body:', JSON.stringify(req.body, null, 2));
|
|
770
642
|
if (!this.matterbridge.plugins.has(param)) {
|
|
771
643
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
772
644
|
}
|
|
@@ -780,58 +652,49 @@ export class Frontend {
|
|
|
780
652
|
res.json({ message: 'Command received' });
|
|
781
653
|
return;
|
|
782
654
|
}
|
|
783
|
-
// Handle the command installplugin from Home
|
|
784
655
|
if (command === 'installplugin') {
|
|
785
656
|
param = param.replace(/\*/g, '\\');
|
|
786
657
|
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
787
658
|
try {
|
|
788
659
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
789
660
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
790
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
791
661
|
}
|
|
792
662
|
catch (error) {
|
|
793
663
|
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
794
664
|
}
|
|
795
665
|
this.wssSendRestartRequired();
|
|
796
666
|
param = param.split('@')[0];
|
|
797
|
-
// Also add the plugin to matterbridge so no return!
|
|
798
667
|
if (param === 'matterbridge') {
|
|
799
|
-
// 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
|
|
800
668
|
res.json({ message: 'Command received' });
|
|
801
669
|
return;
|
|
802
670
|
}
|
|
803
671
|
}
|
|
804
|
-
// Handle the command addplugin from Home
|
|
805
672
|
if (command === 'addplugin' || command === 'installplugin') {
|
|
806
673
|
param = param.replace(/\*/g, '\\');
|
|
807
674
|
const plugin = await this.matterbridge.plugins.add(param);
|
|
808
675
|
if (plugin) {
|
|
809
676
|
if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
810
|
-
// 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
|
|
811
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
812
677
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
813
678
|
}
|
|
814
|
-
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);
|
|
815
680
|
}
|
|
816
681
|
res.json({ message: 'Command received' });
|
|
817
682
|
this.wssSendRefreshRequired();
|
|
818
683
|
return;
|
|
819
684
|
}
|
|
820
|
-
// Handle the command removeplugin from Home
|
|
821
685
|
if (command === 'removeplugin') {
|
|
822
686
|
if (!this.matterbridge.plugins.has(param)) {
|
|
823
687
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
824
688
|
}
|
|
825
689
|
else {
|
|
826
690
|
const plugin = this.matterbridge.plugins.get(param);
|
|
827
|
-
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);
|
|
828
692
|
await this.matterbridge.plugins.remove(param);
|
|
829
693
|
}
|
|
830
694
|
res.json({ message: 'Command received' });
|
|
831
695
|
this.wssSendRefreshRequired();
|
|
832
696
|
return;
|
|
833
697
|
}
|
|
834
|
-
// Handle the command enableplugin from Home
|
|
835
698
|
if (command === 'enableplugin') {
|
|
836
699
|
if (!this.matterbridge.plugins.has(param)) {
|
|
837
700
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -849,17 +712,15 @@ export class Frontend {
|
|
|
849
712
|
plugin.addedDevices = undefined;
|
|
850
713
|
await this.matterbridge.plugins.enable(param);
|
|
851
714
|
if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
|
|
852
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
853
715
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
854
716
|
}
|
|
855
|
-
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);
|
|
856
718
|
}
|
|
857
719
|
}
|
|
858
720
|
res.json({ message: 'Command received' });
|
|
859
721
|
this.wssSendRefreshRequired();
|
|
860
722
|
return;
|
|
861
723
|
}
|
|
862
|
-
// Handle the command disableplugin from Home
|
|
863
724
|
if (command === 'disableplugin') {
|
|
864
725
|
if (!this.matterbridge.plugins.has(param)) {
|
|
865
726
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -867,7 +728,7 @@ export class Frontend {
|
|
|
867
728
|
else {
|
|
868
729
|
const plugin = this.matterbridge.plugins.get(param);
|
|
869
730
|
if (plugin && plugin.enabled) {
|
|
870
|
-
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);
|
|
871
732
|
await this.matterbridge.plugins.disable(param);
|
|
872
733
|
}
|
|
873
734
|
}
|
|
@@ -876,7 +737,6 @@ export class Frontend {
|
|
|
876
737
|
return;
|
|
877
738
|
}
|
|
878
739
|
});
|
|
879
|
-
// Fallback for routing (must be the last route)
|
|
880
740
|
this.expressApp.get('*', (req, res) => {
|
|
881
741
|
this.log.debug('The frontend sent:', req.url);
|
|
882
742
|
this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -885,7 +745,6 @@ export class Frontend {
|
|
|
885
745
|
this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
886
746
|
}
|
|
887
747
|
async stop() {
|
|
888
|
-
// Start the memory check. This will not allow the process to exit but will log the memory usage for 5 minutes.
|
|
889
748
|
if (hasParameter('memorycheck')) {
|
|
890
749
|
await new Promise((resolve) => {
|
|
891
750
|
this.log.debug(`***Memory check started for ${getIntParameter('memorycheck') ?? 5 * 60 * 1000} ms`);
|
|
@@ -895,29 +754,24 @@ export class Frontend {
|
|
|
895
754
|
}, getIntParameter('memorycheck') ?? 5 * 60 * 1000);
|
|
896
755
|
});
|
|
897
756
|
}
|
|
898
|
-
// Close the http server
|
|
899
757
|
if (this.httpServer) {
|
|
900
758
|
this.httpServer.close();
|
|
901
759
|
this.httpServer.removeAllListeners();
|
|
902
760
|
this.httpServer = undefined;
|
|
903
761
|
this.log.debug('Frontend http server closed successfully');
|
|
904
762
|
}
|
|
905
|
-
// Close the https server
|
|
906
763
|
if (this.httpsServer) {
|
|
907
764
|
this.httpsServer.close();
|
|
908
765
|
this.httpsServer.removeAllListeners();
|
|
909
766
|
this.httpsServer = undefined;
|
|
910
767
|
this.log.debug('Frontend https server closed successfully');
|
|
911
768
|
}
|
|
912
|
-
// Remove listeners from the express app
|
|
913
769
|
if (this.expressApp) {
|
|
914
770
|
this.expressApp.removeAllListeners();
|
|
915
771
|
this.expressApp = undefined;
|
|
916
772
|
this.log.debug('Frontend app closed successfully');
|
|
917
773
|
}
|
|
918
|
-
// Close the WebSocket server
|
|
919
774
|
if (this.webSocketServer) {
|
|
920
|
-
// Close all active connections
|
|
921
775
|
this.webSocketServer.clients.forEach((client) => {
|
|
922
776
|
if (client.readyState === WebSocket.OPEN) {
|
|
923
777
|
client.close();
|
|
@@ -933,12 +787,10 @@ export class Frontend {
|
|
|
933
787
|
});
|
|
934
788
|
this.webSocketServer = undefined;
|
|
935
789
|
}
|
|
936
|
-
// Stop the memory dump interval
|
|
937
790
|
if (hasParameter('memorydump')) {
|
|
938
791
|
this.stopCpuMemoryDump();
|
|
939
792
|
}
|
|
940
793
|
}
|
|
941
|
-
// Function to format bytes to KB or MB
|
|
942
794
|
formatMemoryUsage = (bytes) => {
|
|
943
795
|
const kb = bytes / 1024;
|
|
944
796
|
const mb = kb / 1024;
|
|
@@ -950,10 +802,9 @@ export class Frontend {
|
|
|
950
802
|
const interval = () => {
|
|
951
803
|
const currCpus = os.cpus();
|
|
952
804
|
if (currCpus.length !== this.prevCpus.length) {
|
|
953
|
-
this.prevCpus = currCpus;
|
|
805
|
+
this.prevCpus = currCpus;
|
|
954
806
|
}
|
|
955
807
|
let totalIdle = 0, totalTick = 0;
|
|
956
|
-
// Get the cpu usage
|
|
957
808
|
this.prevCpus.forEach((prevCpu, i) => {
|
|
958
809
|
const currCpu = currCpus[i];
|
|
959
810
|
const idleDiff = currCpu.times.idle - prevCpu.times.idle;
|
|
@@ -963,7 +814,6 @@ export class Frontend {
|
|
|
963
814
|
});
|
|
964
815
|
const cpuUsage = (100 - (totalIdle / totalTick) * 100).toFixed(2);
|
|
965
816
|
this.prevCpus = currCpus;
|
|
966
|
-
// Get the memory usage
|
|
967
817
|
const memoryUsageRaw = process.memoryUsage();
|
|
968
818
|
this.memoryData.push({ ...memoryUsageRaw, cpu: cpuUsage });
|
|
969
819
|
const memoryUsage = {
|
|
@@ -996,21 +846,14 @@ export class Frontend {
|
|
|
996
846
|
external: this.formatMemoryUsage(memory.external),
|
|
997
847
|
arrayBuffers: this.formatMemoryUsage(memory.arrayBuffers),
|
|
998
848
|
};
|
|
999
|
-
// eslint-disable-next-line no-console
|
|
1000
849
|
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}`);
|
|
1001
850
|
}
|
|
1002
851
|
this.memoryData = [];
|
|
1003
852
|
this.prevCpus = [];
|
|
1004
853
|
}
|
|
1005
|
-
/**
|
|
1006
|
-
* Retrieves the api settings data.
|
|
1007
|
-
* @returns {Promise<object>} A promise that resolve in the api settings object.
|
|
1008
|
-
*/
|
|
1009
854
|
async getApiSettings() {
|
|
1010
|
-
// Update the system information
|
|
1011
855
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
1012
856
|
this.matterbridge.systemInformation.heap = this.formatMemoryUsage(process.memoryUsage().heapUsed) + ' / ' + this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
1013
|
-
// Update the matterbridge information
|
|
1014
857
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
1015
858
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
1016
859
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -1029,11 +872,6 @@ export class Frontend {
|
|
|
1029
872
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
1030
873
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
1031
874
|
}
|
|
1032
|
-
/**
|
|
1033
|
-
* Retrieves the cluster text description from a given device.
|
|
1034
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
1035
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
1036
|
-
*/
|
|
1037
875
|
getClusterTextFromDevice(device) {
|
|
1038
876
|
const getAttribute = (device, cluster, attribute) => {
|
|
1039
877
|
let value = undefined;
|
|
@@ -1072,7 +910,6 @@ export class Frontend {
|
|
|
1072
910
|
};
|
|
1073
911
|
let attributes = '';
|
|
1074
912
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1075
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
1076
913
|
if (typeof attributeValue === 'undefined')
|
|
1077
914
|
return;
|
|
1078
915
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1150,13 +987,8 @@ export class Frontend {
|
|
|
1150
987
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1151
988
|
attributes += `${getUserLabel(device)} `;
|
|
1152
989
|
});
|
|
1153
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1154
990
|
return attributes.trimStart().trimEnd();
|
|
1155
991
|
}
|
|
1156
|
-
/**
|
|
1157
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
1158
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
1159
|
-
*/
|
|
1160
992
|
getBaseRegisteredPlugins() {
|
|
1161
993
|
const baseRegisteredPlugins = [];
|
|
1162
994
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -1187,14 +1019,6 @@ export class Frontend {
|
|
|
1187
1019
|
}
|
|
1188
1020
|
return baseRegisteredPlugins;
|
|
1189
1021
|
}
|
|
1190
|
-
/**
|
|
1191
|
-
* Handles incoming websocket messages for the Matterbridge.
|
|
1192
|
-
*
|
|
1193
|
-
* @param {Matterbridge} this - The Matterbridge instance.
|
|
1194
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1195
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1196
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1197
|
-
*/
|
|
1198
1022
|
async wsMessageHandler(client, message) {
|
|
1199
1023
|
let data;
|
|
1200
1024
|
try {
|
|
@@ -1282,10 +1106,8 @@ export class Frontend {
|
|
|
1282
1106
|
else if (data.method === '/api/devices') {
|
|
1283
1107
|
const devices = [];
|
|
1284
1108
|
this.matterbridge.devices.forEach(async (device) => {
|
|
1285
|
-
// Filter by pluginName if provided
|
|
1286
1109
|
if (data.params.pluginName && data.params.pluginName !== device.plugin)
|
|
1287
1110
|
return;
|
|
1288
|
-
// Check if the device has the required properties
|
|
1289
1111
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
|
|
1290
1112
|
return;
|
|
1291
1113
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -1368,7 +1190,6 @@ export class Frontend {
|
|
|
1368
1190
|
});
|
|
1369
1191
|
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
1370
1192
|
deviceTypes = [];
|
|
1371
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1372
1193
|
const name = childEndpoint.endpoint?.id;
|
|
1373
1194
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
1374
1195
|
clusterServers.forEach((clusterServer) => {
|
|
@@ -1451,73 +1272,44 @@ export class Frontend {
|
|
|
1451
1272
|
return;
|
|
1452
1273
|
}
|
|
1453
1274
|
}
|
|
1454
|
-
/**
|
|
1455
|
-
* Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1456
|
-
*
|
|
1457
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1458
|
-
* @param {string} time - The time string of the message
|
|
1459
|
-
* @param {string} name - The logger name of the message
|
|
1460
|
-
* @param {string} message - The content of the message.
|
|
1461
|
-
*/
|
|
1462
1275
|
wssSendMessage(level, time, name, message) {
|
|
1463
1276
|
if (!level || !time || !name || !message)
|
|
1464
1277
|
return;
|
|
1465
|
-
// Remove ANSI escape codes from the message
|
|
1466
|
-
// eslint-disable-next-line no-control-regex
|
|
1467
1278
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1468
|
-
// Remove leading asterisks from the message
|
|
1469
1279
|
message = message.replace(/^\*+/, '');
|
|
1470
|
-
// Replace all occurrences of \t and \n
|
|
1471
1280
|
message = message.replace(/[\t\n]/g, '');
|
|
1472
|
-
// Remove non-printable characters
|
|
1473
|
-
// eslint-disable-next-line no-control-regex
|
|
1474
1281
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1475
|
-
// Replace all occurrences of \" with "
|
|
1476
1282
|
message = message.replace(/\\"/g, '"');
|
|
1477
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1478
1283
|
const maxContinuousLength = 100;
|
|
1479
1284
|
const keepStartLength = 20;
|
|
1480
1285
|
const keepEndLength = 20;
|
|
1481
|
-
// Split the message into words
|
|
1482
1286
|
message = message
|
|
1483
1287
|
.split(' ')
|
|
1484
1288
|
.map((word) => {
|
|
1485
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1486
1289
|
if (word.length > maxContinuousLength) {
|
|
1487
1290
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1488
1291
|
}
|
|
1489
1292
|
return word;
|
|
1490
1293
|
})
|
|
1491
1294
|
.join(' ');
|
|
1492
|
-
// Send the message to all connected clients
|
|
1493
1295
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1494
1296
|
if (client.readyState === WebSocket.OPEN) {
|
|
1495
1297
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1496
1298
|
}
|
|
1497
1299
|
});
|
|
1498
1300
|
}
|
|
1499
|
-
/**
|
|
1500
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1501
|
-
*
|
|
1502
|
-
*/
|
|
1503
1301
|
wssSendRefreshRequired() {
|
|
1504
1302
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1505
1303
|
this.matterbridge.matterbridgeInformation.refreshRequired = true;
|
|
1506
|
-
// Send the message to all connected clients
|
|
1507
1304
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1508
1305
|
if (client.readyState === WebSocket.OPEN) {
|
|
1509
1306
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: {} }));
|
|
1510
1307
|
}
|
|
1511
1308
|
});
|
|
1512
1309
|
}
|
|
1513
|
-
/**
|
|
1514
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1515
|
-
*
|
|
1516
|
-
*/
|
|
1517
1310
|
wssSendRestartRequired() {
|
|
1518
1311
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1519
1312
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1520
|
-
// Send the message to all connected clients
|
|
1521
1313
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1522
1314
|
if (client.readyState === WebSocket.OPEN) {
|
|
1523
1315
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
@@ -1525,4 +1317,3 @@ export class Frontend {
|
|
|
1525
1317
|
});
|
|
1526
1318
|
}
|
|
1527
1319
|
}
|
|
1528
|
-
//# sourceMappingURL=frontend.js.map
|