matterbridge 3.3.6 → 3.3.7-dev-20251102-c85d574
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/broadcastServer.js +1 -92
- package/dist/broadcastServerTypes.js +0 -24
- package/dist/cli.js +1 -97
- package/dist/cliEmitter.js +0 -37
- package/dist/cliHistory.js +0 -38
- package/dist/clusters/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -24
- package/dist/deviceManager.js +1 -124
- package/dist/devices/airConditioner.js +0 -57
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/cooktop.js +0 -55
- package/dist/devices/dishwasher.js +0 -57
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.js +0 -5
- package/dist/devices/extractorHood.js +0 -42
- package/dist/devices/heatPump.js +2 -50
- package/dist/devices/laundryDryer.js +3 -62
- package/dist/devices/laundryWasher.js +4 -70
- package/dist/devices/microwaveOven.js +5 -88
- package/dist/devices/oven.js +0 -85
- package/dist/devices/refrigerator.js +0 -102
- package/dist/devices/roboticVacuumCleaner.js +9 -100
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/speaker.js +0 -84
- package/dist/devices/temperatureControl.js +3 -24
- package/dist/devices/waterHeater.js +2 -82
- package/dist/dgram/coap.js +13 -126
- package/dist/dgram/dgram.js +2 -114
- package/dist/dgram/mb_coap.js +3 -41
- package/dist/dgram/mb_mdns.js +15 -80
- package/dist/dgram/mdns.js +137 -299
- package/dist/dgram/multicast.js +1 -62
- package/dist/dgram/unicast.js +0 -54
- package/dist/frontend.js +34 -431
- package/dist/frontendTypes.js +0 -45
- package/dist/helpers.js +0 -53
- package/dist/index.js +0 -25
- 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 +46 -828
- package/dist/matterbridgeAccessoryPlatform.js +0 -37
- package/dist/matterbridgeBehaviors.js +5 -68
- package/dist/matterbridgeDeviceTypes.js +17 -638
- package/dist/matterbridgeDynamicPlatform.js +0 -37
- package/dist/matterbridgeEndpoint.js +52 -1408
- package/dist/matterbridgeEndpointHelpers.js +19 -464
- package/dist/matterbridgePlatform.js +1 -341
- package/dist/matterbridgeTypes.js +0 -26
- package/dist/pluginManager.js +3 -319
- package/dist/shelly.js +7 -168
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -69
- package/dist/utils/colorUtils.js +2 -97
- package/dist/utils/commandLine.js +0 -60
- 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/format.js +0 -49
- package/dist/utils/hex.js +0 -124
- package/dist/utils/inspector.js +1 -69
- package/dist/utils/isvalid.js +0 -101
- package/dist/utils/jestHelpers.js +3 -153
- package/dist/utils/network.js +5 -96
- package/dist/utils/spawn.js +0 -71
- package/dist/utils/tracker.js +1 -64
- package/dist/utils/wait.js +8 -60
- package/frontend/build/assets/index.js +4 -4
- package/frontend/package.json +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- package/dist/broadcastServer.d.ts +0 -112
- package/dist/broadcastServer.d.ts.map +0 -1
- package/dist/broadcastServer.js.map +0 -1
- package/dist/broadcastServerTypes.d.ts +0 -803
- package/dist/broadcastServerTypes.d.ts.map +0 -1
- package/dist/broadcastServerTypes.js.map +0 -1
- package/dist/cli.d.ts +0 -30
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cliEmitter.d.ts +0 -50
- package/dist/cliEmitter.d.ts.map +0 -1
- package/dist/cliEmitter.js.map +0 -1
- package/dist/cliHistory.d.ts +0 -48
- package/dist/cliHistory.d.ts.map +0 -1
- package/dist/cliHistory.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 -117
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/devices/airConditioner.d.ts +0 -98
- package/dist/devices/airConditioner.d.ts.map +0 -1
- package/dist/devices/airConditioner.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/cooktop.d.ts +0 -60
- package/dist/devices/cooktop.d.ts.map +0 -1
- package/dist/devices/cooktop.js.map +0 -1
- package/dist/devices/dishwasher.d.ts +0 -71
- package/dist/devices/dishwasher.d.ts.map +0 -1
- package/dist/devices/dishwasher.js.map +0 -1
- package/dist/devices/evse.d.ts +0 -76
- package/dist/devices/evse.d.ts.map +0 -1
- package/dist/devices/evse.js.map +0 -1
- package/dist/devices/export.d.ts +0 -17
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.js.map +0 -1
- package/dist/devices/extractorHood.d.ts +0 -46
- package/dist/devices/extractorHood.d.ts.map +0 -1
- package/dist/devices/extractorHood.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 -67
- package/dist/devices/laundryDryer.d.ts.map +0 -1
- package/dist/devices/laundryDryer.js.map +0 -1
- package/dist/devices/laundryWasher.d.ts +0 -81
- package/dist/devices/laundryWasher.d.ts.map +0 -1
- package/dist/devices/laundryWasher.js.map +0 -1
- package/dist/devices/microwaveOven.d.ts +0 -168
- package/dist/devices/microwaveOven.d.ts.map +0 -1
- package/dist/devices/microwaveOven.js.map +0 -1
- package/dist/devices/oven.d.ts +0 -105
- package/dist/devices/oven.d.ts.map +0 -1
- package/dist/devices/oven.js.map +0 -1
- package/dist/devices/refrigerator.d.ts +0 -118
- package/dist/devices/refrigerator.d.ts.map +0 -1
- package/dist/devices/refrigerator.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/speaker.d.ts +0 -87
- package/dist/devices/speaker.d.ts.map +0 -1
- package/dist/devices/speaker.js.map +0 -1
- package/dist/devices/temperatureControl.d.ts +0 -166
- package/dist/devices/temperatureControl.d.ts.map +0 -1
- package/dist/devices/temperatureControl.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/dgram/coap.d.ts +0 -205
- package/dist/dgram/coap.d.ts.map +0 -1
- package/dist/dgram/coap.js.map +0 -1
- package/dist/dgram/dgram.d.ts +0 -141
- package/dist/dgram/dgram.d.ts.map +0 -1
- package/dist/dgram/dgram.js.map +0 -1
- package/dist/dgram/mb_coap.d.ts +0 -24
- package/dist/dgram/mb_coap.d.ts.map +0 -1
- package/dist/dgram/mb_coap.js.map +0 -1
- package/dist/dgram/mb_mdns.d.ts +0 -24
- package/dist/dgram/mb_mdns.d.ts.map +0 -1
- package/dist/dgram/mb_mdns.js.map +0 -1
- package/dist/dgram/mdns.d.ts +0 -290
- package/dist/dgram/mdns.d.ts.map +0 -1
- package/dist/dgram/mdns.js.map +0 -1
- package/dist/dgram/multicast.d.ts +0 -67
- package/dist/dgram/multicast.d.ts.map +0 -1
- package/dist/dgram/multicast.js.map +0 -1
- package/dist/dgram/unicast.d.ts +0 -56
- package/dist/dgram/unicast.d.ts.map +0 -1
- package/dist/dgram/unicast.js.map +0 -1
- package/dist/frontend.d.ts +0 -236
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/frontendTypes.d.ts +0 -529
- package/dist/frontendTypes.d.ts.map +0 -1
- package/dist/frontendTypes.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 -476
- 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 -2404
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -770
- 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 -1556
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -758
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -402
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -226
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -347
- 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 -75
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -101
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/commandLine.d.ts +0 -66
- 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 -13
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/format.d.ts +0 -53
- package/dist/utils/format.d.ts.map +0 -1
- package/dist/utils/format.js.map +0 -1
- package/dist/utils/hex.d.ts +0 -89
- package/dist/utils/hex.d.ts.map +0 -1
- package/dist/utils/hex.js.map +0 -1
- package/dist/utils/inspector.d.ts +0 -87
- package/dist/utils/inspector.d.ts.map +0 -1
- package/dist/utils/inspector.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/jestHelpers.d.ts +0 -139
- package/dist/utils/jestHelpers.d.ts.map +0 -1
- package/dist/utils/jestHelpers.js.map +0 -1
- package/dist/utils/network.d.ts +0 -101
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/spawn.d.ts +0 -35
- package/dist/utils/spawn.d.ts.map +0 -1
- package/dist/utils/spawn.js.map +0 -1
- package/dist/utils/tracker.d.ts +0 -108
- package/dist/utils/tracker.d.ts.map +0 -1
- package/dist/utils/tracker.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -54
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
package/dist/frontend.js
CHANGED
|
@@ -1,34 +1,8 @@
|
|
|
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.3.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
|
-
// eslint-disable-next-line no-console
|
|
25
1
|
if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
26
2
|
console.log('\u001B[32mFrontend loaded.\u001B[40;0m');
|
|
27
|
-
// Node modules
|
|
28
3
|
import os from 'node:os';
|
|
29
4
|
import path from 'node:path';
|
|
30
5
|
import EventEmitter from 'node:events';
|
|
31
|
-
// AnsiLogger module
|
|
32
6
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt } from 'node-ansi-logger';
|
|
33
7
|
import { Logger, Diagnostic, LogDestination, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/general';
|
|
34
8
|
import { DeviceAdvertiser, DeviceCommissioner, FabricManager } from '@matter/protocol';
|
|
@@ -61,7 +35,7 @@ export class Frontend extends EventEmitter {
|
|
|
61
35
|
constructor(matterbridge) {
|
|
62
36
|
super();
|
|
63
37
|
this.matterbridge = matterbridge;
|
|
64
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
38
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
65
39
|
this.log.logNameColor = '\x1b[38;5;97m';
|
|
66
40
|
this.server = new BroadcastServer('frontend', this.log);
|
|
67
41
|
this.server.on('broadcast_message', this.msgHandler.bind(this));
|
|
@@ -156,53 +130,23 @@ export class Frontend extends EventEmitter {
|
|
|
156
130
|
this.port = port;
|
|
157
131
|
this.storedPassword = await this.matterbridge.nodeContext?.get('password', '');
|
|
158
132
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
159
|
-
// Initialize multer with the upload directory
|
|
160
133
|
const multer = await import('multer');
|
|
161
|
-
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
134
|
+
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
162
135
|
const upload = multer.default({ dest: uploadDir });
|
|
163
|
-
// Create the express app that serves the frontend
|
|
164
136
|
const express = await import('express');
|
|
165
137
|
this.expressApp = express.default();
|
|
166
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
167
|
-
/*
|
|
168
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
169
|
-
for (const method of methods) {
|
|
170
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
171
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
172
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
173
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
174
|
-
try {
|
|
175
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
176
|
-
return original(path, ...rest);
|
|
177
|
-
} catch (err) {
|
|
178
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
179
|
-
throw err;
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
*/
|
|
184
|
-
// Log all requests to the server for debugging
|
|
185
|
-
/*
|
|
186
|
-
this.expressApp.use((req, res, next) => {
|
|
187
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
188
|
-
next();
|
|
189
|
-
});
|
|
190
|
-
*/
|
|
191
|
-
// Serve static files from 'frontend/build' directory
|
|
192
138
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
193
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
194
139
|
this.log.debug(`Creating WebSocketServer...`);
|
|
195
140
|
const ws = await import('ws');
|
|
196
141
|
this.webSocketServer = new ws.WebSocketServer({ noServer: true });
|
|
197
142
|
this.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
|
|
198
143
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
199
144
|
const clientIp = request.socket.remoteAddress;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
145
|
+
let callbackLogLevel = "notice";
|
|
146
|
+
if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
|
|
147
|
+
callbackLogLevel = "info";
|
|
148
|
+
if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
149
|
+
callbackLogLevel = "debug";
|
|
206
150
|
AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
|
|
207
151
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
208
152
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -224,25 +168,16 @@ export class Frontend extends EventEmitter {
|
|
|
224
168
|
}
|
|
225
169
|
});
|
|
226
170
|
ws.on('error', (error) => {
|
|
227
|
-
// istanbul ignore next
|
|
228
171
|
this.log.error(`WebSocket client error: ${error}`);
|
|
229
172
|
});
|
|
230
173
|
});
|
|
231
174
|
this.webSocketServer.on('close', () => {
|
|
232
175
|
this.log.debug(`WebSocketServer closed`);
|
|
233
176
|
});
|
|
234
|
-
/* With { noServer: true } it never fires
|
|
235
|
-
this.webSocketServer.on('listening', () => {
|
|
236
|
-
this.log.info(`The WebSocketServer is listening`);
|
|
237
|
-
this.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
|
|
238
|
-
});
|
|
239
|
-
*/
|
|
240
|
-
// istanbul ignore next
|
|
241
177
|
this.webSocketServer.on('error', (ws, error) => {
|
|
242
178
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
243
179
|
});
|
|
244
180
|
if (!hasParameter('ssl')) {
|
|
245
|
-
// Create an HTTP server and attach the express app
|
|
246
181
|
const http = await import('node:http');
|
|
247
182
|
try {
|
|
248
183
|
this.log.debug(`Creating HTTP server...`);
|
|
@@ -253,7 +188,6 @@ export class Frontend extends EventEmitter {
|
|
|
253
188
|
this.emit('server_error', error);
|
|
254
189
|
return;
|
|
255
190
|
}
|
|
256
|
-
// Listen on the specified port
|
|
257
191
|
if (hasParameter('ingress')) {
|
|
258
192
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
259
193
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -273,29 +207,23 @@ export class Frontend extends EventEmitter {
|
|
|
273
207
|
}
|
|
274
208
|
this.httpServer.on('upgrade', async (req, socket, head) => {
|
|
275
209
|
try {
|
|
276
|
-
// Only proceed for real WebSocket upgrades
|
|
277
|
-
// istanbul ignore next cause is only a safety check
|
|
278
210
|
if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
|
|
279
211
|
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
280
212
|
return socket.destroy();
|
|
281
213
|
}
|
|
282
|
-
// Build a URL so we can read ?password=...
|
|
283
214
|
const url = new URL(req.url ?? '/', `http://${req.headers.host || 'localhost'}`);
|
|
284
|
-
// Validate WebSocket password
|
|
285
215
|
const password = url.searchParams.get('password') ?? '';
|
|
286
216
|
if (password !== this.storedPassword) {
|
|
287
217
|
this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
|
|
288
218
|
socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
|
|
289
219
|
return socket.destroy();
|
|
290
220
|
}
|
|
291
|
-
// Complete the WebSocket handshake
|
|
292
221
|
this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
|
|
293
222
|
this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
|
|
294
223
|
this.webSocketServer?.emit('connection', ws, req);
|
|
295
224
|
});
|
|
296
225
|
}
|
|
297
226
|
catch (err) {
|
|
298
|
-
/* istanbul ignore next: only triggered on unexpected internal error */
|
|
299
227
|
{
|
|
300
228
|
inspectError(this.log, 'WebSocket upgrade error:', err);
|
|
301
229
|
socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
|
|
@@ -318,7 +246,6 @@ export class Frontend extends EventEmitter {
|
|
|
318
246
|
});
|
|
319
247
|
}
|
|
320
248
|
else {
|
|
321
|
-
// SSL is enabled, load the certificate and the private key
|
|
322
249
|
let cert;
|
|
323
250
|
let key;
|
|
324
251
|
let ca;
|
|
@@ -328,7 +255,6 @@ export class Frontend extends EventEmitter {
|
|
|
328
255
|
let httpsServerOptions = {};
|
|
329
256
|
const fs = await import('node:fs');
|
|
330
257
|
if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
|
|
331
|
-
// Load the p12 certificate and the passphrase
|
|
332
258
|
try {
|
|
333
259
|
pfx = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
|
|
334
260
|
this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
|
|
@@ -340,7 +266,7 @@ export class Frontend extends EventEmitter {
|
|
|
340
266
|
}
|
|
341
267
|
try {
|
|
342
268
|
passphrase = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
|
|
343
|
-
passphrase = passphrase.trim();
|
|
269
|
+
passphrase = passphrase.trim();
|
|
344
270
|
this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
|
|
345
271
|
}
|
|
346
272
|
catch (error) {
|
|
@@ -351,7 +277,6 @@ export class Frontend extends EventEmitter {
|
|
|
351
277
|
httpsServerOptions = { pfx, passphrase };
|
|
352
278
|
}
|
|
353
279
|
else {
|
|
354
|
-
// 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.
|
|
355
280
|
try {
|
|
356
281
|
cert = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
357
282
|
this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
|
|
@@ -381,10 +306,9 @@ export class Frontend extends EventEmitter {
|
|
|
381
306
|
httpsServerOptions = { cert: fullChain ?? cert, key, ca };
|
|
382
307
|
}
|
|
383
308
|
if (hasParameter('mtls')) {
|
|
384
|
-
httpsServerOptions.requestCert = true;
|
|
385
|
-
httpsServerOptions.rejectUnauthorized = true;
|
|
309
|
+
httpsServerOptions.requestCert = true;
|
|
310
|
+
httpsServerOptions.rejectUnauthorized = true;
|
|
386
311
|
}
|
|
387
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
388
312
|
const https = await import('node:https');
|
|
389
313
|
try {
|
|
390
314
|
this.log.debug(`Creating HTTPS server...`);
|
|
@@ -395,7 +319,6 @@ export class Frontend extends EventEmitter {
|
|
|
395
319
|
this.emit('server_error', error);
|
|
396
320
|
return;
|
|
397
321
|
}
|
|
398
|
-
// Listen on the specified port
|
|
399
322
|
if (hasParameter('ingress')) {
|
|
400
323
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
401
324
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -415,29 +338,23 @@ export class Frontend extends EventEmitter {
|
|
|
415
338
|
}
|
|
416
339
|
this.httpsServer.on('upgrade', async (req, socket, head) => {
|
|
417
340
|
try {
|
|
418
|
-
// Only proceed for real WebSocket upgrades
|
|
419
|
-
// istanbul ignore next cause is only a safety check
|
|
420
341
|
if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
|
|
421
342
|
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
422
343
|
return socket.destroy();
|
|
423
344
|
}
|
|
424
|
-
// Build a URL so we can read ?password=...
|
|
425
345
|
const url = new URL(req.url ?? '/', `https://${req.headers.host || 'localhost'}`);
|
|
426
|
-
// Validate WebSocket password
|
|
427
346
|
const password = url.searchParams.get('password') ?? '';
|
|
428
347
|
if (password !== this.storedPassword) {
|
|
429
348
|
this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
|
|
430
349
|
socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
|
|
431
350
|
return socket.destroy();
|
|
432
351
|
}
|
|
433
|
-
// Complete the WebSocket handshake
|
|
434
352
|
this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
|
|
435
353
|
this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
|
|
436
354
|
this.webSocketServer?.emit('connection', ws, req);
|
|
437
355
|
});
|
|
438
356
|
}
|
|
439
357
|
catch (err) {
|
|
440
|
-
/* istanbul ignore next: only triggered on unexpected internal error */
|
|
441
358
|
{
|
|
442
359
|
inspectError(this.log, 'WebSocket upgrade error:', err);
|
|
443
360
|
socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
|
|
@@ -459,7 +376,6 @@ export class Frontend extends EventEmitter {
|
|
|
459
376
|
return;
|
|
460
377
|
});
|
|
461
378
|
}
|
|
462
|
-
// Subscribe to cli events
|
|
463
379
|
cliEmitter.removeAllListeners();
|
|
464
380
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
465
381
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -470,8 +386,6 @@ export class Frontend extends EventEmitter {
|
|
|
470
386
|
cliEmitter.on('cpu', (cpuUsage, processCpuUsage) => {
|
|
471
387
|
this.wssSendCpuUpdate(cpuUsage, processCpuUsage);
|
|
472
388
|
});
|
|
473
|
-
// Endpoint to validate login code
|
|
474
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
475
389
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
476
390
|
const { password } = req.body;
|
|
477
391
|
this.log.debug(`The frontend sent /api/login with password ${password ? '[redacted]' : '(empty)'}`);
|
|
@@ -484,20 +398,17 @@ export class Frontend extends EventEmitter {
|
|
|
484
398
|
res.json({ valid: false });
|
|
485
399
|
}
|
|
486
400
|
});
|
|
487
|
-
// Endpoint to provide health check for docker
|
|
488
401
|
this.expressApp.get('/health', (req, res) => {
|
|
489
402
|
this.log.debug('Express received /health');
|
|
490
403
|
const healthStatus = {
|
|
491
|
-
status: 'ok',
|
|
492
|
-
uptime: process.uptime(),
|
|
493
|
-
timestamp: new Date().toISOString(),
|
|
404
|
+
status: 'ok',
|
|
405
|
+
uptime: process.uptime(),
|
|
406
|
+
timestamp: new Date().toISOString(),
|
|
494
407
|
};
|
|
495
408
|
res.status(200).json(healthStatus);
|
|
496
409
|
});
|
|
497
|
-
// Endpoint to provide memory usage details
|
|
498
410
|
this.expressApp.get('/memory', async (req, res) => {
|
|
499
411
|
this.log.debug('Express received /memory');
|
|
500
|
-
// Memory usage from process
|
|
501
412
|
const memoryUsageRaw = process.memoryUsage();
|
|
502
413
|
const memoryUsage = {
|
|
503
414
|
rss: formatBytes(memoryUsageRaw.rss),
|
|
@@ -506,13 +417,10 @@ export class Frontend extends EventEmitter {
|
|
|
506
417
|
external: formatBytes(memoryUsageRaw.external),
|
|
507
418
|
arrayBuffers: formatBytes(memoryUsageRaw.arrayBuffers),
|
|
508
419
|
};
|
|
509
|
-
// V8 heap statistics
|
|
510
420
|
const { default: v8 } = await import('node:v8');
|
|
511
421
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
512
422
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
513
|
-
// Format heapStats
|
|
514
423
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, formatBytes(value)]));
|
|
515
|
-
// Format heapSpaces
|
|
516
424
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
517
425
|
...space,
|
|
518
426
|
space_size: formatBytes(space.space_size),
|
|
@@ -531,22 +439,18 @@ export class Frontend extends EventEmitter {
|
|
|
531
439
|
};
|
|
532
440
|
res.status(200).json(memoryReport);
|
|
533
441
|
});
|
|
534
|
-
// Endpoint to provide settings
|
|
535
442
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
536
443
|
this.log.debug('The frontend sent /api/settings');
|
|
537
444
|
res.json(await this.getApiSettings());
|
|
538
445
|
});
|
|
539
|
-
// Endpoint to provide plugins
|
|
540
446
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
541
447
|
this.log.debug('The frontend sent /api/plugins');
|
|
542
448
|
res.json(this.matterbridge.hasCleanupStarted ? [] : this.getPlugins());
|
|
543
449
|
});
|
|
544
|
-
// Endpoint to provide devices
|
|
545
450
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
546
451
|
this.log.debug('The frontend sent /api/devices');
|
|
547
452
|
res.json(this.matterbridge.hasCleanupStarted ? [] : this.getDevices());
|
|
548
453
|
});
|
|
549
|
-
// Endpoint to view the matterbridge log
|
|
550
454
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
551
455
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
552
456
|
try {
|
|
@@ -560,7 +464,6 @@ export class Frontend extends EventEmitter {
|
|
|
560
464
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
561
465
|
}
|
|
562
466
|
});
|
|
563
|
-
// Endpoint to view the matter.js log
|
|
564
467
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
565
468
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
566
469
|
try {
|
|
@@ -574,7 +477,6 @@ export class Frontend extends EventEmitter {
|
|
|
574
477
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
575
478
|
}
|
|
576
479
|
});
|
|
577
|
-
// Endpoint to view the diagnostic.log
|
|
578
480
|
this.expressApp.get('/api/view-diagnostic', async (req, res) => {
|
|
579
481
|
this.log.debug('The frontend sent /api/view-diagnostic');
|
|
580
482
|
await this.generateDiagnostic();
|
|
@@ -585,13 +487,10 @@ export class Frontend extends EventEmitter {
|
|
|
585
487
|
res.send(data.slice(29));
|
|
586
488
|
}
|
|
587
489
|
catch (error) {
|
|
588
|
-
// istanbul ignore next
|
|
589
490
|
this.log.error(`Error reading diagnostic log file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
590
|
-
// istanbul ignore next
|
|
591
491
|
res.status(500).send('Error reading diagnostic log file.');
|
|
592
492
|
}
|
|
593
493
|
});
|
|
594
|
-
// Endpoint to download the diagnostic.log
|
|
595
494
|
this.expressApp.get('/api/download-diagnostic', async (req, res) => {
|
|
596
495
|
this.log.debug(`The frontend sent /api/download-diagnostic`);
|
|
597
496
|
await this.generateDiagnostic();
|
|
@@ -602,19 +501,16 @@ export class Frontend extends EventEmitter {
|
|
|
602
501
|
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), data, 'utf-8');
|
|
603
502
|
}
|
|
604
503
|
catch (error) {
|
|
605
|
-
// istanbul ignore next
|
|
606
504
|
this.log.debug(`Error in /api/download-diagnostic: ${error instanceof Error ? error.message : error}`);
|
|
607
505
|
}
|
|
608
506
|
res.type('text/plain');
|
|
609
507
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), MATTERBRIDGE_DIAGNOSTIC_FILE, (error) => {
|
|
610
|
-
/* istanbul ignore if */
|
|
611
508
|
if (error) {
|
|
612
509
|
this.log.error(`Error downloading file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
613
510
|
res.status(500).send('Error downloading the diagnostic log file');
|
|
614
511
|
}
|
|
615
512
|
});
|
|
616
513
|
});
|
|
617
|
-
// Endpoint to view the history.html
|
|
618
514
|
this.expressApp.get('/api/viewhistory', async (req, res) => {
|
|
619
515
|
this.log.debug('The frontend sent /api/viewhistory');
|
|
620
516
|
try {
|
|
@@ -628,7 +524,6 @@ export class Frontend extends EventEmitter {
|
|
|
628
524
|
res.status(500).send('Error reading history file.');
|
|
629
525
|
}
|
|
630
526
|
});
|
|
631
|
-
// Endpoint to download the history.html
|
|
632
527
|
this.expressApp.get('/api/downloadhistory', async (req, res) => {
|
|
633
528
|
this.log.debug(`The frontend sent /api/downloadhistory`);
|
|
634
529
|
try {
|
|
@@ -638,7 +533,6 @@ export class Frontend extends EventEmitter {
|
|
|
638
533
|
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), data, 'utf-8');
|
|
639
534
|
res.type('text/plain');
|
|
640
535
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), MATTERBRIDGE_HISTORY_FILE, (error) => {
|
|
641
|
-
/* istanbul ignore if */
|
|
642
536
|
if (error) {
|
|
643
537
|
this.log.error(`Error in /api/downloadhistory downloading history file ${MATTERBRIDGE_HISTORY_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
644
538
|
res.status(500).send('Error downloading history file');
|
|
@@ -650,7 +544,6 @@ export class Frontend extends EventEmitter {
|
|
|
650
544
|
res.status(500).send('Error reading history file.');
|
|
651
545
|
}
|
|
652
546
|
});
|
|
653
|
-
// Endpoint to view the shelly log
|
|
654
547
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
655
548
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
656
549
|
try {
|
|
@@ -664,7 +557,6 @@ export class Frontend extends EventEmitter {
|
|
|
664
557
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
665
558
|
}
|
|
666
559
|
});
|
|
667
|
-
// Endpoint to download the matterbridge log
|
|
668
560
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
669
561
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
|
|
670
562
|
const fs = await import('node:fs');
|
|
@@ -679,14 +571,12 @@ export class Frontend extends EventEmitter {
|
|
|
679
571
|
}
|
|
680
572
|
res.type('text/plain');
|
|
681
573
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_LOGGER_FILE), 'matterbridge.log', (error) => {
|
|
682
|
-
/* istanbul ignore if */
|
|
683
574
|
if (error) {
|
|
684
575
|
this.log.error(`Error downloading log file ${MATTERBRIDGE_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
685
576
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
686
577
|
}
|
|
687
578
|
});
|
|
688
579
|
});
|
|
689
|
-
// Endpoint to download the matter log
|
|
690
580
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
691
581
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
|
|
692
582
|
const fs = await import('node:fs');
|
|
@@ -701,14 +591,12 @@ export class Frontend extends EventEmitter {
|
|
|
701
591
|
}
|
|
702
592
|
res.type('text/plain');
|
|
703
593
|
res.download(path.join(os.tmpdir(), MATTER_LOGGER_FILE), 'matter.log', (error) => {
|
|
704
|
-
/* istanbul ignore if */
|
|
705
594
|
if (error) {
|
|
706
595
|
this.log.error(`Error downloading log file ${MATTER_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
707
596
|
res.status(500).send('Error downloading the matter log file');
|
|
708
597
|
}
|
|
709
598
|
});
|
|
710
599
|
});
|
|
711
|
-
// Endpoint to download the shelly log
|
|
712
600
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
713
601
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
714
602
|
const fs = await import('node:fs');
|
|
@@ -723,91 +611,75 @@ export class Frontend extends EventEmitter {
|
|
|
723
611
|
}
|
|
724
612
|
res.type('text/plain');
|
|
725
613
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
726
|
-
/* istanbul ignore if */
|
|
727
614
|
if (error) {
|
|
728
615
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
729
616
|
res.status(500).send('Error downloading Shelly system log file');
|
|
730
617
|
}
|
|
731
618
|
});
|
|
732
619
|
});
|
|
733
|
-
// Endpoint to download the matterbridge storage directory
|
|
734
620
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
735
621
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
736
622
|
await createZip(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), path.join(this.matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR));
|
|
737
623
|
res.download(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), `matterbridge.${NODE_STORAGE_DIR}.zip`, (error) => {
|
|
738
|
-
/* istanbul ignore if */
|
|
739
624
|
if (error) {
|
|
740
625
|
this.log.error(`Error downloading file ${`matterbridge.${NODE_STORAGE_DIR}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
741
626
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
742
627
|
}
|
|
743
628
|
});
|
|
744
629
|
});
|
|
745
|
-
// Endpoint to download the matter storage file
|
|
746
630
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
747
631
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
748
632
|
await createZip(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME));
|
|
749
633
|
res.download(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), `matterbridge.${MATTER_STORAGE_NAME}.zip`, (error) => {
|
|
750
|
-
/* istanbul ignore if */
|
|
751
634
|
if (error) {
|
|
752
635
|
this.log.error(`Error downloading the matter storage matterbridge.${MATTER_STORAGE_NAME}.zip: ${error instanceof Error ? error.message : error}`);
|
|
753
636
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
754
637
|
}
|
|
755
638
|
});
|
|
756
639
|
});
|
|
757
|
-
// Endpoint to download the matterbridge plugin directory
|
|
758
640
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
759
641
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
760
642
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
761
643
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
762
|
-
/* istanbul ignore if */
|
|
763
644
|
if (error) {
|
|
764
645
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
765
646
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
766
647
|
}
|
|
767
648
|
});
|
|
768
649
|
});
|
|
769
|
-
// Endpoint to download the matterbridge plugin config files
|
|
770
650
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
771
651
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
772
652
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
773
653
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
774
|
-
/* istanbul ignore if */
|
|
775
654
|
if (error) {
|
|
776
655
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
777
656
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
778
657
|
}
|
|
779
658
|
});
|
|
780
659
|
});
|
|
781
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
782
660
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
783
661
|
this.log.debug('The frontend sent /api/download-backup');
|
|
784
662
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
785
|
-
/* istanbul ignore if */
|
|
786
663
|
if (error) {
|
|
787
664
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
788
665
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
789
666
|
}
|
|
790
667
|
});
|
|
791
668
|
});
|
|
792
|
-
// Endpoint to upload a package
|
|
793
669
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
794
670
|
const { filename } = req.body;
|
|
795
671
|
const file = req.file;
|
|
796
|
-
/* istanbul ignore if */
|
|
797
672
|
if (!file || !filename) {
|
|
798
673
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
799
674
|
res.status(400).send('Invalid request: file and filename are required');
|
|
800
675
|
return;
|
|
801
676
|
}
|
|
802
677
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
803
|
-
// Define the path where the plugin file will be saved
|
|
804
678
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
805
679
|
try {
|
|
806
|
-
// Move the uploaded file to the specified path
|
|
807
680
|
const fs = await import('node:fs');
|
|
808
681
|
await fs.promises.rename(file.path, filePath);
|
|
809
682
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
810
|
-
// Install the plugin package
|
|
811
683
|
if (filename.endsWith('.tgz')) {
|
|
812
684
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
813
685
|
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], 'install', filename);
|
|
@@ -827,7 +699,6 @@ export class Frontend extends EventEmitter {
|
|
|
827
699
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
828
700
|
}
|
|
829
701
|
});
|
|
830
|
-
// Fallback for routing (must be the last route)
|
|
831
702
|
this.expressApp.use((req, res) => {
|
|
832
703
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
|
|
833
704
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -837,16 +708,13 @@ export class Frontend extends EventEmitter {
|
|
|
837
708
|
async stop() {
|
|
838
709
|
this.log.debug('Stopping the frontend...');
|
|
839
710
|
const ws = await import('ws');
|
|
840
|
-
// Remove listeners from the express app
|
|
841
711
|
if (this.expressApp) {
|
|
842
712
|
this.expressApp.removeAllListeners();
|
|
843
713
|
this.expressApp = undefined;
|
|
844
714
|
this.log.debug('Frontend app closed successfully');
|
|
845
715
|
}
|
|
846
|
-
// Close the WebSocket server
|
|
847
716
|
if (this.webSocketServer) {
|
|
848
717
|
this.log.debug('Closing WebSocket server...');
|
|
849
|
-
// Close all active connections
|
|
850
718
|
this.webSocketServer.clients.forEach((client) => {
|
|
851
719
|
if (client.readyState === ws.WebSocket.OPEN) {
|
|
852
720
|
client.close();
|
|
@@ -855,7 +723,6 @@ export class Frontend extends EventEmitter {
|
|
|
855
723
|
await withTimeout(new Promise((resolve) => {
|
|
856
724
|
this.webSocketServer?.close((error) => {
|
|
857
725
|
if (error) {
|
|
858
|
-
// istanbul ignore next
|
|
859
726
|
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
860
727
|
}
|
|
861
728
|
else {
|
|
@@ -868,27 +735,8 @@ export class Frontend extends EventEmitter {
|
|
|
868
735
|
this.webSocketServer.removeAllListeners();
|
|
869
736
|
this.webSocketServer = undefined;
|
|
870
737
|
}
|
|
871
|
-
// Close the http server
|
|
872
738
|
if (this.httpServer) {
|
|
873
739
|
this.log.debug('Closing http server...');
|
|
874
|
-
/*
|
|
875
|
-
await withTimeout(
|
|
876
|
-
new Promise<void>((resolve) => {
|
|
877
|
-
this.httpServer?.close((error) => {
|
|
878
|
-
if (error) {
|
|
879
|
-
// istanbul ignore next
|
|
880
|
-
this.log.error(`Error closing http server: ${error}`);
|
|
881
|
-
} else {
|
|
882
|
-
this.log.debug('Http server closed successfully');
|
|
883
|
-
this.emit('server_stopped');
|
|
884
|
-
}
|
|
885
|
-
resolve();
|
|
886
|
-
});
|
|
887
|
-
}),
|
|
888
|
-
5000,
|
|
889
|
-
false,
|
|
890
|
-
);
|
|
891
|
-
*/
|
|
892
740
|
this.httpServer.close();
|
|
893
741
|
this.log.debug('Http server closed successfully');
|
|
894
742
|
this.listening = false;
|
|
@@ -897,27 +745,8 @@ export class Frontend extends EventEmitter {
|
|
|
897
745
|
this.httpServer = undefined;
|
|
898
746
|
this.log.debug('Frontend http server closed successfully');
|
|
899
747
|
}
|
|
900
|
-
// Close the https server
|
|
901
748
|
if (this.httpsServer) {
|
|
902
749
|
this.log.debug('Closing https server...');
|
|
903
|
-
/*
|
|
904
|
-
await withTimeout(
|
|
905
|
-
new Promise<void>((resolve) => {
|
|
906
|
-
this.httpsServer?.close((error) => {
|
|
907
|
-
if (error) {
|
|
908
|
-
// istanbul ignore next
|
|
909
|
-
this.log.error(`Error closing https server: ${error}`);
|
|
910
|
-
} else {
|
|
911
|
-
this.log.debug('Https server closed successfully');
|
|
912
|
-
this.emit('server_stopped');
|
|
913
|
-
}
|
|
914
|
-
resolve();
|
|
915
|
-
});
|
|
916
|
-
}),
|
|
917
|
-
5000,
|
|
918
|
-
false,
|
|
919
|
-
);
|
|
920
|
-
*/
|
|
921
750
|
this.httpsServer.close();
|
|
922
751
|
this.log.debug('Https server closed successfully');
|
|
923
752
|
this.listening = false;
|
|
@@ -928,13 +757,7 @@ export class Frontend extends EventEmitter {
|
|
|
928
757
|
}
|
|
929
758
|
this.log.debug('Frontend stopped successfully');
|
|
930
759
|
}
|
|
931
|
-
/**
|
|
932
|
-
* Retrieves the api settings data.
|
|
933
|
-
*
|
|
934
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
935
|
-
*/
|
|
936
760
|
async getApiSettings() {
|
|
937
|
-
// Update the variable system information properties
|
|
938
761
|
this.matterbridge.systemInformation.totalMemory = formatBytes(os.totalmem());
|
|
939
762
|
this.matterbridge.systemInformation.freeMemory = formatBytes(os.freemem());
|
|
940
763
|
this.matterbridge.systemInformation.systemUptime = formatUptime(os.uptime());
|
|
@@ -944,7 +767,6 @@ export class Frontend extends EventEmitter {
|
|
|
944
767
|
this.matterbridge.systemInformation.rss = formatBytes(process.memoryUsage().rss);
|
|
945
768
|
this.matterbridge.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
|
|
946
769
|
this.matterbridge.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
|
|
947
|
-
// Create the matterbridge information
|
|
948
770
|
const info = {
|
|
949
771
|
homeDirectory: this.matterbridge.homeDirectory,
|
|
950
772
|
rootDirectory: this.matterbridge.rootDirectory,
|
|
@@ -980,15 +802,9 @@ export class Frontend extends EventEmitter {
|
|
|
980
802
|
};
|
|
981
803
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: info };
|
|
982
804
|
}
|
|
983
|
-
/**
|
|
984
|
-
* Retrieves the reachable attribute.
|
|
985
|
-
*
|
|
986
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
987
|
-
* @returns {boolean} The reachable attribute.
|
|
988
|
-
*/
|
|
989
805
|
getReachability(device) {
|
|
990
806
|
if (this.matterbridge.hasCleanupStarted)
|
|
991
|
-
return false;
|
|
807
|
+
return false;
|
|
992
808
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
993
809
|
return false;
|
|
994
810
|
if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
|
|
@@ -999,15 +815,9 @@ export class Frontend extends EventEmitter {
|
|
|
999
815
|
return true;
|
|
1000
816
|
return false;
|
|
1001
817
|
}
|
|
1002
|
-
/**
|
|
1003
|
-
* Retrieves the power source attribute.
|
|
1004
|
-
*
|
|
1005
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
1006
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
1007
|
-
*/
|
|
1008
818
|
getPowerSource(endpoint) {
|
|
1009
819
|
if (this.matterbridge.hasCleanupStarted)
|
|
1010
|
-
return;
|
|
820
|
+
return;
|
|
1011
821
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
1012
822
|
return undefined;
|
|
1013
823
|
const powerSource = (device) => {
|
|
@@ -1022,25 +832,16 @@ export class Frontend extends EventEmitter {
|
|
|
1022
832
|
}
|
|
1023
833
|
return;
|
|
1024
834
|
};
|
|
1025
|
-
// Root endpoint
|
|
1026
835
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
1027
836
|
return powerSource(endpoint);
|
|
1028
|
-
// Child endpoints
|
|
1029
837
|
for (const child of endpoint.getChildEndpoints()) {
|
|
1030
838
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
1031
839
|
return powerSource(child);
|
|
1032
840
|
}
|
|
1033
841
|
}
|
|
1034
|
-
/**
|
|
1035
|
-
* Retrieves the cluster text description from a given device.
|
|
1036
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
1037
|
-
*
|
|
1038
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
1039
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
1040
|
-
*/
|
|
1041
842
|
getClusterTextFromDevice(device) {
|
|
1042
843
|
if (this.matterbridge.hasCleanupStarted)
|
|
1043
|
-
return '';
|
|
844
|
+
return '';
|
|
1044
845
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1045
846
|
return '';
|
|
1046
847
|
const getUserLabel = (device) => {
|
|
@@ -1050,7 +851,6 @@ export class Frontend extends EventEmitter {
|
|
|
1050
851
|
if (composed)
|
|
1051
852
|
return 'Composed: ' + composed.value;
|
|
1052
853
|
}
|
|
1053
|
-
// istanbul ignore next cause is not reachable
|
|
1054
854
|
return '';
|
|
1055
855
|
};
|
|
1056
856
|
const getFixedLabel = (device) => {
|
|
@@ -1060,13 +860,11 @@ export class Frontend extends EventEmitter {
|
|
|
1060
860
|
if (composed)
|
|
1061
861
|
return 'Composed: ' + composed.value;
|
|
1062
862
|
}
|
|
1063
|
-
// istanbul ignore next cause is not reacheable
|
|
1064
863
|
return '';
|
|
1065
864
|
};
|
|
1066
865
|
let attributes = '';
|
|
1067
866
|
let supportedModes = [];
|
|
1068
867
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1069
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
1070
868
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1071
869
|
return;
|
|
1072
870
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1156,17 +954,11 @@ export class Frontend extends EventEmitter {
|
|
|
1156
954
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1157
955
|
attributes += `${getUserLabel(device)} `;
|
|
1158
956
|
});
|
|
1159
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1160
957
|
return attributes.trimStart().trimEnd();
|
|
1161
958
|
}
|
|
1162
|
-
/**
|
|
1163
|
-
* Retrieves the registered plugins sanitized for res.json().
|
|
1164
|
-
*
|
|
1165
|
-
* @returns {ApiPlugin[]} An array of BaseRegisteredPlugin.
|
|
1166
|
-
*/
|
|
1167
959
|
getPlugins() {
|
|
1168
960
|
if (this.matterbridge.hasCleanupStarted)
|
|
1169
|
-
return [];
|
|
961
|
+
return [];
|
|
1170
962
|
const plugins = [];
|
|
1171
963
|
for (const plugin of this.matterbridge.plugins.array()) {
|
|
1172
964
|
plugins.push({
|
|
@@ -1194,27 +986,18 @@ export class Frontend extends EventEmitter {
|
|
|
1194
986
|
schemaJson: plugin.schemaJson,
|
|
1195
987
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1196
988
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1197
|
-
// Childbridge mode specific data
|
|
1198
989
|
matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
|
|
1199
990
|
});
|
|
1200
991
|
}
|
|
1201
992
|
return plugins;
|
|
1202
993
|
}
|
|
1203
|
-
/**
|
|
1204
|
-
* Retrieves the devices from Matterbridge.
|
|
1205
|
-
*
|
|
1206
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1207
|
-
* @returns {ApiDevice[]} An array of ApiDevices for the frontend.
|
|
1208
|
-
*/
|
|
1209
994
|
getDevices(pluginName) {
|
|
1210
995
|
if (this.matterbridge.hasCleanupStarted)
|
|
1211
|
-
return [];
|
|
996
|
+
return [];
|
|
1212
997
|
const devices = [];
|
|
1213
998
|
for (const device of this.matterbridge.devices.array()) {
|
|
1214
|
-
// Filter by pluginName if provided
|
|
1215
999
|
if (pluginName && pluginName !== device.plugin)
|
|
1216
1000
|
continue;
|
|
1217
|
-
// Check if the device has the required properties
|
|
1218
1001
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1219
1002
|
continue;
|
|
1220
1003
|
devices.push({
|
|
@@ -1234,39 +1017,24 @@ export class Frontend extends EventEmitter {
|
|
|
1234
1017
|
}
|
|
1235
1018
|
return devices;
|
|
1236
1019
|
}
|
|
1237
|
-
/**
|
|
1238
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1239
|
-
*
|
|
1240
|
-
* Response for /api/clusters
|
|
1241
|
-
*
|
|
1242
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1243
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1244
|
-
* @returns {ApiClusters | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1245
|
-
*/
|
|
1246
1020
|
getClusters(pluginName, endpointNumber) {
|
|
1247
1021
|
if (this.matterbridge.hasCleanupStarted)
|
|
1248
|
-
return;
|
|
1022
|
+
return;
|
|
1249
1023
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1250
1024
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.maybeId || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1251
1025
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1252
1026
|
return;
|
|
1253
1027
|
}
|
|
1254
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1255
|
-
// Get the device types from the main endpoint
|
|
1256
1028
|
const deviceTypes = [];
|
|
1257
1029
|
const clusters = [];
|
|
1258
1030
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1259
1031
|
deviceTypes.push(d.deviceType);
|
|
1260
1032
|
});
|
|
1261
|
-
// Get the clusters from the main endpoint
|
|
1262
1033
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1263
1034
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1264
1035
|
return;
|
|
1265
1036
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1266
1037
|
return;
|
|
1267
|
-
// console.log(
|
|
1268
|
-
// `${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}`,
|
|
1269
|
-
// );
|
|
1270
1038
|
clusters.push({
|
|
1271
1039
|
endpoint: endpoint.number.toString(),
|
|
1272
1040
|
number: endpoint.number,
|
|
@@ -1280,19 +1048,12 @@ export class Frontend extends EventEmitter {
|
|
|
1280
1048
|
attributeLocalValue: attributeValue,
|
|
1281
1049
|
});
|
|
1282
1050
|
});
|
|
1283
|
-
// Get the child endpoints
|
|
1284
1051
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1285
|
-
// if (childEndpoints.length === 0) {
|
|
1286
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1287
|
-
// }
|
|
1288
1052
|
childEndpoints.forEach((childEndpoint) => {
|
|
1289
|
-
// istanbul ignore if cause is not reachable: should never happen but ...
|
|
1290
1053
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1291
1054
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1292
1055
|
return;
|
|
1293
1056
|
}
|
|
1294
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1295
|
-
// Get the device types of the child endpoint
|
|
1296
1057
|
const deviceTypes = [];
|
|
1297
1058
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1298
1059
|
deviceTypes.push(d.deviceType);
|
|
@@ -1302,9 +1063,6 @@ export class Frontend extends EventEmitter {
|
|
|
1302
1063
|
return;
|
|
1303
1064
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1304
1065
|
return;
|
|
1305
|
-
// console.log(
|
|
1306
|
-
// `${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}`,
|
|
1307
|
-
// );
|
|
1308
1066
|
clusters.push({
|
|
1309
1067
|
endpoint: childEndpoint.number.toString(),
|
|
1310
1068
|
number: childEndpoint.number,
|
|
@@ -1324,7 +1082,6 @@ export class Frontend extends EventEmitter {
|
|
|
1324
1082
|
async generateDiagnostic() {
|
|
1325
1083
|
this.log.debug('Generating diagnostic...');
|
|
1326
1084
|
const serverNodes = [];
|
|
1327
|
-
// istanbul ignore else
|
|
1328
1085
|
if (this.matterbridge.bridgeMode === 'bridge') {
|
|
1329
1086
|
if (this.matterbridge.serverNode)
|
|
1330
1087
|
serverNodes.push(this.matterbridge.serverNode);
|
|
@@ -1335,7 +1092,6 @@ export class Frontend extends EventEmitter {
|
|
|
1335
1092
|
serverNodes.push(plugin.serverNode);
|
|
1336
1093
|
}
|
|
1337
1094
|
}
|
|
1338
|
-
// istanbul ignore next
|
|
1339
1095
|
for (const device of this.matterbridge.devices.array()) {
|
|
1340
1096
|
if (device.serverNode)
|
|
1341
1097
|
serverNodes.push(device.serverNode);
|
|
@@ -1359,15 +1115,8 @@ export class Frontend extends EventEmitter {
|
|
|
1359
1115
|
values: [...serverNodes],
|
|
1360
1116
|
})));
|
|
1361
1117
|
delete Logger.destinations.diagnostic;
|
|
1362
|
-
await wait(500);
|
|
1118
|
+
await wait(500);
|
|
1363
1119
|
}
|
|
1364
|
-
/**
|
|
1365
|
-
* Handles incoming websocket api request messages from the Matterbridge frontend.
|
|
1366
|
-
*
|
|
1367
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1368
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1369
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1370
|
-
*/
|
|
1371
1120
|
async wsMessageHandler(client, message) {
|
|
1372
1121
|
let data;
|
|
1373
1122
|
const sendResponse = (data) => {
|
|
@@ -1387,7 +1136,7 @@ export class Frontend extends EventEmitter {
|
|
|
1387
1136
|
};
|
|
1388
1137
|
try {
|
|
1389
1138
|
data = JSON.parse(message.toString());
|
|
1390
|
-
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method)
|
|
1139
|
+
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || data.dst !== 'Matterbridge') {
|
|
1391
1140
|
this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
|
|
1392
1141
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
|
|
1393
1142
|
return;
|
|
@@ -1461,7 +1210,6 @@ export class Frontend extends EventEmitter {
|
|
|
1461
1210
|
return;
|
|
1462
1211
|
})
|
|
1463
1212
|
.catch((_error) => {
|
|
1464
|
-
//
|
|
1465
1213
|
});
|
|
1466
1214
|
}
|
|
1467
1215
|
else {
|
|
@@ -1509,7 +1257,6 @@ export class Frontend extends EventEmitter {
|
|
|
1509
1257
|
return;
|
|
1510
1258
|
})
|
|
1511
1259
|
.catch((_error) => {
|
|
1512
|
-
//
|
|
1513
1260
|
});
|
|
1514
1261
|
}
|
|
1515
1262
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1535,7 +1282,6 @@ export class Frontend extends EventEmitter {
|
|
|
1535
1282
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1536
1283
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
|
|
1537
1284
|
if (plugin.serverNode) {
|
|
1538
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1539
1285
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1540
1286
|
plugin.serverNode = undefined;
|
|
1541
1287
|
}
|
|
@@ -1545,20 +1291,18 @@ export class Frontend extends EventEmitter {
|
|
|
1545
1291
|
this.matterbridge.devices.remove(device);
|
|
1546
1292
|
}
|
|
1547
1293
|
}
|
|
1548
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1549
1294
|
if (plugin.type === 'DynamicPlatform' && !plugin.locked)
|
|
1550
1295
|
await this.matterbridge.createDynamicPlugin(plugin);
|
|
1551
1296
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
|
|
1552
|
-
plugin.restartRequired = false;
|
|
1297
|
+
plugin.restartRequired = false;
|
|
1553
1298
|
let needRestart = 0;
|
|
1554
1299
|
for (const plugin of this.matterbridge.plugins) {
|
|
1555
1300
|
if (plugin.restartRequired)
|
|
1556
1301
|
needRestart++;
|
|
1557
1302
|
}
|
|
1558
1303
|
if (needRestart === 0) {
|
|
1559
|
-
this.wssSendRestartNotRequired(true);
|
|
1304
|
+
this.wssSendRestartNotRequired(true);
|
|
1560
1305
|
}
|
|
1561
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1562
1306
|
if (plugin.serverNode)
|
|
1563
1307
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1564
1308
|
this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
|
|
@@ -1824,22 +1568,22 @@ export class Frontend extends EventEmitter {
|
|
|
1824
1568
|
if (isValidString(data.params.value, 4)) {
|
|
1825
1569
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1826
1570
|
if (data.params.value === 'Debug') {
|
|
1827
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1571
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1828
1572
|
}
|
|
1829
1573
|
else if (data.params.value === 'Info') {
|
|
1830
|
-
await this.matterbridge.setLogLevel("info"
|
|
1574
|
+
await this.matterbridge.setLogLevel("info");
|
|
1831
1575
|
}
|
|
1832
1576
|
else if (data.params.value === 'Notice') {
|
|
1833
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1577
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1834
1578
|
}
|
|
1835
1579
|
else if (data.params.value === 'Warn') {
|
|
1836
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1580
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1837
1581
|
}
|
|
1838
1582
|
else if (data.params.value === 'Error') {
|
|
1839
|
-
await this.matterbridge.setLogLevel("error"
|
|
1583
|
+
await this.matterbridge.setLogLevel("error");
|
|
1840
1584
|
}
|
|
1841
1585
|
else if (data.params.value === 'Fatal') {
|
|
1842
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1586
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1843
1587
|
}
|
|
1844
1588
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1845
1589
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1850,7 +1594,6 @@ export class Frontend extends EventEmitter {
|
|
|
1850
1594
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1851
1595
|
this.matterbridge.fileLogger = data.params.value;
|
|
1852
1596
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1853
|
-
// Create the file logger for matterbridge
|
|
1854
1597
|
if (data.params.value)
|
|
1855
1598
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), await this.matterbridge.getLogLevel(), true);
|
|
1856
1599
|
else
|
|
@@ -1879,12 +1622,11 @@ export class Frontend extends EventEmitter {
|
|
|
1879
1622
|
else if (data.params.value === 'Fatal') {
|
|
1880
1623
|
Logger.level = MatterLogLevel.FATAL;
|
|
1881
1624
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
1625
|
+
let callbackLogLevel = "notice";
|
|
1626
|
+
if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
|
|
1627
|
+
callbackLogLevel = "info";
|
|
1628
|
+
if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
1629
|
+
callbackLogLevel = "debug";
|
|
1888
1630
|
AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
|
|
1889
1631
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
1890
1632
|
await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.level);
|
|
@@ -1936,7 +1678,6 @@ export class Frontend extends EventEmitter {
|
|
|
1936
1678
|
}
|
|
1937
1679
|
break;
|
|
1938
1680
|
case 'setmatterport':
|
|
1939
|
-
// eslint-disable-next-line no-case-declarations
|
|
1940
1681
|
const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1941
1682
|
if (isValidNumber(port, 5540, 5600)) {
|
|
1942
1683
|
this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
|
|
@@ -1956,7 +1697,6 @@ export class Frontend extends EventEmitter {
|
|
|
1956
1697
|
}
|
|
1957
1698
|
break;
|
|
1958
1699
|
case 'setmatterdiscriminator':
|
|
1959
|
-
// eslint-disable-next-line no-case-declarations
|
|
1960
1700
|
const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1961
1701
|
if (isValidNumber(discriminator, 0, 4095)) {
|
|
1962
1702
|
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
|
|
@@ -1976,7 +1716,6 @@ export class Frontend extends EventEmitter {
|
|
|
1976
1716
|
}
|
|
1977
1717
|
break;
|
|
1978
1718
|
case 'setmatterpasscode':
|
|
1979
|
-
// eslint-disable-next-line no-case-declarations
|
|
1980
1719
|
const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1981
1720
|
if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
|
|
1982
1721
|
this.matterbridge.passcode = passcode;
|
|
@@ -2022,19 +1761,15 @@ export class Frontend extends EventEmitter {
|
|
|
2022
1761
|
return;
|
|
2023
1762
|
}
|
|
2024
1763
|
const config = plugin.configJson;
|
|
2025
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2026
1764
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
2027
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
2028
1765
|
if (select === 'serial')
|
|
2029
1766
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
2030
1767
|
if (select === 'name')
|
|
2031
1768
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
2032
1769
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
2033
|
-
// Remove postfix from the serial if it exists
|
|
2034
1770
|
if (config.postfix) {
|
|
2035
1771
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
2036
1772
|
}
|
|
2037
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
2038
1773
|
if (isValidArray(config.whiteList, 1)) {
|
|
2039
1774
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
2040
1775
|
config.whiteList.push(data.params.serial);
|
|
@@ -2043,7 +1778,6 @@ export class Frontend extends EventEmitter {
|
|
|
2043
1778
|
config.whiteList.push(data.params.name);
|
|
2044
1779
|
}
|
|
2045
1780
|
}
|
|
2046
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
2047
1781
|
if (isValidArray(config.blackList, 1)) {
|
|
2048
1782
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
2049
1783
|
config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
|
|
@@ -2071,9 +1805,7 @@ export class Frontend extends EventEmitter {
|
|
|
2071
1805
|
return;
|
|
2072
1806
|
}
|
|
2073
1807
|
const config = plugin.configJson;
|
|
2074
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2075
1808
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
2076
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
2077
1809
|
if (select === 'serial')
|
|
2078
1810
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
2079
1811
|
if (select === 'name')
|
|
@@ -2082,7 +1814,6 @@ export class Frontend extends EventEmitter {
|
|
|
2082
1814
|
if (config.postfix) {
|
|
2083
1815
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
2084
1816
|
}
|
|
2085
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
2086
1817
|
if (isValidArray(config.whiteList, 1)) {
|
|
2087
1818
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
2088
1819
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
|
|
@@ -2091,7 +1822,6 @@ export class Frontend extends EventEmitter {
|
|
|
2091
1822
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
|
|
2092
1823
|
}
|
|
2093
1824
|
}
|
|
2094
|
-
// Add the serial to the blackList
|
|
2095
1825
|
if (isValidArray(config.blackList)) {
|
|
2096
1826
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
2097
1827
|
config.blackList.push(data.params.serial);
|
|
@@ -2114,7 +1844,6 @@ export class Frontend extends EventEmitter {
|
|
|
2114
1844
|
}
|
|
2115
1845
|
}
|
|
2116
1846
|
else {
|
|
2117
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2118
1847
|
const localData = data;
|
|
2119
1848
|
this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
|
|
2120
1849
|
sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
|
|
@@ -2124,46 +1853,23 @@ export class Frontend extends EventEmitter {
|
|
|
2124
1853
|
inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
|
|
2125
1854
|
}
|
|
2126
1855
|
}
|
|
2127
|
-
/**
|
|
2128
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
2129
|
-
*
|
|
2130
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
2131
|
-
* @param {string} time - The time string of the message
|
|
2132
|
-
* @param {string} name - The logger name of the message
|
|
2133
|
-
* @param {string} message - The content of the message.
|
|
2134
|
-
*
|
|
2135
|
-
* @remarks
|
|
2136
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
2137
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
2138
|
-
* The function sends the message to all connected clients.
|
|
2139
|
-
*/
|
|
2140
1856
|
wssSendLogMessage(level, time, name, message) {
|
|
2141
1857
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2142
1858
|
return;
|
|
2143
1859
|
if (!level || !time || !name || !message)
|
|
2144
1860
|
return;
|
|
2145
|
-
// Remove ANSI escape codes from the message
|
|
2146
|
-
// eslint-disable-next-line no-control-regex
|
|
2147
1861
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
2148
|
-
// Remove leading asterisks from the message
|
|
2149
1862
|
message = message.replace(/^\*+/, '');
|
|
2150
|
-
// Replace all occurrences of \t and \n
|
|
2151
1863
|
message = message.replace(/[\t\n]/g, '');
|
|
2152
|
-
// Remove non-printable characters
|
|
2153
|
-
// eslint-disable-next-line no-control-regex
|
|
2154
1864
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
2155
|
-
// Replace all occurrences of \" with "
|
|
2156
1865
|
message = message.replace(/\\"/g, '"');
|
|
2157
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
2158
1866
|
const maxContinuousLength = 100;
|
|
2159
1867
|
const keepStartLength = 20;
|
|
2160
1868
|
const keepEndLength = 20;
|
|
2161
|
-
// Split the message into words
|
|
2162
1869
|
if (level !== 'spawn') {
|
|
2163
1870
|
message = message
|
|
2164
1871
|
.split(' ')
|
|
2165
1872
|
.map((word) => {
|
|
2166
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2167
1873
|
if (word.length > maxContinuousLength) {
|
|
2168
1874
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2169
1875
|
}
|
|
@@ -2171,34 +1877,14 @@ export class Frontend extends EventEmitter {
|
|
|
2171
1877
|
})
|
|
2172
1878
|
.join(' ');
|
|
2173
1879
|
}
|
|
2174
|
-
// Send the message to all connected clients
|
|
2175
1880
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
|
|
2176
1881
|
}
|
|
2177
|
-
/**
|
|
2178
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2179
|
-
*
|
|
2180
|
-
* @param {string} changed - The changed value.
|
|
2181
|
-
* @param {Record<string, unknown>} params - Additional parameters to send with the message.
|
|
2182
|
-
* possible values for changed:
|
|
2183
|
-
* - 'settings' (when the bridge has started in bridge mode or childbridge mode and when update finds a new version)
|
|
2184
|
-
* - 'plugins'
|
|
2185
|
-
* - 'devices'
|
|
2186
|
-
* - 'matter' with param 'matter' (QRDiv component)
|
|
2187
|
-
* @param {ApiMatter} params.matter - The matter device that has changed. Required if changed is 'matter'.
|
|
2188
|
-
*/
|
|
2189
1882
|
wssSendRefreshRequired(changed, params) {
|
|
2190
1883
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2191
1884
|
return;
|
|
2192
1885
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
2193
|
-
// Send the message to all connected clients
|
|
2194
1886
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
|
|
2195
1887
|
}
|
|
2196
|
-
/**
|
|
2197
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2198
|
-
*
|
|
2199
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
|
|
2200
|
-
* @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
|
|
2201
|
-
*/
|
|
2202
1888
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
2203
1889
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2204
1890
|
return;
|
|
@@ -2207,14 +1893,8 @@ export class Frontend extends EventEmitter {
|
|
|
2207
1893
|
this.matterbridge.fixedRestartRequired = fixed;
|
|
2208
1894
|
if (snackbar === true)
|
|
2209
1895
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
2210
|
-
// Send the message to all connected clients
|
|
2211
1896
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
|
|
2212
1897
|
}
|
|
2213
|
-
/**
|
|
2214
|
-
* Sends a no need to restart WebSocket message to all connected clients.
|
|
2215
|
-
*
|
|
2216
|
-
* @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients. Default is true.
|
|
2217
|
-
*/
|
|
2218
1898
|
wssSendRestartNotRequired(snackbar = true) {
|
|
2219
1899
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2220
1900
|
return;
|
|
@@ -2222,133 +1902,57 @@ export class Frontend extends EventEmitter {
|
|
|
2222
1902
|
this.matterbridge.restartRequired = false;
|
|
2223
1903
|
if (snackbar === true)
|
|
2224
1904
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2225
|
-
// Send the message to all connected clients
|
|
2226
1905
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
|
|
2227
1906
|
}
|
|
2228
|
-
/**
|
|
2229
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
2230
|
-
*
|
|
2231
|
-
* @param {boolean} devVersion - If true, the update is for a development version. Default is false.
|
|
2232
|
-
*/
|
|
2233
1907
|
wssSendUpdateRequired(devVersion = false) {
|
|
2234
1908
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2235
1909
|
return;
|
|
2236
1910
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2237
1911
|
this.matterbridge.updateRequired = true;
|
|
2238
|
-
// Send the message to all connected clients
|
|
2239
1912
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
|
|
2240
1913
|
}
|
|
2241
|
-
/**
|
|
2242
|
-
* Sends a cpu update message to all connected clients.
|
|
2243
|
-
*
|
|
2244
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2245
|
-
* @param {number} processCpuUsage - The CPU usage percentage of the process to send.
|
|
2246
|
-
*/
|
|
2247
1914
|
wssSendCpuUpdate(cpuUsage, processCpuUsage) {
|
|
2248
1915
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2249
1916
|
return;
|
|
2250
1917
|
if (hasParameter('debug'))
|
|
2251
1918
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2252
|
-
// Send the message to all connected clients
|
|
2253
1919
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', success: true, response: { cpuUsage: Math.round(cpuUsage * 100) / 100, processCpuUsage: Math.round(processCpuUsage * 100) / 100 } });
|
|
2254
1920
|
}
|
|
2255
|
-
/**
|
|
2256
|
-
* Sends a memory update message to all connected clients.
|
|
2257
|
-
*
|
|
2258
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2259
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2260
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2261
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2262
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2263
|
-
* @param {string} external - The external memory in bytes.
|
|
2264
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2265
|
-
*/
|
|
2266
1921
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2267
1922
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2268
1923
|
return;
|
|
2269
1924
|
if (hasParameter('debug'))
|
|
2270
1925
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2271
|
-
// Send the message to all connected clients
|
|
2272
1926
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', success: true, response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
|
|
2273
1927
|
}
|
|
2274
|
-
/**
|
|
2275
|
-
* Sends an uptime update message to all connected clients.
|
|
2276
|
-
*
|
|
2277
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2278
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2279
|
-
*/
|
|
2280
1928
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2281
1929
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2282
1930
|
return;
|
|
2283
1931
|
if (hasParameter('debug'))
|
|
2284
1932
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2285
|
-
// Send the message to all connected clients
|
|
2286
1933
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
|
|
2287
1934
|
}
|
|
2288
|
-
/**
|
|
2289
|
-
* Sends an open snackbar message to all connected clients.
|
|
2290
|
-
*
|
|
2291
|
-
* @param {string} message - The message to send.
|
|
2292
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message. Default is 5 seconds.
|
|
2293
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the message.
|
|
2294
|
-
* possible values are: 'info', 'warning', 'error', 'success'. Default is 'info'.
|
|
2295
|
-
*
|
|
2296
|
-
* @remarks
|
|
2297
|
-
* If timeout is 0, the snackbar message will be displayed until closed by the user.
|
|
2298
|
-
*/
|
|
2299
1935
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2300
1936
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2301
1937
|
return;
|
|
2302
1938
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2303
|
-
// Send the message to all connected clients
|
|
2304
1939
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
|
|
2305
1940
|
}
|
|
2306
|
-
/**
|
|
2307
|
-
* Sends a close snackbar message to all connected clients.
|
|
2308
|
-
* It will close the snackbar message with the same message and timeout = 0.
|
|
2309
|
-
*
|
|
2310
|
-
* @param {string} message - The message to send.
|
|
2311
|
-
*/
|
|
2312
1941
|
wssSendCloseSnackbarMessage(message) {
|
|
2313
1942
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2314
1943
|
return;
|
|
2315
1944
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2316
|
-
// Send the message to all connected clients
|
|
2317
1945
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
|
|
2318
1946
|
}
|
|
2319
|
-
/**
|
|
2320
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2321
|
-
*
|
|
2322
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2323
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2324
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2325
|
-
* @param {EndpointNumber} number - The endpoint number where the attribute belongs.
|
|
2326
|
-
* @param {string} id - The endpoint id where the attribute belongs.
|
|
2327
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2328
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2329
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2330
|
-
*
|
|
2331
|
-
* @remarks
|
|
2332
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2333
|
-
* with the updated attribute information.
|
|
2334
|
-
*/
|
|
2335
1947
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
|
|
2336
1948
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2337
1949
|
return;
|
|
2338
1950
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2339
|
-
// Send the message to all connected clients
|
|
2340
1951
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', success: true, response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value } });
|
|
2341
1952
|
}
|
|
2342
|
-
/**
|
|
2343
|
-
* Sends a message to all connected clients.
|
|
2344
|
-
* This is an helper function to send a broadcast message to all connected clients.
|
|
2345
|
-
*
|
|
2346
|
-
* @param {WsMessageBroadcast} msg - The message to send.
|
|
2347
|
-
*/
|
|
2348
1953
|
wssBroadcastMessage(msg) {
|
|
2349
1954
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2350
1955
|
return;
|
|
2351
|
-
// Send the message to all connected clients
|
|
2352
1956
|
const stringifiedMsg = JSON.stringify(msg);
|
|
2353
1957
|
if (msg.method !== 'log')
|
|
2354
1958
|
this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
|
|
@@ -2359,4 +1963,3 @@ export class Frontend extends EventEmitter {
|
|
|
2359
1963
|
});
|
|
2360
1964
|
}
|
|
2361
1965
|
}
|
|
2362
|
-
//# sourceMappingURL=frontend.js.map
|