matterbridge 3.5.0 → 3.5.1-dev-20260121-22e98b4
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/bin/mb_coap.js +1 -1
- package/bin/mb_mdns.js +1 -1
- package/dist/broadcastServer.d.ts +0 -115
- package/dist/broadcastServer.js +1 -119
- package/dist/broadcastServerTypes.d.ts +0 -43
- package/dist/broadcastServerTypes.js +0 -24
- package/dist/cli.d.ts +1 -26
- package/dist/cli.js +2 -102
- package/dist/cliEmitter.d.ts +0 -36
- package/dist/cliEmitter.js +0 -37
- package/dist/cliHistory.d.ts +0 -42
- package/dist/cliHistory.js +1 -39
- package/dist/clusters/export.d.ts +0 -1
- package/dist/clusters/export.js +0 -2
- package/dist/deviceManager.d.ts +0 -108
- package/dist/deviceManager.js +2 -114
- package/dist/devices/airConditioner.d.ts +0 -75
- package/dist/devices/airConditioner.js +0 -57
- package/dist/devices/batteryStorage.d.ts +0 -43
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/cooktop.d.ts +0 -55
- package/dist/devices/cooktop.js +0 -56
- package/dist/devices/dishwasher.d.ts +0 -55
- package/dist/devices/dishwasher.js +0 -57
- package/dist/devices/evse.d.ts +0 -57
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.d.ts +0 -1
- package/dist/devices/export.js +0 -5
- package/dist/devices/extractorHood.d.ts +0 -41
- package/dist/devices/extractorHood.js +0 -43
- package/dist/devices/heatPump.d.ts +0 -43
- package/dist/devices/heatPump.js +2 -50
- package/dist/devices/laundryDryer.d.ts +0 -58
- package/dist/devices/laundryDryer.js +3 -62
- package/dist/devices/laundryWasher.d.ts +0 -64
- package/dist/devices/laundryWasher.js +4 -70
- package/dist/devices/microwaveOven.d.ts +1 -77
- package/dist/devices/microwaveOven.js +5 -88
- package/dist/devices/oven.d.ts +0 -82
- package/dist/devices/oven.js +0 -85
- package/dist/devices/refrigerator.d.ts +0 -100
- package/dist/devices/refrigerator.js +0 -102
- package/dist/devices/roboticVacuumCleaner.d.ts +0 -83
- package/dist/devices/roboticVacuumCleaner.js +9 -100
- package/dist/devices/solarPower.d.ts +0 -36
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/speaker.d.ts +0 -79
- package/dist/devices/speaker.js +0 -84
- package/dist/devices/temperatureControl.d.ts +0 -21
- package/dist/devices/temperatureControl.js +3 -24
- package/dist/devices/waterHeater.d.ts +0 -74
- package/dist/devices/waterHeater.js +2 -82
- package/dist/frontend.d.ts +0 -187
- package/dist/frontend.js +39 -505
- package/dist/frontendTypes.d.ts +0 -57
- package/dist/frontendTypes.js +0 -45
- package/dist/helpers.d.ts +0 -43
- package/dist/helpers.js +1 -54
- package/dist/index.d.ts +0 -23
- package/dist/index.js +0 -25
- package/dist/jestutils/export.d.ts +0 -1
- package/dist/jestutils/export.js +0 -1
- package/dist/jestutils/jestHelpers.d.ts +0 -255
- package/dist/jestutils/jestHelpers.js +14 -372
- package/dist/logger/export.d.ts +0 -1
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.d.ts +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.d.ts +0 -1
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.d.ts +0 -1
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.d.ts +0 -1
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.d.ts +0 -1
- package/dist/matter/export.js +0 -2
- package/dist/matter/types.d.ts +0 -1
- package/dist/matter/types.js +0 -2
- package/dist/matterNode.d.ts +0 -258
- package/dist/matterNode.js +9 -364
- package/dist/matterbridge.d.ts +0 -362
- package/dist/matterbridge.js +60 -860
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -36
- package/dist/matterbridgeAccessoryPlatform.js +0 -38
- package/dist/matterbridgeBehaviors.d.ts +0 -24
- package/dist/matterbridgeBehaviors.js +5 -68
- package/dist/matterbridgeDeviceTypes.d.ts +0 -649
- package/dist/matterbridgeDeviceTypes.js +6 -673
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -36
- package/dist/matterbridgeDynamicPlatform.js +0 -38
- package/dist/matterbridgeEndpoint.d.ts +2 -1332
- package/dist/matterbridgeEndpoint.js +94 -1459
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -425
- package/dist/matterbridgeEndpointHelpers.js +21 -486
- package/dist/matterbridgeEndpointTypes.d.ts +0 -70
- package/dist/matterbridgeEndpointTypes.js +0 -25
- package/dist/matterbridgePlatform.d.ts +0 -425
- package/dist/matterbridgePlatform.js +2 -453
- package/dist/matterbridgeTypes.d.ts +0 -46
- package/dist/matterbridgeTypes.js +0 -26
- package/dist/mb_coap.d.ts +1 -0
- package/dist/{dgram/mb_coap.js → mb_coap.js} +3 -41
- package/dist/mb_mdns.d.ts +1 -0
- package/dist/{dgram/mb_mdns.js → mb_mdns.js} +37 -81
- package/dist/pluginManager.d.ts +0 -305
- package/dist/pluginManager.js +8 -345
- package/dist/shelly.d.ts +0 -157
- package/dist/shelly.js +7 -178
- package/dist/spawn.d.ts +1 -0
- package/dist/{utils/spawn.js → spawn.js} +3 -73
- package/dist/storage/export.d.ts +0 -1
- package/dist/storage/export.js +0 -1
- package/dist/update.d.ts +0 -75
- package/dist/update.js +7 -100
- package/dist/utils/export.d.ts +1 -13
- package/dist/utils/export.js +1 -13
- package/dist/workerGlobalPrefix.d.ts +0 -24
- package/dist/workerGlobalPrefix.js +6 -40
- package/dist/workerTypes.d.ts +0 -25
- package/dist/workerTypes.js +0 -24
- package/dist/workers.d.ts +0 -61
- package/dist/workers.js +4 -68
- package/npm-shrinkwrap.json +35 -5
- package/package.json +5 -5
- package/dist/broadcastServer.d.ts.map +0 -1
- package/dist/broadcastServer.js.map +0 -1
- package/dist/broadcastServerTypes.d.ts.map +0 -1
- package/dist/broadcastServerTypes.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cliEmitter.d.ts.map +0 -1
- package/dist/cliEmitter.js.map +0 -1
- package/dist/cliHistory.d.ts.map +0 -1
- package/dist/cliHistory.js.map +0 -1
- package/dist/clusters/export.d.ts.map +0 -1
- package/dist/clusters/export.js.map +0 -1
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/devices/airConditioner.d.ts.map +0 -1
- package/dist/devices/airConditioner.js.map +0 -1
- package/dist/devices/batteryStorage.d.ts.map +0 -1
- package/dist/devices/batteryStorage.js.map +0 -1
- package/dist/devices/cooktop.d.ts.map +0 -1
- package/dist/devices/cooktop.js.map +0 -1
- package/dist/devices/dishwasher.d.ts.map +0 -1
- package/dist/devices/dishwasher.js.map +0 -1
- package/dist/devices/evse.d.ts.map +0 -1
- package/dist/devices/evse.js.map +0 -1
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.js.map +0 -1
- package/dist/devices/extractorHood.d.ts.map +0 -1
- package/dist/devices/extractorHood.js.map +0 -1
- package/dist/devices/heatPump.d.ts.map +0 -1
- package/dist/devices/heatPump.js.map +0 -1
- package/dist/devices/laundryDryer.d.ts.map +0 -1
- package/dist/devices/laundryDryer.js.map +0 -1
- package/dist/devices/laundryWasher.d.ts.map +0 -1
- package/dist/devices/laundryWasher.js.map +0 -1
- package/dist/devices/microwaveOven.d.ts.map +0 -1
- package/dist/devices/microwaveOven.js.map +0 -1
- package/dist/devices/oven.d.ts.map +0 -1
- package/dist/devices/oven.js.map +0 -1
- package/dist/devices/refrigerator.d.ts.map +0 -1
- package/dist/devices/refrigerator.js.map +0 -1
- package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
- package/dist/devices/roboticVacuumCleaner.js.map +0 -1
- package/dist/devices/solarPower.d.ts.map +0 -1
- package/dist/devices/solarPower.js.map +0 -1
- package/dist/devices/speaker.d.ts.map +0 -1
- package/dist/devices/speaker.js.map +0 -1
- package/dist/devices/temperatureControl.d.ts.map +0 -1
- package/dist/devices/temperatureControl.js.map +0 -1
- 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 +0 -365
- package/dist/dgram/coap.js.map +0 -1
- package/dist/dgram/dgram.d.ts +0 -144
- package/dist/dgram/dgram.d.ts.map +0 -1
- package/dist/dgram/dgram.js +0 -363
- 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 -371
- package/dist/dgram/mdns.d.ts.map +0 -1
- package/dist/dgram/mdns.js +0 -934
- 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 +0 -179
- package/dist/dgram/multicast.js.map +0 -1
- package/dist/dgram/unicast.d.ts +0 -64
- package/dist/dgram/unicast.d.ts.map +0 -1
- package/dist/dgram/unicast.js +0 -100
- package/dist/dgram/unicast.js.map +0 -1
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/frontendTypes.d.ts.map +0 -1
- package/dist/frontendTypes.js.map +0 -1
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/jestutils/export.d.ts.map +0 -1
- package/dist/jestutils/export.js.map +0 -1
- package/dist/jestutils/jestHelpers.d.ts.map +0 -1
- package/dist/jestutils/jestHelpers.js.map +0 -1
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterNode.d.ts.map +0 -1
- package/dist/matterNode.js.map +0 -1
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgeEndpointTypes.d.ts.map +0 -1
- package/dist/matterbridgeEndpointTypes.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- 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 +0 -282
- 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 +0 -123
- 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 +0 -76
- 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 +0 -54
- 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 +0 -114
- 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 +0 -79
- 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 +0 -129
- 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 +0 -54
- package/dist/utils/error.js.map +0 -1
- 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 +0 -78
- 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 +0 -242
- 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 +0 -268
- 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 +0 -162
- package/dist/utils/isValid.js.map +0 -1
- package/dist/utils/network.d.ts +0 -141
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js +0 -314
- 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 +0 -264
- 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 +0 -125
- package/dist/utils/wait.js.map +0 -1
- package/dist/workerGlobalPrefix.d.ts.map +0 -1
- package/dist/workerGlobalPrefix.js.map +0 -1
- package/dist/workerTypes.d.ts.map +0 -1
- package/dist/workerTypes.js.map +0 -1
- package/dist/workers.d.ts.map +0 -1
- package/dist/workers.js.map +0 -1
package/dist/frontend.js
CHANGED
|
@@ -1,34 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Frontend.
|
|
3
|
-
*
|
|
4
|
-
* @file frontend.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @created 2025-01-13
|
|
7
|
-
* @version 1.3.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';
|
|
@@ -36,13 +10,8 @@ import { FabricIndex } from '@matter/types/datatype';
|
|
|
36
10
|
import { CommissioningOptions } from '@matter/types/commissioning';
|
|
37
11
|
import { BridgedDeviceBasicInformation } from '@matter/types/clusters/bridged-device-basic-information';
|
|
38
12
|
import { PowerSource } from '@matter/types/clusters/power-source';
|
|
13
|
+
import { createZip, formatBytes, formatPercent, formatUptime, getParameter, hasParameter, inspectError, isValidArray, isValidBoolean, isValidNumber, isValidObject, isValidString, wait, withTimeout } from '@matterbridge/utils';
|
|
39
14
|
import { MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_DIAGNOSTIC_FILE, MATTERBRIDGE_HISTORY_FILE, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg } from './matterbridgeTypes.js';
|
|
40
|
-
import { isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean } from './utils/isValid.js';
|
|
41
|
-
import { createZip } from './utils/createZip.js';
|
|
42
|
-
import { hasParameter, getParameter } from './utils/commandLine.js';
|
|
43
|
-
import { withTimeout, wait } from './utils/wait.js';
|
|
44
|
-
import { inspectError } from './utils/error.js';
|
|
45
|
-
import { formatBytes, formatUptime, formatPercent } from './utils/format.js';
|
|
46
15
|
import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
|
|
47
16
|
import { cliEmitter, lastOsCpuUsage, lastProcessCpuUsage } from './cliEmitter.js';
|
|
48
17
|
import { generateHistoryPage } from './cliHistory.js';
|
|
@@ -63,7 +32,7 @@ export class Frontend extends EventEmitter {
|
|
|
63
32
|
constructor(matterbridge) {
|
|
64
33
|
super();
|
|
65
34
|
this.matterbridge = matterbridge;
|
|
66
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
35
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
67
36
|
this.log.logNameColor = '\x1b[38;5;97m';
|
|
68
37
|
this.server = new BroadcastServer('frontend', this.log);
|
|
69
38
|
this.server.on('broadcast_message', this.msgHandler.bind(this));
|
|
@@ -74,7 +43,6 @@ export class Frontend extends EventEmitter {
|
|
|
74
43
|
}
|
|
75
44
|
async msgHandler(msg) {
|
|
76
45
|
if (this.server.isWorkerRequest(msg)) {
|
|
77
|
-
// istanbul ignore else
|
|
78
46
|
if (this.verbose)
|
|
79
47
|
this.log.debug(`Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
|
|
80
48
|
switch (msg.type) {
|
|
@@ -126,13 +94,11 @@ export class Frontend extends EventEmitter {
|
|
|
126
94
|
this.server.respond({ ...msg, result: { success: true } });
|
|
127
95
|
break;
|
|
128
96
|
default:
|
|
129
|
-
// istanbul ignore next
|
|
130
97
|
if (this.verbose)
|
|
131
98
|
this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
|
|
132
99
|
}
|
|
133
100
|
}
|
|
134
101
|
if (this.server.isWorkerResponse(msg) && msg.result) {
|
|
135
|
-
// istanbul ignore next
|
|
136
102
|
if (this.verbose)
|
|
137
103
|
this.log.debug(`Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
|
|
138
104
|
switch (msg.type) {
|
|
@@ -168,55 +134,23 @@ export class Frontend extends EventEmitter {
|
|
|
168
134
|
this.port = port;
|
|
169
135
|
this.storedPassword = await this.matterbridge.nodeContext?.get('password', '');
|
|
170
136
|
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
137
|
const multer = await import('multer');
|
|
173
|
-
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
138
|
+
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
174
139
|
const upload = multer.default({ dest: uploadDir });
|
|
175
|
-
// Create the express app that serves the frontend
|
|
176
140
|
const express = await import('express');
|
|
177
141
|
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
142
|
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
143
|
this.log.debug(`Creating WebSocketServer...`);
|
|
207
144
|
const ws = await import('ws');
|
|
208
145
|
this.webSocketServer = new ws.WebSocketServer({ noServer: true });
|
|
209
146
|
this.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
|
|
210
147
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
211
148
|
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 */;
|
|
149
|
+
let callbackLogLevel = "notice";
|
|
150
|
+
if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
|
|
151
|
+
callbackLogLevel = "info";
|
|
152
|
+
if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
153
|
+
callbackLogLevel = "debug";
|
|
220
154
|
AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
|
|
221
155
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
222
156
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -232,33 +166,22 @@ export class Frontend extends EventEmitter {
|
|
|
232
166
|
});
|
|
233
167
|
ws.on('close', () => {
|
|
234
168
|
this.log.info('WebSocket client disconnected');
|
|
235
|
-
// istanbul ignore else
|
|
236
169
|
if (this.webSocketServer?.clients.size === 0) {
|
|
237
170
|
AnsiLogger.setGlobalCallback(undefined);
|
|
238
171
|
this.log.debug('All WebSocket clients disconnected. WebSocketServer logger global callback removed');
|
|
239
172
|
}
|
|
240
173
|
});
|
|
241
|
-
// istanbul ignore next
|
|
242
174
|
ws.on('error', (error) => {
|
|
243
|
-
// istanbul ignore next
|
|
244
175
|
this.log.error(`WebSocket client error: ${error}`);
|
|
245
176
|
});
|
|
246
177
|
});
|
|
247
178
|
this.webSocketServer.on('close', () => {
|
|
248
179
|
this.log.debug(`WebSocketServer closed`);
|
|
249
180
|
});
|
|
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
181
|
this.webSocketServer.on('error', (ws, error) => {
|
|
258
182
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
259
183
|
});
|
|
260
184
|
if (!hasParameter('ssl')) {
|
|
261
|
-
// Create an HTTP server and attach the express app
|
|
262
185
|
const http = await import('node:http');
|
|
263
186
|
try {
|
|
264
187
|
this.log.debug(`Creating HTTP server...`);
|
|
@@ -269,9 +192,7 @@ export class Frontend extends EventEmitter {
|
|
|
269
192
|
this.emit('server_error', error);
|
|
270
193
|
return;
|
|
271
194
|
}
|
|
272
|
-
// Listen on the specified port
|
|
273
195
|
if (hasParameter('ingress')) {
|
|
274
|
-
// We limit to all ipv4 addresses when running in ingress mode (Home Assistant add-on)
|
|
275
196
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
276
197
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
277
198
|
this.listening = true;
|
|
@@ -279,17 +200,13 @@ export class Frontend extends EventEmitter {
|
|
|
279
200
|
});
|
|
280
201
|
}
|
|
281
202
|
else {
|
|
282
|
-
// We listen to all available addresses
|
|
283
203
|
this.httpServer.listen(this.port, getParameter('bind'), () => {
|
|
284
204
|
const addr = this.httpServer?.address();
|
|
285
|
-
// istanbul ignore else
|
|
286
205
|
if (addr && typeof addr !== 'string') {
|
|
287
206
|
this.log.info(`The frontend http server is bound to ${addr.family} ${addr.address}:${addr.port}`);
|
|
288
207
|
}
|
|
289
|
-
// istanbul ignore else
|
|
290
208
|
if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
|
|
291
209
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
|
|
292
|
-
// istanbul ignore else
|
|
293
210
|
if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
|
|
294
211
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
295
212
|
this.listening = true;
|
|
@@ -298,30 +215,24 @@ export class Frontend extends EventEmitter {
|
|
|
298
215
|
}
|
|
299
216
|
this.httpServer.on('upgrade', async (req, socket, head) => {
|
|
300
217
|
try {
|
|
301
|
-
// Only proceed for real WebSocket upgrades
|
|
302
|
-
// istanbul ignore next cause is only a safety check
|
|
303
218
|
if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
|
|
304
219
|
this.log.error(`WebSocket upgrade error: Invalid upgrade header ${req.headers.upgrade}`);
|
|
305
220
|
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
306
221
|
return socket.destroy();
|
|
307
222
|
}
|
|
308
|
-
// Build a URL so we can read ?password=...
|
|
309
223
|
const url = new URL(req.url ?? '/', `http://${req.headers.host || 'localhost'}`);
|
|
310
|
-
// Validate WebSocket password
|
|
311
224
|
const password = url.searchParams.get('password') ?? '';
|
|
312
225
|
if (password !== this.storedPassword) {
|
|
313
226
|
this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
|
|
314
227
|
socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
|
|
315
228
|
return socket.destroy();
|
|
316
229
|
}
|
|
317
|
-
// Complete the WebSocket handshake
|
|
318
230
|
this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
|
|
319
231
|
this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
|
|
320
232
|
this.webSocketServer?.emit('connection', ws, req);
|
|
321
233
|
});
|
|
322
234
|
}
|
|
323
235
|
catch (err) {
|
|
324
|
-
/* istanbul ignore next: only triggered on unexpected internal error */
|
|
325
236
|
{
|
|
326
237
|
inspectError(this.log, 'WebSocket upgrade error:', err);
|
|
327
238
|
socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
|
|
@@ -344,7 +255,6 @@ export class Frontend extends EventEmitter {
|
|
|
344
255
|
});
|
|
345
256
|
}
|
|
346
257
|
else {
|
|
347
|
-
// SSL is enabled, load the certificate and the private key
|
|
348
258
|
let cert;
|
|
349
259
|
let key;
|
|
350
260
|
let ca;
|
|
@@ -354,7 +264,6 @@ export class Frontend extends EventEmitter {
|
|
|
354
264
|
let httpsServerOptions = {};
|
|
355
265
|
const fs = await import('node:fs');
|
|
356
266
|
if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'))) {
|
|
357
|
-
// Load the p12 certificate and the passphrase
|
|
358
267
|
try {
|
|
359
268
|
pfx = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'));
|
|
360
269
|
this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12')}`);
|
|
@@ -366,7 +275,7 @@ export class Frontend extends EventEmitter {
|
|
|
366
275
|
}
|
|
367
276
|
try {
|
|
368
277
|
passphrase = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass'), 'utf8');
|
|
369
|
-
passphrase = passphrase.trim();
|
|
278
|
+
passphrase = passphrase.trim();
|
|
370
279
|
this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass')}`);
|
|
371
280
|
}
|
|
372
281
|
catch (error) {
|
|
@@ -377,7 +286,6 @@ export class Frontend extends EventEmitter {
|
|
|
377
286
|
httpsServerOptions = { pfx, passphrase };
|
|
378
287
|
}
|
|
379
288
|
else {
|
|
380
|
-
// 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.
|
|
381
289
|
try {
|
|
382
290
|
cert = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem'), 'utf8');
|
|
383
291
|
this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem')}`);
|
|
@@ -407,10 +315,9 @@ export class Frontend extends EventEmitter {
|
|
|
407
315
|
httpsServerOptions = { cert: fullChain ?? cert, key, ca };
|
|
408
316
|
}
|
|
409
317
|
if (hasParameter('mtls')) {
|
|
410
|
-
httpsServerOptions.requestCert = true;
|
|
411
|
-
httpsServerOptions.rejectUnauthorized = true;
|
|
318
|
+
httpsServerOptions.requestCert = true;
|
|
319
|
+
httpsServerOptions.rejectUnauthorized = true;
|
|
412
320
|
}
|
|
413
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
414
321
|
const https = await import('node:https');
|
|
415
322
|
try {
|
|
416
323
|
this.log.debug(`Creating HTTPS server...`);
|
|
@@ -421,9 +328,7 @@ export class Frontend extends EventEmitter {
|
|
|
421
328
|
this.emit('server_error', error);
|
|
422
329
|
return;
|
|
423
330
|
}
|
|
424
|
-
// Listen on the specified port
|
|
425
331
|
if (hasParameter('ingress')) {
|
|
426
|
-
// We limit to all ipv4 addresses when running in ingress mode (Home Assistant add-on)
|
|
427
332
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
428
333
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
429
334
|
this.listening = true;
|
|
@@ -431,17 +336,13 @@ export class Frontend extends EventEmitter {
|
|
|
431
336
|
});
|
|
432
337
|
}
|
|
433
338
|
else {
|
|
434
|
-
// We listen to all available addresses
|
|
435
339
|
this.httpsServer.listen(this.port, getParameter('bind'), () => {
|
|
436
340
|
const addr = this.httpsServer?.address();
|
|
437
|
-
// istanbul ignore else
|
|
438
341
|
if (addr && typeof addr !== 'string') {
|
|
439
342
|
this.log.info(`The frontend https server is bound to ${addr.family} ${addr.address}:${addr.port}`);
|
|
440
343
|
}
|
|
441
|
-
// istanbul ignore else
|
|
442
344
|
if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
|
|
443
345
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
|
|
444
|
-
// istanbul ignore else
|
|
445
346
|
if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
|
|
446
347
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
447
348
|
this.listening = true;
|
|
@@ -450,29 +351,23 @@ export class Frontend extends EventEmitter {
|
|
|
450
351
|
}
|
|
451
352
|
this.httpsServer.on('upgrade', async (req, socket, head) => {
|
|
452
353
|
try {
|
|
453
|
-
// Only proceed for real WebSocket upgrades
|
|
454
|
-
// istanbul ignore next cause is only a safety check
|
|
455
354
|
if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
|
|
456
355
|
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
457
356
|
return socket.destroy();
|
|
458
357
|
}
|
|
459
|
-
// Build a URL so we can read ?password=...
|
|
460
358
|
const url = new URL(req.url ?? '/', `https://${req.headers.host || 'localhost'}`);
|
|
461
|
-
// Validate WebSocket password
|
|
462
359
|
const password = url.searchParams.get('password') ?? '';
|
|
463
360
|
if (password !== this.storedPassword) {
|
|
464
361
|
this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
|
|
465
362
|
socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
|
|
466
363
|
return socket.destroy();
|
|
467
364
|
}
|
|
468
|
-
// Complete the WebSocket handshake
|
|
469
365
|
this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
|
|
470
366
|
this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
|
|
471
367
|
this.webSocketServer?.emit('connection', ws, req);
|
|
472
368
|
});
|
|
473
369
|
}
|
|
474
370
|
catch (err) {
|
|
475
|
-
/* istanbul ignore next: only triggered on unexpected internal error */
|
|
476
371
|
{
|
|
477
372
|
inspectError(this.log, 'WebSocket upgrade error:', err);
|
|
478
373
|
socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
|
|
@@ -494,7 +389,6 @@ export class Frontend extends EventEmitter {
|
|
|
494
389
|
return;
|
|
495
390
|
});
|
|
496
391
|
}
|
|
497
|
-
// Subscribe to cli events
|
|
498
392
|
cliEmitter.removeAllListeners();
|
|
499
393
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
500
394
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -505,8 +399,6 @@ export class Frontend extends EventEmitter {
|
|
|
505
399
|
cliEmitter.on('cpu', (cpuUsage, processCpuUsage) => {
|
|
506
400
|
this.wssSendCpuUpdate(cpuUsage, processCpuUsage);
|
|
507
401
|
});
|
|
508
|
-
// Endpoint to validate login code
|
|
509
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
510
402
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
511
403
|
const { password } = req.body;
|
|
512
404
|
this.log.debug(`The frontend sent /api/login with password ${password ? '[redacted]' : '(empty)'}`);
|
|
@@ -519,20 +411,17 @@ export class Frontend extends EventEmitter {
|
|
|
519
411
|
res.json({ valid: false });
|
|
520
412
|
}
|
|
521
413
|
});
|
|
522
|
-
// Endpoint to provide health check for docker
|
|
523
414
|
this.expressApp.get('/health', (req, res) => {
|
|
524
415
|
this.log.debug('Express received /health');
|
|
525
416
|
const healthStatus = {
|
|
526
|
-
status: 'ok',
|
|
527
|
-
uptime: process.uptime(),
|
|
528
|
-
timestamp: new Date().toISOString(),
|
|
417
|
+
status: 'ok',
|
|
418
|
+
uptime: process.uptime(),
|
|
419
|
+
timestamp: new Date().toISOString(),
|
|
529
420
|
};
|
|
530
421
|
res.status(200).json(healthStatus);
|
|
531
422
|
});
|
|
532
|
-
// Endpoint to provide memory usage details
|
|
533
423
|
this.expressApp.get('/memory', async (req, res) => {
|
|
534
424
|
this.log.debug('Express received /memory');
|
|
535
|
-
// Memory usage from process
|
|
536
425
|
const memoryUsageRaw = process.memoryUsage();
|
|
537
426
|
const memoryUsage = {
|
|
538
427
|
rss: formatBytes(memoryUsageRaw.rss),
|
|
@@ -541,13 +430,10 @@ export class Frontend extends EventEmitter {
|
|
|
541
430
|
external: formatBytes(memoryUsageRaw.external),
|
|
542
431
|
arrayBuffers: formatBytes(memoryUsageRaw.arrayBuffers),
|
|
543
432
|
};
|
|
544
|
-
// V8 heap statistics
|
|
545
433
|
const { default: v8 } = await import('node:v8');
|
|
546
434
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
547
435
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
548
|
-
// Format heapStats
|
|
549
436
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, formatBytes(value)]));
|
|
550
|
-
// Format heapSpaces
|
|
551
437
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
552
438
|
...space,
|
|
553
439
|
space_size: formatBytes(space.space_size),
|
|
@@ -566,22 +452,18 @@ export class Frontend extends EventEmitter {
|
|
|
566
452
|
};
|
|
567
453
|
res.status(200).json(memoryReport);
|
|
568
454
|
});
|
|
569
|
-
// Endpoint to provide settings
|
|
570
455
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
571
456
|
this.log.debug('The frontend sent /api/settings');
|
|
572
457
|
res.json(await this.getApiSettings());
|
|
573
458
|
});
|
|
574
|
-
// Endpoint to provide plugins
|
|
575
459
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
576
460
|
this.log.debug('The frontend sent /api/plugins');
|
|
577
461
|
res.json(this.matterbridge.hasCleanupStarted ? [] : this.getPlugins());
|
|
578
462
|
});
|
|
579
|
-
// Endpoint to provide devices
|
|
580
463
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
581
464
|
this.log.debug('The frontend sent /api/devices');
|
|
582
465
|
res.json(this.matterbridge.hasCleanupStarted ? [] : this.getDevices());
|
|
583
466
|
});
|
|
584
|
-
// Endpoint to view the matterbridge log
|
|
585
467
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
586
468
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
587
469
|
try {
|
|
@@ -595,7 +477,6 @@ export class Frontend extends EventEmitter {
|
|
|
595
477
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
596
478
|
}
|
|
597
479
|
});
|
|
598
|
-
// Endpoint to view the matter.js log
|
|
599
480
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
600
481
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
601
482
|
try {
|
|
@@ -609,7 +490,6 @@ export class Frontend extends EventEmitter {
|
|
|
609
490
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
610
491
|
}
|
|
611
492
|
});
|
|
612
|
-
// Endpoint to view the diagnostic.log
|
|
613
493
|
this.expressApp.get('/api/view-diagnostic', async (req, res) => {
|
|
614
494
|
this.log.debug('The frontend sent /api/view-diagnostic');
|
|
615
495
|
await this.generateDiagnostic();
|
|
@@ -620,13 +500,10 @@ export class Frontend extends EventEmitter {
|
|
|
620
500
|
res.send(data.slice(29));
|
|
621
501
|
}
|
|
622
502
|
catch (error) {
|
|
623
|
-
// istanbul ignore next
|
|
624
503
|
this.log.error(`Error reading diagnostic log file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
625
|
-
// istanbul ignore next
|
|
626
504
|
res.status(500).send('Error reading diagnostic log file.');
|
|
627
505
|
}
|
|
628
506
|
});
|
|
629
|
-
// Endpoint to download the diagnostic.log
|
|
630
507
|
this.expressApp.get('/api/download-diagnostic', async (req, res) => {
|
|
631
508
|
this.log.debug(`The frontend sent /api/download-diagnostic`);
|
|
632
509
|
await this.generateDiagnostic();
|
|
@@ -637,19 +514,16 @@ export class Frontend extends EventEmitter {
|
|
|
637
514
|
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), data, 'utf-8');
|
|
638
515
|
}
|
|
639
516
|
catch (error) {
|
|
640
|
-
// istanbul ignore next
|
|
641
517
|
this.log.debug(`Error in /api/download-diagnostic: ${error instanceof Error ? error.message : error}`);
|
|
642
518
|
}
|
|
643
519
|
res.type('text/plain');
|
|
644
520
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), MATTERBRIDGE_DIAGNOSTIC_FILE, (error) => {
|
|
645
|
-
/* istanbul ignore if */
|
|
646
521
|
if (error) {
|
|
647
522
|
this.log.error(`Error downloading file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
648
523
|
res.status(500).send('Error downloading the diagnostic log file');
|
|
649
524
|
}
|
|
650
525
|
});
|
|
651
526
|
});
|
|
652
|
-
// Endpoint to view the history.html
|
|
653
527
|
this.expressApp.get('/api/viewhistory', async (req, res) => {
|
|
654
528
|
this.log.debug('The frontend sent /api/viewhistory');
|
|
655
529
|
try {
|
|
@@ -663,7 +537,6 @@ export class Frontend extends EventEmitter {
|
|
|
663
537
|
res.status(500).send('Error reading history file.');
|
|
664
538
|
}
|
|
665
539
|
});
|
|
666
|
-
// Endpoint to download the history.html
|
|
667
540
|
this.expressApp.get('/api/downloadhistory', async (req, res) => {
|
|
668
541
|
this.log.debug(`The frontend sent /api/downloadhistory`);
|
|
669
542
|
try {
|
|
@@ -673,7 +546,6 @@ export class Frontend extends EventEmitter {
|
|
|
673
546
|
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), data, 'utf-8');
|
|
674
547
|
res.type('text/plain');
|
|
675
548
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), MATTERBRIDGE_HISTORY_FILE, (error) => {
|
|
676
|
-
/* istanbul ignore if */
|
|
677
549
|
if (error) {
|
|
678
550
|
this.log.error(`Error in /api/downloadhistory downloading history file ${MATTERBRIDGE_HISTORY_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
679
551
|
res.status(500).send('Error downloading history file');
|
|
@@ -685,7 +557,6 @@ export class Frontend extends EventEmitter {
|
|
|
685
557
|
res.status(500).send('Error reading history file.');
|
|
686
558
|
}
|
|
687
559
|
});
|
|
688
|
-
// Endpoint to view the shelly log
|
|
689
560
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
690
561
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
691
562
|
try {
|
|
@@ -699,7 +570,6 @@ export class Frontend extends EventEmitter {
|
|
|
699
570
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
700
571
|
}
|
|
701
572
|
});
|
|
702
|
-
// Endpoint to download the matterbridge log
|
|
703
573
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
704
574
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
|
|
705
575
|
const fs = await import('node:fs');
|
|
@@ -714,14 +584,12 @@ export class Frontend extends EventEmitter {
|
|
|
714
584
|
}
|
|
715
585
|
res.type('text/plain');
|
|
716
586
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_LOGGER_FILE), 'matterbridge.log', (error) => {
|
|
717
|
-
/* istanbul ignore if */
|
|
718
587
|
if (error) {
|
|
719
588
|
this.log.error(`Error downloading log file ${MATTERBRIDGE_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
720
589
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
721
590
|
}
|
|
722
591
|
});
|
|
723
592
|
});
|
|
724
|
-
// Endpoint to download the matter log
|
|
725
593
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
726
594
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
|
|
727
595
|
const fs = await import('node:fs');
|
|
@@ -736,14 +604,12 @@ export class Frontend extends EventEmitter {
|
|
|
736
604
|
}
|
|
737
605
|
res.type('text/plain');
|
|
738
606
|
res.download(path.join(os.tmpdir(), MATTER_LOGGER_FILE), 'matter.log', (error) => {
|
|
739
|
-
/* istanbul ignore if */
|
|
740
607
|
if (error) {
|
|
741
608
|
this.log.error(`Error downloading log file ${MATTER_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
742
609
|
res.status(500).send('Error downloading the matter log file');
|
|
743
610
|
}
|
|
744
611
|
});
|
|
745
612
|
});
|
|
746
|
-
// Endpoint to download the shelly log
|
|
747
613
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
748
614
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
749
615
|
const fs = await import('node:fs');
|
|
@@ -758,93 +624,77 @@ export class Frontend extends EventEmitter {
|
|
|
758
624
|
}
|
|
759
625
|
res.type('text/plain');
|
|
760
626
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
761
|
-
/* istanbul ignore if */
|
|
762
627
|
if (error) {
|
|
763
628
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
764
629
|
res.status(500).send('Error downloading Shelly system log file');
|
|
765
630
|
}
|
|
766
631
|
});
|
|
767
632
|
});
|
|
768
|
-
// Endpoint to download the matterbridge storage directory
|
|
769
633
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
770
634
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
771
635
|
await createZip(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), path.join(this.matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR));
|
|
772
636
|
res.download(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), `matterbridge.${NODE_STORAGE_DIR}.zip`, (error) => {
|
|
773
|
-
/* istanbul ignore if */
|
|
774
637
|
if (error) {
|
|
775
638
|
this.log.error(`Error downloading file ${`matterbridge.${NODE_STORAGE_DIR}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
776
639
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
777
640
|
}
|
|
778
641
|
});
|
|
779
642
|
});
|
|
780
|
-
// Endpoint to download the matter storage file
|
|
781
643
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
782
644
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
783
645
|
await createZip(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME));
|
|
784
646
|
res.download(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), `matterbridge.${MATTER_STORAGE_NAME}.zip`, (error) => {
|
|
785
|
-
/* istanbul ignore if */
|
|
786
647
|
if (error) {
|
|
787
648
|
this.log.error(`Error downloading the matter storage matterbridge.${MATTER_STORAGE_NAME}.zip: ${error instanceof Error ? error.message : error}`);
|
|
788
649
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
789
650
|
}
|
|
790
651
|
});
|
|
791
652
|
});
|
|
792
|
-
// Endpoint to download the matterbridge plugin directory
|
|
793
653
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
794
654
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
795
655
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
796
656
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
797
|
-
/* istanbul ignore if */
|
|
798
657
|
if (error) {
|
|
799
658
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
800
659
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
801
660
|
}
|
|
802
661
|
});
|
|
803
662
|
});
|
|
804
|
-
// Endpoint to download the matterbridge plugin config files
|
|
805
663
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
806
664
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
807
665
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
808
666
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
809
|
-
/* istanbul ignore if */
|
|
810
667
|
if (error) {
|
|
811
668
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
812
669
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
813
670
|
}
|
|
814
671
|
});
|
|
815
672
|
});
|
|
816
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
817
673
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
818
674
|
this.log.debug('The frontend sent /api/download-backup');
|
|
819
675
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
820
|
-
/* istanbul ignore if */
|
|
821
676
|
if (error) {
|
|
822
677
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
823
678
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
824
679
|
}
|
|
825
680
|
});
|
|
826
681
|
});
|
|
827
|
-
// Endpoint to upload a package
|
|
828
682
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
829
683
|
const { filename } = req.body;
|
|
830
684
|
const file = req.file;
|
|
831
|
-
/* istanbul ignore if */
|
|
832
685
|
if (!file || !filename) {
|
|
833
686
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
834
687
|
res.status(400).send('Invalid request: file and filename are required');
|
|
835
688
|
return;
|
|
836
689
|
}
|
|
837
690
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
838
|
-
// Define the path where the plugin file will be saved
|
|
839
691
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
840
692
|
try {
|
|
841
|
-
// Move the uploaded file to the specified path
|
|
842
693
|
const fs = await import('node:fs');
|
|
843
694
|
await fs.promises.rename(file.path, filePath);
|
|
844
695
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
845
|
-
// Install the plugin package
|
|
846
696
|
if (filename.endsWith('.tgz')) {
|
|
847
|
-
const { spawnCommand } = await import('./
|
|
697
|
+
const { spawnCommand } = await import('./spawn.js');
|
|
848
698
|
if (await spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], 'install', filename)) {
|
|
849
699
|
this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
|
|
850
700
|
this.wssSendCloseSnackbarMessage(`Installing package ${filename}. Please wait...`);
|
|
@@ -870,7 +720,6 @@ export class Frontend extends EventEmitter {
|
|
|
870
720
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
871
721
|
}
|
|
872
722
|
});
|
|
873
|
-
// Fallback for routing (must be the last route)
|
|
874
723
|
this.expressApp.use((req, res) => {
|
|
875
724
|
const filePath = path.resolve(this.matterbridge.rootDirectory, 'frontend', 'build');
|
|
876
725
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html in ${filePath} as fallback`);
|
|
@@ -881,16 +730,13 @@ export class Frontend extends EventEmitter {
|
|
|
881
730
|
async stop() {
|
|
882
731
|
this.log.debug('Stopping the frontend...');
|
|
883
732
|
const ws = await import('ws');
|
|
884
|
-
// Remove listeners from the express app
|
|
885
733
|
if (this.expressApp) {
|
|
886
734
|
this.expressApp.removeAllListeners();
|
|
887
735
|
this.expressApp = undefined;
|
|
888
736
|
this.log.debug('Frontend app closed successfully');
|
|
889
737
|
}
|
|
890
|
-
// Close the WebSocket server
|
|
891
738
|
if (this.webSocketServer) {
|
|
892
739
|
this.log.debug('Closing WebSocket server...');
|
|
893
|
-
// Close all active connections
|
|
894
740
|
this.webSocketServer.clients.forEach((client) => {
|
|
895
741
|
if (client.readyState === ws.WebSocket.OPEN) {
|
|
896
742
|
client.close();
|
|
@@ -898,9 +744,7 @@ export class Frontend extends EventEmitter {
|
|
|
898
744
|
});
|
|
899
745
|
await withTimeout(new Promise((resolve) => {
|
|
900
746
|
this.webSocketServer?.close((error) => {
|
|
901
|
-
// istanbul ignore if
|
|
902
747
|
if (error) {
|
|
903
|
-
// istanbul ignore next
|
|
904
748
|
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
905
749
|
}
|
|
906
750
|
else {
|
|
@@ -913,27 +757,8 @@ export class Frontend extends EventEmitter {
|
|
|
913
757
|
this.webSocketServer.removeAllListeners();
|
|
914
758
|
this.webSocketServer = undefined;
|
|
915
759
|
}
|
|
916
|
-
// Close the http server
|
|
917
760
|
if (this.httpServer) {
|
|
918
761
|
this.log.debug('Closing http server...');
|
|
919
|
-
/*
|
|
920
|
-
await withTimeout(
|
|
921
|
-
new Promise<void>((resolve) => {
|
|
922
|
-
this.httpServer?.close((error) => {
|
|
923
|
-
if (error) {
|
|
924
|
-
// istanbul ignore next
|
|
925
|
-
this.log.error(`Error closing http server: ${error}`);
|
|
926
|
-
} else {
|
|
927
|
-
this.log.debug('Http server closed successfully');
|
|
928
|
-
this.emit('server_stopped');
|
|
929
|
-
}
|
|
930
|
-
resolve();
|
|
931
|
-
});
|
|
932
|
-
}),
|
|
933
|
-
5000,
|
|
934
|
-
false,
|
|
935
|
-
);
|
|
936
|
-
*/
|
|
937
762
|
this.httpServer.close();
|
|
938
763
|
this.log.debug('Http server closed successfully');
|
|
939
764
|
this.listening = false;
|
|
@@ -942,27 +767,8 @@ export class Frontend extends EventEmitter {
|
|
|
942
767
|
this.httpServer = undefined;
|
|
943
768
|
this.log.debug('Frontend http server closed successfully');
|
|
944
769
|
}
|
|
945
|
-
// Close the https server
|
|
946
770
|
if (this.httpsServer) {
|
|
947
771
|
this.log.debug('Closing https server...');
|
|
948
|
-
/*
|
|
949
|
-
await withTimeout(
|
|
950
|
-
new Promise<void>((resolve) => {
|
|
951
|
-
this.httpsServer?.close((error) => {
|
|
952
|
-
if (error) {
|
|
953
|
-
// istanbul ignore next
|
|
954
|
-
this.log.error(`Error closing https server: ${error}`);
|
|
955
|
-
} else {
|
|
956
|
-
this.log.debug('Https server closed successfully');
|
|
957
|
-
this.emit('server_stopped');
|
|
958
|
-
}
|
|
959
|
-
resolve();
|
|
960
|
-
});
|
|
961
|
-
}),
|
|
962
|
-
5000,
|
|
963
|
-
false,
|
|
964
|
-
);
|
|
965
|
-
*/
|
|
966
772
|
this.httpsServer.close();
|
|
967
773
|
this.log.debug('Https server closed successfully');
|
|
968
774
|
this.listening = false;
|
|
@@ -973,13 +779,7 @@ export class Frontend extends EventEmitter {
|
|
|
973
779
|
}
|
|
974
780
|
this.log.debug('Frontend stopped successfully');
|
|
975
781
|
}
|
|
976
|
-
/**
|
|
977
|
-
* Retrieves the api settings data.
|
|
978
|
-
*
|
|
979
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
980
|
-
*/
|
|
981
782
|
async getApiSettings() {
|
|
982
|
-
// Update the variable system information properties
|
|
983
783
|
this.matterbridge.systemInformation.totalMemory = formatBytes(os.totalmem());
|
|
984
784
|
this.matterbridge.systemInformation.freeMemory = formatBytes(os.freemem());
|
|
985
785
|
this.matterbridge.systemInformation.systemUptime = formatUptime(os.uptime());
|
|
@@ -989,7 +789,6 @@ export class Frontend extends EventEmitter {
|
|
|
989
789
|
this.matterbridge.systemInformation.rss = formatBytes(process.memoryUsage().rss);
|
|
990
790
|
this.matterbridge.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
|
|
991
791
|
this.matterbridge.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
|
|
992
|
-
// Create the matterbridge information
|
|
993
792
|
const info = {
|
|
994
793
|
homeDirectory: this.matterbridge.homeDirectory,
|
|
995
794
|
rootDirectory: this.matterbridge.rootDirectory,
|
|
@@ -1025,15 +824,9 @@ export class Frontend extends EventEmitter {
|
|
|
1025
824
|
};
|
|
1026
825
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: info };
|
|
1027
826
|
}
|
|
1028
|
-
/**
|
|
1029
|
-
* Retrieves the reachable attribute.
|
|
1030
|
-
*
|
|
1031
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
1032
|
-
* @returns {boolean} The reachable attribute.
|
|
1033
|
-
*/
|
|
1034
827
|
getReachability(device) {
|
|
1035
828
|
if (this.matterbridge.hasCleanupStarted)
|
|
1036
|
-
return false;
|
|
829
|
+
return false;
|
|
1037
830
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1038
831
|
return false;
|
|
1039
832
|
if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
|
|
@@ -1044,15 +837,9 @@ export class Frontend extends EventEmitter {
|
|
|
1044
837
|
return true;
|
|
1045
838
|
return false;
|
|
1046
839
|
}
|
|
1047
|
-
/**
|
|
1048
|
-
* Retrieves the power source attribute.
|
|
1049
|
-
*
|
|
1050
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
1051
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
1052
|
-
*/
|
|
1053
840
|
getPowerSource(endpoint) {
|
|
1054
841
|
if (this.matterbridge.hasCleanupStarted)
|
|
1055
|
-
return undefined;
|
|
842
|
+
return undefined;
|
|
1056
843
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
1057
844
|
return undefined;
|
|
1058
845
|
const powerSource = (device) => {
|
|
@@ -1067,25 +854,16 @@ export class Frontend extends EventEmitter {
|
|
|
1067
854
|
}
|
|
1068
855
|
return;
|
|
1069
856
|
};
|
|
1070
|
-
// Root endpoint
|
|
1071
857
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
1072
858
|
return powerSource(endpoint);
|
|
1073
|
-
// Child endpoints
|
|
1074
859
|
for (const child of endpoint.getChildEndpoints()) {
|
|
1075
|
-
// istanbul ignore else
|
|
1076
860
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
1077
861
|
return powerSource(child);
|
|
1078
862
|
}
|
|
1079
863
|
}
|
|
1080
|
-
/**
|
|
1081
|
-
* Retrieves the battery level attribute.
|
|
1082
|
-
*
|
|
1083
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
1084
|
-
* @returns {number | undefined} The battery level attribute.
|
|
1085
|
-
*/
|
|
1086
864
|
getBatteryLevel(endpoint) {
|
|
1087
865
|
if (this.matterbridge.hasCleanupStarted)
|
|
1088
|
-
return undefined;
|
|
866
|
+
return undefined;
|
|
1089
867
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
1090
868
|
return undefined;
|
|
1091
869
|
const batteryLevel = (device) => {
|
|
@@ -1096,27 +874,16 @@ export class Frontend extends EventEmitter {
|
|
|
1096
874
|
}
|
|
1097
875
|
return undefined;
|
|
1098
876
|
};
|
|
1099
|
-
// Root endpoint
|
|
1100
877
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
1101
878
|
return batteryLevel(endpoint);
|
|
1102
|
-
// Child endpoints
|
|
1103
879
|
for (const child of endpoint.getChildEndpoints()) {
|
|
1104
|
-
// istanbul ignore else
|
|
1105
880
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
1106
881
|
return batteryLevel(child);
|
|
1107
882
|
}
|
|
1108
883
|
}
|
|
1109
|
-
/**
|
|
1110
|
-
* Retrieves the cluster text description from a given device.
|
|
1111
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
1112
|
-
*
|
|
1113
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
1114
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
1115
|
-
*/
|
|
1116
884
|
getClusterTextFromDevice(device) {
|
|
1117
885
|
if (this.matterbridge.hasCleanupStarted)
|
|
1118
|
-
return '';
|
|
1119
|
-
// istanbul ignore else
|
|
886
|
+
return '';
|
|
1120
887
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1121
888
|
return '';
|
|
1122
889
|
const getUserLabel = (device) => {
|
|
@@ -1126,7 +893,6 @@ export class Frontend extends EventEmitter {
|
|
|
1126
893
|
if (composed)
|
|
1127
894
|
return 'Composed: ' + composed.value;
|
|
1128
895
|
}
|
|
1129
|
-
// istanbul ignore next cause is not reachable
|
|
1130
896
|
return '';
|
|
1131
897
|
};
|
|
1132
898
|
const getFixedLabel = (device) => {
|
|
@@ -1136,13 +902,11 @@ export class Frontend extends EventEmitter {
|
|
|
1136
902
|
if (composed)
|
|
1137
903
|
return 'Composed: ' + composed.value;
|
|
1138
904
|
}
|
|
1139
|
-
// istanbul ignore next cause is not reacheable
|
|
1140
905
|
return '';
|
|
1141
906
|
};
|
|
1142
907
|
let attributes = '';
|
|
1143
908
|
let supportedModes = [];
|
|
1144
909
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1145
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
1146
910
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1147
911
|
return;
|
|
1148
912
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1232,17 +996,11 @@ export class Frontend extends EventEmitter {
|
|
|
1232
996
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1233
997
|
attributes += `${getUserLabel(device)} `;
|
|
1234
998
|
});
|
|
1235
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1236
999
|
return attributes.trimStart().trimEnd();
|
|
1237
1000
|
}
|
|
1238
|
-
/**
|
|
1239
|
-
* Retrieves the registered plugins sanitized for res.json().
|
|
1240
|
-
*
|
|
1241
|
-
* @returns {ApiPlugin[]} An array of BaseRegisteredPlugin.
|
|
1242
|
-
*/
|
|
1243
1001
|
getPlugins() {
|
|
1244
1002
|
if (this.matterbridge.hasCleanupStarted)
|
|
1245
|
-
return [];
|
|
1003
|
+
return [];
|
|
1246
1004
|
const plugins = [];
|
|
1247
1005
|
for (const plugin of this.matterbridge.plugins.array()) {
|
|
1248
1006
|
plugins.push({
|
|
@@ -1270,27 +1028,18 @@ export class Frontend extends EventEmitter {
|
|
|
1270
1028
|
schemaJson: plugin.schemaJson,
|
|
1271
1029
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1272
1030
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1273
|
-
// Childbridge mode specific data
|
|
1274
1031
|
matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
|
|
1275
1032
|
});
|
|
1276
1033
|
}
|
|
1277
1034
|
return plugins;
|
|
1278
1035
|
}
|
|
1279
|
-
/**
|
|
1280
|
-
* Retrieves the devices from Matterbridge.
|
|
1281
|
-
*
|
|
1282
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1283
|
-
* @returns {ApiDevice[]} An array of ApiDevices for the frontend.
|
|
1284
|
-
*/
|
|
1285
1036
|
getDevices(pluginName) {
|
|
1286
1037
|
if (this.matterbridge.hasCleanupStarted)
|
|
1287
|
-
return [];
|
|
1038
|
+
return [];
|
|
1288
1039
|
const devices = [];
|
|
1289
1040
|
for (const device of this.matterbridge.devices.array()) {
|
|
1290
|
-
// Filter by pluginName if provided
|
|
1291
1041
|
if (pluginName && pluginName !== device.plugin)
|
|
1292
1042
|
continue;
|
|
1293
|
-
// Check if the device has the required properties
|
|
1294
1043
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1295
1044
|
continue;
|
|
1296
1045
|
devices.push({
|
|
@@ -1311,39 +1060,24 @@ export class Frontend extends EventEmitter {
|
|
|
1311
1060
|
}
|
|
1312
1061
|
return devices;
|
|
1313
1062
|
}
|
|
1314
|
-
/**
|
|
1315
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1316
|
-
*
|
|
1317
|
-
* Response for /api/clusters
|
|
1318
|
-
*
|
|
1319
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1320
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1321
|
-
* @returns {ApiClusters | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1322
|
-
*/
|
|
1323
1063
|
getClusters(pluginName, endpointNumber) {
|
|
1324
1064
|
if (this.matterbridge.hasCleanupStarted)
|
|
1325
|
-
return;
|
|
1065
|
+
return;
|
|
1326
1066
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1327
1067
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.maybeId || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1328
1068
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1329
1069
|
return;
|
|
1330
1070
|
}
|
|
1331
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1332
|
-
// Get the device types from the main endpoint
|
|
1333
1071
|
const deviceTypes = [];
|
|
1334
1072
|
const clusters = [];
|
|
1335
1073
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1336
1074
|
deviceTypes.push(d.deviceType);
|
|
1337
1075
|
});
|
|
1338
|
-
// Get the clusters from the main endpoint
|
|
1339
1076
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1340
1077
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1341
1078
|
return;
|
|
1342
1079
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1343
1080
|
return;
|
|
1344
|
-
// console.log(
|
|
1345
|
-
// `${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}`,
|
|
1346
|
-
// );
|
|
1347
1081
|
clusters.push({
|
|
1348
1082
|
endpoint: endpoint.number.toString(),
|
|
1349
1083
|
number: endpoint.number,
|
|
@@ -1357,19 +1091,12 @@ export class Frontend extends EventEmitter {
|
|
|
1357
1091
|
attributeLocalValue: attributeValue,
|
|
1358
1092
|
});
|
|
1359
1093
|
});
|
|
1360
|
-
// Get the child endpoints
|
|
1361
1094
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1362
|
-
// if (childEndpoints.length === 0) {
|
|
1363
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1364
|
-
// }
|
|
1365
1095
|
childEndpoints.forEach((childEndpoint) => {
|
|
1366
|
-
// istanbul ignore if cause is not reachable: should never happen but ...
|
|
1367
1096
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1368
1097
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1369
1098
|
return;
|
|
1370
1099
|
}
|
|
1371
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1372
|
-
// Get the device types of the child endpoint
|
|
1373
1100
|
const deviceTypes = [];
|
|
1374
1101
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1375
1102
|
deviceTypes.push(d.deviceType);
|
|
@@ -1379,9 +1106,6 @@ export class Frontend extends EventEmitter {
|
|
|
1379
1106
|
return;
|
|
1380
1107
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1381
1108
|
return;
|
|
1382
|
-
// console.log(
|
|
1383
|
-
// `${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}`,
|
|
1384
|
-
// );
|
|
1385
1109
|
clusters.push({
|
|
1386
1110
|
endpoint: childEndpoint.number.toString(),
|
|
1387
1111
|
number: childEndpoint.number,
|
|
@@ -1401,7 +1125,6 @@ export class Frontend extends EventEmitter {
|
|
|
1401
1125
|
async generateDiagnostic() {
|
|
1402
1126
|
this.log.debug('Generating diagnostic...');
|
|
1403
1127
|
const serverNodes = [];
|
|
1404
|
-
// istanbul ignore else
|
|
1405
1128
|
if (this.matterbridge.bridgeMode === 'bridge') {
|
|
1406
1129
|
if (this.matterbridge.serverNode)
|
|
1407
1130
|
serverNodes.push(this.matterbridge.serverNode);
|
|
@@ -1412,7 +1135,6 @@ export class Frontend extends EventEmitter {
|
|
|
1412
1135
|
serverNodes.push(plugin.serverNode);
|
|
1413
1136
|
}
|
|
1414
1137
|
}
|
|
1415
|
-
// istanbul ignore next
|
|
1416
1138
|
for (const device of this.matterbridge.devices.array()) {
|
|
1417
1139
|
if (device.serverNode)
|
|
1418
1140
|
serverNodes.push(device.serverNode);
|
|
@@ -1436,15 +1158,8 @@ export class Frontend extends EventEmitter {
|
|
|
1436
1158
|
values: [...serverNodes],
|
|
1437
1159
|
})));
|
|
1438
1160
|
delete Logger.destinations.diagnostic;
|
|
1439
|
-
await wait(500);
|
|
1161
|
+
await wait(500);
|
|
1440
1162
|
}
|
|
1441
|
-
/**
|
|
1442
|
-
* Handles incoming websocket api request messages from the Matterbridge frontend.
|
|
1443
|
-
*
|
|
1444
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1445
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1446
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1447
|
-
*/
|
|
1448
1163
|
async wsMessageHandler(client, message) {
|
|
1449
1164
|
let data;
|
|
1450
1165
|
const sendResponse = (data) => {
|
|
@@ -1462,13 +1177,12 @@ export class Frontend extends EventEmitter {
|
|
|
1462
1177
|
client.send(JSON.stringify(data));
|
|
1463
1178
|
}
|
|
1464
1179
|
else {
|
|
1465
|
-
// istanbul ignore next cause is only a safety check
|
|
1466
1180
|
this.log.error('Cannot send api response, client not connected');
|
|
1467
1181
|
}
|
|
1468
1182
|
};
|
|
1469
1183
|
try {
|
|
1470
1184
|
data = JSON.parse(message.toString());
|
|
1471
|
-
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method)
|
|
1185
|
+
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || data.dst !== 'Matterbridge') {
|
|
1472
1186
|
this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
|
|
1473
1187
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
|
|
1474
1188
|
return;
|
|
@@ -1525,22 +1239,7 @@ export class Frontend extends EventEmitter {
|
|
|
1525
1239
|
}
|
|
1526
1240
|
this.wssSendSnackbarMessage(`Adding plugin ${data.params.pluginNameOrPath}...`, 5);
|
|
1527
1241
|
this.log.debug(`Adding plugin ${data.params.pluginNameOrPath}...`);
|
|
1528
|
-
data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
|
|
1529
|
-
/*
|
|
1530
|
-
const plugin = (await this.server.fetch({ type: 'plugins_add', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginNameOrPath } }, 5000)).response.plugin;
|
|
1531
|
-
if (plugin) {
|
|
1532
|
-
this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
|
|
1533
|
-
await this.server.fetch({ type: 'plugins_load', src: this.server.name, dst: 'plugins', params: { plugin: plugin.name } }, 5000);
|
|
1534
|
-
this.wssSendRestartRequired();
|
|
1535
|
-
this.wssSendRefreshRequired('plugins');
|
|
1536
|
-
this.wssSendRefreshRequired('devices');
|
|
1537
|
-
this.wssSendSnackbarMessage(`Loaded plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
|
|
1538
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1539
|
-
} else {
|
|
1540
|
-
this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
|
|
1541
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
|
|
1542
|
-
}
|
|
1543
|
-
*/
|
|
1242
|
+
data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
|
|
1544
1243
|
const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
|
|
1545
1244
|
if (plugin) {
|
|
1546
1245
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1553,7 +1252,7 @@ export class Frontend extends EventEmitter {
|
|
|
1553
1252
|
this.wssSendSnackbarMessage(`Loaded plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
|
|
1554
1253
|
return;
|
|
1555
1254
|
})
|
|
1556
|
-
.catch(
|
|
1255
|
+
.catch((_error) => { });
|
|
1557
1256
|
}
|
|
1558
1257
|
else {
|
|
1559
1258
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
|
|
@@ -1567,10 +1266,6 @@ export class Frontend extends EventEmitter {
|
|
|
1567
1266
|
}
|
|
1568
1267
|
this.wssSendSnackbarMessage(`Removing plugin ${data.params.pluginName}...`, 5);
|
|
1569
1268
|
this.log.debug(`Removing plugin ${data.params.pluginName}...`);
|
|
1570
|
-
/*
|
|
1571
|
-
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);
|
|
1572
|
-
await this.server.fetch({ type: 'plugins_remove', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginName } }, 5000);
|
|
1573
|
-
*/
|
|
1574
1269
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1575
1270
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
1576
1271
|
await this.matterbridge.plugins.remove(data.params.pluginName);
|
|
@@ -1598,10 +1293,8 @@ export class Frontend extends EventEmitter {
|
|
|
1598
1293
|
this.wssSendSnackbarMessage(`Enabled plugin ${data.params.pluginName}`, 5, 'success');
|
|
1599
1294
|
setImmediate(async () => {
|
|
1600
1295
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true);
|
|
1601
|
-
// @ts-expect-error Accessing private method
|
|
1602
1296
|
if (plugin.serverNode)
|
|
1603
1297
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1604
|
-
// @ts-expect-error Accessing private method
|
|
1605
1298
|
for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode))
|
|
1606
1299
|
await this.matterbridge.startServerNode(device.serverNode);
|
|
1607
1300
|
this.wssSendSnackbarMessage(`Started plugin ${localData.params.pluginName}`, 5, 'success');
|
|
@@ -1617,11 +1310,9 @@ export class Frontend extends EventEmitter {
|
|
|
1617
1310
|
}
|
|
1618
1311
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1619
1312
|
for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode)) {
|
|
1620
|
-
// @ts-expect-error Accessing private method
|
|
1621
1313
|
await this.matterbridge.stopServerNode(device.serverNode);
|
|
1622
1314
|
device.serverNode = undefined;
|
|
1623
1315
|
}
|
|
1624
|
-
// @ts-expect-error Accessing private method
|
|
1625
1316
|
if (plugin.serverNode)
|
|
1626
1317
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1627
1318
|
plugin.serverNode = undefined;
|
|
@@ -1640,37 +1331,30 @@ export class Frontend extends EventEmitter {
|
|
|
1640
1331
|
this.wssSendSnackbarMessage(`Restarting plugin ${data.params.pluginName}`, 5, 'info');
|
|
1641
1332
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1642
1333
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
|
|
1643
|
-
// Stop server nodes
|
|
1644
1334
|
if (plugin.serverNode) {
|
|
1645
|
-
// @ts-expect-error Accessing private method
|
|
1646
1335
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1647
1336
|
plugin.serverNode = undefined;
|
|
1648
1337
|
}
|
|
1649
1338
|
for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name)) {
|
|
1650
|
-
// @ts-expect-error Accessing private method
|
|
1651
1339
|
if (device.serverNode)
|
|
1652
1340
|
await this.matterbridge.stopServerNode(device.serverNode);
|
|
1653
1341
|
device.serverNode = undefined;
|
|
1654
1342
|
this.log.debug(`Removing device ${device.deviceName} from plugin ${plugin.name}`);
|
|
1655
1343
|
this.matterbridge.devices.remove(device);
|
|
1656
1344
|
}
|
|
1657
|
-
// @ts-expect-error Accessing private method
|
|
1658
1345
|
if (plugin.type === 'DynamicPlatform' && !plugin.locked)
|
|
1659
1346
|
await this.matterbridge.createDynamicPlugin(plugin);
|
|
1660
1347
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
|
|
1661
|
-
plugin.restartRequired = false;
|
|
1348
|
+
plugin.restartRequired = false;
|
|
1662
1349
|
let needRestart = 0;
|
|
1663
1350
|
for (const plugin of this.matterbridge.plugins) {
|
|
1664
1351
|
if (plugin.restartRequired)
|
|
1665
1352
|
needRestart++;
|
|
1666
1353
|
}
|
|
1667
1354
|
if (needRestart === 0)
|
|
1668
|
-
this.wssSendRestartNotRequired(true);
|
|
1669
|
-
// Start server nodes
|
|
1670
|
-
// @ts-expect-error Accessing private method
|
|
1355
|
+
this.wssSendRestartNotRequired(true);
|
|
1671
1356
|
if (plugin.serverNode)
|
|
1672
1357
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1673
|
-
// @ts-expect-error Accessing private method
|
|
1674
1358
|
for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode))
|
|
1675
1359
|
await this.matterbridge.startServerNode(device.serverNode);
|
|
1676
1360
|
this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
|
|
@@ -1815,9 +1499,6 @@ export class Frontend extends EventEmitter {
|
|
|
1815
1499
|
this.wssSendRefreshRequired('matter', { matter: { ...matter, advertiseTime: 0, advertising: false } });
|
|
1816
1500
|
}
|
|
1817
1501
|
if (data.params.advertise) {
|
|
1818
|
-
// TODO: matter.js 0.16.0
|
|
1819
|
-
// await serverNode.env.get(DeviceAdvertiser)?.advertise(true);
|
|
1820
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1821
1502
|
const advertiser = serverNode.env.get(DeviceAdvertiser);
|
|
1822
1503
|
if (advertiser && advertiser.advertise && typeof advertiser.advertise === 'function')
|
|
1823
1504
|
await advertiser.advertise(true);
|
|
@@ -1881,7 +1562,6 @@ export class Frontend extends EventEmitter {
|
|
|
1881
1562
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/devices' });
|
|
1882
1563
|
return;
|
|
1883
1564
|
}
|
|
1884
|
-
// istanbul ignore next
|
|
1885
1565
|
const selectDeviceValues = !plugin.platform ? [] : plugin.platform.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1886
1566
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectDeviceValues });
|
|
1887
1567
|
}
|
|
@@ -1895,7 +1575,6 @@ export class Frontend extends EventEmitter {
|
|
|
1895
1575
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' });
|
|
1896
1576
|
return;
|
|
1897
1577
|
}
|
|
1898
|
-
// istanbul ignore next
|
|
1899
1578
|
const selectEntityValues = !plugin.platform ? [] : plugin.platform.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1900
1579
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectEntityValues });
|
|
1901
1580
|
}
|
|
@@ -1947,22 +1626,22 @@ export class Frontend extends EventEmitter {
|
|
|
1947
1626
|
if (isValidString(data.params.value, 4)) {
|
|
1948
1627
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1949
1628
|
if (data.params.value === 'Debug') {
|
|
1950
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1629
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1951
1630
|
}
|
|
1952
1631
|
else if (data.params.value === 'Info') {
|
|
1953
|
-
await this.matterbridge.setLogLevel("info"
|
|
1632
|
+
await this.matterbridge.setLogLevel("info");
|
|
1954
1633
|
}
|
|
1955
1634
|
else if (data.params.value === 'Notice') {
|
|
1956
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1635
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1957
1636
|
}
|
|
1958
1637
|
else if (data.params.value === 'Warn') {
|
|
1959
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1638
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1960
1639
|
}
|
|
1961
1640
|
else if (data.params.value === 'Error') {
|
|
1962
|
-
await this.matterbridge.setLogLevel("error"
|
|
1641
|
+
await this.matterbridge.setLogLevel("error");
|
|
1963
1642
|
}
|
|
1964
1643
|
else if (data.params.value === 'Fatal') {
|
|
1965
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1644
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1966
1645
|
}
|
|
1967
1646
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1968
1647
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1973,7 +1652,6 @@ export class Frontend extends EventEmitter {
|
|
|
1973
1652
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1974
1653
|
this.matterbridge.fileLogger = data.params.value;
|
|
1975
1654
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1976
|
-
// Create the file logger for matterbridge
|
|
1977
1655
|
if (data.params.value)
|
|
1978
1656
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), await this.matterbridge.getLogLevel(), true);
|
|
1979
1657
|
else
|
|
@@ -2003,12 +1681,11 @@ export class Frontend extends EventEmitter {
|
|
|
2003
1681
|
Logger.level = MatterLogLevel.FATAL;
|
|
2004
1682
|
}
|
|
2005
1683
|
this.matterbridge.matterLogLevel = MatterLogLevel.names[Logger.level];
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
1684
|
+
let callbackLogLevel = "notice";
|
|
1685
|
+
if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
|
|
1686
|
+
callbackLogLevel = "info";
|
|
1687
|
+
if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
1688
|
+
callbackLogLevel = "debug";
|
|
2012
1689
|
AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
|
|
2013
1690
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
2014
1691
|
await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.level);
|
|
@@ -2060,7 +1737,6 @@ export class Frontend extends EventEmitter {
|
|
|
2060
1737
|
}
|
|
2061
1738
|
break;
|
|
2062
1739
|
case 'setmatterport':
|
|
2063
|
-
// eslint-disable-next-line no-case-declarations
|
|
2064
1740
|
const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
2065
1741
|
if (isValidNumber(port, 5540, 5600)) {
|
|
2066
1742
|
this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
|
|
@@ -2080,7 +1756,6 @@ export class Frontend extends EventEmitter {
|
|
|
2080
1756
|
}
|
|
2081
1757
|
break;
|
|
2082
1758
|
case 'setmatterdiscriminator':
|
|
2083
|
-
// eslint-disable-next-line no-case-declarations
|
|
2084
1759
|
const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
2085
1760
|
if (isValidNumber(discriminator, 0, 4095)) {
|
|
2086
1761
|
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
|
|
@@ -2100,7 +1775,6 @@ export class Frontend extends EventEmitter {
|
|
|
2100
1775
|
}
|
|
2101
1776
|
break;
|
|
2102
1777
|
case 'setmatterpasscode':
|
|
2103
|
-
// eslint-disable-next-line no-case-declarations
|
|
2104
1778
|
const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
2105
1779
|
if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
|
|
2106
1780
|
this.matterbridge.passcode = passcode;
|
|
@@ -2146,19 +1820,15 @@ export class Frontend extends EventEmitter {
|
|
|
2146
1820
|
return;
|
|
2147
1821
|
}
|
|
2148
1822
|
const config = plugin.configJson;
|
|
2149
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2150
1823
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
2151
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
2152
1824
|
if (select === 'serial')
|
|
2153
1825
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
2154
1826
|
if (select === 'name')
|
|
2155
1827
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
2156
1828
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
2157
|
-
// Remove postfix from the serial if it exists
|
|
2158
1829
|
if (config.postfix) {
|
|
2159
1830
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
2160
1831
|
}
|
|
2161
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
2162
1832
|
if (isValidArray(config.whiteList, 1)) {
|
|
2163
1833
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
2164
1834
|
config.whiteList.push(data.params.serial);
|
|
@@ -2167,7 +1837,6 @@ export class Frontend extends EventEmitter {
|
|
|
2167
1837
|
config.whiteList.push(data.params.name);
|
|
2168
1838
|
}
|
|
2169
1839
|
}
|
|
2170
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
2171
1840
|
if (isValidArray(config.blackList, 1)) {
|
|
2172
1841
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
2173
1842
|
config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
|
|
@@ -2195,9 +1864,7 @@ export class Frontend extends EventEmitter {
|
|
|
2195
1864
|
return;
|
|
2196
1865
|
}
|
|
2197
1866
|
const config = plugin.configJson;
|
|
2198
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2199
1867
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
2200
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
2201
1868
|
if (select === 'serial')
|
|
2202
1869
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
2203
1870
|
if (select === 'name')
|
|
@@ -2206,7 +1873,6 @@ export class Frontend extends EventEmitter {
|
|
|
2206
1873
|
if (config.postfix) {
|
|
2207
1874
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
2208
1875
|
}
|
|
2209
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
2210
1876
|
if (isValidArray(config.whiteList, 1)) {
|
|
2211
1877
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
2212
1878
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
|
|
@@ -2215,7 +1881,6 @@ export class Frontend extends EventEmitter {
|
|
|
2215
1881
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
|
|
2216
1882
|
}
|
|
2217
1883
|
}
|
|
2218
|
-
// Add the serial to the blackList
|
|
2219
1884
|
if (isValidArray(config.blackList)) {
|
|
2220
1885
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
2221
1886
|
config.blackList.push(data.params.serial);
|
|
@@ -2238,7 +1903,6 @@ export class Frontend extends EventEmitter {
|
|
|
2238
1903
|
}
|
|
2239
1904
|
}
|
|
2240
1905
|
else {
|
|
2241
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2242
1906
|
const localData = data;
|
|
2243
1907
|
this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
|
|
2244
1908
|
sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
|
|
@@ -2248,46 +1912,23 @@ export class Frontend extends EventEmitter {
|
|
|
2248
1912
|
inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
|
|
2249
1913
|
}
|
|
2250
1914
|
}
|
|
2251
|
-
/**
|
|
2252
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
2253
|
-
*
|
|
2254
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
2255
|
-
* @param {string} time - The time string of the message
|
|
2256
|
-
* @param {string} name - The logger name of the message
|
|
2257
|
-
* @param {string} message - The content of the message.
|
|
2258
|
-
*
|
|
2259
|
-
* @remarks
|
|
2260
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
2261
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
2262
|
-
* The function sends the message to all connected clients.
|
|
2263
|
-
*/
|
|
2264
1915
|
wssSendLogMessage(level, time, name, message) {
|
|
2265
1916
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2266
1917
|
return;
|
|
2267
1918
|
if (!level || !time || !name || !message)
|
|
2268
1919
|
return;
|
|
2269
|
-
// Remove ANSI escape codes from the message
|
|
2270
|
-
// eslint-disable-next-line no-control-regex
|
|
2271
1920
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
2272
|
-
// Remove leading asterisks from the message
|
|
2273
1921
|
message = message.replace(/^\*+/, '');
|
|
2274
|
-
// Replace all occurrences of \t and \n
|
|
2275
1922
|
message = message.replace(/[\t\n]/g, '');
|
|
2276
|
-
// Remove non-printable characters
|
|
2277
|
-
// eslint-disable-next-line no-control-regex
|
|
2278
1923
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
2279
|
-
// Replace all occurrences of \" with "
|
|
2280
1924
|
message = message.replace(/\\"/g, '"');
|
|
2281
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
2282
1925
|
const maxContinuousLength = 100;
|
|
2283
1926
|
const keepStartLength = 20;
|
|
2284
1927
|
const keepEndLength = 20;
|
|
2285
|
-
// Split the message into words
|
|
2286
1928
|
if (level !== 'spawn') {
|
|
2287
1929
|
message = message
|
|
2288
1930
|
.split(' ')
|
|
2289
1931
|
.map((word) => {
|
|
2290
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2291
1932
|
if (word.length > maxContinuousLength) {
|
|
2292
1933
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2293
1934
|
}
|
|
@@ -2295,34 +1936,14 @@ export class Frontend extends EventEmitter {
|
|
|
2295
1936
|
})
|
|
2296
1937
|
.join(' ');
|
|
2297
1938
|
}
|
|
2298
|
-
// Send the message to all connected clients
|
|
2299
1939
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
|
|
2300
1940
|
}
|
|
2301
|
-
/**
|
|
2302
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2303
|
-
*
|
|
2304
|
-
* @param {string} changed - The changed value.
|
|
2305
|
-
* @param {Record<string, unknown>} params - Additional parameters to send with the message.
|
|
2306
|
-
* possible values for changed:
|
|
2307
|
-
* - 'settings' (when the bridge has started in bridge mode or childbridge mode and when update finds a new version)
|
|
2308
|
-
* - 'plugins'
|
|
2309
|
-
* - 'devices'
|
|
2310
|
-
* - 'matter' with param 'matter' (QRDiv component)
|
|
2311
|
-
* @param {ApiMatter} params.matter - The matter device that has changed. Required if changed is 'matter'.
|
|
2312
|
-
*/
|
|
2313
1941
|
wssSendRefreshRequired(changed, params) {
|
|
2314
1942
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2315
1943
|
return;
|
|
2316
1944
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
2317
|
-
// Send the message to all connected clients
|
|
2318
1945
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
|
|
2319
1946
|
}
|
|
2320
|
-
/**
|
|
2321
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2322
|
-
*
|
|
2323
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
|
|
2324
|
-
* @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
|
|
2325
|
-
*/
|
|
2326
1947
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
2327
1948
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2328
1949
|
return;
|
|
@@ -2331,14 +1952,8 @@ export class Frontend extends EventEmitter {
|
|
|
2331
1952
|
this.matterbridge.fixedRestartRequired = fixed;
|
|
2332
1953
|
if (snackbar === true)
|
|
2333
1954
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
2334
|
-
// Send the message to all connected clients
|
|
2335
1955
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
|
|
2336
1956
|
}
|
|
2337
|
-
/**
|
|
2338
|
-
* Sends a no need to restart WebSocket message to all connected clients.
|
|
2339
|
-
*
|
|
2340
|
-
* @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients. Default is true.
|
|
2341
|
-
*/
|
|
2342
1957
|
wssSendRestartNotRequired(snackbar = true) {
|
|
2343
1958
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2344
1959
|
return;
|
|
@@ -2346,145 +1961,64 @@ export class Frontend extends EventEmitter {
|
|
|
2346
1961
|
this.matterbridge.restartRequired = false;
|
|
2347
1962
|
if (snackbar === true)
|
|
2348
1963
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2349
|
-
// Send the message to all connected clients
|
|
2350
1964
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
|
|
2351
1965
|
}
|
|
2352
|
-
/**
|
|
2353
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
2354
|
-
*
|
|
2355
|
-
* @param {boolean} devVersion - If true, the update is for a development version. Default is false.
|
|
2356
|
-
*/
|
|
2357
1966
|
wssSendUpdateRequired(devVersion = false) {
|
|
2358
1967
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2359
1968
|
return;
|
|
2360
1969
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2361
1970
|
this.matterbridge.updateRequired = true;
|
|
2362
|
-
// Send the message to all connected clients
|
|
2363
1971
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
|
|
2364
1972
|
}
|
|
2365
|
-
/**
|
|
2366
|
-
* Sends a cpu update message to all connected clients.
|
|
2367
|
-
*
|
|
2368
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2369
|
-
* @param {number} processCpuUsage - The CPU usage percentage of the process to send.
|
|
2370
|
-
*/
|
|
2371
1973
|
wssSendCpuUpdate(cpuUsage, processCpuUsage) {
|
|
2372
1974
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2373
1975
|
return;
|
|
2374
|
-
// istanbul ignore else
|
|
2375
1976
|
if (hasParameter('debug'))
|
|
2376
1977
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2377
|
-
// Send the message to all connected clients
|
|
2378
1978
|
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 } });
|
|
2379
1979
|
}
|
|
2380
|
-
/**
|
|
2381
|
-
* Sends a memory update message to all connected clients.
|
|
2382
|
-
*
|
|
2383
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2384
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2385
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2386
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2387
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2388
|
-
* @param {string} external - The external memory in bytes.
|
|
2389
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2390
|
-
*/
|
|
2391
1980
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2392
1981
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2393
1982
|
return;
|
|
2394
|
-
// istanbul ignore else
|
|
2395
1983
|
if (hasParameter('debug'))
|
|
2396
1984
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2397
|
-
// Send the message to all connected clients
|
|
2398
1985
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', success: true, response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
|
|
2399
1986
|
}
|
|
2400
|
-
/**
|
|
2401
|
-
* Sends an uptime update message to all connected clients.
|
|
2402
|
-
*
|
|
2403
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2404
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2405
|
-
*/
|
|
2406
1987
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2407
1988
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2408
1989
|
return;
|
|
2409
|
-
// istanbul ignore else
|
|
2410
1990
|
if (hasParameter('debug'))
|
|
2411
1991
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2412
|
-
// Send the message to all connected clients
|
|
2413
1992
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
|
|
2414
1993
|
}
|
|
2415
|
-
/**
|
|
2416
|
-
* Sends an open snackbar message to all connected clients.
|
|
2417
|
-
*
|
|
2418
|
-
* @param {string} message - The message to send.
|
|
2419
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message. Default is 5 seconds.
|
|
2420
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the message.
|
|
2421
|
-
* possible values are: 'info', 'warning', 'error', 'success'. Default is 'info'.
|
|
2422
|
-
*
|
|
2423
|
-
* @remarks
|
|
2424
|
-
* If timeout is 0, the snackbar message will be displayed until closed by the user.
|
|
2425
|
-
*/
|
|
2426
1994
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2427
1995
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2428
1996
|
return;
|
|
2429
1997
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2430
|
-
// Send the message to all connected clients
|
|
2431
1998
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
|
|
2432
1999
|
}
|
|
2433
|
-
/**
|
|
2434
|
-
* Sends a close snackbar message to all connected clients.
|
|
2435
|
-
* It will close the snackbar message with the same message and timeout = 0.
|
|
2436
|
-
*
|
|
2437
|
-
* @param {string} message - The message to send.
|
|
2438
|
-
*/
|
|
2439
2000
|
wssSendCloseSnackbarMessage(message) {
|
|
2440
2001
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2441
2002
|
return;
|
|
2442
2003
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2443
|
-
// Send the message to all connected clients
|
|
2444
2004
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
|
|
2445
2005
|
}
|
|
2446
|
-
/**
|
|
2447
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2448
|
-
*
|
|
2449
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2450
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2451
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2452
|
-
* @param {EndpointNumber} number - The endpoint number where the attribute belongs.
|
|
2453
|
-
* @param {string} id - The endpoint id where the attribute belongs.
|
|
2454
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2455
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2456
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2457
|
-
*
|
|
2458
|
-
* @remarks
|
|
2459
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2460
|
-
* with the updated attribute information.
|
|
2461
|
-
*/
|
|
2462
2006
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
|
|
2463
2007
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2464
2008
|
return;
|
|
2465
2009
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2466
|
-
// Send the message to all connected clients
|
|
2467
2010
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', success: true, response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value } });
|
|
2468
2011
|
}
|
|
2469
|
-
/**
|
|
2470
|
-
* Sends a message to all connected clients.
|
|
2471
|
-
* This is an helper function to send a broadcast message to all connected clients.
|
|
2472
|
-
*
|
|
2473
|
-
* @param {WsMessageBroadcast} msg - The message to send.
|
|
2474
|
-
*/
|
|
2475
2012
|
wssBroadcastMessage(msg) {
|
|
2476
2013
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2477
2014
|
return;
|
|
2478
|
-
// Send the message to all connected clients
|
|
2479
2015
|
const stringifiedMsg = JSON.stringify(msg);
|
|
2480
2016
|
if (msg.method !== 'log')
|
|
2481
2017
|
this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
|
|
2482
2018
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2483
|
-
// istanbul ignore else
|
|
2484
2019
|
if (client.readyState === client.OPEN) {
|
|
2485
2020
|
client.send(stringifiedMsg);
|
|
2486
2021
|
}
|
|
2487
2022
|
});
|
|
2488
2023
|
}
|
|
2489
2024
|
}
|
|
2490
|
-
//# sourceMappingURL=frontend.js.map
|