matterbridge 3.4.1 → 3.4.2-dev-20251202-c41a119
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/broadcastServer.js +0 -112
- package/dist/broadcastServerTypes.js +0 -24
- package/dist/cli.js +1 -97
- package/dist/cliEmitter.js +0 -37
- package/dist/cliHistory.js +0 -38
- package/dist/clusters/export.js +0 -2
- package/dist/deviceManager.js +1 -113
- package/dist/devices/airConditioner.js +0 -57
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/cooktop.js +0 -56
- package/dist/devices/dishwasher.js +0 -57
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.js +0 -5
- package/dist/devices/extractorHood.js +0 -43
- package/dist/devices/heatPump.js +2 -50
- package/dist/devices/laundryDryer.js +3 -62
- package/dist/devices/laundryWasher.js +4 -70
- package/dist/devices/microwaveOven.js +5 -88
- package/dist/devices/oven.js +0 -85
- package/dist/devices/refrigerator.js +0 -102
- package/dist/devices/roboticVacuumCleaner.js +9 -100
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/speaker.js +0 -84
- package/dist/devices/temperatureControl.js +3 -24
- package/dist/devices/waterHeater.js +2 -82
- package/dist/dgram/coap.js +13 -126
- package/dist/dgram/dgram.js +2 -114
- package/dist/dgram/mb_coap.js +3 -41
- package/dist/dgram/mb_mdns.js +15 -80
- package/dist/dgram/mdns.js +137 -299
- package/dist/dgram/multicast.js +1 -62
- package/dist/dgram/unicast.js +0 -54
- package/dist/frontend.js +35 -455
- package/dist/frontendTypes.js +0 -45
- package/dist/helpers.js +0 -53
- package/dist/index.js +0 -25
- package/dist/jestutils/export.js +0 -1
- package/dist/jestutils/jestHelpers.js +13 -368
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.js +0 -3
- package/dist/matter/types.js +0 -3
- package/dist/matterNode.js +8 -369
- package/dist/matterbridge.js +46 -807
- package/dist/matterbridgeAccessoryPlatform.js +0 -38
- package/dist/matterbridgeBehaviors.js +5 -68
- package/dist/matterbridgeDeviceTypes.js +14 -635
- package/dist/matterbridgeDynamicPlatform.js +0 -38
- package/dist/matterbridgeEndpoint.js +53 -1444
- package/dist/matterbridgeEndpointHelpers.js +20 -483
- package/dist/matterbridgeEndpointTypes.js +0 -25
- package/dist/matterbridgePlatform.js +1 -450
- package/dist/matterbridgeTypes.js +0 -26
- package/dist/pluginManager.js +5 -341
- package/dist/shelly.js +7 -178
- package/dist/storage/export.js +0 -1
- package/dist/update.js +1 -93
- package/dist/utils/colorUtils.js +2 -97
- package/dist/utils/commandLine.js +0 -60
- package/dist/utils/copyDirectory.js +0 -37
- package/dist/utils/createDirectory.js +0 -33
- package/dist/utils/createZip.js +2 -47
- package/dist/utils/deepCopy.js +0 -39
- package/dist/utils/deepEqual.js +1 -72
- package/dist/utils/error.js +0 -42
- package/dist/utils/export.js +0 -1
- package/dist/utils/format.js +0 -49
- package/dist/utils/hex.js +0 -124
- package/dist/utils/inspector.js +1 -69
- package/dist/utils/isvalid.js +0 -101
- package/dist/utils/network.js +5 -96
- package/dist/utils/spawn.js +1 -71
- package/dist/utils/tracker.js +1 -64
- package/dist/utils/wait.js +8 -60
- package/dist/workerGlobalPrefix.js +5 -37
- package/dist/workerTypes.js +0 -24
- package/dist/workers.js +4 -68
- package/frontend/build/assets/index.js +4 -4
- package/frontend/build/assets/vendor_mui.js +1 -1
- package/frontend/package.json +1 -1
- package/npm-shrinkwrap.json +14 -10
- package/package.json +1 -2
- package/dist/broadcastServer.d.ts +0 -136
- package/dist/broadcastServer.d.ts.map +0 -1
- package/dist/broadcastServer.js.map +0 -1
- package/dist/broadcastServerTypes.d.ts +0 -841
- package/dist/broadcastServerTypes.d.ts.map +0 -1
- package/dist/broadcastServerTypes.js.map +0 -1
- package/dist/cli.d.ts +0 -30
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cliEmitter.d.ts +0 -50
- package/dist/cliEmitter.d.ts.map +0 -1
- package/dist/cliEmitter.js.map +0 -1
- package/dist/cliHistory.d.ts +0 -48
- package/dist/cliHistory.d.ts.map +0 -1
- package/dist/cliHistory.js.map +0 -1
- package/dist/clusters/export.d.ts +0 -2
- package/dist/clusters/export.d.ts.map +0 -1
- package/dist/clusters/export.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -135
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/devices/airConditioner.d.ts +0 -98
- package/dist/devices/airConditioner.d.ts.map +0 -1
- package/dist/devices/airConditioner.js.map +0 -1
- package/dist/devices/batteryStorage.d.ts +0 -48
- package/dist/devices/batteryStorage.d.ts.map +0 -1
- package/dist/devices/batteryStorage.js.map +0 -1
- package/dist/devices/cooktop.d.ts +0 -61
- package/dist/devices/cooktop.d.ts.map +0 -1
- package/dist/devices/cooktop.js.map +0 -1
- package/dist/devices/dishwasher.d.ts +0 -71
- package/dist/devices/dishwasher.d.ts.map +0 -1
- package/dist/devices/dishwasher.js.map +0 -1
- package/dist/devices/evse.d.ts +0 -76
- package/dist/devices/evse.d.ts.map +0 -1
- package/dist/devices/evse.js.map +0 -1
- package/dist/devices/export.d.ts +0 -17
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.js.map +0 -1
- package/dist/devices/extractorHood.d.ts +0 -46
- package/dist/devices/extractorHood.d.ts.map +0 -1
- package/dist/devices/extractorHood.js.map +0 -1
- package/dist/devices/heatPump.d.ts +0 -47
- package/dist/devices/heatPump.d.ts.map +0 -1
- package/dist/devices/heatPump.js.map +0 -1
- package/dist/devices/laundryDryer.d.ts +0 -67
- package/dist/devices/laundryDryer.d.ts.map +0 -1
- package/dist/devices/laundryDryer.js.map +0 -1
- package/dist/devices/laundryWasher.d.ts +0 -81
- package/dist/devices/laundryWasher.d.ts.map +0 -1
- package/dist/devices/laundryWasher.js.map +0 -1
- package/dist/devices/microwaveOven.d.ts +0 -168
- package/dist/devices/microwaveOven.d.ts.map +0 -1
- package/dist/devices/microwaveOven.js.map +0 -1
- package/dist/devices/oven.d.ts +0 -105
- package/dist/devices/oven.d.ts.map +0 -1
- package/dist/devices/oven.js.map +0 -1
- package/dist/devices/refrigerator.d.ts +0 -118
- package/dist/devices/refrigerator.d.ts.map +0 -1
- package/dist/devices/refrigerator.js.map +0 -1
- package/dist/devices/roboticVacuumCleaner.d.ts +0 -112
- package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
- package/dist/devices/roboticVacuumCleaner.js.map +0 -1
- package/dist/devices/solarPower.d.ts +0 -40
- package/dist/devices/solarPower.d.ts.map +0 -1
- package/dist/devices/solarPower.js.map +0 -1
- package/dist/devices/speaker.d.ts +0 -87
- package/dist/devices/speaker.d.ts.map +0 -1
- package/dist/devices/speaker.js.map +0 -1
- package/dist/devices/temperatureControl.d.ts +0 -166
- package/dist/devices/temperatureControl.d.ts.map +0 -1
- package/dist/devices/temperatureControl.js.map +0 -1
- package/dist/devices/waterHeater.d.ts +0 -111
- package/dist/devices/waterHeater.d.ts.map +0 -1
- package/dist/devices/waterHeater.js.map +0 -1
- package/dist/dgram/coap.d.ts +0 -205
- package/dist/dgram/coap.d.ts.map +0 -1
- package/dist/dgram/coap.js.map +0 -1
- package/dist/dgram/dgram.d.ts +0 -141
- package/dist/dgram/dgram.d.ts.map +0 -1
- package/dist/dgram/dgram.js.map +0 -1
- package/dist/dgram/mb_coap.d.ts +0 -24
- package/dist/dgram/mb_coap.d.ts.map +0 -1
- package/dist/dgram/mb_coap.js.map +0 -1
- package/dist/dgram/mb_mdns.d.ts +0 -24
- package/dist/dgram/mb_mdns.d.ts.map +0 -1
- package/dist/dgram/mb_mdns.js.map +0 -1
- package/dist/dgram/mdns.d.ts +0 -290
- package/dist/dgram/mdns.d.ts.map +0 -1
- package/dist/dgram/mdns.js.map +0 -1
- package/dist/dgram/multicast.d.ts +0 -67
- package/dist/dgram/multicast.d.ts.map +0 -1
- package/dist/dgram/multicast.js.map +0 -1
- package/dist/dgram/unicast.d.ts +0 -56
- package/dist/dgram/unicast.d.ts.map +0 -1
- package/dist/dgram/unicast.js.map +0 -1
- package/dist/frontend.d.ts +0 -238
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/frontendTypes.d.ts +0 -529
- package/dist/frontendTypes.d.ts.map +0 -1
- package/dist/frontendTypes.js.map +0 -1
- package/dist/helpers.d.ts +0 -48
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.js.map +0 -1
- package/dist/index.d.ts +0 -34
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/jestutils/export.d.ts +0 -2
- package/dist/jestutils/export.d.ts.map +0 -1
- package/dist/jestutils/export.js.map +0 -1
- package/dist/jestutils/jestHelpers.d.ts +0 -340
- package/dist/jestutils/jestHelpers.d.ts.map +0 -1
- package/dist/jestutils/jestHelpers.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterNode.d.ts +0 -342
- package/dist/matterNode.d.ts.map +0 -1
- package/dist/matterNode.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -493
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -41
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -2404
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -698
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -41
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -1507
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -787
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgeEndpointTypes.d.ts +0 -166
- package/dist/matterbridgeEndpointTypes.d.ts.map +0 -1
- package/dist/matterbridgeEndpointTypes.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -537
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -251
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -372
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -181
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts +0 -84
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -101
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/commandLine.d.ts +0 -66
- package/dist/utils/commandLine.d.ts.map +0 -1
- package/dist/utils/commandLine.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -35
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createDirectory.d.ts +0 -34
- package/dist/utils/createDirectory.d.ts.map +0 -1
- package/dist/utils/createDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -39
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -32
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -54
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/error.d.ts +0 -45
- package/dist/utils/error.d.ts.map +0 -1
- package/dist/utils/error.js.map +0 -1
- package/dist/utils/export.d.ts +0 -13
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/format.d.ts +0 -53
- package/dist/utils/format.d.ts.map +0 -1
- package/dist/utils/format.js.map +0 -1
- package/dist/utils/hex.d.ts +0 -89
- package/dist/utils/hex.d.ts.map +0 -1
- package/dist/utils/hex.js.map +0 -1
- package/dist/utils/inspector.d.ts +0 -87
- package/dist/utils/inspector.d.ts.map +0 -1
- package/dist/utils/inspector.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -103
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/network.d.ts +0 -111
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/spawn.d.ts +0 -33
- package/dist/utils/spawn.d.ts.map +0 -1
- package/dist/utils/spawn.js.map +0 -1
- package/dist/utils/tracker.d.ts +0 -108
- package/dist/utils/tracker.d.ts.map +0 -1
- package/dist/utils/tracker.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -54
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
- package/dist/workerGlobalPrefix.d.ts +0 -25
- package/dist/workerGlobalPrefix.d.ts.map +0 -1
- package/dist/workerGlobalPrefix.js.map +0 -1
- package/dist/workerTypes.d.ts +0 -52
- package/dist/workerTypes.d.ts.map +0 -1
- package/dist/workerTypes.js.map +0 -1
- package/dist/workers.d.ts +0 -69
- package/dist/workers.d.ts.map +0 -1
- package/dist/workers.js.map +0 -1
package/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.2
|
|
8
|
-
* @license Apache-2.0
|
|
9
|
-
*
|
|
10
|
-
* Copyright 2025, 2026, 2027 Luca Liguori.
|
|
11
|
-
*
|
|
12
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
-
* you may not use this file except in compliance with the License.
|
|
14
|
-
* You may obtain a copy of the License at
|
|
15
|
-
*
|
|
16
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
-
*
|
|
18
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
19
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
-
* See the License for the specific language governing permissions and
|
|
22
|
-
* limitations under the License.
|
|
23
|
-
*/
|
|
24
|
-
// eslint-disable-next-line no-console
|
|
25
1
|
if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
26
2
|
console.log('\u001B[32mFrontend loaded.\u001B[40;0m');
|
|
27
|
-
// Node modules
|
|
28
3
|
import os from 'node:os';
|
|
29
4
|
import path from 'node:path';
|
|
30
5
|
import EventEmitter from 'node:events';
|
|
31
|
-
// AnsiLogger module
|
|
32
6
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt } from 'node-ansi-logger';
|
|
33
7
|
import { Logger, Diagnostic, LogDestination, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/general';
|
|
34
8
|
import { DeviceAdvertiser, DeviceCommissioner, FabricManager } from '@matter/protocol';
|
|
@@ -63,7 +37,7 @@ export class Frontend extends EventEmitter {
|
|
|
63
37
|
constructor(matterbridge) {
|
|
64
38
|
super();
|
|
65
39
|
this.matterbridge = matterbridge;
|
|
66
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
40
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
67
41
|
this.log.logNameColor = '\x1b[38;5;97m';
|
|
68
42
|
this.server = new BroadcastServer('frontend', this.log);
|
|
69
43
|
this.server.on('broadcast_message', this.msgHandler.bind(this));
|
|
@@ -165,53 +139,23 @@ export class Frontend extends EventEmitter {
|
|
|
165
139
|
this.port = port;
|
|
166
140
|
this.storedPassword = await this.matterbridge.nodeContext?.get('password', '');
|
|
167
141
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
168
|
-
// Initialize multer with the upload directory
|
|
169
142
|
const multer = await import('multer');
|
|
170
|
-
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
143
|
+
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
171
144
|
const upload = multer.default({ dest: uploadDir });
|
|
172
|
-
// Create the express app that serves the frontend
|
|
173
145
|
const express = await import('express');
|
|
174
146
|
this.expressApp = express.default();
|
|
175
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
176
|
-
/*
|
|
177
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
178
|
-
for (const method of methods) {
|
|
179
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
180
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
181
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
182
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
183
|
-
try {
|
|
184
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
185
|
-
return original(path, ...rest);
|
|
186
|
-
} catch (err) {
|
|
187
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
188
|
-
throw err;
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
*/
|
|
193
|
-
// Log all requests to the server for debugging
|
|
194
|
-
/*
|
|
195
|
-
this.expressApp.use((req, res, next) => {
|
|
196
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
197
|
-
next();
|
|
198
|
-
});
|
|
199
|
-
*/
|
|
200
|
-
// Serve static files from 'frontend/build' directory
|
|
201
147
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend', 'build')));
|
|
202
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
203
148
|
this.log.debug(`Creating WebSocketServer...`);
|
|
204
149
|
const ws = await import('ws');
|
|
205
150
|
this.webSocketServer = new ws.WebSocketServer({ noServer: true });
|
|
206
151
|
this.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
|
|
207
152
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
208
153
|
const clientIp = request.socket.remoteAddress;
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
154
|
+
let callbackLogLevel = "notice";
|
|
155
|
+
if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
|
|
156
|
+
callbackLogLevel = "info";
|
|
157
|
+
if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
158
|
+
callbackLogLevel = "debug";
|
|
215
159
|
AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
|
|
216
160
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
217
161
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -233,25 +177,16 @@ export class Frontend extends EventEmitter {
|
|
|
233
177
|
}
|
|
234
178
|
});
|
|
235
179
|
ws.on('error', (error) => {
|
|
236
|
-
// istanbul ignore next
|
|
237
180
|
this.log.error(`WebSocket client error: ${error}`);
|
|
238
181
|
});
|
|
239
182
|
});
|
|
240
183
|
this.webSocketServer.on('close', () => {
|
|
241
184
|
this.log.debug(`WebSocketServer closed`);
|
|
242
185
|
});
|
|
243
|
-
/* With { noServer: true } it never fires
|
|
244
|
-
this.webSocketServer.on('listening', () => {
|
|
245
|
-
this.log.info(`The WebSocketServer is listening`);
|
|
246
|
-
this.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
|
|
247
|
-
});
|
|
248
|
-
*/
|
|
249
|
-
// istanbul ignore next
|
|
250
186
|
this.webSocketServer.on('error', (ws, error) => {
|
|
251
187
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
252
188
|
});
|
|
253
189
|
if (!hasParameter('ssl')) {
|
|
254
|
-
// Create an HTTP server and attach the express app
|
|
255
190
|
const http = await import('node:http');
|
|
256
191
|
try {
|
|
257
192
|
this.log.debug(`Creating HTTP server...`);
|
|
@@ -262,7 +197,6 @@ export class Frontend extends EventEmitter {
|
|
|
262
197
|
this.emit('server_error', error);
|
|
263
198
|
return;
|
|
264
199
|
}
|
|
265
|
-
// Listen on the specified port
|
|
266
200
|
if (hasParameter('ingress')) {
|
|
267
201
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
268
202
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -282,30 +216,24 @@ export class Frontend extends EventEmitter {
|
|
|
282
216
|
}
|
|
283
217
|
this.httpServer.on('upgrade', async (req, socket, head) => {
|
|
284
218
|
try {
|
|
285
|
-
// Only proceed for real WebSocket upgrades
|
|
286
|
-
// istanbul ignore next cause is only a safety check
|
|
287
219
|
if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
|
|
288
220
|
this.log.error(`WebSocket upgrade error: Invalid upgrade header ${req.headers.upgrade}`);
|
|
289
221
|
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
290
222
|
return socket.destroy();
|
|
291
223
|
}
|
|
292
|
-
// Build a URL so we can read ?password=...
|
|
293
224
|
const url = new URL(req.url ?? '/', `http://${req.headers.host || 'localhost'}`);
|
|
294
|
-
// Validate WebSocket password
|
|
295
225
|
const password = url.searchParams.get('password') ?? '';
|
|
296
226
|
if (password !== this.storedPassword) {
|
|
297
227
|
this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
|
|
298
228
|
socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
|
|
299
229
|
return socket.destroy();
|
|
300
230
|
}
|
|
301
|
-
// Complete the WebSocket handshake
|
|
302
231
|
this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
|
|
303
232
|
this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
|
|
304
233
|
this.webSocketServer?.emit('connection', ws, req);
|
|
305
234
|
});
|
|
306
235
|
}
|
|
307
236
|
catch (err) {
|
|
308
|
-
/* istanbul ignore next: only triggered on unexpected internal error */
|
|
309
237
|
{
|
|
310
238
|
inspectError(this.log, 'WebSocket upgrade error:', err);
|
|
311
239
|
socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
|
|
@@ -328,7 +256,6 @@ export class Frontend extends EventEmitter {
|
|
|
328
256
|
});
|
|
329
257
|
}
|
|
330
258
|
else {
|
|
331
|
-
// SSL is enabled, load the certificate and the private key
|
|
332
259
|
let cert;
|
|
333
260
|
let key;
|
|
334
261
|
let ca;
|
|
@@ -338,7 +265,6 @@ export class Frontend extends EventEmitter {
|
|
|
338
265
|
let httpsServerOptions = {};
|
|
339
266
|
const fs = await import('node:fs');
|
|
340
267
|
if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'))) {
|
|
341
|
-
// Load the p12 certificate and the passphrase
|
|
342
268
|
try {
|
|
343
269
|
pfx = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'));
|
|
344
270
|
this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12')}`);
|
|
@@ -350,7 +276,7 @@ export class Frontend extends EventEmitter {
|
|
|
350
276
|
}
|
|
351
277
|
try {
|
|
352
278
|
passphrase = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass'), 'utf8');
|
|
353
|
-
passphrase = passphrase.trim();
|
|
279
|
+
passphrase = passphrase.trim();
|
|
354
280
|
this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass')}`);
|
|
355
281
|
}
|
|
356
282
|
catch (error) {
|
|
@@ -361,7 +287,6 @@ export class Frontend extends EventEmitter {
|
|
|
361
287
|
httpsServerOptions = { pfx, passphrase };
|
|
362
288
|
}
|
|
363
289
|
else {
|
|
364
|
-
// 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.
|
|
365
290
|
try {
|
|
366
291
|
cert = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem'), 'utf8');
|
|
367
292
|
this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem')}`);
|
|
@@ -391,10 +316,9 @@ export class Frontend extends EventEmitter {
|
|
|
391
316
|
httpsServerOptions = { cert: fullChain ?? cert, key, ca };
|
|
392
317
|
}
|
|
393
318
|
if (hasParameter('mtls')) {
|
|
394
|
-
httpsServerOptions.requestCert = true;
|
|
395
|
-
httpsServerOptions.rejectUnauthorized = true;
|
|
319
|
+
httpsServerOptions.requestCert = true;
|
|
320
|
+
httpsServerOptions.rejectUnauthorized = true;
|
|
396
321
|
}
|
|
397
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
398
322
|
const https = await import('node:https');
|
|
399
323
|
try {
|
|
400
324
|
this.log.debug(`Creating HTTPS server...`);
|
|
@@ -405,7 +329,6 @@ export class Frontend extends EventEmitter {
|
|
|
405
329
|
this.emit('server_error', error);
|
|
406
330
|
return;
|
|
407
331
|
}
|
|
408
|
-
// Listen on the specified port
|
|
409
332
|
if (hasParameter('ingress')) {
|
|
410
333
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
411
334
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -425,29 +348,23 @@ export class Frontend extends EventEmitter {
|
|
|
425
348
|
}
|
|
426
349
|
this.httpsServer.on('upgrade', async (req, socket, head) => {
|
|
427
350
|
try {
|
|
428
|
-
// Only proceed for real WebSocket upgrades
|
|
429
|
-
// istanbul ignore next cause is only a safety check
|
|
430
351
|
if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
|
|
431
352
|
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
432
353
|
return socket.destroy();
|
|
433
354
|
}
|
|
434
|
-
// Build a URL so we can read ?password=...
|
|
435
355
|
const url = new URL(req.url ?? '/', `https://${req.headers.host || 'localhost'}`);
|
|
436
|
-
// Validate WebSocket password
|
|
437
356
|
const password = url.searchParams.get('password') ?? '';
|
|
438
357
|
if (password !== this.storedPassword) {
|
|
439
358
|
this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
|
|
440
359
|
socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
|
|
441
360
|
return socket.destroy();
|
|
442
361
|
}
|
|
443
|
-
// Complete the WebSocket handshake
|
|
444
362
|
this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
|
|
445
363
|
this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
|
|
446
364
|
this.webSocketServer?.emit('connection', ws, req);
|
|
447
365
|
});
|
|
448
366
|
}
|
|
449
367
|
catch (err) {
|
|
450
|
-
/* istanbul ignore next: only triggered on unexpected internal error */
|
|
451
368
|
{
|
|
452
369
|
inspectError(this.log, 'WebSocket upgrade error:', err);
|
|
453
370
|
socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
|
|
@@ -469,7 +386,6 @@ export class Frontend extends EventEmitter {
|
|
|
469
386
|
return;
|
|
470
387
|
});
|
|
471
388
|
}
|
|
472
|
-
// Subscribe to cli events
|
|
473
389
|
cliEmitter.removeAllListeners();
|
|
474
390
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
475
391
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -480,8 +396,6 @@ export class Frontend extends EventEmitter {
|
|
|
480
396
|
cliEmitter.on('cpu', (cpuUsage, processCpuUsage) => {
|
|
481
397
|
this.wssSendCpuUpdate(cpuUsage, processCpuUsage);
|
|
482
398
|
});
|
|
483
|
-
// Endpoint to validate login code
|
|
484
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
485
399
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
486
400
|
const { password } = req.body;
|
|
487
401
|
this.log.debug(`The frontend sent /api/login with password ${password ? '[redacted]' : '(empty)'}`);
|
|
@@ -494,20 +408,17 @@ export class Frontend extends EventEmitter {
|
|
|
494
408
|
res.json({ valid: false });
|
|
495
409
|
}
|
|
496
410
|
});
|
|
497
|
-
// Endpoint to provide health check for docker
|
|
498
411
|
this.expressApp.get('/health', (req, res) => {
|
|
499
412
|
this.log.debug('Express received /health');
|
|
500
413
|
const healthStatus = {
|
|
501
|
-
status: 'ok',
|
|
502
|
-
uptime: process.uptime(),
|
|
503
|
-
timestamp: new Date().toISOString(),
|
|
414
|
+
status: 'ok',
|
|
415
|
+
uptime: process.uptime(),
|
|
416
|
+
timestamp: new Date().toISOString(),
|
|
504
417
|
};
|
|
505
418
|
res.status(200).json(healthStatus);
|
|
506
419
|
});
|
|
507
|
-
// Endpoint to provide memory usage details
|
|
508
420
|
this.expressApp.get('/memory', async (req, res) => {
|
|
509
421
|
this.log.debug('Express received /memory');
|
|
510
|
-
// Memory usage from process
|
|
511
422
|
const memoryUsageRaw = process.memoryUsage();
|
|
512
423
|
const memoryUsage = {
|
|
513
424
|
rss: formatBytes(memoryUsageRaw.rss),
|
|
@@ -516,13 +427,10 @@ export class Frontend extends EventEmitter {
|
|
|
516
427
|
external: formatBytes(memoryUsageRaw.external),
|
|
517
428
|
arrayBuffers: formatBytes(memoryUsageRaw.arrayBuffers),
|
|
518
429
|
};
|
|
519
|
-
// V8 heap statistics
|
|
520
430
|
const { default: v8 } = await import('node:v8');
|
|
521
431
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
522
432
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
523
|
-
// Format heapStats
|
|
524
433
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, formatBytes(value)]));
|
|
525
|
-
// Format heapSpaces
|
|
526
434
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
527
435
|
...space,
|
|
528
436
|
space_size: formatBytes(space.space_size),
|
|
@@ -541,22 +449,18 @@ export class Frontend extends EventEmitter {
|
|
|
541
449
|
};
|
|
542
450
|
res.status(200).json(memoryReport);
|
|
543
451
|
});
|
|
544
|
-
// Endpoint to provide settings
|
|
545
452
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
546
453
|
this.log.debug('The frontend sent /api/settings');
|
|
547
454
|
res.json(await this.getApiSettings());
|
|
548
455
|
});
|
|
549
|
-
// Endpoint to provide plugins
|
|
550
456
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
551
457
|
this.log.debug('The frontend sent /api/plugins');
|
|
552
458
|
res.json(this.matterbridge.hasCleanupStarted ? [] : this.getPlugins());
|
|
553
459
|
});
|
|
554
|
-
// Endpoint to provide devices
|
|
555
460
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
556
461
|
this.log.debug('The frontend sent /api/devices');
|
|
557
462
|
res.json(this.matterbridge.hasCleanupStarted ? [] : this.getDevices());
|
|
558
463
|
});
|
|
559
|
-
// Endpoint to view the matterbridge log
|
|
560
464
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
561
465
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
562
466
|
try {
|
|
@@ -570,7 +474,6 @@ export class Frontend extends EventEmitter {
|
|
|
570
474
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
571
475
|
}
|
|
572
476
|
});
|
|
573
|
-
// Endpoint to view the matter.js log
|
|
574
477
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
575
478
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
576
479
|
try {
|
|
@@ -584,7 +487,6 @@ export class Frontend extends EventEmitter {
|
|
|
584
487
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
585
488
|
}
|
|
586
489
|
});
|
|
587
|
-
// Endpoint to view the diagnostic.log
|
|
588
490
|
this.expressApp.get('/api/view-diagnostic', async (req, res) => {
|
|
589
491
|
this.log.debug('The frontend sent /api/view-diagnostic');
|
|
590
492
|
await this.generateDiagnostic();
|
|
@@ -595,13 +497,10 @@ export class Frontend extends EventEmitter {
|
|
|
595
497
|
res.send(data.slice(29));
|
|
596
498
|
}
|
|
597
499
|
catch (error) {
|
|
598
|
-
// istanbul ignore next
|
|
599
500
|
this.log.error(`Error reading diagnostic log file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
600
|
-
// istanbul ignore next
|
|
601
501
|
res.status(500).send('Error reading diagnostic log file.');
|
|
602
502
|
}
|
|
603
503
|
});
|
|
604
|
-
// Endpoint to download the diagnostic.log
|
|
605
504
|
this.expressApp.get('/api/download-diagnostic', async (req, res) => {
|
|
606
505
|
this.log.debug(`The frontend sent /api/download-diagnostic`);
|
|
607
506
|
await this.generateDiagnostic();
|
|
@@ -612,19 +511,16 @@ export class Frontend extends EventEmitter {
|
|
|
612
511
|
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), data, 'utf-8');
|
|
613
512
|
}
|
|
614
513
|
catch (error) {
|
|
615
|
-
// istanbul ignore next
|
|
616
514
|
this.log.debug(`Error in /api/download-diagnostic: ${error instanceof Error ? error.message : error}`);
|
|
617
515
|
}
|
|
618
516
|
res.type('text/plain');
|
|
619
517
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), MATTERBRIDGE_DIAGNOSTIC_FILE, (error) => {
|
|
620
|
-
/* istanbul ignore if */
|
|
621
518
|
if (error) {
|
|
622
519
|
this.log.error(`Error downloading file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
623
520
|
res.status(500).send('Error downloading the diagnostic log file');
|
|
624
521
|
}
|
|
625
522
|
});
|
|
626
523
|
});
|
|
627
|
-
// Endpoint to view the history.html
|
|
628
524
|
this.expressApp.get('/api/viewhistory', async (req, res) => {
|
|
629
525
|
this.log.debug('The frontend sent /api/viewhistory');
|
|
630
526
|
try {
|
|
@@ -638,7 +534,6 @@ export class Frontend extends EventEmitter {
|
|
|
638
534
|
res.status(500).send('Error reading history file.');
|
|
639
535
|
}
|
|
640
536
|
});
|
|
641
|
-
// Endpoint to download the history.html
|
|
642
537
|
this.expressApp.get('/api/downloadhistory', async (req, res) => {
|
|
643
538
|
this.log.debug(`The frontend sent /api/downloadhistory`);
|
|
644
539
|
try {
|
|
@@ -648,7 +543,6 @@ export class Frontend extends EventEmitter {
|
|
|
648
543
|
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), data, 'utf-8');
|
|
649
544
|
res.type('text/plain');
|
|
650
545
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), MATTERBRIDGE_HISTORY_FILE, (error) => {
|
|
651
|
-
/* istanbul ignore if */
|
|
652
546
|
if (error) {
|
|
653
547
|
this.log.error(`Error in /api/downloadhistory downloading history file ${MATTERBRIDGE_HISTORY_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
654
548
|
res.status(500).send('Error downloading history file');
|
|
@@ -660,7 +554,6 @@ export class Frontend extends EventEmitter {
|
|
|
660
554
|
res.status(500).send('Error reading history file.');
|
|
661
555
|
}
|
|
662
556
|
});
|
|
663
|
-
// Endpoint to view the shelly log
|
|
664
557
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
665
558
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
666
559
|
try {
|
|
@@ -674,7 +567,6 @@ export class Frontend extends EventEmitter {
|
|
|
674
567
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
675
568
|
}
|
|
676
569
|
});
|
|
677
|
-
// Endpoint to download the matterbridge log
|
|
678
570
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
679
571
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
|
|
680
572
|
const fs = await import('node:fs');
|
|
@@ -689,14 +581,12 @@ export class Frontend extends EventEmitter {
|
|
|
689
581
|
}
|
|
690
582
|
res.type('text/plain');
|
|
691
583
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_LOGGER_FILE), 'matterbridge.log', (error) => {
|
|
692
|
-
/* istanbul ignore if */
|
|
693
584
|
if (error) {
|
|
694
585
|
this.log.error(`Error downloading log file ${MATTERBRIDGE_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
695
586
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
696
587
|
}
|
|
697
588
|
});
|
|
698
589
|
});
|
|
699
|
-
// Endpoint to download the matter log
|
|
700
590
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
701
591
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
|
|
702
592
|
const fs = await import('node:fs');
|
|
@@ -711,14 +601,12 @@ export class Frontend extends EventEmitter {
|
|
|
711
601
|
}
|
|
712
602
|
res.type('text/plain');
|
|
713
603
|
res.download(path.join(os.tmpdir(), MATTER_LOGGER_FILE), 'matter.log', (error) => {
|
|
714
|
-
/* istanbul ignore if */
|
|
715
604
|
if (error) {
|
|
716
605
|
this.log.error(`Error downloading log file ${MATTER_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
717
606
|
res.status(500).send('Error downloading the matter log file');
|
|
718
607
|
}
|
|
719
608
|
});
|
|
720
609
|
});
|
|
721
|
-
// Endpoint to download the shelly log
|
|
722
610
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
723
611
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
724
612
|
const fs = await import('node:fs');
|
|
@@ -733,91 +621,75 @@ export class Frontend extends EventEmitter {
|
|
|
733
621
|
}
|
|
734
622
|
res.type('text/plain');
|
|
735
623
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
736
|
-
/* istanbul ignore if */
|
|
737
624
|
if (error) {
|
|
738
625
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
739
626
|
res.status(500).send('Error downloading Shelly system log file');
|
|
740
627
|
}
|
|
741
628
|
});
|
|
742
629
|
});
|
|
743
|
-
// Endpoint to download the matterbridge storage directory
|
|
744
630
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
745
631
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
746
632
|
await createZip(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), path.join(this.matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR));
|
|
747
633
|
res.download(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), `matterbridge.${NODE_STORAGE_DIR}.zip`, (error) => {
|
|
748
|
-
/* istanbul ignore if */
|
|
749
634
|
if (error) {
|
|
750
635
|
this.log.error(`Error downloading file ${`matterbridge.${NODE_STORAGE_DIR}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
751
636
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
752
637
|
}
|
|
753
638
|
});
|
|
754
639
|
});
|
|
755
|
-
// Endpoint to download the matter storage file
|
|
756
640
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
757
641
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
758
642
|
await createZip(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME));
|
|
759
643
|
res.download(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), `matterbridge.${MATTER_STORAGE_NAME}.zip`, (error) => {
|
|
760
|
-
/* istanbul ignore if */
|
|
761
644
|
if (error) {
|
|
762
645
|
this.log.error(`Error downloading the matter storage matterbridge.${MATTER_STORAGE_NAME}.zip: ${error instanceof Error ? error.message : error}`);
|
|
763
646
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
764
647
|
}
|
|
765
648
|
});
|
|
766
649
|
});
|
|
767
|
-
// Endpoint to download the matterbridge plugin directory
|
|
768
650
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
769
651
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
770
652
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
771
653
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
772
|
-
/* istanbul ignore if */
|
|
773
654
|
if (error) {
|
|
774
655
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
775
656
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
776
657
|
}
|
|
777
658
|
});
|
|
778
659
|
});
|
|
779
|
-
// Endpoint to download the matterbridge plugin config files
|
|
780
660
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
781
661
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
782
662
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
783
663
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
784
|
-
/* istanbul ignore if */
|
|
785
664
|
if (error) {
|
|
786
665
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
787
666
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
788
667
|
}
|
|
789
668
|
});
|
|
790
669
|
});
|
|
791
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
792
670
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
793
671
|
this.log.debug('The frontend sent /api/download-backup');
|
|
794
672
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
795
|
-
/* istanbul ignore if */
|
|
796
673
|
if (error) {
|
|
797
674
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
798
675
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
799
676
|
}
|
|
800
677
|
});
|
|
801
678
|
});
|
|
802
|
-
// Endpoint to upload a package
|
|
803
679
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
804
680
|
const { filename } = req.body;
|
|
805
681
|
const file = req.file;
|
|
806
|
-
/* istanbul ignore if */
|
|
807
682
|
if (!file || !filename) {
|
|
808
683
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
809
684
|
res.status(400).send('Invalid request: file and filename are required');
|
|
810
685
|
return;
|
|
811
686
|
}
|
|
812
687
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
813
|
-
// Define the path where the plugin file will be saved
|
|
814
688
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
815
689
|
try {
|
|
816
|
-
// Move the uploaded file to the specified path
|
|
817
690
|
const fs = await import('node:fs');
|
|
818
691
|
await fs.promises.rename(file.path, filePath);
|
|
819
692
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
820
|
-
// Install the plugin package
|
|
821
693
|
if (filename.endsWith('.tgz')) {
|
|
822
694
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
823
695
|
if (await spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], 'install', filename)) {
|
|
@@ -845,7 +717,6 @@ export class Frontend extends EventEmitter {
|
|
|
845
717
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
846
718
|
}
|
|
847
719
|
});
|
|
848
|
-
// Fallback for routing (must be the last route)
|
|
849
720
|
this.expressApp.use((req, res) => {
|
|
850
721
|
const filePath = path.resolve(this.matterbridge.rootDirectory, 'frontend', 'build');
|
|
851
722
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html in ${filePath} as fallback`);
|
|
@@ -856,16 +727,13 @@ export class Frontend extends EventEmitter {
|
|
|
856
727
|
async stop() {
|
|
857
728
|
this.log.debug('Stopping the frontend...');
|
|
858
729
|
const ws = await import('ws');
|
|
859
|
-
// Remove listeners from the express app
|
|
860
730
|
if (this.expressApp) {
|
|
861
731
|
this.expressApp.removeAllListeners();
|
|
862
732
|
this.expressApp = undefined;
|
|
863
733
|
this.log.debug('Frontend app closed successfully');
|
|
864
734
|
}
|
|
865
|
-
// Close the WebSocket server
|
|
866
735
|
if (this.webSocketServer) {
|
|
867
736
|
this.log.debug('Closing WebSocket server...');
|
|
868
|
-
// Close all active connections
|
|
869
737
|
this.webSocketServer.clients.forEach((client) => {
|
|
870
738
|
if (client.readyState === ws.WebSocket.OPEN) {
|
|
871
739
|
client.close();
|
|
@@ -874,7 +742,6 @@ export class Frontend extends EventEmitter {
|
|
|
874
742
|
await withTimeout(new Promise((resolve) => {
|
|
875
743
|
this.webSocketServer?.close((error) => {
|
|
876
744
|
if (error) {
|
|
877
|
-
// istanbul ignore next
|
|
878
745
|
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
879
746
|
}
|
|
880
747
|
else {
|
|
@@ -887,27 +754,8 @@ export class Frontend extends EventEmitter {
|
|
|
887
754
|
this.webSocketServer.removeAllListeners();
|
|
888
755
|
this.webSocketServer = undefined;
|
|
889
756
|
}
|
|
890
|
-
// Close the http server
|
|
891
757
|
if (this.httpServer) {
|
|
892
758
|
this.log.debug('Closing http server...');
|
|
893
|
-
/*
|
|
894
|
-
await withTimeout(
|
|
895
|
-
new Promise<void>((resolve) => {
|
|
896
|
-
this.httpServer?.close((error) => {
|
|
897
|
-
if (error) {
|
|
898
|
-
// istanbul ignore next
|
|
899
|
-
this.log.error(`Error closing http server: ${error}`);
|
|
900
|
-
} else {
|
|
901
|
-
this.log.debug('Http server closed successfully');
|
|
902
|
-
this.emit('server_stopped');
|
|
903
|
-
}
|
|
904
|
-
resolve();
|
|
905
|
-
});
|
|
906
|
-
}),
|
|
907
|
-
5000,
|
|
908
|
-
false,
|
|
909
|
-
);
|
|
910
|
-
*/
|
|
911
759
|
this.httpServer.close();
|
|
912
760
|
this.log.debug('Http server closed successfully');
|
|
913
761
|
this.listening = false;
|
|
@@ -916,27 +764,8 @@ export class Frontend extends EventEmitter {
|
|
|
916
764
|
this.httpServer = undefined;
|
|
917
765
|
this.log.debug('Frontend http server closed successfully');
|
|
918
766
|
}
|
|
919
|
-
// Close the https server
|
|
920
767
|
if (this.httpsServer) {
|
|
921
768
|
this.log.debug('Closing https server...');
|
|
922
|
-
/*
|
|
923
|
-
await withTimeout(
|
|
924
|
-
new Promise<void>((resolve) => {
|
|
925
|
-
this.httpsServer?.close((error) => {
|
|
926
|
-
if (error) {
|
|
927
|
-
// istanbul ignore next
|
|
928
|
-
this.log.error(`Error closing https server: ${error}`);
|
|
929
|
-
} else {
|
|
930
|
-
this.log.debug('Https server closed successfully');
|
|
931
|
-
this.emit('server_stopped');
|
|
932
|
-
}
|
|
933
|
-
resolve();
|
|
934
|
-
});
|
|
935
|
-
}),
|
|
936
|
-
5000,
|
|
937
|
-
false,
|
|
938
|
-
);
|
|
939
|
-
*/
|
|
940
769
|
this.httpsServer.close();
|
|
941
770
|
this.log.debug('Https server closed successfully');
|
|
942
771
|
this.listening = false;
|
|
@@ -947,13 +776,7 @@ export class Frontend extends EventEmitter {
|
|
|
947
776
|
}
|
|
948
777
|
this.log.debug('Frontend stopped successfully');
|
|
949
778
|
}
|
|
950
|
-
/**
|
|
951
|
-
* Retrieves the api settings data.
|
|
952
|
-
*
|
|
953
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
954
|
-
*/
|
|
955
779
|
async getApiSettings() {
|
|
956
|
-
// Update the variable system information properties
|
|
957
780
|
this.matterbridge.systemInformation.totalMemory = formatBytes(os.totalmem());
|
|
958
781
|
this.matterbridge.systemInformation.freeMemory = formatBytes(os.freemem());
|
|
959
782
|
this.matterbridge.systemInformation.systemUptime = formatUptime(os.uptime());
|
|
@@ -963,7 +786,6 @@ export class Frontend extends EventEmitter {
|
|
|
963
786
|
this.matterbridge.systemInformation.rss = formatBytes(process.memoryUsage().rss);
|
|
964
787
|
this.matterbridge.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
|
|
965
788
|
this.matterbridge.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
|
|
966
|
-
// Create the matterbridge information
|
|
967
789
|
const info = {
|
|
968
790
|
homeDirectory: this.matterbridge.homeDirectory,
|
|
969
791
|
rootDirectory: this.matterbridge.rootDirectory,
|
|
@@ -999,15 +821,9 @@ export class Frontend extends EventEmitter {
|
|
|
999
821
|
};
|
|
1000
822
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: info };
|
|
1001
823
|
}
|
|
1002
|
-
/**
|
|
1003
|
-
* Retrieves the reachable attribute.
|
|
1004
|
-
*
|
|
1005
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
1006
|
-
* @returns {boolean} The reachable attribute.
|
|
1007
|
-
*/
|
|
1008
824
|
getReachability(device) {
|
|
1009
825
|
if (this.matterbridge.hasCleanupStarted)
|
|
1010
|
-
return false;
|
|
826
|
+
return false;
|
|
1011
827
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1012
828
|
return false;
|
|
1013
829
|
if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
|
|
@@ -1018,15 +834,9 @@ export class Frontend extends EventEmitter {
|
|
|
1018
834
|
return true;
|
|
1019
835
|
return false;
|
|
1020
836
|
}
|
|
1021
|
-
/**
|
|
1022
|
-
* Retrieves the power source attribute.
|
|
1023
|
-
*
|
|
1024
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
1025
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
1026
|
-
*/
|
|
1027
837
|
getPowerSource(endpoint) {
|
|
1028
838
|
if (this.matterbridge.hasCleanupStarted)
|
|
1029
|
-
return;
|
|
839
|
+
return;
|
|
1030
840
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
1031
841
|
return undefined;
|
|
1032
842
|
const powerSource = (device) => {
|
|
@@ -1041,25 +851,16 @@ export class Frontend extends EventEmitter {
|
|
|
1041
851
|
}
|
|
1042
852
|
return;
|
|
1043
853
|
};
|
|
1044
|
-
// Root endpoint
|
|
1045
854
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
1046
855
|
return powerSource(endpoint);
|
|
1047
|
-
// Child endpoints
|
|
1048
856
|
for (const child of endpoint.getChildEndpoints()) {
|
|
1049
857
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
1050
858
|
return powerSource(child);
|
|
1051
859
|
}
|
|
1052
860
|
}
|
|
1053
|
-
/**
|
|
1054
|
-
* Retrieves the cluster text description from a given device.
|
|
1055
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
1056
|
-
*
|
|
1057
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
1058
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
1059
|
-
*/
|
|
1060
861
|
getClusterTextFromDevice(device) {
|
|
1061
862
|
if (this.matterbridge.hasCleanupStarted)
|
|
1062
|
-
return '';
|
|
863
|
+
return '';
|
|
1063
864
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
1064
865
|
return '';
|
|
1065
866
|
const getUserLabel = (device) => {
|
|
@@ -1069,7 +870,6 @@ export class Frontend extends EventEmitter {
|
|
|
1069
870
|
if (composed)
|
|
1070
871
|
return 'Composed: ' + composed.value;
|
|
1071
872
|
}
|
|
1072
|
-
// istanbul ignore next cause is not reachable
|
|
1073
873
|
return '';
|
|
1074
874
|
};
|
|
1075
875
|
const getFixedLabel = (device) => {
|
|
@@ -1079,13 +879,11 @@ export class Frontend extends EventEmitter {
|
|
|
1079
879
|
if (composed)
|
|
1080
880
|
return 'Composed: ' + composed.value;
|
|
1081
881
|
}
|
|
1082
|
-
// istanbul ignore next cause is not reacheable
|
|
1083
882
|
return '';
|
|
1084
883
|
};
|
|
1085
884
|
let attributes = '';
|
|
1086
885
|
let supportedModes = [];
|
|
1087
886
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1088
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
1089
887
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1090
888
|
return;
|
|
1091
889
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1175,17 +973,11 @@ export class Frontend extends EventEmitter {
|
|
|
1175
973
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1176
974
|
attributes += `${getUserLabel(device)} `;
|
|
1177
975
|
});
|
|
1178
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1179
976
|
return attributes.trimStart().trimEnd();
|
|
1180
977
|
}
|
|
1181
|
-
/**
|
|
1182
|
-
* Retrieves the registered plugins sanitized for res.json().
|
|
1183
|
-
*
|
|
1184
|
-
* @returns {ApiPlugin[]} An array of BaseRegisteredPlugin.
|
|
1185
|
-
*/
|
|
1186
978
|
getPlugins() {
|
|
1187
979
|
if (this.matterbridge.hasCleanupStarted)
|
|
1188
|
-
return [];
|
|
980
|
+
return [];
|
|
1189
981
|
const plugins = [];
|
|
1190
982
|
for (const plugin of this.matterbridge.plugins.array()) {
|
|
1191
983
|
plugins.push({
|
|
@@ -1213,27 +1005,18 @@ export class Frontend extends EventEmitter {
|
|
|
1213
1005
|
schemaJson: plugin.schemaJson,
|
|
1214
1006
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1215
1007
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1216
|
-
// Childbridge mode specific data
|
|
1217
1008
|
matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
|
|
1218
1009
|
});
|
|
1219
1010
|
}
|
|
1220
1011
|
return plugins;
|
|
1221
1012
|
}
|
|
1222
|
-
/**
|
|
1223
|
-
* Retrieves the devices from Matterbridge.
|
|
1224
|
-
*
|
|
1225
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1226
|
-
* @returns {ApiDevice[]} An array of ApiDevices for the frontend.
|
|
1227
|
-
*/
|
|
1228
1013
|
getDevices(pluginName) {
|
|
1229
1014
|
if (this.matterbridge.hasCleanupStarted)
|
|
1230
|
-
return [];
|
|
1015
|
+
return [];
|
|
1231
1016
|
const devices = [];
|
|
1232
1017
|
for (const device of this.matterbridge.devices.array()) {
|
|
1233
|
-
// Filter by pluginName if provided
|
|
1234
1018
|
if (pluginName && pluginName !== device.plugin)
|
|
1235
1019
|
continue;
|
|
1236
|
-
// Check if the device has the required properties
|
|
1237
1020
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1238
1021
|
continue;
|
|
1239
1022
|
devices.push({
|
|
@@ -1253,39 +1036,24 @@ export class Frontend extends EventEmitter {
|
|
|
1253
1036
|
}
|
|
1254
1037
|
return devices;
|
|
1255
1038
|
}
|
|
1256
|
-
/**
|
|
1257
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1258
|
-
*
|
|
1259
|
-
* Response for /api/clusters
|
|
1260
|
-
*
|
|
1261
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1262
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1263
|
-
* @returns {ApiClusters | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1264
|
-
*/
|
|
1265
1039
|
getClusters(pluginName, endpointNumber) {
|
|
1266
1040
|
if (this.matterbridge.hasCleanupStarted)
|
|
1267
|
-
return;
|
|
1041
|
+
return;
|
|
1268
1042
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1269
1043
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.maybeId || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1270
1044
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1271
1045
|
return;
|
|
1272
1046
|
}
|
|
1273
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1274
|
-
// Get the device types from the main endpoint
|
|
1275
1047
|
const deviceTypes = [];
|
|
1276
1048
|
const clusters = [];
|
|
1277
1049
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1278
1050
|
deviceTypes.push(d.deviceType);
|
|
1279
1051
|
});
|
|
1280
|
-
// Get the clusters from the main endpoint
|
|
1281
1052
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1282
1053
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1283
1054
|
return;
|
|
1284
1055
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1285
1056
|
return;
|
|
1286
|
-
// console.log(
|
|
1287
|
-
// `${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}`,
|
|
1288
|
-
// );
|
|
1289
1057
|
clusters.push({
|
|
1290
1058
|
endpoint: endpoint.number.toString(),
|
|
1291
1059
|
number: endpoint.number,
|
|
@@ -1299,19 +1067,12 @@ export class Frontend extends EventEmitter {
|
|
|
1299
1067
|
attributeLocalValue: attributeValue,
|
|
1300
1068
|
});
|
|
1301
1069
|
});
|
|
1302
|
-
// Get the child endpoints
|
|
1303
1070
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1304
|
-
// if (childEndpoints.length === 0) {
|
|
1305
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1306
|
-
// }
|
|
1307
1071
|
childEndpoints.forEach((childEndpoint) => {
|
|
1308
|
-
// istanbul ignore if cause is not reachable: should never happen but ...
|
|
1309
1072
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1310
1073
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1311
1074
|
return;
|
|
1312
1075
|
}
|
|
1313
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1314
|
-
// Get the device types of the child endpoint
|
|
1315
1076
|
const deviceTypes = [];
|
|
1316
1077
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1317
1078
|
deviceTypes.push(d.deviceType);
|
|
@@ -1321,9 +1082,6 @@ export class Frontend extends EventEmitter {
|
|
|
1321
1082
|
return;
|
|
1322
1083
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1323
1084
|
return;
|
|
1324
|
-
// console.log(
|
|
1325
|
-
// `${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}`,
|
|
1326
|
-
// );
|
|
1327
1085
|
clusters.push({
|
|
1328
1086
|
endpoint: childEndpoint.number.toString(),
|
|
1329
1087
|
number: childEndpoint.number,
|
|
@@ -1343,7 +1101,6 @@ export class Frontend extends EventEmitter {
|
|
|
1343
1101
|
async generateDiagnostic() {
|
|
1344
1102
|
this.log.debug('Generating diagnostic...');
|
|
1345
1103
|
const serverNodes = [];
|
|
1346
|
-
// istanbul ignore else
|
|
1347
1104
|
if (this.matterbridge.bridgeMode === 'bridge') {
|
|
1348
1105
|
if (this.matterbridge.serverNode)
|
|
1349
1106
|
serverNodes.push(this.matterbridge.serverNode);
|
|
@@ -1354,7 +1111,6 @@ export class Frontend extends EventEmitter {
|
|
|
1354
1111
|
serverNodes.push(plugin.serverNode);
|
|
1355
1112
|
}
|
|
1356
1113
|
}
|
|
1357
|
-
// istanbul ignore next
|
|
1358
1114
|
for (const device of this.matterbridge.devices.array()) {
|
|
1359
1115
|
if (device.serverNode)
|
|
1360
1116
|
serverNodes.push(device.serverNode);
|
|
@@ -1378,15 +1134,8 @@ export class Frontend extends EventEmitter {
|
|
|
1378
1134
|
values: [...serverNodes],
|
|
1379
1135
|
})));
|
|
1380
1136
|
delete Logger.destinations.diagnostic;
|
|
1381
|
-
await wait(500);
|
|
1137
|
+
await wait(500);
|
|
1382
1138
|
}
|
|
1383
|
-
/**
|
|
1384
|
-
* Handles incoming websocket api request messages from the Matterbridge frontend.
|
|
1385
|
-
*
|
|
1386
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1387
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1388
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1389
|
-
*/
|
|
1390
1139
|
async wsMessageHandler(client, message) {
|
|
1391
1140
|
let data;
|
|
1392
1141
|
const sendResponse = (data) => {
|
|
@@ -1404,13 +1153,12 @@ export class Frontend extends EventEmitter {
|
|
|
1404
1153
|
client.send(JSON.stringify(data));
|
|
1405
1154
|
}
|
|
1406
1155
|
else {
|
|
1407
|
-
// istanbul ignore next cause is only a safety check
|
|
1408
1156
|
this.log.error('Cannot send api response, client not connected');
|
|
1409
1157
|
}
|
|
1410
1158
|
};
|
|
1411
1159
|
try {
|
|
1412
1160
|
data = JSON.parse(message.toString());
|
|
1413
|
-
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method)
|
|
1161
|
+
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || data.dst !== 'Matterbridge') {
|
|
1414
1162
|
this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
|
|
1415
1163
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
|
|
1416
1164
|
return;
|
|
@@ -1467,22 +1215,7 @@ export class Frontend extends EventEmitter {
|
|
|
1467
1215
|
}
|
|
1468
1216
|
this.wssSendSnackbarMessage(`Adding plugin ${data.params.pluginNameOrPath}...`, 5);
|
|
1469
1217
|
this.log.debug(`Adding plugin ${data.params.pluginNameOrPath}...`);
|
|
1470
|
-
data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
|
|
1471
|
-
/*
|
|
1472
|
-
const plugin = (await this.server.fetch({ type: 'plugins_add', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginNameOrPath } }, 5000)).response.plugin;
|
|
1473
|
-
if (plugin) {
|
|
1474
|
-
this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
|
|
1475
|
-
await this.server.fetch({ type: 'plugins_load', src: this.server.name, dst: 'plugins', params: { plugin: plugin.name } }, 5000);
|
|
1476
|
-
this.wssSendRestartRequired();
|
|
1477
|
-
this.wssSendRefreshRequired('plugins');
|
|
1478
|
-
this.wssSendRefreshRequired('devices');
|
|
1479
|
-
this.wssSendSnackbarMessage(`Loaded plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
|
|
1480
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1481
|
-
} else {
|
|
1482
|
-
this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
|
|
1483
|
-
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
|
|
1484
|
-
}
|
|
1485
|
-
*/
|
|
1218
|
+
data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
|
|
1486
1219
|
const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
|
|
1487
1220
|
if (plugin) {
|
|
1488
1221
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1496,7 +1229,6 @@ export class Frontend extends EventEmitter {
|
|
|
1496
1229
|
return;
|
|
1497
1230
|
})
|
|
1498
1231
|
.catch((_error) => {
|
|
1499
|
-
//
|
|
1500
1232
|
});
|
|
1501
1233
|
}
|
|
1502
1234
|
else {
|
|
@@ -1511,10 +1243,6 @@ export class Frontend extends EventEmitter {
|
|
|
1511
1243
|
}
|
|
1512
1244
|
this.wssSendSnackbarMessage(`Removing plugin ${data.params.pluginName}...`, 5);
|
|
1513
1245
|
this.log.debug(`Removing plugin ${data.params.pluginName}...`);
|
|
1514
|
-
/*
|
|
1515
|
-
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);
|
|
1516
|
-
await this.server.fetch({ type: 'plugins_remove', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginName } }, 5000);
|
|
1517
|
-
*/
|
|
1518
1246
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1519
1247
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
1520
1248
|
await this.matterbridge.plugins.remove(data.params.pluginName);
|
|
@@ -1550,7 +1278,6 @@ export class Frontend extends EventEmitter {
|
|
|
1550
1278
|
return;
|
|
1551
1279
|
})
|
|
1552
1280
|
.catch((_error) => {
|
|
1553
|
-
//
|
|
1554
1281
|
});
|
|
1555
1282
|
}
|
|
1556
1283
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1576,7 +1303,6 @@ export class Frontend extends EventEmitter {
|
|
|
1576
1303
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1577
1304
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
|
|
1578
1305
|
if (plugin.serverNode) {
|
|
1579
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1580
1306
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1581
1307
|
plugin.serverNode = undefined;
|
|
1582
1308
|
}
|
|
@@ -1586,20 +1312,18 @@ export class Frontend extends EventEmitter {
|
|
|
1586
1312
|
this.matterbridge.devices.remove(device);
|
|
1587
1313
|
}
|
|
1588
1314
|
}
|
|
1589
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1590
1315
|
if (plugin.type === 'DynamicPlatform' && !plugin.locked)
|
|
1591
1316
|
await this.matterbridge.createDynamicPlugin(plugin);
|
|
1592
1317
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
|
|
1593
|
-
plugin.restartRequired = false;
|
|
1318
|
+
plugin.restartRequired = false;
|
|
1594
1319
|
let needRestart = 0;
|
|
1595
1320
|
for (const plugin of this.matterbridge.plugins) {
|
|
1596
1321
|
if (plugin.restartRequired)
|
|
1597
1322
|
needRestart++;
|
|
1598
1323
|
}
|
|
1599
1324
|
if (needRestart === 0) {
|
|
1600
|
-
this.wssSendRestartNotRequired(true);
|
|
1325
|
+
this.wssSendRestartNotRequired(true);
|
|
1601
1326
|
}
|
|
1602
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1603
1327
|
if (plugin.serverNode)
|
|
1604
1328
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1605
1329
|
this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
|
|
@@ -1744,9 +1468,6 @@ export class Frontend extends EventEmitter {
|
|
|
1744
1468
|
this.wssSendRefreshRequired('matter', { matter: { ...matter, advertiseTime: 0, advertising: false } });
|
|
1745
1469
|
}
|
|
1746
1470
|
if (data.params.advertise) {
|
|
1747
|
-
// TODO: matter.js 0.16.0
|
|
1748
|
-
// await serverNode.env.get(DeviceAdvertiser)?.advertise(true);
|
|
1749
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1750
1471
|
const advertiser = serverNode.env.get(DeviceAdvertiser);
|
|
1751
1472
|
if (advertiser && advertiser.advertise && typeof advertiser.advertise === 'function')
|
|
1752
1473
|
await advertiser.advertise(true);
|
|
@@ -1872,22 +1593,22 @@ export class Frontend extends EventEmitter {
|
|
|
1872
1593
|
if (isValidString(data.params.value, 4)) {
|
|
1873
1594
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1874
1595
|
if (data.params.value === 'Debug') {
|
|
1875
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1596
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1876
1597
|
}
|
|
1877
1598
|
else if (data.params.value === 'Info') {
|
|
1878
|
-
await this.matterbridge.setLogLevel("info"
|
|
1599
|
+
await this.matterbridge.setLogLevel("info");
|
|
1879
1600
|
}
|
|
1880
1601
|
else if (data.params.value === 'Notice') {
|
|
1881
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1602
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1882
1603
|
}
|
|
1883
1604
|
else if (data.params.value === 'Warn') {
|
|
1884
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1605
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1885
1606
|
}
|
|
1886
1607
|
else if (data.params.value === 'Error') {
|
|
1887
|
-
await this.matterbridge.setLogLevel("error"
|
|
1608
|
+
await this.matterbridge.setLogLevel("error");
|
|
1888
1609
|
}
|
|
1889
1610
|
else if (data.params.value === 'Fatal') {
|
|
1890
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1611
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1891
1612
|
}
|
|
1892
1613
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1893
1614
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1898,7 +1619,6 @@ export class Frontend extends EventEmitter {
|
|
|
1898
1619
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1899
1620
|
this.matterbridge.fileLogger = data.params.value;
|
|
1900
1621
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1901
|
-
// Create the file logger for matterbridge
|
|
1902
1622
|
if (data.params.value)
|
|
1903
1623
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), await this.matterbridge.getLogLevel(), true);
|
|
1904
1624
|
else
|
|
@@ -1928,12 +1648,11 @@ export class Frontend extends EventEmitter {
|
|
|
1928
1648
|
Logger.level = MatterLogLevel.FATAL;
|
|
1929
1649
|
}
|
|
1930
1650
|
this.matterbridge.matterLogLevel = MatterLogLevel.names[Logger.level];
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
1651
|
+
let callbackLogLevel = "notice";
|
|
1652
|
+
if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
|
|
1653
|
+
callbackLogLevel = "info";
|
|
1654
|
+
if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
1655
|
+
callbackLogLevel = "debug";
|
|
1937
1656
|
AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
|
|
1938
1657
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
1939
1658
|
await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.level);
|
|
@@ -1985,7 +1704,6 @@ export class Frontend extends EventEmitter {
|
|
|
1985
1704
|
}
|
|
1986
1705
|
break;
|
|
1987
1706
|
case 'setmatterport':
|
|
1988
|
-
// eslint-disable-next-line no-case-declarations
|
|
1989
1707
|
const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1990
1708
|
if (isValidNumber(port, 5540, 5600)) {
|
|
1991
1709
|
this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
|
|
@@ -2005,7 +1723,6 @@ export class Frontend extends EventEmitter {
|
|
|
2005
1723
|
}
|
|
2006
1724
|
break;
|
|
2007
1725
|
case 'setmatterdiscriminator':
|
|
2008
|
-
// eslint-disable-next-line no-case-declarations
|
|
2009
1726
|
const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
2010
1727
|
if (isValidNumber(discriminator, 0, 4095)) {
|
|
2011
1728
|
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
|
|
@@ -2025,7 +1742,6 @@ export class Frontend extends EventEmitter {
|
|
|
2025
1742
|
}
|
|
2026
1743
|
break;
|
|
2027
1744
|
case 'setmatterpasscode':
|
|
2028
|
-
// eslint-disable-next-line no-case-declarations
|
|
2029
1745
|
const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
2030
1746
|
if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
|
|
2031
1747
|
this.matterbridge.passcode = passcode;
|
|
@@ -2071,19 +1787,15 @@ export class Frontend extends EventEmitter {
|
|
|
2071
1787
|
return;
|
|
2072
1788
|
}
|
|
2073
1789
|
const config = plugin.configJson;
|
|
2074
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2075
1790
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
2076
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
2077
1791
|
if (select === 'serial')
|
|
2078
1792
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
2079
1793
|
if (select === 'name')
|
|
2080
1794
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
2081
1795
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
2082
|
-
// Remove postfix from the serial if it exists
|
|
2083
1796
|
if (config.postfix) {
|
|
2084
1797
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
2085
1798
|
}
|
|
2086
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
2087
1799
|
if (isValidArray(config.whiteList, 1)) {
|
|
2088
1800
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
2089
1801
|
config.whiteList.push(data.params.serial);
|
|
@@ -2092,7 +1804,6 @@ export class Frontend extends EventEmitter {
|
|
|
2092
1804
|
config.whiteList.push(data.params.name);
|
|
2093
1805
|
}
|
|
2094
1806
|
}
|
|
2095
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
2096
1807
|
if (isValidArray(config.blackList, 1)) {
|
|
2097
1808
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
2098
1809
|
config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
|
|
@@ -2120,9 +1831,7 @@ export class Frontend extends EventEmitter {
|
|
|
2120
1831
|
return;
|
|
2121
1832
|
}
|
|
2122
1833
|
const config = plugin.configJson;
|
|
2123
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2124
1834
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
2125
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
2126
1835
|
if (select === 'serial')
|
|
2127
1836
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
2128
1837
|
if (select === 'name')
|
|
@@ -2131,7 +1840,6 @@ export class Frontend extends EventEmitter {
|
|
|
2131
1840
|
if (config.postfix) {
|
|
2132
1841
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
2133
1842
|
}
|
|
2134
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
2135
1843
|
if (isValidArray(config.whiteList, 1)) {
|
|
2136
1844
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
2137
1845
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
|
|
@@ -2140,7 +1848,6 @@ export class Frontend extends EventEmitter {
|
|
|
2140
1848
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
|
|
2141
1849
|
}
|
|
2142
1850
|
}
|
|
2143
|
-
// Add the serial to the blackList
|
|
2144
1851
|
if (isValidArray(config.blackList)) {
|
|
2145
1852
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
2146
1853
|
config.blackList.push(data.params.serial);
|
|
@@ -2163,7 +1870,6 @@ export class Frontend extends EventEmitter {
|
|
|
2163
1870
|
}
|
|
2164
1871
|
}
|
|
2165
1872
|
else {
|
|
2166
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2167
1873
|
const localData = data;
|
|
2168
1874
|
this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
|
|
2169
1875
|
sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
|
|
@@ -2173,46 +1879,23 @@ export class Frontend extends EventEmitter {
|
|
|
2173
1879
|
inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
|
|
2174
1880
|
}
|
|
2175
1881
|
}
|
|
2176
|
-
/**
|
|
2177
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
2178
|
-
*
|
|
2179
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
2180
|
-
* @param {string} time - The time string of the message
|
|
2181
|
-
* @param {string} name - The logger name of the message
|
|
2182
|
-
* @param {string} message - The content of the message.
|
|
2183
|
-
*
|
|
2184
|
-
* @remarks
|
|
2185
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
2186
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
2187
|
-
* The function sends the message to all connected clients.
|
|
2188
|
-
*/
|
|
2189
1882
|
wssSendLogMessage(level, time, name, message) {
|
|
2190
1883
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2191
1884
|
return;
|
|
2192
1885
|
if (!level || !time || !name || !message)
|
|
2193
1886
|
return;
|
|
2194
|
-
// Remove ANSI escape codes from the message
|
|
2195
|
-
// eslint-disable-next-line no-control-regex
|
|
2196
1887
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
2197
|
-
// Remove leading asterisks from the message
|
|
2198
1888
|
message = message.replace(/^\*+/, '');
|
|
2199
|
-
// Replace all occurrences of \t and \n
|
|
2200
1889
|
message = message.replace(/[\t\n]/g, '');
|
|
2201
|
-
// Remove non-printable characters
|
|
2202
|
-
// eslint-disable-next-line no-control-regex
|
|
2203
1890
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
2204
|
-
// Replace all occurrences of \" with "
|
|
2205
1891
|
message = message.replace(/\\"/g, '"');
|
|
2206
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
2207
1892
|
const maxContinuousLength = 100;
|
|
2208
1893
|
const keepStartLength = 20;
|
|
2209
1894
|
const keepEndLength = 20;
|
|
2210
|
-
// Split the message into words
|
|
2211
1895
|
if (level !== 'spawn') {
|
|
2212
1896
|
message = message
|
|
2213
1897
|
.split(' ')
|
|
2214
1898
|
.map((word) => {
|
|
2215
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2216
1899
|
if (word.length > maxContinuousLength) {
|
|
2217
1900
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2218
1901
|
}
|
|
@@ -2220,34 +1903,14 @@ export class Frontend extends EventEmitter {
|
|
|
2220
1903
|
})
|
|
2221
1904
|
.join(' ');
|
|
2222
1905
|
}
|
|
2223
|
-
// Send the message to all connected clients
|
|
2224
1906
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
|
|
2225
1907
|
}
|
|
2226
|
-
/**
|
|
2227
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2228
|
-
*
|
|
2229
|
-
* @param {string} changed - The changed value.
|
|
2230
|
-
* @param {Record<string, unknown>} params - Additional parameters to send with the message.
|
|
2231
|
-
* possible values for changed:
|
|
2232
|
-
* - 'settings' (when the bridge has started in bridge mode or childbridge mode and when update finds a new version)
|
|
2233
|
-
* - 'plugins'
|
|
2234
|
-
* - 'devices'
|
|
2235
|
-
* - 'matter' with param 'matter' (QRDiv component)
|
|
2236
|
-
* @param {ApiMatter} params.matter - The matter device that has changed. Required if changed is 'matter'.
|
|
2237
|
-
*/
|
|
2238
1908
|
wssSendRefreshRequired(changed, params) {
|
|
2239
1909
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2240
1910
|
return;
|
|
2241
1911
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
2242
|
-
// Send the message to all connected clients
|
|
2243
1912
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
|
|
2244
1913
|
}
|
|
2245
|
-
/**
|
|
2246
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2247
|
-
*
|
|
2248
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
|
|
2249
|
-
* @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
|
|
2250
|
-
*/
|
|
2251
1914
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
2252
1915
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2253
1916
|
return;
|
|
@@ -2256,14 +1919,8 @@ export class Frontend extends EventEmitter {
|
|
|
2256
1919
|
this.matterbridge.fixedRestartRequired = fixed;
|
|
2257
1920
|
if (snackbar === true)
|
|
2258
1921
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
2259
|
-
// Send the message to all connected clients
|
|
2260
1922
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
|
|
2261
1923
|
}
|
|
2262
|
-
/**
|
|
2263
|
-
* Sends a no need to restart WebSocket message to all connected clients.
|
|
2264
|
-
*
|
|
2265
|
-
* @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients. Default is true.
|
|
2266
|
-
*/
|
|
2267
1924
|
wssSendRestartNotRequired(snackbar = true) {
|
|
2268
1925
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2269
1926
|
return;
|
|
@@ -2271,133 +1928,57 @@ export class Frontend extends EventEmitter {
|
|
|
2271
1928
|
this.matterbridge.restartRequired = false;
|
|
2272
1929
|
if (snackbar === true)
|
|
2273
1930
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2274
|
-
// Send the message to all connected clients
|
|
2275
1931
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
|
|
2276
1932
|
}
|
|
2277
|
-
/**
|
|
2278
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
2279
|
-
*
|
|
2280
|
-
* @param {boolean} devVersion - If true, the update is for a development version. Default is false.
|
|
2281
|
-
*/
|
|
2282
1933
|
wssSendUpdateRequired(devVersion = false) {
|
|
2283
1934
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2284
1935
|
return;
|
|
2285
1936
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2286
1937
|
this.matterbridge.updateRequired = true;
|
|
2287
|
-
// Send the message to all connected clients
|
|
2288
1938
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
|
|
2289
1939
|
}
|
|
2290
|
-
/**
|
|
2291
|
-
* Sends a cpu update message to all connected clients.
|
|
2292
|
-
*
|
|
2293
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2294
|
-
* @param {number} processCpuUsage - The CPU usage percentage of the process to send.
|
|
2295
|
-
*/
|
|
2296
1940
|
wssSendCpuUpdate(cpuUsage, processCpuUsage) {
|
|
2297
1941
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2298
1942
|
return;
|
|
2299
1943
|
if (hasParameter('debug'))
|
|
2300
1944
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2301
|
-
// Send the message to all connected clients
|
|
2302
1945
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', success: true, response: { cpuUsage: Math.round(cpuUsage * 100) / 100, processCpuUsage: Math.round(processCpuUsage * 100) / 100 } });
|
|
2303
1946
|
}
|
|
2304
|
-
/**
|
|
2305
|
-
* Sends a memory update message to all connected clients.
|
|
2306
|
-
*
|
|
2307
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2308
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2309
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2310
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2311
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2312
|
-
* @param {string} external - The external memory in bytes.
|
|
2313
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2314
|
-
*/
|
|
2315
1947
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2316
1948
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2317
1949
|
return;
|
|
2318
1950
|
if (hasParameter('debug'))
|
|
2319
1951
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2320
|
-
// Send the message to all connected clients
|
|
2321
1952
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', success: true, response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
|
|
2322
1953
|
}
|
|
2323
|
-
/**
|
|
2324
|
-
* Sends an uptime update message to all connected clients.
|
|
2325
|
-
*
|
|
2326
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2327
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2328
|
-
*/
|
|
2329
1954
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2330
1955
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2331
1956
|
return;
|
|
2332
1957
|
if (hasParameter('debug'))
|
|
2333
1958
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2334
|
-
// Send the message to all connected clients
|
|
2335
1959
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
|
|
2336
1960
|
}
|
|
2337
|
-
/**
|
|
2338
|
-
* Sends an open snackbar message to all connected clients.
|
|
2339
|
-
*
|
|
2340
|
-
* @param {string} message - The message to send.
|
|
2341
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message. Default is 5 seconds.
|
|
2342
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the message.
|
|
2343
|
-
* possible values are: 'info', 'warning', 'error', 'success'. Default is 'info'.
|
|
2344
|
-
*
|
|
2345
|
-
* @remarks
|
|
2346
|
-
* If timeout is 0, the snackbar message will be displayed until closed by the user.
|
|
2347
|
-
*/
|
|
2348
1961
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2349
1962
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2350
1963
|
return;
|
|
2351
1964
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2352
|
-
// Send the message to all connected clients
|
|
2353
1965
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
|
|
2354
1966
|
}
|
|
2355
|
-
/**
|
|
2356
|
-
* Sends a close snackbar message to all connected clients.
|
|
2357
|
-
* It will close the snackbar message with the same message and timeout = 0.
|
|
2358
|
-
*
|
|
2359
|
-
* @param {string} message - The message to send.
|
|
2360
|
-
*/
|
|
2361
1967
|
wssSendCloseSnackbarMessage(message) {
|
|
2362
1968
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2363
1969
|
return;
|
|
2364
1970
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2365
|
-
// Send the message to all connected clients
|
|
2366
1971
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
|
|
2367
1972
|
}
|
|
2368
|
-
/**
|
|
2369
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2370
|
-
*
|
|
2371
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2372
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2373
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2374
|
-
* @param {EndpointNumber} number - The endpoint number where the attribute belongs.
|
|
2375
|
-
* @param {string} id - The endpoint id where the attribute belongs.
|
|
2376
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2377
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2378
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2379
|
-
*
|
|
2380
|
-
* @remarks
|
|
2381
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2382
|
-
* with the updated attribute information.
|
|
2383
|
-
*/
|
|
2384
1973
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
|
|
2385
1974
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2386
1975
|
return;
|
|
2387
1976
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2388
|
-
// Send the message to all connected clients
|
|
2389
1977
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', success: true, response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value } });
|
|
2390
1978
|
}
|
|
2391
|
-
/**
|
|
2392
|
-
* Sends a message to all connected clients.
|
|
2393
|
-
* This is an helper function to send a broadcast message to all connected clients.
|
|
2394
|
-
*
|
|
2395
|
-
* @param {WsMessageBroadcast} msg - The message to send.
|
|
2396
|
-
*/
|
|
2397
1979
|
wssBroadcastMessage(msg) {
|
|
2398
1980
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2399
1981
|
return;
|
|
2400
|
-
// Send the message to all connected clients
|
|
2401
1982
|
const stringifiedMsg = JSON.stringify(msg);
|
|
2402
1983
|
if (msg.method !== 'log')
|
|
2403
1984
|
this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
|
|
@@ -2408,4 +1989,3 @@ export class Frontend extends EventEmitter {
|
|
|
2408
1989
|
});
|
|
2409
1990
|
}
|
|
2410
1991
|
}
|
|
2411
|
-
//# sourceMappingURL=frontend.js.map
|