matterbridge 2.0.0-edge1 → 2.1.0-dev.1
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 +36 -3
- package/README.md +1 -1
- package/dist/cli.js +0 -26
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +2 -27
- package/dist/frontend.js +99 -245
- package/dist/index.js +2 -33
- package/dist/logger/export.js +0 -1
- package/dist/matter/export.js +0 -7
- package/dist/matterbridge.js +99 -710
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +42 -32
- package/dist/matterbridgeDeviceTypes.js +90 -84
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEndpoint.js +992 -2454
- package/dist/matterbridgePlatform.js +11 -130
- package/dist/matterbridgeTypes.js +0 -25
- package/dist/pluginManager.js +7 -251
- 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 +10 -255
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.ea28015b.js → main.26dbf9b9.js} +3 -3
- package/frontend/build/static/js/main.26dbf9b9.js.map +1 -0
- package/npm-shrinkwrap.json +97 -79
- package/package.json +2 -4
- 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 -46
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -98
- 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/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -357
- 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 -123
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -109
- 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 -1167
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -154
- 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 -238
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/utils.d.ts +0 -221
- package/dist/utils/utils.d.ts.map +0 -1
- package/dist/utils/utils.js.map +0 -1
- package/frontend/build/static/js/main.ea28015b.js.map +0 -1
- /package/frontend/build/static/js/{main.ea28015b.js.LICENSE.txt → main.26dbf9b9.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.0
|
|
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
|
-
|
|
34
|
-
import { AnsiLogger, CYAN, db, debugStringify, er, LogLevel, nf, rs, stringify, TimestampFormat, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from 'node-ansi-logger';
|
|
35
|
-
// Matterbridge
|
|
9
|
+
import { AnsiLogger, CYAN, db, debugStringify, er, nf, rs, stringify, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';
|
|
36
10
|
import { createZip, 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;
|
|
@@ -67,26 +24,15 @@ export class Frontend {
|
|
|
67
24
|
webSocketServer;
|
|
68
25
|
constructor(matterbridge) {
|
|
69
26
|
this.matterbridge = matterbridge;
|
|
70
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat:
|
|
27
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
71
28
|
}
|
|
72
29
|
async start(port = 8283) {
|
|
73
30
|
this.port = port;
|
|
74
31
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
75
|
-
// Create the express app that serves the frontend
|
|
76
32
|
this.expressApp = express();
|
|
77
|
-
// Log all requests to the server for debugging
|
|
78
|
-
/*
|
|
79
|
-
this.expressApp.use((req, res, next) => {
|
|
80
|
-
this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
|
|
81
|
-
next();
|
|
82
|
-
});
|
|
83
|
-
*/
|
|
84
|
-
// Serve static files from '/static' endpoint
|
|
85
33
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
86
34
|
if (!hasParameter('ssl')) {
|
|
87
|
-
// Create an HTTP server and attach the express app
|
|
88
35
|
this.httpServer = createServer(this.expressApp);
|
|
89
|
-
// Listen on the specified port
|
|
90
36
|
if (hasParameter('ingress')) {
|
|
91
37
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
92
38
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -100,7 +46,6 @@ export class Frontend {
|
|
|
100
46
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
101
47
|
});
|
|
102
48
|
}
|
|
103
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
104
49
|
this.httpServer.on('error', (error) => {
|
|
105
50
|
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
106
51
|
switch (error.code) {
|
|
@@ -116,7 +61,6 @@ export class Frontend {
|
|
|
116
61
|
});
|
|
117
62
|
}
|
|
118
63
|
else {
|
|
119
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
120
64
|
let cert;
|
|
121
65
|
try {
|
|
122
66
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -144,9 +88,7 @@ export class Frontend {
|
|
|
144
88
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
145
89
|
}
|
|
146
90
|
const serverOptions = { cert, key, ca };
|
|
147
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
148
91
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
149
|
-
// Listen on the specified port
|
|
150
92
|
if (hasParameter('ingress')) {
|
|
151
93
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
152
94
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -160,7 +102,6 @@ export class Frontend {
|
|
|
160
102
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
161
103
|
});
|
|
162
104
|
}
|
|
163
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
164
105
|
this.httpsServer.on('error', (error) => {
|
|
165
106
|
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
166
107
|
switch (error.code) {
|
|
@@ -177,13 +118,12 @@ export class Frontend {
|
|
|
177
118
|
}
|
|
178
119
|
if (this.initializeError)
|
|
179
120
|
return;
|
|
180
|
-
// Createe a WebSocket server and attach it to the http or https server
|
|
181
121
|
const wssPort = this.port;
|
|
182
122
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
183
123
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
184
124
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
185
125
|
const clientIp = request.socket.remoteAddress;
|
|
186
|
-
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this),
|
|
126
|
+
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug");
|
|
187
127
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
188
128
|
ws.on('message', (message) => {
|
|
189
129
|
this.wsMessageHandler(ws, message);
|
|
@@ -215,7 +155,6 @@ export class Frontend {
|
|
|
215
155
|
this.webSocketServer.on('error', (ws, error) => {
|
|
216
156
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
217
157
|
});
|
|
218
|
-
// Endpoint to validate login code
|
|
219
158
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
220
159
|
const { password } = req.body;
|
|
221
160
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -234,60 +173,74 @@ export class Frontend {
|
|
|
234
173
|
this.log.warn('/api/login error wrong password');
|
|
235
174
|
res.json({ valid: false });
|
|
236
175
|
}
|
|
237
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
238
176
|
}
|
|
239
177
|
catch (error) {
|
|
240
178
|
this.log.error('/api/login error getting password');
|
|
241
179
|
res.json({ valid: false });
|
|
242
180
|
}
|
|
243
181
|
});
|
|
244
|
-
// Endpoint to provide health check
|
|
245
182
|
this.expressApp.get('/health', (req, res) => {
|
|
246
183
|
this.log.debug('Express received /health');
|
|
247
184
|
const healthStatus = {
|
|
248
|
-
status: 'ok',
|
|
249
|
-
uptime: process.uptime(),
|
|
250
|
-
timestamp: new Date().toISOString(),
|
|
185
|
+
status: 'ok',
|
|
186
|
+
uptime: process.uptime(),
|
|
187
|
+
timestamp: new Date().toISOString(),
|
|
251
188
|
};
|
|
252
189
|
res.status(200).json(healthStatus);
|
|
253
190
|
});
|
|
254
|
-
|
|
191
|
+
this.expressApp.get('/memory', async (req, res) => {
|
|
192
|
+
this.log.debug('Express received /memory');
|
|
193
|
+
const formatMemoryUsage = (bytes) => {
|
|
194
|
+
const kb = bytes / 1024;
|
|
195
|
+
const mb = kb / 1024;
|
|
196
|
+
return mb >= 1 ? `${mb.toFixed(2)} MB` : `${kb.toFixed(2)} KB`;
|
|
197
|
+
};
|
|
198
|
+
const memoryUsageRaw = process.memoryUsage();
|
|
199
|
+
const memoryUsage = {
|
|
200
|
+
rss: formatMemoryUsage(memoryUsageRaw.rss),
|
|
201
|
+
heapTotal: formatMemoryUsage(memoryUsageRaw.heapTotal),
|
|
202
|
+
heapUsed: formatMemoryUsage(memoryUsageRaw.heapUsed),
|
|
203
|
+
external: formatMemoryUsage(memoryUsageRaw.external),
|
|
204
|
+
arrayBuffers: formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
205
|
+
};
|
|
206
|
+
const { default: v8 } = await import('node:v8');
|
|
207
|
+
const heapStatsRaw = v8.getHeapStatistics();
|
|
208
|
+
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
209
|
+
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, formatMemoryUsage(value)]));
|
|
210
|
+
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
211
|
+
...space,
|
|
212
|
+
space_size: formatMemoryUsage(space.space_size),
|
|
213
|
+
space_used_size: formatMemoryUsage(space.space_used_size),
|
|
214
|
+
space_available_size: formatMemoryUsage(space.space_available_size),
|
|
215
|
+
physical_space_size: formatMemoryUsage(space.physical_space_size),
|
|
216
|
+
}));
|
|
217
|
+
const { default: module } = await import('module');
|
|
218
|
+
const loadedModules = module._cache ? Object.keys(module._cache).sort() : [];
|
|
219
|
+
const memoryReport = {
|
|
220
|
+
memoryUsage,
|
|
221
|
+
heapStats,
|
|
222
|
+
heapSpaces,
|
|
223
|
+
loadedModules,
|
|
224
|
+
};
|
|
225
|
+
res.status(200).json(memoryReport);
|
|
226
|
+
});
|
|
227
|
+
this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
|
|
228
|
+
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
229
|
+
res.json(pairingCodes);
|
|
230
|
+
});
|
|
255
231
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
256
232
|
this.log.debug('The frontend sent /api/settings');
|
|
257
|
-
|
|
258
|
-
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
259
|
-
this.matterbridge.matterbridgeInformation.loggerLevel = this.log.logLevel;
|
|
260
|
-
this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
|
|
261
|
-
this.matterbridge.matterbridgeInformation.mattermdnsinterface = (await this.matterbridge.nodeContext?.get('mattermdnsinterface', '')) || '';
|
|
262
|
-
this.matterbridge.matterbridgeInformation.matteripv4address = (await this.matterbridge.nodeContext?.get('matteripv4address', '')) || '';
|
|
263
|
-
this.matterbridge.matterbridgeInformation.matteripv6address = (await this.matterbridge.nodeContext?.get('matteripv6address', '')) || '';
|
|
264
|
-
this.matterbridge.matterbridgeInformation.matterPort = (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540;
|
|
265
|
-
this.matterbridge.matterbridgeInformation.matterDiscriminator = await this.matterbridge.nodeContext?.get('matterdiscriminator');
|
|
266
|
-
this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
|
|
267
|
-
this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.matterbridgePaired;
|
|
268
|
-
this.matterbridge.matterbridgeInformation.matterbridgeConnected = this.matterbridge.matterbridgeConnected;
|
|
269
|
-
this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeQrPairingCode;
|
|
270
|
-
this.matterbridge.matterbridgeInformation.matterbridgeManualPairingCode = this.matterbridge.matterbridgeManualPairingCode;
|
|
271
|
-
this.matterbridge.matterbridgeInformation.matterbridgeFabricInformations = this.matterbridge.matterbridgeFabricInformations;
|
|
272
|
-
this.matterbridge.matterbridgeInformation.matterbridgeSessionInformations = Array.from(this.matterbridge.matterbridgeSessionInformations.values());
|
|
273
|
-
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
274
|
-
const response = { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
275
|
-
// this.log.debug('Response:', debugStringify(response));
|
|
276
|
-
res.json(response);
|
|
233
|
+
res.json(await this.getApiSettings());
|
|
277
234
|
});
|
|
278
|
-
// Endpoint to provide plugins
|
|
279
235
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
280
236
|
this.log.debug('The frontend sent /api/plugins');
|
|
281
|
-
const response =
|
|
282
|
-
// this.log.debug('Response:', debugStringify(response));
|
|
237
|
+
const response = this.getBaseRegisteredPlugins();
|
|
283
238
|
res.json(response);
|
|
284
239
|
});
|
|
285
|
-
// Endpoint to provide devices
|
|
286
240
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
287
241
|
this.log.debug('The frontend sent /api/devices');
|
|
288
242
|
const devices = [];
|
|
289
243
|
this.matterbridge.devices.forEach(async (device) => {
|
|
290
|
-
// Check if the device has the required properties
|
|
291
244
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
|
|
292
245
|
return;
|
|
293
246
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -303,10 +256,8 @@ export class Frontend {
|
|
|
303
256
|
cluster: cluster,
|
|
304
257
|
});
|
|
305
258
|
});
|
|
306
|
-
// this.log.debug('Response:', debugStringify(data));
|
|
307
259
|
res.json(devices);
|
|
308
260
|
});
|
|
309
|
-
// Endpoint to provide the cluster servers of the devices
|
|
310
261
|
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
311
262
|
const selectedPluginName = req.params.selectedPluginName;
|
|
312
263
|
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
@@ -379,7 +330,6 @@ export class Frontend {
|
|
|
379
330
|
});
|
|
380
331
|
res.json(data);
|
|
381
332
|
});
|
|
382
|
-
// Endpoint to view the log
|
|
383
333
|
this.expressApp.get('/api/view-log', async (req, res) => {
|
|
384
334
|
this.log.debug('The frontend sent /api/log');
|
|
385
335
|
try {
|
|
@@ -392,12 +342,10 @@ export class Frontend {
|
|
|
392
342
|
res.status(500).send('Error reading log file');
|
|
393
343
|
}
|
|
394
344
|
});
|
|
395
|
-
// Endpoint to download the matterbridge log
|
|
396
345
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
397
346
|
this.log.debug('The frontend sent /api/download-mblog');
|
|
398
347
|
try {
|
|
399
348
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
|
|
400
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
401
349
|
}
|
|
402
350
|
catch (error) {
|
|
403
351
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -409,12 +357,10 @@ export class Frontend {
|
|
|
409
357
|
}
|
|
410
358
|
});
|
|
411
359
|
});
|
|
412
|
-
// Endpoint to download the matter log
|
|
413
360
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
414
361
|
this.log.debug('The frontend sent /api/download-mjlog');
|
|
415
362
|
try {
|
|
416
363
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
417
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
418
364
|
}
|
|
419
365
|
catch (error) {
|
|
420
366
|
fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -426,17 +372,16 @@ export class Frontend {
|
|
|
426
372
|
}
|
|
427
373
|
});
|
|
428
374
|
});
|
|
429
|
-
|
|
430
|
-
this.expressApp.get('/api/download-mjstorage', (req, res) => {
|
|
375
|
+
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
431
376
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
432
|
-
|
|
377
|
+
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
378
|
+
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), `matterbridge.${this.matterbridge.matterStorageName}.zip`, (error) => {
|
|
433
379
|
if (error) {
|
|
434
|
-
this.log.error(`Error downloading
|
|
435
|
-
res.status(500).send('Error downloading the matter storage file');
|
|
380
|
+
this.log.error(`Error downloading the matter storage matterbridge.${this.matterbridge.matterStorageName}.zip: ${error instanceof Error ? error.message : error}`);
|
|
381
|
+
res.status(500).send('Error downloading the matter storage zip file');
|
|
436
382
|
}
|
|
437
383
|
});
|
|
438
384
|
});
|
|
439
|
-
// Endpoint to download the matterbridge storage directory
|
|
440
385
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
441
386
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
442
387
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -447,7 +392,6 @@ export class Frontend {
|
|
|
447
392
|
}
|
|
448
393
|
});
|
|
449
394
|
});
|
|
450
|
-
// Endpoint to download the matterbridge plugin directory
|
|
451
395
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
452
396
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
453
397
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -458,11 +402,9 @@ export class Frontend {
|
|
|
458
402
|
}
|
|
459
403
|
});
|
|
460
404
|
});
|
|
461
|
-
// Endpoint to download the matterbridge plugin config files
|
|
462
405
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
463
406
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
464
407
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
465
|
-
// 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')));
|
|
466
408
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
467
409
|
if (error) {
|
|
468
410
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
@@ -470,7 +412,6 @@ export class Frontend {
|
|
|
470
412
|
}
|
|
471
413
|
});
|
|
472
414
|
});
|
|
473
|
-
// Endpoint to download the matterbridge plugin config files
|
|
474
415
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
475
416
|
this.log.debug('The frontend sent /api/download-backup');
|
|
476
417
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -480,7 +421,6 @@ export class Frontend {
|
|
|
480
421
|
}
|
|
481
422
|
});
|
|
482
423
|
});
|
|
483
|
-
// Endpoint to receive commands
|
|
484
424
|
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
485
425
|
const command = req.params.command;
|
|
486
426
|
let param = req.params.param;
|
|
@@ -490,15 +430,13 @@ export class Frontend {
|
|
|
490
430
|
return;
|
|
491
431
|
}
|
|
492
432
|
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
493
|
-
// Handle the command setpassword from Settings
|
|
494
433
|
if (command === 'setpassword') {
|
|
495
|
-
const password = param.slice(1, -1);
|
|
434
|
+
const password = param.slice(1, -1);
|
|
496
435
|
this.log.debug('setpassword', param, password);
|
|
497
436
|
await this.matterbridge.nodeContext?.set('password', password);
|
|
498
437
|
res.json({ message: 'Command received' });
|
|
499
438
|
return;
|
|
500
439
|
}
|
|
501
|
-
// Handle the command setbridgemode from Settings
|
|
502
440
|
if (command === 'setbridgemode') {
|
|
503
441
|
this.log.debug(`setbridgemode: ${param}`);
|
|
504
442
|
this.wssSendRestartRequired();
|
|
@@ -506,7 +444,6 @@ export class Frontend {
|
|
|
506
444
|
res.json({ message: 'Command received' });
|
|
507
445
|
return;
|
|
508
446
|
}
|
|
509
|
-
// Handle the command backup from Settings
|
|
510
447
|
if (command === 'backup') {
|
|
511
448
|
this.log.notice(`Prepairing the backup...`);
|
|
512
449
|
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
|
|
@@ -514,26 +451,25 @@ export class Frontend {
|
|
|
514
451
|
res.json({ message: 'Command received' });
|
|
515
452
|
return;
|
|
516
453
|
}
|
|
517
|
-
// Handle the command setmbloglevel from Settings
|
|
518
454
|
if (command === 'setmbloglevel') {
|
|
519
455
|
this.log.debug('Matterbridge log level:', param);
|
|
520
456
|
if (param === 'Debug') {
|
|
521
|
-
this.log.logLevel =
|
|
457
|
+
this.log.logLevel = "debug";
|
|
522
458
|
}
|
|
523
459
|
else if (param === 'Info') {
|
|
524
|
-
this.log.logLevel =
|
|
460
|
+
this.log.logLevel = "info";
|
|
525
461
|
}
|
|
526
462
|
else if (param === 'Notice') {
|
|
527
|
-
this.log.logLevel =
|
|
463
|
+
this.log.logLevel = "notice";
|
|
528
464
|
}
|
|
529
465
|
else if (param === 'Warn') {
|
|
530
|
-
this.log.logLevel =
|
|
466
|
+
this.log.logLevel = "warn";
|
|
531
467
|
}
|
|
532
468
|
else if (param === 'Error') {
|
|
533
|
-
this.log.logLevel =
|
|
469
|
+
this.log.logLevel = "error";
|
|
534
470
|
}
|
|
535
471
|
else if (param === 'Fatal') {
|
|
536
|
-
this.log.logLevel =
|
|
472
|
+
this.log.logLevel = "fatal";
|
|
537
473
|
}
|
|
538
474
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
539
475
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
@@ -541,13 +477,12 @@ export class Frontend {
|
|
|
541
477
|
for (const plugin of this.matterbridge.plugins) {
|
|
542
478
|
if (!plugin.platform || !plugin.platform.config)
|
|
543
479
|
continue;
|
|
544
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug ?
|
|
545
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ?
|
|
480
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug" : this.log.logLevel;
|
|
481
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug" : this.log.logLevel);
|
|
546
482
|
}
|
|
547
483
|
res.json({ message: 'Command received' });
|
|
548
484
|
return;
|
|
549
485
|
}
|
|
550
|
-
// Handle the command setmbloglevel from Settings
|
|
551
486
|
if (command === 'setmjloglevel') {
|
|
552
487
|
this.log.debug('Matter.js log level:', param);
|
|
553
488
|
if (param === 'Debug') {
|
|
@@ -572,34 +507,30 @@ export class Frontend {
|
|
|
572
507
|
res.json({ message: 'Command received' });
|
|
573
508
|
return;
|
|
574
509
|
}
|
|
575
|
-
// Handle the command setmdnsinterface from Settings
|
|
576
510
|
if (command === 'setmdnsinterface') {
|
|
577
|
-
param = param.slice(1, -1);
|
|
511
|
+
param = param.slice(1, -1);
|
|
578
512
|
this.matterbridge.matterbridgeInformation.mattermdnsinterface = param;
|
|
579
513
|
this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
|
|
580
514
|
await this.matterbridge.nodeContext?.set('mattermdnsinterface', param);
|
|
581
515
|
res.json({ message: 'Command received' });
|
|
582
516
|
return;
|
|
583
517
|
}
|
|
584
|
-
// Handle the command setipv4address from Settings
|
|
585
518
|
if (command === 'setipv4address') {
|
|
586
|
-
param = param.slice(1, -1);
|
|
519
|
+
param = param.slice(1, -1);
|
|
587
520
|
this.matterbridge.matterbridgeInformation.matteripv4address = param;
|
|
588
521
|
this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
|
|
589
522
|
await this.matterbridge.nodeContext?.set('matteripv4address', param);
|
|
590
523
|
res.json({ message: 'Command received' });
|
|
591
524
|
return;
|
|
592
525
|
}
|
|
593
|
-
// Handle the command setipv6address from Settings
|
|
594
526
|
if (command === 'setipv6address') {
|
|
595
|
-
param = param.slice(1, -1);
|
|
527
|
+
param = param.slice(1, -1);
|
|
596
528
|
this.matterbridge.matterbridgeInformation.matteripv6address = param;
|
|
597
529
|
this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
|
|
598
530
|
await this.matterbridge.nodeContext?.set('matteripv6address', param);
|
|
599
531
|
res.json({ message: 'Command received' });
|
|
600
532
|
return;
|
|
601
533
|
}
|
|
602
|
-
// Handle the command setmatterport from Settings
|
|
603
534
|
if (command === 'setmatterport') {
|
|
604
535
|
const port = Math.min(Math.max(parseInt(param), 5540), 5560);
|
|
605
536
|
this.matterbridge.matterbridgeInformation.matterPort = port;
|
|
@@ -608,7 +539,6 @@ export class Frontend {
|
|
|
608
539
|
res.json({ message: 'Command received' });
|
|
609
540
|
return;
|
|
610
541
|
}
|
|
611
|
-
// Handle the command setmatterdiscriminator from Settings
|
|
612
542
|
if (command === 'setmatterdiscriminator') {
|
|
613
543
|
const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
|
|
614
544
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
|
|
@@ -617,7 +547,6 @@ export class Frontend {
|
|
|
617
547
|
res.json({ message: 'Command received' });
|
|
618
548
|
return;
|
|
619
549
|
}
|
|
620
|
-
// Handle the command setmatterpasscode from Settings
|
|
621
550
|
if (command === 'setmatterpasscode') {
|
|
622
551
|
const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
|
|
623
552
|
this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
|
|
@@ -626,20 +555,17 @@ export class Frontend {
|
|
|
626
555
|
res.json({ message: 'Command received' });
|
|
627
556
|
return;
|
|
628
557
|
}
|
|
629
|
-
// Handle the command setmbloglevel from Settings
|
|
630
558
|
if (command === 'setmblogfile') {
|
|
631
559
|
this.log.debug('Matterbridge file log:', param);
|
|
632
560
|
this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
|
|
633
561
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
|
|
634
|
-
// Create the file logger for matterbridge
|
|
635
562
|
if (param === 'true')
|
|
636
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile),
|
|
563
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
|
|
637
564
|
else
|
|
638
565
|
AnsiLogger.setGlobalLogfile(undefined);
|
|
639
566
|
res.json({ message: 'Command received' });
|
|
640
567
|
return;
|
|
641
568
|
}
|
|
642
|
-
// Handle the command setmbloglevel from Settings
|
|
643
569
|
if (command === 'setmjlogfile') {
|
|
644
570
|
this.log.debug('Matter file log:', param);
|
|
645
571
|
this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
|
|
@@ -666,43 +592,36 @@ export class Frontend {
|
|
|
666
592
|
res.json({ message: 'Command received' });
|
|
667
593
|
return;
|
|
668
594
|
}
|
|
669
|
-
// Handle the command unregister from Settings
|
|
670
595
|
if (command === 'unregister') {
|
|
671
596
|
await this.matterbridge.unregisterAndShutdownProcess();
|
|
672
597
|
res.json({ message: 'Command received' });
|
|
673
598
|
return;
|
|
674
599
|
}
|
|
675
|
-
// Handle the command reset from Settings
|
|
676
600
|
if (command === 'reset') {
|
|
677
601
|
await this.matterbridge.shutdownProcessAndReset();
|
|
678
602
|
res.json({ message: 'Command received' });
|
|
679
603
|
return;
|
|
680
604
|
}
|
|
681
|
-
// Handle the command factoryreset from Settings
|
|
682
605
|
if (command === 'factoryreset') {
|
|
683
606
|
await this.matterbridge.shutdownProcessAndFactoryReset();
|
|
684
607
|
res.json({ message: 'Command received' });
|
|
685
608
|
return;
|
|
686
609
|
}
|
|
687
|
-
// Handle the command shutdown from Header
|
|
688
610
|
if (command === 'shutdown') {
|
|
689
611
|
await this.matterbridge.shutdownProcess();
|
|
690
612
|
res.json({ message: 'Command received' });
|
|
691
613
|
return;
|
|
692
614
|
}
|
|
693
|
-
// Handle the command restart from Header
|
|
694
615
|
if (command === 'restart') {
|
|
695
616
|
await this.matterbridge.restartProcess();
|
|
696
617
|
res.json({ message: 'Command received' });
|
|
697
618
|
return;
|
|
698
619
|
}
|
|
699
|
-
// Handle the command update from Header
|
|
700
620
|
if (command === 'update') {
|
|
701
621
|
this.log.info('Updating matterbridge...');
|
|
702
622
|
try {
|
|
703
623
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose']);
|
|
704
624
|
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
705
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
706
625
|
}
|
|
707
626
|
catch (error) {
|
|
708
627
|
this.log.error('Error updating matterbridge');
|
|
@@ -712,11 +631,9 @@ export class Frontend {
|
|
|
712
631
|
res.json({ message: 'Command received' });
|
|
713
632
|
return;
|
|
714
633
|
}
|
|
715
|
-
// Handle the command saveconfig from Home
|
|
716
634
|
if (command === 'saveconfig') {
|
|
717
635
|
param = param.replace(/\*/g, '\\');
|
|
718
636
|
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
719
|
-
// console.log('Req.body:', JSON.stringify(req.body, null, 2));
|
|
720
637
|
if (!this.matterbridge.plugins.has(param)) {
|
|
721
638
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
722
639
|
}
|
|
@@ -730,58 +647,49 @@ export class Frontend {
|
|
|
730
647
|
res.json({ message: 'Command received' });
|
|
731
648
|
return;
|
|
732
649
|
}
|
|
733
|
-
// Handle the command installplugin from Home
|
|
734
650
|
if (command === 'installplugin') {
|
|
735
651
|
param = param.replace(/\*/g, '\\');
|
|
736
652
|
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
737
653
|
try {
|
|
738
654
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
739
655
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
740
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
741
656
|
}
|
|
742
657
|
catch (error) {
|
|
743
658
|
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
744
659
|
}
|
|
745
660
|
this.wssSendRestartRequired();
|
|
746
661
|
param = param.split('@')[0];
|
|
747
|
-
// Also add the plugin to matterbridge so no return!
|
|
748
662
|
if (param === 'matterbridge') {
|
|
749
|
-
// 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
|
|
750
663
|
res.json({ message: 'Command received' });
|
|
751
664
|
return;
|
|
752
665
|
}
|
|
753
666
|
}
|
|
754
|
-
// Handle the command addplugin from Home
|
|
755
667
|
if (command === 'addplugin' || command === 'installplugin') {
|
|
756
668
|
param = param.replace(/\*/g, '\\');
|
|
757
669
|
const plugin = await this.matterbridge.plugins.add(param);
|
|
758
670
|
if (plugin) {
|
|
759
671
|
if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
760
|
-
// 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
|
|
761
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
762
672
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
763
673
|
}
|
|
764
|
-
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true);
|
|
674
|
+
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true);
|
|
765
675
|
}
|
|
766
676
|
res.json({ message: 'Command received' });
|
|
767
677
|
this.wssSendRefreshRequired();
|
|
768
678
|
return;
|
|
769
679
|
}
|
|
770
|
-
// Handle the command removeplugin from Home
|
|
771
680
|
if (command === 'removeplugin') {
|
|
772
681
|
if (!this.matterbridge.plugins.has(param)) {
|
|
773
682
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
774
683
|
}
|
|
775
684
|
else {
|
|
776
685
|
const plugin = this.matterbridge.plugins.get(param);
|
|
777
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
686
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
778
687
|
await this.matterbridge.plugins.remove(param);
|
|
779
688
|
}
|
|
780
689
|
res.json({ message: 'Command received' });
|
|
781
690
|
this.wssSendRefreshRequired();
|
|
782
691
|
return;
|
|
783
692
|
}
|
|
784
|
-
// Handle the command enableplugin from Home
|
|
785
693
|
if (command === 'enableplugin') {
|
|
786
694
|
if (!this.matterbridge.plugins.has(param)) {
|
|
787
695
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -794,23 +702,20 @@ export class Frontend {
|
|
|
794
702
|
plugin.loaded = undefined;
|
|
795
703
|
plugin.started = undefined;
|
|
796
704
|
plugin.configured = undefined;
|
|
797
|
-
plugin.connected = undefined;
|
|
798
705
|
plugin.platform = undefined;
|
|
799
706
|
plugin.registeredDevices = undefined;
|
|
800
707
|
plugin.addedDevices = undefined;
|
|
801
708
|
await this.matterbridge.plugins.enable(param);
|
|
802
709
|
if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
|
|
803
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
804
710
|
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
805
711
|
}
|
|
806
|
-
this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true);
|
|
712
|
+
this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true);
|
|
807
713
|
}
|
|
808
714
|
}
|
|
809
715
|
res.json({ message: 'Command received' });
|
|
810
716
|
this.wssSendRefreshRequired();
|
|
811
717
|
return;
|
|
812
718
|
}
|
|
813
|
-
// Handle the command disableplugin from Home
|
|
814
719
|
if (command === 'disableplugin') {
|
|
815
720
|
if (!this.matterbridge.plugins.has(param)) {
|
|
816
721
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -818,7 +723,7 @@ export class Frontend {
|
|
|
818
723
|
else {
|
|
819
724
|
const plugin = this.matterbridge.plugins.get(param);
|
|
820
725
|
if (plugin && plugin.enabled) {
|
|
821
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
726
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
822
727
|
await this.matterbridge.plugins.disable(param);
|
|
823
728
|
}
|
|
824
729
|
}
|
|
@@ -827,7 +732,6 @@ export class Frontend {
|
|
|
827
732
|
return;
|
|
828
733
|
}
|
|
829
734
|
});
|
|
830
|
-
// Fallback for routing (must be the last route)
|
|
831
735
|
this.expressApp.get('*', (req, res) => {
|
|
832
736
|
this.log.debug('The frontend sent:', req.url);
|
|
833
737
|
this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -836,29 +740,24 @@ export class Frontend {
|
|
|
836
740
|
this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
837
741
|
}
|
|
838
742
|
async stop() {
|
|
839
|
-
// Close the http server
|
|
840
743
|
if (this.httpServer) {
|
|
841
744
|
this.httpServer.close();
|
|
842
745
|
this.httpServer.removeAllListeners();
|
|
843
746
|
this.httpServer = undefined;
|
|
844
747
|
this.log.debug('Frontend http server closed successfully');
|
|
845
748
|
}
|
|
846
|
-
// Close the https server
|
|
847
749
|
if (this.httpsServer) {
|
|
848
750
|
this.httpsServer.close();
|
|
849
751
|
this.httpsServer.removeAllListeners();
|
|
850
752
|
this.httpsServer = undefined;
|
|
851
753
|
this.log.debug('Frontend https server closed successfully');
|
|
852
754
|
}
|
|
853
|
-
// Remove listeners from the express app
|
|
854
755
|
if (this.expressApp) {
|
|
855
756
|
this.expressApp.removeAllListeners();
|
|
856
757
|
this.expressApp = undefined;
|
|
857
758
|
this.log.debug('Frontend app closed successfully');
|
|
858
759
|
}
|
|
859
|
-
// Close the WebSocket server
|
|
860
760
|
if (this.webSocketServer) {
|
|
861
|
-
// Close all active connections
|
|
862
761
|
this.webSocketServer.clients.forEach((client) => {
|
|
863
762
|
if (client.readyState === WebSocket.OPEN) {
|
|
864
763
|
client.close();
|
|
@@ -875,11 +774,25 @@ export class Frontend {
|
|
|
875
774
|
this.webSocketServer = undefined;
|
|
876
775
|
}
|
|
877
776
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
777
|
+
async getApiSettings() {
|
|
778
|
+
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
779
|
+
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
780
|
+
this.matterbridge.matterbridgeInformation.loggerLevel = this.log.logLevel;
|
|
781
|
+
this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
|
|
782
|
+
this.matterbridge.matterbridgeInformation.mattermdnsinterface = this.matterbridge.mdnsInterface;
|
|
783
|
+
this.matterbridge.matterbridgeInformation.matteripv4address = this.matterbridge.ipv4address;
|
|
784
|
+
this.matterbridge.matterbridgeInformation.matteripv6address = this.matterbridge.ipv6address;
|
|
785
|
+
this.matterbridge.matterbridgeInformation.matterPort = (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540;
|
|
786
|
+
this.matterbridge.matterbridgeInformation.matterDiscriminator = await this.matterbridge.nodeContext?.get('matterdiscriminator');
|
|
787
|
+
this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
|
|
788
|
+
this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.matterbridgePaired;
|
|
789
|
+
this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeQrPairingCode;
|
|
790
|
+
this.matterbridge.matterbridgeInformation.matterbridgeManualPairingCode = this.matterbridge.matterbridgeManualPairingCode;
|
|
791
|
+
this.matterbridge.matterbridgeInformation.matterbridgeFabricInformations = this.matterbridge.matterbridgeFabricInformations;
|
|
792
|
+
this.matterbridge.matterbridgeInformation.matterbridgeSessionInformations = this.matterbridge.matterbridgeSessionInformations;
|
|
793
|
+
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
794
|
+
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
795
|
+
}
|
|
883
796
|
getClusterTextFromDevice(device) {
|
|
884
797
|
const getAttribute = (device, cluster, attribute) => {
|
|
885
798
|
let value = undefined;
|
|
@@ -917,10 +830,10 @@ export class Frontend {
|
|
|
917
830
|
return '';
|
|
918
831
|
};
|
|
919
832
|
let attributes = '';
|
|
920
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
921
833
|
Object.entries(device.state).forEach(([clusterName, clusterAttributes]) => {
|
|
922
834
|
Object.entries(clusterAttributes).forEach(([attributeName, attributeValue]) => {
|
|
923
|
-
|
|
835
|
+
if (typeof attributeValue === 'undefined')
|
|
836
|
+
return;
|
|
924
837
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
925
838
|
attributes += `OnOff: ${attributeValue} `;
|
|
926
839
|
if (clusterName === 'switch' && attributeName === 'currentPosition')
|
|
@@ -991,11 +904,7 @@ export class Frontend {
|
|
|
991
904
|
});
|
|
992
905
|
return attributes.trimStart().trimEnd();
|
|
993
906
|
}
|
|
994
|
-
|
|
995
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
996
|
-
* @returns {BaseRegisteredPlugin[]} A promise that resolves to an array of BaseRegisteredPlugin objects.
|
|
997
|
-
*/
|
|
998
|
-
async getBaseRegisteredPlugins() {
|
|
907
|
+
getBaseRegisteredPlugins() {
|
|
999
908
|
const baseRegisteredPlugins = [];
|
|
1000
909
|
for (const plugin of this.matterbridge.plugins) {
|
|
1001
910
|
baseRegisteredPlugins.push({
|
|
@@ -1013,7 +922,6 @@ export class Frontend {
|
|
|
1013
922
|
started: plugin.started,
|
|
1014
923
|
configured: plugin.configured,
|
|
1015
924
|
paired: plugin.paired,
|
|
1016
|
-
connected: plugin.connected,
|
|
1017
925
|
fabricInformations: plugin.fabricInformations,
|
|
1018
926
|
sessionInformations: plugin.sessionInformations,
|
|
1019
927
|
registeredDevices: plugin.registeredDevices,
|
|
@@ -1026,14 +934,6 @@ export class Frontend {
|
|
|
1026
934
|
}
|
|
1027
935
|
return baseRegisteredPlugins;
|
|
1028
936
|
}
|
|
1029
|
-
/**
|
|
1030
|
-
* Handles incoming websocket messages for the Matterbridge.
|
|
1031
|
-
*
|
|
1032
|
-
* @param {Matterbridge} this - The Matterbridge instance.
|
|
1033
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1034
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1035
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1036
|
-
*/
|
|
1037
937
|
async wsMessageHandler(client, message) {
|
|
1038
938
|
let data;
|
|
1039
939
|
try {
|
|
@@ -1104,40 +1004,25 @@ export class Frontend {
|
|
|
1104
1004
|
await this.matterbridge.shutdownProcess();
|
|
1105
1005
|
return;
|
|
1106
1006
|
}
|
|
1007
|
+
else if (data.method === '/api/advertise') {
|
|
1008
|
+
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
1009
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes }));
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1107
1012
|
else if (data.method === '/api/settings') {
|
|
1108
|
-
|
|
1109
|
-
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
1110
|
-
this.matterbridge.matterbridgeInformation.loggerLevel = this.log.logLevel;
|
|
1111
|
-
this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
|
|
1112
|
-
this.matterbridge.matterbridgeInformation.mattermdnsinterface = (await this.matterbridge.nodeContext?.get('mattermdnsinterface', '')) || '';
|
|
1113
|
-
this.matterbridge.matterbridgeInformation.matteripv4address = (await this.matterbridge.nodeContext?.get('matteripv4address', '')) || '';
|
|
1114
|
-
this.matterbridge.matterbridgeInformation.matteripv6address = (await this.matterbridge.nodeContext?.get('matteripv6address', '')) || '';
|
|
1115
|
-
this.matterbridge.matterbridgeInformation.matterPort = (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540;
|
|
1116
|
-
this.matterbridge.matterbridgeInformation.matterDiscriminator = await this.matterbridge.nodeContext?.get('matterdiscriminator');
|
|
1117
|
-
this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
|
|
1118
|
-
this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.matterbridgePaired;
|
|
1119
|
-
this.matterbridge.matterbridgeInformation.matterbridgeConnected = this.matterbridge.matterbridgeConnected;
|
|
1120
|
-
this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeQrPairingCode;
|
|
1121
|
-
this.matterbridge.matterbridgeInformation.matterbridgeManualPairingCode = this.matterbridge.matterbridgeManualPairingCode;
|
|
1122
|
-
this.matterbridge.matterbridgeInformation.matterbridgeFabricInformations = this.matterbridge.matterbridgeFabricInformations;
|
|
1123
|
-
this.matterbridge.matterbridgeInformation.matterbridgeSessionInformations = Array.from(this.matterbridge.matterbridgeSessionInformations.values());
|
|
1124
|
-
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
1125
|
-
const response = { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
1126
|
-
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
|
|
1013
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: await this.getApiSettings() }));
|
|
1127
1014
|
return;
|
|
1128
1015
|
}
|
|
1129
1016
|
else if (data.method === '/api/plugins') {
|
|
1130
|
-
const response =
|
|
1017
|
+
const response = this.getBaseRegisteredPlugins();
|
|
1131
1018
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
|
|
1132
1019
|
return;
|
|
1133
1020
|
}
|
|
1134
1021
|
else if (data.method === '/api/devices') {
|
|
1135
1022
|
const devices = [];
|
|
1136
1023
|
this.matterbridge.devices.forEach(async (device) => {
|
|
1137
|
-
// Filter by pluginName if provided
|
|
1138
1024
|
if (data.params.pluginName && data.params.pluginName !== device.plugin)
|
|
1139
1025
|
return;
|
|
1140
|
-
// Check if the device has the required properties
|
|
1141
1026
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
|
|
1142
1027
|
return;
|
|
1143
1028
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -1217,7 +1102,6 @@ export class Frontend {
|
|
|
1217
1102
|
});
|
|
1218
1103
|
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
1219
1104
|
deviceTypes = [];
|
|
1220
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1221
1105
|
const name = childEndpoint.endpoint?.id;
|
|
1222
1106
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
1223
1107
|
clusterServers.forEach((clusterServer) => {
|
|
@@ -1300,73 +1184,44 @@ export class Frontend {
|
|
|
1300
1184
|
return;
|
|
1301
1185
|
}
|
|
1302
1186
|
}
|
|
1303
|
-
/**
|
|
1304
|
-
* Sends a WebSocket message to all connected clients.
|
|
1305
|
-
*
|
|
1306
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1307
|
-
* @param {string} time - The time string of the message
|
|
1308
|
-
* @param {string} name - The logger name of the message
|
|
1309
|
-
* @param {string} message - The content of the message.
|
|
1310
|
-
*/
|
|
1311
1187
|
wssSendMessage(level, time, name, message) {
|
|
1312
1188
|
if (!level || !time || !name || !message)
|
|
1313
1189
|
return;
|
|
1314
|
-
// Remove ANSI escape codes from the message
|
|
1315
|
-
// eslint-disable-next-line no-control-regex
|
|
1316
1190
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1317
|
-
// Remove leading asterisks from the message
|
|
1318
1191
|
message = message.replace(/^\*+/, '');
|
|
1319
|
-
// Replace all occurrences of \t and \n
|
|
1320
1192
|
message = message.replace(/[\t\n]/g, '');
|
|
1321
|
-
// Remove non-printable characters
|
|
1322
|
-
// eslint-disable-next-line no-control-regex
|
|
1323
1193
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1324
|
-
// Replace all occurrences of \" with "
|
|
1325
1194
|
message = message.replace(/\\"/g, '"');
|
|
1326
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1327
1195
|
const maxContinuousLength = 100;
|
|
1328
1196
|
const keepStartLength = 20;
|
|
1329
1197
|
const keepEndLength = 20;
|
|
1330
|
-
// Split the message into words
|
|
1331
1198
|
message = message
|
|
1332
1199
|
.split(' ')
|
|
1333
1200
|
.map((word) => {
|
|
1334
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1335
1201
|
if (word.length > maxContinuousLength) {
|
|
1336
1202
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1337
1203
|
}
|
|
1338
1204
|
return word;
|
|
1339
1205
|
})
|
|
1340
1206
|
.join(' ');
|
|
1341
|
-
// Send the message to all connected clients
|
|
1342
1207
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1343
1208
|
if (client.readyState === WebSocket.OPEN) {
|
|
1344
1209
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1345
1210
|
}
|
|
1346
1211
|
});
|
|
1347
1212
|
}
|
|
1348
|
-
/**
|
|
1349
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1350
|
-
*
|
|
1351
|
-
*/
|
|
1352
1213
|
wssSendRefreshRequired() {
|
|
1353
1214
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1354
1215
|
this.matterbridge.matterbridgeInformation.refreshRequired = true;
|
|
1355
|
-
// Send the message to all connected clients
|
|
1356
1216
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1357
1217
|
if (client.readyState === WebSocket.OPEN) {
|
|
1358
1218
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: {} }));
|
|
1359
1219
|
}
|
|
1360
1220
|
});
|
|
1361
1221
|
}
|
|
1362
|
-
/**
|
|
1363
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1364
|
-
*
|
|
1365
|
-
*/
|
|
1366
1222
|
wssSendRestartRequired() {
|
|
1367
1223
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1368
1224
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1369
|
-
// Send the message to all connected clients
|
|
1370
1225
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1371
1226
|
if (client.readyState === WebSocket.OPEN) {
|
|
1372
1227
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
@@ -1374,4 +1229,3 @@ export class Frontend {
|
|
|
1374
1229
|
});
|
|
1375
1230
|
}
|
|
1376
1231
|
}
|
|
1377
|
-
//# sourceMappingURL=frontend.js.map
|