matterbridge 3.1.1 → 3.1.2-dev-20250705-7da1eac
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 -0
- package/README-DEV.md +6 -2
- 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 +8 -84
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/waterHeater.js +2 -82
- package/dist/frontend.js +19 -420
- package/dist/globalMatterbridge.js +0 -47
- package/dist/helpers.js +0 -53
- package/dist/index.js +1 -39
- 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 -802
- 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 +42 -1027
- 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 +2 -2
- 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 -103
- 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 -302
- 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 -41
- 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 -1179
- 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, wr, 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 } 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.matterbrideLoggerFile)}`);
|
|
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.matterbrideLoggerFile), fs.constants.F_OK);
|
|
483
334
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'utf8');
|
|
484
335
|
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), data, 'utf-8');
|
|
@@ -488,20 +339,16 @@ export class Frontend extends EventEmitter {
|
|
|
488
339
|
this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
|
|
489
340
|
}
|
|
490
341
|
res.type('text/plain');
|
|
491
|
-
// res.download(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
|
|
492
342
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
|
|
493
|
-
/* istanbul ignore if */
|
|
494
343
|
if (error) {
|
|
495
344
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
496
345
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
497
346
|
}
|
|
498
347
|
});
|
|
499
348
|
});
|
|
500
|
-
// Endpoint to download the matter log
|
|
501
349
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
502
350
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
|
|
503
351
|
try {
|
|
504
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
505
352
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
506
353
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'utf8');
|
|
507
354
|
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), data, 'utf-8');
|
|
@@ -512,18 +359,15 @@ export class Frontend extends EventEmitter {
|
|
|
512
359
|
}
|
|
513
360
|
res.type('text/plain');
|
|
514
361
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
|
|
515
|
-
/* istanbul ignore if */
|
|
516
362
|
if (error) {
|
|
517
363
|
this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
518
364
|
res.status(500).send('Error downloading the matter log file');
|
|
519
365
|
}
|
|
520
366
|
});
|
|
521
367
|
});
|
|
522
|
-
// Endpoint to download the shelly log
|
|
523
368
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
524
369
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
525
370
|
try {
|
|
526
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
527
371
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
|
|
528
372
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
|
|
529
373
|
await fs.writeFile(path.join(os.tmpdir(), 'shelly.log'), data, 'utf-8');
|
|
@@ -534,14 +378,12 @@ export class Frontend extends EventEmitter {
|
|
|
534
378
|
}
|
|
535
379
|
res.type('text/plain');
|
|
536
380
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
537
|
-
/* istanbul ignore if */
|
|
538
381
|
if (error) {
|
|
539
382
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
540
383
|
res.status(500).send('Error downloading Shelly system log file');
|
|
541
384
|
}
|
|
542
385
|
});
|
|
543
386
|
});
|
|
544
|
-
// Endpoint to download the matterbridge storage directory
|
|
545
387
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
546
388
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
547
389
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -552,7 +394,6 @@ export class Frontend extends EventEmitter {
|
|
|
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));
|
|
@@ -563,7 +404,6 @@ export class Frontend extends EventEmitter {
|
|
|
563
404
|
}
|
|
564
405
|
});
|
|
565
406
|
});
|
|
566
|
-
// Endpoint to download the matterbridge plugin directory
|
|
567
407
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
568
408
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
569
409
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -574,7 +414,6 @@ export class Frontend extends EventEmitter {
|
|
|
574
414
|
}
|
|
575
415
|
});
|
|
576
416
|
});
|
|
577
|
-
// Endpoint to download the matterbridge plugin config files
|
|
578
417
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
579
418
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
580
419
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
@@ -585,7 +424,6 @@ export class Frontend extends EventEmitter {
|
|
|
585
424
|
}
|
|
586
425
|
});
|
|
587
426
|
});
|
|
588
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
589
427
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
590
428
|
this.log.debug('The frontend sent /api/download-backup');
|
|
591
429
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -595,7 +433,6 @@ export class Frontend extends EventEmitter {
|
|
|
595
433
|
}
|
|
596
434
|
});
|
|
597
435
|
});
|
|
598
|
-
// Endpoint to upload a package
|
|
599
436
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
600
437
|
const { filename } = req.body;
|
|
601
438
|
const file = req.file;
|
|
@@ -605,13 +442,10 @@ export class Frontend extends EventEmitter {
|
|
|
605
442
|
return;
|
|
606
443
|
}
|
|
607
444
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
608
|
-
// Define the path where the plugin file will be saved
|
|
609
445
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
610
446
|
try {
|
|
611
|
-
// Move the uploaded file to the specified path
|
|
612
447
|
await fs.rename(file.path, filePath);
|
|
613
448
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
614
|
-
// Install the plugin package
|
|
615
449
|
if (filename.endsWith('.tgz')) {
|
|
616
450
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
617
451
|
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
@@ -631,7 +465,6 @@ export class Frontend extends EventEmitter {
|
|
|
631
465
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
632
466
|
}
|
|
633
467
|
});
|
|
634
|
-
// Fallback for routing (must be the last route)
|
|
635
468
|
this.expressApp.use((req, res) => {
|
|
636
469
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
|
|
637
470
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -640,15 +473,13 @@ export class Frontend extends EventEmitter {
|
|
|
640
473
|
}
|
|
641
474
|
async stop() {
|
|
642
475
|
this.log.debug('Stopping the frontend...');
|
|
643
|
-
// Remove listeners from the express app
|
|
644
476
|
if (this.expressApp) {
|
|
645
477
|
this.expressApp.removeAllListeners();
|
|
646
478
|
this.expressApp = undefined;
|
|
647
479
|
this.log.debug('Frontend app closed successfully');
|
|
648
480
|
}
|
|
649
|
-
// Close the WebSocket server
|
|
650
481
|
if (this.webSocketServer) {
|
|
651
|
-
|
|
482
|
+
this.log.debug('Closing WebSocket server...');
|
|
652
483
|
this.webSocketServer.clients.forEach((client) => {
|
|
653
484
|
if (client.readyState === WebSocket.OPEN) {
|
|
654
485
|
client.close();
|
|
@@ -668,8 +499,8 @@ export class Frontend extends EventEmitter {
|
|
|
668
499
|
this.webSocketServer.removeAllListeners();
|
|
669
500
|
this.webSocketServer = undefined;
|
|
670
501
|
}
|
|
671
|
-
// Close the http server
|
|
672
502
|
if (this.httpServer) {
|
|
503
|
+
this.log.debug('Closing http server...');
|
|
673
504
|
await withTimeout(new Promise((resolve) => {
|
|
674
505
|
this.httpServer?.close((error) => {
|
|
675
506
|
if (error) {
|
|
@@ -685,8 +516,8 @@ export class Frontend extends EventEmitter {
|
|
|
685
516
|
this.httpServer = undefined;
|
|
686
517
|
this.log.debug('Frontend http server closed successfully');
|
|
687
518
|
}
|
|
688
|
-
// Close the https server
|
|
689
519
|
if (this.httpsServer) {
|
|
520
|
+
this.log.debug('Closing https server...');
|
|
690
521
|
await withTimeout(new Promise((resolve) => {
|
|
691
522
|
this.httpsServer?.close((error) => {
|
|
692
523
|
if (error) {
|
|
@@ -704,7 +535,6 @@ export class Frontend extends EventEmitter {
|
|
|
704
535
|
}
|
|
705
536
|
this.log.debug('Frontend stopped successfully');
|
|
706
537
|
}
|
|
707
|
-
// Function to format bytes to KB, MB, or GB
|
|
708
538
|
formatMemoryUsage = (bytes) => {
|
|
709
539
|
if (bytes >= 1024 ** 3) {
|
|
710
540
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -716,7 +546,6 @@ export class Frontend extends EventEmitter {
|
|
|
716
546
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
717
547
|
}
|
|
718
548
|
};
|
|
719
|
-
// Function to format system uptime with only the most significant unit
|
|
720
549
|
formatOsUpTime = (seconds) => {
|
|
721
550
|
if (seconds >= 86400) {
|
|
722
551
|
const days = Math.floor(seconds / 86400);
|
|
@@ -732,13 +561,7 @@ export class Frontend extends EventEmitter {
|
|
|
732
561
|
}
|
|
733
562
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
734
563
|
};
|
|
735
|
-
/**
|
|
736
|
-
* Retrieves the api settings data.
|
|
737
|
-
*
|
|
738
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
739
|
-
*/
|
|
740
564
|
async getApiSettings() {
|
|
741
|
-
// Update the system information
|
|
742
565
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
743
566
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
744
567
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -747,7 +570,6 @@ export class Frontend extends EventEmitter {
|
|
|
747
570
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
748
571
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
749
572
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
750
|
-
// Update the matterbridge information
|
|
751
573
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
752
574
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
753
575
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -766,12 +588,6 @@ export class Frontend extends EventEmitter {
|
|
|
766
588
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
767
589
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
768
590
|
}
|
|
769
|
-
/**
|
|
770
|
-
* Retrieves the reachable attribute.
|
|
771
|
-
*
|
|
772
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
773
|
-
* @returns {boolean} The reachable attribute.
|
|
774
|
-
*/
|
|
775
591
|
getReachability(device) {
|
|
776
592
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
777
593
|
return false;
|
|
@@ -783,12 +599,6 @@ export class Frontend extends EventEmitter {
|
|
|
783
599
|
return true;
|
|
784
600
|
return false;
|
|
785
601
|
}
|
|
786
|
-
/**
|
|
787
|
-
* Retrieves the power source attribute.
|
|
788
|
-
*
|
|
789
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice object.
|
|
790
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
791
|
-
*/
|
|
792
602
|
getPowerSource(endpoint) {
|
|
793
603
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
794
604
|
return undefined;
|
|
@@ -804,21 +614,13 @@ export class Frontend extends EventEmitter {
|
|
|
804
614
|
}
|
|
805
615
|
return;
|
|
806
616
|
};
|
|
807
|
-
// Root endpoint
|
|
808
617
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
809
618
|
return powerSource(endpoint);
|
|
810
|
-
// Child endpoints
|
|
811
619
|
for (const child of endpoint.getChildEndpoints()) {
|
|
812
620
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
813
621
|
return powerSource(child);
|
|
814
622
|
}
|
|
815
623
|
}
|
|
816
|
-
/**
|
|
817
|
-
* Retrieves the matter pairing code from a given device.
|
|
818
|
-
*
|
|
819
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object to retrieve the QR pairing code from.
|
|
820
|
-
* @returns {ApiDevicesMatter | undefined} An ApiDevicesMatter object or undefined if not found.
|
|
821
|
-
*/
|
|
822
624
|
getMatterDataFromDevice(device) {
|
|
823
625
|
if (device.mode === 'server' && device.serverNode && device.serverContext) {
|
|
824
626
|
return {
|
|
@@ -830,12 +632,6 @@ export class Frontend extends EventEmitter {
|
|
|
830
632
|
};
|
|
831
633
|
}
|
|
832
634
|
}
|
|
833
|
-
/**
|
|
834
|
-
* Retrieves the cluster text description from a given device.
|
|
835
|
-
*
|
|
836
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
837
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
838
|
-
*/
|
|
839
635
|
getClusterTextFromDevice(device) {
|
|
840
636
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
841
637
|
return '';
|
|
@@ -876,19 +672,7 @@ export class Frontend extends EventEmitter {
|
|
|
876
672
|
};
|
|
877
673
|
let attributes = '';
|
|
878
674
|
let supportedModes = [];
|
|
879
|
-
/*
|
|
880
|
-
Object.keys(device.behaviors.supported).forEach((clusterName) => {
|
|
881
|
-
const clusterBehavior = device.behaviors.supported[lowercaseFirstLetter(clusterName)] as ClusterBehavior.Type | undefined;
|
|
882
|
-
// console.log(`Device: ${device.deviceName} => Cluster: ${clusterName} Behavior: ${clusterBehavior?.id}`, clusterBehavior);
|
|
883
|
-
if (clusterBehavior && clusterBehavior.cluster && clusterBehavior.cluster.attributes) {
|
|
884
|
-
Object.entries(clusterBehavior.cluster.attributes).forEach(([attributeName, attribute]) => {
|
|
885
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName} Attribute: ${attributeName}`, attribute);
|
|
886
|
-
});
|
|
887
|
-
}
|
|
888
|
-
});
|
|
889
|
-
*/
|
|
890
675
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
891
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
892
676
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
893
677
|
return;
|
|
894
678
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -980,14 +764,8 @@ export class Frontend extends EventEmitter {
|
|
|
980
764
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
981
765
|
attributes += `${getUserLabel(device)} `;
|
|
982
766
|
});
|
|
983
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
984
767
|
return attributes.trimStart().trimEnd();
|
|
985
768
|
}
|
|
986
|
-
/**
|
|
987
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
988
|
-
*
|
|
989
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
990
|
-
*/
|
|
991
769
|
getBaseRegisteredPlugins() {
|
|
992
770
|
const baseRegisteredPlugins = [];
|
|
993
771
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -1026,19 +804,11 @@ export class Frontend extends EventEmitter {
|
|
|
1026
804
|
}
|
|
1027
805
|
return baseRegisteredPlugins;
|
|
1028
806
|
}
|
|
1029
|
-
/**
|
|
1030
|
-
* Retrieves the devices from Matterbridge.
|
|
1031
|
-
*
|
|
1032
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1033
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
|
|
1034
|
-
*/
|
|
1035
807
|
async getDevices(pluginName) {
|
|
1036
808
|
const devices = [];
|
|
1037
809
|
for (const device of this.matterbridge.devices.array()) {
|
|
1038
|
-
// Filter by pluginName if provided
|
|
1039
810
|
if (pluginName && pluginName !== device.plugin)
|
|
1040
811
|
continue;
|
|
1041
|
-
// Check if the device has the required properties
|
|
1042
812
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1043
813
|
continue;
|
|
1044
814
|
devices.push({
|
|
@@ -1058,37 +828,22 @@ export class Frontend extends EventEmitter {
|
|
|
1058
828
|
}
|
|
1059
829
|
return devices;
|
|
1060
830
|
}
|
|
1061
|
-
/**
|
|
1062
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1063
|
-
*
|
|
1064
|
-
* Response for /api/clusters
|
|
1065
|
-
*
|
|
1066
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1067
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1068
|
-
* @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1069
|
-
*/
|
|
1070
831
|
getClusters(pluginName, endpointNumber) {
|
|
1071
832
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1072
833
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1073
834
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1074
835
|
return;
|
|
1075
836
|
}
|
|
1076
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1077
|
-
// Get the device types from the main endpoint
|
|
1078
837
|
const deviceTypes = [];
|
|
1079
838
|
const clusters = [];
|
|
1080
839
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1081
840
|
deviceTypes.push(d.deviceType);
|
|
1082
841
|
});
|
|
1083
|
-
// Get the clusters from the main endpoint
|
|
1084
842
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1085
843
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1086
844
|
return;
|
|
1087
845
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1088
846
|
return;
|
|
1089
|
-
// console.log(
|
|
1090
|
-
// `${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}`,
|
|
1091
|
-
// );
|
|
1092
847
|
clusters.push({
|
|
1093
848
|
endpoint: endpoint.number.toString(),
|
|
1094
849
|
id: 'main',
|
|
@@ -1101,18 +856,12 @@ export class Frontend extends EventEmitter {
|
|
|
1101
856
|
attributeLocalValue: attributeValue,
|
|
1102
857
|
});
|
|
1103
858
|
});
|
|
1104
|
-
// Get the child endpoints
|
|
1105
859
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1106
|
-
// if (childEndpoints.length === 0) {
|
|
1107
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1108
|
-
// }
|
|
1109
860
|
childEndpoints.forEach((childEndpoint) => {
|
|
1110
861
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1111
862
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1112
863
|
return;
|
|
1113
864
|
}
|
|
1114
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1115
|
-
// Get the device types of the child endpoint
|
|
1116
865
|
const deviceTypes = [];
|
|
1117
866
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1118
867
|
deviceTypes.push(d.deviceType);
|
|
@@ -1122,12 +871,9 @@ export class Frontend extends EventEmitter {
|
|
|
1122
871
|
return;
|
|
1123
872
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1124
873
|
return;
|
|
1125
|
-
// console.log(
|
|
1126
|
-
// `${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}`,
|
|
1127
|
-
// );
|
|
1128
874
|
clusters.push({
|
|
1129
875
|
endpoint: childEndpoint.number.toString(),
|
|
1130
|
-
id: childEndpoint.maybeId ?? 'null',
|
|
876
|
+
id: childEndpoint.maybeId ?? 'null',
|
|
1131
877
|
deviceTypes,
|
|
1132
878
|
clusterName: capitalizeFirstLetter(clusterName),
|
|
1133
879
|
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
@@ -1140,13 +886,6 @@ export class Frontend extends EventEmitter {
|
|
|
1140
886
|
});
|
|
1141
887
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
|
|
1142
888
|
}
|
|
1143
|
-
/**
|
|
1144
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1145
|
-
*
|
|
1146
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1147
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1148
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1149
|
-
*/
|
|
1150
889
|
async wsMessageHandler(client, message) {
|
|
1151
890
|
let data;
|
|
1152
891
|
try {
|
|
@@ -1193,41 +932,32 @@ export class Frontend extends EventEmitter {
|
|
|
1193
932
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1194
933
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1195
934
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1196
|
-
// The install comes from InstallPlugins
|
|
1197
935
|
this.matterbridge.plugins
|
|
1198
936
|
.add(packageName)
|
|
1199
937
|
.then((plugin) => {
|
|
1200
938
|
if (plugin) {
|
|
1201
|
-
// The plugin is not registered
|
|
1202
939
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1203
940
|
this.matterbridge.plugins
|
|
1204
941
|
.load(plugin, true, 'The plugin has been added', true)
|
|
1205
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1206
942
|
.then(() => {
|
|
1207
943
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
1208
944
|
this.wssSendRefreshRequired('plugins');
|
|
1209
945
|
return;
|
|
1210
946
|
})
|
|
1211
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1212
947
|
.catch((_error) => {
|
|
1213
|
-
//
|
|
1214
948
|
});
|
|
1215
949
|
}
|
|
1216
950
|
else {
|
|
1217
|
-
// The plugin is already registered
|
|
1218
951
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1219
952
|
this.wssSendRefreshRequired('plugins');
|
|
1220
953
|
this.wssSendRestartRequired();
|
|
1221
954
|
}
|
|
1222
955
|
return;
|
|
1223
956
|
})
|
|
1224
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1225
957
|
.catch((_error) => {
|
|
1226
|
-
//
|
|
1227
958
|
});
|
|
1228
959
|
}
|
|
1229
960
|
else {
|
|
1230
|
-
// The package is matterbridge
|
|
1231
961
|
if (this.matterbridge.restartMode !== '') {
|
|
1232
962
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1233
963
|
this.matterbridge.shutdownProcess();
|
|
@@ -1250,7 +980,6 @@ export class Frontend extends EventEmitter {
|
|
|
1250
980
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1251
981
|
return;
|
|
1252
982
|
}
|
|
1253
|
-
// The package is a plugin
|
|
1254
983
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1255
984
|
if (plugin) {
|
|
1256
985
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
@@ -1259,7 +988,6 @@ export class Frontend extends EventEmitter {
|
|
|
1259
988
|
this.wssSendRefreshRequired('plugins');
|
|
1260
989
|
this.wssSendRefreshRequired('devices');
|
|
1261
990
|
}
|
|
1262
|
-
// Uninstall the package
|
|
1263
991
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1264
992
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
1265
993
|
spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1300,7 +1028,6 @@ export class Frontend extends EventEmitter {
|
|
|
1300
1028
|
return;
|
|
1301
1029
|
})
|
|
1302
1030
|
.catch((_error) => {
|
|
1303
|
-
//
|
|
1304
1031
|
});
|
|
1305
1032
|
}
|
|
1306
1033
|
else {
|
|
@@ -1347,7 +1074,6 @@ export class Frontend extends EventEmitter {
|
|
|
1347
1074
|
return;
|
|
1348
1075
|
})
|
|
1349
1076
|
.catch((_error) => {
|
|
1350
|
-
//
|
|
1351
1077
|
});
|
|
1352
1078
|
}
|
|
1353
1079
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1585,22 +1311,22 @@ export class Frontend extends EventEmitter {
|
|
|
1585
1311
|
if (isValidString(data.params.value, 4)) {
|
|
1586
1312
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1587
1313
|
if (data.params.value === 'Debug') {
|
|
1588
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1314
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1589
1315
|
}
|
|
1590
1316
|
else if (data.params.value === 'Info') {
|
|
1591
|
-
await this.matterbridge.setLogLevel("info"
|
|
1317
|
+
await this.matterbridge.setLogLevel("info");
|
|
1592
1318
|
}
|
|
1593
1319
|
else if (data.params.value === 'Notice') {
|
|
1594
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1320
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1595
1321
|
}
|
|
1596
1322
|
else if (data.params.value === 'Warn') {
|
|
1597
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1323
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1598
1324
|
}
|
|
1599
1325
|
else if (data.params.value === 'Error') {
|
|
1600
|
-
await this.matterbridge.setLogLevel("error"
|
|
1326
|
+
await this.matterbridge.setLogLevel("error");
|
|
1601
1327
|
}
|
|
1602
1328
|
else if (data.params.value === 'Fatal') {
|
|
1603
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1329
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1604
1330
|
}
|
|
1605
1331
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1606
1332
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1611,7 +1337,6 @@ export class Frontend extends EventEmitter {
|
|
|
1611
1337
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1612
1338
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1613
1339
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1614
|
-
// Create the file logger for matterbridge
|
|
1615
1340
|
if (data.params.value)
|
|
1616
1341
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1617
1342
|
else
|
|
@@ -1776,19 +1501,15 @@ export class Frontend extends EventEmitter {
|
|
|
1776
1501
|
return;
|
|
1777
1502
|
}
|
|
1778
1503
|
const config = plugin.configJson;
|
|
1779
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1780
1504
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1781
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1782
1505
|
if (select === 'serial')
|
|
1783
1506
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1784
1507
|
if (select === 'name')
|
|
1785
1508
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1786
1509
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1787
|
-
// Remove postfix from the serial if it exists
|
|
1788
1510
|
if (config.postfix) {
|
|
1789
1511
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1790
1512
|
}
|
|
1791
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1792
1513
|
if (isValidArray(config.whiteList, 1)) {
|
|
1793
1514
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1794
1515
|
config.whiteList.push(data.params.serial);
|
|
@@ -1797,7 +1518,6 @@ export class Frontend extends EventEmitter {
|
|
|
1797
1518
|
config.whiteList.push(data.params.name);
|
|
1798
1519
|
}
|
|
1799
1520
|
}
|
|
1800
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1801
1521
|
if (isValidArray(config.blackList, 1)) {
|
|
1802
1522
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1803
1523
|
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
@@ -1827,9 +1547,7 @@ export class Frontend extends EventEmitter {
|
|
|
1827
1547
|
return;
|
|
1828
1548
|
}
|
|
1829
1549
|
const config = plugin.configJson;
|
|
1830
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1831
1550
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1832
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1833
1551
|
if (select === 'serial')
|
|
1834
1552
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1835
1553
|
if (select === 'name')
|
|
@@ -1838,7 +1556,6 @@ export class Frontend extends EventEmitter {
|
|
|
1838
1556
|
if (config.postfix) {
|
|
1839
1557
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1840
1558
|
}
|
|
1841
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1842
1559
|
if (isValidArray(config.whiteList, 1)) {
|
|
1843
1560
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1844
1561
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
@@ -1847,7 +1564,6 @@ export class Frontend extends EventEmitter {
|
|
|
1847
1564
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1848
1565
|
}
|
|
1849
1566
|
}
|
|
1850
|
-
// Add the serial to the blackList
|
|
1851
1567
|
if (isValidArray(config.blackList)) {
|
|
1852
1568
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1853
1569
|
config.blackList.push(data.params.serial);
|
|
@@ -1880,230 +1596,114 @@ export class Frontend extends EventEmitter {
|
|
|
1880
1596
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1881
1597
|
}
|
|
1882
1598
|
}
|
|
1883
|
-
/**
|
|
1884
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1885
|
-
*
|
|
1886
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1887
|
-
* @param {string} time - The time string of the message
|
|
1888
|
-
* @param {string} name - The logger name of the message
|
|
1889
|
-
* @param {string} message - The content of the message.
|
|
1890
|
-
*
|
|
1891
|
-
* @remarks
|
|
1892
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
1893
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
1894
|
-
* The function sends the message to all connected clients.
|
|
1895
|
-
*/
|
|
1896
1599
|
wssSendMessage(level, time, name, message) {
|
|
1897
1600
|
if (!level || !time || !name || !message)
|
|
1898
1601
|
return;
|
|
1899
|
-
// Remove ANSI escape codes from the message
|
|
1900
|
-
// eslint-disable-next-line no-control-regex
|
|
1901
1602
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1902
|
-
// Remove leading asterisks from the message
|
|
1903
1603
|
message = message.replace(/^\*+/, '');
|
|
1904
|
-
// Replace all occurrences of \t and \n
|
|
1905
1604
|
message = message.replace(/[\t\n]/g, '');
|
|
1906
|
-
// Remove non-printable characters
|
|
1907
|
-
// eslint-disable-next-line no-control-regex
|
|
1908
1605
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1909
|
-
// Replace all occurrences of \" with "
|
|
1910
1606
|
message = message.replace(/\\"/g, '"');
|
|
1911
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1912
1607
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1913
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1914
1608
|
const maxContinuousLength = 100;
|
|
1915
1609
|
const keepStartLength = 20;
|
|
1916
1610
|
const keepEndLength = 20;
|
|
1917
|
-
// Split the message into words
|
|
1918
1611
|
message = message
|
|
1919
1612
|
.split(' ')
|
|
1920
1613
|
.map((word) => {
|
|
1921
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1922
1614
|
if (word.length > maxContinuousLength) {
|
|
1923
1615
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1924
1616
|
}
|
|
1925
1617
|
return word;
|
|
1926
1618
|
})
|
|
1927
1619
|
.join(' ');
|
|
1928
|
-
// Send the message to all connected clients
|
|
1929
1620
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1930
1621
|
if (client.readyState === WebSocket.OPEN) {
|
|
1931
1622
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1932
1623
|
}
|
|
1933
1624
|
});
|
|
1934
1625
|
}
|
|
1935
|
-
/**
|
|
1936
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1937
|
-
*
|
|
1938
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
1939
|
-
* possible values:
|
|
1940
|
-
* - 'matterbridgeLatestVersion'
|
|
1941
|
-
* - 'matterbridgeAdvertise'
|
|
1942
|
-
* - 'online'
|
|
1943
|
-
* - 'offline'
|
|
1944
|
-
* - 'reachability'
|
|
1945
|
-
* - 'settings'
|
|
1946
|
-
* - 'plugins'
|
|
1947
|
-
* - 'pluginsRestart'
|
|
1948
|
-
* - 'devices'
|
|
1949
|
-
* - 'fabrics'
|
|
1950
|
-
* - 'sessions'
|
|
1951
|
-
*/
|
|
1952
1626
|
wssSendRefreshRequired(changed = null) {
|
|
1953
1627
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1954
|
-
// Send the message to all connected clients
|
|
1955
1628
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1956
1629
|
if (client.readyState === WebSocket.OPEN) {
|
|
1957
1630
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1958
1631
|
}
|
|
1959
1632
|
});
|
|
1960
1633
|
}
|
|
1961
|
-
/**
|
|
1962
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1963
|
-
*
|
|
1964
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients.
|
|
1965
|
-
*/
|
|
1966
1634
|
wssSendRestartRequired(snackbar = true) {
|
|
1967
1635
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1968
1636
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1969
1637
|
if (snackbar === true)
|
|
1970
1638
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1971
|
-
// Send the message to all connected clients
|
|
1972
1639
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1973
1640
|
if (client.readyState === WebSocket.OPEN) {
|
|
1974
1641
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1975
1642
|
}
|
|
1976
1643
|
});
|
|
1977
1644
|
}
|
|
1978
|
-
/**
|
|
1979
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
1980
|
-
*
|
|
1981
|
-
*/
|
|
1982
1645
|
wssSendUpdateRequired() {
|
|
1983
1646
|
this.log.debug('Sending an update required message to all connected clients');
|
|
1984
1647
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
1985
|
-
// Send the message to all connected clients
|
|
1986
1648
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1987
1649
|
if (client.readyState === WebSocket.OPEN) {
|
|
1988
1650
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
1989
1651
|
}
|
|
1990
1652
|
});
|
|
1991
1653
|
}
|
|
1992
|
-
/**
|
|
1993
|
-
* Sends a cpu update message to all connected clients.
|
|
1994
|
-
*
|
|
1995
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
1996
|
-
*/
|
|
1997
1654
|
wssSendCpuUpdate(cpuUsage) {
|
|
1998
1655
|
if (hasParameter('debug'))
|
|
1999
1656
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2000
|
-
// Send the message to all connected clients
|
|
2001
1657
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2002
1658
|
if (client.readyState === WebSocket.OPEN) {
|
|
2003
1659
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
2004
1660
|
}
|
|
2005
1661
|
});
|
|
2006
1662
|
}
|
|
2007
|
-
/**
|
|
2008
|
-
* Sends a memory update message to all connected clients.
|
|
2009
|
-
*
|
|
2010
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2011
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2012
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2013
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2014
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2015
|
-
* @param {string} external - The external memory in bytes.
|
|
2016
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2017
|
-
*/
|
|
2018
1663
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2019
1664
|
if (hasParameter('debug'))
|
|
2020
1665
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2021
|
-
// Send the message to all connected clients
|
|
2022
1666
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2023
1667
|
if (client.readyState === WebSocket.OPEN) {
|
|
2024
1668
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
2025
1669
|
}
|
|
2026
1670
|
});
|
|
2027
1671
|
}
|
|
2028
|
-
/**
|
|
2029
|
-
* Sends an uptime update message to all connected clients.
|
|
2030
|
-
*
|
|
2031
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2032
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2033
|
-
*/
|
|
2034
1672
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2035
1673
|
if (hasParameter('debug'))
|
|
2036
1674
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2037
|
-
// Send the message to all connected clients
|
|
2038
1675
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2039
1676
|
if (client.readyState === WebSocket.OPEN) {
|
|
2040
1677
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
2041
1678
|
}
|
|
2042
1679
|
});
|
|
2043
1680
|
}
|
|
2044
|
-
/**
|
|
2045
|
-
* Sends an open snackbar message to all connected clients.
|
|
2046
|
-
*
|
|
2047
|
-
* @param {string} message - The message to send.
|
|
2048
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
2049
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
2050
|
-
*/
|
|
2051
1681
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2052
1682
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2053
|
-
// Send the message to all connected clients
|
|
2054
1683
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2055
1684
|
if (client.readyState === WebSocket.OPEN) {
|
|
2056
1685
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
2057
1686
|
}
|
|
2058
1687
|
});
|
|
2059
1688
|
}
|
|
2060
|
-
/**
|
|
2061
|
-
* Sends a close snackbar message to all connected clients.
|
|
2062
|
-
*
|
|
2063
|
-
* @param {string} message - The message to send.
|
|
2064
|
-
*/
|
|
2065
1689
|
wssSendCloseSnackbarMessage(message) {
|
|
2066
1690
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2067
|
-
// Send the message to all connected clients
|
|
2068
1691
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2069
1692
|
if (client.readyState === WebSocket.OPEN) {
|
|
2070
1693
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
2071
1694
|
}
|
|
2072
1695
|
});
|
|
2073
1696
|
}
|
|
2074
|
-
/**
|
|
2075
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2076
|
-
*
|
|
2077
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2078
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2079
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2080
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2081
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2082
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2083
|
-
*
|
|
2084
|
-
* @remarks
|
|
2085
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2086
|
-
* with the updated attribute information.
|
|
2087
|
-
*/
|
|
2088
1697
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
2089
1698
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2090
|
-
// Send the message to all connected clients
|
|
2091
1699
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2092
1700
|
if (client.readyState === WebSocket.OPEN) {
|
|
2093
1701
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
2094
1702
|
}
|
|
2095
1703
|
});
|
|
2096
1704
|
}
|
|
2097
|
-
/**
|
|
2098
|
-
* Sends a message to all connected clients.
|
|
2099
|
-
*
|
|
2100
|
-
* @param {number} id - The message id.
|
|
2101
|
-
* @param {string} method - The message method.
|
|
2102
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
2103
|
-
*/
|
|
2104
1705
|
wssBroadcastMessage(id, method, params) {
|
|
2105
1706
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
2106
|
-
// Send the message to all connected clients
|
|
2107
1707
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2108
1708
|
if (client.readyState === WebSocket.OPEN) {
|
|
2109
1709
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -2111,4 +1711,3 @@ export class Frontend extends EventEmitter {
|
|
|
2111
1711
|
});
|
|
2112
1712
|
}
|
|
2113
1713
|
}
|
|
2114
|
-
//# sourceMappingURL=frontend.js.map
|