matterbridge 2.2.0-dev.5 → 2.2.0-dev.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -8
- package/dist/cli.js +2 -37
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -94
- package/dist/frontend.js +42 -265
- package/dist/index.js +2 -29
- 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 +1 -3
- package/dist/matterbridge.js +51 -720
- 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 +9 -118
- package/dist/matterbridgePlatform.js +7 -140
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +3 -229
- package/dist/shelly.js +33 -136
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -45
- package/dist/utils/colorUtils.js +2 -205
- package/dist/utils/copyDirectory.js +1 -37
- package/dist/utils/createZip.js +2 -42
- package/dist/utils/deepCopy.js +0 -40
- package/dist/utils/deepEqual.js +1 -65
- package/dist/utils/export.js +0 -1
- package/dist/utils/isvalid.js +0 -86
- package/dist/utils/network.js +5 -77
- package/dist/utils/parameter.js +0 -41
- package/dist/utils/wait.js +5 -48
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.f60aae10.js → main.8240902c.js} +3 -3
- package/frontend/build/static/js/main.8240902c.js.map +1 -0
- package/npm-shrinkwrap.json +44 -44
- package/package.json +2 -2
- package/dist/cli.d.ts +0 -28
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cluster/export.d.ts.map +0 -1
- package/dist/cluster/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -109
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -172
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -410
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -148
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -827
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -123
- 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.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/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/parameter.d.ts.map +0 -1
- package/dist/utils/parameter.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -43
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
- package/frontend/build/static/js/main.f60aae10.js.map +0 -1
- /package/frontend/build/static/js/{main.f60aae10.js.LICENSE.txt → main.8240902c.js.LICENSE.txt} +0 -0
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.2
|
|
8
|
-
*
|
|
9
|
-
* Copyright 2025, 2026, 2027 Luca Liguori.
|
|
10
|
-
*
|
|
11
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
-
* you may not use this file except in compliance with the License.
|
|
13
|
-
* You may obtain a copy of the License at
|
|
14
|
-
*
|
|
15
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
*
|
|
17
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
-
* See the License for the specific language governing permissions and
|
|
21
|
-
* limitations under the License. *
|
|
22
|
-
*/
|
|
23
|
-
// @matter
|
|
24
1
|
import { EndpointServer, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat } from '@matter/main';
|
|
25
|
-
// Node modules
|
|
26
2
|
import { createServer } from 'node:http';
|
|
27
3
|
import https from 'https';
|
|
28
4
|
import express from 'express';
|
|
@@ -30,70 +6,19 @@ import WebSocket, { WebSocketServer } from 'ws';
|
|
|
30
6
|
import os from 'node:os';
|
|
31
7
|
import path from 'node:path';
|
|
32
8
|
import { promises as fs } from 'node:fs';
|
|
33
|
-
// AnsiLogger module
|
|
34
9
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';
|
|
35
|
-
// Matterbridge
|
|
36
10
|
import { createZip, deepCopy, isValidNumber, isValidObject, isValidString } from './utils/export.js';
|
|
37
11
|
import { plg } from './matterbridgeTypes.js';
|
|
38
12
|
import { hasParameter } from './utils/export.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
|
-
* Websocket message ID indicating a cpu update.
|
|
56
|
-
* @constant {number}
|
|
57
|
-
*/
|
|
58
16
|
export const WS_ID_CPU_UPDATE = 3;
|
|
59
|
-
/**
|
|
60
|
-
* Websocket message ID indicating a memory update.
|
|
61
|
-
* @constant {number}
|
|
62
|
-
*/
|
|
63
17
|
export const WS_ID_MEMORY_UPDATE = 4;
|
|
64
|
-
/**
|
|
65
|
-
* Websocket message ID indicating an uptime update.
|
|
66
|
-
* @constant {number}
|
|
67
|
-
*/
|
|
68
18
|
export const WS_ID_UPTIME_UPDATE = 5;
|
|
69
|
-
/**
|
|
70
|
-
* Websocket message ID indicating a memory update.
|
|
71
|
-
* @constant {number}
|
|
72
|
-
*/
|
|
73
19
|
export const WS_ID_SNACKBAR = 6;
|
|
74
|
-
/**
|
|
75
|
-
* Websocket message ID indicating a shelly system update.
|
|
76
|
-
* check:
|
|
77
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/check
|
|
78
|
-
* perform:
|
|
79
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/perform
|
|
80
|
-
* @constant {number}
|
|
81
|
-
*/
|
|
82
20
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
83
|
-
/**
|
|
84
|
-
* Websocket message ID indicating a shelly main update.
|
|
85
|
-
* check:
|
|
86
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/check
|
|
87
|
-
* perform:
|
|
88
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/perform
|
|
89
|
-
* @constant {number}
|
|
90
|
-
*/
|
|
91
21
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
92
|
-
/**
|
|
93
|
-
* Initializes the frontend of Matterbridge.
|
|
94
|
-
*
|
|
95
|
-
* @param port The port number to run the frontend server on. Default is 8283.
|
|
96
|
-
*/
|
|
97
22
|
export class Frontend {
|
|
98
23
|
matterbridge;
|
|
99
24
|
log;
|
|
@@ -110,7 +35,7 @@ export class Frontend {
|
|
|
110
35
|
memoryTimeout;
|
|
111
36
|
constructor(matterbridge) {
|
|
112
37
|
this.matterbridge = matterbridge;
|
|
113
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
38
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
114
39
|
}
|
|
115
40
|
set logLevel(logLevel) {
|
|
116
41
|
this.log.logLevel = logLevel;
|
|
@@ -118,21 +43,10 @@ export class Frontend {
|
|
|
118
43
|
async start(port = 8283) {
|
|
119
44
|
this.port = port;
|
|
120
45
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
121
|
-
// Create the express app that serves the frontend
|
|
122
46
|
this.expressApp = express();
|
|
123
|
-
// Log all requests to the server for debugging
|
|
124
|
-
/*
|
|
125
|
-
this.expressApp.use((req, res, next) => {
|
|
126
|
-
this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
|
|
127
|
-
next();
|
|
128
|
-
});
|
|
129
|
-
*/
|
|
130
|
-
// Serve static files from '/static' endpoint
|
|
131
47
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
132
48
|
if (!hasParameter('ssl')) {
|
|
133
|
-
// Create an HTTP server and attach the express app
|
|
134
49
|
this.httpServer = createServer(this.expressApp);
|
|
135
|
-
// Listen on the specified port
|
|
136
50
|
if (hasParameter('ingress')) {
|
|
137
51
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
138
52
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -146,7 +60,6 @@ export class Frontend {
|
|
|
146
60
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
147
61
|
});
|
|
148
62
|
}
|
|
149
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
150
63
|
this.httpServer.on('error', (error) => {
|
|
151
64
|
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
152
65
|
switch (error.code) {
|
|
@@ -162,7 +75,6 @@ export class Frontend {
|
|
|
162
75
|
});
|
|
163
76
|
}
|
|
164
77
|
else {
|
|
165
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
166
78
|
let cert;
|
|
167
79
|
try {
|
|
168
80
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -190,9 +102,7 @@ export class Frontend {
|
|
|
190
102
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
191
103
|
}
|
|
192
104
|
const serverOptions = { cert, key, ca };
|
|
193
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
194
105
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
195
|
-
// Listen on the specified port
|
|
196
106
|
if (hasParameter('ingress')) {
|
|
197
107
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
198
108
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -206,7 +116,6 @@ export class Frontend {
|
|
|
206
116
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
207
117
|
});
|
|
208
118
|
}
|
|
209
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
210
119
|
this.httpsServer.on('error', (error) => {
|
|
211
120
|
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
212
121
|
switch (error.code) {
|
|
@@ -223,18 +132,16 @@ export class Frontend {
|
|
|
223
132
|
}
|
|
224
133
|
if (this.initializeError)
|
|
225
134
|
return;
|
|
226
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
227
135
|
const wssPort = this.port;
|
|
228
136
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
229
137
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
230
138
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
231
139
|
const clientIp = request.socket.remoteAddress;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
140
|
+
let callbackLogLevel = "notice";
|
|
141
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
142
|
+
callbackLogLevel = "info";
|
|
143
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
144
|
+
callbackLogLevel = "debug";
|
|
238
145
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
239
146
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
240
147
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -268,7 +175,6 @@ export class Frontend {
|
|
|
268
175
|
this.webSocketServer.on('error', (ws, error) => {
|
|
269
176
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
270
177
|
});
|
|
271
|
-
// Subscribe to cli events
|
|
272
178
|
const { cliEmitter } = await import('./cli.js');
|
|
273
179
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
274
180
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -279,7 +185,6 @@ export class Frontend {
|
|
|
279
185
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
280
186
|
this.wssSendCpuUpdate(cpuUsage);
|
|
281
187
|
});
|
|
282
|
-
// Endpoint to validate login code
|
|
283
188
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
284
189
|
const { password } = req.body;
|
|
285
190
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -298,27 +203,23 @@ export class Frontend {
|
|
|
298
203
|
this.log.warn('/api/login error wrong password');
|
|
299
204
|
res.json({ valid: false });
|
|
300
205
|
}
|
|
301
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
302
206
|
}
|
|
303
207
|
catch (error) {
|
|
304
208
|
this.log.error('/api/login error getting password');
|
|
305
209
|
res.json({ valid: false });
|
|
306
210
|
}
|
|
307
211
|
});
|
|
308
|
-
// Endpoint to provide health check
|
|
309
212
|
this.expressApp.get('/health', (req, res) => {
|
|
310
213
|
this.log.debug('Express received /health');
|
|
311
214
|
const healthStatus = {
|
|
312
|
-
status: 'ok',
|
|
313
|
-
uptime: process.uptime(),
|
|
314
|
-
timestamp: new Date().toISOString(),
|
|
215
|
+
status: 'ok',
|
|
216
|
+
uptime: process.uptime(),
|
|
217
|
+
timestamp: new Date().toISOString(),
|
|
315
218
|
};
|
|
316
219
|
res.status(200).json(healthStatus);
|
|
317
220
|
});
|
|
318
|
-
// Endpoint to provide memory usage details
|
|
319
221
|
this.expressApp.get('/memory', async (req, res) => {
|
|
320
222
|
this.log.debug('Express received /memory');
|
|
321
|
-
// Memory usage from process
|
|
322
223
|
const memoryUsageRaw = process.memoryUsage();
|
|
323
224
|
const memoryUsage = {
|
|
324
225
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -327,13 +228,10 @@ export class Frontend {
|
|
|
327
228
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
328
229
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
329
230
|
};
|
|
330
|
-
// V8 heap statistics
|
|
331
231
|
const { default: v8 } = await import('node:v8');
|
|
332
232
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
333
233
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
334
|
-
// Format heapStats
|
|
335
234
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
336
|
-
// Format heapSpaces
|
|
337
235
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
338
236
|
...space,
|
|
339
237
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -351,7 +249,6 @@ export class Frontend {
|
|
|
351
249
|
};
|
|
352
250
|
res.status(200).json(memoryReport);
|
|
353
251
|
});
|
|
354
|
-
// Endpoint to start advertising the server node
|
|
355
252
|
this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
|
|
356
253
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
357
254
|
if (pairingCodes) {
|
|
@@ -362,22 +259,18 @@ export class Frontend {
|
|
|
362
259
|
res.status(500).json({ error: 'Failed to generate pairing codes' });
|
|
363
260
|
}
|
|
364
261
|
});
|
|
365
|
-
// Endpoint to provide settings
|
|
366
262
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
367
263
|
this.log.debug('The frontend sent /api/settings');
|
|
368
264
|
res.json(await this.getApiSettings());
|
|
369
265
|
});
|
|
370
|
-
// Endpoint to provide plugins
|
|
371
266
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
372
267
|
this.log.debug('The frontend sent /api/plugins');
|
|
373
268
|
res.json(this.getBaseRegisteredPlugins());
|
|
374
269
|
});
|
|
375
|
-
// Endpoint to provide devices
|
|
376
270
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
377
271
|
this.log.debug('The frontend sent /api/devices');
|
|
378
272
|
const devices = [];
|
|
379
273
|
this.matterbridge.devices.forEach(async (device) => {
|
|
380
|
-
// Check if the device has the required properties
|
|
381
274
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
382
275
|
return;
|
|
383
276
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -395,7 +288,6 @@ export class Frontend {
|
|
|
395
288
|
});
|
|
396
289
|
res.json(devices);
|
|
397
290
|
});
|
|
398
|
-
// Endpoint to provide the cluster servers of the devices
|
|
399
291
|
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
400
292
|
const selectedPluginName = req.params.selectedPluginName;
|
|
401
293
|
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
@@ -468,7 +360,6 @@ export class Frontend {
|
|
|
468
360
|
});
|
|
469
361
|
res.json(data);
|
|
470
362
|
});
|
|
471
|
-
// Endpoint to view the log
|
|
472
363
|
this.expressApp.get('/api/view-log', async (req, res) => {
|
|
473
364
|
this.log.debug('The frontend sent /api/log');
|
|
474
365
|
try {
|
|
@@ -481,12 +372,10 @@ export class Frontend {
|
|
|
481
372
|
res.status(500).send('Error reading log file');
|
|
482
373
|
}
|
|
483
374
|
});
|
|
484
|
-
// Endpoint to download the matterbridge log
|
|
485
375
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
486
376
|
this.log.debug('The frontend sent /api/download-mblog');
|
|
487
377
|
try {
|
|
488
378
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
|
|
489
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
490
379
|
}
|
|
491
380
|
catch (error) {
|
|
492
381
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -498,12 +387,10 @@ export class Frontend {
|
|
|
498
387
|
}
|
|
499
388
|
});
|
|
500
389
|
});
|
|
501
|
-
// Endpoint to download the matter log
|
|
502
390
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
503
391
|
this.log.debug('The frontend sent /api/download-mjlog');
|
|
504
392
|
try {
|
|
505
393
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
506
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
507
394
|
}
|
|
508
395
|
catch (error) {
|
|
509
396
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -515,7 +402,21 @@ export class Frontend {
|
|
|
515
402
|
}
|
|
516
403
|
});
|
|
517
404
|
});
|
|
518
|
-
|
|
405
|
+
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
406
|
+
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
407
|
+
try {
|
|
408
|
+
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'Create the Shelly system log before downloading it.');
|
|
412
|
+
}
|
|
413
|
+
res.download(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'shelly.log', (error) => {
|
|
414
|
+
if (error) {
|
|
415
|
+
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
416
|
+
res.status(500).send('Error downloading Shelly system log file');
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
});
|
|
519
420
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
520
421
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
521
422
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
@@ -526,7 +427,6 @@ export class Frontend {
|
|
|
526
427
|
}
|
|
527
428
|
});
|
|
528
429
|
});
|
|
529
|
-
// Endpoint to download the matterbridge storage directory
|
|
530
430
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
531
431
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
532
432
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -537,7 +437,6 @@ export class Frontend {
|
|
|
537
437
|
}
|
|
538
438
|
});
|
|
539
439
|
});
|
|
540
|
-
// Endpoint to download the matterbridge plugin directory
|
|
541
440
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
542
441
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
543
442
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -548,11 +447,9 @@ export class Frontend {
|
|
|
548
447
|
}
|
|
549
448
|
});
|
|
550
449
|
});
|
|
551
|
-
// Endpoint to download the matterbridge plugin config files
|
|
552
450
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
553
451
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
554
452
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
555
|
-
// 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')));
|
|
556
453
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
557
454
|
if (error) {
|
|
558
455
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
@@ -560,7 +457,6 @@ export class Frontend {
|
|
|
560
457
|
}
|
|
561
458
|
});
|
|
562
459
|
});
|
|
563
|
-
// Endpoint to download the matterbridge plugin config files
|
|
564
460
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
565
461
|
this.log.debug('The frontend sent /api/download-backup');
|
|
566
462
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -570,7 +466,6 @@ export class Frontend {
|
|
|
570
466
|
}
|
|
571
467
|
});
|
|
572
468
|
});
|
|
573
|
-
// Endpoint to receive commands
|
|
574
469
|
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
575
470
|
const command = req.params.command;
|
|
576
471
|
let param = req.params.param;
|
|
@@ -580,15 +475,13 @@ export class Frontend {
|
|
|
580
475
|
return;
|
|
581
476
|
}
|
|
582
477
|
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
583
|
-
// Handle the command setpassword from Settings
|
|
584
478
|
if (command === 'setpassword') {
|
|
585
|
-
const password = param.slice(1, -1);
|
|
479
|
+
const password = param.slice(1, -1);
|
|
586
480
|
this.log.debug('setpassword', param, password);
|
|
587
481
|
await this.matterbridge.nodeContext?.set('password', password);
|
|
588
482
|
res.json({ message: 'Command received' });
|
|
589
483
|
return;
|
|
590
484
|
}
|
|
591
|
-
// Handle the command setbridgemode from Settings
|
|
592
485
|
if (command === 'setbridgemode') {
|
|
593
486
|
this.log.debug(`setbridgemode: ${param}`);
|
|
594
487
|
this.wssSendRestartRequired();
|
|
@@ -596,7 +489,6 @@ export class Frontend {
|
|
|
596
489
|
res.json({ message: 'Command received' });
|
|
597
490
|
return;
|
|
598
491
|
}
|
|
599
|
-
// Handle the command backup from Settings
|
|
600
492
|
if (command === 'backup') {
|
|
601
493
|
this.log.notice(`Prepairing the backup...`);
|
|
602
494
|
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
|
|
@@ -605,33 +497,31 @@ export class Frontend {
|
|
|
605
497
|
res.json({ message: 'Command received' });
|
|
606
498
|
return;
|
|
607
499
|
}
|
|
608
|
-
// Handle the command setmbloglevel from Settings
|
|
609
500
|
if (command === 'setmbloglevel') {
|
|
610
501
|
this.log.debug('Matterbridge log level:', param);
|
|
611
502
|
if (param === 'Debug') {
|
|
612
|
-
this.log.logLevel = "debug"
|
|
503
|
+
this.log.logLevel = "debug";
|
|
613
504
|
}
|
|
614
505
|
else if (param === 'Info') {
|
|
615
|
-
this.log.logLevel = "info"
|
|
506
|
+
this.log.logLevel = "info";
|
|
616
507
|
}
|
|
617
508
|
else if (param === 'Notice') {
|
|
618
|
-
this.log.logLevel = "notice"
|
|
509
|
+
this.log.logLevel = "notice";
|
|
619
510
|
}
|
|
620
511
|
else if (param === 'Warn') {
|
|
621
|
-
this.log.logLevel = "warn"
|
|
512
|
+
this.log.logLevel = "warn";
|
|
622
513
|
}
|
|
623
514
|
else if (param === 'Error') {
|
|
624
|
-
this.log.logLevel = "error"
|
|
515
|
+
this.log.logLevel = "error";
|
|
625
516
|
}
|
|
626
517
|
else if (param === 'Fatal') {
|
|
627
|
-
this.log.logLevel = "fatal"
|
|
518
|
+
this.log.logLevel = "fatal";
|
|
628
519
|
}
|
|
629
520
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
630
521
|
await this.matterbridge.setLogLevel(this.log.logLevel);
|
|
631
522
|
res.json({ message: 'Command received' });
|
|
632
523
|
return;
|
|
633
524
|
}
|
|
634
|
-
// Handle the command setmbloglevel from Settings
|
|
635
525
|
if (command === 'setmjloglevel') {
|
|
636
526
|
this.log.debug('Matter.js log level:', param);
|
|
637
527
|
if (param === 'Debug') {
|
|
@@ -656,34 +546,30 @@ export class Frontend {
|
|
|
656
546
|
res.json({ message: 'Command received' });
|
|
657
547
|
return;
|
|
658
548
|
}
|
|
659
|
-
// Handle the command setmdnsinterface from Settings
|
|
660
549
|
if (command === 'setmdnsinterface') {
|
|
661
|
-
param = param.slice(1, -1);
|
|
550
|
+
param = param.slice(1, -1);
|
|
662
551
|
this.matterbridge.matterbridgeInformation.mattermdnsinterface = param;
|
|
663
552
|
this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
|
|
664
553
|
await this.matterbridge.nodeContext?.set('mattermdnsinterface', param);
|
|
665
554
|
res.json({ message: 'Command received' });
|
|
666
555
|
return;
|
|
667
556
|
}
|
|
668
|
-
// Handle the command setipv4address from Settings
|
|
669
557
|
if (command === 'setipv4address') {
|
|
670
|
-
param = param.slice(1, -1);
|
|
558
|
+
param = param.slice(1, -1);
|
|
671
559
|
this.matterbridge.matterbridgeInformation.matteripv4address = param;
|
|
672
560
|
this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
|
|
673
561
|
await this.matterbridge.nodeContext?.set('matteripv4address', param);
|
|
674
562
|
res.json({ message: 'Command received' });
|
|
675
563
|
return;
|
|
676
564
|
}
|
|
677
|
-
// Handle the command setipv6address from Settings
|
|
678
565
|
if (command === 'setipv6address') {
|
|
679
|
-
param = param.slice(1, -1);
|
|
566
|
+
param = param.slice(1, -1);
|
|
680
567
|
this.matterbridge.matterbridgeInformation.matteripv6address = param;
|
|
681
568
|
this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
|
|
682
569
|
await this.matterbridge.nodeContext?.set('matteripv6address', param);
|
|
683
570
|
res.json({ message: 'Command received' });
|
|
684
571
|
return;
|
|
685
572
|
}
|
|
686
|
-
// Handle the command setmatterport from Settings
|
|
687
573
|
if (command === 'setmatterport') {
|
|
688
574
|
const port = Math.min(Math.max(parseInt(param), 5540), 5560);
|
|
689
575
|
this.matterbridge.matterbridgeInformation.matterPort = port;
|
|
@@ -692,7 +578,6 @@ export class Frontend {
|
|
|
692
578
|
res.json({ message: 'Command received' });
|
|
693
579
|
return;
|
|
694
580
|
}
|
|
695
|
-
// Handle the command setmatterdiscriminator from Settings
|
|
696
581
|
if (command === 'setmatterdiscriminator') {
|
|
697
582
|
const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
|
|
698
583
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
|
|
@@ -701,7 +586,6 @@ export class Frontend {
|
|
|
701
586
|
res.json({ message: 'Command received' });
|
|
702
587
|
return;
|
|
703
588
|
}
|
|
704
|
-
// Handle the command setmatterpasscode from Settings
|
|
705
589
|
if (command === 'setmatterpasscode') {
|
|
706
590
|
const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
|
|
707
591
|
this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
|
|
@@ -710,20 +594,17 @@ export class Frontend {
|
|
|
710
594
|
res.json({ message: 'Command received' });
|
|
711
595
|
return;
|
|
712
596
|
}
|
|
713
|
-
// Handle the command setmbloglevel from Settings
|
|
714
597
|
if (command === 'setmblogfile') {
|
|
715
598
|
this.log.debug('Matterbridge file log:', param);
|
|
716
599
|
this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
|
|
717
600
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
|
|
718
|
-
// Create the file logger for matterbridge
|
|
719
601
|
if (param === 'true')
|
|
720
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug"
|
|
602
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
|
|
721
603
|
else
|
|
722
604
|
AnsiLogger.setGlobalLogfile(undefined);
|
|
723
605
|
res.json({ message: 'Command received' });
|
|
724
606
|
return;
|
|
725
607
|
}
|
|
726
|
-
// Handle the command setmbloglevel from Settings
|
|
727
608
|
if (command === 'setmjlogfile') {
|
|
728
609
|
this.log.debug('Matter file log:', param);
|
|
729
610
|
this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
|
|
@@ -750,48 +631,40 @@ export class Frontend {
|
|
|
750
631
|
res.json({ message: 'Command received' });
|
|
751
632
|
return;
|
|
752
633
|
}
|
|
753
|
-
// Handle the command unregister from Settings
|
|
754
634
|
if (command === 'unregister') {
|
|
755
635
|
await this.matterbridge.unregisterAndShutdownProcess();
|
|
756
636
|
res.json({ message: 'Command received' });
|
|
757
637
|
return;
|
|
758
638
|
}
|
|
759
|
-
// Handle the command reset from Settings
|
|
760
639
|
if (command === 'reset') {
|
|
761
640
|
await this.matterbridge.shutdownProcessAndReset();
|
|
762
641
|
res.json({ message: 'Command received' });
|
|
763
642
|
return;
|
|
764
643
|
}
|
|
765
|
-
// Handle the command factoryreset from Settings
|
|
766
644
|
if (command === 'factoryreset') {
|
|
767
645
|
await this.matterbridge.shutdownProcessAndFactoryReset();
|
|
768
646
|
res.json({ message: 'Command received' });
|
|
769
647
|
return;
|
|
770
648
|
}
|
|
771
|
-
// Handle the command shutdown from Header
|
|
772
649
|
if (command === 'shutdown') {
|
|
773
650
|
await this.matterbridge.shutdownProcess();
|
|
774
651
|
res.json({ message: 'Command received' });
|
|
775
652
|
return;
|
|
776
653
|
}
|
|
777
|
-
// Handle the command restart from Header
|
|
778
654
|
if (command === 'restart') {
|
|
779
655
|
await this.matterbridge.restartProcess();
|
|
780
656
|
res.json({ message: 'Command received' });
|
|
781
657
|
return;
|
|
782
658
|
}
|
|
783
|
-
// Handle the command update from Header
|
|
784
659
|
if (command === 'update') {
|
|
785
660
|
await this.matterbridge.updateProcess();
|
|
786
661
|
this.wssSendRestartRequired();
|
|
787
662
|
res.json({ message: 'Command received' });
|
|
788
663
|
return;
|
|
789
664
|
}
|
|
790
|
-
// Handle the command saveconfig from Home
|
|
791
665
|
if (command === 'saveconfig') {
|
|
792
666
|
param = param.replace(/\*/g, '\\');
|
|
793
667
|
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
794
|
-
// console.log('Req.body:', JSON.stringify(req.body, null, 2));
|
|
795
668
|
if (!this.matterbridge.plugins.has(param)) {
|
|
796
669
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
797
670
|
}
|
|
@@ -806,7 +679,6 @@ export class Frontend {
|
|
|
806
679
|
res.json({ message: 'Command received' });
|
|
807
680
|
return;
|
|
808
681
|
}
|
|
809
|
-
// Handle the command installplugin from Home
|
|
810
682
|
if (command === 'installplugin') {
|
|
811
683
|
param = param.replace(/\*/g, '\\');
|
|
812
684
|
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
@@ -815,7 +687,6 @@ export class Frontend {
|
|
|
815
687
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
816
688
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
817
689
|
this.wssSendSnackbarMessage(`Installed package ${param}`);
|
|
818
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
819
690
|
}
|
|
820
691
|
catch (error) {
|
|
821
692
|
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
@@ -824,21 +695,16 @@ export class Frontend {
|
|
|
824
695
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
825
696
|
this.wssSendRestartRequired();
|
|
826
697
|
param = param.split('@')[0];
|
|
827
|
-
// Also add the plugin to matterbridge so no return!
|
|
828
698
|
if (param === 'matterbridge') {
|
|
829
|
-
// 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
|
|
830
699
|
res.json({ message: 'Command received' });
|
|
831
700
|
return;
|
|
832
701
|
}
|
|
833
702
|
}
|
|
834
|
-
// Handle the command addplugin from Home
|
|
835
703
|
if (command === 'addplugin' || command === 'installplugin') {
|
|
836
704
|
param = param.replace(/\*/g, '\\');
|
|
837
705
|
const plugin = await this.matterbridge.plugins.add(param);
|
|
838
706
|
if (plugin) {
|
|
839
707
|
if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
840
|
-
// 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
|
|
841
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
842
708
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
843
709
|
}
|
|
844
710
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
@@ -848,21 +714,19 @@ export class Frontend {
|
|
|
848
714
|
res.json({ message: 'Command received' });
|
|
849
715
|
return;
|
|
850
716
|
}
|
|
851
|
-
// Handle the command removeplugin from Home
|
|
852
717
|
if (command === 'removeplugin') {
|
|
853
718
|
if (!this.matterbridge.plugins.has(param)) {
|
|
854
719
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
855
720
|
}
|
|
856
721
|
else {
|
|
857
722
|
const plugin = this.matterbridge.plugins.get(param);
|
|
858
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
723
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
859
724
|
await this.matterbridge.plugins.remove(param);
|
|
860
725
|
}
|
|
861
726
|
res.json({ message: 'Command received' });
|
|
862
727
|
this.wssSendRefreshRequired();
|
|
863
728
|
return;
|
|
864
729
|
}
|
|
865
|
-
// Handle the command enableplugin from Home
|
|
866
730
|
if (command === 'enableplugin') {
|
|
867
731
|
if (!this.matterbridge.plugins.has(param)) {
|
|
868
732
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -880,7 +744,6 @@ export class Frontend {
|
|
|
880
744
|
plugin.addedDevices = undefined;
|
|
881
745
|
await this.matterbridge.plugins.enable(param);
|
|
882
746
|
if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
|
|
883
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
884
747
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
885
748
|
}
|
|
886
749
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
|
|
@@ -892,7 +755,6 @@ export class Frontend {
|
|
|
892
755
|
this.wssSendRefreshRequired();
|
|
893
756
|
return;
|
|
894
757
|
}
|
|
895
|
-
// Handle the command disableplugin from Home
|
|
896
758
|
if (command === 'disableplugin') {
|
|
897
759
|
if (!this.matterbridge.plugins.has(param)) {
|
|
898
760
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -900,7 +762,7 @@ export class Frontend {
|
|
|
900
762
|
else {
|
|
901
763
|
const plugin = this.matterbridge.plugins.get(param);
|
|
902
764
|
if (plugin && plugin.enabled) {
|
|
903
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
765
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
904
766
|
await this.matterbridge.plugins.disable(param);
|
|
905
767
|
}
|
|
906
768
|
}
|
|
@@ -909,7 +771,6 @@ export class Frontend {
|
|
|
909
771
|
return;
|
|
910
772
|
}
|
|
911
773
|
});
|
|
912
|
-
// Fallback for routing (must be the last route)
|
|
913
774
|
this.expressApp.get('*', (req, res) => {
|
|
914
775
|
this.log.debug('The frontend sent:', req.url);
|
|
915
776
|
this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -918,29 +779,24 @@ export class Frontend {
|
|
|
918
779
|
this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
919
780
|
}
|
|
920
781
|
async stop() {
|
|
921
|
-
// Close the http server
|
|
922
782
|
if (this.httpServer) {
|
|
923
783
|
this.httpServer.close();
|
|
924
784
|
this.httpServer.removeAllListeners();
|
|
925
785
|
this.httpServer = undefined;
|
|
926
786
|
this.log.debug('Frontend http server closed successfully');
|
|
927
787
|
}
|
|
928
|
-
// Close the https server
|
|
929
788
|
if (this.httpsServer) {
|
|
930
789
|
this.httpsServer.close();
|
|
931
790
|
this.httpsServer.removeAllListeners();
|
|
932
791
|
this.httpsServer = undefined;
|
|
933
792
|
this.log.debug('Frontend https server closed successfully');
|
|
934
793
|
}
|
|
935
|
-
// Remove listeners from the express app
|
|
936
794
|
if (this.expressApp) {
|
|
937
795
|
this.expressApp.removeAllListeners();
|
|
938
796
|
this.expressApp = undefined;
|
|
939
797
|
this.log.debug('Frontend app closed successfully');
|
|
940
798
|
}
|
|
941
|
-
// Close the WebSocket server
|
|
942
799
|
if (this.webSocketServer) {
|
|
943
|
-
// Close all active connections
|
|
944
800
|
this.webSocketServer.clients.forEach((client) => {
|
|
945
801
|
if (client.readyState === WebSocket.OPEN) {
|
|
946
802
|
client.close();
|
|
@@ -957,7 +813,6 @@ export class Frontend {
|
|
|
957
813
|
this.webSocketServer = undefined;
|
|
958
814
|
}
|
|
959
815
|
}
|
|
960
|
-
// Function to format bytes to KB, MB, or GB
|
|
961
816
|
formatMemoryUsage = (bytes) => {
|
|
962
817
|
if (bytes >= 1024 ** 3) {
|
|
963
818
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -969,7 +824,6 @@ export class Frontend {
|
|
|
969
824
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
970
825
|
}
|
|
971
826
|
};
|
|
972
|
-
// Function to format system uptime with only the most significant unit
|
|
973
827
|
formatOsUpTime = (seconds) => {
|
|
974
828
|
if (seconds >= 86400) {
|
|
975
829
|
const days = Math.floor(seconds / 86400);
|
|
@@ -985,13 +839,8 @@ export class Frontend {
|
|
|
985
839
|
}
|
|
986
840
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
987
841
|
};
|
|
988
|
-
/**
|
|
989
|
-
* Retrieves the api settings data.
|
|
990
|
-
* @returns {Promise<object>} A promise that resolve in the api settings object.
|
|
991
|
-
*/
|
|
992
842
|
async getApiSettings() {
|
|
993
843
|
const { lastCpuUsage } = await import('./cli.js');
|
|
994
|
-
// Update the system information
|
|
995
844
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
996
845
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
997
846
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -1000,7 +849,6 @@ export class Frontend {
|
|
|
1000
849
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
1001
850
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
1002
851
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
1003
|
-
// Update the matterbridge information
|
|
1004
852
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
1005
853
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
1006
854
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -1019,11 +867,6 @@ export class Frontend {
|
|
|
1019
867
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
1020
868
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
1021
869
|
}
|
|
1022
|
-
/**
|
|
1023
|
-
* Retrieves the cluster text description from a given device.
|
|
1024
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
1025
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
1026
|
-
*/
|
|
1027
870
|
getClusterTextFromDevice(device) {
|
|
1028
871
|
const getAttribute = (device, cluster, attribute) => {
|
|
1029
872
|
let value = undefined;
|
|
@@ -1062,7 +905,6 @@ export class Frontend {
|
|
|
1062
905
|
};
|
|
1063
906
|
let attributes = '';
|
|
1064
907
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1065
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
1066
908
|
if (typeof attributeValue === 'undefined')
|
|
1067
909
|
return;
|
|
1068
910
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1140,13 +982,8 @@ export class Frontend {
|
|
|
1140
982
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1141
983
|
attributes += `${getUserLabel(device)} `;
|
|
1142
984
|
});
|
|
1143
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1144
985
|
return attributes.trimStart().trimEnd();
|
|
1145
986
|
}
|
|
1146
|
-
/**
|
|
1147
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
1148
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
1149
|
-
*/
|
|
1150
987
|
getBaseRegisteredPlugins() {
|
|
1151
988
|
const baseRegisteredPlugins = [];
|
|
1152
989
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -1177,13 +1014,6 @@ export class Frontend {
|
|
|
1177
1014
|
}
|
|
1178
1015
|
return baseRegisteredPlugins;
|
|
1179
1016
|
}
|
|
1180
|
-
/**
|
|
1181
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1182
|
-
*
|
|
1183
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1184
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1185
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1186
|
-
*/
|
|
1187
1017
|
async wsMessageHandler(client, message) {
|
|
1188
1018
|
let data;
|
|
1189
1019
|
try {
|
|
@@ -1273,6 +1103,11 @@ export class Frontend {
|
|
|
1273
1103
|
triggerShellyMainUpdate(this.matterbridge);
|
|
1274
1104
|
return;
|
|
1275
1105
|
}
|
|
1106
|
+
else if (data.method === '/api/shellycreatesystemlog') {
|
|
1107
|
+
const { createShellySystemLog } = await import('./shelly.js');
|
|
1108
|
+
createShellySystemLog(this.matterbridge);
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1276
1111
|
else if (data.method === '/api/shellynetconfig') {
|
|
1277
1112
|
this.log.debug('/api/shellynetconfig:', data.params);
|
|
1278
1113
|
const { triggerShellyChangeIp: triggerShellyChangeNet } = await import('./shelly.js');
|
|
@@ -1324,10 +1159,8 @@ export class Frontend {
|
|
|
1324
1159
|
else if (data.method === '/api/devices') {
|
|
1325
1160
|
const devices = [];
|
|
1326
1161
|
this.matterbridge.devices.forEach(async (device) => {
|
|
1327
|
-
// Filter by pluginName if provided
|
|
1328
1162
|
if (data.params.pluginName && data.params.pluginName !== device.plugin)
|
|
1329
1163
|
return;
|
|
1330
|
-
// Check if the device has the required properties
|
|
1331
1164
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1332
1165
|
return;
|
|
1333
1166
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -1410,7 +1243,6 @@ export class Frontend {
|
|
|
1410
1243
|
});
|
|
1411
1244
|
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
1412
1245
|
deviceTypes = [];
|
|
1413
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1414
1246
|
const name = childEndpoint.endpoint?.id;
|
|
1415
1247
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
1416
1248
|
clusterServers.forEach((clusterServer) => {
|
|
@@ -1493,139 +1325,85 @@ export class Frontend {
|
|
|
1493
1325
|
return;
|
|
1494
1326
|
}
|
|
1495
1327
|
}
|
|
1496
|
-
/**
|
|
1497
|
-
* Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1498
|
-
*
|
|
1499
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1500
|
-
* @param {string} time - The time string of the message
|
|
1501
|
-
* @param {string} name - The logger name of the message
|
|
1502
|
-
* @param {string} message - The content of the message.
|
|
1503
|
-
*/
|
|
1504
1328
|
wssSendMessage(level, time, name, message) {
|
|
1505
1329
|
if (!level || !time || !name || !message)
|
|
1506
1330
|
return;
|
|
1507
|
-
// Remove ANSI escape codes from the message
|
|
1508
|
-
// eslint-disable-next-line no-control-regex
|
|
1509
1331
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1510
|
-
// Remove leading asterisks from the message
|
|
1511
1332
|
message = message.replace(/^\*+/, '');
|
|
1512
|
-
// Replace all occurrences of \t and \n
|
|
1513
1333
|
message = message.replace(/[\t\n]/g, '');
|
|
1514
|
-
// Remove non-printable characters
|
|
1515
|
-
// eslint-disable-next-line no-control-regex
|
|
1516
1334
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1517
|
-
// Replace all occurrences of \" with "
|
|
1518
1335
|
message = message.replace(/\\"/g, '"');
|
|
1519
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1520
1336
|
const maxContinuousLength = 100;
|
|
1521
1337
|
const keepStartLength = 20;
|
|
1522
1338
|
const keepEndLength = 20;
|
|
1523
|
-
// Split the message into words
|
|
1524
1339
|
message = message
|
|
1525
1340
|
.split(' ')
|
|
1526
1341
|
.map((word) => {
|
|
1527
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1528
1342
|
if (word.length > maxContinuousLength) {
|
|
1529
1343
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1530
1344
|
}
|
|
1531
1345
|
return word;
|
|
1532
1346
|
})
|
|
1533
1347
|
.join(' ');
|
|
1534
|
-
// Send the message to all connected clients
|
|
1535
1348
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1536
1349
|
if (client.readyState === WebSocket.OPEN) {
|
|
1537
1350
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1538
1351
|
}
|
|
1539
1352
|
});
|
|
1540
1353
|
}
|
|
1541
|
-
/**
|
|
1542
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1543
|
-
*
|
|
1544
|
-
*/
|
|
1545
1354
|
wssSendRefreshRequired() {
|
|
1546
1355
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1547
1356
|
this.matterbridge.matterbridgeInformation.refreshRequired = true;
|
|
1548
|
-
// Send the message to all connected clients
|
|
1549
1357
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1550
1358
|
if (client.readyState === WebSocket.OPEN) {
|
|
1551
1359
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: {} }));
|
|
1552
1360
|
}
|
|
1553
1361
|
});
|
|
1554
1362
|
}
|
|
1555
|
-
/**
|
|
1556
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1557
|
-
*
|
|
1558
|
-
*/
|
|
1559
1363
|
wssSendRestartRequired() {
|
|
1560
1364
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1561
1365
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1562
1366
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1563
|
-
// Send the message to all connected clients
|
|
1564
1367
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1565
1368
|
if (client.readyState === WebSocket.OPEN) {
|
|
1566
1369
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1567
1370
|
}
|
|
1568
1371
|
});
|
|
1569
1372
|
}
|
|
1570
|
-
/**
|
|
1571
|
-
* Sends a memory update message to all connected clients.
|
|
1572
|
-
*
|
|
1573
|
-
*/
|
|
1574
1373
|
wssSendCpuUpdate(cpuUsage) {
|
|
1575
1374
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1576
|
-
// Send the message to all connected clients
|
|
1577
1375
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1578
1376
|
if (client.readyState === WebSocket.OPEN) {
|
|
1579
1377
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
1580
1378
|
}
|
|
1581
1379
|
});
|
|
1582
1380
|
}
|
|
1583
|
-
/**
|
|
1584
|
-
* Sends a cpu update message to all connected clients.
|
|
1585
|
-
*
|
|
1586
|
-
*/
|
|
1587
1381
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
1588
1382
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
1589
|
-
// Send the message to all connected clients
|
|
1590
1383
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1591
1384
|
if (client.readyState === WebSocket.OPEN) {
|
|
1592
1385
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
1593
1386
|
}
|
|
1594
1387
|
});
|
|
1595
1388
|
}
|
|
1596
|
-
/**
|
|
1597
|
-
* Sends a memory update message to all connected clients.
|
|
1598
|
-
*
|
|
1599
|
-
*/
|
|
1600
1389
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
1601
1390
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
1602
|
-
// Send the message to all connected clients
|
|
1603
1391
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1604
1392
|
if (client.readyState === WebSocket.OPEN) {
|
|
1605
1393
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
1606
1394
|
}
|
|
1607
1395
|
});
|
|
1608
1396
|
}
|
|
1609
|
-
/**
|
|
1610
|
-
* Sends a cpu update message to all connected clients.
|
|
1611
|
-
*
|
|
1612
|
-
*/
|
|
1613
1397
|
wssSendSnackbarMessage(message, timeout = 5) {
|
|
1614
1398
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
1615
|
-
// Send the message to all connected clients
|
|
1616
1399
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1617
1400
|
if (client.readyState === WebSocket.OPEN) {
|
|
1618
1401
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { message, timeout } }));
|
|
1619
1402
|
}
|
|
1620
1403
|
});
|
|
1621
1404
|
}
|
|
1622
|
-
/**
|
|
1623
|
-
* Sends a message to all connected clients.
|
|
1624
|
-
*
|
|
1625
|
-
*/
|
|
1626
1405
|
wssBroadcastMessage(id, method, params) {
|
|
1627
1406
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
1628
|
-
// Send the message to all connected clients
|
|
1629
1407
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1630
1408
|
if (client.readyState === WebSocket.OPEN) {
|
|
1631
1409
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -1633,4 +1411,3 @@ export class Frontend {
|
|
|
1633
1411
|
});
|
|
1634
1412
|
}
|
|
1635
1413
|
}
|
|
1636
|
-
//# sourceMappingURL=frontend.js.map
|