matterbridge 3.1.0 → 3.1.1-dev-20250629-f14b886
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 +17 -4
- package/README-DEV.md +14 -2
- package/dist/cli.js +2 -91
- package/dist/clusters/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -24
- package/dist/deviceManager.js +1 -94
- package/dist/devices/export.js +0 -2
- package/dist/evse.js +9 -70
- package/dist/frontend.js +16 -413
- package/dist/globalMatterbridge.js +0 -47
- package/dist/helpers.js +0 -53
- package/dist/index.js +1 -32
- 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 -3
- package/dist/matter/types.js +0 -3
- package/dist/matterbridge.js +50 -797
- package/dist/matterbridgeAccessoryPlatform.js +0 -36
- package/dist/matterbridgeBehaviors.js +1 -55
- package/dist/matterbridgeDeviceTypes.js +15 -579
- package/dist/matterbridgeDynamicPlatform.js +0 -36
- package/dist/matterbridgeEndpoint.js +40 -1022
- package/dist/matterbridgeEndpointHelpers.js +12 -322
- package/dist/matterbridgePlatform.js +0 -233
- package/dist/matterbridgeTypes.js +0 -25
- package/dist/pluginManager.js +3 -269
- package/dist/roboticVacuumCleaner.js +6 -83
- package/dist/shelly.js +7 -168
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -54
- package/dist/utils/colorUtils.js +2 -263
- package/dist/utils/commandLine.js +0 -54
- package/dist/utils/copyDirectory.js +1 -38
- package/dist/utils/createDirectory.js +0 -33
- package/dist/utils/createZip.js +2 -47
- package/dist/utils/deepCopy.js +0 -39
- package/dist/utils/deepEqual.js +1 -72
- package/dist/utils/export.js +0 -1
- package/dist/utils/hex.js +0 -58
- package/dist/utils/isvalid.js +0 -101
- package/dist/utils/network.js +5 -83
- package/dist/utils/spawn.js +0 -18
- package/dist/utils/wait.js +9 -62
- package/dist/waterHeater.js +2 -77
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- package/dist/cli.d.ts +0 -29
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/clusters/export.d.ts +0 -2
- package/dist/clusters/export.d.ts.map +0 -1
- package/dist/clusters/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -28
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -112
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/devices/export.d.ts +0 -5
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.js.map +0 -1
- package/dist/evse.d.ts +0 -72
- package/dist/evse.d.ts.map +0 -1
- package/dist/evse.js.map +0 -1
- package/dist/frontend.d.ts +0 -285
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/globalMatterbridge.d.ts +0 -59
- package/dist/globalMatterbridge.d.ts.map +0 -1
- package/dist/globalMatterbridge.js.map +0 -1
- package/dist/helpers.d.ts +0 -48
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.js.map +0 -1
- package/dist/index.d.ts +0 -38
- 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 -450
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1334
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -709
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -1173
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -3198
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -310
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -184
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -291
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/roboticVacuumCleaner.d.ts +0 -104
- package/dist/roboticVacuumCleaner.d.ts.map +0 -1
- package/dist/roboticVacuumCleaner.js.map +0 -1
- package/dist/shelly.d.ts +0 -174
- 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 -59
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -117
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/commandLine.d.ts +0 -59
- package/dist/utils/commandLine.d.ts.map +0 -1
- package/dist/utils/commandLine.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -33
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createDirectory.d.ts +0 -34
- package/dist/utils/createDirectory.d.ts.map +0 -1
- package/dist/utils/createDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -39
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -32
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -54
- 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 -49
- package/dist/utils/hex.d.ts.map +0 -1
- package/dist/utils/hex.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -103
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/network.d.ts +0 -76
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/spawn.d.ts +0 -14
- package/dist/utils/spawn.d.ts.map +0 -1
- package/dist/utils/spawn.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -56
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
- package/dist/waterHeater.d.ts +0 -106
- package/dist/waterHeater.d.ts.map +0 -1
- package/dist/waterHeater.js.map +0 -1
package/dist/frontend.js
CHANGED
|
@@ -1,125 +1,29 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Frontend.
|
|
3
|
-
*
|
|
4
|
-
* @file frontend.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @created 2025-01-13
|
|
7
|
-
* @version 1.0.2
|
|
8
|
-
* @license Apache-2.0
|
|
9
|
-
*
|
|
10
|
-
* Copyright 2025, 2026, 2027 Luca Liguori.
|
|
11
|
-
*
|
|
12
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
-
* you may not use this file except in compliance with the License.
|
|
14
|
-
* You may obtain a copy of the License at
|
|
15
|
-
*
|
|
16
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
-
*
|
|
18
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
19
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
-
* See the License for the specific language governing permissions and
|
|
22
|
-
* limitations under the License.
|
|
23
|
-
*/
|
|
24
|
-
// Node modules
|
|
25
1
|
import { createServer } from 'node:http';
|
|
26
2
|
import https from 'node:https';
|
|
27
3
|
import os from 'node:os';
|
|
28
4
|
import path from 'node:path';
|
|
29
5
|
import { promises as fs } from 'node:fs';
|
|
30
|
-
// Third-party modules
|
|
31
6
|
import express from 'express';
|
|
32
7
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
33
8
|
import multer from 'multer';
|
|
34
|
-
// AnsiLogger module
|
|
35
9
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from 'node-ansi-logger';
|
|
36
|
-
// @matter
|
|
37
10
|
import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
|
|
38
11
|
import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
|
|
39
|
-
// Matterbridge
|
|
40
12
|
import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout, hasParameter } from './utils/export.js';
|
|
41
13
|
import { plg } from './matterbridgeTypes.js';
|
|
42
14
|
import { capitalizeFirstLetter } from './matterbridgeEndpointHelpers.js';
|
|
43
15
|
import spawn from './utils/spawn.js';
|
|
44
|
-
/**
|
|
45
|
-
* Websocket message ID for logging.
|
|
46
|
-
*
|
|
47
|
-
* @constant {number}
|
|
48
|
-
*/
|
|
49
16
|
export const WS_ID_LOG = 0;
|
|
50
|
-
/**
|
|
51
|
-
* Websocket message ID indicating a refresh is needed.
|
|
52
|
-
*
|
|
53
|
-
* @constant {number}
|
|
54
|
-
*/
|
|
55
17
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
56
|
-
/**
|
|
57
|
-
* Websocket message ID indicating a restart is needed.
|
|
58
|
-
*
|
|
59
|
-
* @constant {number}
|
|
60
|
-
*/
|
|
61
18
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
62
|
-
/**
|
|
63
|
-
* Websocket message ID indicating a cpu update.
|
|
64
|
-
*
|
|
65
|
-
* @constant {number}
|
|
66
|
-
*/
|
|
67
19
|
export const WS_ID_CPU_UPDATE = 3;
|
|
68
|
-
/**
|
|
69
|
-
* Websocket message ID indicating a memory update.
|
|
70
|
-
*
|
|
71
|
-
* @constant {number}
|
|
72
|
-
*/
|
|
73
20
|
export const WS_ID_MEMORY_UPDATE = 4;
|
|
74
|
-
/**
|
|
75
|
-
* Websocket message ID indicating an uptime update.
|
|
76
|
-
*
|
|
77
|
-
* @constant {number}
|
|
78
|
-
*/
|
|
79
21
|
export const WS_ID_UPTIME_UPDATE = 5;
|
|
80
|
-
/**
|
|
81
|
-
* Websocket message ID indicating a snackbar message.
|
|
82
|
-
*
|
|
83
|
-
* @constant {number}
|
|
84
|
-
*/
|
|
85
22
|
export const WS_ID_SNACKBAR = 6;
|
|
86
|
-
/**
|
|
87
|
-
* Websocket message ID indicating matterbridge has un update available.
|
|
88
|
-
*
|
|
89
|
-
* @constant {number}
|
|
90
|
-
*/
|
|
91
23
|
export const WS_ID_UPDATE_NEEDED = 7;
|
|
92
|
-
/**
|
|
93
|
-
* Websocket message ID indicating a state update.
|
|
94
|
-
*
|
|
95
|
-
* @constant {number}
|
|
96
|
-
*/
|
|
97
24
|
export const WS_ID_STATEUPDATE = 8;
|
|
98
|
-
/**
|
|
99
|
-
* Websocket message ID indicating to close a permanent snackbar message.
|
|
100
|
-
*
|
|
101
|
-
* @constant {number}
|
|
102
|
-
*/
|
|
103
25
|
export const WS_ID_CLOSE_SNACKBAR = 9;
|
|
104
|
-
/**
|
|
105
|
-
* Websocket message ID indicating a shelly system update.
|
|
106
|
-
* check:
|
|
107
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/check
|
|
108
|
-
* perform:
|
|
109
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/perform
|
|
110
|
-
*
|
|
111
|
-
* @constant {number}
|
|
112
|
-
*/
|
|
113
26
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
114
|
-
/**
|
|
115
|
-
* Websocket message ID indicating a shelly main update.
|
|
116
|
-
* check:
|
|
117
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/check
|
|
118
|
-
* perform:
|
|
119
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/perform
|
|
120
|
-
*
|
|
121
|
-
* @constant {number}
|
|
122
|
-
*/
|
|
123
27
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
124
28
|
export class Frontend {
|
|
125
29
|
matterbridge;
|
|
@@ -132,7 +36,7 @@ export class Frontend {
|
|
|
132
36
|
webSocketServer;
|
|
133
37
|
constructor(matterbridge) {
|
|
134
38
|
this.matterbridge = matterbridge;
|
|
135
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
39
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
136
40
|
}
|
|
137
41
|
set logLevel(logLevel) {
|
|
138
42
|
this.log.logLevel = logLevel;
|
|
@@ -140,43 +44,13 @@ export class Frontend {
|
|
|
140
44
|
async start(port = 8283) {
|
|
141
45
|
this.port = port;
|
|
142
46
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
143
|
-
// Initialize multer with the upload directory
|
|
144
47
|
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
145
48
|
await fs.mkdir(uploadDir, { recursive: true });
|
|
146
49
|
const upload = multer({ dest: uploadDir });
|
|
147
|
-
// Create the express app that serves the frontend
|
|
148
50
|
this.expressApp = express();
|
|
149
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
150
|
-
/*
|
|
151
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
152
|
-
for (const method of methods) {
|
|
153
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
154
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
155
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
157
|
-
try {
|
|
158
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
159
|
-
return original(path, ...rest);
|
|
160
|
-
} catch (err) {
|
|
161
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
162
|
-
throw err;
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
*/
|
|
167
|
-
// Log all requests to the server for debugging
|
|
168
|
-
/*
|
|
169
|
-
this.expressApp.use((req, res, next) => {
|
|
170
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
171
|
-
next();
|
|
172
|
-
});
|
|
173
|
-
*/
|
|
174
|
-
// Serve static files from '/static' endpoint
|
|
175
51
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
176
52
|
if (!hasParameter('ssl')) {
|
|
177
|
-
// Create an HTTP server and attach the express app
|
|
178
53
|
this.httpServer = createServer(this.expressApp);
|
|
179
|
-
// Listen on the specified port
|
|
180
54
|
if (hasParameter('ingress')) {
|
|
181
55
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
182
56
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -190,7 +64,6 @@ export class Frontend {
|
|
|
190
64
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
191
65
|
});
|
|
192
66
|
}
|
|
193
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
194
67
|
this.httpServer.on('error', (error) => {
|
|
195
68
|
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
196
69
|
switch (error.code) {
|
|
@@ -206,7 +79,6 @@ export class Frontend {
|
|
|
206
79
|
});
|
|
207
80
|
}
|
|
208
81
|
else {
|
|
209
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
210
82
|
let cert;
|
|
211
83
|
try {
|
|
212
84
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -234,9 +106,7 @@ export class Frontend {
|
|
|
234
106
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
235
107
|
}
|
|
236
108
|
const serverOptions = { cert, key, ca };
|
|
237
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
238
109
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
239
|
-
// Listen on the specified port
|
|
240
110
|
if (hasParameter('ingress')) {
|
|
241
111
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
242
112
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -250,7 +120,6 @@ export class Frontend {
|
|
|
250
120
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
251
121
|
});
|
|
252
122
|
}
|
|
253
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
254
123
|
this.httpsServer.on('error', (error) => {
|
|
255
124
|
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
256
125
|
switch (error.code) {
|
|
@@ -267,18 +136,16 @@ export class Frontend {
|
|
|
267
136
|
}
|
|
268
137
|
if (this.initializeError)
|
|
269
138
|
return;
|
|
270
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
271
139
|
const wssPort = this.port;
|
|
272
140
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
273
141
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
274
142
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
275
143
|
const clientIp = request.socket.remoteAddress;
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
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";
|
|
282
149
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
283
150
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
284
151
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -312,7 +179,6 @@ export class Frontend {
|
|
|
312
179
|
this.webSocketServer.on('error', (ws, error) => {
|
|
313
180
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
314
181
|
});
|
|
315
|
-
// Subscribe to cli events
|
|
316
182
|
const { cliEmitter } = await import('./cli.js');
|
|
317
183
|
cliEmitter.removeAllListeners();
|
|
318
184
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
@@ -324,8 +190,6 @@ export class Frontend {
|
|
|
324
190
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
325
191
|
this.wssSendCpuUpdate(cpuUsage);
|
|
326
192
|
});
|
|
327
|
-
// Endpoint to validate login code
|
|
328
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
329
193
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
330
194
|
const { password } = req.body;
|
|
331
195
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -344,27 +208,23 @@ export class Frontend {
|
|
|
344
208
|
this.log.warn('/api/login error wrong password');
|
|
345
209
|
res.json({ valid: false });
|
|
346
210
|
}
|
|
347
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
348
211
|
}
|
|
349
212
|
catch (error) {
|
|
350
213
|
this.log.error('/api/login error getting password');
|
|
351
214
|
res.json({ valid: false });
|
|
352
215
|
}
|
|
353
216
|
});
|
|
354
|
-
// Endpoint to provide health check for docker
|
|
355
217
|
this.expressApp.get('/health', (req, res) => {
|
|
356
218
|
this.log.debug('Express received /health');
|
|
357
219
|
const healthStatus = {
|
|
358
|
-
status: 'ok',
|
|
359
|
-
uptime: process.uptime(),
|
|
360
|
-
timestamp: new Date().toISOString(),
|
|
220
|
+
status: 'ok',
|
|
221
|
+
uptime: process.uptime(),
|
|
222
|
+
timestamp: new Date().toISOString(),
|
|
361
223
|
};
|
|
362
224
|
res.status(200).json(healthStatus);
|
|
363
225
|
});
|
|
364
|
-
// Endpoint to provide memory usage details
|
|
365
226
|
this.expressApp.get('/memory', async (req, res) => {
|
|
366
227
|
this.log.debug('Express received /memory');
|
|
367
|
-
// Memory usage from process
|
|
368
228
|
const memoryUsageRaw = process.memoryUsage();
|
|
369
229
|
const memoryUsage = {
|
|
370
230
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -373,13 +233,10 @@ export class Frontend {
|
|
|
373
233
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
374
234
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
375
235
|
};
|
|
376
|
-
// V8 heap statistics
|
|
377
236
|
const { default: v8 } = await import('node:v8');
|
|
378
237
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
379
238
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
380
|
-
// Format heapStats
|
|
381
239
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
382
|
-
// Format heapSpaces
|
|
383
240
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
384
241
|
...space,
|
|
385
242
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -397,23 +254,19 @@ export class Frontend {
|
|
|
397
254
|
};
|
|
398
255
|
res.status(200).json(memoryReport);
|
|
399
256
|
});
|
|
400
|
-
// Endpoint to provide settings
|
|
401
257
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
402
258
|
this.log.debug('The frontend sent /api/settings');
|
|
403
259
|
res.json(await this.getApiSettings());
|
|
404
260
|
});
|
|
405
|
-
// Endpoint to provide plugins
|
|
406
261
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
407
262
|
this.log.debug('The frontend sent /api/plugins');
|
|
408
263
|
res.json(this.getBaseRegisteredPlugins());
|
|
409
264
|
});
|
|
410
|
-
// Endpoint to provide devices
|
|
411
265
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
412
266
|
this.log.debug('The frontend sent /api/devices');
|
|
413
267
|
const devices = await this.getDevices();
|
|
414
268
|
res.json(devices);
|
|
415
269
|
});
|
|
416
|
-
// Endpoint to view the matterbridge log
|
|
417
270
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
418
271
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
419
272
|
try {
|
|
@@ -426,7 +279,6 @@ export class Frontend {
|
|
|
426
279
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
427
280
|
}
|
|
428
281
|
});
|
|
429
|
-
// Endpoint to view the matter.js log
|
|
430
282
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
431
283
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
432
284
|
try {
|
|
@@ -439,7 +291,6 @@ export class Frontend {
|
|
|
439
291
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
440
292
|
}
|
|
441
293
|
});
|
|
442
|
-
// Endpoint to view the shelly log
|
|
443
294
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
444
295
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
445
296
|
try {
|
|
@@ -452,11 +303,9 @@ export class Frontend {
|
|
|
452
303
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
453
304
|
}
|
|
454
305
|
});
|
|
455
|
-
// Endpoint to download the matterbridge log
|
|
456
306
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
457
307
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
|
|
458
308
|
try {
|
|
459
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
460
309
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
|
|
461
310
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'utf8');
|
|
462
311
|
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), data, 'utf-8');
|
|
@@ -466,7 +315,6 @@ export class Frontend {
|
|
|
466
315
|
this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
|
|
467
316
|
}
|
|
468
317
|
res.type('text/plain');
|
|
469
|
-
// res.download(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
|
|
470
318
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
|
|
471
319
|
if (error) {
|
|
472
320
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
@@ -474,11 +322,9 @@ export class Frontend {
|
|
|
474
322
|
}
|
|
475
323
|
});
|
|
476
324
|
});
|
|
477
|
-
// Endpoint to download the matter log
|
|
478
325
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
479
326
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
|
|
480
327
|
try {
|
|
481
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
482
328
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
483
329
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'utf8');
|
|
484
330
|
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), data, 'utf-8');
|
|
@@ -495,11 +341,9 @@ export class Frontend {
|
|
|
495
341
|
}
|
|
496
342
|
});
|
|
497
343
|
});
|
|
498
|
-
// Endpoint to download the shelly log
|
|
499
344
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
500
345
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
501
346
|
try {
|
|
502
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
503
347
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
|
|
504
348
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
|
|
505
349
|
await fs.writeFile(path.join(os.tmpdir(), 'shelly.log'), data, 'utf-8');
|
|
@@ -516,7 +360,6 @@ export class Frontend {
|
|
|
516
360
|
}
|
|
517
361
|
});
|
|
518
362
|
});
|
|
519
|
-
// Endpoint to download the matterbridge storage directory
|
|
520
363
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
521
364
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
522
365
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -527,7 +370,6 @@ export class Frontend {
|
|
|
527
370
|
}
|
|
528
371
|
});
|
|
529
372
|
});
|
|
530
|
-
// Endpoint to download the matter storage file
|
|
531
373
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
532
374
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
533
375
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
@@ -538,7 +380,6 @@ export class Frontend {
|
|
|
538
380
|
}
|
|
539
381
|
});
|
|
540
382
|
});
|
|
541
|
-
// Endpoint to download the matterbridge plugin directory
|
|
542
383
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
543
384
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
544
385
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -549,7 +390,6 @@ export class Frontend {
|
|
|
549
390
|
}
|
|
550
391
|
});
|
|
551
392
|
});
|
|
552
|
-
// Endpoint to download the matterbridge plugin config files
|
|
553
393
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
554
394
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
555
395
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
@@ -560,7 +400,6 @@ export class Frontend {
|
|
|
560
400
|
}
|
|
561
401
|
});
|
|
562
402
|
});
|
|
563
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
564
403
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
565
404
|
this.log.debug('The frontend sent /api/download-backup');
|
|
566
405
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -570,7 +409,6 @@ export class Frontend {
|
|
|
570
409
|
}
|
|
571
410
|
});
|
|
572
411
|
});
|
|
573
|
-
// Endpoint to upload a package
|
|
574
412
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
575
413
|
const { filename } = req.body;
|
|
576
414
|
const file = req.file;
|
|
@@ -580,13 +418,10 @@ export class Frontend {
|
|
|
580
418
|
return;
|
|
581
419
|
}
|
|
582
420
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
583
|
-
// Define the path where the plugin file will be saved
|
|
584
421
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
585
422
|
try {
|
|
586
|
-
// Move the uploaded file to the specified path
|
|
587
423
|
await fs.rename(file.path, filePath);
|
|
588
424
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
589
|
-
// Install the plugin package
|
|
590
425
|
if (filename.endsWith('.tgz')) {
|
|
591
426
|
await spawn.spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
592
427
|
this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
|
|
@@ -605,7 +440,6 @@ export class Frontend {
|
|
|
605
440
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
606
441
|
}
|
|
607
442
|
});
|
|
608
|
-
// Fallback for routing (must be the last route)
|
|
609
443
|
this.expressApp.use((req, res) => {
|
|
610
444
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
|
|
611
445
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -614,15 +448,12 @@ export class Frontend {
|
|
|
614
448
|
}
|
|
615
449
|
async stop() {
|
|
616
450
|
this.log.debug('Stopping the frontend...');
|
|
617
|
-
// Remove listeners from the express app
|
|
618
451
|
if (this.expressApp) {
|
|
619
452
|
this.expressApp.removeAllListeners();
|
|
620
453
|
this.expressApp = undefined;
|
|
621
454
|
this.log.debug('Frontend app closed successfully');
|
|
622
455
|
}
|
|
623
|
-
// Close the WebSocket server
|
|
624
456
|
if (this.webSocketServer) {
|
|
625
|
-
// Close all active connections
|
|
626
457
|
this.webSocketServer.clients.forEach((client) => {
|
|
627
458
|
if (client.readyState === WebSocket.OPEN) {
|
|
628
459
|
client.close();
|
|
@@ -642,7 +473,6 @@ export class Frontend {
|
|
|
642
473
|
this.webSocketServer.removeAllListeners();
|
|
643
474
|
this.webSocketServer = undefined;
|
|
644
475
|
}
|
|
645
|
-
// Close the http server
|
|
646
476
|
if (this.httpServer) {
|
|
647
477
|
await withTimeout(new Promise((resolve) => {
|
|
648
478
|
this.httpServer?.close((error) => {
|
|
@@ -659,7 +489,6 @@ export class Frontend {
|
|
|
659
489
|
this.httpServer = undefined;
|
|
660
490
|
this.log.debug('Frontend http server closed successfully');
|
|
661
491
|
}
|
|
662
|
-
// Close the https server
|
|
663
492
|
if (this.httpsServer) {
|
|
664
493
|
await withTimeout(new Promise((resolve) => {
|
|
665
494
|
this.httpsServer?.close((error) => {
|
|
@@ -678,7 +507,6 @@ export class Frontend {
|
|
|
678
507
|
}
|
|
679
508
|
this.log.debug('Frontend stopped successfully');
|
|
680
509
|
}
|
|
681
|
-
// Function to format bytes to KB, MB, or GB
|
|
682
510
|
formatMemoryUsage = (bytes) => {
|
|
683
511
|
if (bytes >= 1024 ** 3) {
|
|
684
512
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -690,7 +518,6 @@ export class Frontend {
|
|
|
690
518
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
691
519
|
}
|
|
692
520
|
};
|
|
693
|
-
// Function to format system uptime with only the most significant unit
|
|
694
521
|
formatOsUpTime = (seconds) => {
|
|
695
522
|
if (seconds >= 86400) {
|
|
696
523
|
const days = Math.floor(seconds / 86400);
|
|
@@ -706,14 +533,8 @@ export class Frontend {
|
|
|
706
533
|
}
|
|
707
534
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
708
535
|
};
|
|
709
|
-
/**
|
|
710
|
-
* Retrieves the api settings data.
|
|
711
|
-
*
|
|
712
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
713
|
-
*/
|
|
714
536
|
async getApiSettings() {
|
|
715
537
|
const { lastCpuUsage } = await import('./cli.js');
|
|
716
|
-
// Update the system information
|
|
717
538
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
718
539
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
719
540
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -722,7 +543,6 @@ export class Frontend {
|
|
|
722
543
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
723
544
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
724
545
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
725
|
-
// Update the matterbridge information
|
|
726
546
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
727
547
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
728
548
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -741,12 +561,6 @@ export class Frontend {
|
|
|
741
561
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
742
562
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
743
563
|
}
|
|
744
|
-
/**
|
|
745
|
-
* Retrieves the reachable attribute.
|
|
746
|
-
*
|
|
747
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
748
|
-
* @returns {boolean} The reachable attribute.
|
|
749
|
-
*/
|
|
750
564
|
getReachability(device) {
|
|
751
565
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
752
566
|
return false;
|
|
@@ -756,12 +570,6 @@ export class Frontend {
|
|
|
756
570
|
return true;
|
|
757
571
|
return false;
|
|
758
572
|
}
|
|
759
|
-
/**
|
|
760
|
-
* Retrieves the power source attribute.
|
|
761
|
-
*
|
|
762
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice object.
|
|
763
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
764
|
-
*/
|
|
765
573
|
getPowerSource(endpoint) {
|
|
766
574
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
767
575
|
return undefined;
|
|
@@ -777,21 +585,13 @@ export class Frontend {
|
|
|
777
585
|
}
|
|
778
586
|
return;
|
|
779
587
|
};
|
|
780
|
-
// Root endpoint
|
|
781
588
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
782
589
|
return powerSource(endpoint);
|
|
783
|
-
// Child endpoints
|
|
784
590
|
for (const child of endpoint.getChildEndpoints()) {
|
|
785
591
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
786
592
|
return powerSource(child);
|
|
787
593
|
}
|
|
788
594
|
}
|
|
789
|
-
/**
|
|
790
|
-
* Retrieves the cluster text description from a given device.
|
|
791
|
-
*
|
|
792
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
793
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
794
|
-
*/
|
|
795
595
|
getClusterTextFromDevice(device) {
|
|
796
596
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
797
597
|
return '';
|
|
@@ -832,19 +632,7 @@ export class Frontend {
|
|
|
832
632
|
};
|
|
833
633
|
let attributes = '';
|
|
834
634
|
let supportedModes = [];
|
|
835
|
-
/*
|
|
836
|
-
Object.keys(device.behaviors.supported).forEach((clusterName) => {
|
|
837
|
-
const clusterBehavior = device.behaviors.supported[lowercaseFirstLetter(clusterName)] as ClusterBehavior.Type | undefined;
|
|
838
|
-
// console.log(`Device: ${device.deviceName} => Cluster: ${clusterName} Behavior: ${clusterBehavior?.id}`, clusterBehavior);
|
|
839
|
-
if (clusterBehavior && clusterBehavior.cluster && clusterBehavior.cluster.attributes) {
|
|
840
|
-
Object.entries(clusterBehavior.cluster.attributes).forEach(([attributeName, attribute]) => {
|
|
841
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName} Attribute: ${attributeName}`, attribute);
|
|
842
|
-
});
|
|
843
|
-
}
|
|
844
|
-
});
|
|
845
|
-
*/
|
|
846
635
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
847
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
848
636
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
849
637
|
return;
|
|
850
638
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -936,14 +724,8 @@ export class Frontend {
|
|
|
936
724
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
937
725
|
attributes += `${getUserLabel(device)} `;
|
|
938
726
|
});
|
|
939
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
940
727
|
return attributes.trimStart().trimEnd();
|
|
941
728
|
}
|
|
942
|
-
/**
|
|
943
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
944
|
-
*
|
|
945
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
946
|
-
*/
|
|
947
729
|
getBaseRegisteredPlugins() {
|
|
948
730
|
const baseRegisteredPlugins = [];
|
|
949
731
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -982,19 +764,11 @@ export class Frontend {
|
|
|
982
764
|
}
|
|
983
765
|
return baseRegisteredPlugins;
|
|
984
766
|
}
|
|
985
|
-
/**
|
|
986
|
-
* Retrieves the devices from Matterbridge.
|
|
987
|
-
*
|
|
988
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
989
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices.
|
|
990
|
-
*/
|
|
991
767
|
async getDevices(pluginName) {
|
|
992
768
|
const devices = [];
|
|
993
769
|
this.matterbridge.devices.forEach(async (device) => {
|
|
994
|
-
// Filter by pluginName if provided
|
|
995
770
|
if (pluginName && pluginName !== device.plugin)
|
|
996
771
|
return;
|
|
997
|
-
// Check if the device has the required properties
|
|
998
772
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
999
773
|
return;
|
|
1000
774
|
const cluster = this.getClusterTextFromDevice(device);
|
|
@@ -1014,37 +788,22 @@ export class Frontend {
|
|
|
1014
788
|
});
|
|
1015
789
|
return devices;
|
|
1016
790
|
}
|
|
1017
|
-
/**
|
|
1018
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1019
|
-
*
|
|
1020
|
-
* Response for /api/clusters
|
|
1021
|
-
*
|
|
1022
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1023
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1024
|
-
* @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1025
|
-
*/
|
|
1026
791
|
getClusters(pluginName, endpointNumber) {
|
|
1027
792
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1028
793
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1029
794
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1030
795
|
return;
|
|
1031
796
|
}
|
|
1032
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1033
|
-
// Get the device types from the main endpoint
|
|
1034
797
|
const deviceTypes = [];
|
|
1035
798
|
const clusters = [];
|
|
1036
799
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1037
800
|
deviceTypes.push(d.deviceType);
|
|
1038
801
|
});
|
|
1039
|
-
// Get the clusters from the main endpoint
|
|
1040
802
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1041
803
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1042
804
|
return;
|
|
1043
805
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1044
806
|
return;
|
|
1045
|
-
// console.log(
|
|
1046
|
-
// `${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}`,
|
|
1047
|
-
// );
|
|
1048
807
|
clusters.push({
|
|
1049
808
|
endpoint: endpoint.number.toString(),
|
|
1050
809
|
id: 'main',
|
|
@@ -1057,18 +816,12 @@ export class Frontend {
|
|
|
1057
816
|
attributeLocalValue: attributeValue,
|
|
1058
817
|
});
|
|
1059
818
|
});
|
|
1060
|
-
// Get the child endpoints
|
|
1061
819
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1062
|
-
// if (childEndpoints.length === 0) {
|
|
1063
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1064
|
-
// }
|
|
1065
820
|
childEndpoints.forEach((childEndpoint) => {
|
|
1066
821
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1067
822
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1068
823
|
return;
|
|
1069
824
|
}
|
|
1070
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1071
|
-
// Get the device types of the child endpoint
|
|
1072
825
|
const deviceTypes = [];
|
|
1073
826
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1074
827
|
deviceTypes.push(d.deviceType);
|
|
@@ -1078,12 +831,9 @@ export class Frontend {
|
|
|
1078
831
|
return;
|
|
1079
832
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1080
833
|
return;
|
|
1081
|
-
// console.log(
|
|
1082
|
-
// `${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}`,
|
|
1083
|
-
// );
|
|
1084
834
|
clusters.push({
|
|
1085
835
|
endpoint: childEndpoint.number.toString(),
|
|
1086
|
-
id: childEndpoint.maybeId ?? 'null',
|
|
836
|
+
id: childEndpoint.maybeId ?? 'null',
|
|
1087
837
|
deviceTypes,
|
|
1088
838
|
clusterName: capitalizeFirstLetter(clusterName),
|
|
1089
839
|
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
@@ -1096,13 +846,6 @@ export class Frontend {
|
|
|
1096
846
|
});
|
|
1097
847
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
|
|
1098
848
|
}
|
|
1099
|
-
/**
|
|
1100
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1101
|
-
*
|
|
1102
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1103
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1104
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1105
|
-
*/
|
|
1106
849
|
async wsMessageHandler(client, message) {
|
|
1107
850
|
let data;
|
|
1108
851
|
try {
|
|
@@ -1149,41 +892,32 @@ export class Frontend {
|
|
|
1149
892
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1150
893
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1151
894
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1152
|
-
// The install comes from InstallPlugins
|
|
1153
895
|
this.matterbridge.plugins
|
|
1154
896
|
.add(packageName)
|
|
1155
897
|
.then((plugin) => {
|
|
1156
898
|
if (plugin) {
|
|
1157
|
-
// The plugin is not registered
|
|
1158
899
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1159
900
|
this.matterbridge.plugins
|
|
1160
901
|
.load(plugin, true, 'The plugin has been added', true)
|
|
1161
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1162
902
|
.then(() => {
|
|
1163
903
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
1164
904
|
this.wssSendRefreshRequired('plugins');
|
|
1165
905
|
return;
|
|
1166
906
|
})
|
|
1167
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1168
907
|
.catch((_error) => {
|
|
1169
|
-
//
|
|
1170
908
|
});
|
|
1171
909
|
}
|
|
1172
910
|
else {
|
|
1173
|
-
// The plugin is already registered
|
|
1174
911
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1175
912
|
this.wssSendRefreshRequired('plugins');
|
|
1176
913
|
this.wssSendRestartRequired();
|
|
1177
914
|
}
|
|
1178
915
|
return;
|
|
1179
916
|
})
|
|
1180
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1181
917
|
.catch((_error) => {
|
|
1182
|
-
//
|
|
1183
918
|
});
|
|
1184
919
|
}
|
|
1185
920
|
else {
|
|
1186
|
-
// The package is matterbridge
|
|
1187
921
|
if (this.matterbridge.restartMode !== '') {
|
|
1188
922
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1189
923
|
this.matterbridge.shutdownProcess();
|
|
@@ -1206,7 +940,6 @@ export class Frontend {
|
|
|
1206
940
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1207
941
|
return;
|
|
1208
942
|
}
|
|
1209
|
-
// The package is a plugin
|
|
1210
943
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1211
944
|
if (plugin) {
|
|
1212
945
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
@@ -1215,7 +948,6 @@ export class Frontend {
|
|
|
1215
948
|
this.wssSendRefreshRequired('plugins');
|
|
1216
949
|
this.wssSendRefreshRequired('devices');
|
|
1217
950
|
}
|
|
1218
|
-
// Uninstall the package
|
|
1219
951
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1220
952
|
spawn
|
|
1221
953
|
.spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1256,7 +988,6 @@ export class Frontend {
|
|
|
1256
988
|
return;
|
|
1257
989
|
})
|
|
1258
990
|
.catch((_error) => {
|
|
1259
|
-
//
|
|
1260
991
|
});
|
|
1261
992
|
}
|
|
1262
993
|
else {
|
|
@@ -1303,7 +1034,6 @@ export class Frontend {
|
|
|
1303
1034
|
return;
|
|
1304
1035
|
})
|
|
1305
1036
|
.catch((_error) => {
|
|
1306
|
-
//
|
|
1307
1037
|
});
|
|
1308
1038
|
}
|
|
1309
1039
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1541,22 +1271,22 @@ export class Frontend {
|
|
|
1541
1271
|
if (isValidString(data.params.value, 4)) {
|
|
1542
1272
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1543
1273
|
if (data.params.value === 'Debug') {
|
|
1544
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1274
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1545
1275
|
}
|
|
1546
1276
|
else if (data.params.value === 'Info') {
|
|
1547
|
-
await this.matterbridge.setLogLevel("info"
|
|
1277
|
+
await this.matterbridge.setLogLevel("info");
|
|
1548
1278
|
}
|
|
1549
1279
|
else if (data.params.value === 'Notice') {
|
|
1550
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1280
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1551
1281
|
}
|
|
1552
1282
|
else if (data.params.value === 'Warn') {
|
|
1553
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1283
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1554
1284
|
}
|
|
1555
1285
|
else if (data.params.value === 'Error') {
|
|
1556
|
-
await this.matterbridge.setLogLevel("error"
|
|
1286
|
+
await this.matterbridge.setLogLevel("error");
|
|
1557
1287
|
}
|
|
1558
1288
|
else if (data.params.value === 'Fatal') {
|
|
1559
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1289
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1560
1290
|
}
|
|
1561
1291
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1562
1292
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1567,7 +1297,6 @@ export class Frontend {
|
|
|
1567
1297
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1568
1298
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1569
1299
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1570
|
-
// Create the file logger for matterbridge
|
|
1571
1300
|
if (data.params.value)
|
|
1572
1301
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1573
1302
|
else
|
|
@@ -1732,19 +1461,15 @@ export class Frontend {
|
|
|
1732
1461
|
return;
|
|
1733
1462
|
}
|
|
1734
1463
|
const config = plugin.configJson;
|
|
1735
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1736
1464
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1737
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1738
1465
|
if (select === 'serial')
|
|
1739
1466
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1740
1467
|
if (select === 'name')
|
|
1741
1468
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1742
1469
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1743
|
-
// Remove postfix from the serial if it exists
|
|
1744
1470
|
if (config.postfix) {
|
|
1745
1471
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1746
1472
|
}
|
|
1747
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1748
1473
|
if (isValidArray(config.whiteList, 1)) {
|
|
1749
1474
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1750
1475
|
config.whiteList.push(data.params.serial);
|
|
@@ -1753,7 +1478,6 @@ export class Frontend {
|
|
|
1753
1478
|
config.whiteList.push(data.params.name);
|
|
1754
1479
|
}
|
|
1755
1480
|
}
|
|
1756
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1757
1481
|
if (isValidArray(config.blackList, 1)) {
|
|
1758
1482
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1759
1483
|
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
@@ -1783,9 +1507,7 @@ export class Frontend {
|
|
|
1783
1507
|
return;
|
|
1784
1508
|
}
|
|
1785
1509
|
const config = plugin.configJson;
|
|
1786
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1787
1510
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1788
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1789
1511
|
if (select === 'serial')
|
|
1790
1512
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1791
1513
|
if (select === 'name')
|
|
@@ -1794,7 +1516,6 @@ export class Frontend {
|
|
|
1794
1516
|
if (config.postfix) {
|
|
1795
1517
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1796
1518
|
}
|
|
1797
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1798
1519
|
if (isValidArray(config.whiteList, 1)) {
|
|
1799
1520
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1800
1521
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
@@ -1803,7 +1524,6 @@ export class Frontend {
|
|
|
1803
1524
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1804
1525
|
}
|
|
1805
1526
|
}
|
|
1806
|
-
// Add the serial to the blackList
|
|
1807
1527
|
if (isValidArray(config.blackList)) {
|
|
1808
1528
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1809
1529
|
config.blackList.push(data.params.serial);
|
|
@@ -1836,230 +1556,114 @@ export class Frontend {
|
|
|
1836
1556
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1837
1557
|
}
|
|
1838
1558
|
}
|
|
1839
|
-
/**
|
|
1840
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1841
|
-
*
|
|
1842
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1843
|
-
* @param {string} time - The time string of the message
|
|
1844
|
-
* @param {string} name - The logger name of the message
|
|
1845
|
-
* @param {string} message - The content of the message.
|
|
1846
|
-
*
|
|
1847
|
-
* @remarks
|
|
1848
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
1849
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
1850
|
-
* The function sends the message to all connected clients.
|
|
1851
|
-
*/
|
|
1852
1559
|
wssSendMessage(level, time, name, message) {
|
|
1853
1560
|
if (!level || !time || !name || !message)
|
|
1854
1561
|
return;
|
|
1855
|
-
// Remove ANSI escape codes from the message
|
|
1856
|
-
// eslint-disable-next-line no-control-regex
|
|
1857
1562
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1858
|
-
// Remove leading asterisks from the message
|
|
1859
1563
|
message = message.replace(/^\*+/, '');
|
|
1860
|
-
// Replace all occurrences of \t and \n
|
|
1861
1564
|
message = message.replace(/[\t\n]/g, '');
|
|
1862
|
-
// Remove non-printable characters
|
|
1863
|
-
// eslint-disable-next-line no-control-regex
|
|
1864
1565
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1865
|
-
// Replace all occurrences of \" with "
|
|
1866
1566
|
message = message.replace(/\\"/g, '"');
|
|
1867
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1868
1567
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1869
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1870
1568
|
const maxContinuousLength = 100;
|
|
1871
1569
|
const keepStartLength = 20;
|
|
1872
1570
|
const keepEndLength = 20;
|
|
1873
|
-
// Split the message into words
|
|
1874
1571
|
message = message
|
|
1875
1572
|
.split(' ')
|
|
1876
1573
|
.map((word) => {
|
|
1877
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1878
1574
|
if (word.length > maxContinuousLength) {
|
|
1879
1575
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1880
1576
|
}
|
|
1881
1577
|
return word;
|
|
1882
1578
|
})
|
|
1883
1579
|
.join(' ');
|
|
1884
|
-
// Send the message to all connected clients
|
|
1885
1580
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1886
1581
|
if (client.readyState === WebSocket.OPEN) {
|
|
1887
1582
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1888
1583
|
}
|
|
1889
1584
|
});
|
|
1890
1585
|
}
|
|
1891
|
-
/**
|
|
1892
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1893
|
-
*
|
|
1894
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
1895
|
-
* possible values:
|
|
1896
|
-
* - 'matterbridgeLatestVersion'
|
|
1897
|
-
* - 'matterbridgeAdvertise'
|
|
1898
|
-
* - 'online'
|
|
1899
|
-
* - 'offline'
|
|
1900
|
-
* - 'reachability'
|
|
1901
|
-
* - 'settings'
|
|
1902
|
-
* - 'plugins'
|
|
1903
|
-
* - 'pluginsRestart'
|
|
1904
|
-
* - 'devices'
|
|
1905
|
-
* - 'fabrics'
|
|
1906
|
-
* - 'sessions'
|
|
1907
|
-
*/
|
|
1908
1586
|
wssSendRefreshRequired(changed = null) {
|
|
1909
1587
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1910
|
-
// Send the message to all connected clients
|
|
1911
1588
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1912
1589
|
if (client.readyState === WebSocket.OPEN) {
|
|
1913
1590
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1914
1591
|
}
|
|
1915
1592
|
});
|
|
1916
1593
|
}
|
|
1917
|
-
/**
|
|
1918
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1919
|
-
*
|
|
1920
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients.
|
|
1921
|
-
*/
|
|
1922
1594
|
wssSendRestartRequired(snackbar = true) {
|
|
1923
1595
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1924
1596
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1925
1597
|
if (snackbar === true)
|
|
1926
1598
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1927
|
-
// Send the message to all connected clients
|
|
1928
1599
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1929
1600
|
if (client.readyState === WebSocket.OPEN) {
|
|
1930
1601
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1931
1602
|
}
|
|
1932
1603
|
});
|
|
1933
1604
|
}
|
|
1934
|
-
/**
|
|
1935
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
1936
|
-
*
|
|
1937
|
-
*/
|
|
1938
1605
|
wssSendUpdateRequired() {
|
|
1939
1606
|
this.log.debug('Sending an update required message to all connected clients');
|
|
1940
1607
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
1941
|
-
// Send the message to all connected clients
|
|
1942
1608
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1943
1609
|
if (client.readyState === WebSocket.OPEN) {
|
|
1944
1610
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
1945
1611
|
}
|
|
1946
1612
|
});
|
|
1947
1613
|
}
|
|
1948
|
-
/**
|
|
1949
|
-
* Sends a cpu update message to all connected clients.
|
|
1950
|
-
*
|
|
1951
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
1952
|
-
*/
|
|
1953
1614
|
wssSendCpuUpdate(cpuUsage) {
|
|
1954
1615
|
if (hasParameter('debug'))
|
|
1955
1616
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1956
|
-
// Send the message to all connected clients
|
|
1957
1617
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1958
1618
|
if (client.readyState === WebSocket.OPEN) {
|
|
1959
1619
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
1960
1620
|
}
|
|
1961
1621
|
});
|
|
1962
1622
|
}
|
|
1963
|
-
/**
|
|
1964
|
-
* Sends a memory update message to all connected clients.
|
|
1965
|
-
*
|
|
1966
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
1967
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
1968
|
-
* @param {string} rss - The resident set size in bytes.
|
|
1969
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
1970
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
1971
|
-
* @param {string} external - The external memory in bytes.
|
|
1972
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
1973
|
-
*/
|
|
1974
1623
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
1975
1624
|
if (hasParameter('debug'))
|
|
1976
1625
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
1977
|
-
// Send the message to all connected clients
|
|
1978
1626
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1979
1627
|
if (client.readyState === WebSocket.OPEN) {
|
|
1980
1628
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
1981
1629
|
}
|
|
1982
1630
|
});
|
|
1983
1631
|
}
|
|
1984
|
-
/**
|
|
1985
|
-
* Sends an uptime update message to all connected clients.
|
|
1986
|
-
*
|
|
1987
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
1988
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
1989
|
-
*/
|
|
1990
1632
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
1991
1633
|
if (hasParameter('debug'))
|
|
1992
1634
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
1993
|
-
// Send the message to all connected clients
|
|
1994
1635
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1995
1636
|
if (client.readyState === WebSocket.OPEN) {
|
|
1996
1637
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
1997
1638
|
}
|
|
1998
1639
|
});
|
|
1999
1640
|
}
|
|
2000
|
-
/**
|
|
2001
|
-
* Sends an open snackbar message to all connected clients.
|
|
2002
|
-
*
|
|
2003
|
-
* @param {string} message - The message to send.
|
|
2004
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
2005
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
2006
|
-
*/
|
|
2007
1641
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2008
1642
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2009
|
-
// Send the message to all connected clients
|
|
2010
1643
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2011
1644
|
if (client.readyState === WebSocket.OPEN) {
|
|
2012
1645
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
2013
1646
|
}
|
|
2014
1647
|
});
|
|
2015
1648
|
}
|
|
2016
|
-
/**
|
|
2017
|
-
* Sends a close snackbar message to all connected clients.
|
|
2018
|
-
*
|
|
2019
|
-
* @param {string} message - The message to send.
|
|
2020
|
-
*/
|
|
2021
1649
|
wssSendCloseSnackbarMessage(message) {
|
|
2022
1650
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2023
|
-
// Send the message to all connected clients
|
|
2024
1651
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2025
1652
|
if (client.readyState === WebSocket.OPEN) {
|
|
2026
1653
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
2027
1654
|
}
|
|
2028
1655
|
});
|
|
2029
1656
|
}
|
|
2030
|
-
/**
|
|
2031
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2032
|
-
*
|
|
2033
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2034
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2035
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2036
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2037
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2038
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2039
|
-
*
|
|
2040
|
-
* @remarks
|
|
2041
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2042
|
-
* with the updated attribute information.
|
|
2043
|
-
*/
|
|
2044
1657
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
2045
1658
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2046
|
-
// Send the message to all connected clients
|
|
2047
1659
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2048
1660
|
if (client.readyState === WebSocket.OPEN) {
|
|
2049
1661
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
2050
1662
|
}
|
|
2051
1663
|
});
|
|
2052
1664
|
}
|
|
2053
|
-
/**
|
|
2054
|
-
* Sends a message to all connected clients.
|
|
2055
|
-
*
|
|
2056
|
-
* @param {number} id - The message id.
|
|
2057
|
-
* @param {string} method - The message method.
|
|
2058
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
2059
|
-
*/
|
|
2060
1665
|
wssBroadcastMessage(id, method, params) {
|
|
2061
1666
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
2062
|
-
// Send the message to all connected clients
|
|
2063
1667
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2064
1668
|
if (client.readyState === WebSocket.OPEN) {
|
|
2065
1669
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -2067,4 +1671,3 @@ export class Frontend {
|
|
|
2067
1671
|
});
|
|
2068
1672
|
}
|
|
2069
1673
|
}
|
|
2070
|
-
//# sourceMappingURL=frontend.js.map
|