matterbridge 3.1.2 → 3.1.3-dev-20250712-616f7ed
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/dist/cli.js +2 -91
- package/dist/cliEmitter.js +0 -30
- package/dist/clusters/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -24
- package/dist/deviceManager.js +1 -94
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.js +0 -2
- package/dist/devices/heatPump.js +2 -50
- package/dist/devices/laundryDryer.js +6 -83
- package/dist/devices/laundryWasher.js +7 -91
- package/dist/devices/roboticVacuumCleaner.js +6 -89
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/waterHeater.js +2 -82
- package/dist/frontend.js +16 -417
- package/dist/globalMatterbridge.js +0 -47
- package/dist/helpers.js +0 -53
- package/dist/index.js +1 -30
- 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 +54 -803
- package/dist/matterbridgeAccessoryPlatform.js +0 -36
- package/dist/matterbridgeBehaviors.js +1 -61
- package/dist/matterbridgeDeviceTypes.js +15 -579
- package/dist/matterbridgeDynamicPlatform.js +0 -36
- package/dist/matterbridgeEndpoint.js +51 -1053
- 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/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/npm-shrinkwrap.json +5 -5
- package/package.json +1 -2
- package/dist/cli.d.ts +0 -26
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cliEmitter.d.ts +0 -34
- package/dist/cliEmitter.d.ts.map +0 -1
- package/dist/cliEmitter.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/batteryStorage.d.ts +0 -48
- package/dist/devices/batteryStorage.d.ts.map +0 -1
- package/dist/devices/batteryStorage.js.map +0 -1
- package/dist/devices/evse.d.ts +0 -75
- package/dist/devices/evse.d.ts.map +0 -1
- package/dist/devices/evse.js.map +0 -1
- package/dist/devices/export.d.ts +0 -9
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.js.map +0 -1
- package/dist/devices/heatPump.d.ts +0 -47
- package/dist/devices/heatPump.d.ts.map +0 -1
- package/dist/devices/heatPump.js.map +0 -1
- package/dist/devices/laundryDryer.d.ts +0 -87
- package/dist/devices/laundryDryer.d.ts.map +0 -1
- package/dist/devices/laundryDryer.js.map +0 -1
- package/dist/devices/laundryWasher.d.ts +0 -242
- package/dist/devices/laundryWasher.d.ts.map +0 -1
- package/dist/devices/laundryWasher.js.map +0 -1
- package/dist/devices/roboticVacuumCleaner.d.ts +0 -110
- package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
- package/dist/devices/roboticVacuumCleaner.js.map +0 -1
- package/dist/devices/solarPower.d.ts +0 -40
- package/dist/devices/solarPower.d.ts.map +0 -1
- package/dist/devices/solarPower.js.map +0 -1
- package/dist/devices/waterHeater.d.ts +0 -111
- package/dist/devices/waterHeater.d.ts.map +0 -1
- package/dist/devices/waterHeater.js.map +0 -1
- package/dist/frontend.d.ts +0 -303
- 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 -33
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -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 -1340
- 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 -1196
- 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 -192
- 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/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 -11
- 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/frontend.js
CHANGED
|
@@ -1,126 +1,30 @@
|
|
|
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.1.0
|
|
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
6
|
import EventEmitter from 'node:events';
|
|
31
|
-
// Third-party modules
|
|
32
7
|
import express from 'express';
|
|
33
8
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
34
9
|
import multer from 'multer';
|
|
35
|
-
// AnsiLogger module
|
|
36
10
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt } from 'node-ansi-logger';
|
|
37
|
-
// @matter
|
|
38
11
|
import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
|
|
39
12
|
import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
|
|
40
|
-
// Matterbridge
|
|
41
13
|
import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout, hasParameter } from './utils/export.js';
|
|
42
14
|
import { plg } from './matterbridgeTypes.js';
|
|
43
15
|
import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
|
|
44
16
|
import { cliEmitter, lastCpuUsage } from './cliEmitter.js';
|
|
45
|
-
/**
|
|
46
|
-
* Websocket message ID for logging.
|
|
47
|
-
*
|
|
48
|
-
* @constant {number}
|
|
49
|
-
*/
|
|
50
17
|
export const WS_ID_LOG = 0;
|
|
51
|
-
/**
|
|
52
|
-
* Websocket message ID indicating a refresh is needed.
|
|
53
|
-
*
|
|
54
|
-
* @constant {number}
|
|
55
|
-
*/
|
|
56
18
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
57
|
-
/**
|
|
58
|
-
* Websocket message ID indicating a restart is needed.
|
|
59
|
-
*
|
|
60
|
-
* @constant {number}
|
|
61
|
-
*/
|
|
62
19
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
63
|
-
/**
|
|
64
|
-
* Websocket message ID indicating a cpu update.
|
|
65
|
-
*
|
|
66
|
-
* @constant {number}
|
|
67
|
-
*/
|
|
68
20
|
export const WS_ID_CPU_UPDATE = 3;
|
|
69
|
-
/**
|
|
70
|
-
* Websocket message ID indicating a memory update.
|
|
71
|
-
*
|
|
72
|
-
* @constant {number}
|
|
73
|
-
*/
|
|
74
21
|
export const WS_ID_MEMORY_UPDATE = 4;
|
|
75
|
-
/**
|
|
76
|
-
* Websocket message ID indicating an uptime update.
|
|
77
|
-
*
|
|
78
|
-
* @constant {number}
|
|
79
|
-
*/
|
|
80
22
|
export const WS_ID_UPTIME_UPDATE = 5;
|
|
81
|
-
/**
|
|
82
|
-
* Websocket message ID indicating a snackbar message.
|
|
83
|
-
*
|
|
84
|
-
* @constant {number}
|
|
85
|
-
*/
|
|
86
23
|
export const WS_ID_SNACKBAR = 6;
|
|
87
|
-
/**
|
|
88
|
-
* Websocket message ID indicating matterbridge has un update available.
|
|
89
|
-
*
|
|
90
|
-
* @constant {number}
|
|
91
|
-
*/
|
|
92
24
|
export const WS_ID_UPDATE_NEEDED = 7;
|
|
93
|
-
/**
|
|
94
|
-
* Websocket message ID indicating a state update.
|
|
95
|
-
*
|
|
96
|
-
* @constant {number}
|
|
97
|
-
*/
|
|
98
25
|
export const WS_ID_STATEUPDATE = 8;
|
|
99
|
-
/**
|
|
100
|
-
* Websocket message ID indicating to close a permanent snackbar message.
|
|
101
|
-
*
|
|
102
|
-
* @constant {number}
|
|
103
|
-
*/
|
|
104
26
|
export const WS_ID_CLOSE_SNACKBAR = 9;
|
|
105
|
-
/**
|
|
106
|
-
* Websocket message ID indicating a shelly system update.
|
|
107
|
-
* check:
|
|
108
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/check
|
|
109
|
-
* perform:
|
|
110
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/perform
|
|
111
|
-
*
|
|
112
|
-
* @constant {number}
|
|
113
|
-
*/
|
|
114
27
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
115
|
-
/**
|
|
116
|
-
* Websocket message ID indicating a shelly main update.
|
|
117
|
-
* check:
|
|
118
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/check
|
|
119
|
-
* perform:
|
|
120
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/perform
|
|
121
|
-
*
|
|
122
|
-
* @constant {number}
|
|
123
|
-
*/
|
|
124
28
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
125
29
|
export class Frontend extends EventEmitter {
|
|
126
30
|
matterbridge;
|
|
@@ -134,7 +38,7 @@ export class Frontend extends EventEmitter {
|
|
|
134
38
|
constructor(matterbridge) {
|
|
135
39
|
super();
|
|
136
40
|
this.matterbridge = matterbridge;
|
|
137
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
41
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
138
42
|
}
|
|
139
43
|
set logLevel(logLevel) {
|
|
140
44
|
this.log.logLevel = logLevel;
|
|
@@ -142,41 +46,12 @@ export class Frontend extends EventEmitter {
|
|
|
142
46
|
async start(port = 8283) {
|
|
143
47
|
this.port = port;
|
|
144
48
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
145
|
-
// Initialize multer with the upload directory
|
|
146
49
|
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
147
50
|
await fs.mkdir(uploadDir, { recursive: true });
|
|
148
51
|
const upload = multer({ dest: uploadDir });
|
|
149
|
-
// Create the express app that serves the frontend
|
|
150
52
|
this.expressApp = express();
|
|
151
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
152
|
-
/*
|
|
153
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
154
|
-
for (const method of methods) {
|
|
155
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
157
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
158
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
159
|
-
try {
|
|
160
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
161
|
-
return original(path, ...rest);
|
|
162
|
-
} catch (err) {
|
|
163
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
164
|
-
throw err;
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
*/
|
|
169
|
-
// Log all requests to the server for debugging
|
|
170
|
-
/*
|
|
171
|
-
this.expressApp.use((req, res, next) => {
|
|
172
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
173
|
-
next();
|
|
174
|
-
});
|
|
175
|
-
*/
|
|
176
|
-
// Serve static files from '/static' endpoint
|
|
177
53
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
178
54
|
if (!hasParameter('ssl')) {
|
|
179
|
-
// Create an HTTP server and attach the express app
|
|
180
55
|
try {
|
|
181
56
|
this.httpServer = createServer(this.expressApp);
|
|
182
57
|
}
|
|
@@ -185,7 +60,6 @@ export class Frontend extends EventEmitter {
|
|
|
185
60
|
this.emit('server_error', error);
|
|
186
61
|
return;
|
|
187
62
|
}
|
|
188
|
-
// Listen on the specified port
|
|
189
63
|
if (hasParameter('ingress')) {
|
|
190
64
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
191
65
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -217,7 +91,6 @@ export class Frontend extends EventEmitter {
|
|
|
217
91
|
});
|
|
218
92
|
}
|
|
219
93
|
else {
|
|
220
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
221
94
|
let cert;
|
|
222
95
|
try {
|
|
223
96
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -247,7 +120,6 @@ export class Frontend extends EventEmitter {
|
|
|
247
120
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
248
121
|
}
|
|
249
122
|
const serverOptions = { cert, key, ca };
|
|
250
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
251
123
|
try {
|
|
252
124
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
253
125
|
}
|
|
@@ -256,7 +128,6 @@ export class Frontend extends EventEmitter {
|
|
|
256
128
|
this.emit('server_error', error);
|
|
257
129
|
return;
|
|
258
130
|
}
|
|
259
|
-
// Listen on the specified port
|
|
260
131
|
if (hasParameter('ingress')) {
|
|
261
132
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
262
133
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -289,18 +160,16 @@ export class Frontend extends EventEmitter {
|
|
|
289
160
|
}
|
|
290
161
|
if (this.initializeError)
|
|
291
162
|
return;
|
|
292
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
293
163
|
const wssPort = this.port;
|
|
294
164
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
295
165
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
296
166
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
297
167
|
const clientIp = request.socket.remoteAddress;
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
168
|
+
let callbackLogLevel = "notice";
|
|
169
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
170
|
+
callbackLogLevel = "info";
|
|
171
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
172
|
+
callbackLogLevel = "debug";
|
|
304
173
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
305
174
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
306
175
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -335,7 +204,6 @@ export class Frontend extends EventEmitter {
|
|
|
335
204
|
this.webSocketServer.on('error', (ws, error) => {
|
|
336
205
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
337
206
|
});
|
|
338
|
-
// Subscribe to cli events
|
|
339
207
|
cliEmitter.removeAllListeners();
|
|
340
208
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
341
209
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -346,8 +214,6 @@ export class Frontend extends EventEmitter {
|
|
|
346
214
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
347
215
|
this.wssSendCpuUpdate(cpuUsage);
|
|
348
216
|
});
|
|
349
|
-
// Endpoint to validate login code
|
|
350
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
351
217
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
352
218
|
const { password } = req.body;
|
|
353
219
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -366,27 +232,23 @@ export class Frontend extends EventEmitter {
|
|
|
366
232
|
this.log.warn('/api/login error wrong password');
|
|
367
233
|
res.json({ valid: false });
|
|
368
234
|
}
|
|
369
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
370
235
|
}
|
|
371
236
|
catch (error) {
|
|
372
237
|
this.log.error('/api/login error getting password');
|
|
373
238
|
res.json({ valid: false });
|
|
374
239
|
}
|
|
375
240
|
});
|
|
376
|
-
// Endpoint to provide health check for docker
|
|
377
241
|
this.expressApp.get('/health', (req, res) => {
|
|
378
242
|
this.log.debug('Express received /health');
|
|
379
243
|
const healthStatus = {
|
|
380
|
-
status: 'ok',
|
|
381
|
-
uptime: process.uptime(),
|
|
382
|
-
timestamp: new Date().toISOString(),
|
|
244
|
+
status: 'ok',
|
|
245
|
+
uptime: process.uptime(),
|
|
246
|
+
timestamp: new Date().toISOString(),
|
|
383
247
|
};
|
|
384
248
|
res.status(200).json(healthStatus);
|
|
385
249
|
});
|
|
386
|
-
// Endpoint to provide memory usage details
|
|
387
250
|
this.expressApp.get('/memory', async (req, res) => {
|
|
388
251
|
this.log.debug('Express received /memory');
|
|
389
|
-
// Memory usage from process
|
|
390
252
|
const memoryUsageRaw = process.memoryUsage();
|
|
391
253
|
const memoryUsage = {
|
|
392
254
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -395,13 +257,10 @@ export class Frontend extends EventEmitter {
|
|
|
395
257
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
396
258
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
397
259
|
};
|
|
398
|
-
// V8 heap statistics
|
|
399
260
|
const { default: v8 } = await import('node:v8');
|
|
400
261
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
401
262
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
402
|
-
// Format heapStats
|
|
403
263
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
404
|
-
// Format heapSpaces
|
|
405
264
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
406
265
|
...space,
|
|
407
266
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -419,23 +278,19 @@ export class Frontend extends EventEmitter {
|
|
|
419
278
|
};
|
|
420
279
|
res.status(200).json(memoryReport);
|
|
421
280
|
});
|
|
422
|
-
// Endpoint to provide settings
|
|
423
281
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
424
282
|
this.log.debug('The frontend sent /api/settings');
|
|
425
283
|
res.json(await this.getApiSettings());
|
|
426
284
|
});
|
|
427
|
-
// Endpoint to provide plugins
|
|
428
285
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
429
286
|
this.log.debug('The frontend sent /api/plugins');
|
|
430
287
|
res.json(this.getBaseRegisteredPlugins());
|
|
431
288
|
});
|
|
432
|
-
// Endpoint to provide devices
|
|
433
289
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
434
290
|
this.log.debug('The frontend sent /api/devices');
|
|
435
291
|
const devices = await this.getDevices();
|
|
436
292
|
res.json(devices);
|
|
437
293
|
});
|
|
438
|
-
// Endpoint to view the matterbridge log
|
|
439
294
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
440
295
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
441
296
|
try {
|
|
@@ -448,7 +303,6 @@ export class Frontend extends EventEmitter {
|
|
|
448
303
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
449
304
|
}
|
|
450
305
|
});
|
|
451
|
-
// Endpoint to view the matter.js log
|
|
452
306
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
453
307
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
454
308
|
try {
|
|
@@ -461,7 +315,6 @@ export class Frontend extends EventEmitter {
|
|
|
461
315
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
462
316
|
}
|
|
463
317
|
});
|
|
464
|
-
// Endpoint to view the shelly log
|
|
465
318
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
466
319
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
467
320
|
try {
|
|
@@ -474,11 +327,9 @@ export class Frontend extends EventEmitter {
|
|
|
474
327
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
475
328
|
}
|
|
476
329
|
});
|
|
477
|
-
// Endpoint to download the matterbridge log
|
|
478
330
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
479
331
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
480
332
|
try {
|
|
481
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
482
333
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), fs.constants.F_OK);
|
|
483
334
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), 'utf8');
|
|
484
335
|
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), data, 'utf-8');
|
|
@@ -489,18 +340,15 @@ export class Frontend extends EventEmitter {
|
|
|
489
340
|
}
|
|
490
341
|
res.type('text/plain');
|
|
491
342
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'matterbridge.log', (error) => {
|
|
492
|
-
/* istanbul ignore if */
|
|
493
343
|
if (error) {
|
|
494
344
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
495
345
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
496
346
|
}
|
|
497
347
|
});
|
|
498
348
|
});
|
|
499
|
-
// Endpoint to download the matter log
|
|
500
349
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
501
350
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
502
351
|
try {
|
|
503
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
504
352
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
505
353
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'utf8');
|
|
506
354
|
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), data, 'utf-8');
|
|
@@ -511,18 +359,15 @@ export class Frontend extends EventEmitter {
|
|
|
511
359
|
}
|
|
512
360
|
res.type('text/plain');
|
|
513
361
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
|
|
514
|
-
/* istanbul ignore if */
|
|
515
362
|
if (error) {
|
|
516
363
|
this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
517
364
|
res.status(500).send('Error downloading the matter log file');
|
|
518
365
|
}
|
|
519
366
|
});
|
|
520
367
|
});
|
|
521
|
-
// Endpoint to download the shelly log
|
|
522
368
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
523
369
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
524
370
|
try {
|
|
525
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
526
371
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
|
|
527
372
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
|
|
528
373
|
await fs.writeFile(path.join(os.tmpdir(), 'shelly.log'), data, 'utf-8');
|
|
@@ -533,90 +378,74 @@ export class Frontend extends EventEmitter {
|
|
|
533
378
|
}
|
|
534
379
|
res.type('text/plain');
|
|
535
380
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
536
|
-
/* istanbul ignore if */
|
|
537
381
|
if (error) {
|
|
538
382
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
539
383
|
res.status(500).send('Error downloading Shelly system log file');
|
|
540
384
|
}
|
|
541
385
|
});
|
|
542
386
|
});
|
|
543
|
-
// Endpoint to download the matterbridge storage directory
|
|
544
387
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
545
388
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
546
389
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
547
390
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), `matterbridge.${this.matterbridge.nodeStorageName}.zip`, (error) => {
|
|
548
|
-
/* istanbul ignore if */
|
|
549
391
|
if (error) {
|
|
550
392
|
this.log.error(`Error downloading file ${`matterbridge.${this.matterbridge.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
551
393
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
552
394
|
}
|
|
553
395
|
});
|
|
554
396
|
});
|
|
555
|
-
// Endpoint to download the matter storage file
|
|
556
397
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
557
398
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
558
399
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
559
400
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), `matterbridge.${this.matterbridge.matterStorageName}.zip`, (error) => {
|
|
560
|
-
/* istanbul ignore if */
|
|
561
401
|
if (error) {
|
|
562
402
|
this.log.error(`Error downloading the matter storage matterbridge.${this.matterbridge.matterStorageName}.zip: ${error instanceof Error ? error.message : error}`);
|
|
563
403
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
564
404
|
}
|
|
565
405
|
});
|
|
566
406
|
});
|
|
567
|
-
// Endpoint to download the matterbridge plugin directory
|
|
568
407
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
569
408
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
570
409
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
571
410
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
572
|
-
/* istanbul ignore if */
|
|
573
411
|
if (error) {
|
|
574
412
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
575
413
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
576
414
|
}
|
|
577
415
|
});
|
|
578
416
|
});
|
|
579
|
-
// Endpoint to download the matterbridge plugin config files
|
|
580
417
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
581
418
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
582
419
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
583
420
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
584
|
-
/* istanbul ignore if */
|
|
585
421
|
if (error) {
|
|
586
422
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
587
423
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
588
424
|
}
|
|
589
425
|
});
|
|
590
426
|
});
|
|
591
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
592
427
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
593
428
|
this.log.debug('The frontend sent /api/download-backup');
|
|
594
429
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
595
|
-
/* istanbul ignore if */
|
|
596
430
|
if (error) {
|
|
597
431
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
598
432
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
599
433
|
}
|
|
600
434
|
});
|
|
601
435
|
});
|
|
602
|
-
// Endpoint to upload a package
|
|
603
436
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
604
437
|
const { filename } = req.body;
|
|
605
438
|
const file = req.file;
|
|
606
|
-
/* istanbul ignore if */
|
|
607
439
|
if (!file || !filename) {
|
|
608
440
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
609
441
|
res.status(400).send('Invalid request: file and filename are required');
|
|
610
442
|
return;
|
|
611
443
|
}
|
|
612
444
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
613
|
-
// Define the path where the plugin file will be saved
|
|
614
445
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
615
446
|
try {
|
|
616
|
-
// Move the uploaded file to the specified path
|
|
617
447
|
await fs.rename(file.path, filePath);
|
|
618
448
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
619
|
-
// Install the plugin package
|
|
620
449
|
if (filename.endsWith('.tgz')) {
|
|
621
450
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
622
451
|
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
@@ -636,7 +465,6 @@ export class Frontend extends EventEmitter {
|
|
|
636
465
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
637
466
|
}
|
|
638
467
|
});
|
|
639
|
-
// Fallback for routing (must be the last route)
|
|
640
468
|
this.expressApp.use((req, res) => {
|
|
641
469
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
|
|
642
470
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -645,16 +473,13 @@ export class Frontend extends EventEmitter {
|
|
|
645
473
|
}
|
|
646
474
|
async stop() {
|
|
647
475
|
this.log.debug('Stopping the frontend...');
|
|
648
|
-
// Remove listeners from the express app
|
|
649
476
|
if (this.expressApp) {
|
|
650
477
|
this.expressApp.removeAllListeners();
|
|
651
478
|
this.expressApp = undefined;
|
|
652
479
|
this.log.debug('Frontend app closed successfully');
|
|
653
480
|
}
|
|
654
|
-
// Close the WebSocket server
|
|
655
481
|
if (this.webSocketServer) {
|
|
656
482
|
this.log.debug('Closing WebSocket server...');
|
|
657
|
-
// Close all active connections
|
|
658
483
|
this.webSocketServer.clients.forEach((client) => {
|
|
659
484
|
if (client.readyState === WebSocket.OPEN) {
|
|
660
485
|
client.close();
|
|
@@ -674,7 +499,6 @@ export class Frontend extends EventEmitter {
|
|
|
674
499
|
this.webSocketServer.removeAllListeners();
|
|
675
500
|
this.webSocketServer = undefined;
|
|
676
501
|
}
|
|
677
|
-
// Close the http server
|
|
678
502
|
if (this.httpServer) {
|
|
679
503
|
this.log.debug('Closing http server...');
|
|
680
504
|
await withTimeout(new Promise((resolve) => {
|
|
@@ -692,7 +516,6 @@ export class Frontend extends EventEmitter {
|
|
|
692
516
|
this.httpServer = undefined;
|
|
693
517
|
this.log.debug('Frontend http server closed successfully');
|
|
694
518
|
}
|
|
695
|
-
// Close the https server
|
|
696
519
|
if (this.httpsServer) {
|
|
697
520
|
this.log.debug('Closing https server...');
|
|
698
521
|
await withTimeout(new Promise((resolve) => {
|
|
@@ -712,7 +535,6 @@ export class Frontend extends EventEmitter {
|
|
|
712
535
|
}
|
|
713
536
|
this.log.debug('Frontend stopped successfully');
|
|
714
537
|
}
|
|
715
|
-
// Function to format bytes to KB, MB, or GB
|
|
716
538
|
formatMemoryUsage = (bytes) => {
|
|
717
539
|
if (bytes >= 1024 ** 3) {
|
|
718
540
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -724,7 +546,6 @@ export class Frontend extends EventEmitter {
|
|
|
724
546
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
725
547
|
}
|
|
726
548
|
};
|
|
727
|
-
// Function to format system uptime with only the most significant unit
|
|
728
549
|
formatOsUpTime = (seconds) => {
|
|
729
550
|
if (seconds >= 86400) {
|
|
730
551
|
const days = Math.floor(seconds / 86400);
|
|
@@ -740,13 +561,7 @@ export class Frontend extends EventEmitter {
|
|
|
740
561
|
}
|
|
741
562
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
742
563
|
};
|
|
743
|
-
/**
|
|
744
|
-
* Retrieves the api settings data.
|
|
745
|
-
*
|
|
746
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
747
|
-
*/
|
|
748
564
|
async getApiSettings() {
|
|
749
|
-
// Update the system information
|
|
750
565
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
751
566
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
752
567
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -755,7 +570,6 @@ export class Frontend extends EventEmitter {
|
|
|
755
570
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
756
571
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
757
572
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
758
|
-
// Update the matterbridge information
|
|
759
573
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
760
574
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
761
575
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -774,12 +588,6 @@ export class Frontend extends EventEmitter {
|
|
|
774
588
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
775
589
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
776
590
|
}
|
|
777
|
-
/**
|
|
778
|
-
* Retrieves the reachable attribute.
|
|
779
|
-
*
|
|
780
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
781
|
-
* @returns {boolean} The reachable attribute.
|
|
782
|
-
*/
|
|
783
591
|
getReachability(device) {
|
|
784
592
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
785
593
|
return false;
|
|
@@ -791,12 +599,6 @@ export class Frontend extends EventEmitter {
|
|
|
791
599
|
return true;
|
|
792
600
|
return false;
|
|
793
601
|
}
|
|
794
|
-
/**
|
|
795
|
-
* Retrieves the power source attribute.
|
|
796
|
-
*
|
|
797
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
798
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
799
|
-
*/
|
|
800
602
|
getPowerSource(endpoint) {
|
|
801
603
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
802
604
|
return undefined;
|
|
@@ -812,21 +614,13 @@ export class Frontend extends EventEmitter {
|
|
|
812
614
|
}
|
|
813
615
|
return;
|
|
814
616
|
};
|
|
815
|
-
// Root endpoint
|
|
816
617
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
817
618
|
return powerSource(endpoint);
|
|
818
|
-
// Child endpoints
|
|
819
619
|
for (const child of endpoint.getChildEndpoints()) {
|
|
820
620
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
821
621
|
return powerSource(child);
|
|
822
622
|
}
|
|
823
623
|
}
|
|
824
|
-
/**
|
|
825
|
-
* Retrieves the commissioned status, matter pairing codes, fabrics and sessions from a given device.
|
|
826
|
-
*
|
|
827
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the data from.
|
|
828
|
-
* @returns {ApiDevicesMatter | undefined} An ApiDevicesMatter object or undefined if not found.
|
|
829
|
-
*/
|
|
830
624
|
getMatterDataFromDevice(device) {
|
|
831
625
|
if (device.mode === 'server' && device.serverNode) {
|
|
832
626
|
return {
|
|
@@ -838,13 +632,6 @@ export class Frontend extends EventEmitter {
|
|
|
838
632
|
};
|
|
839
633
|
}
|
|
840
634
|
}
|
|
841
|
-
/**
|
|
842
|
-
* Retrieves the cluster text description from a given device.
|
|
843
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
844
|
-
*
|
|
845
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
846
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
847
|
-
*/
|
|
848
635
|
getClusterTextFromDevice(device) {
|
|
849
636
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
850
637
|
return '';
|
|
@@ -869,7 +656,6 @@ export class Frontend extends EventEmitter {
|
|
|
869
656
|
let attributes = '';
|
|
870
657
|
let supportedModes = [];
|
|
871
658
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
872
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
873
659
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
874
660
|
return;
|
|
875
661
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -959,14 +745,8 @@ export class Frontend extends EventEmitter {
|
|
|
959
745
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
960
746
|
attributes += `${getUserLabel(device)} `;
|
|
961
747
|
});
|
|
962
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
963
748
|
return attributes.trimStart().trimEnd();
|
|
964
749
|
}
|
|
965
|
-
/**
|
|
966
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
967
|
-
*
|
|
968
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
969
|
-
*/
|
|
970
750
|
getBaseRegisteredPlugins() {
|
|
971
751
|
const baseRegisteredPlugins = [];
|
|
972
752
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -1005,19 +785,11 @@ export class Frontend extends EventEmitter {
|
|
|
1005
785
|
}
|
|
1006
786
|
return baseRegisteredPlugins;
|
|
1007
787
|
}
|
|
1008
|
-
/**
|
|
1009
|
-
* Retrieves the devices from Matterbridge.
|
|
1010
|
-
*
|
|
1011
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1012
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
|
|
1013
|
-
*/
|
|
1014
788
|
async getDevices(pluginName) {
|
|
1015
789
|
const devices = [];
|
|
1016
790
|
for (const device of this.matterbridge.devices.array()) {
|
|
1017
|
-
// Filter by pluginName if provided
|
|
1018
791
|
if (pluginName && pluginName !== device.plugin)
|
|
1019
792
|
continue;
|
|
1020
|
-
// Check if the device has the required properties
|
|
1021
793
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1022
794
|
continue;
|
|
1023
795
|
devices.push({
|
|
@@ -1037,37 +809,22 @@ export class Frontend extends EventEmitter {
|
|
|
1037
809
|
}
|
|
1038
810
|
return devices;
|
|
1039
811
|
}
|
|
1040
|
-
/**
|
|
1041
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1042
|
-
*
|
|
1043
|
-
* Response for /api/clusters
|
|
1044
|
-
*
|
|
1045
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1046
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1047
|
-
* @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1048
|
-
*/
|
|
1049
812
|
getClusters(pluginName, endpointNumber) {
|
|
1050
813
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1051
814
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1052
815
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1053
816
|
return;
|
|
1054
817
|
}
|
|
1055
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1056
|
-
// Get the device types from the main endpoint
|
|
1057
818
|
const deviceTypes = [];
|
|
1058
819
|
const clusters = [];
|
|
1059
820
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1060
821
|
deviceTypes.push(d.deviceType);
|
|
1061
822
|
});
|
|
1062
|
-
// Get the clusters from the main endpoint
|
|
1063
823
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1064
824
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1065
825
|
return;
|
|
1066
826
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1067
827
|
return;
|
|
1068
|
-
// console.log(
|
|
1069
|
-
// `${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}`,
|
|
1070
|
-
// );
|
|
1071
828
|
clusters.push({
|
|
1072
829
|
endpoint: endpoint.number.toString(),
|
|
1073
830
|
id: 'main',
|
|
@@ -1080,18 +837,12 @@ export class Frontend extends EventEmitter {
|
|
|
1080
837
|
attributeLocalValue: attributeValue,
|
|
1081
838
|
});
|
|
1082
839
|
});
|
|
1083
|
-
// Get the child endpoints
|
|
1084
840
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1085
|
-
// if (childEndpoints.length === 0) {
|
|
1086
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1087
|
-
// }
|
|
1088
841
|
childEndpoints.forEach((childEndpoint) => {
|
|
1089
842
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1090
843
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1091
844
|
return;
|
|
1092
845
|
}
|
|
1093
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1094
|
-
// Get the device types of the child endpoint
|
|
1095
846
|
const deviceTypes = [];
|
|
1096
847
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1097
848
|
deviceTypes.push(d.deviceType);
|
|
@@ -1101,12 +852,9 @@ export class Frontend extends EventEmitter {
|
|
|
1101
852
|
return;
|
|
1102
853
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1103
854
|
return;
|
|
1104
|
-
// console.log(
|
|
1105
|
-
// `${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}`,
|
|
1106
|
-
// );
|
|
1107
855
|
clusters.push({
|
|
1108
856
|
endpoint: childEndpoint.number.toString(),
|
|
1109
|
-
id: childEndpoint.maybeId ?? 'null',
|
|
857
|
+
id: childEndpoint.maybeId ?? 'null',
|
|
1110
858
|
deviceTypes,
|
|
1111
859
|
clusterName: capitalizeFirstLetter(clusterName),
|
|
1112
860
|
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
@@ -1119,13 +867,6 @@ export class Frontend extends EventEmitter {
|
|
|
1119
867
|
});
|
|
1120
868
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
|
|
1121
869
|
}
|
|
1122
|
-
/**
|
|
1123
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1124
|
-
*
|
|
1125
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1126
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1127
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1128
|
-
*/
|
|
1129
870
|
async wsMessageHandler(client, message) {
|
|
1130
871
|
let data;
|
|
1131
872
|
try {
|
|
@@ -1172,41 +913,32 @@ export class Frontend extends EventEmitter {
|
|
|
1172
913
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1173
914
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1174
915
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1175
|
-
// The install comes from InstallPlugins
|
|
1176
916
|
this.matterbridge.plugins
|
|
1177
917
|
.add(packageName)
|
|
1178
918
|
.then((plugin) => {
|
|
1179
919
|
if (plugin) {
|
|
1180
|
-
// The plugin is not registered
|
|
1181
920
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1182
921
|
this.matterbridge.plugins
|
|
1183
922
|
.load(plugin, true, 'The plugin has been added', true)
|
|
1184
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1185
923
|
.then(() => {
|
|
1186
924
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
1187
925
|
this.wssSendRefreshRequired('plugins');
|
|
1188
926
|
return;
|
|
1189
927
|
})
|
|
1190
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1191
928
|
.catch((_error) => {
|
|
1192
|
-
//
|
|
1193
929
|
});
|
|
1194
930
|
}
|
|
1195
931
|
else {
|
|
1196
|
-
// The plugin is already registered
|
|
1197
932
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1198
933
|
this.wssSendRefreshRequired('plugins');
|
|
1199
934
|
this.wssSendRestartRequired();
|
|
1200
935
|
}
|
|
1201
936
|
return;
|
|
1202
937
|
})
|
|
1203
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1204
938
|
.catch((_error) => {
|
|
1205
|
-
//
|
|
1206
939
|
});
|
|
1207
940
|
}
|
|
1208
941
|
else {
|
|
1209
|
-
// The package is matterbridge
|
|
1210
942
|
if (this.matterbridge.restartMode !== '') {
|
|
1211
943
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1212
944
|
this.matterbridge.shutdownProcess();
|
|
@@ -1229,7 +961,6 @@ export class Frontend extends EventEmitter {
|
|
|
1229
961
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1230
962
|
return;
|
|
1231
963
|
}
|
|
1232
|
-
// The package is a plugin
|
|
1233
964
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1234
965
|
if (plugin) {
|
|
1235
966
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
@@ -1238,7 +969,6 @@ export class Frontend extends EventEmitter {
|
|
|
1238
969
|
this.wssSendRefreshRequired('plugins');
|
|
1239
970
|
this.wssSendRefreshRequired('devices');
|
|
1240
971
|
}
|
|
1241
|
-
// Uninstall the package
|
|
1242
972
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1243
973
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
1244
974
|
spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1279,7 +1009,6 @@ export class Frontend extends EventEmitter {
|
|
|
1279
1009
|
return;
|
|
1280
1010
|
})
|
|
1281
1011
|
.catch((_error) => {
|
|
1282
|
-
//
|
|
1283
1012
|
});
|
|
1284
1013
|
}
|
|
1285
1014
|
else {
|
|
@@ -1326,7 +1055,6 @@ export class Frontend extends EventEmitter {
|
|
|
1326
1055
|
return;
|
|
1327
1056
|
})
|
|
1328
1057
|
.catch((_error) => {
|
|
1329
|
-
//
|
|
1330
1058
|
});
|
|
1331
1059
|
}
|
|
1332
1060
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1561,22 +1289,22 @@ export class Frontend extends EventEmitter {
|
|
|
1561
1289
|
if (isValidString(data.params.value, 4)) {
|
|
1562
1290
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1563
1291
|
if (data.params.value === 'Debug') {
|
|
1564
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1292
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1565
1293
|
}
|
|
1566
1294
|
else if (data.params.value === 'Info') {
|
|
1567
|
-
await this.matterbridge.setLogLevel("info"
|
|
1295
|
+
await this.matterbridge.setLogLevel("info");
|
|
1568
1296
|
}
|
|
1569
1297
|
else if (data.params.value === 'Notice') {
|
|
1570
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1298
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1571
1299
|
}
|
|
1572
1300
|
else if (data.params.value === 'Warn') {
|
|
1573
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1301
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1574
1302
|
}
|
|
1575
1303
|
else if (data.params.value === 'Error') {
|
|
1576
|
-
await this.matterbridge.setLogLevel("error"
|
|
1304
|
+
await this.matterbridge.setLogLevel("error");
|
|
1577
1305
|
}
|
|
1578
1306
|
else if (data.params.value === 'Fatal') {
|
|
1579
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1307
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1580
1308
|
}
|
|
1581
1309
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1582
1310
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1587,7 +1315,6 @@ export class Frontend extends EventEmitter {
|
|
|
1587
1315
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1588
1316
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1589
1317
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1590
|
-
// Create the file logger for matterbridge
|
|
1591
1318
|
if (data.params.value)
|
|
1592
1319
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1593
1320
|
else
|
|
@@ -1634,7 +1361,6 @@ export class Frontend extends EventEmitter {
|
|
|
1634
1361
|
});
|
|
1635
1362
|
}
|
|
1636
1363
|
catch (error) {
|
|
1637
|
-
/* istanbul ignore next */
|
|
1638
1364
|
this.log.debug(`Error adding the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1639
1365
|
}
|
|
1640
1366
|
}
|
|
@@ -1643,7 +1369,6 @@ export class Frontend extends EventEmitter {
|
|
|
1643
1369
|
Logger.removeLogger('matterfilelogger');
|
|
1644
1370
|
}
|
|
1645
1371
|
catch (error) {
|
|
1646
|
-
/* istanbul ignore next */
|
|
1647
1372
|
this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1648
1373
|
}
|
|
1649
1374
|
}
|
|
@@ -1756,19 +1481,15 @@ export class Frontend extends EventEmitter {
|
|
|
1756
1481
|
return;
|
|
1757
1482
|
}
|
|
1758
1483
|
const config = plugin.configJson;
|
|
1759
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1760
1484
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1761
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1762
1485
|
if (select === 'serial')
|
|
1763
1486
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1764
1487
|
if (select === 'name')
|
|
1765
1488
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1766
1489
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1767
|
-
// Remove postfix from the serial if it exists
|
|
1768
1490
|
if (config.postfix) {
|
|
1769
1491
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1770
1492
|
}
|
|
1771
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1772
1493
|
if (isValidArray(config.whiteList, 1)) {
|
|
1773
1494
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1774
1495
|
config.whiteList.push(data.params.serial);
|
|
@@ -1777,7 +1498,6 @@ export class Frontend extends EventEmitter {
|
|
|
1777
1498
|
config.whiteList.push(data.params.name);
|
|
1778
1499
|
}
|
|
1779
1500
|
}
|
|
1780
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1781
1501
|
if (isValidArray(config.blackList, 1)) {
|
|
1782
1502
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1783
1503
|
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
@@ -1808,9 +1528,7 @@ export class Frontend extends EventEmitter {
|
|
|
1808
1528
|
return;
|
|
1809
1529
|
}
|
|
1810
1530
|
const config = plugin.configJson;
|
|
1811
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1812
1531
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1813
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1814
1532
|
if (select === 'serial')
|
|
1815
1533
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1816
1534
|
if (select === 'name')
|
|
@@ -1819,7 +1537,6 @@ export class Frontend extends EventEmitter {
|
|
|
1819
1537
|
if (config.postfix) {
|
|
1820
1538
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1821
1539
|
}
|
|
1822
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1823
1540
|
if (isValidArray(config.whiteList, 1)) {
|
|
1824
1541
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1825
1542
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
@@ -1828,7 +1545,6 @@ export class Frontend extends EventEmitter {
|
|
|
1828
1545
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1829
1546
|
}
|
|
1830
1547
|
}
|
|
1831
|
-
// Add the serial to the blackList
|
|
1832
1548
|
if (isValidArray(config.blackList)) {
|
|
1833
1549
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1834
1550
|
config.blackList.push(data.params.serial);
|
|
@@ -1862,230 +1578,114 @@ export class Frontend extends EventEmitter {
|
|
|
1862
1578
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1863
1579
|
}
|
|
1864
1580
|
}
|
|
1865
|
-
/**
|
|
1866
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1867
|
-
*
|
|
1868
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1869
|
-
* @param {string} time - The time string of the message
|
|
1870
|
-
* @param {string} name - The logger name of the message
|
|
1871
|
-
* @param {string} message - The content of the message.
|
|
1872
|
-
*
|
|
1873
|
-
* @remarks
|
|
1874
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
1875
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
1876
|
-
* The function sends the message to all connected clients.
|
|
1877
|
-
*/
|
|
1878
1581
|
wssSendMessage(level, time, name, message) {
|
|
1879
1582
|
if (!level || !time || !name || !message)
|
|
1880
1583
|
return;
|
|
1881
|
-
// Remove ANSI escape codes from the message
|
|
1882
|
-
// eslint-disable-next-line no-control-regex
|
|
1883
1584
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1884
|
-
// Remove leading asterisks from the message
|
|
1885
1585
|
message = message.replace(/^\*+/, '');
|
|
1886
|
-
// Replace all occurrences of \t and \n
|
|
1887
1586
|
message = message.replace(/[\t\n]/g, '');
|
|
1888
|
-
// Remove non-printable characters
|
|
1889
|
-
// eslint-disable-next-line no-control-regex
|
|
1890
1587
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1891
|
-
// Replace all occurrences of \" with "
|
|
1892
1588
|
message = message.replace(/\\"/g, '"');
|
|
1893
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1894
1589
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1895
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1896
1590
|
const maxContinuousLength = 100;
|
|
1897
1591
|
const keepStartLength = 20;
|
|
1898
1592
|
const keepEndLength = 20;
|
|
1899
|
-
// Split the message into words
|
|
1900
1593
|
message = message
|
|
1901
1594
|
.split(' ')
|
|
1902
1595
|
.map((word) => {
|
|
1903
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1904
1596
|
if (word.length > maxContinuousLength) {
|
|
1905
1597
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1906
1598
|
}
|
|
1907
1599
|
return word;
|
|
1908
1600
|
})
|
|
1909
1601
|
.join(' ');
|
|
1910
|
-
// Send the message to all connected clients
|
|
1911
1602
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1912
1603
|
if (client.readyState === WebSocket.OPEN) {
|
|
1913
1604
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1914
1605
|
}
|
|
1915
1606
|
});
|
|
1916
1607
|
}
|
|
1917
|
-
/**
|
|
1918
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1919
|
-
*
|
|
1920
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
1921
|
-
* possible values:
|
|
1922
|
-
* - 'matterbridgeLatestVersion'
|
|
1923
|
-
* - 'matterbridgeAdvertise'
|
|
1924
|
-
* - 'online'
|
|
1925
|
-
* - 'offline'
|
|
1926
|
-
* - 'reachability'
|
|
1927
|
-
* - 'settings'
|
|
1928
|
-
* - 'plugins'
|
|
1929
|
-
* - 'pluginsRestart'
|
|
1930
|
-
* - 'devices'
|
|
1931
|
-
* - 'fabrics'
|
|
1932
|
-
* - 'sessions'
|
|
1933
|
-
*/
|
|
1934
1608
|
wssSendRefreshRequired(changed = null) {
|
|
1935
1609
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1936
|
-
// Send the message to all connected clients
|
|
1937
1610
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1938
1611
|
if (client.readyState === WebSocket.OPEN) {
|
|
1939
1612
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1940
1613
|
}
|
|
1941
1614
|
});
|
|
1942
1615
|
}
|
|
1943
|
-
/**
|
|
1944
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1945
|
-
*
|
|
1946
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients.
|
|
1947
|
-
*/
|
|
1948
1616
|
wssSendRestartRequired(snackbar = true) {
|
|
1949
1617
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1950
1618
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1951
1619
|
if (snackbar === true)
|
|
1952
1620
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1953
|
-
// Send the message to all connected clients
|
|
1954
1621
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1955
1622
|
if (client.readyState === WebSocket.OPEN) {
|
|
1956
1623
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1957
1624
|
}
|
|
1958
1625
|
});
|
|
1959
1626
|
}
|
|
1960
|
-
/**
|
|
1961
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
1962
|
-
*
|
|
1963
|
-
*/
|
|
1964
1627
|
wssSendUpdateRequired() {
|
|
1965
1628
|
this.log.debug('Sending an update required message to all connected clients');
|
|
1966
1629
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
1967
|
-
// Send the message to all connected clients
|
|
1968
1630
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1969
1631
|
if (client.readyState === WebSocket.OPEN) {
|
|
1970
1632
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
1971
1633
|
}
|
|
1972
1634
|
});
|
|
1973
1635
|
}
|
|
1974
|
-
/**
|
|
1975
|
-
* Sends a cpu update message to all connected clients.
|
|
1976
|
-
*
|
|
1977
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
1978
|
-
*/
|
|
1979
1636
|
wssSendCpuUpdate(cpuUsage) {
|
|
1980
1637
|
if (hasParameter('debug'))
|
|
1981
1638
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1982
|
-
// Send the message to all connected clients
|
|
1983
1639
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1984
1640
|
if (client.readyState === WebSocket.OPEN) {
|
|
1985
1641
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
1986
1642
|
}
|
|
1987
1643
|
});
|
|
1988
1644
|
}
|
|
1989
|
-
/**
|
|
1990
|
-
* Sends a memory update message to all connected clients.
|
|
1991
|
-
*
|
|
1992
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
1993
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
1994
|
-
* @param {string} rss - The resident set size in bytes.
|
|
1995
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
1996
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
1997
|
-
* @param {string} external - The external memory in bytes.
|
|
1998
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
1999
|
-
*/
|
|
2000
1645
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2001
1646
|
if (hasParameter('debug'))
|
|
2002
1647
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2003
|
-
// Send the message to all connected clients
|
|
2004
1648
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2005
1649
|
if (client.readyState === WebSocket.OPEN) {
|
|
2006
1650
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
2007
1651
|
}
|
|
2008
1652
|
});
|
|
2009
1653
|
}
|
|
2010
|
-
/**
|
|
2011
|
-
* Sends an uptime update message to all connected clients.
|
|
2012
|
-
*
|
|
2013
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2014
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2015
|
-
*/
|
|
2016
1654
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2017
1655
|
if (hasParameter('debug'))
|
|
2018
1656
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2019
|
-
// Send the message to all connected clients
|
|
2020
1657
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2021
1658
|
if (client.readyState === WebSocket.OPEN) {
|
|
2022
1659
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
2023
1660
|
}
|
|
2024
1661
|
});
|
|
2025
1662
|
}
|
|
2026
|
-
/**
|
|
2027
|
-
* Sends an open snackbar message to all connected clients.
|
|
2028
|
-
*
|
|
2029
|
-
* @param {string} message - The message to send.
|
|
2030
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
2031
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
2032
|
-
*/
|
|
2033
1663
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2034
1664
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2035
|
-
// Send the message to all connected clients
|
|
2036
1665
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2037
1666
|
if (client.readyState === WebSocket.OPEN) {
|
|
2038
1667
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
2039
1668
|
}
|
|
2040
1669
|
});
|
|
2041
1670
|
}
|
|
2042
|
-
/**
|
|
2043
|
-
* Sends a close snackbar message to all connected clients.
|
|
2044
|
-
*
|
|
2045
|
-
* @param {string} message - The message to send.
|
|
2046
|
-
*/
|
|
2047
1671
|
wssSendCloseSnackbarMessage(message) {
|
|
2048
1672
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2049
|
-
// Send the message to all connected clients
|
|
2050
1673
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2051
1674
|
if (client.readyState === WebSocket.OPEN) {
|
|
2052
1675
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
2053
1676
|
}
|
|
2054
1677
|
});
|
|
2055
1678
|
}
|
|
2056
|
-
/**
|
|
2057
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2058
|
-
*
|
|
2059
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2060
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2061
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2062
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2063
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2064
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2065
|
-
*
|
|
2066
|
-
* @remarks
|
|
2067
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2068
|
-
* with the updated attribute information.
|
|
2069
|
-
*/
|
|
2070
1679
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
2071
1680
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2072
|
-
// Send the message to all connected clients
|
|
2073
1681
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2074
1682
|
if (client.readyState === WebSocket.OPEN) {
|
|
2075
1683
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
2076
1684
|
}
|
|
2077
1685
|
});
|
|
2078
1686
|
}
|
|
2079
|
-
/**
|
|
2080
|
-
* Sends a message to all connected clients.
|
|
2081
|
-
*
|
|
2082
|
-
* @param {number} id - The message id.
|
|
2083
|
-
* @param {string} method - The message method.
|
|
2084
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
2085
|
-
*/
|
|
2086
1687
|
wssBroadcastMessage(id, method, params) {
|
|
2087
1688
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
2088
|
-
// Send the message to all connected clients
|
|
2089
1689
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2090
1690
|
if (client.readyState === WebSocket.OPEN) {
|
|
2091
1691
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -2093,4 +1693,3 @@ export class Frontend extends EventEmitter {
|
|
|
2093
1693
|
});
|
|
2094
1694
|
}
|
|
2095
1695
|
}
|
|
2096
|
-
//# sourceMappingURL=frontend.js.map
|