matterbridge 3.2.8 → 3.2.9-dev-20250922-517cae7
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 +24 -1
- package/README-NGINX.md +47 -23
- package/dist/cli.js +2 -91
- package/dist/cliEmitter.js +0 -30
- package/dist/clusters/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -24
- package/dist/deviceManager.js +1 -94
- 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 -25
- 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 +45 -435
- package/dist/frontendTypes.js +3 -51
- package/dist/globalMatterbridge.js +0 -47
- package/dist/helpers.js +0 -53
- package/dist/index.js +1 -30
- 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 +57 -780
- package/dist/matterbridgeAccessoryPlatform.js +0 -36
- package/dist/matterbridgeBehaviors.js +5 -65
- package/dist/matterbridgeDeviceTypes.js +17 -630
- package/dist/matterbridgeDynamicPlatform.js +0 -36
- package/dist/matterbridgeEndpoint.js +55 -1373
- package/dist/matterbridgeEndpointHelpers.js +12 -345
- package/dist/matterbridgePlatform.js +0 -304
- package/dist/matterbridgeTypes.js +0 -25
- package/dist/pluginManager.js +3 -249
- package/dist/shelly.js +11 -172
- 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 -54
- 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/hex.js +0 -124
- package/dist/utils/isvalid.js +0 -101
- package/dist/utils/jestHelpers.js +3 -153
- package/dist/utils/network.js +5 -91
- package/dist/utils/spawn.js +0 -68
- package/dist/utils/wait.js +8 -60
- package/frontend/build/assets/index.js +2 -2
- package/frontend/build/assets/vendor_lodash.js +1 -1
- package/frontend/build/assets/vendor_mdi.js +1 -1
- package/frontend/build/assets/vendor_mui.js +31 -23
- package/frontend/build/assets/vendor_node_modules.js +28 -45
- package/frontend/build/assets/vendor_notistack.js +2 -2
- package/frontend/build/assets/vendor_qrcode.js +1 -1
- package/frontend/build/assets/vendor_rjsf.js +12 -3
- package/frontend/build/index.html +0 -1
- package/frontend/package-lock.json +208 -283
- package/frontend/package.json +4 -13
- package/npm-shrinkwrap.json +44 -44
- package/package.json +2 -3
- package/dist/cli.d.ts +0 -26
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cliEmitter.d.ts +0 -34
- package/dist/cliEmitter.d.ts.map +0 -1
- package/dist/cliEmitter.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 -112
- 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 -75
- 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 -228
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/frontendTypes.d.ts +0 -572
- package/dist/frontendTypes.d.ts.map +0 -1
- package/dist/frontendTypes.js.map +0 -1
- package/dist/globalMatterbridge.d.ts +0 -59
- package/dist/globalMatterbridge.d.ts.map +0 -1
- package/dist/globalMatterbridge.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 -442
- 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 -1747
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -761
- 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 -1515
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -407
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -380
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -190
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -270
- 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 -59
- 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/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/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 -137
- package/dist/utils/jestHelpers.d.ts.map +0 -1
- package/dist/utils/jestHelpers.js.map +0 -1
- package/dist/utils/network.d.ts +0 -84
- 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/wait.d.ts +0 -54
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
- package/frontend/build/assets/vendor_react_table.js +0 -1
package/dist/frontend.js
CHANGED
|
@@ -1,27 +1,3 @@
|
|
|
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
|
-
// Node modules
|
|
25
1
|
import { createServer } from 'node:http';
|
|
26
2
|
import https from 'node:https';
|
|
27
3
|
import os from 'node:os';
|
|
@@ -29,17 +5,14 @@ import path from 'node:path';
|
|
|
29
5
|
import { existsSync, promises as fs, unlinkSync } from 'node:fs';
|
|
30
6
|
import EventEmitter from 'node:events';
|
|
31
7
|
import { appendFile } from 'node:fs/promises';
|
|
32
|
-
// Third-party modules
|
|
33
8
|
import express from 'express';
|
|
34
9
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
35
10
|
import multer from 'multer';
|
|
36
|
-
// AnsiLogger module
|
|
37
11
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt } from 'node-ansi-logger';
|
|
38
|
-
// @matter
|
|
39
12
|
import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle, LogDestination, Diagnostic, Time, FabricIndex } from '@matter/main';
|
|
40
13
|
import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
|
|
41
14
|
import { DeviceAdvertiser, DeviceCommissioner, FabricManager } from '@matter/main/protocol';
|
|
42
|
-
|
|
15
|
+
import { CommissioningOptions } from '@matter/main/types';
|
|
43
16
|
import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout, hasParameter, wait, inspectError } from './utils/export.js';
|
|
44
17
|
import { plg } from './matterbridgeTypes.js';
|
|
45
18
|
import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
|
|
@@ -55,7 +28,7 @@ export class Frontend extends EventEmitter {
|
|
|
55
28
|
constructor(matterbridge) {
|
|
56
29
|
super();
|
|
57
30
|
this.matterbridge = matterbridge;
|
|
58
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
31
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
59
32
|
this.log.logNameColor = '\x1b[38;5;97m';
|
|
60
33
|
}
|
|
61
34
|
set logLevel(logLevel) {
|
|
@@ -64,39 +37,10 @@ export class Frontend extends EventEmitter {
|
|
|
64
37
|
async start(port = 8283) {
|
|
65
38
|
this.port = port;
|
|
66
39
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
67
|
-
|
|
68
|
-
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads'); // Is created by matterbridge initialize
|
|
40
|
+
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
69
41
|
const upload = multer({ dest: uploadDir });
|
|
70
|
-
// Create the express app that serves the frontend
|
|
71
42
|
this.expressApp = express();
|
|
72
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
73
|
-
/*
|
|
74
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
75
|
-
for (const method of methods) {
|
|
76
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
77
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
78
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
79
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
80
|
-
try {
|
|
81
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
82
|
-
return original(path, ...rest);
|
|
83
|
-
} catch (err) {
|
|
84
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
85
|
-
throw err;
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
*/
|
|
90
|
-
// Log all requests to the server for debugging
|
|
91
|
-
/*
|
|
92
|
-
this.expressApp.use((req, res, next) => {
|
|
93
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
94
|
-
next();
|
|
95
|
-
});
|
|
96
|
-
*/
|
|
97
|
-
// Serve static files from '/static' endpoint
|
|
98
43
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
99
|
-
// Read the package.json file to get the frontend version
|
|
100
44
|
try {
|
|
101
45
|
this.log.debug(`Reading frontend package.json...`);
|
|
102
46
|
const frontendJson = await fs.readFile(path.join(this.matterbridge.rootDirectory, 'frontend/package.json'), 'utf8');
|
|
@@ -104,11 +48,9 @@ export class Frontend extends EventEmitter {
|
|
|
104
48
|
this.log.debug(`Frontend version ${CYAN}${this.matterbridge.matterbridgeInformation.frontendVersion}${db}`);
|
|
105
49
|
}
|
|
106
50
|
catch (error) {
|
|
107
|
-
// istanbul ignore next
|
|
108
51
|
this.log.error(`Failed to read frontend package.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
109
52
|
}
|
|
110
53
|
if (!hasParameter('ssl')) {
|
|
111
|
-
// Create an HTTP server and attach the express app
|
|
112
54
|
try {
|
|
113
55
|
this.log.debug(`Creating HTTP server...`);
|
|
114
56
|
this.httpServer = createServer(this.expressApp);
|
|
@@ -118,7 +60,6 @@ export class Frontend extends EventEmitter {
|
|
|
118
60
|
this.emit('server_error', error);
|
|
119
61
|
return;
|
|
120
62
|
}
|
|
121
|
-
// Listen on the specified port
|
|
122
63
|
if (hasParameter('ingress')) {
|
|
123
64
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
124
65
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -157,7 +98,6 @@ export class Frontend extends EventEmitter {
|
|
|
157
98
|
let passphrase;
|
|
158
99
|
let httpsServerOptions = {};
|
|
159
100
|
if (existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
|
|
160
|
-
// Load the p12 certificate and the passphrase
|
|
161
101
|
try {
|
|
162
102
|
pfx = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
|
|
163
103
|
this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
|
|
@@ -169,7 +109,7 @@ export class Frontend extends EventEmitter {
|
|
|
169
109
|
}
|
|
170
110
|
try {
|
|
171
111
|
passphrase = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
|
|
172
|
-
passphrase = passphrase.trim();
|
|
112
|
+
passphrase = passphrase.trim();
|
|
173
113
|
this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
|
|
174
114
|
}
|
|
175
115
|
catch (error) {
|
|
@@ -180,7 +120,6 @@ export class Frontend extends EventEmitter {
|
|
|
180
120
|
httpsServerOptions = { pfx, passphrase };
|
|
181
121
|
}
|
|
182
122
|
else {
|
|
183
|
-
// 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.
|
|
184
123
|
try {
|
|
185
124
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
186
125
|
this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
|
|
@@ -210,10 +149,9 @@ export class Frontend extends EventEmitter {
|
|
|
210
149
|
httpsServerOptions = { cert: fullChain ?? cert, key, ca };
|
|
211
150
|
}
|
|
212
151
|
if (hasParameter('mtls')) {
|
|
213
|
-
httpsServerOptions.requestCert = true;
|
|
214
|
-
httpsServerOptions.rejectUnauthorized = true;
|
|
152
|
+
httpsServerOptions.requestCert = true;
|
|
153
|
+
httpsServerOptions.rejectUnauthorized = true;
|
|
215
154
|
}
|
|
216
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
217
155
|
try {
|
|
218
156
|
this.log.debug(`Creating HTTPS server...`);
|
|
219
157
|
this.httpsServer = https.createServer(httpsServerOptions, this.expressApp);
|
|
@@ -223,7 +161,6 @@ export class Frontend extends EventEmitter {
|
|
|
223
161
|
this.emit('server_error', error);
|
|
224
162
|
return;
|
|
225
163
|
}
|
|
226
|
-
// Listen on the specified port
|
|
227
164
|
if (hasParameter('ingress')) {
|
|
228
165
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
229
166
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -253,19 +190,17 @@ export class Frontend extends EventEmitter {
|
|
|
253
190
|
return;
|
|
254
191
|
});
|
|
255
192
|
}
|
|
256
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
257
193
|
const wssPort = this.port;
|
|
258
194
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
259
195
|
this.log.debug(`Creating WebSocketServer on host ${CYAN}${wssHost}${db}...`);
|
|
260
196
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
261
197
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
262
198
|
const clientIp = request.socket.remoteAddress;
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
199
|
+
let callbackLogLevel = "notice";
|
|
200
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
201
|
+
callbackLogLevel = "info";
|
|
202
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
203
|
+
callbackLogLevel = "debug";
|
|
269
204
|
AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
|
|
270
205
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
271
206
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -287,7 +222,6 @@ export class Frontend extends EventEmitter {
|
|
|
287
222
|
}
|
|
288
223
|
});
|
|
289
224
|
ws.on('error', (error) => {
|
|
290
|
-
// istanbul ignore next
|
|
291
225
|
this.log.error(`WebSocket client error: ${error}`);
|
|
292
226
|
});
|
|
293
227
|
});
|
|
@@ -301,7 +235,6 @@ export class Frontend extends EventEmitter {
|
|
|
301
235
|
this.webSocketServer.on('error', (ws, error) => {
|
|
302
236
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
303
237
|
});
|
|
304
|
-
// Subscribe to cli events
|
|
305
238
|
cliEmitter.removeAllListeners();
|
|
306
239
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
307
240
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -312,8 +245,6 @@ export class Frontend extends EventEmitter {
|
|
|
312
245
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
313
246
|
this.wssSendCpuUpdate(cpuUsage);
|
|
314
247
|
});
|
|
315
|
-
// Endpoint to validate login code
|
|
316
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
317
248
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
318
249
|
const { password } = req.body;
|
|
319
250
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -332,27 +263,23 @@ export class Frontend extends EventEmitter {
|
|
|
332
263
|
this.log.warn('/api/login error wrong password');
|
|
333
264
|
res.json({ valid: false });
|
|
334
265
|
}
|
|
335
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
336
266
|
}
|
|
337
267
|
catch (error) {
|
|
338
268
|
this.log.error('/api/login error getting password');
|
|
339
269
|
res.json({ valid: false });
|
|
340
270
|
}
|
|
341
271
|
});
|
|
342
|
-
// Endpoint to provide health check for docker
|
|
343
272
|
this.expressApp.get('/health', (req, res) => {
|
|
344
273
|
this.log.debug('Express received /health');
|
|
345
274
|
const healthStatus = {
|
|
346
|
-
status: 'ok',
|
|
347
|
-
uptime: process.uptime(),
|
|
348
|
-
timestamp: new Date().toISOString(),
|
|
275
|
+
status: 'ok',
|
|
276
|
+
uptime: process.uptime(),
|
|
277
|
+
timestamp: new Date().toISOString(),
|
|
349
278
|
};
|
|
350
279
|
res.status(200).json(healthStatus);
|
|
351
280
|
});
|
|
352
|
-
// Endpoint to provide memory usage details
|
|
353
281
|
this.expressApp.get('/memory', async (req, res) => {
|
|
354
282
|
this.log.debug('Express received /memory');
|
|
355
|
-
// Memory usage from process
|
|
356
283
|
const memoryUsageRaw = process.memoryUsage();
|
|
357
284
|
const memoryUsage = {
|
|
358
285
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -361,13 +288,10 @@ export class Frontend extends EventEmitter {
|
|
|
361
288
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
362
289
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
363
290
|
};
|
|
364
|
-
// V8 heap statistics
|
|
365
291
|
const { default: v8 } = await import('node:v8');
|
|
366
292
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
367
293
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
368
|
-
// Format heapStats
|
|
369
294
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
370
|
-
// Format heapSpaces
|
|
371
295
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
372
296
|
...space,
|
|
373
297
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -385,23 +309,19 @@ export class Frontend extends EventEmitter {
|
|
|
385
309
|
};
|
|
386
310
|
res.status(200).json(memoryReport);
|
|
387
311
|
});
|
|
388
|
-
// Endpoint to provide settings
|
|
389
312
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
390
313
|
this.log.debug('The frontend sent /api/settings');
|
|
391
314
|
res.json(await this.getApiSettings());
|
|
392
315
|
});
|
|
393
|
-
// Endpoint to provide plugins
|
|
394
316
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
395
317
|
this.log.debug('The frontend sent /api/plugins');
|
|
396
318
|
res.json(this.getPlugins());
|
|
397
319
|
});
|
|
398
|
-
// Endpoint to provide devices
|
|
399
320
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
400
321
|
this.log.debug('The frontend sent /api/devices');
|
|
401
322
|
const devices = await this.getDevices();
|
|
402
323
|
res.json(devices);
|
|
403
324
|
});
|
|
404
|
-
// Endpoint to view the matterbridge log
|
|
405
325
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
406
326
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
407
327
|
try {
|
|
@@ -414,7 +334,6 @@ export class Frontend extends EventEmitter {
|
|
|
414
334
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
415
335
|
}
|
|
416
336
|
});
|
|
417
|
-
// Endpoint to view the matter.js log
|
|
418
337
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
419
338
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
420
339
|
try {
|
|
@@ -427,11 +346,9 @@ export class Frontend extends EventEmitter {
|
|
|
427
346
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
428
347
|
}
|
|
429
348
|
});
|
|
430
|
-
// Endpoint to view the diagnostic.log
|
|
431
349
|
this.expressApp.get('/api/view-diagnostic', async (req, res) => {
|
|
432
350
|
this.log.debug('The frontend sent /api/view-diagnostic');
|
|
433
351
|
const serverNodes = [];
|
|
434
|
-
// istanbul ignore else
|
|
435
352
|
if (this.matterbridge.bridgeMode === 'bridge') {
|
|
436
353
|
if (this.matterbridge.serverNode)
|
|
437
354
|
serverNodes.push(this.matterbridge.serverNode);
|
|
@@ -442,7 +359,6 @@ export class Frontend extends EventEmitter {
|
|
|
442
359
|
serverNodes.push(plugin.serverNode);
|
|
443
360
|
}
|
|
444
361
|
}
|
|
445
|
-
// istanbul ignore next
|
|
446
362
|
for (const device of this.matterbridge.getDevices()) {
|
|
447
363
|
if (device.serverNode)
|
|
448
364
|
serverNodes.push(device.serverNode);
|
|
@@ -465,20 +381,17 @@ export class Frontend extends EventEmitter {
|
|
|
465
381
|
values: [...serverNodes],
|
|
466
382
|
})));
|
|
467
383
|
delete Logger.destinations.diagnostic;
|
|
468
|
-
await wait(500);
|
|
384
|
+
await wait(500);
|
|
469
385
|
try {
|
|
470
386
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'), 'utf8');
|
|
471
387
|
res.type('text/plain');
|
|
472
388
|
res.send(data.slice(29));
|
|
473
389
|
}
|
|
474
390
|
catch (error) {
|
|
475
|
-
// istanbul ignore next
|
|
476
391
|
this.log.error(`Error reading diagnostic log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
477
|
-
// istanbul ignore next
|
|
478
392
|
res.status(500).send('Error reading diagnostic log file.');
|
|
479
393
|
}
|
|
480
394
|
});
|
|
481
|
-
// Endpoint to view the shelly log
|
|
482
395
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
483
396
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
484
397
|
try {
|
|
@@ -491,7 +404,6 @@ export class Frontend extends EventEmitter {
|
|
|
491
404
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
492
405
|
}
|
|
493
406
|
});
|
|
494
|
-
// Endpoint to download the matterbridge log
|
|
495
407
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
496
408
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
497
409
|
try {
|
|
@@ -505,14 +417,12 @@ export class Frontend extends EventEmitter {
|
|
|
505
417
|
}
|
|
506
418
|
res.type('text/plain');
|
|
507
419
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'matterbridge.log', (error) => {
|
|
508
|
-
/* istanbul ignore if */
|
|
509
420
|
if (error) {
|
|
510
421
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
511
422
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
512
423
|
}
|
|
513
424
|
});
|
|
514
425
|
});
|
|
515
|
-
// Endpoint to download the matter log
|
|
516
426
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
517
427
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
518
428
|
try {
|
|
@@ -526,14 +436,12 @@ export class Frontend extends EventEmitter {
|
|
|
526
436
|
}
|
|
527
437
|
res.type('text/plain');
|
|
528
438
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
|
|
529
|
-
/* istanbul ignore if */
|
|
530
439
|
if (error) {
|
|
531
440
|
this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
532
441
|
res.status(500).send('Error downloading the matter log file');
|
|
533
442
|
}
|
|
534
443
|
});
|
|
535
444
|
});
|
|
536
|
-
// Endpoint to download the shelly log
|
|
537
445
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
538
446
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
539
447
|
try {
|
|
@@ -547,90 +455,74 @@ export class Frontend extends EventEmitter {
|
|
|
547
455
|
}
|
|
548
456
|
res.type('text/plain');
|
|
549
457
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
550
|
-
/* istanbul ignore if */
|
|
551
458
|
if (error) {
|
|
552
459
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
553
460
|
res.status(500).send('Error downloading Shelly system log file');
|
|
554
461
|
}
|
|
555
462
|
});
|
|
556
463
|
});
|
|
557
|
-
// Endpoint to download the matterbridge storage directory
|
|
558
464
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
559
465
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
560
466
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
561
467
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), `matterbridge.${this.matterbridge.nodeStorageName}.zip`, (error) => {
|
|
562
|
-
/* istanbul ignore if */
|
|
563
468
|
if (error) {
|
|
564
469
|
this.log.error(`Error downloading file ${`matterbridge.${this.matterbridge.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
565
470
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
566
471
|
}
|
|
567
472
|
});
|
|
568
473
|
});
|
|
569
|
-
// Endpoint to download the matter storage file
|
|
570
474
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
571
475
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
572
476
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
573
477
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), `matterbridge.${this.matterbridge.matterStorageName}.zip`, (error) => {
|
|
574
|
-
/* istanbul ignore if */
|
|
575
478
|
if (error) {
|
|
576
479
|
this.log.error(`Error downloading the matter storage matterbridge.${this.matterbridge.matterStorageName}.zip: ${error instanceof Error ? error.message : error}`);
|
|
577
480
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
578
481
|
}
|
|
579
482
|
});
|
|
580
483
|
});
|
|
581
|
-
// Endpoint to download the matterbridge plugin directory
|
|
582
484
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
583
485
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
584
486
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
585
487
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
586
|
-
/* istanbul ignore if */
|
|
587
488
|
if (error) {
|
|
588
489
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
589
490
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
590
491
|
}
|
|
591
492
|
});
|
|
592
493
|
});
|
|
593
|
-
// Endpoint to download the matterbridge plugin config files
|
|
594
494
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
595
495
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
596
496
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
597
497
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
598
|
-
/* istanbul ignore if */
|
|
599
498
|
if (error) {
|
|
600
499
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
601
500
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
602
501
|
}
|
|
603
502
|
});
|
|
604
503
|
});
|
|
605
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
606
504
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
607
505
|
this.log.debug('The frontend sent /api/download-backup');
|
|
608
506
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
609
|
-
/* istanbul ignore if */
|
|
610
507
|
if (error) {
|
|
611
508
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
612
509
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
613
510
|
}
|
|
614
511
|
});
|
|
615
512
|
});
|
|
616
|
-
// Endpoint to upload a package
|
|
617
513
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
618
514
|
const { filename } = req.body;
|
|
619
515
|
const file = req.file;
|
|
620
|
-
/* istanbul ignore if */
|
|
621
516
|
if (!file || !filename) {
|
|
622
517
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
623
518
|
res.status(400).send('Invalid request: file and filename are required');
|
|
624
519
|
return;
|
|
625
520
|
}
|
|
626
521
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
627
|
-
// Define the path where the plugin file will be saved
|
|
628
522
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
629
523
|
try {
|
|
630
|
-
// Move the uploaded file to the specified path
|
|
631
524
|
await fs.rename(file.path, filePath);
|
|
632
525
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
633
|
-
// Install the plugin package
|
|
634
526
|
if (filename.endsWith('.tgz')) {
|
|
635
527
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
636
528
|
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
@@ -650,7 +542,6 @@ export class Frontend extends EventEmitter {
|
|
|
650
542
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
651
543
|
}
|
|
652
544
|
});
|
|
653
|
-
// Fallback for routing (must be the last route)
|
|
654
545
|
this.expressApp.use((req, res) => {
|
|
655
546
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
|
|
656
547
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -659,16 +550,13 @@ export class Frontend extends EventEmitter {
|
|
|
659
550
|
}
|
|
660
551
|
async stop() {
|
|
661
552
|
this.log.debug('Stopping the frontend...');
|
|
662
|
-
// Remove listeners from the express app
|
|
663
553
|
if (this.expressApp) {
|
|
664
554
|
this.expressApp.removeAllListeners();
|
|
665
555
|
this.expressApp = undefined;
|
|
666
556
|
this.log.debug('Frontend app closed successfully');
|
|
667
557
|
}
|
|
668
|
-
// Close the WebSocket server
|
|
669
558
|
if (this.webSocketServer) {
|
|
670
559
|
this.log.debug('Closing WebSocket server...');
|
|
671
|
-
// Close all active connections
|
|
672
560
|
this.webSocketServer.clients.forEach((client) => {
|
|
673
561
|
if (client.readyState === WebSocket.OPEN) {
|
|
674
562
|
client.close();
|
|
@@ -677,7 +565,6 @@ export class Frontend extends EventEmitter {
|
|
|
677
565
|
await withTimeout(new Promise((resolve) => {
|
|
678
566
|
this.webSocketServer?.close((error) => {
|
|
679
567
|
if (error) {
|
|
680
|
-
// istanbul ignore next
|
|
681
568
|
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
682
569
|
}
|
|
683
570
|
else {
|
|
@@ -690,27 +577,8 @@ export class Frontend extends EventEmitter {
|
|
|
690
577
|
this.webSocketServer.removeAllListeners();
|
|
691
578
|
this.webSocketServer = undefined;
|
|
692
579
|
}
|
|
693
|
-
// Close the http server
|
|
694
580
|
if (this.httpServer) {
|
|
695
581
|
this.log.debug('Closing http server...');
|
|
696
|
-
/*
|
|
697
|
-
await withTimeout(
|
|
698
|
-
new Promise<void>((resolve) => {
|
|
699
|
-
this.httpServer?.close((error) => {
|
|
700
|
-
if (error) {
|
|
701
|
-
// istanbul ignore next
|
|
702
|
-
this.log.error(`Error closing http server: ${error}`);
|
|
703
|
-
} else {
|
|
704
|
-
this.log.debug('Http server closed successfully');
|
|
705
|
-
this.emit('server_stopped');
|
|
706
|
-
}
|
|
707
|
-
resolve();
|
|
708
|
-
});
|
|
709
|
-
}),
|
|
710
|
-
5000,
|
|
711
|
-
false,
|
|
712
|
-
);
|
|
713
|
-
*/
|
|
714
582
|
this.httpServer.close();
|
|
715
583
|
this.log.debug('Http server closed successfully');
|
|
716
584
|
this.emit('server_stopped');
|
|
@@ -718,27 +586,8 @@ export class Frontend extends EventEmitter {
|
|
|
718
586
|
this.httpServer = undefined;
|
|
719
587
|
this.log.debug('Frontend http server closed successfully');
|
|
720
588
|
}
|
|
721
|
-
// Close the https server
|
|
722
589
|
if (this.httpsServer) {
|
|
723
590
|
this.log.debug('Closing https server...');
|
|
724
|
-
/*
|
|
725
|
-
await withTimeout(
|
|
726
|
-
new Promise<void>((resolve) => {
|
|
727
|
-
this.httpsServer?.close((error) => {
|
|
728
|
-
if (error) {
|
|
729
|
-
// istanbul ignore next
|
|
730
|
-
this.log.error(`Error closing https server: ${error}`);
|
|
731
|
-
} else {
|
|
732
|
-
this.log.debug('Https server closed successfully');
|
|
733
|
-
this.emit('server_stopped');
|
|
734
|
-
}
|
|
735
|
-
resolve();
|
|
736
|
-
});
|
|
737
|
-
}),
|
|
738
|
-
5000,
|
|
739
|
-
false,
|
|
740
|
-
);
|
|
741
|
-
*/
|
|
742
591
|
this.httpsServer.close();
|
|
743
592
|
this.log.debug('Https server closed successfully');
|
|
744
593
|
this.emit('server_stopped');
|
|
@@ -748,7 +597,6 @@ export class Frontend extends EventEmitter {
|
|
|
748
597
|
}
|
|
749
598
|
this.log.debug('Frontend stopped successfully');
|
|
750
599
|
}
|
|
751
|
-
// Function to format bytes to KB, MB, or GB
|
|
752
600
|
formatMemoryUsage = (bytes) => {
|
|
753
601
|
if (bytes >= 1024 ** 3) {
|
|
754
602
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -760,7 +608,6 @@ export class Frontend extends EventEmitter {
|
|
|
760
608
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
761
609
|
}
|
|
762
610
|
};
|
|
763
|
-
// Function to format system uptime with only the most significant unit
|
|
764
611
|
formatOsUpTime = (seconds) => {
|
|
765
612
|
if (seconds >= 86400) {
|
|
766
613
|
const days = Math.floor(seconds / 86400);
|
|
@@ -776,13 +623,7 @@ export class Frontend extends EventEmitter {
|
|
|
776
623
|
}
|
|
777
624
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
778
625
|
};
|
|
779
|
-
/**
|
|
780
|
-
* Retrieves the api settings data.
|
|
781
|
-
*
|
|
782
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
783
|
-
*/
|
|
784
626
|
async getApiSettings() {
|
|
785
|
-
// Update the system information
|
|
786
627
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
787
628
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
788
629
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -791,7 +632,6 @@ export class Frontend extends EventEmitter {
|
|
|
791
632
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
792
633
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
793
634
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
794
|
-
// Update the matterbridge information
|
|
795
635
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
796
636
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
797
637
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
@@ -805,12 +645,6 @@ export class Frontend extends EventEmitter {
|
|
|
805
645
|
this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
|
|
806
646
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
807
647
|
}
|
|
808
|
-
/**
|
|
809
|
-
* Retrieves the reachable attribute.
|
|
810
|
-
*
|
|
811
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
812
|
-
* @returns {boolean} The reachable attribute.
|
|
813
|
-
*/
|
|
814
648
|
getReachability(device) {
|
|
815
649
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
816
650
|
return false;
|
|
@@ -822,12 +656,6 @@ export class Frontend extends EventEmitter {
|
|
|
822
656
|
return true;
|
|
823
657
|
return false;
|
|
824
658
|
}
|
|
825
|
-
/**
|
|
826
|
-
* Retrieves the power source attribute.
|
|
827
|
-
*
|
|
828
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
829
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
830
|
-
*/
|
|
831
659
|
getPowerSource(endpoint) {
|
|
832
660
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
833
661
|
return undefined;
|
|
@@ -843,22 +671,13 @@ export class Frontend extends EventEmitter {
|
|
|
843
671
|
}
|
|
844
672
|
return;
|
|
845
673
|
};
|
|
846
|
-
// Root endpoint
|
|
847
674
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
848
675
|
return powerSource(endpoint);
|
|
849
|
-
// Child endpoints
|
|
850
676
|
for (const child of endpoint.getChildEndpoints()) {
|
|
851
677
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
852
678
|
return powerSource(child);
|
|
853
679
|
}
|
|
854
680
|
}
|
|
855
|
-
/**
|
|
856
|
-
* Retrieves the cluster text description from a given device.
|
|
857
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
858
|
-
*
|
|
859
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
860
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
861
|
-
*/
|
|
862
681
|
getClusterTextFromDevice(device) {
|
|
863
682
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
864
683
|
return '';
|
|
@@ -869,7 +688,6 @@ export class Frontend extends EventEmitter {
|
|
|
869
688
|
if (composed)
|
|
870
689
|
return 'Composed: ' + composed.value;
|
|
871
690
|
}
|
|
872
|
-
// istanbul ignore next cause is not reachable
|
|
873
691
|
return '';
|
|
874
692
|
};
|
|
875
693
|
const getFixedLabel = (device) => {
|
|
@@ -879,13 +697,11 @@ export class Frontend extends EventEmitter {
|
|
|
879
697
|
if (composed)
|
|
880
698
|
return 'Composed: ' + composed.value;
|
|
881
699
|
}
|
|
882
|
-
// istanbul ignore next cause is not reacheable
|
|
883
700
|
return '';
|
|
884
701
|
};
|
|
885
702
|
let attributes = '';
|
|
886
703
|
let supportedModes = [];
|
|
887
704
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
888
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
889
705
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
890
706
|
return;
|
|
891
707
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -975,17 +791,11 @@ export class Frontend extends EventEmitter {
|
|
|
975
791
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
976
792
|
attributes += `${getUserLabel(device)} `;
|
|
977
793
|
});
|
|
978
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
979
794
|
return attributes.trimStart().trimEnd();
|
|
980
795
|
}
|
|
981
|
-
/**
|
|
982
|
-
* Retrieves the registered plugins sanitized for res.json().
|
|
983
|
-
*
|
|
984
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
985
|
-
*/
|
|
986
796
|
getPlugins() {
|
|
987
797
|
if (this.matterbridge.hasCleanupStarted)
|
|
988
|
-
return [];
|
|
798
|
+
return [];
|
|
989
799
|
const baseRegisteredPlugins = [];
|
|
990
800
|
for (const plugin of this.matterbridge.plugins) {
|
|
991
801
|
baseRegisteredPlugins.push({
|
|
@@ -1013,27 +823,18 @@ export class Frontend extends EventEmitter {
|
|
|
1013
823
|
schemaJson: plugin.schemaJson,
|
|
1014
824
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1015
825
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1016
|
-
// Childbridge mode specific data
|
|
1017
826
|
matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
|
|
1018
827
|
});
|
|
1019
828
|
}
|
|
1020
829
|
return baseRegisteredPlugins;
|
|
1021
830
|
}
|
|
1022
|
-
/**
|
|
1023
|
-
* Retrieves the devices from Matterbridge.
|
|
1024
|
-
*
|
|
1025
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1026
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
|
|
1027
|
-
*/
|
|
1028
831
|
async getDevices(pluginName) {
|
|
1029
832
|
if (this.matterbridge.hasCleanupStarted)
|
|
1030
|
-
return [];
|
|
833
|
+
return [];
|
|
1031
834
|
const devices = [];
|
|
1032
835
|
for (const device of this.matterbridge.devices.array()) {
|
|
1033
|
-
// Filter by pluginName if provided
|
|
1034
836
|
if (pluginName && pluginName !== device.plugin)
|
|
1035
837
|
continue;
|
|
1036
|
-
// Check if the device has the required properties
|
|
1037
838
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1038
839
|
continue;
|
|
1039
840
|
devices.push({
|
|
@@ -1053,37 +854,22 @@ export class Frontend extends EventEmitter {
|
|
|
1053
854
|
}
|
|
1054
855
|
return devices;
|
|
1055
856
|
}
|
|
1056
|
-
/**
|
|
1057
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1058
|
-
*
|
|
1059
|
-
* Response for /api/clusters
|
|
1060
|
-
*
|
|
1061
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1062
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1063
|
-
* @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1064
|
-
*/
|
|
1065
857
|
getClusters(pluginName, endpointNumber) {
|
|
1066
858
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1067
859
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1068
860
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1069
861
|
return;
|
|
1070
862
|
}
|
|
1071
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1072
|
-
// Get the device types from the main endpoint
|
|
1073
863
|
const deviceTypes = [];
|
|
1074
864
|
const clusters = [];
|
|
1075
865
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1076
866
|
deviceTypes.push(d.deviceType);
|
|
1077
867
|
});
|
|
1078
|
-
// Get the clusters from the main endpoint
|
|
1079
868
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1080
869
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1081
870
|
return;
|
|
1082
871
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1083
872
|
return;
|
|
1084
|
-
// console.log(
|
|
1085
|
-
// `${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}`,
|
|
1086
|
-
// );
|
|
1087
873
|
clusters.push({
|
|
1088
874
|
endpoint: endpoint.number.toString(),
|
|
1089
875
|
id: 'main',
|
|
@@ -1096,19 +882,12 @@ export class Frontend extends EventEmitter {
|
|
|
1096
882
|
attributeLocalValue: attributeValue,
|
|
1097
883
|
});
|
|
1098
884
|
});
|
|
1099
|
-
// Get the child endpoints
|
|
1100
885
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1101
|
-
// if (childEndpoints.length === 0) {
|
|
1102
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1103
|
-
// }
|
|
1104
886
|
childEndpoints.forEach((childEndpoint) => {
|
|
1105
|
-
// istanbul ignore if cause is not reachable: should never happen but ...
|
|
1106
887
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1107
888
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1108
889
|
return;
|
|
1109
890
|
}
|
|
1110
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1111
|
-
// Get the device types of the child endpoint
|
|
1112
891
|
const deviceTypes = [];
|
|
1113
892
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1114
893
|
deviceTypes.push(d.deviceType);
|
|
@@ -1118,12 +897,9 @@ export class Frontend extends EventEmitter {
|
|
|
1118
897
|
return;
|
|
1119
898
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1120
899
|
return;
|
|
1121
|
-
// console.log(
|
|
1122
|
-
// `${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}`,
|
|
1123
|
-
// );
|
|
1124
900
|
clusters.push({
|
|
1125
901
|
endpoint: childEndpoint.number.toString(),
|
|
1126
|
-
id: childEndpoint.maybeId ?? 'null',
|
|
902
|
+
id: childEndpoint.maybeId ?? 'null',
|
|
1127
903
|
deviceTypes,
|
|
1128
904
|
clusterName: capitalizeFirstLetter(clusterName),
|
|
1129
905
|
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
@@ -1136,13 +912,6 @@ export class Frontend extends EventEmitter {
|
|
|
1136
912
|
});
|
|
1137
913
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
|
|
1138
914
|
}
|
|
1139
|
-
/**
|
|
1140
|
-
* Handles incoming websocket api request messages from the Matterbridge frontend.
|
|
1141
|
-
*
|
|
1142
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1143
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1144
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1145
|
-
*/
|
|
1146
915
|
async wsMessageHandler(client, message) {
|
|
1147
916
|
let data;
|
|
1148
917
|
const sendResponse = (data) => {
|
|
@@ -1162,7 +931,7 @@ export class Frontend extends EventEmitter {
|
|
|
1162
931
|
};
|
|
1163
932
|
try {
|
|
1164
933
|
data = JSON.parse(message.toString());
|
|
1165
|
-
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method)
|
|
934
|
+
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || data.dst !== 'Matterbridge') {
|
|
1166
935
|
this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
|
|
1167
936
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
|
|
1168
937
|
return;
|
|
@@ -1205,48 +974,35 @@ export class Frontend extends EventEmitter {
|
|
|
1205
974
|
this.wssSendSnackbarMessage(`Installed package ${localData.params.packageName}`, 5, 'success');
|
|
1206
975
|
const packageName = localData.params.packageName.replace(/@.*$/, '');
|
|
1207
976
|
if (localData.params.restart === false && packageName !== 'matterbridge') {
|
|
1208
|
-
// The install comes from InstallPlugins
|
|
1209
977
|
this.matterbridge.plugins
|
|
1210
978
|
.add(packageName)
|
|
1211
979
|
.then((plugin) => {
|
|
1212
|
-
// istanbul ignore next if
|
|
1213
980
|
if (plugin) {
|
|
1214
|
-
// The plugin is not registered
|
|
1215
981
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1216
|
-
// In childbridge mode the plugins server node is not started when added
|
|
1217
982
|
if (this.matterbridge.bridgeMode === 'childbridge')
|
|
1218
983
|
this.wssSendRestartRequired(true, true);
|
|
1219
984
|
this.matterbridge.plugins
|
|
1220
985
|
.load(plugin, true, 'The plugin has been added', true)
|
|
1221
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1222
986
|
.then(() => {
|
|
1223
987
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
1224
988
|
this.wssSendRefreshRequired('plugins');
|
|
1225
989
|
return;
|
|
1226
990
|
})
|
|
1227
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1228
991
|
.catch((_error) => {
|
|
1229
|
-
//
|
|
1230
992
|
});
|
|
1231
993
|
}
|
|
1232
994
|
else {
|
|
1233
|
-
// The plugin is already registered
|
|
1234
995
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1235
996
|
this.wssSendRefreshRequired('plugins');
|
|
1236
997
|
this.wssSendRestartRequired(true, true);
|
|
1237
998
|
}
|
|
1238
999
|
return;
|
|
1239
1000
|
})
|
|
1240
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1241
1001
|
.catch((_error) => {
|
|
1242
|
-
//
|
|
1243
1002
|
});
|
|
1244
1003
|
}
|
|
1245
1004
|
else {
|
|
1246
|
-
// The package is matterbridge
|
|
1247
|
-
// istanbul ignore next
|
|
1248
1005
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1249
|
-
// istanbul ignore next if
|
|
1250
1006
|
if (this.matterbridge.restartMode !== '') {
|
|
1251
1007
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1252
1008
|
this.matterbridge.shutdownProcess();
|
|
@@ -1269,9 +1025,7 @@ export class Frontend extends EventEmitter {
|
|
|
1269
1025
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' });
|
|
1270
1026
|
return;
|
|
1271
1027
|
}
|
|
1272
|
-
// The package is a plugin
|
|
1273
1028
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1274
|
-
// istanbul ignore next if
|
|
1275
1029
|
if (plugin) {
|
|
1276
1030
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
1277
1031
|
await this.matterbridge.plugins.remove(data.params.packageName);
|
|
@@ -1279,7 +1033,6 @@ export class Frontend extends EventEmitter {
|
|
|
1279
1033
|
this.wssSendRefreshRequired('plugins');
|
|
1280
1034
|
this.wssSendRefreshRequired('devices');
|
|
1281
1035
|
}
|
|
1282
|
-
// Uninstall the package
|
|
1283
1036
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1284
1037
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
1285
1038
|
spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1322,7 +1075,6 @@ export class Frontend extends EventEmitter {
|
|
|
1322
1075
|
return;
|
|
1323
1076
|
})
|
|
1324
1077
|
.catch((_error) => {
|
|
1325
|
-
//
|
|
1326
1078
|
});
|
|
1327
1079
|
}
|
|
1328
1080
|
else {
|
|
@@ -1370,7 +1122,6 @@ export class Frontend extends EventEmitter {
|
|
|
1370
1122
|
return;
|
|
1371
1123
|
})
|
|
1372
1124
|
.catch((_error) => {
|
|
1373
|
-
//
|
|
1374
1125
|
});
|
|
1375
1126
|
}
|
|
1376
1127
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1396,7 +1147,6 @@ export class Frontend extends EventEmitter {
|
|
|
1396
1147
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1397
1148
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
|
|
1398
1149
|
if (plugin.serverNode) {
|
|
1399
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1400
1150
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1401
1151
|
plugin.serverNode = undefined;
|
|
1402
1152
|
}
|
|
@@ -1406,20 +1156,18 @@ export class Frontend extends EventEmitter {
|
|
|
1406
1156
|
this.matterbridge.devices.remove(device);
|
|
1407
1157
|
}
|
|
1408
1158
|
}
|
|
1409
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1410
1159
|
if (plugin.type === 'DynamicPlatform' && !plugin.locked)
|
|
1411
1160
|
await this.matterbridge.createDynamicPlugin(plugin);
|
|
1412
1161
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
|
|
1413
|
-
plugin.restartRequired = false;
|
|
1162
|
+
plugin.restartRequired = false;
|
|
1414
1163
|
let needRestart = 0;
|
|
1415
1164
|
for (const plugin of this.matterbridge.plugins) {
|
|
1416
1165
|
if (plugin.restartRequired)
|
|
1417
1166
|
needRestart++;
|
|
1418
1167
|
}
|
|
1419
1168
|
if (needRestart === 0) {
|
|
1420
|
-
this.wssSendRestartNotRequired(true);
|
|
1169
|
+
this.wssSendRestartNotRequired(true);
|
|
1421
1170
|
}
|
|
1422
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1423
1171
|
if (plugin.serverNode)
|
|
1424
1172
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1425
1173
|
this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
|
|
@@ -1467,8 +1215,8 @@ export class Frontend extends EventEmitter {
|
|
|
1467
1215
|
}
|
|
1468
1216
|
else if (data.method === '/api/shellynetconfig') {
|
|
1469
1217
|
this.log.debug('/api/shellynetconfig:', data.params);
|
|
1470
|
-
const { triggerShellyChangeIp
|
|
1471
|
-
|
|
1218
|
+
const { triggerShellyChangeIp } = await import('./shelly.js');
|
|
1219
|
+
triggerShellyChangeIp(this.matterbridge, data.params);
|
|
1472
1220
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
1473
1221
|
}
|
|
1474
1222
|
else if (data.method === '/api/softreset') {
|
|
@@ -1612,7 +1360,7 @@ export class Frontend extends EventEmitter {
|
|
|
1612
1360
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/devices' });
|
|
1613
1361
|
return;
|
|
1614
1362
|
}
|
|
1615
|
-
const selectDeviceValues = plugin.platform
|
|
1363
|
+
const selectDeviceValues = !plugin.platform ? [] : plugin.platform.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1616
1364
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectDeviceValues });
|
|
1617
1365
|
}
|
|
1618
1366
|
else if (data.method === '/api/select/entities') {
|
|
@@ -1625,7 +1373,7 @@ export class Frontend extends EventEmitter {
|
|
|
1625
1373
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' });
|
|
1626
1374
|
return;
|
|
1627
1375
|
}
|
|
1628
|
-
const selectEntityValues = plugin.platform
|
|
1376
|
+
const selectEntityValues = !plugin.platform ? [] : plugin.platform.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
|
|
1629
1377
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectEntityValues });
|
|
1630
1378
|
}
|
|
1631
1379
|
else if (data.method === '/api/action') {
|
|
@@ -1675,22 +1423,22 @@ export class Frontend extends EventEmitter {
|
|
|
1675
1423
|
if (isValidString(data.params.value, 4)) {
|
|
1676
1424
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1677
1425
|
if (data.params.value === 'Debug') {
|
|
1678
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1426
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1679
1427
|
}
|
|
1680
1428
|
else if (data.params.value === 'Info') {
|
|
1681
|
-
await this.matterbridge.setLogLevel("info"
|
|
1429
|
+
await this.matterbridge.setLogLevel("info");
|
|
1682
1430
|
}
|
|
1683
1431
|
else if (data.params.value === 'Notice') {
|
|
1684
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1432
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1685
1433
|
}
|
|
1686
1434
|
else if (data.params.value === 'Warn') {
|
|
1687
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1435
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1688
1436
|
}
|
|
1689
1437
|
else if (data.params.value === 'Error') {
|
|
1690
|
-
await this.matterbridge.setLogLevel("error"
|
|
1438
|
+
await this.matterbridge.setLogLevel("error");
|
|
1691
1439
|
}
|
|
1692
1440
|
else if (data.params.value === 'Fatal') {
|
|
1693
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1441
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1694
1442
|
}
|
|
1695
1443
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1696
1444
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1701,7 +1449,6 @@ export class Frontend extends EventEmitter {
|
|
|
1701
1449
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1702
1450
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1703
1451
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1704
|
-
// Create the file logger for matterbridge
|
|
1705
1452
|
if (data.params.value)
|
|
1706
1453
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1707
1454
|
else
|
|
@@ -1780,7 +1527,6 @@ export class Frontend extends EventEmitter {
|
|
|
1780
1527
|
}
|
|
1781
1528
|
break;
|
|
1782
1529
|
case 'setmatterport':
|
|
1783
|
-
// eslint-disable-next-line no-case-declarations
|
|
1784
1530
|
const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1785
1531
|
if (isValidNumber(port, 5540, 5600)) {
|
|
1786
1532
|
this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
|
|
@@ -1798,9 +1544,8 @@ export class Frontend extends EventEmitter {
|
|
|
1798
1544
|
}
|
|
1799
1545
|
break;
|
|
1800
1546
|
case 'setmatterdiscriminator':
|
|
1801
|
-
// eslint-disable-next-line no-case-declarations
|
|
1802
1547
|
const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1803
|
-
if (isValidNumber(discriminator,
|
|
1548
|
+
if (isValidNumber(discriminator, 0, 4095)) {
|
|
1804
1549
|
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
|
|
1805
1550
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
|
|
1806
1551
|
await this.matterbridge.nodeContext?.set('matterdiscriminator', discriminator);
|
|
@@ -1816,9 +1561,8 @@ export class Frontend extends EventEmitter {
|
|
|
1816
1561
|
}
|
|
1817
1562
|
break;
|
|
1818
1563
|
case 'setmatterpasscode':
|
|
1819
|
-
// eslint-disable-next-line no-case-declarations
|
|
1820
1564
|
const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1821
|
-
if (isValidNumber(passcode,
|
|
1565
|
+
if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
|
|
1822
1566
|
this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
|
|
1823
1567
|
this.log.debug(`Set matter commissioning passcode to ${CYAN}${passcode}${db}`);
|
|
1824
1568
|
await this.matterbridge.nodeContext?.set('matterpasscode', passcode);
|
|
@@ -1860,19 +1604,15 @@ export class Frontend extends EventEmitter {
|
|
|
1860
1604
|
return;
|
|
1861
1605
|
}
|
|
1862
1606
|
const config = plugin.configJson;
|
|
1863
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1864
1607
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1865
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1866
1608
|
if (select === 'serial')
|
|
1867
1609
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1868
1610
|
if (select === 'name')
|
|
1869
1611
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1870
1612
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1871
|
-
// Remove postfix from the serial if it exists
|
|
1872
1613
|
if (config.postfix) {
|
|
1873
1614
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1874
1615
|
}
|
|
1875
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1876
1616
|
if (isValidArray(config.whiteList, 1)) {
|
|
1877
1617
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1878
1618
|
config.whiteList.push(data.params.serial);
|
|
@@ -1881,7 +1621,6 @@ export class Frontend extends EventEmitter {
|
|
|
1881
1621
|
config.whiteList.push(data.params.name);
|
|
1882
1622
|
}
|
|
1883
1623
|
}
|
|
1884
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1885
1624
|
if (isValidArray(config.blackList, 1)) {
|
|
1886
1625
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1887
1626
|
config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
|
|
@@ -1909,9 +1648,7 @@ export class Frontend extends EventEmitter {
|
|
|
1909
1648
|
return;
|
|
1910
1649
|
}
|
|
1911
1650
|
const config = plugin.configJson;
|
|
1912
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1913
1651
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1914
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1915
1652
|
if (select === 'serial')
|
|
1916
1653
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1917
1654
|
if (select === 'name')
|
|
@@ -1920,7 +1657,6 @@ export class Frontend extends EventEmitter {
|
|
|
1920
1657
|
if (config.postfix) {
|
|
1921
1658
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1922
1659
|
}
|
|
1923
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1924
1660
|
if (isValidArray(config.whiteList, 1)) {
|
|
1925
1661
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1926
1662
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
|
|
@@ -1929,7 +1665,6 @@ export class Frontend extends EventEmitter {
|
|
|
1929
1665
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
|
|
1930
1666
|
}
|
|
1931
1667
|
}
|
|
1932
|
-
// Add the serial to the blackList
|
|
1933
1668
|
if (isValidArray(config.blackList)) {
|
|
1934
1669
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1935
1670
|
config.blackList.push(data.params.serial);
|
|
@@ -1952,7 +1687,6 @@ export class Frontend extends EventEmitter {
|
|
|
1952
1687
|
}
|
|
1953
1688
|
}
|
|
1954
1689
|
else {
|
|
1955
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1956
1690
|
const localData = data;
|
|
1957
1691
|
this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
|
|
1958
1692
|
sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
|
|
@@ -1962,206 +1696,83 @@ export class Frontend extends EventEmitter {
|
|
|
1962
1696
|
inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
|
|
1963
1697
|
}
|
|
1964
1698
|
}
|
|
1965
|
-
/**
|
|
1966
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1967
|
-
*
|
|
1968
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1969
|
-
* @param {string} time - The time string of the message
|
|
1970
|
-
* @param {string} name - The logger name of the message
|
|
1971
|
-
* @param {string} message - The content of the message.
|
|
1972
|
-
*
|
|
1973
|
-
* @remarks
|
|
1974
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
1975
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
1976
|
-
* The function sends the message to all connected clients.
|
|
1977
|
-
*/
|
|
1978
1699
|
wssSendLogMessage(level, time, name, message) {
|
|
1979
1700
|
if (!level || !time || !name || !message)
|
|
1980
1701
|
return;
|
|
1981
|
-
// Remove ANSI escape codes from the message
|
|
1982
|
-
// eslint-disable-next-line no-control-regex
|
|
1983
1702
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1984
|
-
// Remove leading asterisks from the message
|
|
1985
1703
|
message = message.replace(/^\*+/, '');
|
|
1986
|
-
// Replace all occurrences of \t and \n
|
|
1987
1704
|
message = message.replace(/[\t\n]/g, '');
|
|
1988
|
-
// Remove non-printable characters
|
|
1989
|
-
// eslint-disable-next-line no-control-regex
|
|
1990
1705
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1991
|
-
// Replace all occurrences of \" with "
|
|
1992
1706
|
message = message.replace(/\\"/g, '"');
|
|
1993
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1994
1707
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1995
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1996
1708
|
const maxContinuousLength = 100;
|
|
1997
1709
|
const keepStartLength = 20;
|
|
1998
1710
|
const keepEndLength = 20;
|
|
1999
|
-
// Split the message into words
|
|
2000
1711
|
message = message
|
|
2001
1712
|
.split(' ')
|
|
2002
1713
|
.map((word) => {
|
|
2003
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2004
1714
|
if (word.length > maxContinuousLength) {
|
|
2005
1715
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2006
1716
|
}
|
|
2007
1717
|
return word;
|
|
2008
1718
|
})
|
|
2009
1719
|
.join(' ');
|
|
2010
|
-
|
|
2011
|
-
this.wssBroadcastMessage({ id: 0 /* WsBroadcastMessageId.Log */, src: 'Matterbridge', dst: 'Frontend', method: 'log', params: { level, time, name, message } });
|
|
1720
|
+
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', params: { level, time, name, message } });
|
|
2012
1721
|
}
|
|
2013
|
-
/**
|
|
2014
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2015
|
-
*
|
|
2016
|
-
* @param {string} changed - The changed value.
|
|
2017
|
-
* @param {Record<string, unknown>} params - Additional parameters to send with the message.
|
|
2018
|
-
* possible values for changed:
|
|
2019
|
-
* - 'settings'
|
|
2020
|
-
* - 'plugins'
|
|
2021
|
-
* - 'devices'
|
|
2022
|
-
* - 'matter' with param 'matter' (QRDiv component)
|
|
2023
|
-
* @param {ApiMatterResponse} params.matter - The matter device that has changed. Required if changed is 'matter'.
|
|
2024
|
-
*/
|
|
2025
1722
|
wssSendRefreshRequired(changed, params) {
|
|
2026
1723
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
2027
|
-
|
|
2028
|
-
this.wssBroadcastMessage({ id: 1 /* WsBroadcastMessageId.RefreshRequired */, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed, ...params } });
|
|
1724
|
+
this.wssBroadcastMessage({ id: 1, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed, ...params } });
|
|
2029
1725
|
}
|
|
2030
|
-
/**
|
|
2031
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2032
|
-
*
|
|
2033
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
|
|
2034
|
-
* @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
|
|
2035
|
-
*/
|
|
2036
1726
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
2037
1727
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
2038
1728
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
2039
1729
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
|
|
2040
1730
|
if (snackbar === true)
|
|
2041
1731
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
2042
|
-
|
|
2043
|
-
this.wssBroadcastMessage({ id: 2 /* WsBroadcastMessageId.RestartRequired */, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } });
|
|
1732
|
+
this.wssBroadcastMessage({ id: 2, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } });
|
|
2044
1733
|
}
|
|
2045
|
-
/**
|
|
2046
|
-
* Sends a no need to restart WebSocket message to all connected clients.
|
|
2047
|
-
*
|
|
2048
|
-
* @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients. Default is true.
|
|
2049
|
-
*/
|
|
2050
1734
|
wssSendRestartNotRequired(snackbar = true) {
|
|
2051
1735
|
this.log.debug('Sending a restart not required message to all connected clients');
|
|
2052
1736
|
this.matterbridge.matterbridgeInformation.restartRequired = false;
|
|
2053
1737
|
if (snackbar === true)
|
|
2054
1738
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2055
|
-
|
|
2056
|
-
this.wssBroadcastMessage({ id: 3 /* WsBroadcastMessageId.RestartNotRequired */, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required' });
|
|
1739
|
+
this.wssBroadcastMessage({ id: 3, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required' });
|
|
2057
1740
|
}
|
|
2058
|
-
/**
|
|
2059
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
2060
|
-
*
|
|
2061
|
-
* @param {boolean} devVersion - If true, the update is for a development version. Default is false.
|
|
2062
|
-
*/
|
|
2063
1741
|
wssSendUpdateRequired(devVersion = false) {
|
|
2064
1742
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2065
1743
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
2066
|
-
|
|
2067
|
-
this.wssBroadcastMessage({ id: 8 /* WsBroadcastMessageId.UpdateRequired */, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required' });
|
|
1744
|
+
this.wssBroadcastMessage({ id: 8, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required' });
|
|
2068
1745
|
}
|
|
2069
|
-
/**
|
|
2070
|
-
* Sends a cpu update message to all connected clients.
|
|
2071
|
-
*
|
|
2072
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2073
|
-
*/
|
|
2074
1746
|
wssSendCpuUpdate(cpuUsage) {
|
|
2075
1747
|
if (hasParameter('debug'))
|
|
2076
1748
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2077
|
-
|
|
2078
|
-
this.wssBroadcastMessage({ id: 4 /* WsBroadcastMessageId.CpuUpdate */, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage: Math.round(cpuUsage * 100) / 100 } });
|
|
1749
|
+
this.wssBroadcastMessage({ id: 4, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage: Math.round(cpuUsage * 100) / 100 } });
|
|
2079
1750
|
}
|
|
2080
|
-
/**
|
|
2081
|
-
* Sends a memory update message to all connected clients.
|
|
2082
|
-
*
|
|
2083
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2084
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2085
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2086
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2087
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2088
|
-
* @param {string} external - The external memory in bytes.
|
|
2089
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2090
|
-
*/
|
|
2091
1751
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2092
1752
|
if (hasParameter('debug'))
|
|
2093
1753
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2094
|
-
|
|
2095
|
-
this.wssBroadcastMessage({ id: 5 /* WsBroadcastMessageId.MemoryUpdate */, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
|
|
1754
|
+
this.wssBroadcastMessage({ id: 5, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
|
|
2096
1755
|
}
|
|
2097
|
-
/**
|
|
2098
|
-
* Sends an uptime update message to all connected clients.
|
|
2099
|
-
*
|
|
2100
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2101
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2102
|
-
*/
|
|
2103
1756
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2104
1757
|
if (hasParameter('debug'))
|
|
2105
1758
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2106
|
-
|
|
2107
|
-
this.wssBroadcastMessage({ id: 6 /* WsBroadcastMessageId.UptimeUpdate */, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } });
|
|
1759
|
+
this.wssBroadcastMessage({ id: 6, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } });
|
|
2108
1760
|
}
|
|
2109
|
-
/**
|
|
2110
|
-
* Sends an open snackbar message to all connected clients.
|
|
2111
|
-
*
|
|
2112
|
-
* @param {string} message - The message to send.
|
|
2113
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message. Default is 5 seconds.
|
|
2114
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the message.
|
|
2115
|
-
* possible values are: 'info', 'warning', 'error', 'success'. Default is 'info'.
|
|
2116
|
-
*
|
|
2117
|
-
* @remarks
|
|
2118
|
-
* If timeout is 0, the snackbar message will be displayed until closed by the user.
|
|
2119
|
-
*/
|
|
2120
1761
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2121
1762
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2122
|
-
|
|
2123
|
-
this.wssBroadcastMessage({ id: 7 /* WsBroadcastMessageId.Snackbar */, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', params: { message, timeout, severity } });
|
|
1763
|
+
this.wssBroadcastMessage({ id: 7, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', params: { message, timeout, severity } });
|
|
2124
1764
|
}
|
|
2125
|
-
/**
|
|
2126
|
-
* Sends a close snackbar message to all connected clients.
|
|
2127
|
-
* It will close the snackbar message with the same message and timeout = 0.
|
|
2128
|
-
*
|
|
2129
|
-
* @param {string} message - The message to send.
|
|
2130
|
-
*/
|
|
2131
1765
|
wssSendCloseSnackbarMessage(message) {
|
|
2132
1766
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2133
|
-
|
|
2134
|
-
this.wssBroadcastMessage({ id: 10 /* WsBroadcastMessageId.CloseSnackbar */, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', params: { message } });
|
|
1767
|
+
this.wssBroadcastMessage({ id: 10, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', params: { message } });
|
|
2135
1768
|
}
|
|
2136
|
-
/**
|
|
2137
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2138
|
-
*
|
|
2139
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2140
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2141
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2142
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2143
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2144
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2145
|
-
*
|
|
2146
|
-
* @remarks
|
|
2147
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2148
|
-
* with the updated attribute information.
|
|
2149
|
-
*/
|
|
2150
1769
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
2151
1770
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2152
|
-
|
|
2153
|
-
this.wssBroadcastMessage({ id: 9 /* WsBroadcastMessageId.StateUpdate */, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } });
|
|
1771
|
+
this.wssBroadcastMessage({ id: 9, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } });
|
|
2154
1772
|
}
|
|
2155
|
-
/**
|
|
2156
|
-
* Sends a message to all connected clients.
|
|
2157
|
-
* This is an helper function to send a broadcast message to all connected clients.
|
|
2158
|
-
*
|
|
2159
|
-
* @param {WsMessageBroadcast} msg - The message to send.
|
|
2160
|
-
*/
|
|
2161
1773
|
wssBroadcastMessage(msg) {
|
|
2162
|
-
// Send the message to all connected clients
|
|
2163
1774
|
const stringifiedMsg = JSON.stringify(msg);
|
|
2164
|
-
if (msg.id !== 0
|
|
1775
|
+
if (msg.id !== 0)
|
|
2165
1776
|
this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
|
|
2166
1777
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2167
1778
|
if (client.readyState === WebSocket.OPEN) {
|
|
@@ -2170,4 +1781,3 @@ export class Frontend extends EventEmitter {
|
|
|
2170
1781
|
});
|
|
2171
1782
|
}
|
|
2172
1783
|
}
|
|
2173
|
-
//# sourceMappingURL=frontend.js.map
|