@matterbridge/core 3.5.3 → 3.5.4-dev-20260211-c4f9359
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/README.md +1 -1
- package/dist/cli.d.ts +0 -24
- package/dist/cli.js +1 -97
- package/dist/cliEmitter.d.ts +0 -36
- package/dist/cliEmitter.js +0 -37
- package/dist/cliHistory.d.ts +0 -42
- package/dist/cliHistory.js +0 -38
- 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 +1 -114
- package/dist/devices/airConditioner.d.ts +0 -75
- package/dist/devices/airConditioner.js +0 -57
- package/dist/devices/basicVideoPlayer.d.ts +0 -58
- package/dist/devices/basicVideoPlayer.js +1 -56
- package/dist/devices/batteryStorage.d.ts +0 -43
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/castingVideoPlayer.d.ts +0 -63
- package/dist/devices/castingVideoPlayer.js +2 -65
- 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/dgram/export.d.ts +0 -1
- package/dist/dgram/export.js +0 -1
- package/dist/export.d.ts +0 -23
- package/dist/export.js +0 -28
- package/dist/frontend.d.ts +0 -187
- package/dist/frontend.js +38 -539
- package/dist/helpers.d.ts +0 -43
- package/dist/helpers.js +0 -86
- package/dist/jestutils/export.d.ts +0 -1
- package/dist/jestutils/export.js +0 -1
- package/dist/jestutils/jestHelpers.d.ts +0 -259
- package/dist/jestutils/jestHelpers.js +14 -395
- 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 +8 -356
- package/dist/matterbridge.d.ts +0 -389
- package/dist/matterbridge.js +48 -878
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
- package/dist/matterbridgeAccessoryPlatform.js +0 -50
- package/dist/matterbridgeBehaviors.d.ts +0 -24
- package/dist/matterbridgeBehaviors.js +5 -65
- package/dist/matterbridgeDeviceTypes.d.ts +0 -649
- package/dist/matterbridgeDeviceTypes.js +6 -673
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
- package/dist/matterbridgeDynamicPlatform.js +0 -50
- package/dist/matterbridgeEndpoint.d.ts +0 -1369
- package/dist/matterbridgeEndpoint.js +56 -1514
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -425
- package/dist/matterbridgeEndpointHelpers.js +20 -483
- package/dist/matterbridgeEndpointTypes.d.ts +0 -70
- package/dist/matterbridgeEndpointTypes.js +0 -25
- package/dist/matterbridgePlatform.d.ts +0 -434
- package/dist/matterbridgePlatform.js +1 -473
- package/dist/mb_coap.d.ts +0 -23
- package/dist/mb_coap.js +3 -41
- package/dist/mb_health.d.ts +0 -67
- package/dist/mb_health.js +0 -70
- package/dist/mb_mdns.d.ts +0 -23
- package/dist/mb_mdns.js +36 -94
- package/dist/pluginManager.d.ts +0 -307
- package/dist/pluginManager.js +6 -346
- package/dist/spawn.d.ts +0 -32
- package/dist/spawn.js +1 -71
- package/dist/utils/export.d.ts +0 -1
- package/dist/utils/export.js +0 -1
- package/package.json +27 -6
- 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/crypto/attestationDecoder.d.ts +0 -180
- package/dist/crypto/attestationDecoder.d.ts.map +0 -1
- package/dist/crypto/attestationDecoder.js +0 -176
- package/dist/crypto/attestationDecoder.js.map +0 -1
- package/dist/crypto/declarationDecoder.d.ts +0 -72
- package/dist/crypto/declarationDecoder.d.ts.map +0 -1
- package/dist/crypto/declarationDecoder.js +0 -241
- package/dist/crypto/declarationDecoder.js.map +0 -1
- package/dist/crypto/extract/342/200/220cert/342/200/220extensions.d.ts +0 -9
- package/dist/crypto/extract/342/200/220cert/342/200/220extensions.d.ts.map +0 -1
- package/dist/crypto/extract/342/200/220cert/342/200/220extensions.js +0 -120
- package/dist/crypto/extract/342/200/220cert/342/200/220extensions.js.map +0 -1
- package/dist/crypto/read-extensions.d.ts +0 -2
- package/dist/crypto/read-extensions.d.ts.map +0 -1
- package/dist/crypto/read-extensions.js +0 -81
- package/dist/crypto/read-extensions.js.map +0 -1
- package/dist/crypto/testData.d.ts +0 -31
- package/dist/crypto/testData.d.ts.map +0 -1
- package/dist/crypto/testData.js +0 -131
- package/dist/crypto/testData.js.map +0 -1
- package/dist/crypto/walk-der.d.ts +0 -2
- package/dist/crypto/walk-der.d.ts.map +0 -1
- package/dist/crypto/walk-der.js +0 -165
- package/dist/crypto/walk-der.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/basicVideoPlayer.d.ts.map +0 -1
- package/dist/devices/basicVideoPlayer.js.map +0 -1
- package/dist/devices/batteryStorage.d.ts.map +0 -1
- package/dist/devices/batteryStorage.js.map +0 -1
- package/dist/devices/castingVideoPlayer.d.ts.map +0 -1
- package/dist/devices/castingVideoPlayer.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/export.d.ts.map +0 -1
- package/dist/dgram/export.js.map +0 -1
- package/dist/export.d.ts.map +0 -1
- package/dist/export.js.map +0 -1
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.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/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/mb_coap.d.ts.map +0 -1
- package/dist/mb_coap.js.map +0 -1
- package/dist/mb_health.d.ts.map +0 -1
- package/dist/mb_health.js.map +0 -1
- package/dist/mb_mdns.d.ts.map +0 -1
- package/dist/mb_mdns.js.map +0 -1
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/spawn.d.ts.map +0 -1
- package/dist/spawn.js.map +0 -1
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/workers/brand.d.ts +0 -25
- package/dist/workers/brand.d.ts.map +0 -1
- package/dist/workers/brand.extend.d.ts +0 -10
- package/dist/workers/brand.extend.d.ts.map +0 -1
- package/dist/workers/brand.extend.js +0 -15
- package/dist/workers/brand.extend.js.map +0 -1
- package/dist/workers/brand.invalid.d.ts +0 -9
- package/dist/workers/brand.invalid.d.ts.map +0 -1
- package/dist/workers/brand.invalid.js +0 -19
- package/dist/workers/brand.invalid.js.map +0 -1
- package/dist/workers/brand.js +0 -67
- package/dist/workers/brand.js.map +0 -1
- package/dist/workers/clusterTypes.d.ts +0 -47
- package/dist/workers/clusterTypes.d.ts.map +0 -1
- package/dist/workers/clusterTypes.js +0 -57
- package/dist/workers/clusterTypes.js.map +0 -1
- package/dist/workers/frontendWorker.d.ts +0 -2
- package/dist/workers/frontendWorker.d.ts.map +0 -1
- package/dist/workers/frontendWorker.js +0 -90
- package/dist/workers/frontendWorker.js.map +0 -1
- package/dist/workers/helloWorld.d.ts +0 -2
- package/dist/workers/helloWorld.d.ts.map +0 -1
- package/dist/workers/helloWorld.js +0 -135
- package/dist/workers/helloWorld.js.map +0 -1
- package/dist/workers/matterWorker.d.ts +0 -2
- package/dist/workers/matterWorker.d.ts.map +0 -1
- package/dist/workers/matterWorker.js +0 -104
- package/dist/workers/matterWorker.js.map +0 -1
- package/dist/workers/matterbridgeWorker.d.ts +0 -2
- package/dist/workers/matterbridgeWorker.d.ts.map +0 -1
- package/dist/workers/matterbridgeWorker.js +0 -75
- package/dist/workers/matterbridgeWorker.js.map +0 -1
- package/dist/workers/messageLab.d.ts +0 -134
- package/dist/workers/messageLab.d.ts.map +0 -1
- package/dist/workers/messageLab.js +0 -129
- package/dist/workers/messageLab.js.map +0 -1
- package/dist/workers/testWorker.d.ts +0 -2
- package/dist/workers/testWorker.d.ts.map +0 -1
- package/dist/workers/testWorker.js +0 -45
- package/dist/workers/testWorker.js.map +0 -1
- package/dist/workers/usage.d.ts +0 -19
- package/dist/workers/usage.d.ts.map +0 -1
- package/dist/workers/usage.js +0 -140
- package/dist/workers/usage.js.map +0 -1
- package/dist/workers/workerManager.d.ts +0 -115
- package/dist/workers/workerManager.d.ts.map +0 -1
- package/dist/workers/workerManager.js +0 -464
- package/dist/workers/workerManager.js.map +0 -1
- package/dist/workers/workerServer.d.ts +0 -126
- package/dist/workers/workerServer.d.ts.map +0 -1
- package/dist/workers/workerServer.js +0 -340
- package/dist/workers/workerServer.js.map +0 -1
- package/dist/workers/workerTypes.d.ts +0 -23
- package/dist/workers/workerTypes.d.ts.map +0 -1
- package/dist/workers/workerTypes.js +0 -3
- package/dist/workers/workerTypes.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,7 +10,6 @@ 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';
|
|
39
|
-
// @matterbridge
|
|
40
13
|
import { createZip, formatBytes, formatPercent, formatUptime, getParameter, hasParameter, inspectError, isValidArray, isValidBoolean, isValidNumber, isValidObject, isValidString, wait, withTimeout, } from '@matterbridge/utils';
|
|
41
14
|
import { MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_DIAGNOSTIC_FILE, MATTERBRIDGE_HISTORY_FILE, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg, } from '@matterbridge/types';
|
|
42
15
|
import { BroadcastServer } from '@matterbridge/thread';
|
|
@@ -63,8 +36,8 @@ export class Frontend extends EventEmitter {
|
|
|
63
36
|
this.log = new AnsiLogger({
|
|
64
37
|
logName: 'Frontend',
|
|
65
38
|
logNameColor: '\x1b[38;5;97m',
|
|
66
|
-
logTimestampFormat: 4
|
|
67
|
-
logLevel: hasParameter('debug') ? "debug"
|
|
39
|
+
logTimestampFormat: 4,
|
|
40
|
+
logLevel: hasParameter('debug') ? "debug" : "info",
|
|
68
41
|
});
|
|
69
42
|
this.server = new BroadcastServer('frontend', this.log);
|
|
70
43
|
this.server.on('broadcast_message', this.msgHandler.bind(this));
|
|
@@ -75,7 +48,6 @@ export class Frontend extends EventEmitter {
|
|
|
75
48
|
}
|
|
76
49
|
async msgHandler(msg) {
|
|
77
50
|
if (this.server.isWorkerRequest(msg)) {
|
|
78
|
-
// istanbul ignore else
|
|
79
51
|
if (this.verbose)
|
|
80
52
|
this.log.debug(`Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
|
|
81
53
|
switch (msg.type) {
|
|
@@ -127,13 +99,11 @@ export class Frontend extends EventEmitter {
|
|
|
127
99
|
this.server.respond({ ...msg, result: { success: true } });
|
|
128
100
|
break;
|
|
129
101
|
default:
|
|
130
|
-
// istanbul ignore next
|
|
131
102
|
if (this.verbose)
|
|
132
103
|
this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
|
|
133
104
|
}
|
|
134
105
|
}
|
|
135
106
|
if (this.server.isWorkerResponse(msg) && msg.result) {
|
|
136
|
-
// istanbul ignore next
|
|
137
107
|
if (this.verbose)
|
|
138
108
|
this.log.debug(`Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
|
|
139
109
|
switch (msg.type) {
|
|
@@ -177,55 +147,23 @@ export class Frontend extends EventEmitter {
|
|
|
177
147
|
this.port = port;
|
|
178
148
|
this.storedPassword = await this.matterbridge.nodeContext?.get('password', '');
|
|
179
149
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
180
|
-
// Initialize multer with the upload directory
|
|
181
150
|
const multer = await import('multer');
|
|
182
|
-
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
151
|
+
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
183
152
|
const upload = multer.default({ dest: uploadDir });
|
|
184
|
-
// Create the express app that serves the frontend
|
|
185
153
|
const express = await import('express');
|
|
186
154
|
this.expressApp = express.default();
|
|
187
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
188
|
-
/*
|
|
189
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
190
|
-
for (const method of methods) {
|
|
191
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
192
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
193
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
194
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
195
|
-
try {
|
|
196
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
197
|
-
return original(path, ...rest);
|
|
198
|
-
} catch (err) {
|
|
199
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
200
|
-
throw err;
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
*/
|
|
205
|
-
// Log all requests to the server for debugging
|
|
206
|
-
/*
|
|
207
|
-
this.expressApp.use((req, res, next) => {
|
|
208
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
209
|
-
next();
|
|
210
|
-
});
|
|
211
|
-
*/
|
|
212
|
-
// Serve static files from 'frontend/build' directory
|
|
213
155
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'apps', 'frontend', 'build')));
|
|
214
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
215
156
|
this.log.debug(`Creating WebSocketServer...`);
|
|
216
157
|
const ws = await import('ws');
|
|
217
158
|
this.webSocketServer = new ws.WebSocketServer({ noServer: true });
|
|
218
159
|
this.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
|
|
219
160
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
220
161
|
const clientIp = request.socket.remoteAddress;
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (this.matterbridge.getLogLevel() === "
|
|
225
|
-
callbackLogLevel = "
|
|
226
|
-
// istanbul ignore else
|
|
227
|
-
if (this.matterbridge.getLogLevel() === "debug" /* LogLevel.DEBUG */ || Logger.level === MatterLogLevel.DEBUG)
|
|
228
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
162
|
+
let callbackLogLevel = "notice";
|
|
163
|
+
if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
|
|
164
|
+
callbackLogLevel = "info";
|
|
165
|
+
if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
166
|
+
callbackLogLevel = "debug";
|
|
229
167
|
AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
|
|
230
168
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
231
169
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -241,34 +179,23 @@ export class Frontend extends EventEmitter {
|
|
|
241
179
|
});
|
|
242
180
|
ws.on('close', () => {
|
|
243
181
|
this.log.info('WebSocket client disconnected');
|
|
244
|
-
// istanbul ignore else
|
|
245
182
|
if (this.webSocketServer?.clients.size === 0) {
|
|
246
183
|
AnsiLogger.setGlobalCallback(undefined);
|
|
247
184
|
this.log.debug('All WebSocket clients disconnected. WebSocketServer logger global callback removed');
|
|
248
185
|
this.authClients = [];
|
|
249
186
|
}
|
|
250
187
|
});
|
|
251
|
-
// istanbul ignore next
|
|
252
188
|
ws.on('error', (error) => {
|
|
253
|
-
// istanbul ignore next
|
|
254
189
|
this.log.error(`WebSocket client error: ${error}`);
|
|
255
190
|
});
|
|
256
191
|
});
|
|
257
192
|
this.webSocketServer.on('close', () => {
|
|
258
193
|
this.log.debug(`WebSocketServer closed`);
|
|
259
194
|
});
|
|
260
|
-
/* With { noServer: true } it never fires
|
|
261
|
-
this.webSocketServer.on('listening', () => {
|
|
262
|
-
this.log.info(`The WebSocketServer is listening`);
|
|
263
|
-
this.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
|
|
264
|
-
});
|
|
265
|
-
*/
|
|
266
|
-
// istanbul ignore next
|
|
267
195
|
this.webSocketServer.on('error', (ws, error) => {
|
|
268
196
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
269
197
|
});
|
|
270
198
|
if (!hasParameter('ssl')) {
|
|
271
|
-
// Create an HTTP server and attach the express app
|
|
272
199
|
const http = await import('node:http');
|
|
273
200
|
try {
|
|
274
201
|
this.log.debug(`Creating HTTP server...`);
|
|
@@ -279,9 +206,7 @@ export class Frontend extends EventEmitter {
|
|
|
279
206
|
this.emit('server_error', error);
|
|
280
207
|
return;
|
|
281
208
|
}
|
|
282
|
-
// Listen on the specified port
|
|
283
209
|
if (hasParameter('ingress')) {
|
|
284
|
-
// We limit to all ipv4 addresses when running in ingress mode (Home Assistant add-on)
|
|
285
210
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
286
211
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
287
212
|
this.listening = true;
|
|
@@ -289,17 +214,13 @@ export class Frontend extends EventEmitter {
|
|
|
289
214
|
});
|
|
290
215
|
}
|
|
291
216
|
else {
|
|
292
|
-
// We listen to all available addresses
|
|
293
217
|
this.httpServer.listen(this.port, getParameter('bind'), () => {
|
|
294
218
|
const addr = this.httpServer?.address();
|
|
295
|
-
// istanbul ignore else
|
|
296
219
|
if (addr && typeof addr !== 'string') {
|
|
297
220
|
this.log.info(`The frontend http server is bound to ${addr.family} ${addr.address}:${addr.port}`);
|
|
298
221
|
}
|
|
299
|
-
// istanbul ignore else
|
|
300
222
|
if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
|
|
301
223
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
|
|
302
|
-
// istanbul ignore else
|
|
303
224
|
if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
|
|
304
225
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
305
226
|
this.listening = true;
|
|
@@ -308,25 +229,19 @@ export class Frontend extends EventEmitter {
|
|
|
308
229
|
}
|
|
309
230
|
this.httpServer.on('upgrade', async (req, socket, head) => {
|
|
310
231
|
try {
|
|
311
|
-
// Only proceed for real WebSocket upgrades
|
|
312
|
-
// istanbul ignore next cause is only a safety check
|
|
313
232
|
if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
|
|
314
233
|
this.log.error(`WebSocket upgrade error: Invalid upgrade header ${req.headers.upgrade}`);
|
|
315
234
|
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
316
235
|
return socket.destroy();
|
|
317
236
|
}
|
|
318
|
-
// Build a URL so we can read ?password=...
|
|
319
237
|
const url = new URL(req.url ?? '/', `http://${req.headers.host || 'localhost'}`);
|
|
320
|
-
// Validate WebSocket password
|
|
321
238
|
const password = url.searchParams.get('password') ?? '';
|
|
322
239
|
if (password !== this.storedPassword) {
|
|
323
240
|
this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
|
|
324
241
|
socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
|
|
325
242
|
return socket.destroy();
|
|
326
243
|
}
|
|
327
|
-
// Complete the WebSocket handshake
|
|
328
244
|
this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
|
|
329
|
-
// istanbul ignore else
|
|
330
245
|
if (req.socket.remoteAddress)
|
|
331
246
|
this.authClients.push(req.socket.remoteAddress);
|
|
332
247
|
this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
|
|
@@ -334,7 +249,6 @@ export class Frontend extends EventEmitter {
|
|
|
334
249
|
});
|
|
335
250
|
}
|
|
336
251
|
catch (err) {
|
|
337
|
-
/* istanbul ignore next: only triggered on unexpected internal error */
|
|
338
252
|
{
|
|
339
253
|
inspectError(this.log, 'WebSocket upgrade error:', err);
|
|
340
254
|
socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
|
|
@@ -357,7 +271,6 @@ export class Frontend extends EventEmitter {
|
|
|
357
271
|
});
|
|
358
272
|
}
|
|
359
273
|
else {
|
|
360
|
-
// SSL is enabled, load the certificate and the private key
|
|
361
274
|
let cert;
|
|
362
275
|
let key;
|
|
363
276
|
let ca;
|
|
@@ -367,7 +280,6 @@ export class Frontend extends EventEmitter {
|
|
|
367
280
|
let httpsServerOptions = {};
|
|
368
281
|
const fs = await import('node:fs');
|
|
369
282
|
if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'))) {
|
|
370
|
-
// Load the p12 certificate and the passphrase
|
|
371
283
|
try {
|
|
372
284
|
pfx = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'));
|
|
373
285
|
this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12')}`);
|
|
@@ -379,7 +291,7 @@ export class Frontend extends EventEmitter {
|
|
|
379
291
|
}
|
|
380
292
|
try {
|
|
381
293
|
passphrase = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass'), 'utf8');
|
|
382
|
-
passphrase = passphrase.trim();
|
|
294
|
+
passphrase = passphrase.trim();
|
|
383
295
|
this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass')}`);
|
|
384
296
|
}
|
|
385
297
|
catch (error) {
|
|
@@ -390,7 +302,6 @@ export class Frontend extends EventEmitter {
|
|
|
390
302
|
httpsServerOptions = { pfx, passphrase };
|
|
391
303
|
}
|
|
392
304
|
else {
|
|
393
|
-
// 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.
|
|
394
305
|
try {
|
|
395
306
|
cert = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem'), 'utf8');
|
|
396
307
|
this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem')}`);
|
|
@@ -420,10 +331,9 @@ export class Frontend extends EventEmitter {
|
|
|
420
331
|
httpsServerOptions = { cert: fullChain ?? cert, key, ca };
|
|
421
332
|
}
|
|
422
333
|
if (hasParameter('mtls')) {
|
|
423
|
-
httpsServerOptions.requestCert = true;
|
|
424
|
-
httpsServerOptions.rejectUnauthorized = true;
|
|
334
|
+
httpsServerOptions.requestCert = true;
|
|
335
|
+
httpsServerOptions.rejectUnauthorized = true;
|
|
425
336
|
}
|
|
426
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
427
337
|
const https = await import('node:https');
|
|
428
338
|
try {
|
|
429
339
|
this.log.debug(`Creating HTTPS server...`);
|
|
@@ -434,9 +344,7 @@ export class Frontend extends EventEmitter {
|
|
|
434
344
|
this.emit('server_error', error);
|
|
435
345
|
return;
|
|
436
346
|
}
|
|
437
|
-
// Listen on the specified port
|
|
438
347
|
if (hasParameter('ingress')) {
|
|
439
|
-
// We limit to all ipv4 addresses when running in ingress mode (Home Assistant add-on)
|
|
440
348
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
441
349
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
442
350
|
this.listening = true;
|
|
@@ -444,17 +352,13 @@ export class Frontend extends EventEmitter {
|
|
|
444
352
|
});
|
|
445
353
|
}
|
|
446
354
|
else {
|
|
447
|
-
// We listen to all available addresses
|
|
448
355
|
this.httpsServer.listen(this.port, getParameter('bind'), () => {
|
|
449
356
|
const addr = this.httpsServer?.address();
|
|
450
|
-
// istanbul ignore else
|
|
451
357
|
if (addr && typeof addr !== 'string') {
|
|
452
358
|
this.log.info(`The frontend https server is bound to ${addr.family} ${addr.address}:${addr.port}`);
|
|
453
359
|
}
|
|
454
|
-
// istanbul ignore else
|
|
455
360
|
if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
|
|
456
361
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
|
|
457
|
-
// istanbul ignore else
|
|
458
362
|
if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
|
|
459
363
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
460
364
|
this.listening = true;
|
|
@@ -463,24 +367,18 @@ export class Frontend extends EventEmitter {
|
|
|
463
367
|
}
|
|
464
368
|
this.httpsServer.on('upgrade', async (req, socket, head) => {
|
|
465
369
|
try {
|
|
466
|
-
// Only proceed for real WebSocket upgrades
|
|
467
|
-
// istanbul ignore next cause is only a safety check
|
|
468
370
|
if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
|
|
469
371
|
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
470
372
|
return socket.destroy();
|
|
471
373
|
}
|
|
472
|
-
// Build a URL so we can read ?password=...
|
|
473
374
|
const url = new URL(req.url ?? '/', `https://${req.headers.host || 'localhost'}`);
|
|
474
|
-
// Validate WebSocket password
|
|
475
375
|
const password = url.searchParams.get('password') ?? '';
|
|
476
376
|
if (password !== this.storedPassword) {
|
|
477
377
|
this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
|
|
478
378
|
socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
|
|
479
379
|
return socket.destroy();
|
|
480
380
|
}
|
|
481
|
-
// Complete the WebSocket handshake
|
|
482
381
|
this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
|
|
483
|
-
// istanbul ignore else
|
|
484
382
|
if (req.socket.remoteAddress)
|
|
485
383
|
this.authClients.push(req.socket.remoteAddress);
|
|
486
384
|
this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
|
|
@@ -488,7 +386,6 @@ export class Frontend extends EventEmitter {
|
|
|
488
386
|
});
|
|
489
387
|
}
|
|
490
388
|
catch (err) {
|
|
491
|
-
/* istanbul ignore next: only triggered on unexpected internal error */
|
|
492
389
|
{
|
|
493
390
|
inspectError(this.log, 'WebSocket upgrade error:', err);
|
|
494
391
|
socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
|
|
@@ -510,7 +407,6 @@ export class Frontend extends EventEmitter {
|
|
|
510
407
|
return;
|
|
511
408
|
});
|
|
512
409
|
}
|
|
513
|
-
// Subscribe to cli events
|
|
514
410
|
cliEmitter.removeAllListeners();
|
|
515
411
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
516
412
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -521,8 +417,6 @@ export class Frontend extends EventEmitter {
|
|
|
521
417
|
cliEmitter.on('cpu', (cpuUsage, processCpuUsage) => {
|
|
522
418
|
this.wssSendCpuUpdate(cpuUsage, processCpuUsage);
|
|
523
419
|
});
|
|
524
|
-
// Endpoint to validate login code
|
|
525
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
526
420
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
527
421
|
const { password } = req.body;
|
|
528
422
|
this.log.debug(`The frontend sent /api/login with password ${password ? '[redacted]' : '(empty)'}`);
|
|
@@ -537,20 +431,17 @@ export class Frontend extends EventEmitter {
|
|
|
537
431
|
res.json({ valid: false });
|
|
538
432
|
}
|
|
539
433
|
});
|
|
540
|
-
// Endpoint to provide health check for docker
|
|
541
434
|
this.expressApp.get('/health', (req, res) => {
|
|
542
435
|
this.log.debug('Express received /health');
|
|
543
436
|
const healthStatus = {
|
|
544
|
-
status: 'ok',
|
|
545
|
-
uptime: process.uptime(),
|
|
546
|
-
timestamp: new Date().toISOString(),
|
|
437
|
+
status: 'ok',
|
|
438
|
+
uptime: process.uptime(),
|
|
439
|
+
timestamp: new Date().toISOString(),
|
|
547
440
|
};
|
|
548
441
|
res.status(200).json(healthStatus);
|
|
549
442
|
});
|
|
550
|
-
// Endpoint to provide memory usage details
|
|
551
443
|
this.expressApp.get('/memory', async (req, res) => {
|
|
552
444
|
this.log.debug('Express received /memory');
|
|
553
|
-
// Memory usage from process
|
|
554
445
|
const memoryUsageRaw = process.memoryUsage();
|
|
555
446
|
const memoryUsage = {
|
|
556
447
|
rss: formatBytes(memoryUsageRaw.rss),
|
|
@@ -559,13 +450,10 @@ export class Frontend extends EventEmitter {
|
|
|
559
450
|
external: formatBytes(memoryUsageRaw.external),
|
|
560
451
|
arrayBuffers: formatBytes(memoryUsageRaw.arrayBuffers),
|
|
561
452
|
};
|
|
562
|
-
// V8 heap statistics
|
|
563
453
|
const { default: v8 } = await import('node:v8');
|
|
564
454
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
565
455
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
566
|
-
// Format heapStats
|
|
567
456
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, formatBytes(value)]));
|
|
568
|
-
// Format heapSpaces
|
|
569
457
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
570
458
|
...space,
|
|
571
459
|
space_size: formatBytes(space.space_size),
|
|
@@ -584,28 +472,24 @@ export class Frontend extends EventEmitter {
|
|
|
584
472
|
};
|
|
585
473
|
res.status(200).json(memoryReport);
|
|
586
474
|
});
|
|
587
|
-
// Endpoint to provide settings
|
|
588
475
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
589
476
|
this.log.debug('The frontend sent /api/settings');
|
|
590
477
|
if (!this.validateReq(req, res))
|
|
591
478
|
return;
|
|
592
479
|
res.json(await this.getApiSettings());
|
|
593
480
|
});
|
|
594
|
-
// Endpoint to provide plugins
|
|
595
481
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
596
482
|
this.log.debug('The frontend sent /api/plugins');
|
|
597
483
|
if (!this.validateReq(req, res))
|
|
598
484
|
return;
|
|
599
485
|
res.json(this.matterbridge.hasCleanupStarted ? [] : this.getPlugins());
|
|
600
486
|
});
|
|
601
|
-
// Endpoint to provide devices
|
|
602
487
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
603
488
|
this.log.debug('The frontend sent /api/devices');
|
|
604
489
|
if (!this.validateReq(req, res))
|
|
605
490
|
return;
|
|
606
491
|
res.json(this.matterbridge.hasCleanupStarted ? [] : this.getDevices());
|
|
607
492
|
});
|
|
608
|
-
// Endpoint to view the matterbridge log
|
|
609
493
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
610
494
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
611
495
|
if (!this.validateReq(req, res))
|
|
@@ -621,7 +505,6 @@ export class Frontend extends EventEmitter {
|
|
|
621
505
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
622
506
|
}
|
|
623
507
|
});
|
|
624
|
-
// Endpoint to view the matter.js log
|
|
625
508
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
626
509
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
627
510
|
if (!this.validateReq(req, res))
|
|
@@ -637,7 +520,6 @@ export class Frontend extends EventEmitter {
|
|
|
637
520
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
638
521
|
}
|
|
639
522
|
});
|
|
640
|
-
// Endpoint to view the diagnostic.log
|
|
641
523
|
this.expressApp.get('/api/view-diagnostic', async (req, res) => {
|
|
642
524
|
this.log.debug('The frontend sent /api/view-diagnostic');
|
|
643
525
|
if (!this.validateReq(req, res))
|
|
@@ -650,13 +532,10 @@ export class Frontend extends EventEmitter {
|
|
|
650
532
|
res.send(data.slice(29));
|
|
651
533
|
}
|
|
652
534
|
catch (error) {
|
|
653
|
-
// istanbul ignore next
|
|
654
535
|
this.log.error(`Error reading diagnostic log file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
655
|
-
// istanbul ignore next
|
|
656
536
|
res.status(500).send('Error reading diagnostic log file.');
|
|
657
537
|
}
|
|
658
538
|
});
|
|
659
|
-
// Endpoint to download the diagnostic.log
|
|
660
539
|
this.expressApp.get('/api/download-diagnostic', async (req, res) => {
|
|
661
540
|
this.log.debug(`The frontend sent /api/download-diagnostic`);
|
|
662
541
|
if (!this.validateReq(req, res))
|
|
@@ -669,19 +548,16 @@ export class Frontend extends EventEmitter {
|
|
|
669
548
|
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), data, 'utf-8');
|
|
670
549
|
}
|
|
671
550
|
catch (error) {
|
|
672
|
-
// istanbul ignore next
|
|
673
551
|
this.log.debug(`Error in /api/download-diagnostic: ${error instanceof Error ? error.message : error}`);
|
|
674
552
|
}
|
|
675
553
|
res.type('text/plain');
|
|
676
554
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), MATTERBRIDGE_DIAGNOSTIC_FILE, (error) => {
|
|
677
|
-
/* istanbul ignore if */
|
|
678
555
|
if (error) {
|
|
679
556
|
this.log.error(`Error downloading file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
680
557
|
res.status(500).send('Error downloading the diagnostic log file');
|
|
681
558
|
}
|
|
682
559
|
});
|
|
683
560
|
});
|
|
684
|
-
// Endpoint to view the history.html
|
|
685
561
|
this.expressApp.get('/api/viewhistory', async (req, res) => {
|
|
686
562
|
this.log.debug('The frontend sent /api/viewhistory');
|
|
687
563
|
if (!this.validateReq(req, res))
|
|
@@ -697,7 +573,6 @@ export class Frontend extends EventEmitter {
|
|
|
697
573
|
res.status(500).send('Error reading history file.');
|
|
698
574
|
}
|
|
699
575
|
});
|
|
700
|
-
// Endpoint to download the history.html
|
|
701
576
|
this.expressApp.get('/api/downloadhistory', async (req, res) => {
|
|
702
577
|
this.log.debug(`The frontend sent /api/downloadhistory`);
|
|
703
578
|
if (!this.validateReq(req, res))
|
|
@@ -709,7 +584,6 @@ export class Frontend extends EventEmitter {
|
|
|
709
584
|
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), data, 'utf-8');
|
|
710
585
|
res.type('text/plain');
|
|
711
586
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), MATTERBRIDGE_HISTORY_FILE, (error) => {
|
|
712
|
-
/* istanbul ignore if */
|
|
713
587
|
if (error) {
|
|
714
588
|
this.log.error(`Error in /api/downloadhistory downloading history file ${MATTERBRIDGE_HISTORY_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
715
589
|
res.status(500).send('Error downloading history file');
|
|
@@ -721,7 +595,6 @@ export class Frontend extends EventEmitter {
|
|
|
721
595
|
res.status(500).send('Error reading history file.');
|
|
722
596
|
}
|
|
723
597
|
});
|
|
724
|
-
// Endpoint to view the shelly log
|
|
725
598
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
726
599
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
727
600
|
if (!this.validateReq(req, res))
|
|
@@ -737,7 +610,6 @@ export class Frontend extends EventEmitter {
|
|
|
737
610
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
738
611
|
}
|
|
739
612
|
});
|
|
740
|
-
// Endpoint to download the matterbridge log
|
|
741
613
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
742
614
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
|
|
743
615
|
if (!this.validateReq(req, res))
|
|
@@ -754,14 +626,12 @@ export class Frontend extends EventEmitter {
|
|
|
754
626
|
}
|
|
755
627
|
res.type('text/plain');
|
|
756
628
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_LOGGER_FILE), 'matterbridge.log', (error) => {
|
|
757
|
-
/* istanbul ignore if */
|
|
758
629
|
if (error) {
|
|
759
630
|
this.log.error(`Error downloading log file ${MATTERBRIDGE_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
760
631
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
761
632
|
}
|
|
762
633
|
});
|
|
763
634
|
});
|
|
764
|
-
// Endpoint to download the matter log
|
|
765
635
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
766
636
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
|
|
767
637
|
if (!this.validateReq(req, res))
|
|
@@ -778,14 +648,12 @@ export class Frontend extends EventEmitter {
|
|
|
778
648
|
}
|
|
779
649
|
res.type('text/plain');
|
|
780
650
|
res.download(path.join(os.tmpdir(), MATTER_LOGGER_FILE), 'matter.log', (error) => {
|
|
781
|
-
/* istanbul ignore if */
|
|
782
651
|
if (error) {
|
|
783
652
|
this.log.error(`Error downloading log file ${MATTER_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
784
653
|
res.status(500).send('Error downloading the matter log file');
|
|
785
654
|
}
|
|
786
655
|
});
|
|
787
656
|
});
|
|
788
|
-
// Endpoint to download the shelly log
|
|
789
657
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
790
658
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
791
659
|
if (!this.validateReq(req, res))
|
|
@@ -802,103 +670,87 @@ export class Frontend extends EventEmitter {
|
|
|
802
670
|
}
|
|
803
671
|
res.type('text/plain');
|
|
804
672
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
805
|
-
/* istanbul ignore if */
|
|
806
673
|
if (error) {
|
|
807
674
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
808
675
|
res.status(500).send('Error downloading Shelly system log file');
|
|
809
676
|
}
|
|
810
677
|
});
|
|
811
678
|
});
|
|
812
|
-
// Endpoint to download the matterbridge storage directory
|
|
813
679
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
814
680
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
815
681
|
if (!this.validateReq(req, res))
|
|
816
682
|
return;
|
|
817
683
|
await createZip(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), path.join(this.matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR));
|
|
818
684
|
res.download(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), `matterbridge.${NODE_STORAGE_DIR}.zip`, (error) => {
|
|
819
|
-
/* istanbul ignore if */
|
|
820
685
|
if (error) {
|
|
821
686
|
this.log.error(`Error downloading file ${`matterbridge.${NODE_STORAGE_DIR}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
822
687
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
823
688
|
}
|
|
824
689
|
});
|
|
825
690
|
});
|
|
826
|
-
// Endpoint to download the matter storage file
|
|
827
691
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
828
692
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
829
693
|
if (!this.validateReq(req, res))
|
|
830
694
|
return;
|
|
831
695
|
await createZip(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME));
|
|
832
696
|
res.download(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), `matterbridge.${MATTER_STORAGE_NAME}.zip`, (error) => {
|
|
833
|
-
/* istanbul ignore if */
|
|
834
697
|
if (error) {
|
|
835
698
|
this.log.error(`Error downloading the matter storage matterbridge.${MATTER_STORAGE_NAME}.zip: ${error instanceof Error ? error.message : error}`);
|
|
836
699
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
837
700
|
}
|
|
838
701
|
});
|
|
839
702
|
});
|
|
840
|
-
// Endpoint to download the matterbridge plugin directory
|
|
841
703
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
842
704
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
843
705
|
if (!this.validateReq(req, res))
|
|
844
706
|
return;
|
|
845
707
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
846
708
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
847
|
-
/* istanbul ignore if */
|
|
848
709
|
if (error) {
|
|
849
710
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
850
711
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
851
712
|
}
|
|
852
713
|
});
|
|
853
714
|
});
|
|
854
|
-
// Endpoint to download the matterbridge plugin config files
|
|
855
715
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
856
716
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
857
717
|
if (!this.validateReq(req, res))
|
|
858
718
|
return;
|
|
859
719
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
860
720
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
861
|
-
/* istanbul ignore if */
|
|
862
721
|
if (error) {
|
|
863
722
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
864
723
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
865
724
|
}
|
|
866
725
|
});
|
|
867
726
|
});
|
|
868
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
869
727
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
870
728
|
this.log.debug('The frontend sent /api/download-backup');
|
|
871
729
|
if (!this.validateReq(req, res))
|
|
872
730
|
return;
|
|
873
731
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
874
|
-
/* istanbul ignore if */
|
|
875
732
|
if (error) {
|
|
876
733
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
877
734
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
878
735
|
}
|
|
879
736
|
});
|
|
880
737
|
});
|
|
881
|
-
// Endpoint to upload a package
|
|
882
738
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
883
739
|
if (!this.validateReq(req, res))
|
|
884
740
|
return;
|
|
885
741
|
const { filename } = req.body;
|
|
886
742
|
const file = req.file;
|
|
887
|
-
/* istanbul ignore if */
|
|
888
743
|
if (!file || !filename) {
|
|
889
744
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
890
745
|
res.status(400).send('Invalid request: file and filename are required');
|
|
891
746
|
return;
|
|
892
747
|
}
|
|
893
748
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
894
|
-
// Define the path where the plugin file will be saved
|
|
895
749
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
896
750
|
try {
|
|
897
|
-
// Move the uploaded file to the specified path
|
|
898
751
|
const fs = await import('node:fs');
|
|
899
752
|
await fs.promises.rename(file.path, filePath);
|
|
900
753
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
901
|
-
// Install the plugin package
|
|
902
754
|
if (filename.endsWith('.tgz')) {
|
|
903
755
|
const { spawnCommand } = await import('./spawn.js');
|
|
904
756
|
if (await spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], 'install', filename)) {
|
|
@@ -926,7 +778,6 @@ export class Frontend extends EventEmitter {
|
|
|
926
778
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
927
779
|
}
|
|
928
780
|
});
|
|
929
|
-
// Fallback for routing (must be the last route)
|
|
930
781
|
this.expressApp.use((req, res) => {
|
|
931
782
|
const filePath = path.resolve(this.matterbridge.rootDirectory, 'apps', 'frontend', 'build');
|
|
932
783
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html in ${filePath} as fallback`);
|
|
@@ -937,16 +788,13 @@ export class Frontend extends EventEmitter {
|
|
|
937
788
|
async stop() {
|
|
938
789
|
this.log.debug('Stopping the frontend...');
|
|
939
790
|
const ws = await import('ws');
|
|
940
|
-
// Remove listeners from the express app
|
|
941
791
|
if (this.expressApp) {
|
|
942
792
|
this.expressApp.removeAllListeners();
|
|
943
793
|
this.expressApp = undefined;
|
|
944
794
|
this.log.debug('Frontend app closed successfully');
|
|
945
795
|
}
|
|
946
|
-
// Close the WebSocket server
|
|
947
796
|
if (this.webSocketServer) {
|
|
948
797
|
this.log.debug('Closing WebSocket server...');
|
|
949
|
-
// Close all active connections
|
|
950
798
|
this.webSocketServer.clients.forEach((client) => {
|
|
951
799
|
if (client.readyState === ws.WebSocket.OPEN) {
|
|
952
800
|
client.close();
|
|
@@ -954,9 +802,7 @@ export class Frontend extends EventEmitter {
|
|
|
954
802
|
});
|
|
955
803
|
await withTimeout(new Promise((resolve) => {
|
|
956
804
|
this.webSocketServer?.close((error) => {
|
|
957
|
-
// istanbul ignore if
|
|
958
805
|
if (error) {
|
|
959
|
-
// istanbul ignore next
|
|
960
806
|
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
961
807
|
}
|
|
962
808
|
else {
|
|
@@ -969,27 +815,8 @@ export class Frontend extends EventEmitter {
|
|
|
969
815
|
this.webSocketServer.removeAllListeners();
|
|
970
816
|
this.webSocketServer = undefined;
|
|
971
817
|
}
|
|
972
|
-
// Close the http server
|
|
973
818
|
if (this.httpServer) {
|
|
974
819
|
this.log.debug('Closing http server...');
|
|
975
|
-
/*
|
|
976
|
-
await withTimeout(
|
|
977
|
-
new Promise<void>((resolve) => {
|
|
978
|
-
this.httpServer?.close((error) => {
|
|
979
|
-
if (error) {
|
|
980
|
-
// istanbul ignore next
|
|
981
|
-
this.log.error(`Error closing http server: ${error}`);
|
|
982
|
-
} else {
|
|
983
|
-
this.log.debug('Http server closed successfully');
|
|
984
|
-
this.emit('server_stopped');
|
|
985
|
-
}
|
|
986
|
-
resolve();
|
|
987
|
-
});
|
|
988
|
-
}),
|
|
989
|
-
5000,
|
|
990
|
-
false,
|
|
991
|
-
);
|
|
992
|
-
*/
|
|
993
820
|
this.httpServer.close();
|
|
994
821
|
this.log.debug('Http server closed successfully');
|
|
995
822
|
this.listening = false;
|
|
@@ -998,27 +825,8 @@ export class Frontend extends EventEmitter {
|
|
|
998
825
|
this.httpServer = undefined;
|
|
999
826
|
this.log.debug('Frontend http server closed successfully');
|
|
1000
827
|
}
|
|
1001
|
-
// Close the https server
|
|
1002
828
|
if (this.httpsServer) {
|
|
1003
829
|
this.log.debug('Closing https server...');
|
|
1004
|
-
/*
|
|
1005
|
-
await withTimeout(
|
|
1006
|
-
new Promise<void>((resolve) => {
|
|
1007
|
-
this.httpsServer?.close((error) => {
|
|
1008
|
-
if (error) {
|
|
1009
|
-
// istanbul ignore next
|
|
1010
|
-
this.log.error(`Error closing https server: ${error}`);
|
|
1011
|
-
} else {
|
|
1012
|
-
this.log.debug('Https server closed successfully');
|
|
1013
|
-
this.emit('server_stopped');
|
|
1014
|
-
}
|
|
1015
|
-
resolve();
|
|
1016
|
-
});
|
|
1017
|
-
}),
|
|
1018
|
-
5000,
|
|
1019
|
-
false,
|
|
1020
|
-
);
|
|
1021
|
-
*/
|
|
1022
830
|
this.httpsServer.close();
|
|
1023
831
|
this.log.debug('Https server closed successfully');
|
|
1024
832
|
this.listening = false;
|
|
@@ -1029,13 +837,7 @@ export class Frontend extends EventEmitter {
|
|
|
1029
837
|
}
|
|
1030
838
|
this.log.debug('Frontend stopped successfully');
|
|
1031
839
|
}
|
|
1032
|
-
/**
|
|
1033
|
-
* Retrieves the api settings data.
|
|
1034
|
-
*
|
|
1035
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
1036
|
-
*/
|
|
1037
840
|
async getApiSettings() {
|
|
1038
|
-
// Update the variable system information properties
|
|
1039
841
|
this.matterbridge.systemInformation.totalMemory = formatBytes(os.totalmem());
|
|
1040
842
|
this.matterbridge.systemInformation.freeMemory = formatBytes(os.freemem());
|
|
1041
843
|
this.matterbridge.systemInformation.systemUptime = formatUptime(os.uptime());
|
|
@@ -1045,7 +847,6 @@ export class Frontend extends EventEmitter {
|
|
|
1045
847
|
this.matterbridge.systemInformation.rss = formatBytes(process.memoryUsage().rss);
|
|
1046
848
|
this.matterbridge.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
|
|
1047
849
|
this.matterbridge.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
|
|
1048
|
-
// Create the matterbridge information
|
|
1049
850
|
const info = {
|
|
1050
851
|
homeDirectory: this.matterbridge.homeDirectory,
|
|
1051
852
|
rootDirectory: this.matterbridge.rootDirectory,
|
|
@@ -1081,15 +882,9 @@ export class Frontend extends EventEmitter {
|
|
|
1081
882
|
};
|
|
1082
883
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: info };
|
|
1083
884
|
}
|
|
1084
|
-
/**
|
|
1085
|
-
* Retrieves the reachable attribute.
|
|
1086
|
-
*
|
|
1087
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
1088
|
-
* @returns {boolean} The reachable attribute.
|
|
1089
|
-
*/
|
|
1090
885
|
getReachability(device) {
|
|
1091
886
|
if (this.matterbridge.hasCleanupStarted)
|
|
1092
|
-
return false;
|
|
887
|
+
return false;
|
|
1093
888
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1094
889
|
return false;
|
|
1095
890
|
if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
|
|
@@ -1100,15 +895,9 @@ export class Frontend extends EventEmitter {
|
|
|
1100
895
|
return true;
|
|
1101
896
|
return false;
|
|
1102
897
|
}
|
|
1103
|
-
/**
|
|
1104
|
-
* Retrieves the power source attribute.
|
|
1105
|
-
*
|
|
1106
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
1107
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
1108
|
-
*/
|
|
1109
898
|
getPowerSource(endpoint) {
|
|
1110
899
|
if (this.matterbridge.hasCleanupStarted)
|
|
1111
|
-
return undefined;
|
|
900
|
+
return undefined;
|
|
1112
901
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
1113
902
|
return undefined;
|
|
1114
903
|
const powerSource = (device) => {
|
|
@@ -1123,25 +912,16 @@ export class Frontend extends EventEmitter {
|
|
|
1123
912
|
}
|
|
1124
913
|
return;
|
|
1125
914
|
};
|
|
1126
|
-
// Root endpoint
|
|
1127
915
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
1128
916
|
return powerSource(endpoint);
|
|
1129
|
-
// Child endpoints
|
|
1130
917
|
for (const child of endpoint.getChildEndpoints()) {
|
|
1131
|
-
// istanbul ignore else
|
|
1132
918
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
1133
919
|
return powerSource(child);
|
|
1134
920
|
}
|
|
1135
921
|
}
|
|
1136
|
-
/**
|
|
1137
|
-
* Retrieves the battery level attribute.
|
|
1138
|
-
*
|
|
1139
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
1140
|
-
* @returns {number | undefined} The battery level attribute.
|
|
1141
|
-
*/
|
|
1142
922
|
getBatteryLevel(endpoint) {
|
|
1143
923
|
if (this.matterbridge.hasCleanupStarted)
|
|
1144
|
-
return undefined;
|
|
924
|
+
return undefined;
|
|
1145
925
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
1146
926
|
return undefined;
|
|
1147
927
|
const batteryLevel = (device) => {
|
|
@@ -1152,27 +932,16 @@ export class Frontend extends EventEmitter {
|
|
|
1152
932
|
}
|
|
1153
933
|
return undefined;
|
|
1154
934
|
};
|
|
1155
|
-
// Root endpoint
|
|
1156
935
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
1157
936
|
return batteryLevel(endpoint);
|
|
1158
|
-
// Child endpoints
|
|
1159
937
|
for (const child of endpoint.getChildEndpoints()) {
|
|
1160
|
-
// istanbul ignore else
|
|
1161
938
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
1162
939
|
return batteryLevel(child);
|
|
1163
940
|
}
|
|
1164
941
|
}
|
|
1165
|
-
/**
|
|
1166
|
-
* Retrieves the cluster text description from a given device.
|
|
1167
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
1168
|
-
*
|
|
1169
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
1170
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
1171
|
-
*/
|
|
1172
942
|
getClusterTextFromDevice(device) {
|
|
1173
943
|
if (this.matterbridge.hasCleanupStarted)
|
|
1174
|
-
return '';
|
|
1175
|
-
// istanbul ignore else
|
|
944
|
+
return '';
|
|
1176
945
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1177
946
|
return '';
|
|
1178
947
|
const getUserLabel = (device) => {
|
|
@@ -1182,7 +951,6 @@ export class Frontend extends EventEmitter {
|
|
|
1182
951
|
if (composed)
|
|
1183
952
|
return 'Composed: ' + composed.value;
|
|
1184
953
|
}
|
|
1185
|
-
// istanbul ignore next cause is not reachable
|
|
1186
954
|
return '';
|
|
1187
955
|
};
|
|
1188
956
|
const getFixedLabel = (device) => {
|
|
@@ -1192,13 +960,11 @@ export class Frontend extends EventEmitter {
|
|
|
1192
960
|
if (composed)
|
|
1193
961
|
return 'Composed: ' + composed.value;
|
|
1194
962
|
}
|
|
1195
|
-
// istanbul ignore next cause is not reacheable
|
|
1196
963
|
return '';
|
|
1197
964
|
};
|
|
1198
965
|
let attributes = '';
|
|
1199
966
|
let supportedModes = [];
|
|
1200
967
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1201
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
1202
968
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1203
969
|
return;
|
|
1204
970
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1288,17 +1054,11 @@ export class Frontend extends EventEmitter {
|
|
|
1288
1054
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1289
1055
|
attributes += `${getUserLabel(device)} `;
|
|
1290
1056
|
});
|
|
1291
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1292
1057
|
return attributes.trimStart().trimEnd();
|
|
1293
1058
|
}
|
|
1294
|
-
/**
|
|
1295
|
-
* Retrieves the registered plugins sanitized for res.json().
|
|
1296
|
-
*
|
|
1297
|
-
* @returns {ApiPlugin[]} An array of BaseRegisteredPlugin.
|
|
1298
|
-
*/
|
|
1299
1059
|
getPlugins() {
|
|
1300
1060
|
if (this.matterbridge.hasCleanupStarted)
|
|
1301
|
-
return [];
|
|
1061
|
+
return [];
|
|
1302
1062
|
const plugins = [];
|
|
1303
1063
|
for (const plugin of this.matterbridge.plugins.array()) {
|
|
1304
1064
|
plugins.push({
|
|
@@ -1326,27 +1086,18 @@ export class Frontend extends EventEmitter {
|
|
|
1326
1086
|
schemaJson: plugin.schemaJson,
|
|
1327
1087
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1328
1088
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1329
|
-
// Childbridge mode specific data
|
|
1330
1089
|
matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
|
|
1331
1090
|
});
|
|
1332
1091
|
}
|
|
1333
1092
|
return plugins;
|
|
1334
1093
|
}
|
|
1335
|
-
/**
|
|
1336
|
-
* Retrieves the devices from Matterbridge.
|
|
1337
|
-
*
|
|
1338
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1339
|
-
* @returns {ApiDevice[]} An array of ApiDevices for the frontend.
|
|
1340
|
-
*/
|
|
1341
1094
|
getDevices(pluginName) {
|
|
1342
1095
|
if (this.matterbridge.hasCleanupStarted)
|
|
1343
|
-
return [];
|
|
1096
|
+
return [];
|
|
1344
1097
|
const devices = [];
|
|
1345
1098
|
for (const device of this.matterbridge.devices.array()) {
|
|
1346
|
-
// Filter by pluginName if provided
|
|
1347
1099
|
if (pluginName && pluginName !== device.plugin)
|
|
1348
1100
|
continue;
|
|
1349
|
-
// Check if the device has the required properties
|
|
1350
1101
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1351
1102
|
continue;
|
|
1352
1103
|
devices.push({
|
|
@@ -1367,40 +1118,24 @@ export class Frontend extends EventEmitter {
|
|
|
1367
1118
|
}
|
|
1368
1119
|
return devices;
|
|
1369
1120
|
}
|
|
1370
|
-
/**
|
|
1371
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1372
|
-
*
|
|
1373
|
-
* Response for /api/clusters
|
|
1374
|
-
*
|
|
1375
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1376
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1377
|
-
* @returns {ApiClusters | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1378
|
-
*/
|
|
1379
1121
|
getClusters(pluginName, endpointNumber) {
|
|
1380
1122
|
if (this.matterbridge.hasCleanupStarted)
|
|
1381
|
-
return;
|
|
1123
|
+
return;
|
|
1382
1124
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1383
1125
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.maybeId || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1384
1126
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1385
1127
|
return;
|
|
1386
1128
|
}
|
|
1387
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1388
|
-
// Get the device types from the main endpoint
|
|
1389
1129
|
const deviceTypes = [];
|
|
1390
1130
|
const clusters = [];
|
|
1391
1131
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1392
1132
|
deviceTypes.push(d.deviceType);
|
|
1393
1133
|
});
|
|
1394
|
-
// Get the clusters from the main endpoint
|
|
1395
1134
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1396
1135
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1397
1136
|
return;
|
|
1398
|
-
// istanbul ignore if cause is not reachable without the EveHistory cluster
|
|
1399
1137
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1400
1138
|
return;
|
|
1401
|
-
// console.log(
|
|
1402
|
-
// `${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}`,
|
|
1403
|
-
// );
|
|
1404
1139
|
clusters.push({
|
|
1405
1140
|
endpoint: endpoint.number.toString(),
|
|
1406
1141
|
number: endpoint.number,
|
|
@@ -1414,19 +1149,12 @@ export class Frontend extends EventEmitter {
|
|
|
1414
1149
|
attributeLocalValue: attributeValue,
|
|
1415
1150
|
});
|
|
1416
1151
|
});
|
|
1417
|
-
// Get the child endpoints
|
|
1418
1152
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1419
|
-
// if (childEndpoints.length === 0) {
|
|
1420
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1421
|
-
// }
|
|
1422
1153
|
childEndpoints.forEach((childEndpoint) => {
|
|
1423
|
-
// istanbul ignore if cause is not reachable: should never happen but ...
|
|
1424
1154
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1425
1155
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1426
1156
|
return;
|
|
1427
1157
|
}
|
|
1428
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1429
|
-
// Get the device types of the child endpoint
|
|
1430
1158
|
const deviceTypes = [];
|
|
1431
1159
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1432
1160
|
deviceTypes.push(d.deviceType);
|
|
@@ -1434,13 +1162,9 @@ export class Frontend extends EventEmitter {
|
|
|
1434
1162
|
childEndpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1435
1163
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1436
1164
|
return;
|
|
1437
|
-
// istanbul ignore if cause is not reachable without the EveHistory cluster
|
|
1438
1165
|
if (clusterName === 'EveHistory' &&
|
|
1439
1166
|
['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1440
1167
|
return;
|
|
1441
|
-
// console.log(
|
|
1442
|
-
// `${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}`,
|
|
1443
|
-
// );
|
|
1444
1168
|
clusters.push({
|
|
1445
1169
|
endpoint: childEndpoint.number.toString(),
|
|
1446
1170
|
number: childEndpoint.number,
|
|
@@ -1460,7 +1184,6 @@ export class Frontend extends EventEmitter {
|
|
|
1460
1184
|
async generateDiagnostic() {
|
|
1461
1185
|
this.log.debug('Generating diagnostic...');
|
|
1462
1186
|
const serverNodes = [];
|
|
1463
|
-
// istanbul ignore else
|
|
1464
1187
|
if (this.matterbridge.bridgeMode === 'bridge') {
|
|
1465
1188
|
if (this.matterbridge.serverNode)
|
|
1466
1189
|
serverNodes.push(this.matterbridge.serverNode);
|
|
@@ -1471,7 +1194,6 @@ export class Frontend extends EventEmitter {
|
|
|
1471
1194
|
serverNodes.push(plugin.serverNode);
|
|
1472
1195
|
}
|
|
1473
1196
|
}
|
|
1474
|
-
// istanbul ignore next
|
|
1475
1197
|
for (const device of this.matterbridge.devices.array()) {
|
|
1476
1198
|
if (device.serverNode)
|
|
1477
1199
|
serverNodes.push(device.serverNode);
|
|
@@ -1495,15 +1217,8 @@ export class Frontend extends EventEmitter {
|
|
|
1495
1217
|
values: [...serverNodes],
|
|
1496
1218
|
})));
|
|
1497
1219
|
delete Logger.destinations.diagnostic;
|
|
1498
|
-
await wait(500);
|
|
1220
|
+
await wait(500);
|
|
1499
1221
|
}
|
|
1500
|
-
/**
|
|
1501
|
-
* Handles incoming websocket api request messages from the Matterbridge frontend.
|
|
1502
|
-
*
|
|
1503
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1504
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1505
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1506
|
-
*/
|
|
1507
1222
|
async wsMessageHandler(client, message) {
|
|
1508
1223
|
let data;
|
|
1509
1224
|
const sendResponse = (data) => {
|
|
@@ -1521,7 +1236,6 @@ export class Frontend extends EventEmitter {
|
|
|
1521
1236
|
client.send(JSON.stringify(data));
|
|
1522
1237
|
}
|
|
1523
1238
|
else {
|
|
1524
|
-
// istanbul ignore next cause is only a safety check
|
|
1525
1239
|
this.log.error('Cannot send api response, client not connected');
|
|
1526
1240
|
}
|
|
1527
1241
|
};
|
|
@@ -1530,7 +1244,7 @@ export class Frontend extends EventEmitter {
|
|
|
1530
1244
|
if (!isValidNumber(data.id) ||
|
|
1531
1245
|
!isValidString(data.dst) ||
|
|
1532
1246
|
!isValidString(data.src) ||
|
|
1533
|
-
!isValidString(data.method)
|
|
1247
|
+
!isValidString(data.method) ||
|
|
1534
1248
|
data.dst !== 'Matterbridge') {
|
|
1535
1249
|
this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
|
|
1536
1250
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
|
|
@@ -1588,22 +1302,7 @@ export class Frontend extends EventEmitter {
|
|
|
1588
1302
|
}
|
|
1589
1303
|
this.wssSendSnackbarMessage(`Adding plugin ${data.params.pluginNameOrPath}...`, 5);
|
|
1590
1304
|
this.log.debug(`Adding plugin ${data.params.pluginNameOrPath}...`);
|
|
1591
|
-
data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
|
|
1592
|
-
/*
|
|
1593
|
-
const plugin = (await this.server.fetch({ type: 'plugins_add', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginNameOrPath } }, 5000)).response.plugin;
|
|
1594
|
-
if (plugin) {
|
|
1595
|
-
this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
|
|
1596
|
-
await this.server.fetch({ type: 'plugins_load', src: this.server.name, dst: 'plugins', params: { plugin: plugin.name } }, 5000);
|
|
1597
|
-
this.wssSendRestartRequired();
|
|
1598
|
-
this.wssSendRefreshRequired('plugins');
|
|
1599
|
-
this.wssSendRefreshRequired('devices');
|
|
1600
|
-
this.wssSendSnackbarMessage(`Loaded plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
|
|
1601
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1602
|
-
} else {
|
|
1603
|
-
this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
|
|
1604
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
|
|
1605
|
-
}
|
|
1606
|
-
*/
|
|
1305
|
+
data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
|
|
1607
1306
|
const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
|
|
1608
1307
|
if (plugin) {
|
|
1609
1308
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1616,7 +1315,7 @@ export class Frontend extends EventEmitter {
|
|
|
1616
1315
|
this.wssSendSnackbarMessage(`Loaded plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
|
|
1617
1316
|
return;
|
|
1618
1317
|
})
|
|
1619
|
-
.catch(
|
|
1318
|
+
.catch((_error) => { });
|
|
1620
1319
|
}
|
|
1621
1320
|
else {
|
|
1622
1321
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
|
|
@@ -1630,10 +1329,6 @@ export class Frontend extends EventEmitter {
|
|
|
1630
1329
|
}
|
|
1631
1330
|
this.wssSendSnackbarMessage(`Removing plugin ${data.params.pluginName}...`, 5);
|
|
1632
1331
|
this.log.debug(`Removing plugin ${data.params.pluginName}...`);
|
|
1633
|
-
/*
|
|
1634
|
-
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);
|
|
1635
|
-
await this.server.fetch({ type: 'plugins_remove', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginName } }, 5000);
|
|
1636
|
-
*/
|
|
1637
1332
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1638
1333
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
1639
1334
|
await this.matterbridge.plugins.remove(data.params.pluginName);
|
|
@@ -1661,11 +1356,9 @@ export class Frontend extends EventEmitter {
|
|
|
1661
1356
|
this.wssSendSnackbarMessage(`Enabled plugin ${data.params.pluginName}`, 5, 'success');
|
|
1662
1357
|
setImmediate(async () => {
|
|
1663
1358
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true);
|
|
1664
|
-
// @ts-expect-error Accessing private method
|
|
1665
1359
|
if (plugin.serverNode)
|
|
1666
1360
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1667
1361
|
for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode))
|
|
1668
|
-
// @ts-expect-error Accessing private method
|
|
1669
1362
|
await this.matterbridge.startServerNode(device.serverNode);
|
|
1670
1363
|
this.wssSendSnackbarMessage(`Started plugin ${localData.params.pluginName}`, 5, 'success');
|
|
1671
1364
|
this.wssSendRefreshRequired('plugins');
|
|
@@ -1679,16 +1372,12 @@ export class Frontend extends EventEmitter {
|
|
|
1679
1372
|
return;
|
|
1680
1373
|
}
|
|
1681
1374
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1682
|
-
// Stop server nodes devices first
|
|
1683
1375
|
for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode)) {
|
|
1684
|
-
// @ts-expect-error Accessing private method
|
|
1685
1376
|
await this.matterbridge.stopServerNode(device.serverNode);
|
|
1686
1377
|
device.serverNode = undefined;
|
|
1687
1378
|
}
|
|
1688
|
-
// Then shutdown plugin removing devices, disable it and stop plugin server node
|
|
1689
1379
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
1690
1380
|
await this.matterbridge.plugins.disable(data.params.pluginName);
|
|
1691
|
-
// @ts-expect-error Accessing private method
|
|
1692
1381
|
if (plugin.serverNode)
|
|
1693
1382
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1694
1383
|
plugin.serverNode = undefined;
|
|
@@ -1705,37 +1394,30 @@ export class Frontend extends EventEmitter {
|
|
|
1705
1394
|
this.wssSendSnackbarMessage(`Restarting plugin ${data.params.pluginName}`, 5, 'info');
|
|
1706
1395
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1707
1396
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
|
|
1708
|
-
// Stop server nodes
|
|
1709
1397
|
if (plugin.serverNode) {
|
|
1710
|
-
// @ts-expect-error Accessing private method
|
|
1711
1398
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1712
1399
|
plugin.serverNode = undefined;
|
|
1713
1400
|
}
|
|
1714
1401
|
for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name)) {
|
|
1715
|
-
// @ts-expect-error Accessing private method
|
|
1716
1402
|
if (device.serverNode)
|
|
1717
1403
|
await this.matterbridge.stopServerNode(device.serverNode);
|
|
1718
1404
|
device.serverNode = undefined;
|
|
1719
1405
|
this.log.debug(`Removing device ${device.deviceName} from plugin ${plugin.name}`);
|
|
1720
1406
|
this.matterbridge.devices.remove(device);
|
|
1721
1407
|
}
|
|
1722
|
-
// @ts-expect-error Accessing private method
|
|
1723
1408
|
if (plugin.type === 'DynamicPlatform' && !plugin.locked)
|
|
1724
1409
|
await this.matterbridge.createDynamicPlugin(plugin);
|
|
1725
1410
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
|
|
1726
|
-
plugin.restartRequired = false;
|
|
1411
|
+
plugin.restartRequired = false;
|
|
1727
1412
|
let needRestart = 0;
|
|
1728
1413
|
for (const plugin of this.matterbridge.plugins) {
|
|
1729
1414
|
if (plugin.restartRequired)
|
|
1730
1415
|
needRestart++;
|
|
1731
1416
|
}
|
|
1732
1417
|
if (needRestart === 0)
|
|
1733
|
-
this.wssSendRestartNotRequired(true);
|
|
1734
|
-
// Start server nodes
|
|
1735
|
-
// @ts-expect-error Accessing private method
|
|
1418
|
+
this.wssSendRestartNotRequired(true);
|
|
1736
1419
|
if (plugin.serverNode)
|
|
1737
1420
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1738
|
-
// @ts-expect-error Accessing private method
|
|
1739
1421
|
for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode))
|
|
1740
1422
|
await this.matterbridge.startServerNode(device.serverNode);
|
|
1741
1423
|
this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
|
|
@@ -1767,54 +1449,18 @@ export class Frontend extends EventEmitter {
|
|
|
1767
1449
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1768
1450
|
}
|
|
1769
1451
|
else if (data.method === '/api/shellysysupdate') {
|
|
1770
|
-
/*
|
|
1771
|
-
const { triggerShellySysUpdate } = await import('./shelly.js');
|
|
1772
|
-
triggerShellySysUpdate(this.matterbridge);
|
|
1773
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1774
|
-
*/
|
|
1775
1452
|
}
|
|
1776
1453
|
else if (data.method === '/api/shellymainupdate') {
|
|
1777
|
-
/*
|
|
1778
|
-
const { triggerShellyMainUpdate } = await import('./shelly.js');
|
|
1779
|
-
triggerShellyMainUpdate(this.matterbridge);
|
|
1780
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1781
|
-
*/
|
|
1782
1454
|
}
|
|
1783
1455
|
else if (data.method === '/api/shellycreatesystemlog') {
|
|
1784
|
-
/*
|
|
1785
|
-
const { createShellySystemLog } = await import('./shelly.js');
|
|
1786
|
-
createShellySystemLog(this.matterbridge);
|
|
1787
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1788
|
-
*/
|
|
1789
1456
|
}
|
|
1790
1457
|
else if (data.method === '/api/shellynetconfig') {
|
|
1791
|
-
/*
|
|
1792
|
-
this.log.debug('/api/shellynetconfig:', data.params);
|
|
1793
|
-
const { triggerShellyChangeIp } = await import('./shelly.js');
|
|
1794
|
-
triggerShellyChangeIp(this.matterbridge, data.params as { type: 'static' | 'dhcp'; ip: string; subnet: string; gateway: string; dns: string });
|
|
1795
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1796
|
-
*/
|
|
1797
1458
|
}
|
|
1798
1459
|
else if (data.method === '/api/softreset') {
|
|
1799
|
-
/*
|
|
1800
|
-
const { triggerShellySoftReset } = await import('./shelly.js');
|
|
1801
|
-
triggerShellySoftReset(this.matterbridge);
|
|
1802
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1803
|
-
*/
|
|
1804
1460
|
}
|
|
1805
1461
|
else if (data.method === '/api/hardreset') {
|
|
1806
|
-
/*
|
|
1807
|
-
const { triggerShellyHardReset } = await import('./shelly.js');
|
|
1808
|
-
triggerShellyHardReset(this.matterbridge);
|
|
1809
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1810
|
-
*/
|
|
1811
1462
|
}
|
|
1812
1463
|
else if (data.method === '/api/reboot') {
|
|
1813
|
-
/*
|
|
1814
|
-
const { triggerShellyReboot } = await import('./shelly.js');
|
|
1815
|
-
triggerShellyReboot(this.matterbridge);
|
|
1816
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1817
|
-
*/
|
|
1818
1464
|
}
|
|
1819
1465
|
else if (data.method === '/api/restart') {
|
|
1820
1466
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
@@ -1954,7 +1600,6 @@ export class Frontend extends EventEmitter {
|
|
|
1954
1600
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/devices' });
|
|
1955
1601
|
return;
|
|
1956
1602
|
}
|
|
1957
|
-
// istanbul ignore next
|
|
1958
1603
|
const selectDeviceValues = !plugin.platform ? [] : plugin.platform.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1959
1604
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectDeviceValues });
|
|
1960
1605
|
}
|
|
@@ -1968,7 +1613,6 @@ export class Frontend extends EventEmitter {
|
|
|
1968
1613
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' });
|
|
1969
1614
|
return;
|
|
1970
1615
|
}
|
|
1971
|
-
// istanbul ignore next
|
|
1972
1616
|
const selectEntityValues = !plugin.platform ? [] : plugin.platform.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1973
1617
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectEntityValues });
|
|
1974
1618
|
}
|
|
@@ -2026,22 +1670,22 @@ export class Frontend extends EventEmitter {
|
|
|
2026
1670
|
if (isValidString(data.params.value, 4)) {
|
|
2027
1671
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
2028
1672
|
if (data.params.value === 'Debug') {
|
|
2029
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1673
|
+
await this.matterbridge.setLogLevel("debug");
|
|
2030
1674
|
}
|
|
2031
1675
|
else if (data.params.value === 'Info') {
|
|
2032
|
-
await this.matterbridge.setLogLevel("info"
|
|
1676
|
+
await this.matterbridge.setLogLevel("info");
|
|
2033
1677
|
}
|
|
2034
1678
|
else if (data.params.value === 'Notice') {
|
|
2035
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1679
|
+
await this.matterbridge.setLogLevel("notice");
|
|
2036
1680
|
}
|
|
2037
1681
|
else if (data.params.value === 'Warn') {
|
|
2038
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1682
|
+
await this.matterbridge.setLogLevel("warn");
|
|
2039
1683
|
}
|
|
2040
1684
|
else if (data.params.value === 'Error') {
|
|
2041
|
-
await this.matterbridge.setLogLevel("error"
|
|
1685
|
+
await this.matterbridge.setLogLevel("error");
|
|
2042
1686
|
}
|
|
2043
1687
|
else if (data.params.value === 'Fatal') {
|
|
2044
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1688
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
2045
1689
|
}
|
|
2046
1690
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
2047
1691
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -2052,7 +1696,6 @@ export class Frontend extends EventEmitter {
|
|
|
2052
1696
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
2053
1697
|
this.matterbridge.fileLogger = data.params.value;
|
|
2054
1698
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
2055
|
-
// Create the file logger for matterbridge
|
|
2056
1699
|
if (data.params.value)
|
|
2057
1700
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), await this.matterbridge.getLogLevel(), true);
|
|
2058
1701
|
else
|
|
@@ -2082,12 +1725,11 @@ export class Frontend extends EventEmitter {
|
|
|
2082
1725
|
Logger.level = MatterLogLevel.FATAL;
|
|
2083
1726
|
}
|
|
2084
1727
|
this.matterbridge.matterLogLevel = MatterLogLevel.names[Logger.level];
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
1728
|
+
let callbackLogLevel = "notice";
|
|
1729
|
+
if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
|
|
1730
|
+
callbackLogLevel = "info";
|
|
1731
|
+
if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
1732
|
+
callbackLogLevel = "debug";
|
|
2091
1733
|
AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
|
|
2092
1734
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
2093
1735
|
await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.level);
|
|
@@ -2139,7 +1781,6 @@ export class Frontend extends EventEmitter {
|
|
|
2139
1781
|
}
|
|
2140
1782
|
break;
|
|
2141
1783
|
case 'setmatterport':
|
|
2142
|
-
// eslint-disable-next-line no-case-declarations
|
|
2143
1784
|
const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
2144
1785
|
if (isValidNumber(port, 5540, 5600)) {
|
|
2145
1786
|
this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
|
|
@@ -2159,7 +1800,6 @@ export class Frontend extends EventEmitter {
|
|
|
2159
1800
|
}
|
|
2160
1801
|
break;
|
|
2161
1802
|
case 'setmatterdiscriminator':
|
|
2162
|
-
// eslint-disable-next-line no-case-declarations
|
|
2163
1803
|
const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
2164
1804
|
if (isValidNumber(discriminator, 0, 4095)) {
|
|
2165
1805
|
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
|
|
@@ -2185,7 +1825,6 @@ export class Frontend extends EventEmitter {
|
|
|
2185
1825
|
}
|
|
2186
1826
|
break;
|
|
2187
1827
|
case 'setmatterpasscode':
|
|
2188
|
-
// eslint-disable-next-line no-case-declarations
|
|
2189
1828
|
const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
2190
1829
|
if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
|
|
2191
1830
|
this.matterbridge.passcode = passcode;
|
|
@@ -2237,19 +1876,15 @@ export class Frontend extends EventEmitter {
|
|
|
2237
1876
|
return;
|
|
2238
1877
|
}
|
|
2239
1878
|
const config = plugin.configJson;
|
|
2240
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2241
1879
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
2242
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
2243
1880
|
if (select === 'serial')
|
|
2244
1881
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
2245
1882
|
if (select === 'name')
|
|
2246
1883
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
2247
1884
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
2248
|
-
// Remove postfix from the serial if it exists
|
|
2249
1885
|
if (config.postfix) {
|
|
2250
1886
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
2251
1887
|
}
|
|
2252
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
2253
1888
|
if (isValidArray(config.whiteList, 1)) {
|
|
2254
1889
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
2255
1890
|
config.whiteList.push(data.params.serial);
|
|
@@ -2258,7 +1893,6 @@ export class Frontend extends EventEmitter {
|
|
|
2258
1893
|
config.whiteList.push(data.params.name);
|
|
2259
1894
|
}
|
|
2260
1895
|
}
|
|
2261
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
2262
1896
|
if (isValidArray(config.blackList, 1)) {
|
|
2263
1897
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
2264
1898
|
config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
|
|
@@ -2289,9 +1923,7 @@ export class Frontend extends EventEmitter {
|
|
|
2289
1923
|
return;
|
|
2290
1924
|
}
|
|
2291
1925
|
const config = plugin.configJson;
|
|
2292
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2293
1926
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
2294
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
2295
1927
|
if (select === 'serial')
|
|
2296
1928
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
2297
1929
|
if (select === 'name')
|
|
@@ -2300,7 +1932,6 @@ export class Frontend extends EventEmitter {
|
|
|
2300
1932
|
if (config.postfix) {
|
|
2301
1933
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
2302
1934
|
}
|
|
2303
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
2304
1935
|
if (isValidArray(config.whiteList, 1)) {
|
|
2305
1936
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
2306
1937
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
|
|
@@ -2309,7 +1940,6 @@ export class Frontend extends EventEmitter {
|
|
|
2309
1940
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
|
|
2310
1941
|
}
|
|
2311
1942
|
}
|
|
2312
|
-
// Add the serial to the blackList
|
|
2313
1943
|
if (isValidArray(config.blackList)) {
|
|
2314
1944
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
2315
1945
|
config.blackList.push(data.params.serial);
|
|
@@ -2332,7 +1962,6 @@ export class Frontend extends EventEmitter {
|
|
|
2332
1962
|
}
|
|
2333
1963
|
}
|
|
2334
1964
|
else {
|
|
2335
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2336
1965
|
const localData = data;
|
|
2337
1966
|
this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
|
|
2338
1967
|
sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
|
|
@@ -2342,46 +1971,23 @@ export class Frontend extends EventEmitter {
|
|
|
2342
1971
|
inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
|
|
2343
1972
|
}
|
|
2344
1973
|
}
|
|
2345
|
-
/**
|
|
2346
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
2347
|
-
*
|
|
2348
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
2349
|
-
* @param {string} time - The time string of the message
|
|
2350
|
-
* @param {string} name - The logger name of the message
|
|
2351
|
-
* @param {string} message - The content of the message.
|
|
2352
|
-
*
|
|
2353
|
-
* @remarks
|
|
2354
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
2355
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
2356
|
-
* The function sends the message to all connected clients.
|
|
2357
|
-
*/
|
|
2358
1974
|
wssSendLogMessage(level, time, name, message) {
|
|
2359
1975
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2360
1976
|
return;
|
|
2361
1977
|
if (!level || !time || !name || !message)
|
|
2362
1978
|
return;
|
|
2363
|
-
// Remove ANSI escape codes from the message
|
|
2364
|
-
// eslint-disable-next-line no-control-regex
|
|
2365
1979
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
2366
|
-
// Remove leading asterisks from the message
|
|
2367
1980
|
message = message.replace(/^\*+/, '');
|
|
2368
|
-
// Replace all occurrences of \t and \n
|
|
2369
1981
|
message = message.replace(/[\t\n]/g, '');
|
|
2370
|
-
// Remove non-printable characters
|
|
2371
|
-
// eslint-disable-next-line no-control-regex
|
|
2372
1982
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
2373
|
-
// Replace all occurrences of \" with "
|
|
2374
1983
|
message = message.replace(/\\"/g, '"');
|
|
2375
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
2376
1984
|
const maxContinuousLength = 100;
|
|
2377
1985
|
const keepStartLength = 20;
|
|
2378
1986
|
const keepEndLength = 20;
|
|
2379
|
-
// Split the message into words
|
|
2380
1987
|
if (level !== 'spawn') {
|
|
2381
1988
|
message = message
|
|
2382
1989
|
.split(' ')
|
|
2383
1990
|
.map((word) => {
|
|
2384
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2385
1991
|
if (word.length > maxContinuousLength) {
|
|
2386
1992
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2387
1993
|
}
|
|
@@ -2389,34 +1995,14 @@ export class Frontend extends EventEmitter {
|
|
|
2389
1995
|
})
|
|
2390
1996
|
.join(' ');
|
|
2391
1997
|
}
|
|
2392
|
-
// Send the message to all connected clients
|
|
2393
1998
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
|
|
2394
1999
|
}
|
|
2395
|
-
/**
|
|
2396
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2397
|
-
*
|
|
2398
|
-
* @param {string} changed - The changed value.
|
|
2399
|
-
* @param {Record<string, unknown>} params - Additional parameters to send with the message.
|
|
2400
|
-
* possible values for changed:
|
|
2401
|
-
* - 'settings' (when the bridge has started in bridge mode or childbridge mode and when update finds a new version)
|
|
2402
|
-
* - 'plugins'
|
|
2403
|
-
* - 'devices'
|
|
2404
|
-
* - 'matter' with param 'matter' (QRDiv component)
|
|
2405
|
-
* @param {ApiMatter} params.matter - The matter device that has changed. Required if changed is 'matter'.
|
|
2406
|
-
*/
|
|
2407
2000
|
wssSendRefreshRequired(changed, params) {
|
|
2408
2001
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2409
2002
|
return;
|
|
2410
2003
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
2411
|
-
// Send the message to all connected clients
|
|
2412
2004
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
|
|
2413
2005
|
}
|
|
2414
|
-
/**
|
|
2415
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2416
|
-
*
|
|
2417
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
|
|
2418
|
-
* @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
|
|
2419
|
-
*/
|
|
2420
2006
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
2421
2007
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2422
2008
|
return;
|
|
@@ -2425,14 +2011,8 @@ export class Frontend extends EventEmitter {
|
|
|
2425
2011
|
this.matterbridge.fixedRestartRequired = fixed;
|
|
2426
2012
|
if (snackbar === true)
|
|
2427
2013
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
2428
|
-
// Send the message to all connected clients
|
|
2429
2014
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
|
|
2430
2015
|
}
|
|
2431
|
-
/**
|
|
2432
|
-
* Sends a no need to restart WebSocket message to all connected clients.
|
|
2433
|
-
*
|
|
2434
|
-
* @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients. Default is true.
|
|
2435
|
-
*/
|
|
2436
2016
|
wssSendRestartNotRequired(snackbar = true) {
|
|
2437
2017
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2438
2018
|
return;
|
|
@@ -2440,35 +2020,20 @@ export class Frontend extends EventEmitter {
|
|
|
2440
2020
|
this.matterbridge.restartRequired = false;
|
|
2441
2021
|
if (snackbar === true)
|
|
2442
2022
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2443
|
-
// Send the message to all connected clients
|
|
2444
2023
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
|
|
2445
2024
|
}
|
|
2446
|
-
/**
|
|
2447
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
2448
|
-
*
|
|
2449
|
-
* @param {boolean} devVersion - If true, the update is for a development version. Default is false.
|
|
2450
|
-
*/
|
|
2451
2025
|
wssSendUpdateRequired(devVersion = false) {
|
|
2452
2026
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2453
2027
|
return;
|
|
2454
2028
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2455
2029
|
this.matterbridge.updateRequired = true;
|
|
2456
|
-
// Send the message to all connected clients
|
|
2457
2030
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
|
|
2458
2031
|
}
|
|
2459
|
-
/**
|
|
2460
|
-
* Sends a cpu update message to all connected clients.
|
|
2461
|
-
*
|
|
2462
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2463
|
-
* @param {number} processCpuUsage - The CPU usage percentage of the process to send.
|
|
2464
|
-
*/
|
|
2465
2032
|
wssSendCpuUpdate(cpuUsage, processCpuUsage) {
|
|
2466
2033
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2467
2034
|
return;
|
|
2468
|
-
// istanbul ignore else
|
|
2469
2035
|
if (hasParameter('debug'))
|
|
2470
2036
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2471
|
-
// Send the message to all connected clients
|
|
2472
2037
|
this.wssBroadcastMessage({
|
|
2473
2038
|
id: 0,
|
|
2474
2039
|
src: 'Matterbridge',
|
|
@@ -2478,24 +2043,11 @@ export class Frontend extends EventEmitter {
|
|
|
2478
2043
|
response: { cpuUsage: Math.round(cpuUsage * 100) / 100, processCpuUsage: Math.round(processCpuUsage * 100) / 100 },
|
|
2479
2044
|
});
|
|
2480
2045
|
}
|
|
2481
|
-
/**
|
|
2482
|
-
* Sends a memory update message to all connected clients.
|
|
2483
|
-
*
|
|
2484
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2485
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2486
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2487
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2488
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2489
|
-
* @param {string} external - The external memory in bytes.
|
|
2490
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2491
|
-
*/
|
|
2492
2046
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2493
2047
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2494
2048
|
return;
|
|
2495
|
-
// istanbul ignore else
|
|
2496
2049
|
if (hasParameter('debug'))
|
|
2497
2050
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2498
|
-
// Send the message to all connected clients
|
|
2499
2051
|
this.wssBroadcastMessage({
|
|
2500
2052
|
id: 0,
|
|
2501
2053
|
src: 'Matterbridge',
|
|
@@ -2505,73 +2057,29 @@ export class Frontend extends EventEmitter {
|
|
|
2505
2057
|
response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers },
|
|
2506
2058
|
});
|
|
2507
2059
|
}
|
|
2508
|
-
/**
|
|
2509
|
-
* Sends an uptime update message to all connected clients.
|
|
2510
|
-
*
|
|
2511
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2512
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2513
|
-
*/
|
|
2514
2060
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2515
2061
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2516
2062
|
return;
|
|
2517
|
-
// istanbul ignore else
|
|
2518
2063
|
if (hasParameter('debug'))
|
|
2519
2064
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2520
|
-
// Send the message to all connected clients
|
|
2521
2065
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
|
|
2522
2066
|
}
|
|
2523
|
-
/**
|
|
2524
|
-
* Sends an open snackbar message to all connected clients.
|
|
2525
|
-
*
|
|
2526
|
-
* @param {string} message - The message to send.
|
|
2527
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message. Default is 5 seconds.
|
|
2528
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the message.
|
|
2529
|
-
* possible values are: 'info', 'warning', 'error', 'success'. Default is 'info'.
|
|
2530
|
-
*
|
|
2531
|
-
* @remarks
|
|
2532
|
-
* If timeout is 0, the snackbar message will be displayed until closed by the user.
|
|
2533
|
-
*/
|
|
2534
2067
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2535
2068
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2536
2069
|
return;
|
|
2537
2070
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2538
|
-
// Send the message to all connected clients
|
|
2539
2071
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
|
|
2540
2072
|
}
|
|
2541
|
-
/**
|
|
2542
|
-
* Sends a close snackbar message to all connected clients.
|
|
2543
|
-
* It will close the snackbar message with the same message and timeout = 0.
|
|
2544
|
-
*
|
|
2545
|
-
* @param {string} message - The message to send.
|
|
2546
|
-
*/
|
|
2547
2073
|
wssSendCloseSnackbarMessage(message) {
|
|
2548
2074
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2549
2075
|
return;
|
|
2550
2076
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2551
|
-
// Send the message to all connected clients
|
|
2552
2077
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
|
|
2553
2078
|
}
|
|
2554
|
-
/**
|
|
2555
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2556
|
-
*
|
|
2557
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2558
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2559
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2560
|
-
* @param {EndpointNumber} number - The endpoint number where the attribute belongs.
|
|
2561
|
-
* @param {string} id - The endpoint id where the attribute belongs.
|
|
2562
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2563
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2564
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2565
|
-
*
|
|
2566
|
-
* @remarks
|
|
2567
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2568
|
-
* with the updated attribute information.
|
|
2569
|
-
*/
|
|
2570
2079
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
|
|
2571
2080
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2572
2081
|
return;
|
|
2573
2082
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2574
|
-
// Send the message to all connected clients
|
|
2575
2083
|
this.wssBroadcastMessage({
|
|
2576
2084
|
id: 0,
|
|
2577
2085
|
src: 'Matterbridge',
|
|
@@ -2581,25 +2089,16 @@ export class Frontend extends EventEmitter {
|
|
|
2581
2089
|
response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value },
|
|
2582
2090
|
});
|
|
2583
2091
|
}
|
|
2584
|
-
/**
|
|
2585
|
-
* Sends a message to all connected clients.
|
|
2586
|
-
* This is an helper function to send a broadcast message to all connected clients.
|
|
2587
|
-
*
|
|
2588
|
-
* @param {WsMessageBroadcast} msg - The message to send.
|
|
2589
|
-
*/
|
|
2590
2092
|
wssBroadcastMessage(msg) {
|
|
2591
2093
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2592
2094
|
return;
|
|
2593
|
-
// Send the message to all connected clients
|
|
2594
2095
|
const stringifiedMsg = JSON.stringify(msg);
|
|
2595
2096
|
if (msg.method !== 'log')
|
|
2596
2097
|
this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
|
|
2597
2098
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2598
|
-
// istanbul ignore else
|
|
2599
2099
|
if (client.readyState === client.OPEN) {
|
|
2600
2100
|
client.send(stringifiedMsg);
|
|
2601
2101
|
}
|
|
2602
2102
|
});
|
|
2603
2103
|
}
|
|
2604
2104
|
}
|
|
2605
|
-
//# sourceMappingURL=frontend.js.map
|