matterbridge 3.1.5 → 3.1.6-dev-20250721-75fab6b
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 +18 -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/dishwasher.js +90 -0
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.js +2 -2
- package/dist/devices/extractorHood.js +35 -0
- 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 +7 -93
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/waterHeater.js +2 -82
- package/dist/frontend.js +21 -429
- 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 +79 -802
- package/dist/matterbridgeAccessoryPlatform.js +0 -36
- package/dist/matterbridgeBehaviors.js +24 -61
- package/dist/matterbridgeDeviceTypes.js +15 -579
- package/dist/matterbridgeDynamicPlatform.js +0 -36
- package/dist/matterbridgeEndpoint.js +64 -1113
- package/dist/matterbridgeEndpointHelpers.js +12 -322
- package/dist/matterbridgePlatform.js +0 -233
- package/dist/matterbridgeTypes.js +0 -25
- package/dist/pluginManager.js +3 -249
- 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/error.js +0 -41
- 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 -81
- package/dist/utils/spawn.js +0 -40
- package/dist/utils/wait.js +9 -62
- package/npm-shrinkwrap.json +9 -9
- 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 -112
- 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 -304
- 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 -447
- 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 -270
- 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/error.d.ts +0 -44
- package/dist/utils/error.d.ts.map +0 -1
- package/dist/utils/error.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 -74
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/spawn.d.ts +0 -33
- 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.2.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 { existsSync, 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;
|
|
@@ -133,7 +37,7 @@ export class Frontend extends EventEmitter {
|
|
|
133
37
|
constructor(matterbridge) {
|
|
134
38
|
super();
|
|
135
39
|
this.matterbridge = matterbridge;
|
|
136
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
40
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
137
41
|
}
|
|
138
42
|
set logLevel(logLevel) {
|
|
139
43
|
this.log.logLevel = logLevel;
|
|
@@ -141,41 +45,12 @@ export class Frontend extends EventEmitter {
|
|
|
141
45
|
async start(port = 8283) {
|
|
142
46
|
this.port = port;
|
|
143
47
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
144
|
-
// Initialize multer with the upload directory
|
|
145
48
|
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
146
49
|
await fs.mkdir(uploadDir, { recursive: true });
|
|
147
50
|
const upload = multer({ dest: uploadDir });
|
|
148
|
-
// Create the express app that serves the frontend
|
|
149
51
|
this.expressApp = express();
|
|
150
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
151
|
-
/*
|
|
152
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
153
|
-
for (const method of methods) {
|
|
154
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
155
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
156
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
157
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
158
|
-
try {
|
|
159
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
160
|
-
return original(path, ...rest);
|
|
161
|
-
} catch (err) {
|
|
162
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
163
|
-
throw err;
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
*/
|
|
168
|
-
// Log all requests to the server for debugging
|
|
169
|
-
/*
|
|
170
|
-
this.expressApp.use((req, res, next) => {
|
|
171
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
172
|
-
next();
|
|
173
|
-
});
|
|
174
|
-
*/
|
|
175
|
-
// Serve static files from '/static' endpoint
|
|
176
52
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
177
53
|
if (!hasParameter('ssl')) {
|
|
178
|
-
// Create an HTTP server and attach the express app
|
|
179
54
|
try {
|
|
180
55
|
this.log.debug(`Creating HTTP server...`);
|
|
181
56
|
this.httpServer = createServer(this.expressApp);
|
|
@@ -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}`);
|
|
@@ -224,7 +98,6 @@ export class Frontend extends EventEmitter {
|
|
|
224
98
|
let passphrase;
|
|
225
99
|
let httpsServerOptions = {};
|
|
226
100
|
if (existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
|
|
227
|
-
// Load the p12 certificate and the passphrase
|
|
228
101
|
try {
|
|
229
102
|
pfx = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
|
|
230
103
|
this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
|
|
@@ -236,7 +109,7 @@ export class Frontend extends EventEmitter {
|
|
|
236
109
|
}
|
|
237
110
|
try {
|
|
238
111
|
passphrase = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
|
|
239
|
-
passphrase = passphrase.trim();
|
|
112
|
+
passphrase = passphrase.trim();
|
|
240
113
|
this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
|
|
241
114
|
}
|
|
242
115
|
catch (error) {
|
|
@@ -247,7 +120,6 @@ export class Frontend extends EventEmitter {
|
|
|
247
120
|
httpsServerOptions = { pfx, passphrase };
|
|
248
121
|
}
|
|
249
122
|
else {
|
|
250
|
-
// Load the SSL certificate, the private key and optionally the CA certificate. If the CA certificate is present, it will be used to create a full chain certificate.
|
|
251
123
|
try {
|
|
252
124
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
253
125
|
this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
|
|
@@ -277,10 +149,9 @@ export class Frontend extends EventEmitter {
|
|
|
277
149
|
httpsServerOptions = { cert: fullChain ?? cert, key, ca };
|
|
278
150
|
}
|
|
279
151
|
if (hasParameter('mtls')) {
|
|
280
|
-
httpsServerOptions.requestCert = true;
|
|
281
|
-
httpsServerOptions.rejectUnauthorized = true;
|
|
152
|
+
httpsServerOptions.requestCert = true;
|
|
153
|
+
httpsServerOptions.rejectUnauthorized = true;
|
|
282
154
|
}
|
|
283
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
284
155
|
try {
|
|
285
156
|
this.log.debug(`Creating HTTPS server...`);
|
|
286
157
|
this.httpsServer = https.createServer(httpsServerOptions, this.expressApp);
|
|
@@ -290,7 +161,6 @@ export class Frontend extends EventEmitter {
|
|
|
290
161
|
this.emit('server_error', error);
|
|
291
162
|
return;
|
|
292
163
|
}
|
|
293
|
-
// Listen on the specified port
|
|
294
164
|
if (hasParameter('ingress')) {
|
|
295
165
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
296
166
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -320,19 +190,17 @@ export class Frontend extends EventEmitter {
|
|
|
320
190
|
return;
|
|
321
191
|
});
|
|
322
192
|
}
|
|
323
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
324
193
|
const wssPort = this.port;
|
|
325
194
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
326
195
|
this.log.debug(`Creating WebSocketServer on host ${CYAN}${wssHost}${db}...`);
|
|
327
196
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
328
197
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
329
198
|
const clientIp = request.socket.remoteAddress;
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
199
|
+
let callbackLogLevel = "notice";
|
|
200
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
201
|
+
callbackLogLevel = "info";
|
|
202
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
203
|
+
callbackLogLevel = "debug";
|
|
336
204
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
337
205
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
338
206
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -354,7 +222,6 @@ export class Frontend extends EventEmitter {
|
|
|
354
222
|
}
|
|
355
223
|
});
|
|
356
224
|
ws.on('error', (error) => {
|
|
357
|
-
// istanbul ignore next
|
|
358
225
|
this.log.error(`WebSocket client error: ${error}`);
|
|
359
226
|
});
|
|
360
227
|
});
|
|
@@ -368,7 +235,6 @@ export class Frontend extends EventEmitter {
|
|
|
368
235
|
this.webSocketServer.on('error', (ws, error) => {
|
|
369
236
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
370
237
|
});
|
|
371
|
-
// Subscribe to cli events
|
|
372
238
|
cliEmitter.removeAllListeners();
|
|
373
239
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
374
240
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -379,8 +245,6 @@ export class Frontend extends EventEmitter {
|
|
|
379
245
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
380
246
|
this.wssSendCpuUpdate(cpuUsage);
|
|
381
247
|
});
|
|
382
|
-
// Endpoint to validate login code
|
|
383
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
384
248
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
385
249
|
const { password } = req.body;
|
|
386
250
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -399,27 +263,23 @@ export class Frontend extends EventEmitter {
|
|
|
399
263
|
this.log.warn('/api/login error wrong password');
|
|
400
264
|
res.json({ valid: false });
|
|
401
265
|
}
|
|
402
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
403
266
|
}
|
|
404
267
|
catch (error) {
|
|
405
268
|
this.log.error('/api/login error getting password');
|
|
406
269
|
res.json({ valid: false });
|
|
407
270
|
}
|
|
408
271
|
});
|
|
409
|
-
// Endpoint to provide health check for docker
|
|
410
272
|
this.expressApp.get('/health', (req, res) => {
|
|
411
273
|
this.log.debug('Express received /health');
|
|
412
274
|
const healthStatus = {
|
|
413
|
-
status: 'ok',
|
|
414
|
-
uptime: process.uptime(),
|
|
415
|
-
timestamp: new Date().toISOString(),
|
|
275
|
+
status: 'ok',
|
|
276
|
+
uptime: process.uptime(),
|
|
277
|
+
timestamp: new Date().toISOString(),
|
|
416
278
|
};
|
|
417
279
|
res.status(200).json(healthStatus);
|
|
418
280
|
});
|
|
419
|
-
// Endpoint to provide memory usage details
|
|
420
281
|
this.expressApp.get('/memory', async (req, res) => {
|
|
421
282
|
this.log.debug('Express received /memory');
|
|
422
|
-
// Memory usage from process
|
|
423
283
|
const memoryUsageRaw = process.memoryUsage();
|
|
424
284
|
const memoryUsage = {
|
|
425
285
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -428,13 +288,10 @@ export class Frontend extends EventEmitter {
|
|
|
428
288
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
429
289
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
430
290
|
};
|
|
431
|
-
// V8 heap statistics
|
|
432
291
|
const { default: v8 } = await import('node:v8');
|
|
433
292
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
434
293
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
435
|
-
// Format heapStats
|
|
436
294
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
437
|
-
// Format heapSpaces
|
|
438
295
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
439
296
|
...space,
|
|
440
297
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -452,23 +309,19 @@ export class Frontend extends EventEmitter {
|
|
|
452
309
|
};
|
|
453
310
|
res.status(200).json(memoryReport);
|
|
454
311
|
});
|
|
455
|
-
// Endpoint to provide settings
|
|
456
312
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
457
313
|
this.log.debug('The frontend sent /api/settings');
|
|
458
314
|
res.json(await this.getApiSettings());
|
|
459
315
|
});
|
|
460
|
-
// Endpoint to provide plugins
|
|
461
316
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
462
317
|
this.log.debug('The frontend sent /api/plugins');
|
|
463
318
|
res.json(this.getBaseRegisteredPlugins());
|
|
464
319
|
});
|
|
465
|
-
// Endpoint to provide devices
|
|
466
320
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
467
321
|
this.log.debug('The frontend sent /api/devices');
|
|
468
322
|
const devices = await this.getDevices();
|
|
469
323
|
res.json(devices);
|
|
470
324
|
});
|
|
471
|
-
// Endpoint to view the matterbridge log
|
|
472
325
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
473
326
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
474
327
|
try {
|
|
@@ -481,7 +334,6 @@ export class Frontend extends EventEmitter {
|
|
|
481
334
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
482
335
|
}
|
|
483
336
|
});
|
|
484
|
-
// Endpoint to view the matter.js log
|
|
485
337
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
486
338
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
487
339
|
try {
|
|
@@ -494,7 +346,6 @@ export class Frontend extends EventEmitter {
|
|
|
494
346
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
495
347
|
}
|
|
496
348
|
});
|
|
497
|
-
// Endpoint to view the shelly log
|
|
498
349
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
499
350
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
500
351
|
try {
|
|
@@ -507,11 +358,9 @@ export class Frontend extends EventEmitter {
|
|
|
507
358
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
508
359
|
}
|
|
509
360
|
});
|
|
510
|
-
// Endpoint to download the matterbridge log
|
|
511
361
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
512
362
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
513
363
|
try {
|
|
514
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
515
364
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), fs.constants.F_OK);
|
|
516
365
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), 'utf8');
|
|
517
366
|
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), data, 'utf-8');
|
|
@@ -522,18 +371,15 @@ export class Frontend extends EventEmitter {
|
|
|
522
371
|
}
|
|
523
372
|
res.type('text/plain');
|
|
524
373
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'matterbridge.log', (error) => {
|
|
525
|
-
/* istanbul ignore if */
|
|
526
374
|
if (error) {
|
|
527
375
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
528
376
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
529
377
|
}
|
|
530
378
|
});
|
|
531
379
|
});
|
|
532
|
-
// Endpoint to download the matter log
|
|
533
380
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
534
381
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
535
382
|
try {
|
|
536
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
537
383
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
538
384
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'utf8');
|
|
539
385
|
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), data, 'utf-8');
|
|
@@ -544,18 +390,15 @@ export class Frontend extends EventEmitter {
|
|
|
544
390
|
}
|
|
545
391
|
res.type('text/plain');
|
|
546
392
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
|
|
547
|
-
/* istanbul ignore if */
|
|
548
393
|
if (error) {
|
|
549
394
|
this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
550
395
|
res.status(500).send('Error downloading the matter log file');
|
|
551
396
|
}
|
|
552
397
|
});
|
|
553
398
|
});
|
|
554
|
-
// Endpoint to download the shelly log
|
|
555
399
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
556
400
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
557
401
|
try {
|
|
558
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
559
402
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
|
|
560
403
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
|
|
561
404
|
await fs.writeFile(path.join(os.tmpdir(), 'shelly.log'), data, 'utf-8');
|
|
@@ -566,90 +409,74 @@ export class Frontend extends EventEmitter {
|
|
|
566
409
|
}
|
|
567
410
|
res.type('text/plain');
|
|
568
411
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
569
|
-
/* istanbul ignore if */
|
|
570
412
|
if (error) {
|
|
571
413
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
572
414
|
res.status(500).send('Error downloading Shelly system log file');
|
|
573
415
|
}
|
|
574
416
|
});
|
|
575
417
|
});
|
|
576
|
-
// Endpoint to download the matterbridge storage directory
|
|
577
418
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
578
419
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
579
420
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
580
421
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), `matterbridge.${this.matterbridge.nodeStorageName}.zip`, (error) => {
|
|
581
|
-
/* istanbul ignore if */
|
|
582
422
|
if (error) {
|
|
583
423
|
this.log.error(`Error downloading file ${`matterbridge.${this.matterbridge.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
584
424
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
585
425
|
}
|
|
586
426
|
});
|
|
587
427
|
});
|
|
588
|
-
// Endpoint to download the matter storage file
|
|
589
428
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
590
429
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
591
430
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
592
431
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), `matterbridge.${this.matterbridge.matterStorageName}.zip`, (error) => {
|
|
593
|
-
/* istanbul ignore if */
|
|
594
432
|
if (error) {
|
|
595
433
|
this.log.error(`Error downloading the matter storage matterbridge.${this.matterbridge.matterStorageName}.zip: ${error instanceof Error ? error.message : error}`);
|
|
596
434
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
597
435
|
}
|
|
598
436
|
});
|
|
599
437
|
});
|
|
600
|
-
// Endpoint to download the matterbridge plugin directory
|
|
601
438
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
602
439
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
603
440
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
604
441
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
605
|
-
/* istanbul ignore if */
|
|
606
442
|
if (error) {
|
|
607
443
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
608
444
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
609
445
|
}
|
|
610
446
|
});
|
|
611
447
|
});
|
|
612
|
-
// Endpoint to download the matterbridge plugin config files
|
|
613
448
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
614
449
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
615
450
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
616
451
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
617
|
-
/* istanbul ignore if */
|
|
618
452
|
if (error) {
|
|
619
453
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
620
454
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
621
455
|
}
|
|
622
456
|
});
|
|
623
457
|
});
|
|
624
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
625
458
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
626
459
|
this.log.debug('The frontend sent /api/download-backup');
|
|
627
460
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
628
|
-
/* istanbul ignore if */
|
|
629
461
|
if (error) {
|
|
630
462
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
631
463
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
632
464
|
}
|
|
633
465
|
});
|
|
634
466
|
});
|
|
635
|
-
// Endpoint to upload a package
|
|
636
467
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
637
468
|
const { filename } = req.body;
|
|
638
469
|
const file = req.file;
|
|
639
|
-
/* istanbul ignore if */
|
|
640
470
|
if (!file || !filename) {
|
|
641
471
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
642
472
|
res.status(400).send('Invalid request: file and filename are required');
|
|
643
473
|
return;
|
|
644
474
|
}
|
|
645
475
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
646
|
-
// Define the path where the plugin file will be saved
|
|
647
476
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
648
477
|
try {
|
|
649
|
-
// Move the uploaded file to the specified path
|
|
650
478
|
await fs.rename(file.path, filePath);
|
|
651
479
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
652
|
-
// Install the plugin package
|
|
653
480
|
if (filename.endsWith('.tgz')) {
|
|
654
481
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
655
482
|
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
@@ -669,7 +496,6 @@ export class Frontend extends EventEmitter {
|
|
|
669
496
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
670
497
|
}
|
|
671
498
|
});
|
|
672
|
-
// Fallback for routing (must be the last route)
|
|
673
499
|
this.expressApp.use((req, res) => {
|
|
674
500
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
|
|
675
501
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -678,16 +504,13 @@ export class Frontend extends EventEmitter {
|
|
|
678
504
|
}
|
|
679
505
|
async stop() {
|
|
680
506
|
this.log.debug('Stopping the frontend...');
|
|
681
|
-
// Remove listeners from the express app
|
|
682
507
|
if (this.expressApp) {
|
|
683
508
|
this.expressApp.removeAllListeners();
|
|
684
509
|
this.expressApp = undefined;
|
|
685
510
|
this.log.debug('Frontend app closed successfully');
|
|
686
511
|
}
|
|
687
|
-
// Close the WebSocket server
|
|
688
512
|
if (this.webSocketServer) {
|
|
689
513
|
this.log.debug('Closing WebSocket server...');
|
|
690
|
-
// Close all active connections
|
|
691
514
|
this.webSocketServer.clients.forEach((client) => {
|
|
692
515
|
if (client.readyState === WebSocket.OPEN) {
|
|
693
516
|
client.close();
|
|
@@ -708,7 +531,6 @@ export class Frontend extends EventEmitter {
|
|
|
708
531
|
this.webSocketServer.removeAllListeners();
|
|
709
532
|
this.webSocketServer = undefined;
|
|
710
533
|
}
|
|
711
|
-
// Close the http server
|
|
712
534
|
if (this.httpServer) {
|
|
713
535
|
this.log.debug('Closing http server...');
|
|
714
536
|
await withTimeout(new Promise((resolve) => {
|
|
@@ -727,7 +549,6 @@ export class Frontend extends EventEmitter {
|
|
|
727
549
|
this.httpServer = undefined;
|
|
728
550
|
this.log.debug('Frontend http server closed successfully');
|
|
729
551
|
}
|
|
730
|
-
// Close the https server
|
|
731
552
|
if (this.httpsServer) {
|
|
732
553
|
this.log.debug('Closing https server...');
|
|
733
554
|
await withTimeout(new Promise((resolve) => {
|
|
@@ -748,7 +569,6 @@ export class Frontend extends EventEmitter {
|
|
|
748
569
|
}
|
|
749
570
|
this.log.debug('Frontend stopped successfully');
|
|
750
571
|
}
|
|
751
|
-
// Function to format bytes to KB, MB, or GB
|
|
752
572
|
formatMemoryUsage = (bytes) => {
|
|
753
573
|
if (bytes >= 1024 ** 3) {
|
|
754
574
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -760,7 +580,6 @@ export class Frontend extends EventEmitter {
|
|
|
760
580
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
761
581
|
}
|
|
762
582
|
};
|
|
763
|
-
// Function to format system uptime with only the most significant unit
|
|
764
583
|
formatOsUpTime = (seconds) => {
|
|
765
584
|
if (seconds >= 86400) {
|
|
766
585
|
const days = Math.floor(seconds / 86400);
|
|
@@ -776,13 +595,7 @@ export class Frontend extends EventEmitter {
|
|
|
776
595
|
}
|
|
777
596
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
778
597
|
};
|
|
779
|
-
/**
|
|
780
|
-
* Retrieves the api settings data.
|
|
781
|
-
*
|
|
782
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
783
|
-
*/
|
|
784
598
|
async getApiSettings() {
|
|
785
|
-
// Update the system information
|
|
786
599
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
787
600
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
788
601
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -791,7 +604,6 @@ export class Frontend extends EventEmitter {
|
|
|
791
604
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
792
605
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
793
606
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
794
|
-
// Update the matterbridge information
|
|
795
607
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
796
608
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
797
609
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
@@ -803,7 +615,6 @@ export class Frontend extends EventEmitter {
|
|
|
803
615
|
this.matterbridge.matterbridgeInformation.matterPort = (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540;
|
|
804
616
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = await this.matterbridge.nodeContext?.get('matterdiscriminator');
|
|
805
617
|
this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
|
|
806
|
-
// Update the matterbridge information in bridge mode
|
|
807
618
|
if (this.matterbridge.bridgeMode === 'bridge' && this.matterbridge.serverNode && !this.matterbridge.hasCleanupStarted) {
|
|
808
619
|
this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.serverNode.state.commissioning.commissioned;
|
|
809
620
|
this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.qrPairingCode;
|
|
@@ -813,12 +624,6 @@ export class Frontend extends EventEmitter {
|
|
|
813
624
|
}
|
|
814
625
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
815
626
|
}
|
|
816
|
-
/**
|
|
817
|
-
* Retrieves the reachable attribute.
|
|
818
|
-
*
|
|
819
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
820
|
-
* @returns {boolean} The reachable attribute.
|
|
821
|
-
*/
|
|
822
627
|
getReachability(device) {
|
|
823
628
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
824
629
|
return false;
|
|
@@ -830,12 +635,6 @@ export class Frontend extends EventEmitter {
|
|
|
830
635
|
return true;
|
|
831
636
|
return false;
|
|
832
637
|
}
|
|
833
|
-
/**
|
|
834
|
-
* Retrieves the power source attribute.
|
|
835
|
-
*
|
|
836
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
837
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
838
|
-
*/
|
|
839
638
|
getPowerSource(endpoint) {
|
|
840
639
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
841
640
|
return undefined;
|
|
@@ -851,21 +650,13 @@ export class Frontend extends EventEmitter {
|
|
|
851
650
|
}
|
|
852
651
|
return;
|
|
853
652
|
};
|
|
854
|
-
// Root endpoint
|
|
855
653
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
856
654
|
return powerSource(endpoint);
|
|
857
|
-
// Child endpoints
|
|
858
655
|
for (const child of endpoint.getChildEndpoints()) {
|
|
859
656
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
860
657
|
return powerSource(child);
|
|
861
658
|
}
|
|
862
659
|
}
|
|
863
|
-
/**
|
|
864
|
-
* Retrieves the commissioned status, matter pairing codes, fabrics and sessions from a given device in server mode.
|
|
865
|
-
*
|
|
866
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the data from.
|
|
867
|
-
* @returns {ApiDevicesMatter | undefined} An ApiDevicesMatter object or undefined if not found.
|
|
868
|
-
*/
|
|
869
660
|
getMatterDataFromDevice(device) {
|
|
870
661
|
if (device.mode === 'server' && device.serverNode) {
|
|
871
662
|
return {
|
|
@@ -877,13 +668,6 @@ export class Frontend extends EventEmitter {
|
|
|
877
668
|
};
|
|
878
669
|
}
|
|
879
670
|
}
|
|
880
|
-
/**
|
|
881
|
-
* Retrieves the cluster text description from a given device.
|
|
882
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
883
|
-
*
|
|
884
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
885
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
886
|
-
*/
|
|
887
671
|
getClusterTextFromDevice(device) {
|
|
888
672
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
889
673
|
return '';
|
|
@@ -908,7 +692,6 @@ export class Frontend extends EventEmitter {
|
|
|
908
692
|
let attributes = '';
|
|
909
693
|
let supportedModes = [];
|
|
910
694
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
911
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
912
695
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
913
696
|
return;
|
|
914
697
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -998,17 +781,11 @@ export class Frontend extends EventEmitter {
|
|
|
998
781
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
999
782
|
attributes += `${getUserLabel(device)} `;
|
|
1000
783
|
});
|
|
1001
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1002
784
|
return attributes.trimStart().trimEnd();
|
|
1003
785
|
}
|
|
1004
|
-
/**
|
|
1005
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
1006
|
-
*
|
|
1007
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
1008
|
-
*/
|
|
1009
786
|
getBaseRegisteredPlugins() {
|
|
1010
787
|
if (this.matterbridge.hasCleanupStarted)
|
|
1011
|
-
return [];
|
|
788
|
+
return [];
|
|
1012
789
|
const baseRegisteredPlugins = [];
|
|
1013
790
|
for (const plugin of this.matterbridge.plugins) {
|
|
1014
791
|
baseRegisteredPlugins.push({
|
|
@@ -1037,7 +814,6 @@ export class Frontend extends EventEmitter {
|
|
|
1037
814
|
schemaJson: plugin.schemaJson,
|
|
1038
815
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1039
816
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1040
|
-
// Childbridge mode specific data
|
|
1041
817
|
paired: plugin.serverNode?.state.commissioning.commissioned,
|
|
1042
818
|
qrPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode?.state.commissioning.pairingCodes.qrPairingCode,
|
|
1043
819
|
manualPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode?.state.commissioning.pairingCodes.manualPairingCode,
|
|
@@ -1047,21 +823,13 @@ export class Frontend extends EventEmitter {
|
|
|
1047
823
|
}
|
|
1048
824
|
return baseRegisteredPlugins;
|
|
1049
825
|
}
|
|
1050
|
-
/**
|
|
1051
|
-
* Retrieves the devices from Matterbridge.
|
|
1052
|
-
*
|
|
1053
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1054
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
|
|
1055
|
-
*/
|
|
1056
826
|
async getDevices(pluginName) {
|
|
1057
827
|
if (this.matterbridge.hasCleanupStarted)
|
|
1058
|
-
return [];
|
|
828
|
+
return [];
|
|
1059
829
|
const devices = [];
|
|
1060
830
|
for (const device of this.matterbridge.devices.array()) {
|
|
1061
|
-
// Filter by pluginName if provided
|
|
1062
831
|
if (pluginName && pluginName !== device.plugin)
|
|
1063
832
|
continue;
|
|
1064
|
-
// Check if the device has the required properties
|
|
1065
833
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1066
834
|
continue;
|
|
1067
835
|
devices.push({
|
|
@@ -1081,37 +849,22 @@ export class Frontend extends EventEmitter {
|
|
|
1081
849
|
}
|
|
1082
850
|
return devices;
|
|
1083
851
|
}
|
|
1084
|
-
/**
|
|
1085
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1086
|
-
*
|
|
1087
|
-
* Response for /api/clusters
|
|
1088
|
-
*
|
|
1089
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1090
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1091
|
-
* @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1092
|
-
*/
|
|
1093
852
|
getClusters(pluginName, endpointNumber) {
|
|
1094
853
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1095
854
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1096
855
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1097
856
|
return;
|
|
1098
857
|
}
|
|
1099
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1100
|
-
// Get the device types from the main endpoint
|
|
1101
858
|
const deviceTypes = [];
|
|
1102
859
|
const clusters = [];
|
|
1103
860
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1104
861
|
deviceTypes.push(d.deviceType);
|
|
1105
862
|
});
|
|
1106
|
-
// Get the clusters from the main endpoint
|
|
1107
863
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1108
864
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1109
865
|
return;
|
|
1110
866
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1111
867
|
return;
|
|
1112
|
-
// console.log(
|
|
1113
|
-
// `${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}`,
|
|
1114
|
-
// );
|
|
1115
868
|
clusters.push({
|
|
1116
869
|
endpoint: endpoint.number.toString(),
|
|
1117
870
|
id: 'main',
|
|
@@ -1124,18 +877,12 @@ export class Frontend extends EventEmitter {
|
|
|
1124
877
|
attributeLocalValue: attributeValue,
|
|
1125
878
|
});
|
|
1126
879
|
});
|
|
1127
|
-
// Get the child endpoints
|
|
1128
880
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1129
|
-
// if (childEndpoints.length === 0) {
|
|
1130
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1131
|
-
// }
|
|
1132
881
|
childEndpoints.forEach((childEndpoint) => {
|
|
1133
882
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1134
883
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1135
884
|
return;
|
|
1136
885
|
}
|
|
1137
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1138
|
-
// Get the device types of the child endpoint
|
|
1139
886
|
const deviceTypes = [];
|
|
1140
887
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1141
888
|
deviceTypes.push(d.deviceType);
|
|
@@ -1145,12 +892,9 @@ export class Frontend extends EventEmitter {
|
|
|
1145
892
|
return;
|
|
1146
893
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1147
894
|
return;
|
|
1148
|
-
// console.log(
|
|
1149
|
-
// `${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}`,
|
|
1150
|
-
// );
|
|
1151
895
|
clusters.push({
|
|
1152
896
|
endpoint: childEndpoint.number.toString(),
|
|
1153
|
-
id: childEndpoint.maybeId ?? 'null',
|
|
897
|
+
id: childEndpoint.maybeId ?? 'null',
|
|
1154
898
|
deviceTypes,
|
|
1155
899
|
clusterName: capitalizeFirstLetter(clusterName),
|
|
1156
900
|
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
@@ -1163,13 +907,6 @@ export class Frontend extends EventEmitter {
|
|
|
1163
907
|
});
|
|
1164
908
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
|
|
1165
909
|
}
|
|
1166
|
-
/**
|
|
1167
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1168
|
-
*
|
|
1169
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1170
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1171
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1172
|
-
*/
|
|
1173
910
|
async wsMessageHandler(client, message) {
|
|
1174
911
|
let data;
|
|
1175
912
|
try {
|
|
@@ -1216,42 +953,32 @@ export class Frontend extends EventEmitter {
|
|
|
1216
953
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1217
954
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1218
955
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1219
|
-
// The install comes from InstallPlugins
|
|
1220
956
|
this.matterbridge.plugins
|
|
1221
957
|
.add(packageName)
|
|
1222
958
|
.then((plugin) => {
|
|
1223
959
|
if (plugin) {
|
|
1224
|
-
// The plugin is not registered
|
|
1225
960
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1226
961
|
this.matterbridge.plugins
|
|
1227
962
|
.load(plugin, true, 'The plugin has been added', true)
|
|
1228
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1229
963
|
.then(() => {
|
|
1230
964
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
1231
965
|
this.wssSendRefreshRequired('plugins');
|
|
1232
966
|
return;
|
|
1233
967
|
})
|
|
1234
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1235
968
|
.catch((_error) => {
|
|
1236
|
-
//
|
|
1237
969
|
});
|
|
1238
970
|
}
|
|
1239
971
|
else {
|
|
1240
|
-
// The plugin is already registered
|
|
1241
972
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1242
973
|
this.wssSendRefreshRequired('plugins');
|
|
1243
974
|
this.wssSendRestartRequired();
|
|
1244
975
|
}
|
|
1245
976
|
return;
|
|
1246
977
|
})
|
|
1247
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1248
978
|
.catch((_error) => {
|
|
1249
|
-
//
|
|
1250
979
|
});
|
|
1251
980
|
}
|
|
1252
981
|
else {
|
|
1253
|
-
// The package is matterbridge
|
|
1254
|
-
// istanbul ignore next if
|
|
1255
982
|
if (this.matterbridge.restartMode !== '') {
|
|
1256
983
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1257
984
|
this.matterbridge.shutdownProcess();
|
|
@@ -1274,7 +1001,6 @@ export class Frontend extends EventEmitter {
|
|
|
1274
1001
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1275
1002
|
return;
|
|
1276
1003
|
}
|
|
1277
|
-
// The package is a plugin
|
|
1278
1004
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1279
1005
|
if (plugin) {
|
|
1280
1006
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
@@ -1283,7 +1009,6 @@ export class Frontend extends EventEmitter {
|
|
|
1283
1009
|
this.wssSendRefreshRequired('plugins');
|
|
1284
1010
|
this.wssSendRefreshRequired('devices');
|
|
1285
1011
|
}
|
|
1286
|
-
// Uninstall the package
|
|
1287
1012
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1288
1013
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
1289
1014
|
spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1324,7 +1049,6 @@ export class Frontend extends EventEmitter {
|
|
|
1324
1049
|
return;
|
|
1325
1050
|
})
|
|
1326
1051
|
.catch((_error) => {
|
|
1327
|
-
//
|
|
1328
1052
|
});
|
|
1329
1053
|
}
|
|
1330
1054
|
else {
|
|
@@ -1371,7 +1095,6 @@ export class Frontend extends EventEmitter {
|
|
|
1371
1095
|
return;
|
|
1372
1096
|
})
|
|
1373
1097
|
.catch((_error) => {
|
|
1374
|
-
//
|
|
1375
1098
|
});
|
|
1376
1099
|
}
|
|
1377
1100
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1482,8 +1205,6 @@ export class Frontend extends EventEmitter {
|
|
|
1482
1205
|
else if (data.method === '/api/advertise') {
|
|
1483
1206
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
1484
1207
|
this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
|
|
1485
|
-
// this.matterbridge.matterbridgeQrPairingCode = pairingCodes?.qrPairingCode;
|
|
1486
|
-
// this.matterbridge.matterbridgeManualPairingCode = pairingCodes?.manualPairingCode;
|
|
1487
1208
|
this.wssSendRefreshRequired('matterbridgeAdvertise');
|
|
1488
1209
|
this.wssSendSnackbarMessage(`Started fabrics share`, 0);
|
|
1489
1210
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes, success: true }));
|
|
@@ -1606,22 +1327,22 @@ export class Frontend extends EventEmitter {
|
|
|
1606
1327
|
if (isValidString(data.params.value, 4)) {
|
|
1607
1328
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1608
1329
|
if (data.params.value === 'Debug') {
|
|
1609
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1330
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1610
1331
|
}
|
|
1611
1332
|
else if (data.params.value === 'Info') {
|
|
1612
|
-
await this.matterbridge.setLogLevel("info"
|
|
1333
|
+
await this.matterbridge.setLogLevel("info");
|
|
1613
1334
|
}
|
|
1614
1335
|
else if (data.params.value === 'Notice') {
|
|
1615
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1336
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1616
1337
|
}
|
|
1617
1338
|
else if (data.params.value === 'Warn') {
|
|
1618
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1339
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1619
1340
|
}
|
|
1620
1341
|
else if (data.params.value === 'Error') {
|
|
1621
|
-
await this.matterbridge.setLogLevel("error"
|
|
1342
|
+
await this.matterbridge.setLogLevel("error");
|
|
1622
1343
|
}
|
|
1623
1344
|
else if (data.params.value === 'Fatal') {
|
|
1624
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1345
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1625
1346
|
}
|
|
1626
1347
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1627
1348
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1632,7 +1353,6 @@ export class Frontend extends EventEmitter {
|
|
|
1632
1353
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1633
1354
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1634
1355
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1635
|
-
// Create the file logger for matterbridge
|
|
1636
1356
|
if (data.params.value)
|
|
1637
1357
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1638
1358
|
else
|
|
@@ -1679,7 +1399,6 @@ export class Frontend extends EventEmitter {
|
|
|
1679
1399
|
});
|
|
1680
1400
|
}
|
|
1681
1401
|
catch (error) {
|
|
1682
|
-
/* istanbul ignore next */
|
|
1683
1402
|
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}`);
|
|
1684
1403
|
}
|
|
1685
1404
|
}
|
|
@@ -1688,7 +1407,6 @@ export class Frontend extends EventEmitter {
|
|
|
1688
1407
|
Logger.removeLogger('matterfilelogger');
|
|
1689
1408
|
}
|
|
1690
1409
|
catch (error) {
|
|
1691
|
-
/* istanbul ignore next */
|
|
1692
1410
|
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}`);
|
|
1693
1411
|
}
|
|
1694
1412
|
}
|
|
@@ -1801,19 +1519,15 @@ export class Frontend extends EventEmitter {
|
|
|
1801
1519
|
return;
|
|
1802
1520
|
}
|
|
1803
1521
|
const config = plugin.configJson;
|
|
1804
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1805
1522
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1806
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1807
1523
|
if (select === 'serial')
|
|
1808
1524
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1809
1525
|
if (select === 'name')
|
|
1810
1526
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1811
1527
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1812
|
-
// Remove postfix from the serial if it exists
|
|
1813
1528
|
if (config.postfix) {
|
|
1814
1529
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1815
1530
|
}
|
|
1816
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1817
1531
|
if (isValidArray(config.whiteList, 1)) {
|
|
1818
1532
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1819
1533
|
config.whiteList.push(data.params.serial);
|
|
@@ -1822,7 +1536,6 @@ export class Frontend extends EventEmitter {
|
|
|
1822
1536
|
config.whiteList.push(data.params.name);
|
|
1823
1537
|
}
|
|
1824
1538
|
}
|
|
1825
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1826
1539
|
if (isValidArray(config.blackList, 1)) {
|
|
1827
1540
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1828
1541
|
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
@@ -1853,9 +1566,7 @@ export class Frontend extends EventEmitter {
|
|
|
1853
1566
|
return;
|
|
1854
1567
|
}
|
|
1855
1568
|
const config = plugin.configJson;
|
|
1856
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1857
1569
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1858
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1859
1570
|
if (select === 'serial')
|
|
1860
1571
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1861
1572
|
if (select === 'name')
|
|
@@ -1864,7 +1575,6 @@ export class Frontend extends EventEmitter {
|
|
|
1864
1575
|
if (config.postfix) {
|
|
1865
1576
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1866
1577
|
}
|
|
1867
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1868
1578
|
if (isValidArray(config.whiteList, 1)) {
|
|
1869
1579
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1870
1580
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
@@ -1873,7 +1583,6 @@ export class Frontend extends EventEmitter {
|
|
|
1873
1583
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1874
1584
|
}
|
|
1875
1585
|
}
|
|
1876
|
-
// Add the serial to the blackList
|
|
1877
1586
|
if (isValidArray(config.blackList)) {
|
|
1878
1587
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1879
1588
|
config.blackList.push(data.params.serial);
|
|
@@ -1907,230 +1616,114 @@ export class Frontend extends EventEmitter {
|
|
|
1907
1616
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1908
1617
|
}
|
|
1909
1618
|
}
|
|
1910
|
-
/**
|
|
1911
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1912
|
-
*
|
|
1913
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1914
|
-
* @param {string} time - The time string of the message
|
|
1915
|
-
* @param {string} name - The logger name of the message
|
|
1916
|
-
* @param {string} message - The content of the message.
|
|
1917
|
-
*
|
|
1918
|
-
* @remarks
|
|
1919
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
1920
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
1921
|
-
* The function sends the message to all connected clients.
|
|
1922
|
-
*/
|
|
1923
1619
|
wssSendMessage(level, time, name, message) {
|
|
1924
1620
|
if (!level || !time || !name || !message)
|
|
1925
1621
|
return;
|
|
1926
|
-
// Remove ANSI escape codes from the message
|
|
1927
|
-
// eslint-disable-next-line no-control-regex
|
|
1928
1622
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1929
|
-
// Remove leading asterisks from the message
|
|
1930
1623
|
message = message.replace(/^\*+/, '');
|
|
1931
|
-
// Replace all occurrences of \t and \n
|
|
1932
1624
|
message = message.replace(/[\t\n]/g, '');
|
|
1933
|
-
// Remove non-printable characters
|
|
1934
|
-
// eslint-disable-next-line no-control-regex
|
|
1935
1625
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1936
|
-
// Replace all occurrences of \" with "
|
|
1937
1626
|
message = message.replace(/\\"/g, '"');
|
|
1938
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1939
1627
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1940
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1941
1628
|
const maxContinuousLength = 100;
|
|
1942
1629
|
const keepStartLength = 20;
|
|
1943
1630
|
const keepEndLength = 20;
|
|
1944
|
-
// Split the message into words
|
|
1945
1631
|
message = message
|
|
1946
1632
|
.split(' ')
|
|
1947
1633
|
.map((word) => {
|
|
1948
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1949
1634
|
if (word.length > maxContinuousLength) {
|
|
1950
1635
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1951
1636
|
}
|
|
1952
1637
|
return word;
|
|
1953
1638
|
})
|
|
1954
1639
|
.join(' ');
|
|
1955
|
-
// Send the message to all connected clients
|
|
1956
1640
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1957
1641
|
if (client.readyState === WebSocket.OPEN) {
|
|
1958
1642
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1959
1643
|
}
|
|
1960
1644
|
});
|
|
1961
1645
|
}
|
|
1962
|
-
/**
|
|
1963
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1964
|
-
*
|
|
1965
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
1966
|
-
* possible values:
|
|
1967
|
-
* - 'matterbridgeLatestVersion'
|
|
1968
|
-
* - 'matterbridgeAdvertise'
|
|
1969
|
-
* - 'online'
|
|
1970
|
-
* - 'offline'
|
|
1971
|
-
* - 'reachability'
|
|
1972
|
-
* - 'settings'
|
|
1973
|
-
* - 'plugins'
|
|
1974
|
-
* - 'pluginsRestart'
|
|
1975
|
-
* - 'devices'
|
|
1976
|
-
* - 'fabrics'
|
|
1977
|
-
* - 'sessions'
|
|
1978
|
-
*/
|
|
1979
1646
|
wssSendRefreshRequired(changed = null) {
|
|
1980
1647
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1981
|
-
// Send the message to all connected clients
|
|
1982
1648
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1983
1649
|
if (client.readyState === WebSocket.OPEN) {
|
|
1984
1650
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1985
1651
|
}
|
|
1986
1652
|
});
|
|
1987
1653
|
}
|
|
1988
|
-
/**
|
|
1989
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1990
|
-
*
|
|
1991
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients.
|
|
1992
|
-
*/
|
|
1993
1654
|
wssSendRestartRequired(snackbar = true) {
|
|
1994
1655
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1995
1656
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1996
1657
|
if (snackbar === true)
|
|
1997
1658
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1998
|
-
// Send the message to all connected clients
|
|
1999
1659
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2000
1660
|
if (client.readyState === WebSocket.OPEN) {
|
|
2001
1661
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
2002
1662
|
}
|
|
2003
1663
|
});
|
|
2004
1664
|
}
|
|
2005
|
-
/**
|
|
2006
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
2007
|
-
*
|
|
2008
|
-
*/
|
|
2009
1665
|
wssSendUpdateRequired() {
|
|
2010
1666
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2011
1667
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
2012
|
-
// Send the message to all connected clients
|
|
2013
1668
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2014
1669
|
if (client.readyState === WebSocket.OPEN) {
|
|
2015
1670
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
2016
1671
|
}
|
|
2017
1672
|
});
|
|
2018
1673
|
}
|
|
2019
|
-
/**
|
|
2020
|
-
* Sends a cpu update message to all connected clients.
|
|
2021
|
-
*
|
|
2022
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2023
|
-
*/
|
|
2024
1674
|
wssSendCpuUpdate(cpuUsage) {
|
|
2025
1675
|
if (hasParameter('debug'))
|
|
2026
1676
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2027
|
-
// Send the message to all connected clients
|
|
2028
1677
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2029
1678
|
if (client.readyState === WebSocket.OPEN) {
|
|
2030
1679
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
2031
1680
|
}
|
|
2032
1681
|
});
|
|
2033
1682
|
}
|
|
2034
|
-
/**
|
|
2035
|
-
* Sends a memory update message to all connected clients.
|
|
2036
|
-
*
|
|
2037
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2038
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2039
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2040
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2041
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2042
|
-
* @param {string} external - The external memory in bytes.
|
|
2043
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2044
|
-
*/
|
|
2045
1683
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2046
1684
|
if (hasParameter('debug'))
|
|
2047
1685
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2048
|
-
// Send the message to all connected clients
|
|
2049
1686
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2050
1687
|
if (client.readyState === WebSocket.OPEN) {
|
|
2051
1688
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
2052
1689
|
}
|
|
2053
1690
|
});
|
|
2054
1691
|
}
|
|
2055
|
-
/**
|
|
2056
|
-
* Sends an uptime update message to all connected clients.
|
|
2057
|
-
*
|
|
2058
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2059
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2060
|
-
*/
|
|
2061
1692
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2062
1693
|
if (hasParameter('debug'))
|
|
2063
1694
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2064
|
-
// Send the message to all connected clients
|
|
2065
1695
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2066
1696
|
if (client.readyState === WebSocket.OPEN) {
|
|
2067
1697
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
2068
1698
|
}
|
|
2069
1699
|
});
|
|
2070
1700
|
}
|
|
2071
|
-
/**
|
|
2072
|
-
* Sends an open snackbar message to all connected clients.
|
|
2073
|
-
*
|
|
2074
|
-
* @param {string} message - The message to send.
|
|
2075
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
2076
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
2077
|
-
*/
|
|
2078
1701
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2079
1702
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2080
|
-
// Send the message to all connected clients
|
|
2081
1703
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2082
1704
|
if (client.readyState === WebSocket.OPEN) {
|
|
2083
1705
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
2084
1706
|
}
|
|
2085
1707
|
});
|
|
2086
1708
|
}
|
|
2087
|
-
/**
|
|
2088
|
-
* Sends a close snackbar message to all connected clients.
|
|
2089
|
-
*
|
|
2090
|
-
* @param {string} message - The message to send.
|
|
2091
|
-
*/
|
|
2092
1709
|
wssSendCloseSnackbarMessage(message) {
|
|
2093
1710
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2094
|
-
// Send the message to all connected clients
|
|
2095
1711
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2096
1712
|
if (client.readyState === WebSocket.OPEN) {
|
|
2097
1713
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
2098
1714
|
}
|
|
2099
1715
|
});
|
|
2100
1716
|
}
|
|
2101
|
-
/**
|
|
2102
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2103
|
-
*
|
|
2104
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2105
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2106
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2107
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2108
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2109
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2110
|
-
*
|
|
2111
|
-
* @remarks
|
|
2112
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2113
|
-
* with the updated attribute information.
|
|
2114
|
-
*/
|
|
2115
1717
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
2116
1718
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2117
|
-
// Send the message to all connected clients
|
|
2118
1719
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2119
1720
|
if (client.readyState === WebSocket.OPEN) {
|
|
2120
1721
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
2121
1722
|
}
|
|
2122
1723
|
});
|
|
2123
1724
|
}
|
|
2124
|
-
/**
|
|
2125
|
-
* Sends a message to all connected clients.
|
|
2126
|
-
*
|
|
2127
|
-
* @param {number} id - The message id.
|
|
2128
|
-
* @param {string} method - The message method.
|
|
2129
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
2130
|
-
*/
|
|
2131
1725
|
wssBroadcastMessage(id, method, params) {
|
|
2132
1726
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
2133
|
-
// Send the message to all connected clients
|
|
2134
1727
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2135
1728
|
if (client.readyState === WebSocket.OPEN) {
|
|
2136
1729
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -2138,4 +1731,3 @@ export class Frontend extends EventEmitter {
|
|
|
2138
1731
|
});
|
|
2139
1732
|
}
|
|
2140
1733
|
}
|
|
2141
|
-
//# sourceMappingURL=frontend.js.map
|