matterbridge 3.1.2 → 3.1.3-dev-20250714-c9b85b3
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 +31 -0
- package/README.md +8 -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 +33 -430
- 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 +77 -911
- 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 -271
- 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,9 +570,9 @@ 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;
|
|
575
|
+
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
761
576
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
762
577
|
this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
|
|
763
578
|
this.matterbridge.matterbridgeInformation.mattermdnsinterface = this.matterbridge.mdnsInterface;
|
|
@@ -766,20 +581,15 @@ export class Frontend extends EventEmitter {
|
|
|
766
581
|
this.matterbridge.matterbridgeInformation.matterPort = (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540;
|
|
767
582
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = await this.matterbridge.nodeContext?.get('matterdiscriminator');
|
|
768
583
|
this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
|
|
769
|
-
this.matterbridge.
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
584
|
+
if (this.matterbridge.bridgeMode === 'bridge' && this.matterbridge.serverNode) {
|
|
585
|
+
this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.serverNode.state.commissioning.commissioned;
|
|
586
|
+
this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.qrPairingCode;
|
|
587
|
+
this.matterbridge.matterbridgeInformation.matterbridgeManualPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.manualPairingCode;
|
|
588
|
+
this.matterbridge.matterbridgeInformation.matterbridgeFabricInformations = this.matterbridge.sanitizeFabricInformations(Object.values(this.matterbridge.serverNode.state.commissioning.fabrics));
|
|
589
|
+
this.matterbridge.matterbridgeInformation.matterbridgeSessionInformations = this.matterbridge.sanitizeSessionInformation(Object.values(this.matterbridge.serverNode.state.sessions.sessions));
|
|
590
|
+
}
|
|
775
591
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
776
592
|
}
|
|
777
|
-
/**
|
|
778
|
-
* Retrieves the reachable attribute.
|
|
779
|
-
*
|
|
780
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
781
|
-
* @returns {boolean} The reachable attribute.
|
|
782
|
-
*/
|
|
783
593
|
getReachability(device) {
|
|
784
594
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
785
595
|
return false;
|
|
@@ -791,12 +601,6 @@ export class Frontend extends EventEmitter {
|
|
|
791
601
|
return true;
|
|
792
602
|
return false;
|
|
793
603
|
}
|
|
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
604
|
getPowerSource(endpoint) {
|
|
801
605
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
802
606
|
return undefined;
|
|
@@ -812,21 +616,13 @@ export class Frontend extends EventEmitter {
|
|
|
812
616
|
}
|
|
813
617
|
return;
|
|
814
618
|
};
|
|
815
|
-
// Root endpoint
|
|
816
619
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
817
620
|
return powerSource(endpoint);
|
|
818
|
-
// Child endpoints
|
|
819
621
|
for (const child of endpoint.getChildEndpoints()) {
|
|
820
622
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
821
623
|
return powerSource(child);
|
|
822
624
|
}
|
|
823
625
|
}
|
|
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
626
|
getMatterDataFromDevice(device) {
|
|
831
627
|
if (device.mode === 'server' && device.serverNode) {
|
|
832
628
|
return {
|
|
@@ -838,13 +634,6 @@ export class Frontend extends EventEmitter {
|
|
|
838
634
|
};
|
|
839
635
|
}
|
|
840
636
|
}
|
|
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
637
|
getClusterTextFromDevice(device) {
|
|
849
638
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
850
639
|
return '';
|
|
@@ -869,7 +658,6 @@ export class Frontend extends EventEmitter {
|
|
|
869
658
|
let attributes = '';
|
|
870
659
|
let supportedModes = [];
|
|
871
660
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
872
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
873
661
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
874
662
|
return;
|
|
875
663
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -959,15 +747,11 @@ export class Frontend extends EventEmitter {
|
|
|
959
747
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
960
748
|
attributes += `${getUserLabel(device)} `;
|
|
961
749
|
});
|
|
962
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
963
750
|
return attributes.trimStart().trimEnd();
|
|
964
751
|
}
|
|
965
|
-
/**
|
|
966
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
967
|
-
*
|
|
968
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
969
|
-
*/
|
|
970
752
|
getBaseRegisteredPlugins() {
|
|
753
|
+
if (this.matterbridge.hasCleanupStarted)
|
|
754
|
+
return [];
|
|
971
755
|
const baseRegisteredPlugins = [];
|
|
972
756
|
for (const plugin of this.matterbridge.plugins) {
|
|
973
757
|
baseRegisteredPlugins.push({
|
|
@@ -989,35 +773,29 @@ export class Frontend extends EventEmitter {
|
|
|
989
773
|
loaded: plugin.loaded,
|
|
990
774
|
started: plugin.started,
|
|
991
775
|
configured: plugin.configured,
|
|
992
|
-
paired: plugin.paired,
|
|
993
776
|
restartRequired: plugin.restartRequired,
|
|
994
|
-
fabricInformations: plugin.fabricInformations,
|
|
995
|
-
sessionInformations: plugin.sessionInformations,
|
|
996
777
|
registeredDevices: plugin.registeredDevices,
|
|
997
778
|
addedDevices: plugin.addedDevices,
|
|
998
|
-
qrPairingCode: plugin.qrPairingCode,
|
|
999
|
-
manualPairingCode: plugin.manualPairingCode,
|
|
1000
779
|
configJson: plugin.configJson,
|
|
1001
780
|
schemaJson: plugin.schemaJson,
|
|
1002
781
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1003
782
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
783
|
+
paired: plugin.serverNode?.state.commissioning.commissioned,
|
|
784
|
+
qrPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode?.state.commissioning.pairingCodes.qrPairingCode,
|
|
785
|
+
manualPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode?.state.commissioning.pairingCodes.manualPairingCode,
|
|
786
|
+
fabricInformations: plugin.serverNode ? this.matterbridge.sanitizeFabricInformations(Object.values(plugin.serverNode?.state.commissioning.fabrics)) : undefined,
|
|
787
|
+
sessionInformations: plugin.serverNode ? this.matterbridge.sanitizeSessionInformation(Object.values(plugin.serverNode?.state.sessions.sessions)) : undefined,
|
|
1004
788
|
});
|
|
1005
789
|
}
|
|
1006
790
|
return baseRegisteredPlugins;
|
|
1007
791
|
}
|
|
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
792
|
async getDevices(pluginName) {
|
|
793
|
+
if (this.matterbridge.hasCleanupStarted)
|
|
794
|
+
return [];
|
|
1015
795
|
const devices = [];
|
|
1016
796
|
for (const device of this.matterbridge.devices.array()) {
|
|
1017
|
-
// Filter by pluginName if provided
|
|
1018
797
|
if (pluginName && pluginName !== device.plugin)
|
|
1019
798
|
continue;
|
|
1020
|
-
// Check if the device has the required properties
|
|
1021
799
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1022
800
|
continue;
|
|
1023
801
|
devices.push({
|
|
@@ -1037,37 +815,22 @@ export class Frontend extends EventEmitter {
|
|
|
1037
815
|
}
|
|
1038
816
|
return devices;
|
|
1039
817
|
}
|
|
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
818
|
getClusters(pluginName, endpointNumber) {
|
|
1050
819
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1051
820
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1052
821
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1053
822
|
return;
|
|
1054
823
|
}
|
|
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
824
|
const deviceTypes = [];
|
|
1058
825
|
const clusters = [];
|
|
1059
826
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1060
827
|
deviceTypes.push(d.deviceType);
|
|
1061
828
|
});
|
|
1062
|
-
// Get the clusters from the main endpoint
|
|
1063
829
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1064
830
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1065
831
|
return;
|
|
1066
832
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1067
833
|
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
834
|
clusters.push({
|
|
1072
835
|
endpoint: endpoint.number.toString(),
|
|
1073
836
|
id: 'main',
|
|
@@ -1080,18 +843,12 @@ export class Frontend extends EventEmitter {
|
|
|
1080
843
|
attributeLocalValue: attributeValue,
|
|
1081
844
|
});
|
|
1082
845
|
});
|
|
1083
|
-
// Get the child endpoints
|
|
1084
846
|
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
847
|
childEndpoints.forEach((childEndpoint) => {
|
|
1089
848
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1090
849
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1091
850
|
return;
|
|
1092
851
|
}
|
|
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
852
|
const deviceTypes = [];
|
|
1096
853
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1097
854
|
deviceTypes.push(d.deviceType);
|
|
@@ -1101,12 +858,9 @@ export class Frontend extends EventEmitter {
|
|
|
1101
858
|
return;
|
|
1102
859
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1103
860
|
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
861
|
clusters.push({
|
|
1108
862
|
endpoint: childEndpoint.number.toString(),
|
|
1109
|
-
id: childEndpoint.maybeId ?? 'null',
|
|
863
|
+
id: childEndpoint.maybeId ?? 'null',
|
|
1110
864
|
deviceTypes,
|
|
1111
865
|
clusterName: capitalizeFirstLetter(clusterName),
|
|
1112
866
|
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
@@ -1119,13 +873,6 @@ export class Frontend extends EventEmitter {
|
|
|
1119
873
|
});
|
|
1120
874
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
|
|
1121
875
|
}
|
|
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
876
|
async wsMessageHandler(client, message) {
|
|
1130
877
|
let data;
|
|
1131
878
|
try {
|
|
@@ -1172,41 +919,32 @@ export class Frontend extends EventEmitter {
|
|
|
1172
919
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1173
920
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1174
921
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1175
|
-
// The install comes from InstallPlugins
|
|
1176
922
|
this.matterbridge.plugins
|
|
1177
923
|
.add(packageName)
|
|
1178
924
|
.then((plugin) => {
|
|
1179
925
|
if (plugin) {
|
|
1180
|
-
// The plugin is not registered
|
|
1181
926
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1182
927
|
this.matterbridge.plugins
|
|
1183
928
|
.load(plugin, true, 'The plugin has been added', true)
|
|
1184
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1185
929
|
.then(() => {
|
|
1186
930
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
1187
931
|
this.wssSendRefreshRequired('plugins');
|
|
1188
932
|
return;
|
|
1189
933
|
})
|
|
1190
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1191
934
|
.catch((_error) => {
|
|
1192
|
-
//
|
|
1193
935
|
});
|
|
1194
936
|
}
|
|
1195
937
|
else {
|
|
1196
|
-
// The plugin is already registered
|
|
1197
938
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1198
939
|
this.wssSendRefreshRequired('plugins');
|
|
1199
940
|
this.wssSendRestartRequired();
|
|
1200
941
|
}
|
|
1201
942
|
return;
|
|
1202
943
|
})
|
|
1203
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1204
944
|
.catch((_error) => {
|
|
1205
|
-
//
|
|
1206
945
|
});
|
|
1207
946
|
}
|
|
1208
947
|
else {
|
|
1209
|
-
// The package is matterbridge
|
|
1210
948
|
if (this.matterbridge.restartMode !== '') {
|
|
1211
949
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1212
950
|
this.matterbridge.shutdownProcess();
|
|
@@ -1229,7 +967,6 @@ export class Frontend extends EventEmitter {
|
|
|
1229
967
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1230
968
|
return;
|
|
1231
969
|
}
|
|
1232
|
-
// The package is a plugin
|
|
1233
970
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1234
971
|
if (plugin) {
|
|
1235
972
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
@@ -1238,7 +975,6 @@ export class Frontend extends EventEmitter {
|
|
|
1238
975
|
this.wssSendRefreshRequired('plugins');
|
|
1239
976
|
this.wssSendRefreshRequired('devices');
|
|
1240
977
|
}
|
|
1241
|
-
// Uninstall the package
|
|
1242
978
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1243
979
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
1244
980
|
spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1279,7 +1015,6 @@ export class Frontend extends EventEmitter {
|
|
|
1279
1015
|
return;
|
|
1280
1016
|
})
|
|
1281
1017
|
.catch((_error) => {
|
|
1282
|
-
//
|
|
1283
1018
|
});
|
|
1284
1019
|
}
|
|
1285
1020
|
else {
|
|
@@ -1326,7 +1061,6 @@ export class Frontend extends EventEmitter {
|
|
|
1326
1061
|
return;
|
|
1327
1062
|
})
|
|
1328
1063
|
.catch((_error) => {
|
|
1329
|
-
//
|
|
1330
1064
|
});
|
|
1331
1065
|
}
|
|
1332
1066
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1437,8 +1171,6 @@ export class Frontend extends EventEmitter {
|
|
|
1437
1171
|
else if (data.method === '/api/advertise') {
|
|
1438
1172
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
1439
1173
|
this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
|
|
1440
|
-
this.matterbridge.matterbridgeQrPairingCode = pairingCodes?.qrPairingCode;
|
|
1441
|
-
this.matterbridge.matterbridgeManualPairingCode = pairingCodes?.manualPairingCode;
|
|
1442
1174
|
this.wssSendRefreshRequired('matterbridgeAdvertise');
|
|
1443
1175
|
this.wssSendSnackbarMessage(`Started fabrics share`, 0);
|
|
1444
1176
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes, success: true }));
|
|
@@ -1561,22 +1293,22 @@ export class Frontend extends EventEmitter {
|
|
|
1561
1293
|
if (isValidString(data.params.value, 4)) {
|
|
1562
1294
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1563
1295
|
if (data.params.value === 'Debug') {
|
|
1564
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1296
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1565
1297
|
}
|
|
1566
1298
|
else if (data.params.value === 'Info') {
|
|
1567
|
-
await this.matterbridge.setLogLevel("info"
|
|
1299
|
+
await this.matterbridge.setLogLevel("info");
|
|
1568
1300
|
}
|
|
1569
1301
|
else if (data.params.value === 'Notice') {
|
|
1570
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1302
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1571
1303
|
}
|
|
1572
1304
|
else if (data.params.value === 'Warn') {
|
|
1573
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1305
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1574
1306
|
}
|
|
1575
1307
|
else if (data.params.value === 'Error') {
|
|
1576
|
-
await this.matterbridge.setLogLevel("error"
|
|
1308
|
+
await this.matterbridge.setLogLevel("error");
|
|
1577
1309
|
}
|
|
1578
1310
|
else if (data.params.value === 'Fatal') {
|
|
1579
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1311
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1580
1312
|
}
|
|
1581
1313
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1582
1314
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1587,7 +1319,6 @@ export class Frontend extends EventEmitter {
|
|
|
1587
1319
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1588
1320
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1589
1321
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1590
|
-
// Create the file logger for matterbridge
|
|
1591
1322
|
if (data.params.value)
|
|
1592
1323
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1593
1324
|
else
|
|
@@ -1634,7 +1365,6 @@ export class Frontend extends EventEmitter {
|
|
|
1634
1365
|
});
|
|
1635
1366
|
}
|
|
1636
1367
|
catch (error) {
|
|
1637
|
-
/* istanbul ignore next */
|
|
1638
1368
|
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
1369
|
}
|
|
1640
1370
|
}
|
|
@@ -1643,7 +1373,6 @@ export class Frontend extends EventEmitter {
|
|
|
1643
1373
|
Logger.removeLogger('matterfilelogger');
|
|
1644
1374
|
}
|
|
1645
1375
|
catch (error) {
|
|
1646
|
-
/* istanbul ignore next */
|
|
1647
1376
|
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
1377
|
}
|
|
1649
1378
|
}
|
|
@@ -1756,19 +1485,15 @@ export class Frontend extends EventEmitter {
|
|
|
1756
1485
|
return;
|
|
1757
1486
|
}
|
|
1758
1487
|
const config = plugin.configJson;
|
|
1759
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1760
1488
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1761
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1762
1489
|
if (select === 'serial')
|
|
1763
1490
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1764
1491
|
if (select === 'name')
|
|
1765
1492
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1766
1493
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1767
|
-
// Remove postfix from the serial if it exists
|
|
1768
1494
|
if (config.postfix) {
|
|
1769
1495
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1770
1496
|
}
|
|
1771
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1772
1497
|
if (isValidArray(config.whiteList, 1)) {
|
|
1773
1498
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1774
1499
|
config.whiteList.push(data.params.serial);
|
|
@@ -1777,7 +1502,6 @@ export class Frontend extends EventEmitter {
|
|
|
1777
1502
|
config.whiteList.push(data.params.name);
|
|
1778
1503
|
}
|
|
1779
1504
|
}
|
|
1780
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1781
1505
|
if (isValidArray(config.blackList, 1)) {
|
|
1782
1506
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1783
1507
|
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
@@ -1808,9 +1532,7 @@ export class Frontend extends EventEmitter {
|
|
|
1808
1532
|
return;
|
|
1809
1533
|
}
|
|
1810
1534
|
const config = plugin.configJson;
|
|
1811
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1812
1535
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1813
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1814
1536
|
if (select === 'serial')
|
|
1815
1537
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1816
1538
|
if (select === 'name')
|
|
@@ -1819,7 +1541,6 @@ export class Frontend extends EventEmitter {
|
|
|
1819
1541
|
if (config.postfix) {
|
|
1820
1542
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1821
1543
|
}
|
|
1822
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1823
1544
|
if (isValidArray(config.whiteList, 1)) {
|
|
1824
1545
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1825
1546
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
@@ -1828,7 +1549,6 @@ export class Frontend extends EventEmitter {
|
|
|
1828
1549
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1829
1550
|
}
|
|
1830
1551
|
}
|
|
1831
|
-
// Add the serial to the blackList
|
|
1832
1552
|
if (isValidArray(config.blackList)) {
|
|
1833
1553
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1834
1554
|
config.blackList.push(data.params.serial);
|
|
@@ -1862,230 +1582,114 @@ export class Frontend extends EventEmitter {
|
|
|
1862
1582
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1863
1583
|
}
|
|
1864
1584
|
}
|
|
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
1585
|
wssSendMessage(level, time, name, message) {
|
|
1879
1586
|
if (!level || !time || !name || !message)
|
|
1880
1587
|
return;
|
|
1881
|
-
// Remove ANSI escape codes from the message
|
|
1882
|
-
// eslint-disable-next-line no-control-regex
|
|
1883
1588
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1884
|
-
// Remove leading asterisks from the message
|
|
1885
1589
|
message = message.replace(/^\*+/, '');
|
|
1886
|
-
// Replace all occurrences of \t and \n
|
|
1887
1590
|
message = message.replace(/[\t\n]/g, '');
|
|
1888
|
-
// Remove non-printable characters
|
|
1889
|
-
// eslint-disable-next-line no-control-regex
|
|
1890
1591
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1891
|
-
// Replace all occurrences of \" with "
|
|
1892
1592
|
message = message.replace(/\\"/g, '"');
|
|
1893
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1894
1593
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1895
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1896
1594
|
const maxContinuousLength = 100;
|
|
1897
1595
|
const keepStartLength = 20;
|
|
1898
1596
|
const keepEndLength = 20;
|
|
1899
|
-
// Split the message into words
|
|
1900
1597
|
message = message
|
|
1901
1598
|
.split(' ')
|
|
1902
1599
|
.map((word) => {
|
|
1903
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1904
1600
|
if (word.length > maxContinuousLength) {
|
|
1905
1601
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1906
1602
|
}
|
|
1907
1603
|
return word;
|
|
1908
1604
|
})
|
|
1909
1605
|
.join(' ');
|
|
1910
|
-
// Send the message to all connected clients
|
|
1911
1606
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1912
1607
|
if (client.readyState === WebSocket.OPEN) {
|
|
1913
1608
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1914
1609
|
}
|
|
1915
1610
|
});
|
|
1916
1611
|
}
|
|
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
1612
|
wssSendRefreshRequired(changed = null) {
|
|
1935
1613
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1936
|
-
// Send the message to all connected clients
|
|
1937
1614
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1938
1615
|
if (client.readyState === WebSocket.OPEN) {
|
|
1939
1616
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1940
1617
|
}
|
|
1941
1618
|
});
|
|
1942
1619
|
}
|
|
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
1620
|
wssSendRestartRequired(snackbar = true) {
|
|
1949
1621
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1950
1622
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1951
1623
|
if (snackbar === true)
|
|
1952
1624
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1953
|
-
// Send the message to all connected clients
|
|
1954
1625
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1955
1626
|
if (client.readyState === WebSocket.OPEN) {
|
|
1956
1627
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1957
1628
|
}
|
|
1958
1629
|
});
|
|
1959
1630
|
}
|
|
1960
|
-
/**
|
|
1961
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
1962
|
-
*
|
|
1963
|
-
*/
|
|
1964
1631
|
wssSendUpdateRequired() {
|
|
1965
1632
|
this.log.debug('Sending an update required message to all connected clients');
|
|
1966
1633
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
1967
|
-
// Send the message to all connected clients
|
|
1968
1634
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1969
1635
|
if (client.readyState === WebSocket.OPEN) {
|
|
1970
1636
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
1971
1637
|
}
|
|
1972
1638
|
});
|
|
1973
1639
|
}
|
|
1974
|
-
/**
|
|
1975
|
-
* Sends a cpu update message to all connected clients.
|
|
1976
|
-
*
|
|
1977
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
1978
|
-
*/
|
|
1979
1640
|
wssSendCpuUpdate(cpuUsage) {
|
|
1980
1641
|
if (hasParameter('debug'))
|
|
1981
1642
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
1982
|
-
// Send the message to all connected clients
|
|
1983
1643
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1984
1644
|
if (client.readyState === WebSocket.OPEN) {
|
|
1985
1645
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
1986
1646
|
}
|
|
1987
1647
|
});
|
|
1988
1648
|
}
|
|
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
1649
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2001
1650
|
if (hasParameter('debug'))
|
|
2002
1651
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2003
|
-
// Send the message to all connected clients
|
|
2004
1652
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2005
1653
|
if (client.readyState === WebSocket.OPEN) {
|
|
2006
1654
|
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
1655
|
}
|
|
2008
1656
|
});
|
|
2009
1657
|
}
|
|
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
1658
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2017
1659
|
if (hasParameter('debug'))
|
|
2018
1660
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2019
|
-
// Send the message to all connected clients
|
|
2020
1661
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2021
1662
|
if (client.readyState === WebSocket.OPEN) {
|
|
2022
1663
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
2023
1664
|
}
|
|
2024
1665
|
});
|
|
2025
1666
|
}
|
|
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
1667
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2034
1668
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2035
|
-
// Send the message to all connected clients
|
|
2036
1669
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2037
1670
|
if (client.readyState === WebSocket.OPEN) {
|
|
2038
1671
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
2039
1672
|
}
|
|
2040
1673
|
});
|
|
2041
1674
|
}
|
|
2042
|
-
/**
|
|
2043
|
-
* Sends a close snackbar message to all connected clients.
|
|
2044
|
-
*
|
|
2045
|
-
* @param {string} message - The message to send.
|
|
2046
|
-
*/
|
|
2047
1675
|
wssSendCloseSnackbarMessage(message) {
|
|
2048
1676
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2049
|
-
// Send the message to all connected clients
|
|
2050
1677
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2051
1678
|
if (client.readyState === WebSocket.OPEN) {
|
|
2052
1679
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
2053
1680
|
}
|
|
2054
1681
|
});
|
|
2055
1682
|
}
|
|
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
1683
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
2071
1684
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2072
|
-
// Send the message to all connected clients
|
|
2073
1685
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2074
1686
|
if (client.readyState === WebSocket.OPEN) {
|
|
2075
1687
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
2076
1688
|
}
|
|
2077
1689
|
});
|
|
2078
1690
|
}
|
|
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
1691
|
wssBroadcastMessage(id, method, params) {
|
|
2087
1692
|
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
1693
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2090
1694
|
if (client.readyState === WebSocket.OPEN) {
|
|
2091
1695
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -2093,4 +1697,3 @@ export class Frontend extends EventEmitter {
|
|
|
2093
1697
|
});
|
|
2094
1698
|
}
|
|
2095
1699
|
}
|
|
2096
|
-
//# sourceMappingURL=frontend.js.map
|