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