matterbridge 3.3.4 → 3.3.5-dev-20251025-26d5c31
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 +10 -0
- package/dist/broadcastServer.js +1 -92
- package/dist/broadcastServerTypes.js +0 -24
- package/dist/cli.js +1 -97
- package/dist/cliEmitter.js +0 -37
- package/dist/cliHistory.js +0 -38
- package/dist/clusters/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -24
- package/dist/deviceManager.js +1 -124
- package/dist/devices/airConditioner.js +0 -57
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/cooktop.js +0 -55
- package/dist/devices/dishwasher.js +0 -57
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.js +0 -5
- package/dist/devices/extractorHood.js +0 -42
- package/dist/devices/heatPump.js +2 -50
- package/dist/devices/laundryDryer.js +3 -62
- package/dist/devices/laundryWasher.js +4 -70
- package/dist/devices/microwaveOven.js +5 -88
- package/dist/devices/oven.js +0 -85
- package/dist/devices/refrigerator.js +0 -102
- package/dist/devices/roboticVacuumCleaner.js +9 -100
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/speaker.js +0 -84
- package/dist/devices/temperatureControl.js +3 -24
- package/dist/devices/waterHeater.js +2 -82
- package/dist/dgram/coap.js +13 -126
- package/dist/dgram/dgram.js +2 -114
- package/dist/dgram/mb_coap.js +3 -41
- package/dist/dgram/mb_mdns.js +15 -80
- package/dist/dgram/mdns.js +137 -299
- package/dist/dgram/multicast.js +1 -62
- package/dist/dgram/unicast.js +0 -54
- package/dist/frontend.js +34 -436
- package/dist/frontendTypes.js +0 -45
- package/dist/helpers.js +0 -53
- package/dist/index.js +0 -25
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.js +0 -3
- package/dist/matter/types.js +0 -3
- package/dist/matterbridge.js +50 -828
- package/dist/matterbridgeAccessoryPlatform.js +0 -37
- package/dist/matterbridgeBehaviors.js +5 -68
- package/dist/matterbridgeDeviceTypes.js +17 -638
- package/dist/matterbridgeDynamicPlatform.js +0 -37
- package/dist/matterbridgeEndpoint.js +52 -1402
- package/dist/matterbridgeEndpointHelpers.js +19 -464
- package/dist/matterbridgePlatform.js +1 -341
- package/dist/matterbridgeTypes.js +0 -26
- package/dist/pluginManager.js +3 -319
- package/dist/shelly.js +7 -168
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -69
- package/dist/utils/colorUtils.js +2 -97
- package/dist/utils/commandLine.js +0 -60
- package/dist/utils/copyDirectory.js +1 -38
- package/dist/utils/createDirectory.js +0 -33
- package/dist/utils/createZip.js +2 -47
- package/dist/utils/deepCopy.js +0 -39
- package/dist/utils/deepEqual.js +1 -72
- package/dist/utils/error.js +0 -41
- package/dist/utils/export.js +0 -1
- package/dist/utils/format.js +0 -49
- package/dist/utils/hex.js +0 -124
- package/dist/utils/inspector.js +1 -69
- package/dist/utils/isvalid.js +0 -101
- package/dist/utils/jestHelpers.js +3 -153
- package/dist/utils/network.js +5 -96
- package/dist/utils/spawn.js +0 -71
- package/dist/utils/tracker.js +1 -64
- package/dist/utils/wait.js +8 -60
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- package/dist/broadcastServer.d.ts +0 -112
- package/dist/broadcastServer.d.ts.map +0 -1
- package/dist/broadcastServer.js.map +0 -1
- package/dist/broadcastServerTypes.d.ts +0 -793
- package/dist/broadcastServerTypes.d.ts.map +0 -1
- package/dist/broadcastServerTypes.js.map +0 -1
- package/dist/cli.d.ts +0 -30
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cliEmitter.d.ts +0 -50
- package/dist/cliEmitter.d.ts.map +0 -1
- package/dist/cliEmitter.js.map +0 -1
- package/dist/cliHistory.d.ts +0 -48
- package/dist/cliHistory.d.ts.map +0 -1
- package/dist/cliHistory.js.map +0 -1
- package/dist/clusters/export.d.ts +0 -2
- package/dist/clusters/export.d.ts.map +0 -1
- package/dist/clusters/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -28
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -117
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/devices/airConditioner.d.ts +0 -98
- package/dist/devices/airConditioner.d.ts.map +0 -1
- package/dist/devices/airConditioner.js.map +0 -1
- package/dist/devices/batteryStorage.d.ts +0 -48
- package/dist/devices/batteryStorage.d.ts.map +0 -1
- package/dist/devices/batteryStorage.js.map +0 -1
- package/dist/devices/cooktop.d.ts +0 -60
- package/dist/devices/cooktop.d.ts.map +0 -1
- package/dist/devices/cooktop.js.map +0 -1
- package/dist/devices/dishwasher.d.ts +0 -71
- package/dist/devices/dishwasher.d.ts.map +0 -1
- package/dist/devices/dishwasher.js.map +0 -1
- package/dist/devices/evse.d.ts +0 -76
- package/dist/devices/evse.d.ts.map +0 -1
- package/dist/devices/evse.js.map +0 -1
- package/dist/devices/export.d.ts +0 -17
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.js.map +0 -1
- package/dist/devices/extractorHood.d.ts +0 -46
- package/dist/devices/extractorHood.d.ts.map +0 -1
- package/dist/devices/extractorHood.js.map +0 -1
- package/dist/devices/heatPump.d.ts +0 -47
- package/dist/devices/heatPump.d.ts.map +0 -1
- package/dist/devices/heatPump.js.map +0 -1
- package/dist/devices/laundryDryer.d.ts +0 -67
- package/dist/devices/laundryDryer.d.ts.map +0 -1
- package/dist/devices/laundryDryer.js.map +0 -1
- package/dist/devices/laundryWasher.d.ts +0 -81
- package/dist/devices/laundryWasher.d.ts.map +0 -1
- package/dist/devices/laundryWasher.js.map +0 -1
- package/dist/devices/microwaveOven.d.ts +0 -168
- package/dist/devices/microwaveOven.d.ts.map +0 -1
- package/dist/devices/microwaveOven.js.map +0 -1
- package/dist/devices/oven.d.ts +0 -105
- package/dist/devices/oven.d.ts.map +0 -1
- package/dist/devices/oven.js.map +0 -1
- package/dist/devices/refrigerator.d.ts +0 -118
- package/dist/devices/refrigerator.d.ts.map +0 -1
- package/dist/devices/refrigerator.js.map +0 -1
- package/dist/devices/roboticVacuumCleaner.d.ts +0 -112
- package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
- package/dist/devices/roboticVacuumCleaner.js.map +0 -1
- package/dist/devices/solarPower.d.ts +0 -40
- package/dist/devices/solarPower.d.ts.map +0 -1
- package/dist/devices/solarPower.js.map +0 -1
- package/dist/devices/speaker.d.ts +0 -87
- package/dist/devices/speaker.d.ts.map +0 -1
- package/dist/devices/speaker.js.map +0 -1
- package/dist/devices/temperatureControl.d.ts +0 -166
- package/dist/devices/temperatureControl.d.ts.map +0 -1
- package/dist/devices/temperatureControl.js.map +0 -1
- package/dist/devices/waterHeater.d.ts +0 -111
- package/dist/devices/waterHeater.d.ts.map +0 -1
- package/dist/devices/waterHeater.js.map +0 -1
- package/dist/dgram/coap.d.ts +0 -205
- package/dist/dgram/coap.d.ts.map +0 -1
- package/dist/dgram/coap.js.map +0 -1
- package/dist/dgram/dgram.d.ts +0 -141
- package/dist/dgram/dgram.d.ts.map +0 -1
- package/dist/dgram/dgram.js.map +0 -1
- package/dist/dgram/mb_coap.d.ts +0 -24
- package/dist/dgram/mb_coap.d.ts.map +0 -1
- package/dist/dgram/mb_coap.js.map +0 -1
- package/dist/dgram/mb_mdns.d.ts +0 -24
- package/dist/dgram/mb_mdns.d.ts.map +0 -1
- package/dist/dgram/mb_mdns.js.map +0 -1
- package/dist/dgram/mdns.d.ts +0 -290
- package/dist/dgram/mdns.d.ts.map +0 -1
- package/dist/dgram/mdns.js.map +0 -1
- package/dist/dgram/multicast.d.ts +0 -67
- package/dist/dgram/multicast.d.ts.map +0 -1
- package/dist/dgram/multicast.js.map +0 -1
- package/dist/dgram/unicast.d.ts +0 -56
- package/dist/dgram/unicast.d.ts.map +0 -1
- package/dist/dgram/unicast.js.map +0 -1
- package/dist/frontend.d.ts +0 -235
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/frontendTypes.d.ts +0 -529
- package/dist/frontendTypes.d.ts.map +0 -1
- package/dist/frontendTypes.js.map +0 -1
- package/dist/helpers.d.ts +0 -48
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.js.map +0 -1
- package/dist/index.d.ts +0 -33
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -475
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -2404
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -770
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -1550
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -758
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -402
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -226
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -347
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -174
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts +0 -75
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -99
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/commandLine.d.ts +0 -66
- package/dist/utils/commandLine.d.ts.map +0 -1
- package/dist/utils/commandLine.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -33
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createDirectory.d.ts +0 -34
- package/dist/utils/createDirectory.d.ts.map +0 -1
- package/dist/utils/createDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -39
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -32
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -54
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/error.d.ts +0 -44
- package/dist/utils/error.d.ts.map +0 -1
- package/dist/utils/error.js.map +0 -1
- package/dist/utils/export.d.ts +0 -13
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/format.d.ts +0 -53
- package/dist/utils/format.d.ts.map +0 -1
- package/dist/utils/format.js.map +0 -1
- package/dist/utils/hex.d.ts +0 -89
- package/dist/utils/hex.d.ts.map +0 -1
- package/dist/utils/hex.js.map +0 -1
- package/dist/utils/inspector.d.ts +0 -87
- package/dist/utils/inspector.d.ts.map +0 -1
- package/dist/utils/inspector.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -103
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/jestHelpers.d.ts +0 -139
- package/dist/utils/jestHelpers.d.ts.map +0 -1
- package/dist/utils/jestHelpers.js.map +0 -1
- package/dist/utils/network.d.ts +0 -101
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/spawn.d.ts +0 -35
- package/dist/utils/spawn.d.ts.map +0 -1
- package/dist/utils/spawn.js.map +0 -1
- package/dist/utils/tracker.d.ts +0 -108
- package/dist/utils/tracker.d.ts.map +0 -1
- package/dist/utils/tracker.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -54
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
package/dist/frontend.js
CHANGED
|
@@ -1,34 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Frontend.
|
|
3
|
-
*
|
|
4
|
-
* @file frontend.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @created 2025-01-13
|
|
7
|
-
* @version 1.3.0
|
|
8
|
-
* @license Apache-2.0
|
|
9
|
-
*
|
|
10
|
-
* Copyright 2025, 2026, 2027 Luca Liguori.
|
|
11
|
-
*
|
|
12
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
-
* you may not use this file except in compliance with the License.
|
|
14
|
-
* You may obtain a copy of the License at
|
|
15
|
-
*
|
|
16
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
-
*
|
|
18
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
19
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
-
* See the License for the specific language governing permissions and
|
|
22
|
-
* limitations under the License.
|
|
23
|
-
*/
|
|
24
|
-
// eslint-disable-next-line no-console
|
|
25
1
|
if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
26
2
|
console.log('\u001B[32mFrontend loaded.\u001B[40;0m');
|
|
27
|
-
// Node modules
|
|
28
3
|
import os from 'node:os';
|
|
29
4
|
import path from 'node:path';
|
|
30
5
|
import EventEmitter from 'node:events';
|
|
31
|
-
// AnsiLogger module
|
|
32
6
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt } from 'node-ansi-logger';
|
|
33
7
|
import { Logger, Diagnostic, LogDestination, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/general';
|
|
34
8
|
import { DeviceAdvertiser, DeviceCommissioner, FabricManager } from '@matter/protocol';
|
|
@@ -60,7 +34,7 @@ export class Frontend extends EventEmitter {
|
|
|
60
34
|
constructor(matterbridge) {
|
|
61
35
|
super();
|
|
62
36
|
this.matterbridge = matterbridge;
|
|
63
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
37
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
64
38
|
this.log.logNameColor = '\x1b[38;5;97m';
|
|
65
39
|
this.server = new BroadcastServer('frontend', this.log);
|
|
66
40
|
this.server.on('broadcast_message', this.msgHandler.bind(this));
|
|
@@ -120,42 +94,13 @@ export class Frontend extends EventEmitter {
|
|
|
120
94
|
async start(port = 8283) {
|
|
121
95
|
this.port = port;
|
|
122
96
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
123
|
-
// Initialize multer with the upload directory
|
|
124
97
|
const multer = await import('multer');
|
|
125
|
-
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
98
|
+
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
126
99
|
const upload = multer.default({ dest: uploadDir });
|
|
127
|
-
// Create the express app that serves the frontend
|
|
128
100
|
const express = await import('express');
|
|
129
101
|
this.expressApp = express.default();
|
|
130
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
131
|
-
/*
|
|
132
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
133
|
-
for (const method of methods) {
|
|
134
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
135
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
136
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
137
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
138
|
-
try {
|
|
139
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
140
|
-
return original(path, ...rest);
|
|
141
|
-
} catch (err) {
|
|
142
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
143
|
-
throw err;
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
*/
|
|
148
|
-
// Log all requests to the server for debugging
|
|
149
|
-
/*
|
|
150
|
-
this.expressApp.use((req, res, next) => {
|
|
151
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
152
|
-
next();
|
|
153
|
-
});
|
|
154
|
-
*/
|
|
155
|
-
// Serve static files from 'frontend/build' directory
|
|
156
102
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
157
103
|
if (!hasParameter('ssl')) {
|
|
158
|
-
// Create an HTTP server and attach the express app
|
|
159
104
|
const http = await import('node:http');
|
|
160
105
|
try {
|
|
161
106
|
this.log.debug(`Creating HTTP server...`);
|
|
@@ -166,7 +111,6 @@ export class Frontend extends EventEmitter {
|
|
|
166
111
|
this.emit('server_error', error);
|
|
167
112
|
return;
|
|
168
113
|
}
|
|
169
|
-
// Listen on the specified port
|
|
170
114
|
if (hasParameter('ingress')) {
|
|
171
115
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
172
116
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -199,7 +143,6 @@ export class Frontend extends EventEmitter {
|
|
|
199
143
|
});
|
|
200
144
|
}
|
|
201
145
|
else {
|
|
202
|
-
// SSL is enabled, load the certificate and the private key
|
|
203
146
|
let cert;
|
|
204
147
|
let key;
|
|
205
148
|
let ca;
|
|
@@ -209,7 +152,6 @@ export class Frontend extends EventEmitter {
|
|
|
209
152
|
let httpsServerOptions = {};
|
|
210
153
|
const fs = await import('node:fs');
|
|
211
154
|
if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
|
|
212
|
-
// Load the p12 certificate and the passphrase
|
|
213
155
|
try {
|
|
214
156
|
pfx = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
|
|
215
157
|
this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
|
|
@@ -221,7 +163,7 @@ export class Frontend extends EventEmitter {
|
|
|
221
163
|
}
|
|
222
164
|
try {
|
|
223
165
|
passphrase = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
|
|
224
|
-
passphrase = passphrase.trim();
|
|
166
|
+
passphrase = passphrase.trim();
|
|
225
167
|
this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
|
|
226
168
|
}
|
|
227
169
|
catch (error) {
|
|
@@ -232,7 +174,6 @@ export class Frontend extends EventEmitter {
|
|
|
232
174
|
httpsServerOptions = { pfx, passphrase };
|
|
233
175
|
}
|
|
234
176
|
else {
|
|
235
|
-
// 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.
|
|
236
177
|
try {
|
|
237
178
|
cert = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
238
179
|
this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
|
|
@@ -262,10 +203,9 @@ export class Frontend extends EventEmitter {
|
|
|
262
203
|
httpsServerOptions = { cert: fullChain ?? cert, key, ca };
|
|
263
204
|
}
|
|
264
205
|
if (hasParameter('mtls')) {
|
|
265
|
-
httpsServerOptions.requestCert = true;
|
|
266
|
-
httpsServerOptions.rejectUnauthorized = true;
|
|
206
|
+
httpsServerOptions.requestCert = true;
|
|
207
|
+
httpsServerOptions.rejectUnauthorized = true;
|
|
267
208
|
}
|
|
268
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
269
209
|
const https = await import('node:https');
|
|
270
210
|
try {
|
|
271
211
|
this.log.debug(`Creating HTTPS server...`);
|
|
@@ -276,7 +216,6 @@ export class Frontend extends EventEmitter {
|
|
|
276
216
|
this.emit('server_error', error);
|
|
277
217
|
return;
|
|
278
218
|
}
|
|
279
|
-
// Listen on the specified port
|
|
280
219
|
if (hasParameter('ingress')) {
|
|
281
220
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
282
221
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -308,41 +247,16 @@ export class Frontend extends EventEmitter {
|
|
|
308
247
|
return;
|
|
309
248
|
});
|
|
310
249
|
}
|
|
311
|
-
// Load the stored password
|
|
312
|
-
/*
|
|
313
|
-
let storedPassword = '';
|
|
314
|
-
try {
|
|
315
|
-
if (!this.matterbridge.nodeContext) throw new Error('nodeContext not found');
|
|
316
|
-
storedPassword = await this.matterbridge.nodeContext.get('password', '');
|
|
317
|
-
} catch (error) {
|
|
318
|
-
inspectError(this.log, 'Error getting password', error);
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
*/
|
|
322
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
323
250
|
const ws = await import('ws');
|
|
324
251
|
this.log.debug(`Creating WebSocketServer...`);
|
|
325
252
|
this.webSocketServer = new ws.WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
326
253
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
327
254
|
const clientIp = request.socket.remoteAddress;
|
|
328
|
-
|
|
329
|
-
if (
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if (password !== storedPassword) {
|
|
334
|
-
this.log.error(`WebSocket client "${clientIp}" failed authentication: ${storedPassword}-${password}`);
|
|
335
|
-
// ws.close();
|
|
336
|
-
// return;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
*/
|
|
340
|
-
// Set the global logger callback for the WebSocketServer
|
|
341
|
-
let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
|
|
342
|
-
if (this.matterbridge.getLogLevel() === "info" /* LogLevel.INFO */ || Logger.level === MatterLogLevel.INFO)
|
|
343
|
-
callbackLogLevel = "info" /* LogLevel.INFO */;
|
|
344
|
-
if (this.matterbridge.getLogLevel() === "debug" /* LogLevel.DEBUG */ || Logger.level === MatterLogLevel.DEBUG)
|
|
345
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
255
|
+
let callbackLogLevel = "notice";
|
|
256
|
+
if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
|
|
257
|
+
callbackLogLevel = "info";
|
|
258
|
+
if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
259
|
+
callbackLogLevel = "debug";
|
|
346
260
|
AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
|
|
347
261
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
348
262
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -364,7 +278,6 @@ export class Frontend extends EventEmitter {
|
|
|
364
278
|
}
|
|
365
279
|
});
|
|
366
280
|
ws.on('error', (error) => {
|
|
367
|
-
// istanbul ignore next
|
|
368
281
|
this.log.error(`WebSocket client error: ${error}`);
|
|
369
282
|
});
|
|
370
283
|
});
|
|
@@ -378,7 +291,6 @@ export class Frontend extends EventEmitter {
|
|
|
378
291
|
this.webSocketServer.on('error', (ws, error) => {
|
|
379
292
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
380
293
|
});
|
|
381
|
-
// Subscribe to cli events
|
|
382
294
|
cliEmitter.removeAllListeners();
|
|
383
295
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
384
296
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -389,8 +301,6 @@ export class Frontend extends EventEmitter {
|
|
|
389
301
|
cliEmitter.on('cpu', (cpuUsage, processCpuUsage) => {
|
|
390
302
|
this.wssSendCpuUpdate(cpuUsage, processCpuUsage);
|
|
391
303
|
});
|
|
392
|
-
// Endpoint to validate login code
|
|
393
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
394
304
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
395
305
|
const { password } = req.body;
|
|
396
306
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -409,27 +319,23 @@ export class Frontend extends EventEmitter {
|
|
|
409
319
|
this.log.warn('/api/login error wrong password');
|
|
410
320
|
res.json({ valid: false });
|
|
411
321
|
}
|
|
412
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
413
322
|
}
|
|
414
323
|
catch (error) {
|
|
415
324
|
this.log.error('/api/login error getting password');
|
|
416
325
|
res.json({ valid: false });
|
|
417
326
|
}
|
|
418
327
|
});
|
|
419
|
-
// Endpoint to provide health check for docker
|
|
420
328
|
this.expressApp.get('/health', (req, res) => {
|
|
421
329
|
this.log.debug('Express received /health');
|
|
422
330
|
const healthStatus = {
|
|
423
|
-
status: 'ok',
|
|
424
|
-
uptime: process.uptime(),
|
|
425
|
-
timestamp: new Date().toISOString(),
|
|
331
|
+
status: 'ok',
|
|
332
|
+
uptime: process.uptime(),
|
|
333
|
+
timestamp: new Date().toISOString(),
|
|
426
334
|
};
|
|
427
335
|
res.status(200).json(healthStatus);
|
|
428
336
|
});
|
|
429
|
-
// Endpoint to provide memory usage details
|
|
430
337
|
this.expressApp.get('/memory', async (req, res) => {
|
|
431
338
|
this.log.debug('Express received /memory');
|
|
432
|
-
// Memory usage from process
|
|
433
339
|
const memoryUsageRaw = process.memoryUsage();
|
|
434
340
|
const memoryUsage = {
|
|
435
341
|
rss: formatBytes(memoryUsageRaw.rss),
|
|
@@ -438,13 +344,10 @@ export class Frontend extends EventEmitter {
|
|
|
438
344
|
external: formatBytes(memoryUsageRaw.external),
|
|
439
345
|
arrayBuffers: formatBytes(memoryUsageRaw.arrayBuffers),
|
|
440
346
|
};
|
|
441
|
-
// V8 heap statistics
|
|
442
347
|
const { default: v8 } = await import('node:v8');
|
|
443
348
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
444
349
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
445
|
-
// Format heapStats
|
|
446
350
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, formatBytes(value)]));
|
|
447
|
-
// Format heapSpaces
|
|
448
351
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
449
352
|
...space,
|
|
450
353
|
space_size: formatBytes(space.space_size),
|
|
@@ -463,22 +366,18 @@ export class Frontend extends EventEmitter {
|
|
|
463
366
|
};
|
|
464
367
|
res.status(200).json(memoryReport);
|
|
465
368
|
});
|
|
466
|
-
// Endpoint to provide settings
|
|
467
369
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
468
370
|
this.log.debug('The frontend sent /api/settings');
|
|
469
371
|
res.json(await this.getApiSettings());
|
|
470
372
|
});
|
|
471
|
-
// Endpoint to provide plugins
|
|
472
373
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
473
374
|
this.log.debug('The frontend sent /api/plugins');
|
|
474
375
|
res.json(this.matterbridge.hasCleanupStarted ? [] : this.getPlugins());
|
|
475
376
|
});
|
|
476
|
-
// Endpoint to provide devices
|
|
477
377
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
478
378
|
this.log.debug('The frontend sent /api/devices');
|
|
479
379
|
res.json(this.matterbridge.hasCleanupStarted ? [] : this.getDevices());
|
|
480
380
|
});
|
|
481
|
-
// Endpoint to view the matterbridge log
|
|
482
381
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
483
382
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
484
383
|
try {
|
|
@@ -492,7 +391,6 @@ export class Frontend extends EventEmitter {
|
|
|
492
391
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
493
392
|
}
|
|
494
393
|
});
|
|
495
|
-
// Endpoint to view the matter.js log
|
|
496
394
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
497
395
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
498
396
|
try {
|
|
@@ -506,7 +404,6 @@ export class Frontend extends EventEmitter {
|
|
|
506
404
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
507
405
|
}
|
|
508
406
|
});
|
|
509
|
-
// Endpoint to view the diagnostic.log
|
|
510
407
|
this.expressApp.get('/api/view-diagnostic', async (req, res) => {
|
|
511
408
|
this.log.debug('The frontend sent /api/view-diagnostic');
|
|
512
409
|
await this.generateDiagnostic();
|
|
@@ -517,13 +414,10 @@ export class Frontend extends EventEmitter {
|
|
|
517
414
|
res.send(data.slice(29));
|
|
518
415
|
}
|
|
519
416
|
catch (error) {
|
|
520
|
-
// istanbul ignore next
|
|
521
417
|
this.log.error(`Error reading diagnostic log file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
522
|
-
// istanbul ignore next
|
|
523
418
|
res.status(500).send('Error reading diagnostic log file.');
|
|
524
419
|
}
|
|
525
420
|
});
|
|
526
|
-
// Endpoint to download the diagnostic.log
|
|
527
421
|
this.expressApp.get('/api/download-diagnostic', async (req, res) => {
|
|
528
422
|
this.log.debug(`The frontend sent /api/download-diagnostic`);
|
|
529
423
|
await this.generateDiagnostic();
|
|
@@ -534,19 +428,16 @@ export class Frontend extends EventEmitter {
|
|
|
534
428
|
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), data, 'utf-8');
|
|
535
429
|
}
|
|
536
430
|
catch (error) {
|
|
537
|
-
// istanbul ignore next
|
|
538
431
|
this.log.debug(`Error in /api/download-diagnostic: ${error instanceof Error ? error.message : error}`);
|
|
539
432
|
}
|
|
540
433
|
res.type('text/plain');
|
|
541
434
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), MATTERBRIDGE_DIAGNOSTIC_FILE, (error) => {
|
|
542
|
-
/* istanbul ignore if */
|
|
543
435
|
if (error) {
|
|
544
436
|
this.log.error(`Error downloading file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
545
437
|
res.status(500).send('Error downloading the diagnostic log file');
|
|
546
438
|
}
|
|
547
439
|
});
|
|
548
440
|
});
|
|
549
|
-
// Endpoint to view the history.html
|
|
550
441
|
this.expressApp.get('/api/viewhistory', async (req, res) => {
|
|
551
442
|
this.log.debug('The frontend sent /api/viewhistory');
|
|
552
443
|
try {
|
|
@@ -560,7 +451,6 @@ export class Frontend extends EventEmitter {
|
|
|
560
451
|
res.status(500).send('Error reading history file.');
|
|
561
452
|
}
|
|
562
453
|
});
|
|
563
|
-
// Endpoint to download the history.html
|
|
564
454
|
this.expressApp.get('/api/downloadhistory', async (req, res) => {
|
|
565
455
|
this.log.debug(`The frontend sent /api/downloadhistory`);
|
|
566
456
|
try {
|
|
@@ -570,7 +460,6 @@ export class Frontend extends EventEmitter {
|
|
|
570
460
|
await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), data, 'utf-8');
|
|
571
461
|
res.type('text/plain');
|
|
572
462
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), MATTERBRIDGE_HISTORY_FILE, (error) => {
|
|
573
|
-
/* istanbul ignore if */
|
|
574
463
|
if (error) {
|
|
575
464
|
this.log.error(`Error in /api/downloadhistory downloading history file ${MATTERBRIDGE_HISTORY_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
576
465
|
res.status(500).send('Error downloading history file');
|
|
@@ -582,7 +471,6 @@ export class Frontend extends EventEmitter {
|
|
|
582
471
|
res.status(500).send('Error reading history file.');
|
|
583
472
|
}
|
|
584
473
|
});
|
|
585
|
-
// Endpoint to view the shelly log
|
|
586
474
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
587
475
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
588
476
|
try {
|
|
@@ -596,7 +484,6 @@ export class Frontend extends EventEmitter {
|
|
|
596
484
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
597
485
|
}
|
|
598
486
|
});
|
|
599
|
-
// Endpoint to download the matterbridge log
|
|
600
487
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
601
488
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
|
|
602
489
|
const fs = await import('node:fs');
|
|
@@ -611,14 +498,12 @@ export class Frontend extends EventEmitter {
|
|
|
611
498
|
}
|
|
612
499
|
res.type('text/plain');
|
|
613
500
|
res.download(path.join(os.tmpdir(), MATTERBRIDGE_LOGGER_FILE), 'matterbridge.log', (error) => {
|
|
614
|
-
/* istanbul ignore if */
|
|
615
501
|
if (error) {
|
|
616
502
|
this.log.error(`Error downloading log file ${MATTERBRIDGE_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
617
503
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
618
504
|
}
|
|
619
505
|
});
|
|
620
506
|
});
|
|
621
|
-
// Endpoint to download the matter log
|
|
622
507
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
623
508
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
|
|
624
509
|
const fs = await import('node:fs');
|
|
@@ -633,14 +518,12 @@ export class Frontend extends EventEmitter {
|
|
|
633
518
|
}
|
|
634
519
|
res.type('text/plain');
|
|
635
520
|
res.download(path.join(os.tmpdir(), MATTER_LOGGER_FILE), 'matter.log', (error) => {
|
|
636
|
-
/* istanbul ignore if */
|
|
637
521
|
if (error) {
|
|
638
522
|
this.log.error(`Error downloading log file ${MATTER_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
|
|
639
523
|
res.status(500).send('Error downloading the matter log file');
|
|
640
524
|
}
|
|
641
525
|
});
|
|
642
526
|
});
|
|
643
|
-
// Endpoint to download the shelly log
|
|
644
527
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
645
528
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
646
529
|
const fs = await import('node:fs');
|
|
@@ -655,91 +538,75 @@ export class Frontend extends EventEmitter {
|
|
|
655
538
|
}
|
|
656
539
|
res.type('text/plain');
|
|
657
540
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
658
|
-
/* istanbul ignore if */
|
|
659
541
|
if (error) {
|
|
660
542
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
661
543
|
res.status(500).send('Error downloading Shelly system log file');
|
|
662
544
|
}
|
|
663
545
|
});
|
|
664
546
|
});
|
|
665
|
-
// Endpoint to download the matterbridge storage directory
|
|
666
547
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
667
548
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
668
549
|
await createZip(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), path.join(this.matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR));
|
|
669
550
|
res.download(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), `matterbridge.${NODE_STORAGE_DIR}.zip`, (error) => {
|
|
670
|
-
/* istanbul ignore if */
|
|
671
551
|
if (error) {
|
|
672
552
|
this.log.error(`Error downloading file ${`matterbridge.${NODE_STORAGE_DIR}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
673
553
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
674
554
|
}
|
|
675
555
|
});
|
|
676
556
|
});
|
|
677
|
-
// Endpoint to download the matter storage file
|
|
678
557
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
679
558
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
680
559
|
await createZip(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME));
|
|
681
560
|
res.download(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), `matterbridge.${MATTER_STORAGE_NAME}.zip`, (error) => {
|
|
682
|
-
/* istanbul ignore if */
|
|
683
561
|
if (error) {
|
|
684
562
|
this.log.error(`Error downloading the matter storage matterbridge.${MATTER_STORAGE_NAME}.zip: ${error instanceof Error ? error.message : error}`);
|
|
685
563
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
686
564
|
}
|
|
687
565
|
});
|
|
688
566
|
});
|
|
689
|
-
// Endpoint to download the matterbridge plugin directory
|
|
690
567
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
691
568
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
692
569
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
693
570
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
694
|
-
/* istanbul ignore if */
|
|
695
571
|
if (error) {
|
|
696
572
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
697
573
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
698
574
|
}
|
|
699
575
|
});
|
|
700
576
|
});
|
|
701
|
-
// Endpoint to download the matterbridge plugin config files
|
|
702
577
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
703
578
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
704
579
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
705
580
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
706
|
-
/* istanbul ignore if */
|
|
707
581
|
if (error) {
|
|
708
582
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
709
583
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
710
584
|
}
|
|
711
585
|
});
|
|
712
586
|
});
|
|
713
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
714
587
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
715
588
|
this.log.debug('The frontend sent /api/download-backup');
|
|
716
589
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
717
|
-
/* istanbul ignore if */
|
|
718
590
|
if (error) {
|
|
719
591
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
720
592
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
721
593
|
}
|
|
722
594
|
});
|
|
723
595
|
});
|
|
724
|
-
// Endpoint to upload a package
|
|
725
596
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
726
597
|
const { filename } = req.body;
|
|
727
598
|
const file = req.file;
|
|
728
|
-
/* istanbul ignore if */
|
|
729
599
|
if (!file || !filename) {
|
|
730
600
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
731
601
|
res.status(400).send('Invalid request: file and filename are required');
|
|
732
602
|
return;
|
|
733
603
|
}
|
|
734
604
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
735
|
-
// Define the path where the plugin file will be saved
|
|
736
605
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
737
606
|
try {
|
|
738
|
-
// Move the uploaded file to the specified path
|
|
739
607
|
const fs = await import('node:fs');
|
|
740
608
|
await fs.promises.rename(file.path, filePath);
|
|
741
609
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
742
|
-
// Install the plugin package
|
|
743
610
|
if (filename.endsWith('.tgz')) {
|
|
744
611
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
745
612
|
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], 'install', filename);
|
|
@@ -759,7 +626,6 @@ export class Frontend extends EventEmitter {
|
|
|
759
626
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
760
627
|
}
|
|
761
628
|
});
|
|
762
|
-
// Fallback for routing (must be the last route)
|
|
763
629
|
this.expressApp.use((req, res) => {
|
|
764
630
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
|
|
765
631
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -769,16 +635,13 @@ export class Frontend extends EventEmitter {
|
|
|
769
635
|
async stop() {
|
|
770
636
|
this.log.debug('Stopping the frontend...');
|
|
771
637
|
const ws = await import('ws');
|
|
772
|
-
// Remove listeners from the express app
|
|
773
638
|
if (this.expressApp) {
|
|
774
639
|
this.expressApp.removeAllListeners();
|
|
775
640
|
this.expressApp = undefined;
|
|
776
641
|
this.log.debug('Frontend app closed successfully');
|
|
777
642
|
}
|
|
778
|
-
// Close the WebSocket server
|
|
779
643
|
if (this.webSocketServer) {
|
|
780
644
|
this.log.debug('Closing WebSocket server...');
|
|
781
|
-
// Close all active connections
|
|
782
645
|
this.webSocketServer.clients.forEach((client) => {
|
|
783
646
|
if (client.readyState === ws.WebSocket.OPEN) {
|
|
784
647
|
client.close();
|
|
@@ -787,7 +650,6 @@ export class Frontend extends EventEmitter {
|
|
|
787
650
|
await withTimeout(new Promise((resolve) => {
|
|
788
651
|
this.webSocketServer?.close((error) => {
|
|
789
652
|
if (error) {
|
|
790
|
-
// istanbul ignore next
|
|
791
653
|
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
792
654
|
}
|
|
793
655
|
else {
|
|
@@ -800,27 +662,8 @@ export class Frontend extends EventEmitter {
|
|
|
800
662
|
this.webSocketServer.removeAllListeners();
|
|
801
663
|
this.webSocketServer = undefined;
|
|
802
664
|
}
|
|
803
|
-
// Close the http server
|
|
804
665
|
if (this.httpServer) {
|
|
805
666
|
this.log.debug('Closing http server...');
|
|
806
|
-
/*
|
|
807
|
-
await withTimeout(
|
|
808
|
-
new Promise<void>((resolve) => {
|
|
809
|
-
this.httpServer?.close((error) => {
|
|
810
|
-
if (error) {
|
|
811
|
-
// istanbul ignore next
|
|
812
|
-
this.log.error(`Error closing http server: ${error}`);
|
|
813
|
-
} else {
|
|
814
|
-
this.log.debug('Http server closed successfully');
|
|
815
|
-
this.emit('server_stopped');
|
|
816
|
-
}
|
|
817
|
-
resolve();
|
|
818
|
-
});
|
|
819
|
-
}),
|
|
820
|
-
5000,
|
|
821
|
-
false,
|
|
822
|
-
);
|
|
823
|
-
*/
|
|
824
667
|
this.httpServer.close();
|
|
825
668
|
this.log.debug('Http server closed successfully');
|
|
826
669
|
this.listening = false;
|
|
@@ -829,27 +672,8 @@ export class Frontend extends EventEmitter {
|
|
|
829
672
|
this.httpServer = undefined;
|
|
830
673
|
this.log.debug('Frontend http server closed successfully');
|
|
831
674
|
}
|
|
832
|
-
// Close the https server
|
|
833
675
|
if (this.httpsServer) {
|
|
834
676
|
this.log.debug('Closing https server...');
|
|
835
|
-
/*
|
|
836
|
-
await withTimeout(
|
|
837
|
-
new Promise<void>((resolve) => {
|
|
838
|
-
this.httpsServer?.close((error) => {
|
|
839
|
-
if (error) {
|
|
840
|
-
// istanbul ignore next
|
|
841
|
-
this.log.error(`Error closing https server: ${error}`);
|
|
842
|
-
} else {
|
|
843
|
-
this.log.debug('Https server closed successfully');
|
|
844
|
-
this.emit('server_stopped');
|
|
845
|
-
}
|
|
846
|
-
resolve();
|
|
847
|
-
});
|
|
848
|
-
}),
|
|
849
|
-
5000,
|
|
850
|
-
false,
|
|
851
|
-
);
|
|
852
|
-
*/
|
|
853
677
|
this.httpsServer.close();
|
|
854
678
|
this.log.debug('Https server closed successfully');
|
|
855
679
|
this.listening = false;
|
|
@@ -860,13 +684,7 @@ export class Frontend extends EventEmitter {
|
|
|
860
684
|
}
|
|
861
685
|
this.log.debug('Frontend stopped successfully');
|
|
862
686
|
}
|
|
863
|
-
/**
|
|
864
|
-
* Retrieves the api settings data.
|
|
865
|
-
*
|
|
866
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
867
|
-
*/
|
|
868
687
|
async getApiSettings() {
|
|
869
|
-
// Update the variable system information properties
|
|
870
688
|
this.matterbridge.systemInformation.totalMemory = formatBytes(os.totalmem());
|
|
871
689
|
this.matterbridge.systemInformation.freeMemory = formatBytes(os.freemem());
|
|
872
690
|
this.matterbridge.systemInformation.systemUptime = formatUptime(os.uptime());
|
|
@@ -876,7 +694,6 @@ export class Frontend extends EventEmitter {
|
|
|
876
694
|
this.matterbridge.systemInformation.rss = formatBytes(process.memoryUsage().rss);
|
|
877
695
|
this.matterbridge.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
|
|
878
696
|
this.matterbridge.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
|
|
879
|
-
// Create the matterbridge information
|
|
880
697
|
const info = {
|
|
881
698
|
homeDirectory: this.matterbridge.homeDirectory,
|
|
882
699
|
rootDirectory: this.matterbridge.rootDirectory,
|
|
@@ -912,15 +729,9 @@ export class Frontend extends EventEmitter {
|
|
|
912
729
|
};
|
|
913
730
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: info };
|
|
914
731
|
}
|
|
915
|
-
/**
|
|
916
|
-
* Retrieves the reachable attribute.
|
|
917
|
-
*
|
|
918
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
919
|
-
* @returns {boolean} The reachable attribute.
|
|
920
|
-
*/
|
|
921
732
|
getReachability(device) {
|
|
922
733
|
if (this.matterbridge.hasCleanupStarted)
|
|
923
|
-
return false;
|
|
734
|
+
return false;
|
|
924
735
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
925
736
|
return false;
|
|
926
737
|
if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
|
|
@@ -931,15 +742,9 @@ export class Frontend extends EventEmitter {
|
|
|
931
742
|
return true;
|
|
932
743
|
return false;
|
|
933
744
|
}
|
|
934
|
-
/**
|
|
935
|
-
* Retrieves the power source attribute.
|
|
936
|
-
*
|
|
937
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
938
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
939
|
-
*/
|
|
940
745
|
getPowerSource(endpoint) {
|
|
941
746
|
if (this.matterbridge.hasCleanupStarted)
|
|
942
|
-
return;
|
|
747
|
+
return;
|
|
943
748
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
944
749
|
return undefined;
|
|
945
750
|
const powerSource = (device) => {
|
|
@@ -954,25 +759,16 @@ export class Frontend extends EventEmitter {
|
|
|
954
759
|
}
|
|
955
760
|
return;
|
|
956
761
|
};
|
|
957
|
-
// Root endpoint
|
|
958
762
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
959
763
|
return powerSource(endpoint);
|
|
960
|
-
// Child endpoints
|
|
961
764
|
for (const child of endpoint.getChildEndpoints()) {
|
|
962
765
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
963
766
|
return powerSource(child);
|
|
964
767
|
}
|
|
965
768
|
}
|
|
966
|
-
/**
|
|
967
|
-
* Retrieves the cluster text description from a given device.
|
|
968
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
969
|
-
*
|
|
970
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
971
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
972
|
-
*/
|
|
973
769
|
getClusterTextFromDevice(device) {
|
|
974
770
|
if (this.matterbridge.hasCleanupStarted)
|
|
975
|
-
return '';
|
|
771
|
+
return '';
|
|
976
772
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
977
773
|
return '';
|
|
978
774
|
const getUserLabel = (device) => {
|
|
@@ -982,7 +778,6 @@ export class Frontend extends EventEmitter {
|
|
|
982
778
|
if (composed)
|
|
983
779
|
return 'Composed: ' + composed.value;
|
|
984
780
|
}
|
|
985
|
-
// istanbul ignore next cause is not reachable
|
|
986
781
|
return '';
|
|
987
782
|
};
|
|
988
783
|
const getFixedLabel = (device) => {
|
|
@@ -992,13 +787,11 @@ export class Frontend extends EventEmitter {
|
|
|
992
787
|
if (composed)
|
|
993
788
|
return 'Composed: ' + composed.value;
|
|
994
789
|
}
|
|
995
|
-
// istanbul ignore next cause is not reacheable
|
|
996
790
|
return '';
|
|
997
791
|
};
|
|
998
792
|
let attributes = '';
|
|
999
793
|
let supportedModes = [];
|
|
1000
794
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1001
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
1002
795
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1003
796
|
return;
|
|
1004
797
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1088,17 +881,11 @@ export class Frontend extends EventEmitter {
|
|
|
1088
881
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1089
882
|
attributes += `${getUserLabel(device)} `;
|
|
1090
883
|
});
|
|
1091
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1092
884
|
return attributes.trimStart().trimEnd();
|
|
1093
885
|
}
|
|
1094
|
-
/**
|
|
1095
|
-
* Retrieves the registered plugins sanitized for res.json().
|
|
1096
|
-
*
|
|
1097
|
-
* @returns {ApiPlugin[]} An array of BaseRegisteredPlugin.
|
|
1098
|
-
*/
|
|
1099
886
|
getPlugins() {
|
|
1100
887
|
if (this.matterbridge.hasCleanupStarted)
|
|
1101
|
-
return [];
|
|
888
|
+
return [];
|
|
1102
889
|
const plugins = [];
|
|
1103
890
|
for (const plugin of this.matterbridge.plugins.array()) {
|
|
1104
891
|
plugins.push({
|
|
@@ -1126,27 +913,18 @@ export class Frontend extends EventEmitter {
|
|
|
1126
913
|
schemaJson: plugin.schemaJson,
|
|
1127
914
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1128
915
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1129
|
-
// Childbridge mode specific data
|
|
1130
916
|
matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
|
|
1131
917
|
});
|
|
1132
918
|
}
|
|
1133
919
|
return plugins;
|
|
1134
920
|
}
|
|
1135
|
-
/**
|
|
1136
|
-
* Retrieves the devices from Matterbridge.
|
|
1137
|
-
*
|
|
1138
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1139
|
-
* @returns {ApiDevice[]} An array of ApiDevices for the frontend.
|
|
1140
|
-
*/
|
|
1141
921
|
getDevices(pluginName) {
|
|
1142
922
|
if (this.matterbridge.hasCleanupStarted)
|
|
1143
|
-
return [];
|
|
923
|
+
return [];
|
|
1144
924
|
const devices = [];
|
|
1145
925
|
for (const device of this.matterbridge.devices.array()) {
|
|
1146
|
-
// Filter by pluginName if provided
|
|
1147
926
|
if (pluginName && pluginName !== device.plugin)
|
|
1148
927
|
continue;
|
|
1149
|
-
// Check if the device has the required properties
|
|
1150
928
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1151
929
|
continue;
|
|
1152
930
|
devices.push({
|
|
@@ -1166,39 +944,24 @@ export class Frontend extends EventEmitter {
|
|
|
1166
944
|
}
|
|
1167
945
|
return devices;
|
|
1168
946
|
}
|
|
1169
|
-
/**
|
|
1170
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1171
|
-
*
|
|
1172
|
-
* Response for /api/clusters
|
|
1173
|
-
*
|
|
1174
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1175
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1176
|
-
* @returns {ApiClusters | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1177
|
-
*/
|
|
1178
947
|
getClusters(pluginName, endpointNumber) {
|
|
1179
948
|
if (this.matterbridge.hasCleanupStarted)
|
|
1180
|
-
return;
|
|
949
|
+
return;
|
|
1181
950
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1182
951
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.maybeId || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1183
952
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1184
953
|
return;
|
|
1185
954
|
}
|
|
1186
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1187
|
-
// Get the device types from the main endpoint
|
|
1188
955
|
const deviceTypes = [];
|
|
1189
956
|
const clusters = [];
|
|
1190
957
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1191
958
|
deviceTypes.push(d.deviceType);
|
|
1192
959
|
});
|
|
1193
|
-
// Get the clusters from the main endpoint
|
|
1194
960
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1195
961
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1196
962
|
return;
|
|
1197
963
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1198
964
|
return;
|
|
1199
|
-
// console.log(
|
|
1200
|
-
// `${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}`,
|
|
1201
|
-
// );
|
|
1202
965
|
clusters.push({
|
|
1203
966
|
endpoint: endpoint.number.toString(),
|
|
1204
967
|
number: endpoint.number,
|
|
@@ -1212,19 +975,12 @@ export class Frontend extends EventEmitter {
|
|
|
1212
975
|
attributeLocalValue: attributeValue,
|
|
1213
976
|
});
|
|
1214
977
|
});
|
|
1215
|
-
// Get the child endpoints
|
|
1216
978
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1217
|
-
// if (childEndpoints.length === 0) {
|
|
1218
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1219
|
-
// }
|
|
1220
979
|
childEndpoints.forEach((childEndpoint) => {
|
|
1221
|
-
// istanbul ignore if cause is not reachable: should never happen but ...
|
|
1222
980
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1223
981
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1224
982
|
return;
|
|
1225
983
|
}
|
|
1226
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1227
|
-
// Get the device types of the child endpoint
|
|
1228
984
|
const deviceTypes = [];
|
|
1229
985
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1230
986
|
deviceTypes.push(d.deviceType);
|
|
@@ -1234,9 +990,6 @@ export class Frontend extends EventEmitter {
|
|
|
1234
990
|
return;
|
|
1235
991
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1236
992
|
return;
|
|
1237
|
-
// console.log(
|
|
1238
|
-
// `${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}`,
|
|
1239
|
-
// );
|
|
1240
993
|
clusters.push({
|
|
1241
994
|
endpoint: childEndpoint.number.toString(),
|
|
1242
995
|
number: childEndpoint.number,
|
|
@@ -1256,7 +1009,6 @@ export class Frontend extends EventEmitter {
|
|
|
1256
1009
|
async generateDiagnostic() {
|
|
1257
1010
|
this.log.debug('Generating diagnostic...');
|
|
1258
1011
|
const serverNodes = [];
|
|
1259
|
-
// istanbul ignore else
|
|
1260
1012
|
if (this.matterbridge.bridgeMode === 'bridge') {
|
|
1261
1013
|
if (this.matterbridge.serverNode)
|
|
1262
1014
|
serverNodes.push(this.matterbridge.serverNode);
|
|
@@ -1267,7 +1019,6 @@ export class Frontend extends EventEmitter {
|
|
|
1267
1019
|
serverNodes.push(plugin.serverNode);
|
|
1268
1020
|
}
|
|
1269
1021
|
}
|
|
1270
|
-
// istanbul ignore next
|
|
1271
1022
|
for (const device of this.matterbridge.devices.array()) {
|
|
1272
1023
|
if (device.serverNode)
|
|
1273
1024
|
serverNodes.push(device.serverNode);
|
|
@@ -1291,15 +1042,8 @@ export class Frontend extends EventEmitter {
|
|
|
1291
1042
|
values: [...serverNodes],
|
|
1292
1043
|
})));
|
|
1293
1044
|
delete Logger.destinations.diagnostic;
|
|
1294
|
-
await wait(500);
|
|
1045
|
+
await wait(500);
|
|
1295
1046
|
}
|
|
1296
|
-
/**
|
|
1297
|
-
* Handles incoming websocket api request messages from the Matterbridge frontend.
|
|
1298
|
-
*
|
|
1299
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1300
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1301
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1302
|
-
*/
|
|
1303
1047
|
async wsMessageHandler(client, message) {
|
|
1304
1048
|
let data;
|
|
1305
1049
|
const sendResponse = (data) => {
|
|
@@ -1319,7 +1063,7 @@ export class Frontend extends EventEmitter {
|
|
|
1319
1063
|
};
|
|
1320
1064
|
try {
|
|
1321
1065
|
data = JSON.parse(message.toString());
|
|
1322
|
-
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method)
|
|
1066
|
+
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || data.dst !== 'Matterbridge') {
|
|
1323
1067
|
this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
|
|
1324
1068
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
|
|
1325
1069
|
return;
|
|
@@ -1393,7 +1137,6 @@ export class Frontend extends EventEmitter {
|
|
|
1393
1137
|
return;
|
|
1394
1138
|
})
|
|
1395
1139
|
.catch((_error) => {
|
|
1396
|
-
//
|
|
1397
1140
|
});
|
|
1398
1141
|
}
|
|
1399
1142
|
else {
|
|
@@ -1441,7 +1184,6 @@ export class Frontend extends EventEmitter {
|
|
|
1441
1184
|
return;
|
|
1442
1185
|
})
|
|
1443
1186
|
.catch((_error) => {
|
|
1444
|
-
//
|
|
1445
1187
|
});
|
|
1446
1188
|
}
|
|
1447
1189
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1467,7 +1209,6 @@ export class Frontend extends EventEmitter {
|
|
|
1467
1209
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1468
1210
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
|
|
1469
1211
|
if (plugin.serverNode) {
|
|
1470
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1471
1212
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1472
1213
|
plugin.serverNode = undefined;
|
|
1473
1214
|
}
|
|
@@ -1477,20 +1218,18 @@ export class Frontend extends EventEmitter {
|
|
|
1477
1218
|
this.matterbridge.devices.remove(device);
|
|
1478
1219
|
}
|
|
1479
1220
|
}
|
|
1480
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1481
1221
|
if (plugin.type === 'DynamicPlatform' && !plugin.locked)
|
|
1482
1222
|
await this.matterbridge.createDynamicPlugin(plugin);
|
|
1483
1223
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
|
|
1484
|
-
plugin.restartRequired = false;
|
|
1224
|
+
plugin.restartRequired = false;
|
|
1485
1225
|
let needRestart = 0;
|
|
1486
1226
|
for (const plugin of this.matterbridge.plugins) {
|
|
1487
1227
|
if (plugin.restartRequired)
|
|
1488
1228
|
needRestart++;
|
|
1489
1229
|
}
|
|
1490
1230
|
if (needRestart === 0) {
|
|
1491
|
-
this.wssSendRestartNotRequired(true);
|
|
1231
|
+
this.wssSendRestartNotRequired(true);
|
|
1492
1232
|
}
|
|
1493
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1494
1233
|
if (plugin.serverNode)
|
|
1495
1234
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1496
1235
|
this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
|
|
@@ -1755,22 +1494,22 @@ export class Frontend extends EventEmitter {
|
|
|
1755
1494
|
if (isValidString(data.params.value, 4)) {
|
|
1756
1495
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1757
1496
|
if (data.params.value === 'Debug') {
|
|
1758
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1497
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1759
1498
|
}
|
|
1760
1499
|
else if (data.params.value === 'Info') {
|
|
1761
|
-
await this.matterbridge.setLogLevel("info"
|
|
1500
|
+
await this.matterbridge.setLogLevel("info");
|
|
1762
1501
|
}
|
|
1763
1502
|
else if (data.params.value === 'Notice') {
|
|
1764
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1503
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1765
1504
|
}
|
|
1766
1505
|
else if (data.params.value === 'Warn') {
|
|
1767
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1506
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1768
1507
|
}
|
|
1769
1508
|
else if (data.params.value === 'Error') {
|
|
1770
|
-
await this.matterbridge.setLogLevel("error"
|
|
1509
|
+
await this.matterbridge.setLogLevel("error");
|
|
1771
1510
|
}
|
|
1772
1511
|
else if (data.params.value === 'Fatal') {
|
|
1773
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1512
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1774
1513
|
}
|
|
1775
1514
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1776
1515
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1781,7 +1520,6 @@ export class Frontend extends EventEmitter {
|
|
|
1781
1520
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1782
1521
|
this.matterbridge.fileLogger = data.params.value;
|
|
1783
1522
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1784
|
-
// Create the file logger for matterbridge
|
|
1785
1523
|
if (data.params.value)
|
|
1786
1524
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), await this.matterbridge.getLogLevel(), true);
|
|
1787
1525
|
else
|
|
@@ -1810,12 +1548,11 @@ export class Frontend extends EventEmitter {
|
|
|
1810
1548
|
else if (data.params.value === 'Fatal') {
|
|
1811
1549
|
Logger.level = MatterLogLevel.FATAL;
|
|
1812
1550
|
}
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
1551
|
+
let callbackLogLevel = "notice";
|
|
1552
|
+
if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
|
|
1553
|
+
callbackLogLevel = "info";
|
|
1554
|
+
if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
1555
|
+
callbackLogLevel = "debug";
|
|
1819
1556
|
AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
|
|
1820
1557
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
1821
1558
|
await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.level);
|
|
@@ -1867,7 +1604,6 @@ export class Frontend extends EventEmitter {
|
|
|
1867
1604
|
}
|
|
1868
1605
|
break;
|
|
1869
1606
|
case 'setmatterport':
|
|
1870
|
-
// eslint-disable-next-line no-case-declarations
|
|
1871
1607
|
const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1872
1608
|
if (isValidNumber(port, 5540, 5600)) {
|
|
1873
1609
|
this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
|
|
@@ -1887,7 +1623,6 @@ export class Frontend extends EventEmitter {
|
|
|
1887
1623
|
}
|
|
1888
1624
|
break;
|
|
1889
1625
|
case 'setmatterdiscriminator':
|
|
1890
|
-
// eslint-disable-next-line no-case-declarations
|
|
1891
1626
|
const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1892
1627
|
if (isValidNumber(discriminator, 0, 4095)) {
|
|
1893
1628
|
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
|
|
@@ -1907,7 +1642,6 @@ export class Frontend extends EventEmitter {
|
|
|
1907
1642
|
}
|
|
1908
1643
|
break;
|
|
1909
1644
|
case 'setmatterpasscode':
|
|
1910
|
-
// eslint-disable-next-line no-case-declarations
|
|
1911
1645
|
const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1912
1646
|
if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
|
|
1913
1647
|
this.matterbridge.passcode = passcode;
|
|
@@ -1953,19 +1687,15 @@ export class Frontend extends EventEmitter {
|
|
|
1953
1687
|
return;
|
|
1954
1688
|
}
|
|
1955
1689
|
const config = plugin.configJson;
|
|
1956
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1957
1690
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1958
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1959
1691
|
if (select === 'serial')
|
|
1960
1692
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1961
1693
|
if (select === 'name')
|
|
1962
1694
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1963
1695
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1964
|
-
// Remove postfix from the serial if it exists
|
|
1965
1696
|
if (config.postfix) {
|
|
1966
1697
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1967
1698
|
}
|
|
1968
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1969
1699
|
if (isValidArray(config.whiteList, 1)) {
|
|
1970
1700
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1971
1701
|
config.whiteList.push(data.params.serial);
|
|
@@ -1974,7 +1704,6 @@ export class Frontend extends EventEmitter {
|
|
|
1974
1704
|
config.whiteList.push(data.params.name);
|
|
1975
1705
|
}
|
|
1976
1706
|
}
|
|
1977
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1978
1707
|
if (isValidArray(config.blackList, 1)) {
|
|
1979
1708
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1980
1709
|
config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
|
|
@@ -2002,9 +1731,7 @@ export class Frontend extends EventEmitter {
|
|
|
2002
1731
|
return;
|
|
2003
1732
|
}
|
|
2004
1733
|
const config = plugin.configJson;
|
|
2005
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2006
1734
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
2007
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
2008
1735
|
if (select === 'serial')
|
|
2009
1736
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
2010
1737
|
if (select === 'name')
|
|
@@ -2013,7 +1740,6 @@ export class Frontend extends EventEmitter {
|
|
|
2013
1740
|
if (config.postfix) {
|
|
2014
1741
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
2015
1742
|
}
|
|
2016
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
2017
1743
|
if (isValidArray(config.whiteList, 1)) {
|
|
2018
1744
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
2019
1745
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
|
|
@@ -2022,7 +1748,6 @@ export class Frontend extends EventEmitter {
|
|
|
2022
1748
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
|
|
2023
1749
|
}
|
|
2024
1750
|
}
|
|
2025
|
-
// Add the serial to the blackList
|
|
2026
1751
|
if (isValidArray(config.blackList)) {
|
|
2027
1752
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
2028
1753
|
config.blackList.push(data.params.serial);
|
|
@@ -2045,7 +1770,6 @@ export class Frontend extends EventEmitter {
|
|
|
2045
1770
|
}
|
|
2046
1771
|
}
|
|
2047
1772
|
else {
|
|
2048
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2049
1773
|
const localData = data;
|
|
2050
1774
|
this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
|
|
2051
1775
|
sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
|
|
@@ -2055,46 +1779,23 @@ export class Frontend extends EventEmitter {
|
|
|
2055
1779
|
inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
|
|
2056
1780
|
}
|
|
2057
1781
|
}
|
|
2058
|
-
/**
|
|
2059
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
2060
|
-
*
|
|
2061
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
2062
|
-
* @param {string} time - The time string of the message
|
|
2063
|
-
* @param {string} name - The logger name of the message
|
|
2064
|
-
* @param {string} message - The content of the message.
|
|
2065
|
-
*
|
|
2066
|
-
* @remarks
|
|
2067
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
2068
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
2069
|
-
* The function sends the message to all connected clients.
|
|
2070
|
-
*/
|
|
2071
1782
|
wssSendLogMessage(level, time, name, message) {
|
|
2072
1783
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2073
1784
|
return;
|
|
2074
1785
|
if (!level || !time || !name || !message)
|
|
2075
1786
|
return;
|
|
2076
|
-
// Remove ANSI escape codes from the message
|
|
2077
|
-
// eslint-disable-next-line no-control-regex
|
|
2078
1787
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
2079
|
-
// Remove leading asterisks from the message
|
|
2080
1788
|
message = message.replace(/^\*+/, '');
|
|
2081
|
-
// Replace all occurrences of \t and \n
|
|
2082
1789
|
message = message.replace(/[\t\n]/g, '');
|
|
2083
|
-
// Remove non-printable characters
|
|
2084
|
-
// eslint-disable-next-line no-control-regex
|
|
2085
1790
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
2086
|
-
// Replace all occurrences of \" with "
|
|
2087
1791
|
message = message.replace(/\\"/g, '"');
|
|
2088
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
2089
1792
|
const maxContinuousLength = 100;
|
|
2090
1793
|
const keepStartLength = 20;
|
|
2091
1794
|
const keepEndLength = 20;
|
|
2092
|
-
// Split the message into words
|
|
2093
1795
|
if (level !== 'spawn') {
|
|
2094
1796
|
message = message
|
|
2095
1797
|
.split(' ')
|
|
2096
1798
|
.map((word) => {
|
|
2097
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2098
1799
|
if (word.length > maxContinuousLength) {
|
|
2099
1800
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2100
1801
|
}
|
|
@@ -2102,34 +1803,14 @@ export class Frontend extends EventEmitter {
|
|
|
2102
1803
|
})
|
|
2103
1804
|
.join(' ');
|
|
2104
1805
|
}
|
|
2105
|
-
// Send the message to all connected clients
|
|
2106
1806
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
|
|
2107
1807
|
}
|
|
2108
|
-
/**
|
|
2109
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2110
|
-
*
|
|
2111
|
-
* @param {string} changed - The changed value.
|
|
2112
|
-
* @param {Record<string, unknown>} params - Additional parameters to send with the message.
|
|
2113
|
-
* possible values for changed:
|
|
2114
|
-
* - 'settings' (when the bridge has started in bridge mode or childbridge mode and when update finds a new version)
|
|
2115
|
-
* - 'plugins'
|
|
2116
|
-
* - 'devices'
|
|
2117
|
-
* - 'matter' with param 'matter' (QRDiv component)
|
|
2118
|
-
* @param {ApiMatter} params.matter - The matter device that has changed. Required if changed is 'matter'.
|
|
2119
|
-
*/
|
|
2120
1808
|
wssSendRefreshRequired(changed, params) {
|
|
2121
1809
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2122
1810
|
return;
|
|
2123
1811
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
2124
|
-
// Send the message to all connected clients
|
|
2125
1812
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
|
|
2126
1813
|
}
|
|
2127
|
-
/**
|
|
2128
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2129
|
-
*
|
|
2130
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
|
|
2131
|
-
* @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
|
|
2132
|
-
*/
|
|
2133
1814
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
2134
1815
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2135
1816
|
return;
|
|
@@ -2138,14 +1819,8 @@ export class Frontend extends EventEmitter {
|
|
|
2138
1819
|
this.matterbridge.fixedRestartRequired = fixed;
|
|
2139
1820
|
if (snackbar === true)
|
|
2140
1821
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
2141
|
-
// Send the message to all connected clients
|
|
2142
1822
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
|
|
2143
1823
|
}
|
|
2144
|
-
/**
|
|
2145
|
-
* Sends a no need to restart WebSocket message to all connected clients.
|
|
2146
|
-
*
|
|
2147
|
-
* @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients. Default is true.
|
|
2148
|
-
*/
|
|
2149
1824
|
wssSendRestartNotRequired(snackbar = true) {
|
|
2150
1825
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2151
1826
|
return;
|
|
@@ -2153,133 +1828,57 @@ export class Frontend extends EventEmitter {
|
|
|
2153
1828
|
this.matterbridge.restartRequired = false;
|
|
2154
1829
|
if (snackbar === true)
|
|
2155
1830
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2156
|
-
// Send the message to all connected clients
|
|
2157
1831
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
|
|
2158
1832
|
}
|
|
2159
|
-
/**
|
|
2160
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
2161
|
-
*
|
|
2162
|
-
* @param {boolean} devVersion - If true, the update is for a development version. Default is false.
|
|
2163
|
-
*/
|
|
2164
1833
|
wssSendUpdateRequired(devVersion = false) {
|
|
2165
1834
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2166
1835
|
return;
|
|
2167
1836
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2168
1837
|
this.matterbridge.updateRequired = true;
|
|
2169
|
-
// Send the message to all connected clients
|
|
2170
1838
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
|
|
2171
1839
|
}
|
|
2172
|
-
/**
|
|
2173
|
-
* Sends a cpu update message to all connected clients.
|
|
2174
|
-
*
|
|
2175
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2176
|
-
* @param {number} processCpuUsage - The CPU usage percentage of the process to send.
|
|
2177
|
-
*/
|
|
2178
1840
|
wssSendCpuUpdate(cpuUsage, processCpuUsage) {
|
|
2179
1841
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2180
1842
|
return;
|
|
2181
1843
|
if (hasParameter('debug'))
|
|
2182
1844
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2183
|
-
// Send the message to all connected clients
|
|
2184
1845
|
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 } });
|
|
2185
1846
|
}
|
|
2186
|
-
/**
|
|
2187
|
-
* Sends a memory update message to all connected clients.
|
|
2188
|
-
*
|
|
2189
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2190
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2191
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2192
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2193
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2194
|
-
* @param {string} external - The external memory in bytes.
|
|
2195
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2196
|
-
*/
|
|
2197
1847
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2198
1848
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2199
1849
|
return;
|
|
2200
1850
|
if (hasParameter('debug'))
|
|
2201
1851
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2202
|
-
// Send the message to all connected clients
|
|
2203
1852
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', success: true, response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
|
|
2204
1853
|
}
|
|
2205
|
-
/**
|
|
2206
|
-
* Sends an uptime update message to all connected clients.
|
|
2207
|
-
*
|
|
2208
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2209
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2210
|
-
*/
|
|
2211
1854
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2212
1855
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2213
1856
|
return;
|
|
2214
1857
|
if (hasParameter('debug'))
|
|
2215
1858
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2216
|
-
// Send the message to all connected clients
|
|
2217
1859
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
|
|
2218
1860
|
}
|
|
2219
|
-
/**
|
|
2220
|
-
* Sends an open snackbar message to all connected clients.
|
|
2221
|
-
*
|
|
2222
|
-
* @param {string} message - The message to send.
|
|
2223
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message. Default is 5 seconds.
|
|
2224
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the message.
|
|
2225
|
-
* possible values are: 'info', 'warning', 'error', 'success'. Default is 'info'.
|
|
2226
|
-
*
|
|
2227
|
-
* @remarks
|
|
2228
|
-
* If timeout is 0, the snackbar message will be displayed until closed by the user.
|
|
2229
|
-
*/
|
|
2230
1861
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2231
1862
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2232
1863
|
return;
|
|
2233
1864
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2234
|
-
// Send the message to all connected clients
|
|
2235
1865
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
|
|
2236
1866
|
}
|
|
2237
|
-
/**
|
|
2238
|
-
* Sends a close snackbar message to all connected clients.
|
|
2239
|
-
* It will close the snackbar message with the same message and timeout = 0.
|
|
2240
|
-
*
|
|
2241
|
-
* @param {string} message - The message to send.
|
|
2242
|
-
*/
|
|
2243
1867
|
wssSendCloseSnackbarMessage(message) {
|
|
2244
1868
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2245
1869
|
return;
|
|
2246
1870
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2247
|
-
// Send the message to all connected clients
|
|
2248
1871
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
|
|
2249
1872
|
}
|
|
2250
|
-
/**
|
|
2251
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2252
|
-
*
|
|
2253
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2254
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2255
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2256
|
-
* @param {EndpointNumber} number - The endpoint number where the attribute belongs.
|
|
2257
|
-
* @param {string} id - The endpoint id where the attribute belongs.
|
|
2258
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2259
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2260
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2261
|
-
*
|
|
2262
|
-
* @remarks
|
|
2263
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2264
|
-
* with the updated attribute information.
|
|
2265
|
-
*/
|
|
2266
1873
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
|
|
2267
1874
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2268
1875
|
return;
|
|
2269
1876
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2270
|
-
// Send the message to all connected clients
|
|
2271
1877
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', success: true, response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value } });
|
|
2272
1878
|
}
|
|
2273
|
-
/**
|
|
2274
|
-
* Sends a message to all connected clients.
|
|
2275
|
-
* This is an helper function to send a broadcast message to all connected clients.
|
|
2276
|
-
*
|
|
2277
|
-
* @param {WsMessageBroadcast} msg - The message to send.
|
|
2278
|
-
*/
|
|
2279
1879
|
wssBroadcastMessage(msg) {
|
|
2280
1880
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2281
1881
|
return;
|
|
2282
|
-
// Send the message to all connected clients
|
|
2283
1882
|
const stringifiedMsg = JSON.stringify(msg);
|
|
2284
1883
|
if (msg.method !== 'log')
|
|
2285
1884
|
this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
|
|
@@ -2290,4 +1889,3 @@ export class Frontend extends EventEmitter {
|
|
|
2290
1889
|
});
|
|
2291
1890
|
}
|
|
2292
1891
|
}
|
|
2293
|
-
//# sourceMappingURL=frontend.js.map
|