matterbridge 3.4.4 → 3.4.5-dev-20251222-5853b56
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 +14 -0
- package/README.md +17 -2
- package/dist/broadcastServer.js +0 -117
- 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/deviceManager.js +1 -113
- package/dist/devices/airConditioner.js +0 -57
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/cooktop.js +0 -56
- 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 -43
- 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 +38 -485
- package/dist/frontendTypes.js +0 -45
- package/dist/helpers.js +0 -53
- package/dist/index.js +0 -25
- package/dist/jestutils/export.js +0 -1
- package/dist/jestutils/jestHelpers.js +14 -371
- 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/matterNode.js +8 -369
- package/dist/matterbridge.js +46 -824
- package/dist/matterbridgeAccessoryPlatform.js +0 -38
- package/dist/matterbridgeBehaviors.js +5 -68
- package/dist/matterbridgeDeviceTypes.js +14 -635
- package/dist/matterbridgeDynamicPlatform.js +0 -38
- package/dist/matterbridgeEndpoint.js +53 -1457
- package/dist/matterbridgeEndpointHelpers.js +20 -483
- package/dist/matterbridgeEndpointTypes.js +0 -25
- package/dist/matterbridgePlatform.js +1 -451
- package/dist/matterbridgeTypes.js +0 -26
- package/dist/pluginManager.js +5 -341
- package/dist/shelly.js +7 -178
- package/dist/storage/export.js +0 -1
- package/dist/update.js +1 -93
- package/dist/utils/colorUtils.js +2 -97
- package/dist/utils/commandLine.js +0 -60
- package/dist/utils/copyDirectory.js +0 -37
- 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 -42
- 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/network.js +5 -96
- package/dist/utils/spawn.js +1 -71
- package/dist/utils/tracker.js +1 -64
- package/dist/utils/wait.js +8 -60
- package/dist/workerGlobalPrefix.js +5 -37
- package/dist/workerTypes.js +0 -24
- package/dist/workers.js +4 -68
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- package/dist/broadcastServer.d.ts +0 -144
- package/dist/broadcastServer.d.ts.map +0 -1
- package/dist/broadcastServer.js.map +0 -1
- package/dist/broadcastServerTypes.d.ts +0 -841
- 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/deviceManager.d.ts +0 -135
- 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 -61
- 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 -245
- 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 -34
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/jestutils/export.d.ts +0 -2
- package/dist/jestutils/export.d.ts.map +0 -1
- package/dist/jestutils/export.js.map +0 -1
- package/dist/jestutils/jestHelpers.d.ts +0 -345
- package/dist/jestutils/jestHelpers.d.ts.map +0 -1
- package/dist/jestutils/jestHelpers.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/matterNode.d.ts +0 -342
- package/dist/matterNode.d.ts.map +0 -1
- package/dist/matterNode.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -505
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -41
- 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 -698
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -41
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -1507
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -787
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgeEndpointTypes.d.ts +0 -166
- package/dist/matterbridgeEndpointTypes.d.ts.map +0 -1
- package/dist/matterbridgeEndpointTypes.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -539
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -252
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -372
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -181
- 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 -84
- 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 -35
- 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 -45
- 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/network.d.ts +0 -111
- 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/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/workerGlobalPrefix.d.ts +0 -25
- package/dist/workerGlobalPrefix.d.ts.map +0 -1
- package/dist/workerGlobalPrefix.js.map +0 -1
- package/dist/workerTypes.d.ts +0 -52
- package/dist/workerTypes.d.ts.map +0 -1
- package/dist/workerTypes.js.map +0 -1
- package/dist/workers.d.ts +0 -69
- package/dist/workers.d.ts.map +0 -1
- package/dist/workers.js.map +0 -1
- package/marked.ps1 +0 -25
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.3
|
|
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 */ /* istanbul ignore next */
|
|
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';
|
|
@@ -63,7 +37,7 @@ export class Frontend extends EventEmitter {
|
|
|
63
37
|
constructor(matterbridge) {
|
|
64
38
|
super();
|
|
65
39
|
this.matterbridge = matterbridge;
|
|
66
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
40
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
67
41
|
this.log.logNameColor = '\x1b[38;5;97m';
|
|
68
42
|
this.server = new BroadcastServer('frontend', this.log);
|
|
69
43
|
this.server.on('broadcast_message', this.msgHandler.bind(this));
|
|
@@ -74,7 +48,6 @@ export class Frontend extends EventEmitter {
|
|
|
74
48
|
}
|
|
75
49
|
async msgHandler(msg) {
|
|
76
50
|
if (this.server.isWorkerRequest(msg)) {
|
|
77
|
-
// istanbul ignore else
|
|
78
51
|
if (this.verbose)
|
|
79
52
|
this.log.debug(`Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
|
|
80
53
|
switch (msg.type) {
|
|
@@ -126,13 +99,11 @@ export class Frontend extends EventEmitter {
|
|
|
126
99
|
this.server.respond({ ...msg, result: { success: true } });
|
|
127
100
|
break;
|
|
128
101
|
default:
|
|
129
|
-
// istanbul ignore next
|
|
130
102
|
if (this.verbose)
|
|
131
103
|
this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
|
|
132
104
|
}
|
|
133
105
|
}
|
|
134
106
|
if (this.server.isWorkerResponse(msg) && msg.result) {
|
|
135
|
-
// istanbul ignore next
|
|
136
107
|
if (this.verbose)
|
|
137
108
|
this.log.debug(`Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
|
|
138
109
|
switch (msg.type) {
|
|
@@ -168,55 +139,23 @@ export class Frontend extends EventEmitter {
|
|
|
168
139
|
this.port = port;
|
|
169
140
|
this.storedPassword = await this.matterbridge.nodeContext?.get('password', '');
|
|
170
141
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
171
|
-
// Initialize multer with the upload directory
|
|
172
142
|
const multer = await import('multer');
|
|
173
|
-
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
143
|
+
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
174
144
|
const upload = multer.default({ dest: uploadDir });
|
|
175
|
-
// Create the express app that serves the frontend
|
|
176
145
|
const express = await import('express');
|
|
177
146
|
this.expressApp = express.default();
|
|
178
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
179
|
-
/*
|
|
180
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
181
|
-
for (const method of methods) {
|
|
182
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
183
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
184
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
185
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
186
|
-
try {
|
|
187
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
188
|
-
return original(path, ...rest);
|
|
189
|
-
} catch (err) {
|
|
190
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
191
|
-
throw err;
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
*/
|
|
196
|
-
// Log all requests to the server for debugging
|
|
197
|
-
/*
|
|
198
|
-
this.expressApp.use((req, res, next) => {
|
|
199
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
200
|
-
next();
|
|
201
|
-
});
|
|
202
|
-
*/
|
|
203
|
-
// Serve static files from 'frontend/build' directory
|
|
204
147
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend', 'build')));
|
|
205
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
206
148
|
this.log.debug(`Creating WebSocketServer...`);
|
|
207
149
|
const ws = await import('ws');
|
|
208
150
|
this.webSocketServer = new ws.WebSocketServer({ noServer: true });
|
|
209
151
|
this.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
|
|
210
152
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
211
153
|
const clientIp = request.socket.remoteAddress;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if (this.matterbridge.getLogLevel() === "
|
|
216
|
-
callbackLogLevel = "
|
|
217
|
-
// istanbul ignore else
|
|
218
|
-
if (this.matterbridge.getLogLevel() === "debug" /* LogLevel.DEBUG */ || Logger.level === MatterLogLevel.DEBUG)
|
|
219
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
154
|
+
let callbackLogLevel = "notice";
|
|
155
|
+
if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
|
|
156
|
+
callbackLogLevel = "info";
|
|
157
|
+
if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
158
|
+
callbackLogLevel = "debug";
|
|
220
159
|
AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
|
|
221
160
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
222
161
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -232,33 +171,22 @@ export class Frontend extends EventEmitter {
|
|
|
232
171
|
});
|
|
233
172
|
ws.on('close', () => {
|
|
234
173
|
this.log.info('WebSocket client disconnected');
|
|
235
|
-
// istanbul ignore else
|
|
236
174
|
if (this.webSocketServer?.clients.size === 0) {
|
|
237
175
|
AnsiLogger.setGlobalCallback(undefined);
|
|
238
176
|
this.log.debug('All WebSocket clients disconnected. WebSocketServer logger global callback removed');
|
|
239
177
|
}
|
|
240
178
|
});
|
|
241
|
-
// istanbul ignore next
|
|
242
179
|
ws.on('error', (error) => {
|
|
243
|
-
// istanbul ignore next
|
|
244
180
|
this.log.error(`WebSocket client error: ${error}`);
|
|
245
181
|
});
|
|
246
182
|
});
|
|
247
183
|
this.webSocketServer.on('close', () => {
|
|
248
184
|
this.log.debug(`WebSocketServer closed`);
|
|
249
185
|
});
|
|
250
|
-
/* With { noServer: true } it never fires
|
|
251
|
-
this.webSocketServer.on('listening', () => {
|
|
252
|
-
this.log.info(`The WebSocketServer is listening`);
|
|
253
|
-
this.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
|
|
254
|
-
});
|
|
255
|
-
*/
|
|
256
|
-
// istanbul ignore next
|
|
257
186
|
this.webSocketServer.on('error', (ws, error) => {
|
|
258
187
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
259
188
|
});
|
|
260
189
|
if (!hasParameter('ssl')) {
|
|
261
|
-
// Create an HTTP server and attach the express app
|
|
262
190
|
const http = await import('node:http');
|
|
263
191
|
try {
|
|
264
192
|
this.log.debug(`Creating HTTP server...`);
|
|
@@ -269,7 +197,6 @@ export class Frontend extends EventEmitter {
|
|
|
269
197
|
this.emit('server_error', error);
|
|
270
198
|
return;
|
|
271
199
|
}
|
|
272
|
-
// Listen on the specified port
|
|
273
200
|
if (hasParameter('ingress')) {
|
|
274
201
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
275
202
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -279,10 +206,8 @@ export class Frontend extends EventEmitter {
|
|
|
279
206
|
}
|
|
280
207
|
else {
|
|
281
208
|
this.httpServer.listen(this.port, () => {
|
|
282
|
-
// istanbul ignore else
|
|
283
209
|
if (this.matterbridge.systemInformation.ipv4Address !== '')
|
|
284
210
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
|
|
285
|
-
// istanbul ignore else
|
|
286
211
|
if (this.matterbridge.systemInformation.ipv6Address !== '')
|
|
287
212
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
288
213
|
this.listening = true;
|
|
@@ -291,30 +216,24 @@ export class Frontend extends EventEmitter {
|
|
|
291
216
|
}
|
|
292
217
|
this.httpServer.on('upgrade', async (req, socket, head) => {
|
|
293
218
|
try {
|
|
294
|
-
// Only proceed for real WebSocket upgrades
|
|
295
|
-
// istanbul ignore next cause is only a safety check
|
|
296
219
|
if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
|
|
297
220
|
this.log.error(`WebSocket upgrade error: Invalid upgrade header ${req.headers.upgrade}`);
|
|
298
221
|
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
299
222
|
return socket.destroy();
|
|
300
223
|
}
|
|
301
|
-
// Build a URL so we can read ?password=...
|
|
302
224
|
const url = new URL(req.url ?? '/', `http://${req.headers.host || 'localhost'}`);
|
|
303
|
-
// Validate WebSocket password
|
|
304
225
|
const password = url.searchParams.get('password') ?? '';
|
|
305
226
|
if (password !== this.storedPassword) {
|
|
306
227
|
this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
|
|
307
228
|
socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
|
|
308
229
|
return socket.destroy();
|
|
309
230
|
}
|
|
310
|
-
// Complete the WebSocket handshake
|
|
311
231
|
this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
|
|
312
232
|
this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
|
|
313
233
|
this.webSocketServer?.emit('connection', ws, req);
|
|
314
234
|
});
|
|
315
235
|
}
|
|
316
236
|
catch (err) {
|
|
317
|
-
/* istanbul ignore next: only triggered on unexpected internal error */
|
|
318
237
|
{
|
|
319
238
|
inspectError(this.log, 'WebSocket upgrade error:', err);
|
|
320
239
|
socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
|
|
@@ -337,7 +256,6 @@ export class Frontend extends EventEmitter {
|
|
|
337
256
|
});
|
|
338
257
|
}
|
|
339
258
|
else {
|
|
340
|
-
// SSL is enabled, load the certificate and the private key
|
|
341
259
|
let cert;
|
|
342
260
|
let key;
|
|
343
261
|
let ca;
|
|
@@ -347,7 +265,6 @@ export class Frontend extends EventEmitter {
|
|
|
347
265
|
let httpsServerOptions = {};
|
|
348
266
|
const fs = await import('node:fs');
|
|
349
267
|
if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'))) {
|
|
350
|
-
// Load the p12 certificate and the passphrase
|
|
351
268
|
try {
|
|
352
269
|
pfx = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'));
|
|
353
270
|
this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12')}`);
|
|
@@ -359,7 +276,7 @@ export class Frontend extends EventEmitter {
|
|
|
359
276
|
}
|
|
360
277
|
try {
|
|
361
278
|
passphrase = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass'), 'utf8');
|
|
362
|
-
passphrase = passphrase.trim();
|
|
279
|
+
passphrase = passphrase.trim();
|
|
363
280
|
this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass')}`);
|
|
364
281
|
}
|
|
365
282
|
catch (error) {
|
|
@@ -370,7 +287,6 @@ export class Frontend extends EventEmitter {
|
|
|
370
287
|
httpsServerOptions = { pfx, passphrase };
|
|
371
288
|
}
|
|
372
289
|
else {
|
|
373
|
-
// 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.
|
|
374
290
|
try {
|
|
375
291
|
cert = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem'), 'utf8');
|
|
376
292
|
this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem')}`);
|
|
@@ -400,10 +316,9 @@ export class Frontend extends EventEmitter {
|
|
|
400
316
|
httpsServerOptions = { cert: fullChain ?? cert, key, ca };
|
|
401
317
|
}
|
|
402
318
|
if (hasParameter('mtls')) {
|
|
403
|
-
httpsServerOptions.requestCert = true;
|
|
404
|
-
httpsServerOptions.rejectUnauthorized = true;
|
|
319
|
+
httpsServerOptions.requestCert = true;
|
|
320
|
+
httpsServerOptions.rejectUnauthorized = true;
|
|
405
321
|
}
|
|
406
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
407
322
|
const https = await import('node:https');
|
|
408
323
|
try {
|
|
409
324
|
this.log.debug(`Creating HTTPS server...`);
|
|
@@ -414,7 +329,6 @@ export class Frontend extends EventEmitter {
|
|
|
414
329
|
this.emit('server_error', error);
|
|
415
330
|
return;
|
|
416
331
|
}
|
|
417
|
-
// Listen on the specified port
|
|
418
332
|
if (hasParameter('ingress')) {
|
|
419
333
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
420
334
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -424,10 +338,8 @@ export class Frontend extends EventEmitter {
|
|
|
424
338
|
}
|
|
425
339
|
else {
|
|
426
340
|
this.httpsServer.listen(this.port, () => {
|
|
427
|
-
// istanbul ignore else
|
|
428
341
|
if (this.matterbridge.systemInformation.ipv4Address !== '')
|
|
429
342
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
|
|
430
|
-
// istanbul ignore else
|
|
431
343
|
if (this.matterbridge.systemInformation.ipv6Address !== '')
|
|
432
344
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
433
345
|
this.listening = true;
|
|
@@ -436,29 +348,23 @@ export class Frontend extends EventEmitter {
|
|
|
436
348
|
}
|
|
437
349
|
this.httpsServer.on('upgrade', async (req, socket, head) => {
|
|
438
350
|
try {
|
|
439
|
-
// Only proceed for real WebSocket upgrades
|
|
440
|
-
// istanbul ignore next cause is only a safety check
|
|
441
351
|
if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
|
|
442
352
|
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
443
353
|
return socket.destroy();
|
|
444
354
|
}
|
|
445
|
-
// Build a URL so we can read ?password=...
|
|
446
355
|
const url = new URL(req.url ?? '/', `https://${req.headers.host || 'localhost'}`);
|
|
447
|
-
// Validate WebSocket password
|
|
448
356
|
const password = url.searchParams.get('password') ?? '';
|
|
449
357
|
if (password !== this.storedPassword) {
|
|
450
358
|
this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
|
|
451
359
|
socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
|
|
452
360
|
return socket.destroy();
|
|
453
361
|
}
|
|
454
|
-
// Complete the WebSocket handshake
|
|
455
362
|
this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
|
|
456
363
|
this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
|
|
457
364
|
this.webSocketServer?.emit('connection', ws, req);
|
|
458
365
|
});
|
|
459
366
|
}
|
|
460
367
|
catch (err) {
|
|
461
|
-
/* istanbul ignore next: only triggered on unexpected internal error */
|
|
462
368
|
{
|
|
463
369
|
inspectError(this.log, 'WebSocket upgrade error:', err);
|
|
464
370
|
socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
|
|
@@ -480,7 +386,6 @@ export class Frontend extends EventEmitter {
|
|
|
480
386
|
return;
|
|
481
387
|
});
|
|
482
388
|
}
|
|
483
|
-
// Subscribe to cli events
|
|
484
389
|
cliEmitter.removeAllListeners();
|
|
485
390
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
486
391
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -491,8 +396,6 @@ export class Frontend extends EventEmitter {
|
|
|
491
396
|
cliEmitter.on('cpu', (cpuUsage, processCpuUsage) => {
|
|
492
397
|
this.wssSendCpuUpdate(cpuUsage, processCpuUsage);
|
|
493
398
|
});
|
|
494
|
-
// Endpoint to validate login code
|
|
495
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
496
399
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
497
400
|
const { password } = req.body;
|
|
498
401
|
this.log.debug(`The frontend sent /api/login with password ${password ? '[redacted]' : '(empty)'}`);
|
|
@@ -505,20 +408,17 @@ export class Frontend extends EventEmitter {
|
|
|
505
408
|
res.json({ valid: false });
|
|
506
409
|
}
|
|
507
410
|
});
|
|
508
|
-
// Endpoint to provide health check for docker
|
|
509
411
|
this.expressApp.get('/health', (req, res) => {
|
|
510
412
|
this.log.debug('Express received /health');
|
|
511
413
|
const healthStatus = {
|
|
512
|
-
status: 'ok',
|
|
513
|
-
uptime: process.uptime(),
|
|
514
|
-
timestamp: new Date().toISOString(),
|
|
414
|
+
status: 'ok',
|
|
415
|
+
uptime: process.uptime(),
|
|
416
|
+
timestamp: new Date().toISOString(),
|
|
515
417
|
};
|
|
516
418
|
res.status(200).json(healthStatus);
|
|
517
419
|
});
|
|
518
|
-
// Endpoint to provide memory usage details
|
|
519
420
|
this.expressApp.get('/memory', async (req, res) => {
|
|
520
421
|
this.log.debug('Express received /memory');
|
|
521
|
-
// Memory usage from process
|
|
522
422
|
const memoryUsageRaw = process.memoryUsage();
|
|
523
423
|
const memoryUsage = {
|
|
524
424
|
rss: formatBytes(memoryUsageRaw.rss),
|
|
@@ -527,13 +427,10 @@ export class Frontend extends EventEmitter {
|
|
|
527
427
|
external: formatBytes(memoryUsageRaw.external),
|
|
528
428
|
arrayBuffers: formatBytes(memoryUsageRaw.arrayBuffers),
|
|
529
429
|
};
|
|
530
|
-
// V8 heap statistics
|
|
531
430
|
const { default: v8 } = await import('node:v8');
|
|
532
431
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
533
432
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
534
|
-
// Format heapStats
|
|
535
433
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, formatBytes(value)]));
|
|
536
|
-
// Format heapSpaces
|
|
537
434
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
538
435
|
...space,
|
|
539
436
|
space_size: formatBytes(space.space_size),
|
|
@@ -552,22 +449,18 @@ export class Frontend extends EventEmitter {
|
|
|
552
449
|
};
|
|
553
450
|
res.status(200).json(memoryReport);
|
|
554
451
|
});
|
|
555
|
-
// Endpoint to provide settings
|
|
556
452
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
557
453
|
this.log.debug('The frontend sent /api/settings');
|
|
558
454
|
res.json(await this.getApiSettings());
|
|
559
455
|
});
|
|
560
|
-
// Endpoint to provide plugins
|
|
561
456
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
562
457
|
this.log.debug('The frontend sent /api/plugins');
|
|
563
458
|
res.json(this.matterbridge.hasCleanupStarted ? [] : this.getPlugins());
|
|
564
459
|
});
|
|
565
|
-
// Endpoint to provide devices
|
|
566
460
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
567
461
|
this.log.debug('The frontend sent /api/devices');
|
|
568
462
|
res.json(this.matterbridge.hasCleanupStarted ? [] : this.getDevices());
|
|
569
463
|
});
|
|
570
|
-
// Endpoint to view the matterbridge log
|
|
571
464
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
572
465
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
573
466
|
try {
|
|
@@ -581,7 +474,6 @@ export class Frontend extends EventEmitter {
|
|
|
581
474
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
582
475
|
}
|
|
583
476
|
});
|
|
584
|
-
// Endpoint to view the matter.js log
|
|
585
477
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
586
478
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
587
479
|
try {
|
|
@@ -595,7 +487,6 @@ export class Frontend extends EventEmitter {
|
|
|
595
487
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
596
488
|
}
|
|
597
489
|
});
|
|
598
|
-
// Endpoint to view the diagnostic.log
|
|
599
490
|
this.expressApp.get('/api/view-diagnostic', async (req, res) => {
|
|
600
491
|
this.log.debug('The frontend sent /api/view-diagnostic');
|
|
601
492
|
await this.generateDiagnostic();
|
|
@@ -606,13 +497,10 @@ export class Frontend extends EventEmitter {
|
|
|
606
497
|
res.send(data.slice(29));
|
|
607
498
|
}
|
|
608
499
|
catch (error) {
|
|
609
|
-
// istanbul ignore next
|
|
610
500
|
this.log.error(`Error reading diagnostic log file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
611
|
-
// istanbul ignore next
|
|
612
501
|
res.status(500).send('Error reading diagnostic log file.');
|
|
613
502
|
}
|
|
614
503
|
});
|
|
615
|
-
// Endpoint to download the diagnostic.log
|
|
616
504
|
this.expressApp.get('/api/download-diagnostic', async (req, res) => {
|
|
617
505
|
this.log.debug(`The frontend sent /api/download-diagnostic`);
|
|
618
506
|
await this.generateDiagnostic();
|
|
@@ -623,19 +511,16 @@ export class Frontend extends EventEmitter {
|
|
|
623
511
|
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), data, 'utf-8');
|
|
624
512
|
}
|
|
625
513
|
catch (error) {
|
|
626
|
-
// istanbul ignore next
|
|
627
514
|
this.log.debug(`Error in /api/download-diagnostic: ${error instanceof Error ? error.message : error}`);
|
|
628
515
|
}
|
|
629
516
|
res.type('text/plain');
|
|
630
517
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), MATTERBRIDGE_DIAGNOSTIC_FILE, (error) => {
|
|
631
|
-
/* istanbul ignore if */
|
|
632
518
|
if (error) {
|
|
633
519
|
this.log.error(`Error downloading file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
634
520
|
res.status(500).send('Error downloading the diagnostic log file');
|
|
635
521
|
}
|
|
636
522
|
});
|
|
637
523
|
});
|
|
638
|
-
// Endpoint to view the history.html
|
|
639
524
|
this.expressApp.get('/api/viewhistory', async (req, res) => {
|
|
640
525
|
this.log.debug('The frontend sent /api/viewhistory');
|
|
641
526
|
try {
|
|
@@ -649,7 +534,6 @@ export class Frontend extends EventEmitter {
|
|
|
649
534
|
res.status(500).send('Error reading history file.');
|
|
650
535
|
}
|
|
651
536
|
});
|
|
652
|
-
// Endpoint to download the history.html
|
|
653
537
|
this.expressApp.get('/api/downloadhistory', async (req, res) => {
|
|
654
538
|
this.log.debug(`The frontend sent /api/downloadhistory`);
|
|
655
539
|
try {
|
|
@@ -659,7 +543,6 @@ export class Frontend extends EventEmitter {
|
|
|
659
543
|
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), data, 'utf-8');
|
|
660
544
|
res.type('text/plain');
|
|
661
545
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), MATTERBRIDGE_HISTORY_FILE, (error) => {
|
|
662
|
-
/* istanbul ignore if */
|
|
663
546
|
if (error) {
|
|
664
547
|
this.log.error(`Error in /api/downloadhistory downloading history file ${MATTERBRIDGE_HISTORY_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
665
548
|
res.status(500).send('Error downloading history file');
|
|
@@ -671,7 +554,6 @@ export class Frontend extends EventEmitter {
|
|
|
671
554
|
res.status(500).send('Error reading history file.');
|
|
672
555
|
}
|
|
673
556
|
});
|
|
674
|
-
// Endpoint to view the shelly log
|
|
675
557
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
676
558
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
677
559
|
try {
|
|
@@ -685,7 +567,6 @@ export class Frontend extends EventEmitter {
|
|
|
685
567
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
686
568
|
}
|
|
687
569
|
});
|
|
688
|
-
// Endpoint to download the matterbridge log
|
|
689
570
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
690
571
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
|
|
691
572
|
const fs = await import('node:fs');
|
|
@@ -700,14 +581,12 @@ export class Frontend extends EventEmitter {
|
|
|
700
581
|
}
|
|
701
582
|
res.type('text/plain');
|
|
702
583
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_LOGGER_FILE), 'matterbridge.log', (error) => {
|
|
703
|
-
/* istanbul ignore if */
|
|
704
584
|
if (error) {
|
|
705
585
|
this.log.error(`Error downloading log file ${MATTERBRIDGE_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
706
586
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
707
587
|
}
|
|
708
588
|
});
|
|
709
589
|
});
|
|
710
|
-
// Endpoint to download the matter log
|
|
711
590
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
712
591
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
|
|
713
592
|
const fs = await import('node:fs');
|
|
@@ -722,14 +601,12 @@ export class Frontend extends EventEmitter {
|
|
|
722
601
|
}
|
|
723
602
|
res.type('text/plain');
|
|
724
603
|
res.download(path.join(os.tmpdir(), MATTER_LOGGER_FILE), 'matter.log', (error) => {
|
|
725
|
-
/* istanbul ignore if */
|
|
726
604
|
if (error) {
|
|
727
605
|
this.log.error(`Error downloading log file ${MATTER_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
728
606
|
res.status(500).send('Error downloading the matter log file');
|
|
729
607
|
}
|
|
730
608
|
});
|
|
731
609
|
});
|
|
732
|
-
// Endpoint to download the shelly log
|
|
733
610
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
734
611
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
735
612
|
const fs = await import('node:fs');
|
|
@@ -744,91 +621,75 @@ export class Frontend extends EventEmitter {
|
|
|
744
621
|
}
|
|
745
622
|
res.type('text/plain');
|
|
746
623
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
747
|
-
/* istanbul ignore if */
|
|
748
624
|
if (error) {
|
|
749
625
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
750
626
|
res.status(500).send('Error downloading Shelly system log file');
|
|
751
627
|
}
|
|
752
628
|
});
|
|
753
629
|
});
|
|
754
|
-
// Endpoint to download the matterbridge storage directory
|
|
755
630
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
756
631
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
757
632
|
await createZip(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), path.join(this.matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR));
|
|
758
633
|
res.download(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), `matterbridge.${NODE_STORAGE_DIR}.zip`, (error) => {
|
|
759
|
-
/* istanbul ignore if */
|
|
760
634
|
if (error) {
|
|
761
635
|
this.log.error(`Error downloading file ${`matterbridge.${NODE_STORAGE_DIR}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
762
636
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
763
637
|
}
|
|
764
638
|
});
|
|
765
639
|
});
|
|
766
|
-
// Endpoint to download the matter storage file
|
|
767
640
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
768
641
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
769
642
|
await createZip(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME));
|
|
770
643
|
res.download(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), `matterbridge.${MATTER_STORAGE_NAME}.zip`, (error) => {
|
|
771
|
-
/* istanbul ignore if */
|
|
772
644
|
if (error) {
|
|
773
645
|
this.log.error(`Error downloading the matter storage matterbridge.${MATTER_STORAGE_NAME}.zip: ${error instanceof Error ? error.message : error}`);
|
|
774
646
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
775
647
|
}
|
|
776
648
|
});
|
|
777
649
|
});
|
|
778
|
-
// Endpoint to download the matterbridge plugin directory
|
|
779
650
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
780
651
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
781
652
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
782
653
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
783
|
-
/* istanbul ignore if */
|
|
784
654
|
if (error) {
|
|
785
655
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
786
656
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
787
657
|
}
|
|
788
658
|
});
|
|
789
659
|
});
|
|
790
|
-
// Endpoint to download the matterbridge plugin config files
|
|
791
660
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
792
661
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
793
662
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
794
663
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
795
|
-
/* istanbul ignore if */
|
|
796
664
|
if (error) {
|
|
797
665
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
798
666
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
799
667
|
}
|
|
800
668
|
});
|
|
801
669
|
});
|
|
802
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
803
670
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
804
671
|
this.log.debug('The frontend sent /api/download-backup');
|
|
805
672
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
806
|
-
/* istanbul ignore if */
|
|
807
673
|
if (error) {
|
|
808
674
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
809
675
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
810
676
|
}
|
|
811
677
|
});
|
|
812
678
|
});
|
|
813
|
-
// Endpoint to upload a package
|
|
814
679
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
815
680
|
const { filename } = req.body;
|
|
816
681
|
const file = req.file;
|
|
817
|
-
/* istanbul ignore if */
|
|
818
682
|
if (!file || !filename) {
|
|
819
683
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
820
684
|
res.status(400).send('Invalid request: file and filename are required');
|
|
821
685
|
return;
|
|
822
686
|
}
|
|
823
687
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
824
|
-
// Define the path where the plugin file will be saved
|
|
825
688
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
826
689
|
try {
|
|
827
|
-
// Move the uploaded file to the specified path
|
|
828
690
|
const fs = await import('node:fs');
|
|
829
691
|
await fs.promises.rename(file.path, filePath);
|
|
830
692
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
831
|
-
// Install the plugin package
|
|
832
693
|
if (filename.endsWith('.tgz')) {
|
|
833
694
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
834
695
|
if (await spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], 'install', filename)) {
|
|
@@ -856,7 +717,6 @@ export class Frontend extends EventEmitter {
|
|
|
856
717
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
857
718
|
}
|
|
858
719
|
});
|
|
859
|
-
// Fallback for routing (must be the last route)
|
|
860
720
|
this.expressApp.use((req, res) => {
|
|
861
721
|
const filePath = path.resolve(this.matterbridge.rootDirectory, 'frontend', 'build');
|
|
862
722
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html in ${filePath} as fallback`);
|
|
@@ -867,16 +727,13 @@ export class Frontend extends EventEmitter {
|
|
|
867
727
|
async stop() {
|
|
868
728
|
this.log.debug('Stopping the frontend...');
|
|
869
729
|
const ws = await import('ws');
|
|
870
|
-
// Remove listeners from the express app
|
|
871
730
|
if (this.expressApp) {
|
|
872
731
|
this.expressApp.removeAllListeners();
|
|
873
732
|
this.expressApp = undefined;
|
|
874
733
|
this.log.debug('Frontend app closed successfully');
|
|
875
734
|
}
|
|
876
|
-
// Close the WebSocket server
|
|
877
735
|
if (this.webSocketServer) {
|
|
878
736
|
this.log.debug('Closing WebSocket server...');
|
|
879
|
-
// Close all active connections
|
|
880
737
|
this.webSocketServer.clients.forEach((client) => {
|
|
881
738
|
if (client.readyState === ws.WebSocket.OPEN) {
|
|
882
739
|
client.close();
|
|
@@ -884,9 +741,7 @@ export class Frontend extends EventEmitter {
|
|
|
884
741
|
});
|
|
885
742
|
await withTimeout(new Promise((resolve) => {
|
|
886
743
|
this.webSocketServer?.close((error) => {
|
|
887
|
-
// istanbul ignore if
|
|
888
744
|
if (error) {
|
|
889
|
-
// istanbul ignore next
|
|
890
745
|
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
891
746
|
}
|
|
892
747
|
else {
|
|
@@ -899,27 +754,8 @@ export class Frontend extends EventEmitter {
|
|
|
899
754
|
this.webSocketServer.removeAllListeners();
|
|
900
755
|
this.webSocketServer = undefined;
|
|
901
756
|
}
|
|
902
|
-
// Close the http server
|
|
903
757
|
if (this.httpServer) {
|
|
904
758
|
this.log.debug('Closing http server...');
|
|
905
|
-
/*
|
|
906
|
-
await withTimeout(
|
|
907
|
-
new Promise<void>((resolve) => {
|
|
908
|
-
this.httpServer?.close((error) => {
|
|
909
|
-
if (error) {
|
|
910
|
-
// istanbul ignore next
|
|
911
|
-
this.log.error(`Error closing http server: ${error}`);
|
|
912
|
-
} else {
|
|
913
|
-
this.log.debug('Http server closed successfully');
|
|
914
|
-
this.emit('server_stopped');
|
|
915
|
-
}
|
|
916
|
-
resolve();
|
|
917
|
-
});
|
|
918
|
-
}),
|
|
919
|
-
5000,
|
|
920
|
-
false,
|
|
921
|
-
);
|
|
922
|
-
*/
|
|
923
759
|
this.httpServer.close();
|
|
924
760
|
this.log.debug('Http server closed successfully');
|
|
925
761
|
this.listening = false;
|
|
@@ -928,27 +764,8 @@ export class Frontend extends EventEmitter {
|
|
|
928
764
|
this.httpServer = undefined;
|
|
929
765
|
this.log.debug('Frontend http server closed successfully');
|
|
930
766
|
}
|
|
931
|
-
// Close the https server
|
|
932
767
|
if (this.httpsServer) {
|
|
933
768
|
this.log.debug('Closing https server...');
|
|
934
|
-
/*
|
|
935
|
-
await withTimeout(
|
|
936
|
-
new Promise<void>((resolve) => {
|
|
937
|
-
this.httpsServer?.close((error) => {
|
|
938
|
-
if (error) {
|
|
939
|
-
// istanbul ignore next
|
|
940
|
-
this.log.error(`Error closing https server: ${error}`);
|
|
941
|
-
} else {
|
|
942
|
-
this.log.debug('Https server closed successfully');
|
|
943
|
-
this.emit('server_stopped');
|
|
944
|
-
}
|
|
945
|
-
resolve();
|
|
946
|
-
});
|
|
947
|
-
}),
|
|
948
|
-
5000,
|
|
949
|
-
false,
|
|
950
|
-
);
|
|
951
|
-
*/
|
|
952
769
|
this.httpsServer.close();
|
|
953
770
|
this.log.debug('Https server closed successfully');
|
|
954
771
|
this.listening = false;
|
|
@@ -959,13 +776,7 @@ export class Frontend extends EventEmitter {
|
|
|
959
776
|
}
|
|
960
777
|
this.log.debug('Frontend stopped successfully');
|
|
961
778
|
}
|
|
962
|
-
/**
|
|
963
|
-
* Retrieves the api settings data.
|
|
964
|
-
*
|
|
965
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
966
|
-
*/
|
|
967
779
|
async getApiSettings() {
|
|
968
|
-
// Update the variable system information properties
|
|
969
780
|
this.matterbridge.systemInformation.totalMemory = formatBytes(os.totalmem());
|
|
970
781
|
this.matterbridge.systemInformation.freeMemory = formatBytes(os.freemem());
|
|
971
782
|
this.matterbridge.systemInformation.systemUptime = formatUptime(os.uptime());
|
|
@@ -975,7 +786,6 @@ export class Frontend extends EventEmitter {
|
|
|
975
786
|
this.matterbridge.systemInformation.rss = formatBytes(process.memoryUsage().rss);
|
|
976
787
|
this.matterbridge.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
|
|
977
788
|
this.matterbridge.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
|
|
978
|
-
// Create the matterbridge information
|
|
979
789
|
const info = {
|
|
980
790
|
homeDirectory: this.matterbridge.homeDirectory,
|
|
981
791
|
rootDirectory: this.matterbridge.rootDirectory,
|
|
@@ -1011,15 +821,9 @@ export class Frontend extends EventEmitter {
|
|
|
1011
821
|
};
|
|
1012
822
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: info };
|
|
1013
823
|
}
|
|
1014
|
-
/**
|
|
1015
|
-
* Retrieves the reachable attribute.
|
|
1016
|
-
*
|
|
1017
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
1018
|
-
* @returns {boolean} The reachable attribute.
|
|
1019
|
-
*/
|
|
1020
824
|
getReachability(device) {
|
|
1021
825
|
if (this.matterbridge.hasCleanupStarted)
|
|
1022
|
-
return false;
|
|
826
|
+
return false;
|
|
1023
827
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1024
828
|
return false;
|
|
1025
829
|
if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
|
|
@@ -1030,15 +834,9 @@ export class Frontend extends EventEmitter {
|
|
|
1030
834
|
return true;
|
|
1031
835
|
return false;
|
|
1032
836
|
}
|
|
1033
|
-
/**
|
|
1034
|
-
* Retrieves the power source attribute.
|
|
1035
|
-
*
|
|
1036
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
1037
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
1038
|
-
*/
|
|
1039
837
|
getPowerSource(endpoint) {
|
|
1040
838
|
if (this.matterbridge.hasCleanupStarted)
|
|
1041
|
-
return undefined;
|
|
839
|
+
return undefined;
|
|
1042
840
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
1043
841
|
return undefined;
|
|
1044
842
|
const powerSource = (device) => {
|
|
@@ -1053,25 +851,16 @@ export class Frontend extends EventEmitter {
|
|
|
1053
851
|
}
|
|
1054
852
|
return;
|
|
1055
853
|
};
|
|
1056
|
-
// Root endpoint
|
|
1057
854
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
1058
855
|
return powerSource(endpoint);
|
|
1059
|
-
// Child endpoints
|
|
1060
856
|
for (const child of endpoint.getChildEndpoints()) {
|
|
1061
|
-
// istanbul ignore else
|
|
1062
857
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
1063
858
|
return powerSource(child);
|
|
1064
859
|
}
|
|
1065
860
|
}
|
|
1066
|
-
/**
|
|
1067
|
-
* Retrieves the battery level attribute.
|
|
1068
|
-
*
|
|
1069
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
1070
|
-
* @returns {number | undefined} The battery level attribute.
|
|
1071
|
-
*/
|
|
1072
861
|
getBatteryLevel(endpoint) {
|
|
1073
862
|
if (this.matterbridge.hasCleanupStarted)
|
|
1074
|
-
return undefined;
|
|
863
|
+
return undefined;
|
|
1075
864
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
1076
865
|
return undefined;
|
|
1077
866
|
const batteryLevel = (device) => {
|
|
@@ -1082,27 +871,16 @@ export class Frontend extends EventEmitter {
|
|
|
1082
871
|
}
|
|
1083
872
|
return undefined;
|
|
1084
873
|
};
|
|
1085
|
-
// Root endpoint
|
|
1086
874
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
1087
875
|
return batteryLevel(endpoint);
|
|
1088
|
-
// Child endpoints
|
|
1089
876
|
for (const child of endpoint.getChildEndpoints()) {
|
|
1090
|
-
// istanbul ignore else
|
|
1091
877
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
1092
878
|
return batteryLevel(child);
|
|
1093
879
|
}
|
|
1094
880
|
}
|
|
1095
|
-
/**
|
|
1096
|
-
* Retrieves the cluster text description from a given device.
|
|
1097
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
1098
|
-
*
|
|
1099
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
1100
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
1101
|
-
*/
|
|
1102
881
|
getClusterTextFromDevice(device) {
|
|
1103
882
|
if (this.matterbridge.hasCleanupStarted)
|
|
1104
|
-
return '';
|
|
1105
|
-
// istanbul ignore else
|
|
883
|
+
return '';
|
|
1106
884
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1107
885
|
return '';
|
|
1108
886
|
const getUserLabel = (device) => {
|
|
@@ -1112,7 +890,6 @@ export class Frontend extends EventEmitter {
|
|
|
1112
890
|
if (composed)
|
|
1113
891
|
return 'Composed: ' + composed.value;
|
|
1114
892
|
}
|
|
1115
|
-
// istanbul ignore next cause is not reachable
|
|
1116
893
|
return '';
|
|
1117
894
|
};
|
|
1118
895
|
const getFixedLabel = (device) => {
|
|
@@ -1122,13 +899,11 @@ export class Frontend extends EventEmitter {
|
|
|
1122
899
|
if (composed)
|
|
1123
900
|
return 'Composed: ' + composed.value;
|
|
1124
901
|
}
|
|
1125
|
-
// istanbul ignore next cause is not reacheable
|
|
1126
902
|
return '';
|
|
1127
903
|
};
|
|
1128
904
|
let attributes = '';
|
|
1129
905
|
let supportedModes = [];
|
|
1130
906
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1131
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
1132
907
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1133
908
|
return;
|
|
1134
909
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1218,17 +993,11 @@ export class Frontend extends EventEmitter {
|
|
|
1218
993
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1219
994
|
attributes += `${getUserLabel(device)} `;
|
|
1220
995
|
});
|
|
1221
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1222
996
|
return attributes.trimStart().trimEnd();
|
|
1223
997
|
}
|
|
1224
|
-
/**
|
|
1225
|
-
* Retrieves the registered plugins sanitized for res.json().
|
|
1226
|
-
*
|
|
1227
|
-
* @returns {ApiPlugin[]} An array of BaseRegisteredPlugin.
|
|
1228
|
-
*/
|
|
1229
998
|
getPlugins() {
|
|
1230
999
|
if (this.matterbridge.hasCleanupStarted)
|
|
1231
|
-
return [];
|
|
1000
|
+
return [];
|
|
1232
1001
|
const plugins = [];
|
|
1233
1002
|
for (const plugin of this.matterbridge.plugins.array()) {
|
|
1234
1003
|
plugins.push({
|
|
@@ -1256,27 +1025,18 @@ export class Frontend extends EventEmitter {
|
|
|
1256
1025
|
schemaJson: plugin.schemaJson,
|
|
1257
1026
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1258
1027
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1259
|
-
// Childbridge mode specific data
|
|
1260
1028
|
matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
|
|
1261
1029
|
});
|
|
1262
1030
|
}
|
|
1263
1031
|
return plugins;
|
|
1264
1032
|
}
|
|
1265
|
-
/**
|
|
1266
|
-
* Retrieves the devices from Matterbridge.
|
|
1267
|
-
*
|
|
1268
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1269
|
-
* @returns {ApiDevice[]} An array of ApiDevices for the frontend.
|
|
1270
|
-
*/
|
|
1271
1033
|
getDevices(pluginName) {
|
|
1272
1034
|
if (this.matterbridge.hasCleanupStarted)
|
|
1273
|
-
return [];
|
|
1035
|
+
return [];
|
|
1274
1036
|
const devices = [];
|
|
1275
1037
|
for (const device of this.matterbridge.devices.array()) {
|
|
1276
|
-
// Filter by pluginName if provided
|
|
1277
1038
|
if (pluginName && pluginName !== device.plugin)
|
|
1278
1039
|
continue;
|
|
1279
|
-
// Check if the device has the required properties
|
|
1280
1040
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1281
1041
|
continue;
|
|
1282
1042
|
devices.push({
|
|
@@ -1297,39 +1057,24 @@ export class Frontend extends EventEmitter {
|
|
|
1297
1057
|
}
|
|
1298
1058
|
return devices;
|
|
1299
1059
|
}
|
|
1300
|
-
/**
|
|
1301
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1302
|
-
*
|
|
1303
|
-
* Response for /api/clusters
|
|
1304
|
-
*
|
|
1305
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1306
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1307
|
-
* @returns {ApiClusters | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1308
|
-
*/
|
|
1309
1060
|
getClusters(pluginName, endpointNumber) {
|
|
1310
1061
|
if (this.matterbridge.hasCleanupStarted)
|
|
1311
|
-
return;
|
|
1062
|
+
return;
|
|
1312
1063
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1313
1064
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.maybeId || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1314
1065
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1315
1066
|
return;
|
|
1316
1067
|
}
|
|
1317
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1318
|
-
// Get the device types from the main endpoint
|
|
1319
1068
|
const deviceTypes = [];
|
|
1320
1069
|
const clusters = [];
|
|
1321
1070
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1322
1071
|
deviceTypes.push(d.deviceType);
|
|
1323
1072
|
});
|
|
1324
|
-
// Get the clusters from the main endpoint
|
|
1325
1073
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1326
1074
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1327
1075
|
return;
|
|
1328
1076
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1329
1077
|
return;
|
|
1330
|
-
// console.log(
|
|
1331
|
-
// `${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}`,
|
|
1332
|
-
// );
|
|
1333
1078
|
clusters.push({
|
|
1334
1079
|
endpoint: endpoint.number.toString(),
|
|
1335
1080
|
number: endpoint.number,
|
|
@@ -1343,19 +1088,12 @@ export class Frontend extends EventEmitter {
|
|
|
1343
1088
|
attributeLocalValue: attributeValue,
|
|
1344
1089
|
});
|
|
1345
1090
|
});
|
|
1346
|
-
// Get the child endpoints
|
|
1347
1091
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1348
|
-
// if (childEndpoints.length === 0) {
|
|
1349
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1350
|
-
// }
|
|
1351
1092
|
childEndpoints.forEach((childEndpoint) => {
|
|
1352
|
-
// istanbul ignore if cause is not reachable: should never happen but ...
|
|
1353
1093
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1354
1094
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1355
1095
|
return;
|
|
1356
1096
|
}
|
|
1357
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1358
|
-
// Get the device types of the child endpoint
|
|
1359
1097
|
const deviceTypes = [];
|
|
1360
1098
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1361
1099
|
deviceTypes.push(d.deviceType);
|
|
@@ -1365,9 +1103,6 @@ export class Frontend extends EventEmitter {
|
|
|
1365
1103
|
return;
|
|
1366
1104
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1367
1105
|
return;
|
|
1368
|
-
// console.log(
|
|
1369
|
-
// `${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}`,
|
|
1370
|
-
// );
|
|
1371
1106
|
clusters.push({
|
|
1372
1107
|
endpoint: childEndpoint.number.toString(),
|
|
1373
1108
|
number: childEndpoint.number,
|
|
@@ -1387,7 +1122,6 @@ export class Frontend extends EventEmitter {
|
|
|
1387
1122
|
async generateDiagnostic() {
|
|
1388
1123
|
this.log.debug('Generating diagnostic...');
|
|
1389
1124
|
const serverNodes = [];
|
|
1390
|
-
// istanbul ignore else
|
|
1391
1125
|
if (this.matterbridge.bridgeMode === 'bridge') {
|
|
1392
1126
|
if (this.matterbridge.serverNode)
|
|
1393
1127
|
serverNodes.push(this.matterbridge.serverNode);
|
|
@@ -1398,7 +1132,6 @@ export class Frontend extends EventEmitter {
|
|
|
1398
1132
|
serverNodes.push(plugin.serverNode);
|
|
1399
1133
|
}
|
|
1400
1134
|
}
|
|
1401
|
-
// istanbul ignore next
|
|
1402
1135
|
for (const device of this.matterbridge.devices.array()) {
|
|
1403
1136
|
if (device.serverNode)
|
|
1404
1137
|
serverNodes.push(device.serverNode);
|
|
@@ -1422,15 +1155,8 @@ export class Frontend extends EventEmitter {
|
|
|
1422
1155
|
values: [...serverNodes],
|
|
1423
1156
|
})));
|
|
1424
1157
|
delete Logger.destinations.diagnostic;
|
|
1425
|
-
await wait(500);
|
|
1158
|
+
await wait(500);
|
|
1426
1159
|
}
|
|
1427
|
-
/**
|
|
1428
|
-
* Handles incoming websocket api request messages from the Matterbridge frontend.
|
|
1429
|
-
*
|
|
1430
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1431
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1432
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1433
|
-
*/
|
|
1434
1160
|
async wsMessageHandler(client, message) {
|
|
1435
1161
|
let data;
|
|
1436
1162
|
const sendResponse = (data) => {
|
|
@@ -1448,13 +1174,12 @@ export class Frontend extends EventEmitter {
|
|
|
1448
1174
|
client.send(JSON.stringify(data));
|
|
1449
1175
|
}
|
|
1450
1176
|
else {
|
|
1451
|
-
// istanbul ignore next cause is only a safety check
|
|
1452
1177
|
this.log.error('Cannot send api response, client not connected');
|
|
1453
1178
|
}
|
|
1454
1179
|
};
|
|
1455
1180
|
try {
|
|
1456
1181
|
data = JSON.parse(message.toString());
|
|
1457
|
-
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method)
|
|
1182
|
+
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || data.dst !== 'Matterbridge') {
|
|
1458
1183
|
this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
|
|
1459
1184
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
|
|
1460
1185
|
return;
|
|
@@ -1511,22 +1236,7 @@ export class Frontend extends EventEmitter {
|
|
|
1511
1236
|
}
|
|
1512
1237
|
this.wssSendSnackbarMessage(`Adding plugin ${data.params.pluginNameOrPath}...`, 5);
|
|
1513
1238
|
this.log.debug(`Adding plugin ${data.params.pluginNameOrPath}...`);
|
|
1514
|
-
data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
|
|
1515
|
-
/*
|
|
1516
|
-
const plugin = (await this.server.fetch({ type: 'plugins_add', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginNameOrPath } }, 5000)).response.plugin;
|
|
1517
|
-
if (plugin) {
|
|
1518
|
-
this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
|
|
1519
|
-
await this.server.fetch({ type: 'plugins_load', src: this.server.name, dst: 'plugins', params: { plugin: plugin.name } }, 5000);
|
|
1520
|
-
this.wssSendRestartRequired();
|
|
1521
|
-
this.wssSendRefreshRequired('plugins');
|
|
1522
|
-
this.wssSendRefreshRequired('devices');
|
|
1523
|
-
this.wssSendSnackbarMessage(`Loaded plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
|
|
1524
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1525
|
-
} else {
|
|
1526
|
-
this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
|
|
1527
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
|
|
1528
|
-
}
|
|
1529
|
-
*/
|
|
1239
|
+
data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
|
|
1530
1240
|
const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
|
|
1531
1241
|
if (plugin) {
|
|
1532
1242
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1539,7 +1249,7 @@ export class Frontend extends EventEmitter {
|
|
|
1539
1249
|
this.wssSendSnackbarMessage(`Loaded plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
|
|
1540
1250
|
return;
|
|
1541
1251
|
})
|
|
1542
|
-
.catch(
|
|
1252
|
+
.catch((_error) => { });
|
|
1543
1253
|
}
|
|
1544
1254
|
else {
|
|
1545
1255
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
|
|
@@ -1553,10 +1263,6 @@ export class Frontend extends EventEmitter {
|
|
|
1553
1263
|
}
|
|
1554
1264
|
this.wssSendSnackbarMessage(`Removing plugin ${data.params.pluginName}...`, 5);
|
|
1555
1265
|
this.log.debug(`Removing plugin ${data.params.pluginName}...`);
|
|
1556
|
-
/*
|
|
1557
|
-
await this.server.fetch({ type: 'plugins_shutdown', src: this.server.name, dst: 'plugins', params: { plugin: data.params.pluginName, reason: 'The plugin has been removed.', removeAllDevices: true } }, 5000);
|
|
1558
|
-
await this.server.fetch({ type: 'plugins_remove', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginName } }, 5000);
|
|
1559
|
-
*/
|
|
1560
1266
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1561
1267
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
1562
1268
|
await this.matterbridge.plugins.remove(data.params.pluginName);
|
|
@@ -1591,7 +1297,7 @@ export class Frontend extends EventEmitter {
|
|
|
1591
1297
|
this.wssSendSnackbarMessage(`Started plugin ${localData.params.pluginName}`, 5, 'success');
|
|
1592
1298
|
return;
|
|
1593
1299
|
})
|
|
1594
|
-
.catch(
|
|
1300
|
+
.catch((_error) => { });
|
|
1595
1301
|
}
|
|
1596
1302
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1597
1303
|
}
|
|
@@ -1616,7 +1322,6 @@ export class Frontend extends EventEmitter {
|
|
|
1616
1322
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1617
1323
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
|
|
1618
1324
|
if (plugin.serverNode) {
|
|
1619
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1620
1325
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1621
1326
|
plugin.serverNode = undefined;
|
|
1622
1327
|
}
|
|
@@ -1626,20 +1331,18 @@ export class Frontend extends EventEmitter {
|
|
|
1626
1331
|
this.matterbridge.devices.remove(device);
|
|
1627
1332
|
}
|
|
1628
1333
|
}
|
|
1629
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1630
1334
|
if (plugin.type === 'DynamicPlatform' && !plugin.locked)
|
|
1631
1335
|
await this.matterbridge.createDynamicPlugin(plugin);
|
|
1632
1336
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
|
|
1633
|
-
plugin.restartRequired = false;
|
|
1337
|
+
plugin.restartRequired = false;
|
|
1634
1338
|
let needRestart = 0;
|
|
1635
1339
|
for (const plugin of this.matterbridge.plugins) {
|
|
1636
1340
|
if (plugin.restartRequired)
|
|
1637
1341
|
needRestart++;
|
|
1638
1342
|
}
|
|
1639
1343
|
if (needRestart === 0) {
|
|
1640
|
-
this.wssSendRestartNotRequired(true);
|
|
1344
|
+
this.wssSendRestartNotRequired(true);
|
|
1641
1345
|
}
|
|
1642
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1643
1346
|
if (plugin.serverNode)
|
|
1644
1347
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1645
1348
|
this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
|
|
@@ -1784,9 +1487,6 @@ export class Frontend extends EventEmitter {
|
|
|
1784
1487
|
this.wssSendRefreshRequired('matter', { matter: { ...matter, advertiseTime: 0, advertising: false } });
|
|
1785
1488
|
}
|
|
1786
1489
|
if (data.params.advertise) {
|
|
1787
|
-
// TODO: matter.js 0.16.0
|
|
1788
|
-
// await serverNode.env.get(DeviceAdvertiser)?.advertise(true);
|
|
1789
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1790
1490
|
const advertiser = serverNode.env.get(DeviceAdvertiser);
|
|
1791
1491
|
if (advertiser && advertiser.advertise && typeof advertiser.advertise === 'function')
|
|
1792
1492
|
await advertiser.advertise(true);
|
|
@@ -1848,7 +1548,6 @@ export class Frontend extends EventEmitter {
|
|
|
1848
1548
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/devices' });
|
|
1849
1549
|
return;
|
|
1850
1550
|
}
|
|
1851
|
-
// istanbul ignore next
|
|
1852
1551
|
const selectDeviceValues = !plugin.platform ? [] : plugin.platform.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1853
1552
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectDeviceValues });
|
|
1854
1553
|
}
|
|
@@ -1862,7 +1561,6 @@ export class Frontend extends EventEmitter {
|
|
|
1862
1561
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' });
|
|
1863
1562
|
return;
|
|
1864
1563
|
}
|
|
1865
|
-
// istanbul ignore next
|
|
1866
1564
|
const selectEntityValues = !plugin.platform ? [] : plugin.platform.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1867
1565
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectEntityValues });
|
|
1868
1566
|
}
|
|
@@ -1914,22 +1612,22 @@ export class Frontend extends EventEmitter {
|
|
|
1914
1612
|
if (isValidString(data.params.value, 4)) {
|
|
1915
1613
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1916
1614
|
if (data.params.value === 'Debug') {
|
|
1917
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1615
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1918
1616
|
}
|
|
1919
1617
|
else if (data.params.value === 'Info') {
|
|
1920
|
-
await this.matterbridge.setLogLevel("info"
|
|
1618
|
+
await this.matterbridge.setLogLevel("info");
|
|
1921
1619
|
}
|
|
1922
1620
|
else if (data.params.value === 'Notice') {
|
|
1923
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1621
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1924
1622
|
}
|
|
1925
1623
|
else if (data.params.value === 'Warn') {
|
|
1926
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1624
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1927
1625
|
}
|
|
1928
1626
|
else if (data.params.value === 'Error') {
|
|
1929
|
-
await this.matterbridge.setLogLevel("error"
|
|
1627
|
+
await this.matterbridge.setLogLevel("error");
|
|
1930
1628
|
}
|
|
1931
1629
|
else if (data.params.value === 'Fatal') {
|
|
1932
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1630
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1933
1631
|
}
|
|
1934
1632
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1935
1633
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1940,7 +1638,6 @@ export class Frontend extends EventEmitter {
|
|
|
1940
1638
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1941
1639
|
this.matterbridge.fileLogger = data.params.value;
|
|
1942
1640
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1943
|
-
// Create the file logger for matterbridge
|
|
1944
1641
|
if (data.params.value)
|
|
1945
1642
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), await this.matterbridge.getLogLevel(), true);
|
|
1946
1643
|
else
|
|
@@ -1970,12 +1667,11 @@ export class Frontend extends EventEmitter {
|
|
|
1970
1667
|
Logger.level = MatterLogLevel.FATAL;
|
|
1971
1668
|
}
|
|
1972
1669
|
this.matterbridge.matterLogLevel = MatterLogLevel.names[Logger.level];
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
1670
|
+
let callbackLogLevel = "notice";
|
|
1671
|
+
if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
|
|
1672
|
+
callbackLogLevel = "info";
|
|
1673
|
+
if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
1674
|
+
callbackLogLevel = "debug";
|
|
1979
1675
|
AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
|
|
1980
1676
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
1981
1677
|
await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.level);
|
|
@@ -2027,7 +1723,6 @@ export class Frontend extends EventEmitter {
|
|
|
2027
1723
|
}
|
|
2028
1724
|
break;
|
|
2029
1725
|
case 'setmatterport':
|
|
2030
|
-
// eslint-disable-next-line no-case-declarations
|
|
2031
1726
|
const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
2032
1727
|
if (isValidNumber(port, 5540, 5600)) {
|
|
2033
1728
|
this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
|
|
@@ -2047,7 +1742,6 @@ export class Frontend extends EventEmitter {
|
|
|
2047
1742
|
}
|
|
2048
1743
|
break;
|
|
2049
1744
|
case 'setmatterdiscriminator':
|
|
2050
|
-
// eslint-disable-next-line no-case-declarations
|
|
2051
1745
|
const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
2052
1746
|
if (isValidNumber(discriminator, 0, 4095)) {
|
|
2053
1747
|
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
|
|
@@ -2067,7 +1761,6 @@ export class Frontend extends EventEmitter {
|
|
|
2067
1761
|
}
|
|
2068
1762
|
break;
|
|
2069
1763
|
case 'setmatterpasscode':
|
|
2070
|
-
// eslint-disable-next-line no-case-declarations
|
|
2071
1764
|
const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
2072
1765
|
if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
|
|
2073
1766
|
this.matterbridge.passcode = passcode;
|
|
@@ -2113,19 +1806,15 @@ export class Frontend extends EventEmitter {
|
|
|
2113
1806
|
return;
|
|
2114
1807
|
}
|
|
2115
1808
|
const config = plugin.configJson;
|
|
2116
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2117
1809
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
2118
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
2119
1810
|
if (select === 'serial')
|
|
2120
1811
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
2121
1812
|
if (select === 'name')
|
|
2122
1813
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
2123
1814
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
2124
|
-
// Remove postfix from the serial if it exists
|
|
2125
1815
|
if (config.postfix) {
|
|
2126
1816
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
2127
1817
|
}
|
|
2128
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
2129
1818
|
if (isValidArray(config.whiteList, 1)) {
|
|
2130
1819
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
2131
1820
|
config.whiteList.push(data.params.serial);
|
|
@@ -2134,7 +1823,6 @@ export class Frontend extends EventEmitter {
|
|
|
2134
1823
|
config.whiteList.push(data.params.name);
|
|
2135
1824
|
}
|
|
2136
1825
|
}
|
|
2137
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
2138
1826
|
if (isValidArray(config.blackList, 1)) {
|
|
2139
1827
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
2140
1828
|
config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
|
|
@@ -2162,9 +1850,7 @@ export class Frontend extends EventEmitter {
|
|
|
2162
1850
|
return;
|
|
2163
1851
|
}
|
|
2164
1852
|
const config = plugin.configJson;
|
|
2165
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2166
1853
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
2167
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
2168
1854
|
if (select === 'serial')
|
|
2169
1855
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
2170
1856
|
if (select === 'name')
|
|
@@ -2173,7 +1859,6 @@ export class Frontend extends EventEmitter {
|
|
|
2173
1859
|
if (config.postfix) {
|
|
2174
1860
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
2175
1861
|
}
|
|
2176
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
2177
1862
|
if (isValidArray(config.whiteList, 1)) {
|
|
2178
1863
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
2179
1864
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
|
|
@@ -2182,7 +1867,6 @@ export class Frontend extends EventEmitter {
|
|
|
2182
1867
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
|
|
2183
1868
|
}
|
|
2184
1869
|
}
|
|
2185
|
-
// Add the serial to the blackList
|
|
2186
1870
|
if (isValidArray(config.blackList)) {
|
|
2187
1871
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
2188
1872
|
config.blackList.push(data.params.serial);
|
|
@@ -2205,7 +1889,6 @@ export class Frontend extends EventEmitter {
|
|
|
2205
1889
|
}
|
|
2206
1890
|
}
|
|
2207
1891
|
else {
|
|
2208
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2209
1892
|
const localData = data;
|
|
2210
1893
|
this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
|
|
2211
1894
|
sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
|
|
@@ -2215,46 +1898,23 @@ export class Frontend extends EventEmitter {
|
|
|
2215
1898
|
inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
|
|
2216
1899
|
}
|
|
2217
1900
|
}
|
|
2218
|
-
/**
|
|
2219
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
2220
|
-
*
|
|
2221
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
2222
|
-
* @param {string} time - The time string of the message
|
|
2223
|
-
* @param {string} name - The logger name of the message
|
|
2224
|
-
* @param {string} message - The content of the message.
|
|
2225
|
-
*
|
|
2226
|
-
* @remarks
|
|
2227
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
2228
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
2229
|
-
* The function sends the message to all connected clients.
|
|
2230
|
-
*/
|
|
2231
1901
|
wssSendLogMessage(level, time, name, message) {
|
|
2232
1902
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2233
1903
|
return;
|
|
2234
1904
|
if (!level || !time || !name || !message)
|
|
2235
1905
|
return;
|
|
2236
|
-
// Remove ANSI escape codes from the message
|
|
2237
|
-
// eslint-disable-next-line no-control-regex
|
|
2238
1906
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
2239
|
-
// Remove leading asterisks from the message
|
|
2240
1907
|
message = message.replace(/^\*+/, '');
|
|
2241
|
-
// Replace all occurrences of \t and \n
|
|
2242
1908
|
message = message.replace(/[\t\n]/g, '');
|
|
2243
|
-
// Remove non-printable characters
|
|
2244
|
-
// eslint-disable-next-line no-control-regex
|
|
2245
1909
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
2246
|
-
// Replace all occurrences of \" with "
|
|
2247
1910
|
message = message.replace(/\\"/g, '"');
|
|
2248
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
2249
1911
|
const maxContinuousLength = 100;
|
|
2250
1912
|
const keepStartLength = 20;
|
|
2251
1913
|
const keepEndLength = 20;
|
|
2252
|
-
// Split the message into words
|
|
2253
1914
|
if (level !== 'spawn') {
|
|
2254
1915
|
message = message
|
|
2255
1916
|
.split(' ')
|
|
2256
1917
|
.map((word) => {
|
|
2257
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2258
1918
|
if (word.length > maxContinuousLength) {
|
|
2259
1919
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2260
1920
|
}
|
|
@@ -2262,34 +1922,14 @@ export class Frontend extends EventEmitter {
|
|
|
2262
1922
|
})
|
|
2263
1923
|
.join(' ');
|
|
2264
1924
|
}
|
|
2265
|
-
// Send the message to all connected clients
|
|
2266
1925
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
|
|
2267
1926
|
}
|
|
2268
|
-
/**
|
|
2269
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2270
|
-
*
|
|
2271
|
-
* @param {string} changed - The changed value.
|
|
2272
|
-
* @param {Record<string, unknown>} params - Additional parameters to send with the message.
|
|
2273
|
-
* possible values for changed:
|
|
2274
|
-
* - 'settings' (when the bridge has started in bridge mode or childbridge mode and when update finds a new version)
|
|
2275
|
-
* - 'plugins'
|
|
2276
|
-
* - 'devices'
|
|
2277
|
-
* - 'matter' with param 'matter' (QRDiv component)
|
|
2278
|
-
* @param {ApiMatter} params.matter - The matter device that has changed. Required if changed is 'matter'.
|
|
2279
|
-
*/
|
|
2280
1927
|
wssSendRefreshRequired(changed, params) {
|
|
2281
1928
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2282
1929
|
return;
|
|
2283
1930
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
2284
|
-
// Send the message to all connected clients
|
|
2285
1931
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
|
|
2286
1932
|
}
|
|
2287
|
-
/**
|
|
2288
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2289
|
-
*
|
|
2290
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
|
|
2291
|
-
* @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
|
|
2292
|
-
*/
|
|
2293
1933
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
2294
1934
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2295
1935
|
return;
|
|
@@ -2298,14 +1938,8 @@ export class Frontend extends EventEmitter {
|
|
|
2298
1938
|
this.matterbridge.fixedRestartRequired = fixed;
|
|
2299
1939
|
if (snackbar === true)
|
|
2300
1940
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
2301
|
-
// Send the message to all connected clients
|
|
2302
1941
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
|
|
2303
1942
|
}
|
|
2304
|
-
/**
|
|
2305
|
-
* Sends a no need to restart WebSocket message to all connected clients.
|
|
2306
|
-
*
|
|
2307
|
-
* @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients. Default is true.
|
|
2308
|
-
*/
|
|
2309
1943
|
wssSendRestartNotRequired(snackbar = true) {
|
|
2310
1944
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2311
1945
|
return;
|
|
@@ -2313,145 +1947,64 @@ export class Frontend extends EventEmitter {
|
|
|
2313
1947
|
this.matterbridge.restartRequired = false;
|
|
2314
1948
|
if (snackbar === true)
|
|
2315
1949
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2316
|
-
// Send the message to all connected clients
|
|
2317
1950
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
|
|
2318
1951
|
}
|
|
2319
|
-
/**
|
|
2320
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
2321
|
-
*
|
|
2322
|
-
* @param {boolean} devVersion - If true, the update is for a development version. Default is false.
|
|
2323
|
-
*/
|
|
2324
1952
|
wssSendUpdateRequired(devVersion = false) {
|
|
2325
1953
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2326
1954
|
return;
|
|
2327
1955
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2328
1956
|
this.matterbridge.updateRequired = true;
|
|
2329
|
-
// Send the message to all connected clients
|
|
2330
1957
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
|
|
2331
1958
|
}
|
|
2332
|
-
/**
|
|
2333
|
-
* Sends a cpu update message to all connected clients.
|
|
2334
|
-
*
|
|
2335
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2336
|
-
* @param {number} processCpuUsage - The CPU usage percentage of the process to send.
|
|
2337
|
-
*/
|
|
2338
1959
|
wssSendCpuUpdate(cpuUsage, processCpuUsage) {
|
|
2339
1960
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2340
1961
|
return;
|
|
2341
|
-
// istanbul ignore else
|
|
2342
1962
|
if (hasParameter('debug'))
|
|
2343
1963
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2344
|
-
// Send the message to all connected clients
|
|
2345
1964
|
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 } });
|
|
2346
1965
|
}
|
|
2347
|
-
/**
|
|
2348
|
-
* Sends a memory update message to all connected clients.
|
|
2349
|
-
*
|
|
2350
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2351
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2352
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2353
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2354
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2355
|
-
* @param {string} external - The external memory in bytes.
|
|
2356
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2357
|
-
*/
|
|
2358
1966
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2359
1967
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2360
1968
|
return;
|
|
2361
|
-
// istanbul ignore else
|
|
2362
1969
|
if (hasParameter('debug'))
|
|
2363
1970
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2364
|
-
// Send the message to all connected clients
|
|
2365
1971
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', success: true, response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
|
|
2366
1972
|
}
|
|
2367
|
-
/**
|
|
2368
|
-
* Sends an uptime update message to all connected clients.
|
|
2369
|
-
*
|
|
2370
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2371
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2372
|
-
*/
|
|
2373
1973
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2374
1974
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2375
1975
|
return;
|
|
2376
|
-
// istanbul ignore else
|
|
2377
1976
|
if (hasParameter('debug'))
|
|
2378
1977
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2379
|
-
// Send the message to all connected clients
|
|
2380
1978
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
|
|
2381
1979
|
}
|
|
2382
|
-
/**
|
|
2383
|
-
* Sends an open snackbar message to all connected clients.
|
|
2384
|
-
*
|
|
2385
|
-
* @param {string} message - The message to send.
|
|
2386
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message. Default is 5 seconds.
|
|
2387
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the message.
|
|
2388
|
-
* possible values are: 'info', 'warning', 'error', 'success'. Default is 'info'.
|
|
2389
|
-
*
|
|
2390
|
-
* @remarks
|
|
2391
|
-
* If timeout is 0, the snackbar message will be displayed until closed by the user.
|
|
2392
|
-
*/
|
|
2393
1980
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2394
1981
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2395
1982
|
return;
|
|
2396
1983
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2397
|
-
// Send the message to all connected clients
|
|
2398
1984
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
|
|
2399
1985
|
}
|
|
2400
|
-
/**
|
|
2401
|
-
* Sends a close snackbar message to all connected clients.
|
|
2402
|
-
* It will close the snackbar message with the same message and timeout = 0.
|
|
2403
|
-
*
|
|
2404
|
-
* @param {string} message - The message to send.
|
|
2405
|
-
*/
|
|
2406
1986
|
wssSendCloseSnackbarMessage(message) {
|
|
2407
1987
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2408
1988
|
return;
|
|
2409
1989
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2410
|
-
// Send the message to all connected clients
|
|
2411
1990
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
|
|
2412
1991
|
}
|
|
2413
|
-
/**
|
|
2414
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2415
|
-
*
|
|
2416
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2417
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2418
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2419
|
-
* @param {EndpointNumber} number - The endpoint number where the attribute belongs.
|
|
2420
|
-
* @param {string} id - The endpoint id where the attribute belongs.
|
|
2421
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2422
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2423
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2424
|
-
*
|
|
2425
|
-
* @remarks
|
|
2426
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2427
|
-
* with the updated attribute information.
|
|
2428
|
-
*/
|
|
2429
1992
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
|
|
2430
1993
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2431
1994
|
return;
|
|
2432
1995
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2433
|
-
// Send the message to all connected clients
|
|
2434
1996
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', success: true, response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value } });
|
|
2435
1997
|
}
|
|
2436
|
-
/**
|
|
2437
|
-
* Sends a message to all connected clients.
|
|
2438
|
-
* This is an helper function to send a broadcast message to all connected clients.
|
|
2439
|
-
*
|
|
2440
|
-
* @param {WsMessageBroadcast} msg - The message to send.
|
|
2441
|
-
*/
|
|
2442
1998
|
wssBroadcastMessage(msg) {
|
|
2443
1999
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2444
2000
|
return;
|
|
2445
|
-
// Send the message to all connected clients
|
|
2446
2001
|
const stringifiedMsg = JSON.stringify(msg);
|
|
2447
2002
|
if (msg.method !== 'log')
|
|
2448
2003
|
this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
|
|
2449
2004
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2450
|
-
// istanbul ignore else
|
|
2451
2005
|
if (client.readyState === client.OPEN) {
|
|
2452
2006
|
client.send(stringifiedMsg);
|
|
2453
2007
|
}
|
|
2454
2008
|
});
|
|
2455
2009
|
}
|
|
2456
2010
|
}
|
|
2457
|
-
//# sourceMappingURL=frontend.js.map
|