matterbridge 3.0.1 → 3.0.2-dev-20250509-ae61aa7
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 +26 -3
- package/README.md +2 -2
- 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 +37 -341
- package/dist/index.js +2 -28
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.js +0 -2
- package/dist/matter/types.js +0 -2
- package/dist/matterbridge.js +107 -748
- package/dist/matterbridgeAccessoryPlatform.js +0 -34
- package/dist/matterbridgeBehaviors.js +109 -48
- package/dist/matterbridgeDeviceTypes.js +12 -431
- package/dist/matterbridgeDynamicPlatform.js +0 -34
- package/dist/matterbridgeEndpoint.js +16 -814
- package/dist/matterbridgeEndpointHelpers.js +44 -148
- package/dist/matterbridgePlatform.js +7 -225
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +3 -264
- package/dist/roboticVacuumCleaner.js +87 -0
- package/dist/shelly.js +6 -146
- package/dist/storage/export.js +0 -1
- package/dist/update.js +1 -53
- package/dist/utils/colorUtils.js +2 -205
- package/dist/utils/{parameter.js → commandLine.js} +1 -54
- package/dist/utils/copyDirectory.js +1 -37
- package/dist/utils/createZip.js +2 -42
- package/dist/utils/deepCopy.js +8 -43
- package/dist/utils/deepEqual.js +7 -69
- package/dist/utils/export.js +2 -2
- package/dist/utils/hex.js +27 -0
- package/dist/utils/isvalid.js +3 -93
- package/dist/utils/network.js +7 -78
- package/dist/utils/wait.js +5 -48
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- package/dist/cli.d.ts +0 -29
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cluster/export.d.ts +0 -2
- package/dist/cluster/export.d.ts.map +0 -1
- package/dist/cluster/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -27
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -114
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -240
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/index.d.ts +0 -35
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -433
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -40
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1166
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -494
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -40
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -956
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2706
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -294
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -187
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -273
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -92
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts +0 -32
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -61
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -32
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -38
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -31
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -53
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/export.d.ts +0 -10
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -95
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/network.d.ts +0 -69
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/parameter.d.ts +0 -58
- 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/dist/frontend.js
CHANGED
|
@@ -1,111 +1,28 @@
|
|
|
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, Lifecycle } from '@matter/main';
|
|
25
|
-
// Node modules
|
|
26
2
|
import { createServer } from 'node:http';
|
|
27
3
|
import https from 'node:https';
|
|
28
4
|
import os from 'node:os';
|
|
29
5
|
import path from 'node:path';
|
|
30
6
|
import { promises as fs } from 'node:fs';
|
|
31
|
-
// Third-party modules
|
|
32
7
|
import express from 'express';
|
|
33
8
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
34
9
|
import multer from 'multer';
|
|
35
|
-
// AnsiLogger module
|
|
36
10
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from './logger/export.js';
|
|
37
|
-
|
|
38
|
-
import { createZip, deepCopy, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean } from './utils/export.js';
|
|
11
|
+
import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean } from './utils/export.js';
|
|
39
12
|
import { plg } from './matterbridgeTypes.js';
|
|
40
13
|
import { hasParameter } from './utils/export.js';
|
|
41
14
|
import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
|
|
42
|
-
/**
|
|
43
|
-
* Websocket message ID for logging.
|
|
44
|
-
* @constant {number}
|
|
45
|
-
*/
|
|
46
15
|
export const WS_ID_LOG = 0;
|
|
47
|
-
/**
|
|
48
|
-
* Websocket message ID indicating a refresh is needed.
|
|
49
|
-
* @constant {number}
|
|
50
|
-
*/
|
|
51
16
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
52
|
-
/**
|
|
53
|
-
* Websocket message ID indicating a restart is needed.
|
|
54
|
-
* @constant {number}
|
|
55
|
-
*/
|
|
56
17
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
57
|
-
/**
|
|
58
|
-
* Websocket message ID indicating a cpu update.
|
|
59
|
-
* @constant {number}
|
|
60
|
-
*/
|
|
61
18
|
export const WS_ID_CPU_UPDATE = 3;
|
|
62
|
-
/**
|
|
63
|
-
* Websocket message ID indicating a memory update.
|
|
64
|
-
* @constant {number}
|
|
65
|
-
*/
|
|
66
19
|
export const WS_ID_MEMORY_UPDATE = 4;
|
|
67
|
-
/**
|
|
68
|
-
* Websocket message ID indicating an uptime update.
|
|
69
|
-
* @constant {number}
|
|
70
|
-
*/
|
|
71
20
|
export const WS_ID_UPTIME_UPDATE = 5;
|
|
72
|
-
/**
|
|
73
|
-
* Websocket message ID indicating a snackbar message.
|
|
74
|
-
* @constant {number}
|
|
75
|
-
*/
|
|
76
21
|
export const WS_ID_SNACKBAR = 6;
|
|
77
|
-
/**
|
|
78
|
-
* Websocket message ID indicating matterbridge has un update available.
|
|
79
|
-
* @constant {number}
|
|
80
|
-
*/
|
|
81
22
|
export const WS_ID_UPDATE_NEEDED = 7;
|
|
82
|
-
/**
|
|
83
|
-
* Websocket message ID indicating a state update.
|
|
84
|
-
* @constant {number}
|
|
85
|
-
*/
|
|
86
23
|
export const WS_ID_STATEUPDATE = 8;
|
|
87
|
-
/**
|
|
88
|
-
* Websocket message ID indicating to close a permanent snackbar message.
|
|
89
|
-
* @constant {number}
|
|
90
|
-
*/
|
|
91
24
|
export const WS_ID_CLOSE_SNACKBAR = 9;
|
|
92
|
-
/**
|
|
93
|
-
* Websocket message ID indicating a shelly system update.
|
|
94
|
-
* check:
|
|
95
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/check
|
|
96
|
-
* perform:
|
|
97
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/perform
|
|
98
|
-
* @constant {number}
|
|
99
|
-
*/
|
|
100
25
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
101
|
-
/**
|
|
102
|
-
* Websocket message ID indicating a shelly main update.
|
|
103
|
-
* check:
|
|
104
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/check
|
|
105
|
-
* perform:
|
|
106
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/perform
|
|
107
|
-
* @constant {number}
|
|
108
|
-
*/
|
|
109
26
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
110
27
|
export class Frontend {
|
|
111
28
|
matterbridge;
|
|
@@ -116,14 +33,9 @@ export class Frontend {
|
|
|
116
33
|
httpServer;
|
|
117
34
|
httpsServer;
|
|
118
35
|
webSocketServer;
|
|
119
|
-
prevCpus = deepCopy(os.cpus());
|
|
120
|
-
lastCpuUsage = 0;
|
|
121
|
-
memoryData = [];
|
|
122
|
-
memoryInterval;
|
|
123
|
-
memoryTimeout;
|
|
124
36
|
constructor(matterbridge) {
|
|
125
37
|
this.matterbridge = matterbridge;
|
|
126
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
38
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
127
39
|
}
|
|
128
40
|
set logLevel(logLevel) {
|
|
129
41
|
this.log.logLevel = logLevel;
|
|
@@ -131,43 +43,13 @@ export class Frontend {
|
|
|
131
43
|
async start(port = 8283) {
|
|
132
44
|
this.port = port;
|
|
133
45
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
134
|
-
// Initialize multer with the upload directory
|
|
135
46
|
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
136
47
|
await fs.mkdir(uploadDir, { recursive: true });
|
|
137
48
|
const upload = multer({ dest: uploadDir });
|
|
138
|
-
// Create the express app that serves the frontend
|
|
139
49
|
this.expressApp = express();
|
|
140
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
141
|
-
/*
|
|
142
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
143
|
-
for (const method of methods) {
|
|
144
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
145
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
146
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
147
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
148
|
-
try {
|
|
149
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
150
|
-
return original(path, ...rest);
|
|
151
|
-
} catch (err) {
|
|
152
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
153
|
-
throw err;
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
*/
|
|
158
|
-
// Log all requests to the server for debugging
|
|
159
|
-
/*
|
|
160
|
-
this.expressApp.use((req, res, next) => {
|
|
161
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
162
|
-
next();
|
|
163
|
-
});
|
|
164
|
-
*/
|
|
165
|
-
// Serve static files from '/static' endpoint
|
|
166
50
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
167
51
|
if (!hasParameter('ssl')) {
|
|
168
|
-
// Create an HTTP server and attach the express app
|
|
169
52
|
this.httpServer = createServer(this.expressApp);
|
|
170
|
-
// Listen on the specified port
|
|
171
53
|
if (hasParameter('ingress')) {
|
|
172
54
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
173
55
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -181,7 +63,6 @@ export class Frontend {
|
|
|
181
63
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
182
64
|
});
|
|
183
65
|
}
|
|
184
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
185
66
|
this.httpServer.on('error', (error) => {
|
|
186
67
|
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
187
68
|
switch (error.code) {
|
|
@@ -197,7 +78,6 @@ export class Frontend {
|
|
|
197
78
|
});
|
|
198
79
|
}
|
|
199
80
|
else {
|
|
200
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
201
81
|
let cert;
|
|
202
82
|
try {
|
|
203
83
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -225,9 +105,7 @@ export class Frontend {
|
|
|
225
105
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
226
106
|
}
|
|
227
107
|
const serverOptions = { cert, key, ca };
|
|
228
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
229
108
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
230
|
-
// Listen on the specified port
|
|
231
109
|
if (hasParameter('ingress')) {
|
|
232
110
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
233
111
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -241,7 +119,6 @@ export class Frontend {
|
|
|
241
119
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
242
120
|
});
|
|
243
121
|
}
|
|
244
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
245
122
|
this.httpsServer.on('error', (error) => {
|
|
246
123
|
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
247
124
|
switch (error.code) {
|
|
@@ -258,18 +135,16 @@ export class Frontend {
|
|
|
258
135
|
}
|
|
259
136
|
if (this.initializeError)
|
|
260
137
|
return;
|
|
261
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
262
138
|
const wssPort = this.port;
|
|
263
139
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
264
140
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
265
141
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
266
142
|
const clientIp = request.socket.remoteAddress;
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
143
|
+
let callbackLogLevel = "notice";
|
|
144
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
145
|
+
callbackLogLevel = "info";
|
|
146
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
147
|
+
callbackLogLevel = "debug";
|
|
273
148
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
274
149
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
275
150
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -303,7 +178,6 @@ export class Frontend {
|
|
|
303
178
|
this.webSocketServer.on('error', (ws, error) => {
|
|
304
179
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
305
180
|
});
|
|
306
|
-
// Subscribe to cli events
|
|
307
181
|
const { cliEmitter } = await import('./cli.js');
|
|
308
182
|
cliEmitter.removeAllListeners();
|
|
309
183
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
@@ -315,7 +189,6 @@ export class Frontend {
|
|
|
315
189
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
316
190
|
this.wssSendCpuUpdate(cpuUsage);
|
|
317
191
|
});
|
|
318
|
-
// Endpoint to validate login code
|
|
319
192
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
320
193
|
const { password } = req.body;
|
|
321
194
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -334,27 +207,23 @@ export class Frontend {
|
|
|
334
207
|
this.log.warn('/api/login error wrong password');
|
|
335
208
|
res.json({ valid: false });
|
|
336
209
|
}
|
|
337
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
338
210
|
}
|
|
339
211
|
catch (error) {
|
|
340
212
|
this.log.error('/api/login error getting password');
|
|
341
213
|
res.json({ valid: false });
|
|
342
214
|
}
|
|
343
215
|
});
|
|
344
|
-
// Endpoint to provide health check for docker
|
|
345
216
|
this.expressApp.get('/health', (req, res) => {
|
|
346
217
|
this.log.debug('Express received /health');
|
|
347
218
|
const healthStatus = {
|
|
348
|
-
status: 'ok',
|
|
349
|
-
uptime: process.uptime(),
|
|
350
|
-
timestamp: new Date().toISOString(),
|
|
219
|
+
status: 'ok',
|
|
220
|
+
uptime: process.uptime(),
|
|
221
|
+
timestamp: new Date().toISOString(),
|
|
351
222
|
};
|
|
352
223
|
res.status(200).json(healthStatus);
|
|
353
224
|
});
|
|
354
|
-
// Endpoint to provide memory usage details
|
|
355
225
|
this.expressApp.get('/memory', async (req, res) => {
|
|
356
226
|
this.log.debug('Express received /memory');
|
|
357
|
-
// Memory usage from process
|
|
358
227
|
const memoryUsageRaw = process.memoryUsage();
|
|
359
228
|
const memoryUsage = {
|
|
360
229
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -363,13 +232,10 @@ export class Frontend {
|
|
|
363
232
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
364
233
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
365
234
|
};
|
|
366
|
-
// V8 heap statistics
|
|
367
235
|
const { default: v8 } = await import('node:v8');
|
|
368
236
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
369
237
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
370
|
-
// Format heapStats
|
|
371
238
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
372
|
-
// Format heapSpaces
|
|
373
239
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
374
240
|
...space,
|
|
375
241
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -387,23 +253,19 @@ export class Frontend {
|
|
|
387
253
|
};
|
|
388
254
|
res.status(200).json(memoryReport);
|
|
389
255
|
});
|
|
390
|
-
// Endpoint to provide settings
|
|
391
256
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
392
257
|
this.log.debug('The frontend sent /api/settings');
|
|
393
258
|
res.json(await this.getApiSettings());
|
|
394
259
|
});
|
|
395
|
-
// Endpoint to provide plugins
|
|
396
260
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
397
261
|
this.log.debug('The frontend sent /api/plugins');
|
|
398
262
|
res.json(this.getBaseRegisteredPlugins());
|
|
399
263
|
});
|
|
400
|
-
// Endpoint to provide devices
|
|
401
264
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
402
265
|
this.log.debug('The frontend sent /api/devices');
|
|
403
266
|
const devices = await this.getDevices();
|
|
404
267
|
res.json(devices);
|
|
405
268
|
});
|
|
406
|
-
// Endpoint to view the matterbridge log
|
|
407
269
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
408
270
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
409
271
|
try {
|
|
@@ -416,7 +278,6 @@ export class Frontend {
|
|
|
416
278
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
417
279
|
}
|
|
418
280
|
});
|
|
419
|
-
// Endpoint to view the matter.js log
|
|
420
281
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
421
282
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
422
283
|
try {
|
|
@@ -429,7 +290,6 @@ export class Frontend {
|
|
|
429
290
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
430
291
|
}
|
|
431
292
|
});
|
|
432
|
-
// Endpoint to view the shelly log
|
|
433
293
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
434
294
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
435
295
|
try {
|
|
@@ -442,7 +302,6 @@ export class Frontend {
|
|
|
442
302
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
443
303
|
}
|
|
444
304
|
});
|
|
445
|
-
// Endpoint to download the matterbridge log
|
|
446
305
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
447
306
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
|
|
448
307
|
try {
|
|
@@ -455,7 +314,6 @@ export class Frontend {
|
|
|
455
314
|
this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
|
|
456
315
|
}
|
|
457
316
|
res.type('text/plain');
|
|
458
|
-
// res.download(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
|
|
459
317
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
|
|
460
318
|
if (error) {
|
|
461
319
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
@@ -463,7 +321,6 @@ export class Frontend {
|
|
|
463
321
|
}
|
|
464
322
|
});
|
|
465
323
|
});
|
|
466
|
-
// Endpoint to download the matter log
|
|
467
324
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
468
325
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
|
|
469
326
|
try {
|
|
@@ -483,7 +340,6 @@ export class Frontend {
|
|
|
483
340
|
}
|
|
484
341
|
});
|
|
485
342
|
});
|
|
486
|
-
// Endpoint to download the shelly log
|
|
487
343
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
488
344
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
489
345
|
try {
|
|
@@ -503,7 +359,6 @@ export class Frontend {
|
|
|
503
359
|
}
|
|
504
360
|
});
|
|
505
361
|
});
|
|
506
|
-
// Endpoint to download the matterbridge storage directory
|
|
507
362
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
508
363
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
509
364
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -514,7 +369,6 @@ export class Frontend {
|
|
|
514
369
|
}
|
|
515
370
|
});
|
|
516
371
|
});
|
|
517
|
-
// Endpoint to download the matter storage file
|
|
518
372
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
519
373
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
520
374
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
@@ -525,7 +379,6 @@ export class Frontend {
|
|
|
525
379
|
}
|
|
526
380
|
});
|
|
527
381
|
});
|
|
528
|
-
// Endpoint to download the matterbridge plugin directory
|
|
529
382
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
530
383
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
531
384
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -536,11 +389,9 @@ export class Frontend {
|
|
|
536
389
|
}
|
|
537
390
|
});
|
|
538
391
|
});
|
|
539
|
-
// Endpoint to download the matterbridge plugin config files
|
|
540
392
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
541
393
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
542
394
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
543
|
-
// 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')));
|
|
544
395
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
545
396
|
if (error) {
|
|
546
397
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
@@ -548,7 +399,6 @@ export class Frontend {
|
|
|
548
399
|
}
|
|
549
400
|
});
|
|
550
401
|
});
|
|
551
|
-
// Endpoint to download the matterbridge plugin config files
|
|
552
402
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
553
403
|
this.log.debug('The frontend sent /api/download-backup');
|
|
554
404
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -558,7 +408,6 @@ export class Frontend {
|
|
|
558
408
|
}
|
|
559
409
|
});
|
|
560
410
|
});
|
|
561
|
-
// Endpoint to upload a package
|
|
562
411
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
563
412
|
const { filename } = req.body;
|
|
564
413
|
const file = req.file;
|
|
@@ -568,13 +417,10 @@ export class Frontend {
|
|
|
568
417
|
return;
|
|
569
418
|
}
|
|
570
419
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
571
|
-
// Define the path where the plugin file will be saved
|
|
572
420
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
573
421
|
try {
|
|
574
|
-
// Move the uploaded file to the specified path
|
|
575
422
|
await fs.rename(file.path, filePath);
|
|
576
423
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
577
|
-
// Install the plugin package
|
|
578
424
|
if (filename.endsWith('.tgz')) {
|
|
579
425
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
580
426
|
this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
|
|
@@ -593,7 +439,6 @@ export class Frontend {
|
|
|
593
439
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
594
440
|
}
|
|
595
441
|
});
|
|
596
|
-
// Fallback for routing (must be the last route)
|
|
597
442
|
this.expressApp.use((req, res) => {
|
|
598
443
|
this.log.debug('The frontend sent:', req.url);
|
|
599
444
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -601,29 +446,38 @@ export class Frontend {
|
|
|
601
446
|
this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
602
447
|
}
|
|
603
448
|
async stop() {
|
|
604
|
-
// Close the http server
|
|
605
449
|
if (this.httpServer) {
|
|
606
|
-
this.httpServer.close()
|
|
450
|
+
this.httpServer.close((error) => {
|
|
451
|
+
if (error) {
|
|
452
|
+
this.log.error(`Error closing http server: ${error}`);
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
this.log.debug('Http server closed successfully');
|
|
456
|
+
}
|
|
457
|
+
});
|
|
607
458
|
this.httpServer.removeAllListeners();
|
|
608
459
|
this.httpServer = undefined;
|
|
609
460
|
this.log.debug('Frontend http server closed successfully');
|
|
610
461
|
}
|
|
611
|
-
// Close the https server
|
|
612
462
|
if (this.httpsServer) {
|
|
613
|
-
this.httpsServer.close()
|
|
463
|
+
this.httpsServer.close((error) => {
|
|
464
|
+
if (error) {
|
|
465
|
+
this.log.error(`Error closing https server: ${error}`);
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
this.log.debug('Https server closed successfully');
|
|
469
|
+
}
|
|
470
|
+
});
|
|
614
471
|
this.httpsServer.removeAllListeners();
|
|
615
472
|
this.httpsServer = undefined;
|
|
616
473
|
this.log.debug('Frontend https server closed successfully');
|
|
617
474
|
}
|
|
618
|
-
// Remove listeners from the express app
|
|
619
475
|
if (this.expressApp) {
|
|
620
476
|
this.expressApp.removeAllListeners();
|
|
621
477
|
this.expressApp = undefined;
|
|
622
478
|
this.log.debug('Frontend app closed successfully');
|
|
623
479
|
}
|
|
624
|
-
// Close the WebSocket server
|
|
625
480
|
if (this.webSocketServer) {
|
|
626
|
-
// Close all active connections
|
|
627
481
|
this.webSocketServer.clients.forEach((client) => {
|
|
628
482
|
if (client.readyState === WebSocket.OPEN) {
|
|
629
483
|
client.close();
|
|
@@ -640,7 +494,6 @@ export class Frontend {
|
|
|
640
494
|
this.webSocketServer = undefined;
|
|
641
495
|
}
|
|
642
496
|
}
|
|
643
|
-
// Function to format bytes to KB, MB, or GB
|
|
644
497
|
formatMemoryUsage = (bytes) => {
|
|
645
498
|
if (bytes >= 1024 ** 3) {
|
|
646
499
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -652,7 +505,6 @@ export class Frontend {
|
|
|
652
505
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
653
506
|
}
|
|
654
507
|
};
|
|
655
|
-
// Function to format system uptime with only the most significant unit
|
|
656
508
|
formatOsUpTime = (seconds) => {
|
|
657
509
|
if (seconds >= 86400) {
|
|
658
510
|
const days = Math.floor(seconds / 86400);
|
|
@@ -668,13 +520,8 @@ export class Frontend {
|
|
|
668
520
|
}
|
|
669
521
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
670
522
|
};
|
|
671
|
-
/**
|
|
672
|
-
* Retrieves the api settings data.
|
|
673
|
-
* @returns {Promise<object>} A promise that resolve in the api settings object.
|
|
674
|
-
*/
|
|
675
523
|
async getApiSettings() {
|
|
676
524
|
const { lastCpuUsage } = await import('./cli.js');
|
|
677
|
-
// Update the system information
|
|
678
525
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
679
526
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
680
527
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -683,7 +530,6 @@ export class Frontend {
|
|
|
683
530
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
684
531
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
685
532
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
686
|
-
// Update the matterbridge information
|
|
687
533
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
688
534
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
689
535
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -702,11 +548,6 @@ export class Frontend {
|
|
|
702
548
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
703
549
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
704
550
|
}
|
|
705
|
-
/**
|
|
706
|
-
* Retrieves the reachable attribute.
|
|
707
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
708
|
-
* @returns {boolean} The reachable attribute.
|
|
709
|
-
*/
|
|
710
551
|
getReachability(device) {
|
|
711
552
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
712
553
|
return false;
|
|
@@ -731,20 +572,13 @@ export class Frontend {
|
|
|
731
572
|
}
|
|
732
573
|
return;
|
|
733
574
|
};
|
|
734
|
-
// Root endpoint
|
|
735
575
|
if (device.hasClusterServer(PowerSource.Cluster.id))
|
|
736
576
|
return powerSource(device);
|
|
737
|
-
// Child endpoints
|
|
738
577
|
for (const child of device.getChildEndpoints()) {
|
|
739
578
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
740
579
|
return powerSource(child);
|
|
741
580
|
}
|
|
742
581
|
}
|
|
743
|
-
/**
|
|
744
|
-
* Retrieves the cluster text description from a given device.
|
|
745
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
746
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
747
|
-
*/
|
|
748
582
|
getClusterTextFromDevice(device) {
|
|
749
583
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
750
584
|
return '';
|
|
@@ -786,7 +620,6 @@ export class Frontend {
|
|
|
786
620
|
let attributes = '';
|
|
787
621
|
let supportedModes = [];
|
|
788
622
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
789
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
790
623
|
if (typeof attributeValue === 'undefined')
|
|
791
624
|
return;
|
|
792
625
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -878,13 +711,8 @@ export class Frontend {
|
|
|
878
711
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
879
712
|
attributes += `${getUserLabel(device)} `;
|
|
880
713
|
});
|
|
881
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
882
714
|
return attributes.trimStart().trimEnd();
|
|
883
715
|
}
|
|
884
|
-
/**
|
|
885
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
886
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
887
|
-
*/
|
|
888
716
|
getBaseRegisteredPlugins() {
|
|
889
717
|
const baseRegisteredPlugins = [];
|
|
890
718
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -923,18 +751,11 @@ export class Frontend {
|
|
|
923
751
|
}
|
|
924
752
|
return baseRegisteredPlugins;
|
|
925
753
|
}
|
|
926
|
-
/**
|
|
927
|
-
* Retrieves the devices from Matterbridge.
|
|
928
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
929
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices.
|
|
930
|
-
*/
|
|
931
754
|
async getDevices(pluginName) {
|
|
932
755
|
const devices = [];
|
|
933
756
|
this.matterbridge.devices.forEach(async (device) => {
|
|
934
|
-
// Filter by pluginName if provided
|
|
935
757
|
if (pluginName && pluginName !== device.plugin)
|
|
936
758
|
return;
|
|
937
|
-
// Check if the device has the required properties
|
|
938
759
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
939
760
|
return;
|
|
940
761
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -954,13 +775,6 @@ export class Frontend {
|
|
|
954
775
|
});
|
|
955
776
|
return devices;
|
|
956
777
|
}
|
|
957
|
-
/**
|
|
958
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
959
|
-
*
|
|
960
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
961
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
962
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
963
|
-
*/
|
|
964
778
|
async wsMessageHandler(client, message) {
|
|
965
779
|
let data;
|
|
966
780
|
try {
|
|
@@ -1007,10 +821,8 @@ export class Frontend {
|
|
|
1007
821
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1008
822
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1009
823
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1010
|
-
// The install comes from InstallPlugins
|
|
1011
824
|
this.matterbridge.plugins.add(packageName).then((plugin) => {
|
|
1012
825
|
if (plugin) {
|
|
1013
|
-
// The plugin is not registered
|
|
1014
826
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1015
827
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
1016
828
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
@@ -1018,7 +830,6 @@ export class Frontend {
|
|
|
1018
830
|
});
|
|
1019
831
|
}
|
|
1020
832
|
else {
|
|
1021
|
-
// The plugin is already registered
|
|
1022
833
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1023
834
|
this.wssSendRefreshRequired('plugins');
|
|
1024
835
|
this.wssSendRestartRequired();
|
|
@@ -1026,7 +837,6 @@ export class Frontend {
|
|
|
1026
837
|
});
|
|
1027
838
|
}
|
|
1028
839
|
else {
|
|
1029
|
-
// The package is matterbridge
|
|
1030
840
|
if (this.matterbridge.restartMode !== '') {
|
|
1031
841
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1032
842
|
this.matterbridge.shutdownProcess();
|
|
@@ -1048,7 +858,6 @@ export class Frontend {
|
|
|
1048
858
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1049
859
|
return;
|
|
1050
860
|
}
|
|
1051
|
-
// The package is a plugin
|
|
1052
861
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1053
862
|
if (plugin) {
|
|
1054
863
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
@@ -1057,7 +866,6 @@ export class Frontend {
|
|
|
1057
866
|
this.wssSendRefreshRequired('plugins');
|
|
1058
867
|
this.wssSendRefreshRequired('devices');
|
|
1059
868
|
}
|
|
1060
|
-
// Uninstall the package
|
|
1061
869
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1062
870
|
this.matterbridge
|
|
1063
871
|
.spawnCommand('npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1334,7 +1142,6 @@ export class Frontend {
|
|
|
1334
1142
|
});
|
|
1335
1143
|
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
1336
1144
|
deviceTypes = [];
|
|
1337
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1338
1145
|
const name = childEndpoint.endpoint?.id;
|
|
1339
1146
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
1340
1147
|
clusterServers.forEach((clusterServer) => {
|
|
@@ -1405,7 +1212,7 @@ export class Frontend {
|
|
|
1405
1212
|
}
|
|
1406
1213
|
else if (data.method === '/api/action') {
|
|
1407
1214
|
if (!isValidString(data.params.plugin, 5) || !isValidString(data.params.action, 1)) {
|
|
1408
|
-
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter
|
|
1215
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/action' }));
|
|
1409
1216
|
return;
|
|
1410
1217
|
}
|
|
1411
1218
|
const plugin = this.matterbridge.plugins.get(data.params.plugin);
|
|
@@ -1448,22 +1255,22 @@ export class Frontend {
|
|
|
1448
1255
|
if (isValidString(data.params.value, 4)) {
|
|
1449
1256
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1450
1257
|
if (data.params.value === 'Debug') {
|
|
1451
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1258
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1452
1259
|
}
|
|
1453
1260
|
else if (data.params.value === 'Info') {
|
|
1454
|
-
await this.matterbridge.setLogLevel("info"
|
|
1261
|
+
await this.matterbridge.setLogLevel("info");
|
|
1455
1262
|
}
|
|
1456
1263
|
else if (data.params.value === 'Notice') {
|
|
1457
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1264
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1458
1265
|
}
|
|
1459
1266
|
else if (data.params.value === 'Warn') {
|
|
1460
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1267
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1461
1268
|
}
|
|
1462
1269
|
else if (data.params.value === 'Error') {
|
|
1463
|
-
await this.matterbridge.setLogLevel("error"
|
|
1270
|
+
await this.matterbridge.setLogLevel("error");
|
|
1464
1271
|
}
|
|
1465
1272
|
else if (data.params.value === 'Fatal') {
|
|
1466
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1273
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1467
1274
|
}
|
|
1468
1275
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1469
1276
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1474,7 +1281,6 @@ export class Frontend {
|
|
|
1474
1281
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1475
1282
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1476
1283
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1477
|
-
// Create the file logger for matterbridge
|
|
1478
1284
|
if (data.params.value)
|
|
1479
1285
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1480
1286
|
else
|
|
@@ -1630,19 +1436,15 @@ export class Frontend {
|
|
|
1630
1436
|
return;
|
|
1631
1437
|
}
|
|
1632
1438
|
const config = plugin.configJson;
|
|
1633
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1634
1439
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1635
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1636
1440
|
if (select === 'serial')
|
|
1637
1441
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1638
1442
|
if (select === 'name')
|
|
1639
1443
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1640
1444
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1641
|
-
// Remove postfix from the serial if it exists
|
|
1642
1445
|
if (config.postfix) {
|
|
1643
1446
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1644
1447
|
}
|
|
1645
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1646
1448
|
if (isValidArray(config.whiteList, 1)) {
|
|
1647
1449
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1648
1450
|
config.whiteList.push(data.params.serial);
|
|
@@ -1651,13 +1453,12 @@ export class Frontend {
|
|
|
1651
1453
|
config.whiteList.push(data.params.name);
|
|
1652
1454
|
}
|
|
1653
1455
|
}
|
|
1654
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1655
1456
|
if (isValidArray(config.blackList, 1)) {
|
|
1656
1457
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1657
|
-
config.blackList = config.blackList.filter((
|
|
1458
|
+
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
1658
1459
|
}
|
|
1659
1460
|
else if (select === 'name' && config.blackList.includes(data.params.name)) {
|
|
1660
|
-
config.blackList = config.blackList.filter((
|
|
1461
|
+
config.blackList = config.blackList.filter((item) => item !== data.params.name);
|
|
1661
1462
|
}
|
|
1662
1463
|
}
|
|
1663
1464
|
if (plugin.platform)
|
|
@@ -1681,9 +1482,7 @@ export class Frontend {
|
|
|
1681
1482
|
return;
|
|
1682
1483
|
}
|
|
1683
1484
|
const config = plugin.configJson;
|
|
1684
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1685
1485
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1686
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1687
1486
|
if (select === 'serial')
|
|
1688
1487
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1689
1488
|
if (select === 'name')
|
|
@@ -1692,16 +1491,14 @@ export class Frontend {
|
|
|
1692
1491
|
if (config.postfix) {
|
|
1693
1492
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1694
1493
|
}
|
|
1695
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1696
1494
|
if (isValidArray(config.whiteList, 1)) {
|
|
1697
1495
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1698
|
-
config.whiteList = config.whiteList.filter((
|
|
1496
|
+
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
1699
1497
|
}
|
|
1700
1498
|
else if (select === 'name' && config.whiteList.includes(data.params.name)) {
|
|
1701
|
-
config.whiteList = config.whiteList.filter((
|
|
1499
|
+
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1702
1500
|
}
|
|
1703
1501
|
}
|
|
1704
|
-
// Add the serial to the blackList
|
|
1705
1502
|
if (isValidArray(config.blackList)) {
|
|
1706
1503
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1707
1504
|
config.blackList.push(data.params.serial);
|
|
@@ -1734,214 +1531,114 @@ export class Frontend {
|
|
|
1734
1531
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1735
1532
|
}
|
|
1736
1533
|
}
|
|
1737
|
-
/**
|
|
1738
|
-
* Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1739
|
-
*
|
|
1740
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1741
|
-
* @param {string} time - The time string of the message
|
|
1742
|
-
* @param {string} name - The logger name of the message
|
|
1743
|
-
* @param {string} message - The content of the message.
|
|
1744
|
-
*/
|
|
1745
1534
|
wssSendMessage(level, time, name, message) {
|
|
1746
1535
|
if (!level || !time || !name || !message)
|
|
1747
1536
|
return;
|
|
1748
|
-
// Remove ANSI escape codes from the message
|
|
1749
|
-
// eslint-disable-next-line no-control-regex
|
|
1750
1537
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1751
|
-
// Remove leading asterisks from the message
|
|
1752
1538
|
message = message.replace(/^\*+/, '');
|
|
1753
|
-
// Replace all occurrences of \t and \n
|
|
1754
1539
|
message = message.replace(/[\t\n]/g, '');
|
|
1755
|
-
// Remove non-printable characters
|
|
1756
|
-
// eslint-disable-next-line no-control-regex
|
|
1757
1540
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1758
|
-
// Replace all occurrences of \" with "
|
|
1759
1541
|
message = message.replace(/\\"/g, '"');
|
|
1760
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1761
1542
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1762
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1763
1543
|
const maxContinuousLength = 100;
|
|
1764
1544
|
const keepStartLength = 20;
|
|
1765
1545
|
const keepEndLength = 20;
|
|
1766
|
-
// Split the message into words
|
|
1767
1546
|
message = message
|
|
1768
1547
|
.split(' ')
|
|
1769
1548
|
.map((word) => {
|
|
1770
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1771
1549
|
if (word.length > maxContinuousLength) {
|
|
1772
1550
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1773
1551
|
}
|
|
1774
1552
|
return word;
|
|
1775
1553
|
})
|
|
1776
1554
|
.join(' ');
|
|
1777
|
-
// Send the message to all connected clients
|
|
1778
1555
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1779
1556
|
if (client.readyState === WebSocket.OPEN) {
|
|
1780
1557
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1781
1558
|
}
|
|
1782
1559
|
});
|
|
1783
1560
|
}
|
|
1784
|
-
/**
|
|
1785
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1786
|
-
*
|
|
1787
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
1788
|
-
* possible values:
|
|
1789
|
-
* - 'matterbridgeLatestVersion'
|
|
1790
|
-
* - 'matterbridgeAdvertise'
|
|
1791
|
-
* - 'online'
|
|
1792
|
-
* - 'offline'
|
|
1793
|
-
* - 'reachability'
|
|
1794
|
-
* - 'settings'
|
|
1795
|
-
* - 'plugins'
|
|
1796
|
-
* - 'pluginsRestart'
|
|
1797
|
-
* - 'devices'
|
|
1798
|
-
* - 'fabrics'
|
|
1799
|
-
* - 'sessions'
|
|
1800
|
-
*/
|
|
1801
1561
|
wssSendRefreshRequired(changed = null) {
|
|
1802
1562
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1803
|
-
// Send the message to all connected clients
|
|
1804
1563
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1805
1564
|
if (client.readyState === WebSocket.OPEN) {
|
|
1806
1565
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1807
1566
|
}
|
|
1808
1567
|
});
|
|
1809
1568
|
}
|
|
1810
|
-
/**
|
|
1811
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1812
|
-
*
|
|
1813
|
-
*/
|
|
1814
1569
|
wssSendRestartRequired(snackbar = true) {
|
|
1815
1570
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1816
1571
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1817
1572
|
if (snackbar === true)
|
|
1818
1573
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1819
|
-
// Send the message to all connected clients
|
|
1820
1574
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1821
1575
|
if (client.readyState === WebSocket.OPEN) {
|
|
1822
1576
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1823
1577
|
}
|
|
1824
1578
|
});
|
|
1825
1579
|
}
|
|
1826
|
-
/**
|
|
1827
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
1828
|
-
*
|
|
1829
|
-
*/
|
|
1830
1580
|
wssSendUpdateRequired() {
|
|
1831
1581
|
this.log.debug('Sending an update required message to all connected clients');
|
|
1832
1582
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
1833
|
-
// Send the message to all connected clients
|
|
1834
1583
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1835
1584
|
if (client.readyState === WebSocket.OPEN) {
|
|
1836
1585
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
1837
1586
|
}
|
|
1838
1587
|
});
|
|
1839
1588
|
}
|
|
1840
|
-
/**
|
|
1841
|
-
* Sends a cpu update message to all connected clients.
|
|
1842
|
-
*
|
|
1843
|
-
*/
|
|
1844
1589
|
wssSendCpuUpdate(cpuUsage) {
|
|
1845
1590
|
if (hasParameter('debug'))
|
|
1846
1591
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1847
|
-
// Send the message to all connected clients
|
|
1848
1592
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1849
1593
|
if (client.readyState === WebSocket.OPEN) {
|
|
1850
1594
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
1851
1595
|
}
|
|
1852
1596
|
});
|
|
1853
1597
|
}
|
|
1854
|
-
/**
|
|
1855
|
-
* Sends a memory update message to all connected clients.
|
|
1856
|
-
*
|
|
1857
|
-
*/
|
|
1858
1598
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
1859
1599
|
if (hasParameter('debug'))
|
|
1860
1600
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
1861
|
-
// Send the message to all connected clients
|
|
1862
1601
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1863
1602
|
if (client.readyState === WebSocket.OPEN) {
|
|
1864
1603
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
1865
1604
|
}
|
|
1866
1605
|
});
|
|
1867
1606
|
}
|
|
1868
|
-
/**
|
|
1869
|
-
* Sends an uptime update message to all connected clients.
|
|
1870
|
-
*
|
|
1871
|
-
*/
|
|
1872
1607
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
1873
1608
|
if (hasParameter('debug'))
|
|
1874
1609
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
1875
|
-
// Send the message to all connected clients
|
|
1876
1610
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1877
1611
|
if (client.readyState === WebSocket.OPEN) {
|
|
1878
1612
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
1879
1613
|
}
|
|
1880
1614
|
});
|
|
1881
1615
|
}
|
|
1882
|
-
/**
|
|
1883
|
-
* Sends an open snackbar message to all connected clients.
|
|
1884
|
-
* @param {string} message - The message to send.
|
|
1885
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
1886
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
1887
|
-
*
|
|
1888
|
-
*/
|
|
1889
1616
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
1890
1617
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
1891
|
-
// Send the message to all connected clients
|
|
1892
1618
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1893
1619
|
if (client.readyState === WebSocket.OPEN) {
|
|
1894
1620
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
1895
1621
|
}
|
|
1896
1622
|
});
|
|
1897
1623
|
}
|
|
1898
|
-
/**
|
|
1899
|
-
* Sends a close snackbar message to all connected clients.
|
|
1900
|
-
* @param {string} message - The message to send.
|
|
1901
|
-
*
|
|
1902
|
-
*/
|
|
1903
1624
|
wssSendCloseSnackbarMessage(message) {
|
|
1904
1625
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
1905
|
-
// Send the message to all connected clients
|
|
1906
1626
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1907
1627
|
if (client.readyState === WebSocket.OPEN) {
|
|
1908
1628
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
1909
1629
|
}
|
|
1910
1630
|
});
|
|
1911
1631
|
}
|
|
1912
|
-
/**
|
|
1913
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
1914
|
-
*
|
|
1915
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
1916
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
1917
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
1918
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
1919
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
1920
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
1921
|
-
*
|
|
1922
|
-
* @remarks
|
|
1923
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
1924
|
-
* with the updated attribute information.
|
|
1925
|
-
*/
|
|
1926
1632
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
1927
1633
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
1928
|
-
// Send the message to all connected clients
|
|
1929
1634
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1930
1635
|
if (client.readyState === WebSocket.OPEN) {
|
|
1931
1636
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
1932
1637
|
}
|
|
1933
1638
|
});
|
|
1934
1639
|
}
|
|
1935
|
-
/**
|
|
1936
|
-
* Sends a message to all connected clients.
|
|
1937
|
-
* @param {number} id - The message id.
|
|
1938
|
-
* @param {string} method - The message method.
|
|
1939
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
1940
|
-
*
|
|
1941
|
-
*/
|
|
1942
1640
|
wssBroadcastMessage(id, method, params) {
|
|
1943
1641
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
1944
|
-
// Send the message to all connected clients
|
|
1945
1642
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1946
1643
|
if (client.readyState === WebSocket.OPEN) {
|
|
1947
1644
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -1949,4 +1646,3 @@ export class Frontend {
|
|
|
1949
1646
|
});
|
|
1950
1647
|
}
|
|
1951
1648
|
}
|
|
1952
|
-
//# sourceMappingURL=frontend.js.map
|