matterbridge 3.0.4 → 3.0.5-dev-20250526-422f029
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/cli.js +16 -43
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -94
- package/dist/frontend.js +16 -363
- package/dist/helpers.js +0 -51
- package/dist/index.js +1 -27
- package/dist/laundryWasher.js +122 -0
- 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 +47 -747
- package/dist/matterbridgeAccessoryPlatform.js +0 -34
- package/dist/matterbridgeBehaviors.js +4 -63
- package/dist/matterbridgeDeviceTypes.js +15 -563
- package/dist/matterbridgeDynamicPlatform.js +0 -34
- package/dist/matterbridgeEndpoint.js +23 -903
- package/dist/matterbridgeEndpointHelpers.js +11 -173
- package/dist/matterbridgePlatform.js +7 -225
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +3 -264
- package/dist/roboticVacuumCleaner.js +3 -78
- 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 +9 -58
- package/dist/waterHeater.js +0 -52
- 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 -256
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/helpers.d.ts +0 -47
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.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 -445
- 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 -1398
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -629
- 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 -1053
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2749
- 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 -196
- 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 -82
- 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 -52
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
- package/dist/waterHeater.d.ts +0 -75
- package/dist/waterHeater.d.ts.map +0 -1
- package/dist/waterHeater.js.map +0 -1
package/dist/frontend.js
CHANGED
|
@@ -1,112 +1,29 @@
|
|
|
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 { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
|
|
25
2
|
import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
|
|
26
|
-
// Node modules
|
|
27
3
|
import { createServer } from 'node:http';
|
|
28
4
|
import https from 'node:https';
|
|
29
5
|
import os from 'node:os';
|
|
30
6
|
import path from 'node:path';
|
|
31
7
|
import { promises as fs } from 'node:fs';
|
|
32
|
-
// Third-party modules
|
|
33
8
|
import express from 'express';
|
|
34
9
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
35
10
|
import multer from 'multer';
|
|
36
|
-
// AnsiLogger module
|
|
37
11
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from './logger/export.js';
|
|
38
|
-
// Matterbridge
|
|
39
12
|
import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout } from './utils/export.js';
|
|
40
13
|
import { plg } from './matterbridgeTypes.js';
|
|
41
14
|
import { hasParameter } from './utils/export.js';
|
|
42
15
|
import { capitalizeFirstLetter } from './matterbridgeEndpointHelpers.js';
|
|
43
|
-
/**
|
|
44
|
-
* Websocket message ID for logging.
|
|
45
|
-
* @constant {number}
|
|
46
|
-
*/
|
|
47
16
|
export const WS_ID_LOG = 0;
|
|
48
|
-
/**
|
|
49
|
-
* Websocket message ID indicating a refresh is needed.
|
|
50
|
-
* @constant {number}
|
|
51
|
-
*/
|
|
52
17
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
53
|
-
/**
|
|
54
|
-
* Websocket message ID indicating a restart is needed.
|
|
55
|
-
* @constant {number}
|
|
56
|
-
*/
|
|
57
18
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
58
|
-
/**
|
|
59
|
-
* Websocket message ID indicating a cpu update.
|
|
60
|
-
* @constant {number}
|
|
61
|
-
*/
|
|
62
19
|
export const WS_ID_CPU_UPDATE = 3;
|
|
63
|
-
/**
|
|
64
|
-
* Websocket message ID indicating a memory update.
|
|
65
|
-
* @constant {number}
|
|
66
|
-
*/
|
|
67
20
|
export const WS_ID_MEMORY_UPDATE = 4;
|
|
68
|
-
/**
|
|
69
|
-
* Websocket message ID indicating an uptime update.
|
|
70
|
-
* @constant {number}
|
|
71
|
-
*/
|
|
72
21
|
export const WS_ID_UPTIME_UPDATE = 5;
|
|
73
|
-
/**
|
|
74
|
-
* Websocket message ID indicating a snackbar message.
|
|
75
|
-
* @constant {number}
|
|
76
|
-
*/
|
|
77
22
|
export const WS_ID_SNACKBAR = 6;
|
|
78
|
-
/**
|
|
79
|
-
* Websocket message ID indicating matterbridge has un update available.
|
|
80
|
-
* @constant {number}
|
|
81
|
-
*/
|
|
82
23
|
export const WS_ID_UPDATE_NEEDED = 7;
|
|
83
|
-
/**
|
|
84
|
-
* Websocket message ID indicating a state update.
|
|
85
|
-
* @constant {number}
|
|
86
|
-
*/
|
|
87
24
|
export const WS_ID_STATEUPDATE = 8;
|
|
88
|
-
/**
|
|
89
|
-
* Websocket message ID indicating to close a permanent snackbar message.
|
|
90
|
-
* @constant {number}
|
|
91
|
-
*/
|
|
92
25
|
export const WS_ID_CLOSE_SNACKBAR = 9;
|
|
93
|
-
/**
|
|
94
|
-
* Websocket message ID indicating a shelly system update.
|
|
95
|
-
* check:
|
|
96
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/check
|
|
97
|
-
* perform:
|
|
98
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/perform
|
|
99
|
-
* @constant {number}
|
|
100
|
-
*/
|
|
101
26
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
102
|
-
/**
|
|
103
|
-
* Websocket message ID indicating a shelly main update.
|
|
104
|
-
* check:
|
|
105
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/check
|
|
106
|
-
* perform:
|
|
107
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/perform
|
|
108
|
-
* @constant {number}
|
|
109
|
-
*/
|
|
110
27
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
111
28
|
export class Frontend {
|
|
112
29
|
matterbridge;
|
|
@@ -119,7 +36,7 @@ export class Frontend {
|
|
|
119
36
|
webSocketServer;
|
|
120
37
|
constructor(matterbridge) {
|
|
121
38
|
this.matterbridge = matterbridge;
|
|
122
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
39
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
123
40
|
}
|
|
124
41
|
set logLevel(logLevel) {
|
|
125
42
|
this.log.logLevel = logLevel;
|
|
@@ -127,43 +44,13 @@ export class Frontend {
|
|
|
127
44
|
async start(port = 8283) {
|
|
128
45
|
this.port = port;
|
|
129
46
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
130
|
-
// Initialize multer with the upload directory
|
|
131
47
|
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
132
48
|
await fs.mkdir(uploadDir, { recursive: true });
|
|
133
49
|
const upload = multer({ dest: uploadDir });
|
|
134
|
-
// Create the express app that serves the frontend
|
|
135
50
|
this.expressApp = express();
|
|
136
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
137
|
-
/*
|
|
138
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
139
|
-
for (const method of methods) {
|
|
140
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
141
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
142
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
143
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
144
|
-
try {
|
|
145
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
146
|
-
return original(path, ...rest);
|
|
147
|
-
} catch (err) {
|
|
148
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
149
|
-
throw err;
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
*/
|
|
154
|
-
// Log all requests to the server for debugging
|
|
155
|
-
/*
|
|
156
|
-
this.expressApp.use((req, res, next) => {
|
|
157
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
158
|
-
next();
|
|
159
|
-
});
|
|
160
|
-
*/
|
|
161
|
-
// Serve static files from '/static' endpoint
|
|
162
51
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
163
52
|
if (!hasParameter('ssl')) {
|
|
164
|
-
// Create an HTTP server and attach the express app
|
|
165
53
|
this.httpServer = createServer(this.expressApp);
|
|
166
|
-
// Listen on the specified port
|
|
167
54
|
if (hasParameter('ingress')) {
|
|
168
55
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
169
56
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -177,7 +64,6 @@ export class Frontend {
|
|
|
177
64
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
178
65
|
});
|
|
179
66
|
}
|
|
180
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
181
67
|
this.httpServer.on('error', (error) => {
|
|
182
68
|
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
183
69
|
switch (error.code) {
|
|
@@ -193,7 +79,6 @@ export class Frontend {
|
|
|
193
79
|
});
|
|
194
80
|
}
|
|
195
81
|
else {
|
|
196
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
197
82
|
let cert;
|
|
198
83
|
try {
|
|
199
84
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -221,9 +106,7 @@ export class Frontend {
|
|
|
221
106
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
222
107
|
}
|
|
223
108
|
const serverOptions = { cert, key, ca };
|
|
224
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
225
109
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
226
|
-
// Listen on the specified port
|
|
227
110
|
if (hasParameter('ingress')) {
|
|
228
111
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
229
112
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -237,7 +120,6 @@ export class Frontend {
|
|
|
237
120
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
238
121
|
});
|
|
239
122
|
}
|
|
240
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
241
123
|
this.httpsServer.on('error', (error) => {
|
|
242
124
|
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
243
125
|
switch (error.code) {
|
|
@@ -254,18 +136,16 @@ export class Frontend {
|
|
|
254
136
|
}
|
|
255
137
|
if (this.initializeError)
|
|
256
138
|
return;
|
|
257
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
258
139
|
const wssPort = this.port;
|
|
259
140
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
260
141
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
261
142
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
262
143
|
const clientIp = request.socket.remoteAddress;
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
144
|
+
let callbackLogLevel = "notice";
|
|
145
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
146
|
+
callbackLogLevel = "info";
|
|
147
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
148
|
+
callbackLogLevel = "debug";
|
|
269
149
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
270
150
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
271
151
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -299,7 +179,6 @@ export class Frontend {
|
|
|
299
179
|
this.webSocketServer.on('error', (ws, error) => {
|
|
300
180
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
301
181
|
});
|
|
302
|
-
// Subscribe to cli events
|
|
303
182
|
const { cliEmitter } = await import('./cli.js');
|
|
304
183
|
cliEmitter.removeAllListeners();
|
|
305
184
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
@@ -311,8 +190,6 @@ export class Frontend {
|
|
|
311
190
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
312
191
|
this.wssSendCpuUpdate(cpuUsage);
|
|
313
192
|
});
|
|
314
|
-
// Endpoint to validate login code
|
|
315
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
316
193
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
317
194
|
const { password } = req.body;
|
|
318
195
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -331,27 +208,23 @@ export class Frontend {
|
|
|
331
208
|
this.log.warn('/api/login error wrong password');
|
|
332
209
|
res.json({ valid: false });
|
|
333
210
|
}
|
|
334
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
335
211
|
}
|
|
336
212
|
catch (error) {
|
|
337
213
|
this.log.error('/api/login error getting password');
|
|
338
214
|
res.json({ valid: false });
|
|
339
215
|
}
|
|
340
216
|
});
|
|
341
|
-
// Endpoint to provide health check for docker
|
|
342
217
|
this.expressApp.get('/health', (req, res) => {
|
|
343
218
|
this.log.debug('Express received /health');
|
|
344
219
|
const healthStatus = {
|
|
345
|
-
status: 'ok',
|
|
346
|
-
uptime: process.uptime(),
|
|
347
|
-
timestamp: new Date().toISOString(),
|
|
220
|
+
status: 'ok',
|
|
221
|
+
uptime: process.uptime(),
|
|
222
|
+
timestamp: new Date().toISOString(),
|
|
348
223
|
};
|
|
349
224
|
res.status(200).json(healthStatus);
|
|
350
225
|
});
|
|
351
|
-
// Endpoint to provide memory usage details
|
|
352
226
|
this.expressApp.get('/memory', async (req, res) => {
|
|
353
227
|
this.log.debug('Express received /memory');
|
|
354
|
-
// Memory usage from process
|
|
355
228
|
const memoryUsageRaw = process.memoryUsage();
|
|
356
229
|
const memoryUsage = {
|
|
357
230
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -360,13 +233,10 @@ export class Frontend {
|
|
|
360
233
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
361
234
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
362
235
|
};
|
|
363
|
-
// V8 heap statistics
|
|
364
236
|
const { default: v8 } = await import('node:v8');
|
|
365
237
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
366
238
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
367
|
-
// Format heapStats
|
|
368
239
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
369
|
-
// Format heapSpaces
|
|
370
240
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
371
241
|
...space,
|
|
372
242
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -384,23 +254,19 @@ export class Frontend {
|
|
|
384
254
|
};
|
|
385
255
|
res.status(200).json(memoryReport);
|
|
386
256
|
});
|
|
387
|
-
// Endpoint to provide settings
|
|
388
257
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
389
258
|
this.log.debug('The frontend sent /api/settings');
|
|
390
259
|
res.json(await this.getApiSettings());
|
|
391
260
|
});
|
|
392
|
-
// Endpoint to provide plugins
|
|
393
261
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
394
262
|
this.log.debug('The frontend sent /api/plugins');
|
|
395
263
|
res.json(this.getBaseRegisteredPlugins());
|
|
396
264
|
});
|
|
397
|
-
// Endpoint to provide devices
|
|
398
265
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
399
266
|
this.log.debug('The frontend sent /api/devices');
|
|
400
267
|
const devices = await this.getDevices();
|
|
401
268
|
res.json(devices);
|
|
402
269
|
});
|
|
403
|
-
// Endpoint to view the matterbridge log
|
|
404
270
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
405
271
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
406
272
|
try {
|
|
@@ -413,7 +279,6 @@ export class Frontend {
|
|
|
413
279
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
414
280
|
}
|
|
415
281
|
});
|
|
416
|
-
// Endpoint to view the matter.js log
|
|
417
282
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
418
283
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
419
284
|
try {
|
|
@@ -426,7 +291,6 @@ export class Frontend {
|
|
|
426
291
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
427
292
|
}
|
|
428
293
|
});
|
|
429
|
-
// Endpoint to view the shelly log
|
|
430
294
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
431
295
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
432
296
|
try {
|
|
@@ -439,7 +303,6 @@ export class Frontend {
|
|
|
439
303
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
440
304
|
}
|
|
441
305
|
});
|
|
442
|
-
// Endpoint to download the matterbridge log
|
|
443
306
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
444
307
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
|
|
445
308
|
try {
|
|
@@ -452,7 +315,6 @@ export class Frontend {
|
|
|
452
315
|
this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
|
|
453
316
|
}
|
|
454
317
|
res.type('text/plain');
|
|
455
|
-
// res.download(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
|
|
456
318
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
|
|
457
319
|
if (error) {
|
|
458
320
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
@@ -460,7 +322,6 @@ export class Frontend {
|
|
|
460
322
|
}
|
|
461
323
|
});
|
|
462
324
|
});
|
|
463
|
-
// Endpoint to download the matter log
|
|
464
325
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
465
326
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
|
|
466
327
|
try {
|
|
@@ -480,7 +341,6 @@ export class Frontend {
|
|
|
480
341
|
}
|
|
481
342
|
});
|
|
482
343
|
});
|
|
483
|
-
// Endpoint to download the shelly log
|
|
484
344
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
485
345
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
486
346
|
try {
|
|
@@ -500,7 +360,6 @@ export class Frontend {
|
|
|
500
360
|
}
|
|
501
361
|
});
|
|
502
362
|
});
|
|
503
|
-
// Endpoint to download the matterbridge storage directory
|
|
504
363
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
505
364
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
506
365
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -511,7 +370,6 @@ export class Frontend {
|
|
|
511
370
|
}
|
|
512
371
|
});
|
|
513
372
|
});
|
|
514
|
-
// Endpoint to download the matter storage file
|
|
515
373
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
516
374
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
517
375
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
@@ -522,7 +380,6 @@ export class Frontend {
|
|
|
522
380
|
}
|
|
523
381
|
});
|
|
524
382
|
});
|
|
525
|
-
// Endpoint to download the matterbridge plugin directory
|
|
526
383
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
527
384
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
528
385
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -533,7 +390,6 @@ export class Frontend {
|
|
|
533
390
|
}
|
|
534
391
|
});
|
|
535
392
|
});
|
|
536
|
-
// Endpoint to download the matterbridge plugin config files
|
|
537
393
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
538
394
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
539
395
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
@@ -544,7 +400,6 @@ export class Frontend {
|
|
|
544
400
|
}
|
|
545
401
|
});
|
|
546
402
|
});
|
|
547
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
548
403
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
549
404
|
this.log.debug('The frontend sent /api/download-backup');
|
|
550
405
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -554,7 +409,6 @@ export class Frontend {
|
|
|
554
409
|
}
|
|
555
410
|
});
|
|
556
411
|
});
|
|
557
|
-
// Endpoint to upload a package
|
|
558
412
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
559
413
|
const { filename } = req.body;
|
|
560
414
|
const file = req.file;
|
|
@@ -564,13 +418,10 @@ export class Frontend {
|
|
|
564
418
|
return;
|
|
565
419
|
}
|
|
566
420
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
567
|
-
// Define the path where the plugin file will be saved
|
|
568
421
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
569
422
|
try {
|
|
570
|
-
// Move the uploaded file to the specified path
|
|
571
423
|
await fs.rename(file.path, filePath);
|
|
572
424
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
573
|
-
// Install the plugin package
|
|
574
425
|
if (filename.endsWith('.tgz')) {
|
|
575
426
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
576
427
|
this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
|
|
@@ -589,7 +440,6 @@ export class Frontend {
|
|
|
589
440
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
590
441
|
}
|
|
591
442
|
});
|
|
592
|
-
// Fallback for routing (must be the last route)
|
|
593
443
|
this.expressApp.use((req, res) => {
|
|
594
444
|
this.log.debug('The frontend sent:', req.url);
|
|
595
445
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -598,15 +448,12 @@ export class Frontend {
|
|
|
598
448
|
}
|
|
599
449
|
async stop() {
|
|
600
450
|
this.log.debug('Stopping the frontend...');
|
|
601
|
-
// Remove listeners from the express app
|
|
602
451
|
if (this.expressApp) {
|
|
603
452
|
this.expressApp.removeAllListeners();
|
|
604
453
|
this.expressApp = undefined;
|
|
605
454
|
this.log.debug('Frontend app closed successfully');
|
|
606
455
|
}
|
|
607
|
-
// Close the WebSocket server
|
|
608
456
|
if (this.webSocketServer) {
|
|
609
|
-
// Close all active connections
|
|
610
457
|
this.webSocketServer.clients.forEach((client) => {
|
|
611
458
|
if (client.readyState === WebSocket.OPEN) {
|
|
612
459
|
client.close();
|
|
@@ -626,7 +473,6 @@ export class Frontend {
|
|
|
626
473
|
this.webSocketServer.removeAllListeners();
|
|
627
474
|
this.webSocketServer = undefined;
|
|
628
475
|
}
|
|
629
|
-
// Close the http server
|
|
630
476
|
if (this.httpServer) {
|
|
631
477
|
await withTimeout(new Promise((resolve) => {
|
|
632
478
|
this.httpServer?.close((error) => {
|
|
@@ -643,7 +489,6 @@ export class Frontend {
|
|
|
643
489
|
this.httpServer = undefined;
|
|
644
490
|
this.log.debug('Frontend http server closed successfully');
|
|
645
491
|
}
|
|
646
|
-
// Close the https server
|
|
647
492
|
if (this.httpsServer) {
|
|
648
493
|
await withTimeout(new Promise((resolve) => {
|
|
649
494
|
this.httpsServer?.close((error) => {
|
|
@@ -662,7 +507,6 @@ export class Frontend {
|
|
|
662
507
|
}
|
|
663
508
|
this.log.debug('Frontend stopped successfully');
|
|
664
509
|
}
|
|
665
|
-
// Function to format bytes to KB, MB, or GB
|
|
666
510
|
formatMemoryUsage = (bytes) => {
|
|
667
511
|
if (bytes >= 1024 ** 3) {
|
|
668
512
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -674,7 +518,6 @@ export class Frontend {
|
|
|
674
518
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
675
519
|
}
|
|
676
520
|
};
|
|
677
|
-
// Function to format system uptime with only the most significant unit
|
|
678
521
|
formatOsUpTime = (seconds) => {
|
|
679
522
|
if (seconds >= 86400) {
|
|
680
523
|
const days = Math.floor(seconds / 86400);
|
|
@@ -690,14 +533,8 @@ export class Frontend {
|
|
|
690
533
|
}
|
|
691
534
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
692
535
|
};
|
|
693
|
-
/**
|
|
694
|
-
* Retrieves the api settings data.
|
|
695
|
-
*
|
|
696
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
697
|
-
*/
|
|
698
536
|
async getApiSettings() {
|
|
699
537
|
const { lastCpuUsage } = await import('./cli.js');
|
|
700
|
-
// Update the system information
|
|
701
538
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
702
539
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
703
540
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -706,7 +543,6 @@ export class Frontend {
|
|
|
706
543
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
707
544
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
708
545
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
709
|
-
// Update the matterbridge information
|
|
710
546
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
711
547
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
712
548
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -725,11 +561,6 @@ export class Frontend {
|
|
|
725
561
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
726
562
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
727
563
|
}
|
|
728
|
-
/**
|
|
729
|
-
* Retrieves the reachable attribute.
|
|
730
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
731
|
-
* @returns {boolean} The reachable attribute.
|
|
732
|
-
*/
|
|
733
564
|
getReachability(device) {
|
|
734
565
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
735
566
|
return false;
|
|
@@ -739,11 +570,6 @@ export class Frontend {
|
|
|
739
570
|
return true;
|
|
740
571
|
return false;
|
|
741
572
|
}
|
|
742
|
-
/**
|
|
743
|
-
* Retrieves the power source attribute.
|
|
744
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice object.
|
|
745
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
746
|
-
*/
|
|
747
573
|
getPowerSource(endpoint) {
|
|
748
574
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
749
575
|
return undefined;
|
|
@@ -759,20 +585,13 @@ export class Frontend {
|
|
|
759
585
|
}
|
|
760
586
|
return;
|
|
761
587
|
};
|
|
762
|
-
// Root endpoint
|
|
763
588
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
764
589
|
return powerSource(endpoint);
|
|
765
|
-
// Child endpoints
|
|
766
590
|
for (const child of endpoint.getChildEndpoints()) {
|
|
767
591
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
768
592
|
return powerSource(child);
|
|
769
593
|
}
|
|
770
594
|
}
|
|
771
|
-
/**
|
|
772
|
-
* Retrieves the cluster text description from a given device.
|
|
773
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
774
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
775
|
-
*/
|
|
776
595
|
getClusterTextFromDevice(device) {
|
|
777
596
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
778
597
|
return '';
|
|
@@ -814,7 +633,6 @@ export class Frontend {
|
|
|
814
633
|
let attributes = '';
|
|
815
634
|
let supportedModes = [];
|
|
816
635
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
817
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
818
636
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
819
637
|
return;
|
|
820
638
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -906,13 +724,8 @@ export class Frontend {
|
|
|
906
724
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
907
725
|
attributes += `${getUserLabel(device)} `;
|
|
908
726
|
});
|
|
909
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
910
727
|
return attributes.trimStart().trimEnd();
|
|
911
728
|
}
|
|
912
|
-
/**
|
|
913
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
914
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
915
|
-
*/
|
|
916
729
|
getBaseRegisteredPlugins() {
|
|
917
730
|
const baseRegisteredPlugins = [];
|
|
918
731
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -951,18 +764,11 @@ export class Frontend {
|
|
|
951
764
|
}
|
|
952
765
|
return baseRegisteredPlugins;
|
|
953
766
|
}
|
|
954
|
-
/**
|
|
955
|
-
* Retrieves the devices from Matterbridge.
|
|
956
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
957
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices.
|
|
958
|
-
*/
|
|
959
767
|
async getDevices(pluginName) {
|
|
960
768
|
const devices = [];
|
|
961
769
|
this.matterbridge.devices.forEach(async (device) => {
|
|
962
|
-
// Filter by pluginName if provided
|
|
963
770
|
if (pluginName && pluginName !== device.plugin)
|
|
964
771
|
return;
|
|
965
|
-
// Check if the device has the required properties
|
|
966
772
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
967
773
|
return;
|
|
968
774
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -982,37 +788,22 @@ export class Frontend {
|
|
|
982
788
|
});
|
|
983
789
|
return devices;
|
|
984
790
|
}
|
|
985
|
-
/**
|
|
986
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
987
|
-
*
|
|
988
|
-
* Response for /api/clusters
|
|
989
|
-
*
|
|
990
|
-
* @param {string} pluginName - The name of the plugin.
|
|
991
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
992
|
-
* @returns {Promise<ApiClustersResponse | undefined>} A promise that resolves to the clusters or undefined if not found.
|
|
993
|
-
*/
|
|
994
791
|
getClusters(pluginName, endpointNumber) {
|
|
995
792
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
996
793
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
997
794
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
998
795
|
return;
|
|
999
796
|
}
|
|
1000
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1001
|
-
// Get the device types from the main endpoint
|
|
1002
797
|
const deviceTypes = [];
|
|
1003
798
|
const clusters = [];
|
|
1004
799
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1005
800
|
deviceTypes.push(d.deviceType);
|
|
1006
801
|
});
|
|
1007
|
-
// Get the clusters from the main endpoint
|
|
1008
802
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1009
803
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1010
804
|
return;
|
|
1011
805
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1012
806
|
return;
|
|
1013
|
-
// console.log(
|
|
1014
|
-
// `${idn}${endpoint.deviceName}${rs}${nf} => Cluster: ${CYAN}${clusterName} (0x${clusterId.toString(16).padStart(2, '0')})${nf} Attribute: ${CYAN}${attributeName} (0x${attributeId.toString(16).padStart(2, '0')})${nf} Value: ${YELLOW}${typeof attributeValue === 'object' ? stringify(attributeValue as object) : attributeValue}${nf}`,
|
|
1015
|
-
// );
|
|
1016
807
|
clusters.push({
|
|
1017
808
|
endpoint: endpoint.number.toString(),
|
|
1018
809
|
id: 'main',
|
|
@@ -1025,18 +816,12 @@ export class Frontend {
|
|
|
1025
816
|
attributeLocalValue: attributeValue,
|
|
1026
817
|
});
|
|
1027
818
|
});
|
|
1028
|
-
// Get the child endpoints
|
|
1029
819
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1030
|
-
// if (childEndpoints.length === 0) {
|
|
1031
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1032
|
-
// }
|
|
1033
820
|
childEndpoints.forEach((childEndpoint) => {
|
|
1034
821
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1035
822
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1036
823
|
return;
|
|
1037
824
|
}
|
|
1038
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1039
|
-
// Get the device types of the child endpoint
|
|
1040
825
|
const deviceTypes = [];
|
|
1041
826
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1042
827
|
deviceTypes.push(d.deviceType);
|
|
@@ -1046,12 +831,9 @@ export class Frontend {
|
|
|
1046
831
|
return;
|
|
1047
832
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1048
833
|
return;
|
|
1049
|
-
// console.log(
|
|
1050
|
-
// `${idn}${childEndpoint.deviceName}${rs}${nf} => Cluster: ${CYAN}${clusterName} (0x${clusterId.toString(16).padStart(2, '0')})${nf} Attribute: ${CYAN}${attributeName} (0x${attributeId.toString(16).padStart(2, '0')})${nf} Value: ${YELLOW}${typeof attributeValue === 'object' ? stringify(attributeValue as object) : attributeValue}${nf}`,
|
|
1051
|
-
// );
|
|
1052
834
|
clusters.push({
|
|
1053
835
|
endpoint: childEndpoint.number.toString(),
|
|
1054
|
-
id: childEndpoint.maybeId ?? 'null',
|
|
836
|
+
id: childEndpoint.maybeId ?? 'null',
|
|
1055
837
|
deviceTypes,
|
|
1056
838
|
clusterName: capitalizeFirstLetter(clusterName),
|
|
1057
839
|
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
@@ -1064,13 +846,6 @@ export class Frontend {
|
|
|
1064
846
|
});
|
|
1065
847
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
|
|
1066
848
|
}
|
|
1067
|
-
/**
|
|
1068
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1069
|
-
*
|
|
1070
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1071
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1072
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1073
|
-
*/
|
|
1074
849
|
async wsMessageHandler(client, message) {
|
|
1075
850
|
let data;
|
|
1076
851
|
try {
|
|
@@ -1117,10 +892,8 @@ export class Frontend {
|
|
|
1117
892
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1118
893
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1119
894
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1120
|
-
// The install comes from InstallPlugins
|
|
1121
895
|
this.matterbridge.plugins.add(packageName).then((plugin) => {
|
|
1122
896
|
if (plugin) {
|
|
1123
|
-
// The plugin is not registered
|
|
1124
897
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1125
898
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
1126
899
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
@@ -1128,7 +901,6 @@ export class Frontend {
|
|
|
1128
901
|
});
|
|
1129
902
|
}
|
|
1130
903
|
else {
|
|
1131
|
-
// The plugin is already registered
|
|
1132
904
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1133
905
|
this.wssSendRefreshRequired('plugins');
|
|
1134
906
|
this.wssSendRestartRequired();
|
|
@@ -1136,7 +908,6 @@ export class Frontend {
|
|
|
1136
908
|
});
|
|
1137
909
|
}
|
|
1138
910
|
else {
|
|
1139
|
-
// The package is matterbridge
|
|
1140
911
|
if (this.matterbridge.restartMode !== '') {
|
|
1141
912
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1142
913
|
this.matterbridge.shutdownProcess();
|
|
@@ -1158,7 +929,6 @@ export class Frontend {
|
|
|
1158
929
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1159
930
|
return;
|
|
1160
931
|
}
|
|
1161
|
-
// The package is a plugin
|
|
1162
932
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1163
933
|
if (plugin) {
|
|
1164
934
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
@@ -1167,7 +937,6 @@ export class Frontend {
|
|
|
1167
937
|
this.wssSendRefreshRequired('plugins');
|
|
1168
938
|
this.wssSendRefreshRequired('devices');
|
|
1169
939
|
}
|
|
1170
|
-
// Uninstall the package
|
|
1171
940
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1172
941
|
this.matterbridge
|
|
1173
942
|
.spawnCommand('npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1479,22 +1248,22 @@ export class Frontend {
|
|
|
1479
1248
|
if (isValidString(data.params.value, 4)) {
|
|
1480
1249
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1481
1250
|
if (data.params.value === 'Debug') {
|
|
1482
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1251
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1483
1252
|
}
|
|
1484
1253
|
else if (data.params.value === 'Info') {
|
|
1485
|
-
await this.matterbridge.setLogLevel("info"
|
|
1254
|
+
await this.matterbridge.setLogLevel("info");
|
|
1486
1255
|
}
|
|
1487
1256
|
else if (data.params.value === 'Notice') {
|
|
1488
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1257
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1489
1258
|
}
|
|
1490
1259
|
else if (data.params.value === 'Warn') {
|
|
1491
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1260
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1492
1261
|
}
|
|
1493
1262
|
else if (data.params.value === 'Error') {
|
|
1494
|
-
await this.matterbridge.setLogLevel("error"
|
|
1263
|
+
await this.matterbridge.setLogLevel("error");
|
|
1495
1264
|
}
|
|
1496
1265
|
else if (data.params.value === 'Fatal') {
|
|
1497
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1266
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1498
1267
|
}
|
|
1499
1268
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1500
1269
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1505,7 +1274,6 @@ export class Frontend {
|
|
|
1505
1274
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1506
1275
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1507
1276
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1508
|
-
// Create the file logger for matterbridge
|
|
1509
1277
|
if (data.params.value)
|
|
1510
1278
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1511
1279
|
else
|
|
@@ -1670,19 +1438,15 @@ export class Frontend {
|
|
|
1670
1438
|
return;
|
|
1671
1439
|
}
|
|
1672
1440
|
const config = plugin.configJson;
|
|
1673
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1674
1441
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1675
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1676
1442
|
if (select === 'serial')
|
|
1677
1443
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1678
1444
|
if (select === 'name')
|
|
1679
1445
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1680
1446
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1681
|
-
// Remove postfix from the serial if it exists
|
|
1682
1447
|
if (config.postfix) {
|
|
1683
1448
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1684
1449
|
}
|
|
1685
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1686
1450
|
if (isValidArray(config.whiteList, 1)) {
|
|
1687
1451
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1688
1452
|
config.whiteList.push(data.params.serial);
|
|
@@ -1691,7 +1455,6 @@ export class Frontend {
|
|
|
1691
1455
|
config.whiteList.push(data.params.name);
|
|
1692
1456
|
}
|
|
1693
1457
|
}
|
|
1694
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1695
1458
|
if (isValidArray(config.blackList, 1)) {
|
|
1696
1459
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1697
1460
|
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
@@ -1721,9 +1484,7 @@ export class Frontend {
|
|
|
1721
1484
|
return;
|
|
1722
1485
|
}
|
|
1723
1486
|
const config = plugin.configJson;
|
|
1724
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1725
1487
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1726
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1727
1488
|
if (select === 'serial')
|
|
1728
1489
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1729
1490
|
if (select === 'name')
|
|
@@ -1732,7 +1493,6 @@ export class Frontend {
|
|
|
1732
1493
|
if (config.postfix) {
|
|
1733
1494
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1734
1495
|
}
|
|
1735
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1736
1496
|
if (isValidArray(config.whiteList, 1)) {
|
|
1737
1497
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1738
1498
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
@@ -1741,7 +1501,6 @@ export class Frontend {
|
|
|
1741
1501
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1742
1502
|
}
|
|
1743
1503
|
}
|
|
1744
|
-
// Add the serial to the blackList
|
|
1745
1504
|
if (isValidArray(config.blackList)) {
|
|
1746
1505
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1747
1506
|
config.blackList.push(data.params.serial);
|
|
@@ -1774,219 +1533,114 @@ export class Frontend {
|
|
|
1774
1533
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1775
1534
|
}
|
|
1776
1535
|
}
|
|
1777
|
-
/**
|
|
1778
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1779
|
-
*
|
|
1780
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1781
|
-
* @param {string} time - The time string of the message
|
|
1782
|
-
* @param {string} name - The logger name of the message
|
|
1783
|
-
* @param {string} message - The content of the message.
|
|
1784
|
-
*
|
|
1785
|
-
* @remark
|
|
1786
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
1787
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
1788
|
-
* The function sends the message to all connected clients.
|
|
1789
|
-
*/
|
|
1790
1536
|
wssSendMessage(level, time, name, message) {
|
|
1791
1537
|
if (!level || !time || !name || !message)
|
|
1792
1538
|
return;
|
|
1793
|
-
// Remove ANSI escape codes from the message
|
|
1794
|
-
// eslint-disable-next-line no-control-regex
|
|
1795
1539
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1796
|
-
// Remove leading asterisks from the message
|
|
1797
1540
|
message = message.replace(/^\*+/, '');
|
|
1798
|
-
// Replace all occurrences of \t and \n
|
|
1799
1541
|
message = message.replace(/[\t\n]/g, '');
|
|
1800
|
-
// Remove non-printable characters
|
|
1801
|
-
// eslint-disable-next-line no-control-regex
|
|
1802
1542
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1803
|
-
// Replace all occurrences of \" with "
|
|
1804
1543
|
message = message.replace(/\\"/g, '"');
|
|
1805
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1806
1544
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1807
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1808
1545
|
const maxContinuousLength = 100;
|
|
1809
1546
|
const keepStartLength = 20;
|
|
1810
1547
|
const keepEndLength = 20;
|
|
1811
|
-
// Split the message into words
|
|
1812
1548
|
message = message
|
|
1813
1549
|
.split(' ')
|
|
1814
1550
|
.map((word) => {
|
|
1815
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1816
1551
|
if (word.length > maxContinuousLength) {
|
|
1817
1552
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1818
1553
|
}
|
|
1819
1554
|
return word;
|
|
1820
1555
|
})
|
|
1821
1556
|
.join(' ');
|
|
1822
|
-
// Send the message to all connected clients
|
|
1823
1557
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1824
1558
|
if (client.readyState === WebSocket.OPEN) {
|
|
1825
1559
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1826
1560
|
}
|
|
1827
1561
|
});
|
|
1828
1562
|
}
|
|
1829
|
-
/**
|
|
1830
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1831
|
-
*
|
|
1832
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
1833
|
-
* possible values:
|
|
1834
|
-
* - 'matterbridgeLatestVersion'
|
|
1835
|
-
* - 'matterbridgeAdvertise'
|
|
1836
|
-
* - 'online'
|
|
1837
|
-
* - 'offline'
|
|
1838
|
-
* - 'reachability'
|
|
1839
|
-
* - 'settings'
|
|
1840
|
-
* - 'plugins'
|
|
1841
|
-
* - 'pluginsRestart'
|
|
1842
|
-
* - 'devices'
|
|
1843
|
-
* - 'fabrics'
|
|
1844
|
-
* - 'sessions'
|
|
1845
|
-
*/
|
|
1846
1563
|
wssSendRefreshRequired(changed = null) {
|
|
1847
1564
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1848
|
-
// Send the message to all connected clients
|
|
1849
1565
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1850
1566
|
if (client.readyState === WebSocket.OPEN) {
|
|
1851
1567
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1852
1568
|
}
|
|
1853
1569
|
});
|
|
1854
1570
|
}
|
|
1855
|
-
/**
|
|
1856
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1857
|
-
*
|
|
1858
|
-
*/
|
|
1859
1571
|
wssSendRestartRequired(snackbar = true) {
|
|
1860
1572
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1861
1573
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1862
1574
|
if (snackbar === true)
|
|
1863
1575
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1864
|
-
// Send the message to all connected clients
|
|
1865
1576
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1866
1577
|
if (client.readyState === WebSocket.OPEN) {
|
|
1867
1578
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1868
1579
|
}
|
|
1869
1580
|
});
|
|
1870
1581
|
}
|
|
1871
|
-
/**
|
|
1872
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
1873
|
-
*
|
|
1874
|
-
*/
|
|
1875
1582
|
wssSendUpdateRequired() {
|
|
1876
1583
|
this.log.debug('Sending an update required message to all connected clients');
|
|
1877
1584
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
1878
|
-
// Send the message to all connected clients
|
|
1879
1585
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1880
1586
|
if (client.readyState === WebSocket.OPEN) {
|
|
1881
1587
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
1882
1588
|
}
|
|
1883
1589
|
});
|
|
1884
1590
|
}
|
|
1885
|
-
/**
|
|
1886
|
-
* Sends a cpu update message to all connected clients.
|
|
1887
|
-
*
|
|
1888
|
-
*/
|
|
1889
1591
|
wssSendCpuUpdate(cpuUsage) {
|
|
1890
1592
|
if (hasParameter('debug'))
|
|
1891
1593
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1892
|
-
// Send the message to all connected clients
|
|
1893
1594
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1894
1595
|
if (client.readyState === WebSocket.OPEN) {
|
|
1895
1596
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
1896
1597
|
}
|
|
1897
1598
|
});
|
|
1898
1599
|
}
|
|
1899
|
-
/**
|
|
1900
|
-
* Sends a memory update message to all connected clients.
|
|
1901
|
-
*
|
|
1902
|
-
*/
|
|
1903
1600
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
1904
1601
|
if (hasParameter('debug'))
|
|
1905
1602
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
1906
|
-
// Send the message to all connected clients
|
|
1907
1603
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1908
1604
|
if (client.readyState === WebSocket.OPEN) {
|
|
1909
1605
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
1910
1606
|
}
|
|
1911
1607
|
});
|
|
1912
1608
|
}
|
|
1913
|
-
/**
|
|
1914
|
-
* Sends an uptime update message to all connected clients.
|
|
1915
|
-
*
|
|
1916
|
-
*/
|
|
1917
1609
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
1918
1610
|
if (hasParameter('debug'))
|
|
1919
1611
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
1920
|
-
// Send the message to all connected clients
|
|
1921
1612
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1922
1613
|
if (client.readyState === WebSocket.OPEN) {
|
|
1923
1614
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
1924
1615
|
}
|
|
1925
1616
|
});
|
|
1926
1617
|
}
|
|
1927
|
-
/**
|
|
1928
|
-
* Sends an open snackbar message to all connected clients.
|
|
1929
|
-
* @param {string} message - The message to send.
|
|
1930
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
1931
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
1932
|
-
*
|
|
1933
|
-
*/
|
|
1934
1618
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
1935
1619
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
1936
|
-
// Send the message to all connected clients
|
|
1937
1620
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1938
1621
|
if (client.readyState === WebSocket.OPEN) {
|
|
1939
1622
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
1940
1623
|
}
|
|
1941
1624
|
});
|
|
1942
1625
|
}
|
|
1943
|
-
/**
|
|
1944
|
-
* Sends a close snackbar message to all connected clients.
|
|
1945
|
-
* @param {string} message - The message to send.
|
|
1946
|
-
*
|
|
1947
|
-
*/
|
|
1948
1626
|
wssSendCloseSnackbarMessage(message) {
|
|
1949
1627
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
1950
|
-
// Send the message to all connected clients
|
|
1951
1628
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1952
1629
|
if (client.readyState === WebSocket.OPEN) {
|
|
1953
1630
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
1954
1631
|
}
|
|
1955
1632
|
});
|
|
1956
1633
|
}
|
|
1957
|
-
/**
|
|
1958
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
1959
|
-
*
|
|
1960
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
1961
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
1962
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
1963
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
1964
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
1965
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
1966
|
-
*
|
|
1967
|
-
* @remarks
|
|
1968
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
1969
|
-
* with the updated attribute information.
|
|
1970
|
-
*/
|
|
1971
1634
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
1972
1635
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
1973
|
-
// Send the message to all connected clients
|
|
1974
1636
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1975
1637
|
if (client.readyState === WebSocket.OPEN) {
|
|
1976
1638
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
1977
1639
|
}
|
|
1978
1640
|
});
|
|
1979
1641
|
}
|
|
1980
|
-
/**
|
|
1981
|
-
* Sends a message to all connected clients.
|
|
1982
|
-
* @param {number} id - The message id.
|
|
1983
|
-
* @param {string} method - The message method.
|
|
1984
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
1985
|
-
*
|
|
1986
|
-
*/
|
|
1987
1642
|
wssBroadcastMessage(id, method, params) {
|
|
1988
1643
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
1989
|
-
// Send the message to all connected clients
|
|
1990
1644
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1991
1645
|
if (client.readyState === WebSocket.OPEN) {
|
|
1992
1646
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -1994,4 +1648,3 @@ export class Frontend {
|
|
|
1994
1648
|
});
|
|
1995
1649
|
}
|
|
1996
1650
|
}
|
|
1997
|
-
//# sourceMappingURL=frontend.js.map
|