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