matterbridge 3.0.2 → 3.0.3-dev-20250517-bcc5d13
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 +20 -3
- 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 +62 -369
- package/dist/helpers.js +0 -49
- package/dist/index.js +2 -31
- 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 +134 -873
- package/dist/matterbridgeAccessoryPlatform.js +0 -34
- package/dist/matterbridgeBehaviors.js +4 -53
- package/dist/matterbridgeDeviceTypes.js +12 -431
- package/dist/matterbridgeDynamicPlatform.js +0 -34
- package/dist/matterbridgeEndpoint.js +11 -807
- package/dist/matterbridgeEndpointHelpers.js +9 -147
- package/dist/matterbridgePlatform.js +7 -225
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +3 -264
- package/dist/roboticVacuumCleaner.js +3 -39
- package/dist/shelly.js +7 -155
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -53
- package/dist/utils/colorUtils.js +2 -205
- package/dist/utils/commandLine.js +0 -53
- package/dist/utils/copyDirectory.js +1 -37
- package/dist/utils/createZip.js +2 -42
- package/dist/utils/deepCopy.js +0 -38
- package/dist/utils/deepEqual.js +1 -71
- package/dist/utils/export.js +0 -1
- package/dist/utils/hex.js +0 -57
- package/dist/utils/isvalid.js +0 -100
- package/dist/utils/network.js +5 -76
- package/dist/utils/wait.js +20 -56
- 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 -241
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/helpers.d.ts +0 -46
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.js.map +0 -1
- package/dist/index.d.ts +0 -36
- 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 -435
- 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 -1188
- 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 -965
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2728
- 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/roboticVacuumCleaner.d.ts +0 -43
- package/dist/roboticVacuumCleaner.d.ts.map +0 -1
- package/dist/roboticVacuumCleaner.js.map +0 -1
- package/dist/shelly.d.ts +0 -153
- 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 -58
- 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/commandLine.d.ts +0 -58
- package/dist/utils/commandLine.d.ts.map +0 -1
- package/dist/utils/commandLine.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 -11
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/hex.d.ts +0 -48
- package/dist/utils/hex.d.ts.map +0 -1
- package/dist/utils/hex.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -102
- 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/wait.d.ts +0 -51
- 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, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean } from './utils/export.js';
|
|
11
|
+
import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout } 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;
|
|
@@ -118,7 +35,7 @@ export class Frontend {
|
|
|
118
35
|
webSocketServer;
|
|
119
36
|
constructor(matterbridge) {
|
|
120
37
|
this.matterbridge = matterbridge;
|
|
121
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
38
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
122
39
|
}
|
|
123
40
|
set logLevel(logLevel) {
|
|
124
41
|
this.log.logLevel = logLevel;
|
|
@@ -126,43 +43,13 @@ export class Frontend {
|
|
|
126
43
|
async start(port = 8283) {
|
|
127
44
|
this.port = port;
|
|
128
45
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
129
|
-
// Initialize multer with the upload directory
|
|
130
46
|
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
131
47
|
await fs.mkdir(uploadDir, { recursive: true });
|
|
132
48
|
const upload = multer({ dest: uploadDir });
|
|
133
|
-
// Create the express app that serves the frontend
|
|
134
49
|
this.expressApp = express();
|
|
135
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
136
|
-
/*
|
|
137
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
138
|
-
for (const method of methods) {
|
|
139
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
140
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
141
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
142
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
143
|
-
try {
|
|
144
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
145
|
-
return original(path, ...rest);
|
|
146
|
-
} catch (err) {
|
|
147
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
148
|
-
throw err;
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
*/
|
|
153
|
-
// Log all requests to the server for debugging
|
|
154
|
-
/*
|
|
155
|
-
this.expressApp.use((req, res, next) => {
|
|
156
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
157
|
-
next();
|
|
158
|
-
});
|
|
159
|
-
*/
|
|
160
|
-
// Serve static files from '/static' endpoint
|
|
161
50
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
162
51
|
if (!hasParameter('ssl')) {
|
|
163
|
-
// Create an HTTP server and attach the express app
|
|
164
52
|
this.httpServer = createServer(this.expressApp);
|
|
165
|
-
// Listen on the specified port
|
|
166
53
|
if (hasParameter('ingress')) {
|
|
167
54
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
168
55
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -176,7 +63,6 @@ export class Frontend {
|
|
|
176
63
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
177
64
|
});
|
|
178
65
|
}
|
|
179
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
180
66
|
this.httpServer.on('error', (error) => {
|
|
181
67
|
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
182
68
|
switch (error.code) {
|
|
@@ -192,7 +78,6 @@ export class Frontend {
|
|
|
192
78
|
});
|
|
193
79
|
}
|
|
194
80
|
else {
|
|
195
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
196
81
|
let cert;
|
|
197
82
|
try {
|
|
198
83
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -220,9 +105,7 @@ export class Frontend {
|
|
|
220
105
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
221
106
|
}
|
|
222
107
|
const serverOptions = { cert, key, ca };
|
|
223
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
224
108
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
225
|
-
// Listen on the specified port
|
|
226
109
|
if (hasParameter('ingress')) {
|
|
227
110
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
228
111
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -236,7 +119,6 @@ export class Frontend {
|
|
|
236
119
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
237
120
|
});
|
|
238
121
|
}
|
|
239
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
240
122
|
this.httpsServer.on('error', (error) => {
|
|
241
123
|
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
242
124
|
switch (error.code) {
|
|
@@ -253,18 +135,16 @@ export class Frontend {
|
|
|
253
135
|
}
|
|
254
136
|
if (this.initializeError)
|
|
255
137
|
return;
|
|
256
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
257
138
|
const wssPort = this.port;
|
|
258
139
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
259
140
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
260
141
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
261
142
|
const clientIp = request.socket.remoteAddress;
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
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";
|
|
268
148
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
269
149
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
270
150
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -298,7 +178,6 @@ export class Frontend {
|
|
|
298
178
|
this.webSocketServer.on('error', (ws, error) => {
|
|
299
179
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
300
180
|
});
|
|
301
|
-
// Subscribe to cli events
|
|
302
181
|
const { cliEmitter } = await import('./cli.js');
|
|
303
182
|
cliEmitter.removeAllListeners();
|
|
304
183
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
@@ -310,7 +189,6 @@ export class Frontend {
|
|
|
310
189
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
311
190
|
this.wssSendCpuUpdate(cpuUsage);
|
|
312
191
|
});
|
|
313
|
-
// Endpoint to validate login code
|
|
314
192
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
315
193
|
const { password } = req.body;
|
|
316
194
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -329,27 +207,23 @@ export class Frontend {
|
|
|
329
207
|
this.log.warn('/api/login error wrong password');
|
|
330
208
|
res.json({ valid: false });
|
|
331
209
|
}
|
|
332
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
333
210
|
}
|
|
334
211
|
catch (error) {
|
|
335
212
|
this.log.error('/api/login error getting password');
|
|
336
213
|
res.json({ valid: false });
|
|
337
214
|
}
|
|
338
215
|
});
|
|
339
|
-
// Endpoint to provide health check for docker
|
|
340
216
|
this.expressApp.get('/health', (req, res) => {
|
|
341
217
|
this.log.debug('Express received /health');
|
|
342
218
|
const healthStatus = {
|
|
343
|
-
status: 'ok',
|
|
344
|
-
uptime: process.uptime(),
|
|
345
|
-
timestamp: new Date().toISOString(),
|
|
219
|
+
status: 'ok',
|
|
220
|
+
uptime: process.uptime(),
|
|
221
|
+
timestamp: new Date().toISOString(),
|
|
346
222
|
};
|
|
347
223
|
res.status(200).json(healthStatus);
|
|
348
224
|
});
|
|
349
|
-
// Endpoint to provide memory usage details
|
|
350
225
|
this.expressApp.get('/memory', async (req, res) => {
|
|
351
226
|
this.log.debug('Express received /memory');
|
|
352
|
-
// Memory usage from process
|
|
353
227
|
const memoryUsageRaw = process.memoryUsage();
|
|
354
228
|
const memoryUsage = {
|
|
355
229
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -358,13 +232,10 @@ export class Frontend {
|
|
|
358
232
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
359
233
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
360
234
|
};
|
|
361
|
-
// V8 heap statistics
|
|
362
235
|
const { default: v8 } = await import('node:v8');
|
|
363
236
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
364
237
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
365
|
-
// Format heapStats
|
|
366
238
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
367
|
-
// Format heapSpaces
|
|
368
239
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
369
240
|
...space,
|
|
370
241
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -382,23 +253,19 @@ export class Frontend {
|
|
|
382
253
|
};
|
|
383
254
|
res.status(200).json(memoryReport);
|
|
384
255
|
});
|
|
385
|
-
// Endpoint to provide settings
|
|
386
256
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
387
257
|
this.log.debug('The frontend sent /api/settings');
|
|
388
258
|
res.json(await this.getApiSettings());
|
|
389
259
|
});
|
|
390
|
-
// Endpoint to provide plugins
|
|
391
260
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
392
261
|
this.log.debug('The frontend sent /api/plugins');
|
|
393
262
|
res.json(this.getBaseRegisteredPlugins());
|
|
394
263
|
});
|
|
395
|
-
// Endpoint to provide devices
|
|
396
264
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
397
265
|
this.log.debug('The frontend sent /api/devices');
|
|
398
266
|
const devices = await this.getDevices();
|
|
399
267
|
res.json(devices);
|
|
400
268
|
});
|
|
401
|
-
// Endpoint to view the matterbridge log
|
|
402
269
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
403
270
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
404
271
|
try {
|
|
@@ -411,7 +278,6 @@ export class Frontend {
|
|
|
411
278
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
412
279
|
}
|
|
413
280
|
});
|
|
414
|
-
// Endpoint to view the matter.js log
|
|
415
281
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
416
282
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
417
283
|
try {
|
|
@@ -424,7 +290,6 @@ export class Frontend {
|
|
|
424
290
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
425
291
|
}
|
|
426
292
|
});
|
|
427
|
-
// Endpoint to view the shelly log
|
|
428
293
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
429
294
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
430
295
|
try {
|
|
@@ -437,7 +302,6 @@ export class Frontend {
|
|
|
437
302
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
438
303
|
}
|
|
439
304
|
});
|
|
440
|
-
// Endpoint to download the matterbridge log
|
|
441
305
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
442
306
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
|
|
443
307
|
try {
|
|
@@ -450,7 +314,6 @@ export class Frontend {
|
|
|
450
314
|
this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
|
|
451
315
|
}
|
|
452
316
|
res.type('text/plain');
|
|
453
|
-
// res.download(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
|
|
454
317
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
|
|
455
318
|
if (error) {
|
|
456
319
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
@@ -458,7 +321,6 @@ export class Frontend {
|
|
|
458
321
|
}
|
|
459
322
|
});
|
|
460
323
|
});
|
|
461
|
-
// Endpoint to download the matter log
|
|
462
324
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
463
325
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
|
|
464
326
|
try {
|
|
@@ -478,7 +340,6 @@ export class Frontend {
|
|
|
478
340
|
}
|
|
479
341
|
});
|
|
480
342
|
});
|
|
481
|
-
// Endpoint to download the shelly log
|
|
482
343
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
483
344
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
484
345
|
try {
|
|
@@ -498,7 +359,6 @@ export class Frontend {
|
|
|
498
359
|
}
|
|
499
360
|
});
|
|
500
361
|
});
|
|
501
|
-
// Endpoint to download the matterbridge storage directory
|
|
502
362
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
503
363
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
504
364
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -509,7 +369,6 @@ export class Frontend {
|
|
|
509
369
|
}
|
|
510
370
|
});
|
|
511
371
|
});
|
|
512
|
-
// Endpoint to download the matter storage file
|
|
513
372
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
514
373
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
515
374
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
@@ -520,7 +379,6 @@ export class Frontend {
|
|
|
520
379
|
}
|
|
521
380
|
});
|
|
522
381
|
});
|
|
523
|
-
// Endpoint to download the matterbridge plugin directory
|
|
524
382
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
525
383
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
526
384
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -531,7 +389,6 @@ export class Frontend {
|
|
|
531
389
|
}
|
|
532
390
|
});
|
|
533
391
|
});
|
|
534
|
-
// Endpoint to download the matterbridge plugin config files
|
|
535
392
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
536
393
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
537
394
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
@@ -542,7 +399,6 @@ export class Frontend {
|
|
|
542
399
|
}
|
|
543
400
|
});
|
|
544
401
|
});
|
|
545
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
546
402
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
547
403
|
this.log.debug('The frontend sent /api/download-backup');
|
|
548
404
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -552,7 +408,6 @@ export class Frontend {
|
|
|
552
408
|
}
|
|
553
409
|
});
|
|
554
410
|
});
|
|
555
|
-
// Endpoint to upload a package
|
|
556
411
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
557
412
|
const { filename } = req.body;
|
|
558
413
|
const file = req.file;
|
|
@@ -562,13 +417,10 @@ export class Frontend {
|
|
|
562
417
|
return;
|
|
563
418
|
}
|
|
564
419
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
565
|
-
// Define the path where the plugin file will be saved
|
|
566
420
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
567
421
|
try {
|
|
568
|
-
// Move the uploaded file to the specified path
|
|
569
422
|
await fs.rename(file.path, filePath);
|
|
570
423
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
571
|
-
// Install the plugin package
|
|
572
424
|
if (filename.endsWith('.tgz')) {
|
|
573
425
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
574
426
|
this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
|
|
@@ -587,7 +439,6 @@ export class Frontend {
|
|
|
587
439
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
588
440
|
}
|
|
589
441
|
});
|
|
590
|
-
// Fallback for routing (must be the last route)
|
|
591
442
|
this.expressApp.use((req, res) => {
|
|
592
443
|
this.log.debug('The frontend sent:', req.url);
|
|
593
444
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -595,61 +446,66 @@ export class Frontend {
|
|
|
595
446
|
this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
596
447
|
}
|
|
597
448
|
async stop() {
|
|
598
|
-
|
|
599
|
-
if (this.httpServer) {
|
|
600
|
-
this.httpServer.close((error) => {
|
|
601
|
-
if (error) {
|
|
602
|
-
this.log.error(`Error closing http server: ${error}`);
|
|
603
|
-
}
|
|
604
|
-
else {
|
|
605
|
-
this.log.debug('Http server closed successfully');
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
this.httpServer.removeAllListeners();
|
|
609
|
-
this.httpServer = undefined;
|
|
610
|
-
this.log.debug('Frontend http server closed successfully');
|
|
611
|
-
}
|
|
612
|
-
// Close the https server
|
|
613
|
-
if (this.httpsServer) {
|
|
614
|
-
this.httpsServer.close((error) => {
|
|
615
|
-
if (error) {
|
|
616
|
-
this.log.error(`Error closing https server: ${error}`);
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
this.log.debug('Https server closed successfully');
|
|
620
|
-
}
|
|
621
|
-
});
|
|
622
|
-
this.httpsServer.removeAllListeners();
|
|
623
|
-
this.httpsServer = undefined;
|
|
624
|
-
this.log.debug('Frontend https server closed successfully');
|
|
625
|
-
}
|
|
626
|
-
// Remove listeners from the express app
|
|
449
|
+
this.log.debug('Stopping the frontend...');
|
|
627
450
|
if (this.expressApp) {
|
|
628
451
|
this.expressApp.removeAllListeners();
|
|
629
452
|
this.expressApp = undefined;
|
|
630
453
|
this.log.debug('Frontend app closed successfully');
|
|
631
454
|
}
|
|
632
|
-
// Close the WebSocket server
|
|
633
455
|
if (this.webSocketServer) {
|
|
634
|
-
// Close all active connections
|
|
635
456
|
this.webSocketServer.clients.forEach((client) => {
|
|
636
457
|
if (client.readyState === WebSocket.OPEN) {
|
|
637
458
|
client.close();
|
|
638
459
|
}
|
|
639
460
|
});
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
461
|
+
await withTimeout(new Promise((resolve) => {
|
|
462
|
+
this.webSocketServer?.close((error) => {
|
|
463
|
+
if (error) {
|
|
464
|
+
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
this.log.debug('WebSocket server closed successfully');
|
|
468
|
+
}
|
|
469
|
+
resolve();
|
|
470
|
+
});
|
|
471
|
+
}), 10000, false);
|
|
472
|
+
this.webSocketServer.removeAllListeners();
|
|
649
473
|
this.webSocketServer = undefined;
|
|
650
474
|
}
|
|
475
|
+
if (this.httpServer) {
|
|
476
|
+
await withTimeout(new Promise((resolve) => {
|
|
477
|
+
this.httpServer?.close((error) => {
|
|
478
|
+
if (error) {
|
|
479
|
+
this.log.error(`Error closing http server: ${error}`);
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
this.log.debug('Http server closed successfully');
|
|
483
|
+
}
|
|
484
|
+
resolve();
|
|
485
|
+
});
|
|
486
|
+
}), 10000, false);
|
|
487
|
+
this.httpServer.removeAllListeners();
|
|
488
|
+
this.httpServer = undefined;
|
|
489
|
+
this.log.debug('Frontend http server closed successfully');
|
|
490
|
+
}
|
|
491
|
+
if (this.httpsServer) {
|
|
492
|
+
await withTimeout(new Promise((resolve) => {
|
|
493
|
+
this.httpsServer?.close((error) => {
|
|
494
|
+
if (error) {
|
|
495
|
+
this.log.error(`Error closing https server: ${error}`);
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
this.log.debug('Https server closed successfully');
|
|
499
|
+
}
|
|
500
|
+
resolve();
|
|
501
|
+
});
|
|
502
|
+
}), 10000, false);
|
|
503
|
+
this.httpsServer.removeAllListeners();
|
|
504
|
+
this.httpsServer = undefined;
|
|
505
|
+
this.log.debug('Frontend https server closed successfully');
|
|
506
|
+
}
|
|
507
|
+
this.log.debug('Frontend stopped successfully');
|
|
651
508
|
}
|
|
652
|
-
// Function to format bytes to KB, MB, or GB
|
|
653
509
|
formatMemoryUsage = (bytes) => {
|
|
654
510
|
if (bytes >= 1024 ** 3) {
|
|
655
511
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -661,7 +517,6 @@ export class Frontend {
|
|
|
661
517
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
662
518
|
}
|
|
663
519
|
};
|
|
664
|
-
// Function to format system uptime with only the most significant unit
|
|
665
520
|
formatOsUpTime = (seconds) => {
|
|
666
521
|
if (seconds >= 86400) {
|
|
667
522
|
const days = Math.floor(seconds / 86400);
|
|
@@ -677,14 +532,8 @@ export class Frontend {
|
|
|
677
532
|
}
|
|
678
533
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
679
534
|
};
|
|
680
|
-
/**
|
|
681
|
-
* Retrieves the api settings data.
|
|
682
|
-
*
|
|
683
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
684
|
-
*/
|
|
685
535
|
async getApiSettings() {
|
|
686
536
|
const { lastCpuUsage } = await import('./cli.js');
|
|
687
|
-
// Update the system information
|
|
688
537
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
689
538
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
690
539
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -693,7 +542,6 @@ export class Frontend {
|
|
|
693
542
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
694
543
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
695
544
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
696
|
-
// Update the matterbridge information
|
|
697
545
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
698
546
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
699
547
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -712,11 +560,6 @@ export class Frontend {
|
|
|
712
560
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
713
561
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
714
562
|
}
|
|
715
|
-
/**
|
|
716
|
-
* Retrieves the reachable attribute.
|
|
717
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
718
|
-
* @returns {boolean} The reachable attribute.
|
|
719
|
-
*/
|
|
720
563
|
getReachability(device) {
|
|
721
564
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
722
565
|
return false;
|
|
@@ -741,20 +584,13 @@ export class Frontend {
|
|
|
741
584
|
}
|
|
742
585
|
return;
|
|
743
586
|
};
|
|
744
|
-
// Root endpoint
|
|
745
587
|
if (device.hasClusterServer(PowerSource.Cluster.id))
|
|
746
588
|
return powerSource(device);
|
|
747
|
-
// Child endpoints
|
|
748
589
|
for (const child of device.getChildEndpoints()) {
|
|
749
590
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
750
591
|
return powerSource(child);
|
|
751
592
|
}
|
|
752
593
|
}
|
|
753
|
-
/**
|
|
754
|
-
* Retrieves the cluster text description from a given device.
|
|
755
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
756
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
757
|
-
*/
|
|
758
594
|
getClusterTextFromDevice(device) {
|
|
759
595
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
760
596
|
return '';
|
|
@@ -796,7 +632,6 @@ export class Frontend {
|
|
|
796
632
|
let attributes = '';
|
|
797
633
|
let supportedModes = [];
|
|
798
634
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
799
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
800
635
|
if (typeof attributeValue === 'undefined')
|
|
801
636
|
return;
|
|
802
637
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -888,13 +723,8 @@ export class Frontend {
|
|
|
888
723
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
889
724
|
attributes += `${getUserLabel(device)} `;
|
|
890
725
|
});
|
|
891
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
892
726
|
return attributes.trimStart().trimEnd();
|
|
893
727
|
}
|
|
894
|
-
/**
|
|
895
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
896
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
897
|
-
*/
|
|
898
728
|
getBaseRegisteredPlugins() {
|
|
899
729
|
const baseRegisteredPlugins = [];
|
|
900
730
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -933,18 +763,11 @@ export class Frontend {
|
|
|
933
763
|
}
|
|
934
764
|
return baseRegisteredPlugins;
|
|
935
765
|
}
|
|
936
|
-
/**
|
|
937
|
-
* Retrieves the devices from Matterbridge.
|
|
938
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
939
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices.
|
|
940
|
-
*/
|
|
941
766
|
async getDevices(pluginName) {
|
|
942
767
|
const devices = [];
|
|
943
768
|
this.matterbridge.devices.forEach(async (device) => {
|
|
944
|
-
// Filter by pluginName if provided
|
|
945
769
|
if (pluginName && pluginName !== device.plugin)
|
|
946
770
|
return;
|
|
947
|
-
// Check if the device has the required properties
|
|
948
771
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
949
772
|
return;
|
|
950
773
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -964,13 +787,6 @@ export class Frontend {
|
|
|
964
787
|
});
|
|
965
788
|
return devices;
|
|
966
789
|
}
|
|
967
|
-
/**
|
|
968
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
969
|
-
*
|
|
970
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
971
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
972
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
973
|
-
*/
|
|
974
790
|
async wsMessageHandler(client, message) {
|
|
975
791
|
let data;
|
|
976
792
|
try {
|
|
@@ -1017,10 +833,8 @@ export class Frontend {
|
|
|
1017
833
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1018
834
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1019
835
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1020
|
-
// The install comes from InstallPlugins
|
|
1021
836
|
this.matterbridge.plugins.add(packageName).then((plugin) => {
|
|
1022
837
|
if (plugin) {
|
|
1023
|
-
// The plugin is not registered
|
|
1024
838
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1025
839
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
1026
840
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
@@ -1028,7 +842,6 @@ export class Frontend {
|
|
|
1028
842
|
});
|
|
1029
843
|
}
|
|
1030
844
|
else {
|
|
1031
|
-
// The plugin is already registered
|
|
1032
845
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1033
846
|
this.wssSendRefreshRequired('plugins');
|
|
1034
847
|
this.wssSendRestartRequired();
|
|
@@ -1036,7 +849,6 @@ export class Frontend {
|
|
|
1036
849
|
});
|
|
1037
850
|
}
|
|
1038
851
|
else {
|
|
1039
|
-
// The package is matterbridge
|
|
1040
852
|
if (this.matterbridge.restartMode !== '') {
|
|
1041
853
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1042
854
|
this.matterbridge.shutdownProcess();
|
|
@@ -1058,7 +870,6 @@ export class Frontend {
|
|
|
1058
870
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1059
871
|
return;
|
|
1060
872
|
}
|
|
1061
|
-
// The package is a plugin
|
|
1062
873
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1063
874
|
if (plugin) {
|
|
1064
875
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
@@ -1067,7 +878,6 @@ export class Frontend {
|
|
|
1067
878
|
this.wssSendRefreshRequired('plugins');
|
|
1068
879
|
this.wssSendRefreshRequired('devices');
|
|
1069
880
|
}
|
|
1070
|
-
// Uninstall the package
|
|
1071
881
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1072
882
|
this.matterbridge
|
|
1073
883
|
.spawnCommand('npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1344,7 +1154,6 @@ export class Frontend {
|
|
|
1344
1154
|
});
|
|
1345
1155
|
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
1346
1156
|
deviceTypes = [];
|
|
1347
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1348
1157
|
const name = childEndpoint.endpoint?.id;
|
|
1349
1158
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
1350
1159
|
clusterServers.forEach((clusterServer) => {
|
|
@@ -1458,22 +1267,22 @@ export class Frontend {
|
|
|
1458
1267
|
if (isValidString(data.params.value, 4)) {
|
|
1459
1268
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1460
1269
|
if (data.params.value === 'Debug') {
|
|
1461
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1270
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1462
1271
|
}
|
|
1463
1272
|
else if (data.params.value === 'Info') {
|
|
1464
|
-
await this.matterbridge.setLogLevel("info"
|
|
1273
|
+
await this.matterbridge.setLogLevel("info");
|
|
1465
1274
|
}
|
|
1466
1275
|
else if (data.params.value === 'Notice') {
|
|
1467
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1276
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1468
1277
|
}
|
|
1469
1278
|
else if (data.params.value === 'Warn') {
|
|
1470
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1279
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1471
1280
|
}
|
|
1472
1281
|
else if (data.params.value === 'Error') {
|
|
1473
|
-
await this.matterbridge.setLogLevel("error"
|
|
1282
|
+
await this.matterbridge.setLogLevel("error");
|
|
1474
1283
|
}
|
|
1475
1284
|
else if (data.params.value === 'Fatal') {
|
|
1476
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1285
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1477
1286
|
}
|
|
1478
1287
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1479
1288
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1484,7 +1293,6 @@ export class Frontend {
|
|
|
1484
1293
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1485
1294
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1486
1295
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1487
|
-
// Create the file logger for matterbridge
|
|
1488
1296
|
if (data.params.value)
|
|
1489
1297
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1490
1298
|
else
|
|
@@ -1640,19 +1448,15 @@ export class Frontend {
|
|
|
1640
1448
|
return;
|
|
1641
1449
|
}
|
|
1642
1450
|
const config = plugin.configJson;
|
|
1643
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1644
1451
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1645
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1646
1452
|
if (select === 'serial')
|
|
1647
1453
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1648
1454
|
if (select === 'name')
|
|
1649
1455
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1650
1456
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1651
|
-
// Remove postfix from the serial if it exists
|
|
1652
1457
|
if (config.postfix) {
|
|
1653
1458
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1654
1459
|
}
|
|
1655
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1656
1460
|
if (isValidArray(config.whiteList, 1)) {
|
|
1657
1461
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1658
1462
|
config.whiteList.push(data.params.serial);
|
|
@@ -1661,7 +1465,6 @@ export class Frontend {
|
|
|
1661
1465
|
config.whiteList.push(data.params.name);
|
|
1662
1466
|
}
|
|
1663
1467
|
}
|
|
1664
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1665
1468
|
if (isValidArray(config.blackList, 1)) {
|
|
1666
1469
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1667
1470
|
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
@@ -1691,9 +1494,7 @@ export class Frontend {
|
|
|
1691
1494
|
return;
|
|
1692
1495
|
}
|
|
1693
1496
|
const config = plugin.configJson;
|
|
1694
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1695
1497
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1696
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1697
1498
|
if (select === 'serial')
|
|
1698
1499
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1699
1500
|
if (select === 'name')
|
|
@@ -1702,7 +1503,6 @@ export class Frontend {
|
|
|
1702
1503
|
if (config.postfix) {
|
|
1703
1504
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1704
1505
|
}
|
|
1705
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1706
1506
|
if (isValidArray(config.whiteList, 1)) {
|
|
1707
1507
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1708
1508
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
@@ -1711,7 +1511,6 @@ export class Frontend {
|
|
|
1711
1511
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1712
1512
|
}
|
|
1713
1513
|
}
|
|
1714
|
-
// Add the serial to the blackList
|
|
1715
1514
|
if (isValidArray(config.blackList)) {
|
|
1716
1515
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1717
1516
|
config.blackList.push(data.params.serial);
|
|
@@ -1744,219 +1543,114 @@ export class Frontend {
|
|
|
1744
1543
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1745
1544
|
}
|
|
1746
1545
|
}
|
|
1747
|
-
/**
|
|
1748
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1749
|
-
*
|
|
1750
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1751
|
-
* @param {string} time - The time string of the message
|
|
1752
|
-
* @param {string} name - The logger name of the message
|
|
1753
|
-
* @param {string} message - The content of the message.
|
|
1754
|
-
*
|
|
1755
|
-
* @remark
|
|
1756
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
1757
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
1758
|
-
* The function sends the message to all connected clients.
|
|
1759
|
-
*/
|
|
1760
1546
|
wssSendMessage(level, time, name, message) {
|
|
1761
1547
|
if (!level || !time || !name || !message)
|
|
1762
1548
|
return;
|
|
1763
|
-
// Remove ANSI escape codes from the message
|
|
1764
|
-
// eslint-disable-next-line no-control-regex
|
|
1765
1549
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1766
|
-
// Remove leading asterisks from the message
|
|
1767
1550
|
message = message.replace(/^\*+/, '');
|
|
1768
|
-
// Replace all occurrences of \t and \n
|
|
1769
1551
|
message = message.replace(/[\t\n]/g, '');
|
|
1770
|
-
// Remove non-printable characters
|
|
1771
|
-
// eslint-disable-next-line no-control-regex
|
|
1772
1552
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1773
|
-
// Replace all occurrences of \" with "
|
|
1774
1553
|
message = message.replace(/\\"/g, '"');
|
|
1775
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1776
1554
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1777
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1778
1555
|
const maxContinuousLength = 100;
|
|
1779
1556
|
const keepStartLength = 20;
|
|
1780
1557
|
const keepEndLength = 20;
|
|
1781
|
-
// Split the message into words
|
|
1782
1558
|
message = message
|
|
1783
1559
|
.split(' ')
|
|
1784
1560
|
.map((word) => {
|
|
1785
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1786
1561
|
if (word.length > maxContinuousLength) {
|
|
1787
1562
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1788
1563
|
}
|
|
1789
1564
|
return word;
|
|
1790
1565
|
})
|
|
1791
1566
|
.join(' ');
|
|
1792
|
-
// Send the message to all connected clients
|
|
1793
1567
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1794
1568
|
if (client.readyState === WebSocket.OPEN) {
|
|
1795
1569
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1796
1570
|
}
|
|
1797
1571
|
});
|
|
1798
1572
|
}
|
|
1799
|
-
/**
|
|
1800
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1801
|
-
*
|
|
1802
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
1803
|
-
* possible values:
|
|
1804
|
-
* - 'matterbridgeLatestVersion'
|
|
1805
|
-
* - 'matterbridgeAdvertise'
|
|
1806
|
-
* - 'online'
|
|
1807
|
-
* - 'offline'
|
|
1808
|
-
* - 'reachability'
|
|
1809
|
-
* - 'settings'
|
|
1810
|
-
* - 'plugins'
|
|
1811
|
-
* - 'pluginsRestart'
|
|
1812
|
-
* - 'devices'
|
|
1813
|
-
* - 'fabrics'
|
|
1814
|
-
* - 'sessions'
|
|
1815
|
-
*/
|
|
1816
1573
|
wssSendRefreshRequired(changed = null) {
|
|
1817
1574
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1818
|
-
// Send the message to all connected clients
|
|
1819
1575
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1820
1576
|
if (client.readyState === WebSocket.OPEN) {
|
|
1821
1577
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1822
1578
|
}
|
|
1823
1579
|
});
|
|
1824
1580
|
}
|
|
1825
|
-
/**
|
|
1826
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1827
|
-
*
|
|
1828
|
-
*/
|
|
1829
1581
|
wssSendRestartRequired(snackbar = true) {
|
|
1830
1582
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1831
1583
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1832
1584
|
if (snackbar === true)
|
|
1833
1585
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1834
|
-
// Send the message to all connected clients
|
|
1835
1586
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1836
1587
|
if (client.readyState === WebSocket.OPEN) {
|
|
1837
1588
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1838
1589
|
}
|
|
1839
1590
|
});
|
|
1840
1591
|
}
|
|
1841
|
-
/**
|
|
1842
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
1843
|
-
*
|
|
1844
|
-
*/
|
|
1845
1592
|
wssSendUpdateRequired() {
|
|
1846
1593
|
this.log.debug('Sending an update required message to all connected clients');
|
|
1847
1594
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
1848
|
-
// Send the message to all connected clients
|
|
1849
1595
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1850
1596
|
if (client.readyState === WebSocket.OPEN) {
|
|
1851
1597
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
1852
1598
|
}
|
|
1853
1599
|
});
|
|
1854
1600
|
}
|
|
1855
|
-
/**
|
|
1856
|
-
* Sends a cpu update message to all connected clients.
|
|
1857
|
-
*
|
|
1858
|
-
*/
|
|
1859
1601
|
wssSendCpuUpdate(cpuUsage) {
|
|
1860
1602
|
if (hasParameter('debug'))
|
|
1861
1603
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1862
|
-
// Send the message to all connected clients
|
|
1863
1604
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1864
1605
|
if (client.readyState === WebSocket.OPEN) {
|
|
1865
1606
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
1866
1607
|
}
|
|
1867
1608
|
});
|
|
1868
1609
|
}
|
|
1869
|
-
/**
|
|
1870
|
-
* Sends a memory update message to all connected clients.
|
|
1871
|
-
*
|
|
1872
|
-
*/
|
|
1873
1610
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
1874
1611
|
if (hasParameter('debug'))
|
|
1875
1612
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
1876
|
-
// Send the message to all connected clients
|
|
1877
1613
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1878
1614
|
if (client.readyState === WebSocket.OPEN) {
|
|
1879
1615
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
1880
1616
|
}
|
|
1881
1617
|
});
|
|
1882
1618
|
}
|
|
1883
|
-
/**
|
|
1884
|
-
* Sends an uptime update message to all connected clients.
|
|
1885
|
-
*
|
|
1886
|
-
*/
|
|
1887
1619
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
1888
1620
|
if (hasParameter('debug'))
|
|
1889
1621
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
1890
|
-
// Send the message to all connected clients
|
|
1891
1622
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1892
1623
|
if (client.readyState === WebSocket.OPEN) {
|
|
1893
1624
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
1894
1625
|
}
|
|
1895
1626
|
});
|
|
1896
1627
|
}
|
|
1897
|
-
/**
|
|
1898
|
-
* Sends an open snackbar message to all connected clients.
|
|
1899
|
-
* @param {string} message - The message to send.
|
|
1900
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
1901
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
1902
|
-
*
|
|
1903
|
-
*/
|
|
1904
1628
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
1905
1629
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
1906
|
-
// Send the message to all connected clients
|
|
1907
1630
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1908
1631
|
if (client.readyState === WebSocket.OPEN) {
|
|
1909
1632
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
1910
1633
|
}
|
|
1911
1634
|
});
|
|
1912
1635
|
}
|
|
1913
|
-
/**
|
|
1914
|
-
* Sends a close snackbar message to all connected clients.
|
|
1915
|
-
* @param {string} message - The message to send.
|
|
1916
|
-
*
|
|
1917
|
-
*/
|
|
1918
1636
|
wssSendCloseSnackbarMessage(message) {
|
|
1919
1637
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
1920
|
-
// Send the message to all connected clients
|
|
1921
1638
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1922
1639
|
if (client.readyState === WebSocket.OPEN) {
|
|
1923
1640
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
1924
1641
|
}
|
|
1925
1642
|
});
|
|
1926
1643
|
}
|
|
1927
|
-
/**
|
|
1928
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
1929
|
-
*
|
|
1930
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
1931
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
1932
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
1933
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
1934
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
1935
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
1936
|
-
*
|
|
1937
|
-
* @remarks
|
|
1938
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
1939
|
-
* with the updated attribute information.
|
|
1940
|
-
*/
|
|
1941
1644
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
1942
1645
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
1943
|
-
// Send the message to all connected clients
|
|
1944
1646
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1945
1647
|
if (client.readyState === WebSocket.OPEN) {
|
|
1946
1648
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
1947
1649
|
}
|
|
1948
1650
|
});
|
|
1949
1651
|
}
|
|
1950
|
-
/**
|
|
1951
|
-
* Sends a message to all connected clients.
|
|
1952
|
-
* @param {number} id - The message id.
|
|
1953
|
-
* @param {string} method - The message method.
|
|
1954
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
1955
|
-
*
|
|
1956
|
-
*/
|
|
1957
1652
|
wssBroadcastMessage(id, method, params) {
|
|
1958
1653
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
1959
|
-
// Send the message to all connected clients
|
|
1960
1654
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1961
1655
|
if (client.readyState === WebSocket.OPEN) {
|
|
1962
1656
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -1964,4 +1658,3 @@ export class Frontend {
|
|
|
1964
1658
|
});
|
|
1965
1659
|
}
|
|
1966
1660
|
}
|
|
1967
|
-
//# sourceMappingURL=frontend.js.map
|