matterbridge 3.0.3 → 3.0.4-dev-20250525-c88cf84
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 +25 -4
- package/README.md +6 -12
- package/dist/cli.js +2 -37
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -94
- package/dist/frontend.js +100 -437
- package/dist/helpers.js +9 -53
- package/dist/index.js +1 -27
- 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 +1 -3
- package/dist/matter/types.js +0 -2
- package/dist/matterbridge.js +47 -747
- package/dist/matterbridgeAccessoryPlatform.js +0 -34
- package/dist/matterbridgeBehaviors.js +43 -61
- package/dist/matterbridgeDeviceTypes.js +15 -563
- package/dist/matterbridgeDynamicPlatform.js +0 -34
- package/dist/matterbridgeEndpoint.js +68 -837
- package/dist/matterbridgeEndpointHelpers.js +33 -204
- 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 +10 -10
- package/package.json +2 -3
- package/dist/cli.d.ts +0 -29
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cluster/export.d.ts +0 -2
- package/dist/cluster/export.d.ts.map +0 -1
- package/dist/cluster/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -27
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -114
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -241
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/helpers.d.ts +0 -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 -1201
- 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 -967
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2728
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -294
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -188
- 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,111 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
-
import { EndpointServer, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
|
|
25
|
-
// Node modules
|
|
1
|
+
import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
|
|
2
|
+
import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
|
|
26
3
|
import { createServer } from 'node:http';
|
|
27
4
|
import https from 'node:https';
|
|
28
5
|
import os from 'node:os';
|
|
29
6
|
import path from 'node:path';
|
|
30
7
|
import { promises as fs } from 'node:fs';
|
|
31
|
-
// Third-party modules
|
|
32
8
|
import express from 'express';
|
|
33
9
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
34
10
|
import multer from 'multer';
|
|
35
|
-
// AnsiLogger module
|
|
36
11
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from './logger/export.js';
|
|
37
|
-
// Matterbridge
|
|
38
12
|
import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout } from './utils/export.js';
|
|
39
13
|
import { plg } from './matterbridgeTypes.js';
|
|
40
14
|
import { hasParameter } from './utils/export.js';
|
|
41
|
-
import {
|
|
42
|
-
/**
|
|
43
|
-
* Websocket message ID for logging.
|
|
44
|
-
* @constant {number}
|
|
45
|
-
*/
|
|
15
|
+
import { capitalizeFirstLetter } from './matterbridgeEndpointHelpers.js';
|
|
46
16
|
export const WS_ID_LOG = 0;
|
|
47
|
-
/**
|
|
48
|
-
* Websocket message ID indicating a refresh is needed.
|
|
49
|
-
* @constant {number}
|
|
50
|
-
*/
|
|
51
17
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
52
|
-
/**
|
|
53
|
-
* Websocket message ID indicating a restart is needed.
|
|
54
|
-
* @constant {number}
|
|
55
|
-
*/
|
|
56
18
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
57
|
-
/**
|
|
58
|
-
* Websocket message ID indicating a cpu update.
|
|
59
|
-
* @constant {number}
|
|
60
|
-
*/
|
|
61
19
|
export const WS_ID_CPU_UPDATE = 3;
|
|
62
|
-
/**
|
|
63
|
-
* Websocket message ID indicating a memory update.
|
|
64
|
-
* @constant {number}
|
|
65
|
-
*/
|
|
66
20
|
export const WS_ID_MEMORY_UPDATE = 4;
|
|
67
|
-
/**
|
|
68
|
-
* Websocket message ID indicating an uptime update.
|
|
69
|
-
* @constant {number}
|
|
70
|
-
*/
|
|
71
21
|
export const WS_ID_UPTIME_UPDATE = 5;
|
|
72
|
-
/**
|
|
73
|
-
* Websocket message ID indicating a snackbar message.
|
|
74
|
-
* @constant {number}
|
|
75
|
-
*/
|
|
76
22
|
export const WS_ID_SNACKBAR = 6;
|
|
77
|
-
/**
|
|
78
|
-
* Websocket message ID indicating matterbridge has un update available.
|
|
79
|
-
* @constant {number}
|
|
80
|
-
*/
|
|
81
23
|
export const WS_ID_UPDATE_NEEDED = 7;
|
|
82
|
-
/**
|
|
83
|
-
* Websocket message ID indicating a state update.
|
|
84
|
-
* @constant {number}
|
|
85
|
-
*/
|
|
86
24
|
export const WS_ID_STATEUPDATE = 8;
|
|
87
|
-
/**
|
|
88
|
-
* Websocket message ID indicating to close a permanent snackbar message.
|
|
89
|
-
* @constant {number}
|
|
90
|
-
*/
|
|
91
25
|
export const WS_ID_CLOSE_SNACKBAR = 9;
|
|
92
|
-
/**
|
|
93
|
-
* Websocket message ID indicating a shelly system update.
|
|
94
|
-
* check:
|
|
95
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/check
|
|
96
|
-
* perform:
|
|
97
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/perform
|
|
98
|
-
* @constant {number}
|
|
99
|
-
*/
|
|
100
26
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
101
|
-
/**
|
|
102
|
-
* Websocket message ID indicating a shelly main update.
|
|
103
|
-
* check:
|
|
104
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/check
|
|
105
|
-
* perform:
|
|
106
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/perform
|
|
107
|
-
* @constant {number}
|
|
108
|
-
*/
|
|
109
27
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
110
28
|
export class Frontend {
|
|
111
29
|
matterbridge;
|
|
@@ -118,7 +36,7 @@ export class Frontend {
|
|
|
118
36
|
webSocketServer;
|
|
119
37
|
constructor(matterbridge) {
|
|
120
38
|
this.matterbridge = matterbridge;
|
|
121
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
39
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
122
40
|
}
|
|
123
41
|
set logLevel(logLevel) {
|
|
124
42
|
this.log.logLevel = logLevel;
|
|
@@ -126,43 +44,13 @@ export class Frontend {
|
|
|
126
44
|
async start(port = 8283) {
|
|
127
45
|
this.port = port;
|
|
128
46
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
129
|
-
// Initialize multer with the upload directory
|
|
130
47
|
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
131
48
|
await fs.mkdir(uploadDir, { recursive: true });
|
|
132
49
|
const upload = multer({ dest: uploadDir });
|
|
133
|
-
// Create the express app that serves the frontend
|
|
134
50
|
this.expressApp = express();
|
|
135
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
136
|
-
/*
|
|
137
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
138
|
-
for (const method of methods) {
|
|
139
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
140
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
141
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
142
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
143
|
-
try {
|
|
144
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
145
|
-
return original(path, ...rest);
|
|
146
|
-
} catch (err) {
|
|
147
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
148
|
-
throw err;
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
*/
|
|
153
|
-
// Log all requests to the server for debugging
|
|
154
|
-
/*
|
|
155
|
-
this.expressApp.use((req, res, next) => {
|
|
156
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
157
|
-
next();
|
|
158
|
-
});
|
|
159
|
-
*/
|
|
160
|
-
// Serve static files from '/static' endpoint
|
|
161
51
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
162
52
|
if (!hasParameter('ssl')) {
|
|
163
|
-
// Create an HTTP server and attach the express app
|
|
164
53
|
this.httpServer = createServer(this.expressApp);
|
|
165
|
-
// Listen on the specified port
|
|
166
54
|
if (hasParameter('ingress')) {
|
|
167
55
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
168
56
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -176,7 +64,6 @@ export class Frontend {
|
|
|
176
64
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
177
65
|
});
|
|
178
66
|
}
|
|
179
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
180
67
|
this.httpServer.on('error', (error) => {
|
|
181
68
|
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
182
69
|
switch (error.code) {
|
|
@@ -192,7 +79,6 @@ export class Frontend {
|
|
|
192
79
|
});
|
|
193
80
|
}
|
|
194
81
|
else {
|
|
195
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
196
82
|
let cert;
|
|
197
83
|
try {
|
|
198
84
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -220,9 +106,7 @@ export class Frontend {
|
|
|
220
106
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
221
107
|
}
|
|
222
108
|
const serverOptions = { cert, key, ca };
|
|
223
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
224
109
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
225
|
-
// Listen on the specified port
|
|
226
110
|
if (hasParameter('ingress')) {
|
|
227
111
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
228
112
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -236,7 +120,6 @@ export class Frontend {
|
|
|
236
120
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
237
121
|
});
|
|
238
122
|
}
|
|
239
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
240
123
|
this.httpsServer.on('error', (error) => {
|
|
241
124
|
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
242
125
|
switch (error.code) {
|
|
@@ -253,18 +136,16 @@ export class Frontend {
|
|
|
253
136
|
}
|
|
254
137
|
if (this.initializeError)
|
|
255
138
|
return;
|
|
256
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
257
139
|
const wssPort = this.port;
|
|
258
140
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
259
141
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
260
142
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
261
143
|
const clientIp = request.socket.remoteAddress;
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
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";
|
|
268
149
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
269
150
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
270
151
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -298,7 +179,6 @@ export class Frontend {
|
|
|
298
179
|
this.webSocketServer.on('error', (ws, error) => {
|
|
299
180
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
300
181
|
});
|
|
301
|
-
// Subscribe to cli events
|
|
302
182
|
const { cliEmitter } = await import('./cli.js');
|
|
303
183
|
cliEmitter.removeAllListeners();
|
|
304
184
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
@@ -310,7 +190,6 @@ export class Frontend {
|
|
|
310
190
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
311
191
|
this.wssSendCpuUpdate(cpuUsage);
|
|
312
192
|
});
|
|
313
|
-
// Endpoint to validate login code
|
|
314
193
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
315
194
|
const { password } = req.body;
|
|
316
195
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -329,27 +208,23 @@ export class Frontend {
|
|
|
329
208
|
this.log.warn('/api/login error wrong password');
|
|
330
209
|
res.json({ valid: false });
|
|
331
210
|
}
|
|
332
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
333
211
|
}
|
|
334
212
|
catch (error) {
|
|
335
213
|
this.log.error('/api/login error getting password');
|
|
336
214
|
res.json({ valid: false });
|
|
337
215
|
}
|
|
338
216
|
});
|
|
339
|
-
// Endpoint to provide health check for docker
|
|
340
217
|
this.expressApp.get('/health', (req, res) => {
|
|
341
218
|
this.log.debug('Express received /health');
|
|
342
219
|
const healthStatus = {
|
|
343
|
-
status: 'ok',
|
|
344
|
-
uptime: process.uptime(),
|
|
345
|
-
timestamp: new Date().toISOString(),
|
|
220
|
+
status: 'ok',
|
|
221
|
+
uptime: process.uptime(),
|
|
222
|
+
timestamp: new Date().toISOString(),
|
|
346
223
|
};
|
|
347
224
|
res.status(200).json(healthStatus);
|
|
348
225
|
});
|
|
349
|
-
// Endpoint to provide memory usage details
|
|
350
226
|
this.expressApp.get('/memory', async (req, res) => {
|
|
351
227
|
this.log.debug('Express received /memory');
|
|
352
|
-
// Memory usage from process
|
|
353
228
|
const memoryUsageRaw = process.memoryUsage();
|
|
354
229
|
const memoryUsage = {
|
|
355
230
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -358,13 +233,10 @@ export class Frontend {
|
|
|
358
233
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
359
234
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
360
235
|
};
|
|
361
|
-
// V8 heap statistics
|
|
362
236
|
const { default: v8 } = await import('node:v8');
|
|
363
237
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
364
238
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
365
|
-
// Format heapStats
|
|
366
239
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
367
|
-
// Format heapSpaces
|
|
368
240
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
369
241
|
...space,
|
|
370
242
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -382,23 +254,19 @@ export class Frontend {
|
|
|
382
254
|
};
|
|
383
255
|
res.status(200).json(memoryReport);
|
|
384
256
|
});
|
|
385
|
-
// Endpoint to provide settings
|
|
386
257
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
387
258
|
this.log.debug('The frontend sent /api/settings');
|
|
388
259
|
res.json(await this.getApiSettings());
|
|
389
260
|
});
|
|
390
|
-
// Endpoint to provide plugins
|
|
391
261
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
392
262
|
this.log.debug('The frontend sent /api/plugins');
|
|
393
263
|
res.json(this.getBaseRegisteredPlugins());
|
|
394
264
|
});
|
|
395
|
-
// Endpoint to provide devices
|
|
396
265
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
397
266
|
this.log.debug('The frontend sent /api/devices');
|
|
398
267
|
const devices = await this.getDevices();
|
|
399
268
|
res.json(devices);
|
|
400
269
|
});
|
|
401
|
-
// Endpoint to view the matterbridge log
|
|
402
270
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
403
271
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
404
272
|
try {
|
|
@@ -411,7 +279,6 @@ export class Frontend {
|
|
|
411
279
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
412
280
|
}
|
|
413
281
|
});
|
|
414
|
-
// Endpoint to view the matter.js log
|
|
415
282
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
416
283
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
417
284
|
try {
|
|
@@ -424,7 +291,6 @@ export class Frontend {
|
|
|
424
291
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
425
292
|
}
|
|
426
293
|
});
|
|
427
|
-
// Endpoint to view the shelly log
|
|
428
294
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
429
295
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
430
296
|
try {
|
|
@@ -437,7 +303,6 @@ export class Frontend {
|
|
|
437
303
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
438
304
|
}
|
|
439
305
|
});
|
|
440
|
-
// Endpoint to download the matterbridge log
|
|
441
306
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
442
307
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
|
|
443
308
|
try {
|
|
@@ -450,7 +315,6 @@ export class Frontend {
|
|
|
450
315
|
this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
|
|
451
316
|
}
|
|
452
317
|
res.type('text/plain');
|
|
453
|
-
// res.download(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
|
|
454
318
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
|
|
455
319
|
if (error) {
|
|
456
320
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
@@ -458,7 +322,6 @@ export class Frontend {
|
|
|
458
322
|
}
|
|
459
323
|
});
|
|
460
324
|
});
|
|
461
|
-
// Endpoint to download the matter log
|
|
462
325
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
463
326
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
|
|
464
327
|
try {
|
|
@@ -478,7 +341,6 @@ export class Frontend {
|
|
|
478
341
|
}
|
|
479
342
|
});
|
|
480
343
|
});
|
|
481
|
-
// Endpoint to download the shelly log
|
|
482
344
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
483
345
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
484
346
|
try {
|
|
@@ -498,7 +360,6 @@ export class Frontend {
|
|
|
498
360
|
}
|
|
499
361
|
});
|
|
500
362
|
});
|
|
501
|
-
// Endpoint to download the matterbridge storage directory
|
|
502
363
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
503
364
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
504
365
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -509,7 +370,6 @@ export class Frontend {
|
|
|
509
370
|
}
|
|
510
371
|
});
|
|
511
372
|
});
|
|
512
|
-
// Endpoint to download the matter storage file
|
|
513
373
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
514
374
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
515
375
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
@@ -520,7 +380,6 @@ export class Frontend {
|
|
|
520
380
|
}
|
|
521
381
|
});
|
|
522
382
|
});
|
|
523
|
-
// Endpoint to download the matterbridge plugin directory
|
|
524
383
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
525
384
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
526
385
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -531,7 +390,6 @@ export class Frontend {
|
|
|
531
390
|
}
|
|
532
391
|
});
|
|
533
392
|
});
|
|
534
|
-
// Endpoint to download the matterbridge plugin config files
|
|
535
393
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
536
394
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
537
395
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
@@ -542,7 +400,6 @@ export class Frontend {
|
|
|
542
400
|
}
|
|
543
401
|
});
|
|
544
402
|
});
|
|
545
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
546
403
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
547
404
|
this.log.debug('The frontend sent /api/download-backup');
|
|
548
405
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -552,7 +409,6 @@ export class Frontend {
|
|
|
552
409
|
}
|
|
553
410
|
});
|
|
554
411
|
});
|
|
555
|
-
// Endpoint to upload a package
|
|
556
412
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
557
413
|
const { filename } = req.body;
|
|
558
414
|
const file = req.file;
|
|
@@ -562,13 +418,10 @@ export class Frontend {
|
|
|
562
418
|
return;
|
|
563
419
|
}
|
|
564
420
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
565
|
-
// Define the path where the plugin file will be saved
|
|
566
421
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
567
422
|
try {
|
|
568
|
-
// Move the uploaded file to the specified path
|
|
569
423
|
await fs.rename(file.path, filePath);
|
|
570
424
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
571
|
-
// Install the plugin package
|
|
572
425
|
if (filename.endsWith('.tgz')) {
|
|
573
426
|
await this.matterbridge.spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
574
427
|
this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
|
|
@@ -587,7 +440,6 @@ export class Frontend {
|
|
|
587
440
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
588
441
|
}
|
|
589
442
|
});
|
|
590
|
-
// Fallback for routing (must be the last route)
|
|
591
443
|
this.expressApp.use((req, res) => {
|
|
592
444
|
this.log.debug('The frontend sent:', req.url);
|
|
593
445
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -596,15 +448,12 @@ export class Frontend {
|
|
|
596
448
|
}
|
|
597
449
|
async stop() {
|
|
598
450
|
this.log.debug('Stopping the frontend...');
|
|
599
|
-
// Remove listeners from the express app
|
|
600
451
|
if (this.expressApp) {
|
|
601
452
|
this.expressApp.removeAllListeners();
|
|
602
453
|
this.expressApp = undefined;
|
|
603
454
|
this.log.debug('Frontend app closed successfully');
|
|
604
455
|
}
|
|
605
|
-
// Close the WebSocket server
|
|
606
456
|
if (this.webSocketServer) {
|
|
607
|
-
// Close all active connections
|
|
608
457
|
this.webSocketServer.clients.forEach((client) => {
|
|
609
458
|
if (client.readyState === WebSocket.OPEN) {
|
|
610
459
|
client.close();
|
|
@@ -624,7 +473,6 @@ export class Frontend {
|
|
|
624
473
|
this.webSocketServer.removeAllListeners();
|
|
625
474
|
this.webSocketServer = undefined;
|
|
626
475
|
}
|
|
627
|
-
// Close the http server
|
|
628
476
|
if (this.httpServer) {
|
|
629
477
|
await withTimeout(new Promise((resolve) => {
|
|
630
478
|
this.httpServer?.close((error) => {
|
|
@@ -641,7 +489,6 @@ export class Frontend {
|
|
|
641
489
|
this.httpServer = undefined;
|
|
642
490
|
this.log.debug('Frontend http server closed successfully');
|
|
643
491
|
}
|
|
644
|
-
// Close the https server
|
|
645
492
|
if (this.httpsServer) {
|
|
646
493
|
await withTimeout(new Promise((resolve) => {
|
|
647
494
|
this.httpsServer?.close((error) => {
|
|
@@ -660,7 +507,6 @@ export class Frontend {
|
|
|
660
507
|
}
|
|
661
508
|
this.log.debug('Frontend stopped successfully');
|
|
662
509
|
}
|
|
663
|
-
// Function to format bytes to KB, MB, or GB
|
|
664
510
|
formatMemoryUsage = (bytes) => {
|
|
665
511
|
if (bytes >= 1024 ** 3) {
|
|
666
512
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -672,7 +518,6 @@ export class Frontend {
|
|
|
672
518
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
673
519
|
}
|
|
674
520
|
};
|
|
675
|
-
// Function to format system uptime with only the most significant unit
|
|
676
521
|
formatOsUpTime = (seconds) => {
|
|
677
522
|
if (seconds >= 86400) {
|
|
678
523
|
const days = Math.floor(seconds / 86400);
|
|
@@ -688,14 +533,8 @@ export class Frontend {
|
|
|
688
533
|
}
|
|
689
534
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
690
535
|
};
|
|
691
|
-
/**
|
|
692
|
-
* Retrieves the api settings data.
|
|
693
|
-
*
|
|
694
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
695
|
-
*/
|
|
696
536
|
async getApiSettings() {
|
|
697
537
|
const { lastCpuUsage } = await import('./cli.js');
|
|
698
|
-
// Update the system information
|
|
699
538
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
700
539
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
701
540
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -704,7 +543,6 @@ export class Frontend {
|
|
|
704
543
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
705
544
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
706
545
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
707
|
-
// Update the matterbridge information
|
|
708
546
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
709
547
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
710
548
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -723,11 +561,6 @@ export class Frontend {
|
|
|
723
561
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
724
562
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
725
563
|
}
|
|
726
|
-
/**
|
|
727
|
-
* Retrieves the reachable attribute.
|
|
728
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
729
|
-
* @returns {boolean} The reachable attribute.
|
|
730
|
-
*/
|
|
731
564
|
getReachability(device) {
|
|
732
565
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
733
566
|
return false;
|
|
@@ -737,8 +570,8 @@ export class Frontend {
|
|
|
737
570
|
return true;
|
|
738
571
|
return false;
|
|
739
572
|
}
|
|
740
|
-
getPowerSource(
|
|
741
|
-
if (!
|
|
573
|
+
getPowerSource(endpoint) {
|
|
574
|
+
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
742
575
|
return undefined;
|
|
743
576
|
const powerSource = (device) => {
|
|
744
577
|
const featureMap = device.getAttribute(PowerSource.Cluster.id, 'featureMap');
|
|
@@ -752,20 +585,13 @@ export class Frontend {
|
|
|
752
585
|
}
|
|
753
586
|
return;
|
|
754
587
|
};
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
// Child endpoints
|
|
759
|
-
for (const child of device.getChildEndpoints()) {
|
|
588
|
+
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
589
|
+
return powerSource(endpoint);
|
|
590
|
+
for (const child of endpoint.getChildEndpoints()) {
|
|
760
591
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
761
592
|
return powerSource(child);
|
|
762
593
|
}
|
|
763
594
|
}
|
|
764
|
-
/**
|
|
765
|
-
* Retrieves the cluster text description from a given device.
|
|
766
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
767
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
768
|
-
*/
|
|
769
595
|
getClusterTextFromDevice(device) {
|
|
770
596
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
771
597
|
return '';
|
|
@@ -807,8 +633,7 @@ export class Frontend {
|
|
|
807
633
|
let attributes = '';
|
|
808
634
|
let supportedModes = [];
|
|
809
635
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
810
|
-
|
|
811
|
-
if (typeof attributeValue === 'undefined')
|
|
636
|
+
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
812
637
|
return;
|
|
813
638
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
814
639
|
attributes += `OnOff: ${attributeValue} `;
|
|
@@ -899,13 +724,8 @@ export class Frontend {
|
|
|
899
724
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
900
725
|
attributes += `${getUserLabel(device)} `;
|
|
901
726
|
});
|
|
902
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
903
727
|
return attributes.trimStart().trimEnd();
|
|
904
728
|
}
|
|
905
|
-
/**
|
|
906
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
907
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
908
|
-
*/
|
|
909
729
|
getBaseRegisteredPlugins() {
|
|
910
730
|
const baseRegisteredPlugins = [];
|
|
911
731
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -944,18 +764,11 @@ export class Frontend {
|
|
|
944
764
|
}
|
|
945
765
|
return baseRegisteredPlugins;
|
|
946
766
|
}
|
|
947
|
-
/**
|
|
948
|
-
* Retrieves the devices from Matterbridge.
|
|
949
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
950
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices.
|
|
951
|
-
*/
|
|
952
767
|
async getDevices(pluginName) {
|
|
953
768
|
const devices = [];
|
|
954
769
|
this.matterbridge.devices.forEach(async (device) => {
|
|
955
|
-
// Filter by pluginName if provided
|
|
956
770
|
if (pluginName && pluginName !== device.plugin)
|
|
957
771
|
return;
|
|
958
|
-
// Check if the device has the required properties
|
|
959
772
|
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
960
773
|
return;
|
|
961
774
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -975,13 +788,64 @@ export class Frontend {
|
|
|
975
788
|
});
|
|
976
789
|
return devices;
|
|
977
790
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
791
|
+
getClusters(pluginName, endpointNumber) {
|
|
792
|
+
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
793
|
+
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
794
|
+
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
const deviceTypes = [];
|
|
798
|
+
const clusters = [];
|
|
799
|
+
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
800
|
+
deviceTypes.push(d.deviceType);
|
|
801
|
+
});
|
|
802
|
+
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
803
|
+
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
804
|
+
return;
|
|
805
|
+
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
806
|
+
return;
|
|
807
|
+
clusters.push({
|
|
808
|
+
endpoint: endpoint.number.toString(),
|
|
809
|
+
id: 'main',
|
|
810
|
+
deviceTypes,
|
|
811
|
+
clusterName: capitalizeFirstLetter(clusterName),
|
|
812
|
+
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
813
|
+
attributeName,
|
|
814
|
+
attributeId: '0x' + attributeId.toString(16).padStart(2, '0'),
|
|
815
|
+
attributeValue: typeof attributeValue === 'object' ? stringify(attributeValue) : attributeValue.toString(),
|
|
816
|
+
attributeLocalValue: attributeValue,
|
|
817
|
+
});
|
|
818
|
+
});
|
|
819
|
+
const childEndpoints = endpoint.getChildEndpoints();
|
|
820
|
+
childEndpoints.forEach((childEndpoint) => {
|
|
821
|
+
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
822
|
+
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
const deviceTypes = [];
|
|
826
|
+
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
827
|
+
deviceTypes.push(d.deviceType);
|
|
828
|
+
});
|
|
829
|
+
childEndpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
830
|
+
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
831
|
+
return;
|
|
832
|
+
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
833
|
+
return;
|
|
834
|
+
clusters.push({
|
|
835
|
+
endpoint: childEndpoint.number.toString(),
|
|
836
|
+
id: childEndpoint.maybeId ?? 'null',
|
|
837
|
+
deviceTypes,
|
|
838
|
+
clusterName: capitalizeFirstLetter(clusterName),
|
|
839
|
+
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
840
|
+
attributeName,
|
|
841
|
+
attributeId: '0x' + attributeId.toString(16).padStart(2, '0'),
|
|
842
|
+
attributeValue: typeof attributeValue === 'object' ? stringify(attributeValue) : attributeValue.toString(),
|
|
843
|
+
attributeLocalValue: attributeValue,
|
|
844
|
+
});
|
|
845
|
+
});
|
|
846
|
+
});
|
|
847
|
+
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
|
|
848
|
+
}
|
|
985
849
|
async wsMessageHandler(client, message) {
|
|
986
850
|
let data;
|
|
987
851
|
try {
|
|
@@ -1028,10 +892,8 @@ export class Frontend {
|
|
|
1028
892
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1029
893
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1030
894
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1031
|
-
// The install comes from InstallPlugins
|
|
1032
895
|
this.matterbridge.plugins.add(packageName).then((plugin) => {
|
|
1033
896
|
if (plugin) {
|
|
1034
|
-
// The plugin is not registered
|
|
1035
897
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1036
898
|
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
1037
899
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
@@ -1039,7 +901,6 @@ export class Frontend {
|
|
|
1039
901
|
});
|
|
1040
902
|
}
|
|
1041
903
|
else {
|
|
1042
|
-
// The plugin is already registered
|
|
1043
904
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1044
905
|
this.wssSendRefreshRequired('plugins');
|
|
1045
906
|
this.wssSendRestartRequired();
|
|
@@ -1047,7 +908,6 @@ export class Frontend {
|
|
|
1047
908
|
});
|
|
1048
909
|
}
|
|
1049
910
|
else {
|
|
1050
|
-
// The package is matterbridge
|
|
1051
911
|
if (this.matterbridge.restartMode !== '') {
|
|
1052
912
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1053
913
|
this.matterbridge.shutdownProcess();
|
|
@@ -1069,7 +929,6 @@ export class Frontend {
|
|
|
1069
929
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1070
930
|
return;
|
|
1071
931
|
}
|
|
1072
|
-
// The package is a plugin
|
|
1073
932
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1074
933
|
if (plugin) {
|
|
1075
934
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
@@ -1078,7 +937,6 @@ export class Frontend {
|
|
|
1078
937
|
this.wssSendRefreshRequired('plugins');
|
|
1079
938
|
this.wssSendRefreshRequired('devices');
|
|
1080
939
|
}
|
|
1081
|
-
// Uninstall the package
|
|
1082
940
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1083
941
|
this.matterbridge
|
|
1084
942
|
.spawnCommand('npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1300,103 +1158,24 @@ export class Frontend {
|
|
|
1300
1158
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter endpoint in /api/clusters' }));
|
|
1301
1159
|
return;
|
|
1302
1160
|
}
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
return;
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
if (clusterServer.name === 'Descriptor' && key === 'deviceTypeList') {
|
|
1325
|
-
value.getLocal().forEach((deviceType) => {
|
|
1326
|
-
deviceTypes.push(deviceType.deviceType);
|
|
1327
|
-
});
|
|
1328
|
-
}
|
|
1329
|
-
let attributeValue;
|
|
1330
|
-
let attributeLocalValue;
|
|
1331
|
-
try {
|
|
1332
|
-
if (typeof value.getLocal() === 'object')
|
|
1333
|
-
attributeValue = stringify(value.getLocal());
|
|
1334
|
-
else
|
|
1335
|
-
attributeValue = value.getLocal().toString();
|
|
1336
|
-
attributeLocalValue = value.getLocal();
|
|
1337
|
-
}
|
|
1338
|
-
catch (error) {
|
|
1339
|
-
attributeValue = 'Fabric-Scoped';
|
|
1340
|
-
attributeLocalValue = 'Fabric-Scoped';
|
|
1341
|
-
this.log.debug(`GetLocal value ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
|
|
1342
|
-
}
|
|
1343
|
-
clusters.push({
|
|
1344
|
-
endpoint: device.number ? device.number.toString() : '...',
|
|
1345
|
-
id: 'main',
|
|
1346
|
-
deviceTypes,
|
|
1347
|
-
clusterName: clusterServer.name,
|
|
1348
|
-
clusterId: '0x' + clusterServer.id.toString(16).padStart(2, '0'),
|
|
1349
|
-
attributeName: key,
|
|
1350
|
-
attributeId: '0x' + value.id.toString(16).padStart(2, '0'),
|
|
1351
|
-
attributeValue,
|
|
1352
|
-
attributeLocalValue,
|
|
1353
|
-
});
|
|
1354
|
-
});
|
|
1355
|
-
});
|
|
1356
|
-
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
1357
|
-
deviceTypes = [];
|
|
1358
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1359
|
-
const name = childEndpoint.endpoint?.id;
|
|
1360
|
-
const clusterServers = childEndpoint.getAllClusterServers();
|
|
1361
|
-
clusterServers.forEach((clusterServer) => {
|
|
1362
|
-
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
1363
|
-
if (clusterServer.name === 'EveHistory')
|
|
1364
|
-
return;
|
|
1365
|
-
if (clusterServer.name === 'Descriptor' && key === 'deviceTypeList') {
|
|
1366
|
-
value.getLocal().forEach((deviceType) => {
|
|
1367
|
-
deviceTypes.push(deviceType.deviceType);
|
|
1368
|
-
});
|
|
1369
|
-
}
|
|
1370
|
-
let attributeValue;
|
|
1371
|
-
let attributeLocalValue;
|
|
1372
|
-
try {
|
|
1373
|
-
if (typeof value.getLocal() === 'object')
|
|
1374
|
-
attributeValue = stringify(value.getLocal());
|
|
1375
|
-
else
|
|
1376
|
-
attributeValue = value.getLocal().toString();
|
|
1377
|
-
attributeLocalValue = value.getLocal();
|
|
1378
|
-
}
|
|
1379
|
-
catch (error) {
|
|
1380
|
-
attributeValue = 'Fabric-Scoped';
|
|
1381
|
-
attributeLocalValue = 'Fabric-Scoped';
|
|
1382
|
-
this.log.debug(`GetLocal error ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
|
|
1383
|
-
}
|
|
1384
|
-
clusters.push({
|
|
1385
|
-
endpoint: childEndpoint.number ? childEndpoint.number.toString() : '...',
|
|
1386
|
-
id: name,
|
|
1387
|
-
deviceTypes,
|
|
1388
|
-
clusterName: clusterServer.name,
|
|
1389
|
-
clusterId: '0x' + clusterServer.id.toString(16).padStart(2, '0'),
|
|
1390
|
-
attributeName: key,
|
|
1391
|
-
attributeId: '0x' + value.id.toString(16).padStart(2, '0'),
|
|
1392
|
-
attributeValue,
|
|
1393
|
-
attributeLocalValue,
|
|
1394
|
-
});
|
|
1395
|
-
});
|
|
1396
|
-
});
|
|
1397
|
-
});
|
|
1398
|
-
});
|
|
1399
|
-
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, deviceName, serialNumber, endpoint: data.params.endpoint, deviceTypes, response: clusters }));
|
|
1161
|
+
const response = this.getClusters(data.params.plugin, data.params.endpoint);
|
|
1162
|
+
if (response) {
|
|
1163
|
+
client.send(JSON.stringify({
|
|
1164
|
+
id: data.id,
|
|
1165
|
+
method: data.method,
|
|
1166
|
+
src: 'Matterbridge',
|
|
1167
|
+
dst: data.src,
|
|
1168
|
+
plugin: data.params.plugin,
|
|
1169
|
+
deviceName: response.deviceName,
|
|
1170
|
+
serialNumber: response.serialNumber,
|
|
1171
|
+
endpoint: response.endpoint,
|
|
1172
|
+
deviceTypes: response.deviceTypes,
|
|
1173
|
+
response: response.clusters,
|
|
1174
|
+
}));
|
|
1175
|
+
}
|
|
1176
|
+
else {
|
|
1177
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Endpoint not found in /api/clusters' }));
|
|
1178
|
+
}
|
|
1400
1179
|
}
|
|
1401
1180
|
else if (data.method === '/api/select' || data.method === '/api/select/devices') {
|
|
1402
1181
|
if (!isValidString(data.params.plugin, 10)) {
|
|
@@ -1469,22 +1248,22 @@ export class Frontend {
|
|
|
1469
1248
|
if (isValidString(data.params.value, 4)) {
|
|
1470
1249
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1471
1250
|
if (data.params.value === 'Debug') {
|
|
1472
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1251
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1473
1252
|
}
|
|
1474
1253
|
else if (data.params.value === 'Info') {
|
|
1475
|
-
await this.matterbridge.setLogLevel("info"
|
|
1254
|
+
await this.matterbridge.setLogLevel("info");
|
|
1476
1255
|
}
|
|
1477
1256
|
else if (data.params.value === 'Notice') {
|
|
1478
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1257
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1479
1258
|
}
|
|
1480
1259
|
else if (data.params.value === 'Warn') {
|
|
1481
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1260
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1482
1261
|
}
|
|
1483
1262
|
else if (data.params.value === 'Error') {
|
|
1484
|
-
await this.matterbridge.setLogLevel("error"
|
|
1263
|
+
await this.matterbridge.setLogLevel("error");
|
|
1485
1264
|
}
|
|
1486
1265
|
else if (data.params.value === 'Fatal') {
|
|
1487
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1266
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1488
1267
|
}
|
|
1489
1268
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1490
1269
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1495,7 +1274,6 @@ export class Frontend {
|
|
|
1495
1274
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1496
1275
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1497
1276
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1498
|
-
// Create the file logger for matterbridge
|
|
1499
1277
|
if (data.params.value)
|
|
1500
1278
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1501
1279
|
else
|
|
@@ -1660,19 +1438,15 @@ export class Frontend {
|
|
|
1660
1438
|
return;
|
|
1661
1439
|
}
|
|
1662
1440
|
const config = plugin.configJson;
|
|
1663
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1664
1441
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1665
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1666
1442
|
if (select === 'serial')
|
|
1667
1443
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1668
1444
|
if (select === 'name')
|
|
1669
1445
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1670
1446
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1671
|
-
// Remove postfix from the serial if it exists
|
|
1672
1447
|
if (config.postfix) {
|
|
1673
1448
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1674
1449
|
}
|
|
1675
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1676
1450
|
if (isValidArray(config.whiteList, 1)) {
|
|
1677
1451
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1678
1452
|
config.whiteList.push(data.params.serial);
|
|
@@ -1681,7 +1455,6 @@ export class Frontend {
|
|
|
1681
1455
|
config.whiteList.push(data.params.name);
|
|
1682
1456
|
}
|
|
1683
1457
|
}
|
|
1684
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1685
1458
|
if (isValidArray(config.blackList, 1)) {
|
|
1686
1459
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1687
1460
|
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
@@ -1711,9 +1484,7 @@ export class Frontend {
|
|
|
1711
1484
|
return;
|
|
1712
1485
|
}
|
|
1713
1486
|
const config = plugin.configJson;
|
|
1714
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1715
1487
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1716
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1717
1488
|
if (select === 'serial')
|
|
1718
1489
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1719
1490
|
if (select === 'name')
|
|
@@ -1722,7 +1493,6 @@ export class Frontend {
|
|
|
1722
1493
|
if (config.postfix) {
|
|
1723
1494
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1724
1495
|
}
|
|
1725
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1726
1496
|
if (isValidArray(config.whiteList, 1)) {
|
|
1727
1497
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1728
1498
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
@@ -1731,7 +1501,6 @@ export class Frontend {
|
|
|
1731
1501
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1732
1502
|
}
|
|
1733
1503
|
}
|
|
1734
|
-
// Add the serial to the blackList
|
|
1735
1504
|
if (isValidArray(config.blackList)) {
|
|
1736
1505
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1737
1506
|
config.blackList.push(data.params.serial);
|
|
@@ -1764,219 +1533,114 @@ export class Frontend {
|
|
|
1764
1533
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1765
1534
|
}
|
|
1766
1535
|
}
|
|
1767
|
-
/**
|
|
1768
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1769
|
-
*
|
|
1770
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1771
|
-
* @param {string} time - The time string of the message
|
|
1772
|
-
* @param {string} name - The logger name of the message
|
|
1773
|
-
* @param {string} message - The content of the message.
|
|
1774
|
-
*
|
|
1775
|
-
* @remark
|
|
1776
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
1777
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
1778
|
-
* The function sends the message to all connected clients.
|
|
1779
|
-
*/
|
|
1780
1536
|
wssSendMessage(level, time, name, message) {
|
|
1781
1537
|
if (!level || !time || !name || !message)
|
|
1782
1538
|
return;
|
|
1783
|
-
// Remove ANSI escape codes from the message
|
|
1784
|
-
// eslint-disable-next-line no-control-regex
|
|
1785
1539
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1786
|
-
// Remove leading asterisks from the message
|
|
1787
1540
|
message = message.replace(/^\*+/, '');
|
|
1788
|
-
// Replace all occurrences of \t and \n
|
|
1789
1541
|
message = message.replace(/[\t\n]/g, '');
|
|
1790
|
-
// Remove non-printable characters
|
|
1791
|
-
// eslint-disable-next-line no-control-regex
|
|
1792
1542
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1793
|
-
// Replace all occurrences of \" with "
|
|
1794
1543
|
message = message.replace(/\\"/g, '"');
|
|
1795
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1796
1544
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1797
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1798
1545
|
const maxContinuousLength = 100;
|
|
1799
1546
|
const keepStartLength = 20;
|
|
1800
1547
|
const keepEndLength = 20;
|
|
1801
|
-
// Split the message into words
|
|
1802
1548
|
message = message
|
|
1803
1549
|
.split(' ')
|
|
1804
1550
|
.map((word) => {
|
|
1805
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1806
1551
|
if (word.length > maxContinuousLength) {
|
|
1807
1552
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1808
1553
|
}
|
|
1809
1554
|
return word;
|
|
1810
1555
|
})
|
|
1811
1556
|
.join(' ');
|
|
1812
|
-
// Send the message to all connected clients
|
|
1813
1557
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1814
1558
|
if (client.readyState === WebSocket.OPEN) {
|
|
1815
1559
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1816
1560
|
}
|
|
1817
1561
|
});
|
|
1818
1562
|
}
|
|
1819
|
-
/**
|
|
1820
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1821
|
-
*
|
|
1822
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
1823
|
-
* possible values:
|
|
1824
|
-
* - 'matterbridgeLatestVersion'
|
|
1825
|
-
* - 'matterbridgeAdvertise'
|
|
1826
|
-
* - 'online'
|
|
1827
|
-
* - 'offline'
|
|
1828
|
-
* - 'reachability'
|
|
1829
|
-
* - 'settings'
|
|
1830
|
-
* - 'plugins'
|
|
1831
|
-
* - 'pluginsRestart'
|
|
1832
|
-
* - 'devices'
|
|
1833
|
-
* - 'fabrics'
|
|
1834
|
-
* - 'sessions'
|
|
1835
|
-
*/
|
|
1836
1563
|
wssSendRefreshRequired(changed = null) {
|
|
1837
1564
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1838
|
-
// Send the message to all connected clients
|
|
1839
1565
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1840
1566
|
if (client.readyState === WebSocket.OPEN) {
|
|
1841
1567
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1842
1568
|
}
|
|
1843
1569
|
});
|
|
1844
1570
|
}
|
|
1845
|
-
/**
|
|
1846
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1847
|
-
*
|
|
1848
|
-
*/
|
|
1849
1571
|
wssSendRestartRequired(snackbar = true) {
|
|
1850
1572
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1851
1573
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1852
1574
|
if (snackbar === true)
|
|
1853
1575
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1854
|
-
// Send the message to all connected clients
|
|
1855
1576
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1856
1577
|
if (client.readyState === WebSocket.OPEN) {
|
|
1857
1578
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1858
1579
|
}
|
|
1859
1580
|
});
|
|
1860
1581
|
}
|
|
1861
|
-
/**
|
|
1862
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
1863
|
-
*
|
|
1864
|
-
*/
|
|
1865
1582
|
wssSendUpdateRequired() {
|
|
1866
1583
|
this.log.debug('Sending an update required message to all connected clients');
|
|
1867
1584
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
1868
|
-
// Send the message to all connected clients
|
|
1869
1585
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1870
1586
|
if (client.readyState === WebSocket.OPEN) {
|
|
1871
1587
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
1872
1588
|
}
|
|
1873
1589
|
});
|
|
1874
1590
|
}
|
|
1875
|
-
/**
|
|
1876
|
-
* Sends a cpu update message to all connected clients.
|
|
1877
|
-
*
|
|
1878
|
-
*/
|
|
1879
1591
|
wssSendCpuUpdate(cpuUsage) {
|
|
1880
1592
|
if (hasParameter('debug'))
|
|
1881
1593
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1882
|
-
// Send the message to all connected clients
|
|
1883
1594
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1884
1595
|
if (client.readyState === WebSocket.OPEN) {
|
|
1885
1596
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
1886
1597
|
}
|
|
1887
1598
|
});
|
|
1888
1599
|
}
|
|
1889
|
-
/**
|
|
1890
|
-
* Sends a memory update message to all connected clients.
|
|
1891
|
-
*
|
|
1892
|
-
*/
|
|
1893
1600
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
1894
1601
|
if (hasParameter('debug'))
|
|
1895
1602
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
1896
|
-
// Send the message to all connected clients
|
|
1897
1603
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1898
1604
|
if (client.readyState === WebSocket.OPEN) {
|
|
1899
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 } }));
|
|
1900
1606
|
}
|
|
1901
1607
|
});
|
|
1902
1608
|
}
|
|
1903
|
-
/**
|
|
1904
|
-
* Sends an uptime update message to all connected clients.
|
|
1905
|
-
*
|
|
1906
|
-
*/
|
|
1907
1609
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
1908
1610
|
if (hasParameter('debug'))
|
|
1909
1611
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
1910
|
-
// Send the message to all connected clients
|
|
1911
1612
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1912
1613
|
if (client.readyState === WebSocket.OPEN) {
|
|
1913
1614
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
1914
1615
|
}
|
|
1915
1616
|
});
|
|
1916
1617
|
}
|
|
1917
|
-
/**
|
|
1918
|
-
* Sends an open snackbar message to all connected clients.
|
|
1919
|
-
* @param {string} message - The message to send.
|
|
1920
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
1921
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
1922
|
-
*
|
|
1923
|
-
*/
|
|
1924
1618
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
1925
1619
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
1926
|
-
// Send the message to all connected clients
|
|
1927
1620
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1928
1621
|
if (client.readyState === WebSocket.OPEN) {
|
|
1929
1622
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
1930
1623
|
}
|
|
1931
1624
|
});
|
|
1932
1625
|
}
|
|
1933
|
-
/**
|
|
1934
|
-
* Sends a close snackbar message to all connected clients.
|
|
1935
|
-
* @param {string} message - The message to send.
|
|
1936
|
-
*
|
|
1937
|
-
*/
|
|
1938
1626
|
wssSendCloseSnackbarMessage(message) {
|
|
1939
1627
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
1940
|
-
// Send the message to all connected clients
|
|
1941
1628
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1942
1629
|
if (client.readyState === WebSocket.OPEN) {
|
|
1943
1630
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
1944
1631
|
}
|
|
1945
1632
|
});
|
|
1946
1633
|
}
|
|
1947
|
-
/**
|
|
1948
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
1949
|
-
*
|
|
1950
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
1951
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
1952
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
1953
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
1954
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
1955
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
1956
|
-
*
|
|
1957
|
-
* @remarks
|
|
1958
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
1959
|
-
* with the updated attribute information.
|
|
1960
|
-
*/
|
|
1961
1634
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
1962
1635
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
1963
|
-
// Send the message to all connected clients
|
|
1964
1636
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1965
1637
|
if (client.readyState === WebSocket.OPEN) {
|
|
1966
1638
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
1967
1639
|
}
|
|
1968
1640
|
});
|
|
1969
1641
|
}
|
|
1970
|
-
/**
|
|
1971
|
-
* Sends a message to all connected clients.
|
|
1972
|
-
* @param {number} id - The message id.
|
|
1973
|
-
* @param {string} method - The message method.
|
|
1974
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
1975
|
-
*
|
|
1976
|
-
*/
|
|
1977
1642
|
wssBroadcastMessage(id, method, params) {
|
|
1978
1643
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
1979
|
-
// Send the message to all connected clients
|
|
1980
1644
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1981
1645
|
if (client.readyState === WebSocket.OPEN) {
|
|
1982
1646
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -1984,4 +1648,3 @@ export class Frontend {
|
|
|
1984
1648
|
});
|
|
1985
1649
|
}
|
|
1986
1650
|
}
|
|
1987
|
-
//# sourceMappingURL=frontend.js.map
|