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