matterbridge 3.2.9 → 3.3.0-dev-20250928-3d2b558
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 +83 -0
- 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 +29 -418
- package/dist/frontendTypes.js +0 -45
- package/dist/globalMatterbridge.js +0 -47
- package/dist/helpers.js +0 -53
- package/dist/index.js +2 -31
- 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 +52 -785
- 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 +58 -1398
- package/dist/matterbridgeEndpointHelpers.js +12 -345
- package/dist/matterbridgePlatform.js +32 -326
- package/dist/matterbridgeTypes.js +0 -25
- package/dist/pluginManager.js +3 -249
- 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 -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 -69
- package/dist/utils/wait.js +8 -60
- package/frontend/build/assets/index.js +7 -7
- package/frontend/build/assets/vendor_mui.js +23 -31
- package/frontend/build/assets/vendor_node_modules.js +33 -25
- 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 +7 -7
- package/frontend/package-lock.json +401 -310
- package/frontend/package.json +9 -9
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- 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 -232
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/frontendTypes.d.ts +0 -514
- 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 -444
- 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 -1534
- 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 -365
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -197
- 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 -34
- 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/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,18 +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
|
-
// Matterbridge
|
|
44
16
|
import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout, hasParameter, wait, inspectError } from './utils/export.js';
|
|
45
17
|
import { plg } from './matterbridgeTypes.js';
|
|
46
18
|
import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
|
|
@@ -57,7 +29,7 @@ export class Frontend extends EventEmitter {
|
|
|
57
29
|
constructor(matterbridge) {
|
|
58
30
|
super();
|
|
59
31
|
this.matterbridge = matterbridge;
|
|
60
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
32
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
61
33
|
this.log.logNameColor = '\x1b[38;5;97m';
|
|
62
34
|
}
|
|
63
35
|
set logLevel(logLevel) {
|
|
@@ -66,39 +38,10 @@ export class Frontend extends EventEmitter {
|
|
|
66
38
|
async start(port = 8283) {
|
|
67
39
|
this.port = port;
|
|
68
40
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
69
|
-
|
|
70
|
-
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads'); // Is created by matterbridge initialize
|
|
41
|
+
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
71
42
|
const upload = multer({ dest: uploadDir });
|
|
72
|
-
// Create the express app that serves the frontend
|
|
73
43
|
this.expressApp = express();
|
|
74
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
75
|
-
/*
|
|
76
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
77
|
-
for (const method of methods) {
|
|
78
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
79
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
80
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
81
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
82
|
-
try {
|
|
83
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
84
|
-
return original(path, ...rest);
|
|
85
|
-
} catch (err) {
|
|
86
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
87
|
-
throw err;
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
*/
|
|
92
|
-
// Log all requests to the server for debugging
|
|
93
|
-
/*
|
|
94
|
-
this.expressApp.use((req, res, next) => {
|
|
95
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
96
|
-
next();
|
|
97
|
-
});
|
|
98
|
-
*/
|
|
99
|
-
// Serve static files from '/static' endpoint
|
|
100
44
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
101
|
-
// Read the package.json file to get the frontend version
|
|
102
45
|
try {
|
|
103
46
|
this.log.debug(`Reading frontend package.json...`);
|
|
104
47
|
const frontendJson = await fs.readFile(path.join(this.matterbridge.rootDirectory, 'frontend/package.json'), 'utf8');
|
|
@@ -106,11 +49,9 @@ export class Frontend extends EventEmitter {
|
|
|
106
49
|
this.log.debug(`Frontend version ${CYAN}${this.matterbridge.matterbridgeInformation.frontendVersion}${db}`);
|
|
107
50
|
}
|
|
108
51
|
catch (error) {
|
|
109
|
-
// istanbul ignore next
|
|
110
52
|
this.log.error(`Failed to read frontend package.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
111
53
|
}
|
|
112
54
|
if (!hasParameter('ssl')) {
|
|
113
|
-
// Create an HTTP server and attach the express app
|
|
114
55
|
try {
|
|
115
56
|
this.log.debug(`Creating HTTP server...`);
|
|
116
57
|
this.httpServer = createServer(this.expressApp);
|
|
@@ -120,7 +61,6 @@ export class Frontend extends EventEmitter {
|
|
|
120
61
|
this.emit('server_error', error);
|
|
121
62
|
return;
|
|
122
63
|
}
|
|
123
|
-
// Listen on the specified port
|
|
124
64
|
if (hasParameter('ingress')) {
|
|
125
65
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
126
66
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -161,7 +101,6 @@ export class Frontend extends EventEmitter {
|
|
|
161
101
|
let passphrase;
|
|
162
102
|
let httpsServerOptions = {};
|
|
163
103
|
if (existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
|
|
164
|
-
// Load the p12 certificate and the passphrase
|
|
165
104
|
try {
|
|
166
105
|
pfx = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
|
|
167
106
|
this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
|
|
@@ -173,7 +112,7 @@ export class Frontend extends EventEmitter {
|
|
|
173
112
|
}
|
|
174
113
|
try {
|
|
175
114
|
passphrase = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
|
|
176
|
-
passphrase = passphrase.trim();
|
|
115
|
+
passphrase = passphrase.trim();
|
|
177
116
|
this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
|
|
178
117
|
}
|
|
179
118
|
catch (error) {
|
|
@@ -184,7 +123,6 @@ export class Frontend extends EventEmitter {
|
|
|
184
123
|
httpsServerOptions = { pfx, passphrase };
|
|
185
124
|
}
|
|
186
125
|
else {
|
|
187
|
-
// 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.
|
|
188
126
|
try {
|
|
189
127
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
190
128
|
this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
|
|
@@ -214,10 +152,9 @@ export class Frontend extends EventEmitter {
|
|
|
214
152
|
httpsServerOptions = { cert: fullChain ?? cert, key, ca };
|
|
215
153
|
}
|
|
216
154
|
if (hasParameter('mtls')) {
|
|
217
|
-
httpsServerOptions.requestCert = true;
|
|
218
|
-
httpsServerOptions.rejectUnauthorized = true;
|
|
155
|
+
httpsServerOptions.requestCert = true;
|
|
156
|
+
httpsServerOptions.rejectUnauthorized = true;
|
|
219
157
|
}
|
|
220
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
221
158
|
try {
|
|
222
159
|
this.log.debug(`Creating HTTPS server...`);
|
|
223
160
|
this.httpsServer = https.createServer(httpsServerOptions, this.expressApp);
|
|
@@ -227,7 +164,6 @@ export class Frontend extends EventEmitter {
|
|
|
227
164
|
this.emit('server_error', error);
|
|
228
165
|
return;
|
|
229
166
|
}
|
|
230
|
-
// Listen on the specified port
|
|
231
167
|
if (hasParameter('ingress')) {
|
|
232
168
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
233
169
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -259,19 +195,17 @@ export class Frontend extends EventEmitter {
|
|
|
259
195
|
return;
|
|
260
196
|
});
|
|
261
197
|
}
|
|
262
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
263
198
|
const wssPort = this.port;
|
|
264
199
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
265
200
|
this.log.debug(`Creating WebSocketServer on host ${CYAN}${wssHost}${db}...`);
|
|
266
201
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
267
202
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
268
203
|
const clientIp = request.socket.remoteAddress;
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
204
|
+
let callbackLogLevel = "notice";
|
|
205
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
206
|
+
callbackLogLevel = "info";
|
|
207
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
208
|
+
callbackLogLevel = "debug";
|
|
275
209
|
AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
|
|
276
210
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
277
211
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -293,7 +227,6 @@ export class Frontend extends EventEmitter {
|
|
|
293
227
|
}
|
|
294
228
|
});
|
|
295
229
|
ws.on('error', (error) => {
|
|
296
|
-
// istanbul ignore next
|
|
297
230
|
this.log.error(`WebSocket client error: ${error}`);
|
|
298
231
|
});
|
|
299
232
|
});
|
|
@@ -307,7 +240,6 @@ export class Frontend extends EventEmitter {
|
|
|
307
240
|
this.webSocketServer.on('error', (ws, error) => {
|
|
308
241
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
309
242
|
});
|
|
310
|
-
// Subscribe to cli events
|
|
311
243
|
cliEmitter.removeAllListeners();
|
|
312
244
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
313
245
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -318,8 +250,6 @@ export class Frontend extends EventEmitter {
|
|
|
318
250
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
319
251
|
this.wssSendCpuUpdate(cpuUsage);
|
|
320
252
|
});
|
|
321
|
-
// Endpoint to validate login code
|
|
322
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
323
253
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
324
254
|
const { password } = req.body;
|
|
325
255
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -338,27 +268,23 @@ export class Frontend extends EventEmitter {
|
|
|
338
268
|
this.log.warn('/api/login error wrong password');
|
|
339
269
|
res.json({ valid: false });
|
|
340
270
|
}
|
|
341
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
342
271
|
}
|
|
343
272
|
catch (error) {
|
|
344
273
|
this.log.error('/api/login error getting password');
|
|
345
274
|
res.json({ valid: false });
|
|
346
275
|
}
|
|
347
276
|
});
|
|
348
|
-
// Endpoint to provide health check for docker
|
|
349
277
|
this.expressApp.get('/health', (req, res) => {
|
|
350
278
|
this.log.debug('Express received /health');
|
|
351
279
|
const healthStatus = {
|
|
352
|
-
status: 'ok',
|
|
353
|
-
uptime: process.uptime(),
|
|
354
|
-
timestamp: new Date().toISOString(),
|
|
280
|
+
status: 'ok',
|
|
281
|
+
uptime: process.uptime(),
|
|
282
|
+
timestamp: new Date().toISOString(),
|
|
355
283
|
};
|
|
356
284
|
res.status(200).json(healthStatus);
|
|
357
285
|
});
|
|
358
|
-
// Endpoint to provide memory usage details
|
|
359
286
|
this.expressApp.get('/memory', async (req, res) => {
|
|
360
287
|
this.log.debug('Express received /memory');
|
|
361
|
-
// Memory usage from process
|
|
362
288
|
const memoryUsageRaw = process.memoryUsage();
|
|
363
289
|
const memoryUsage = {
|
|
364
290
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -367,13 +293,10 @@ export class Frontend extends EventEmitter {
|
|
|
367
293
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
368
294
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
369
295
|
};
|
|
370
|
-
// V8 heap statistics
|
|
371
296
|
const { default: v8 } = await import('node:v8');
|
|
372
297
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
373
298
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
374
|
-
// Format heapStats
|
|
375
299
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
376
|
-
// Format heapSpaces
|
|
377
300
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
378
301
|
...space,
|
|
379
302
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -391,23 +314,19 @@ export class Frontend extends EventEmitter {
|
|
|
391
314
|
};
|
|
392
315
|
res.status(200).json(memoryReport);
|
|
393
316
|
});
|
|
394
|
-
// Endpoint to provide settings
|
|
395
317
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
396
318
|
this.log.debug('The frontend sent /api/settings');
|
|
397
319
|
res.json(await this.getApiSettings());
|
|
398
320
|
});
|
|
399
|
-
// Endpoint to provide plugins
|
|
400
321
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
401
322
|
this.log.debug('The frontend sent /api/plugins');
|
|
402
323
|
res.json(this.getPlugins());
|
|
403
324
|
});
|
|
404
|
-
// Endpoint to provide devices
|
|
405
325
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
406
326
|
this.log.debug('The frontend sent /api/devices');
|
|
407
327
|
const devices = await this.getDevices();
|
|
408
328
|
res.json(devices);
|
|
409
329
|
});
|
|
410
|
-
// Endpoint to view the matterbridge log
|
|
411
330
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
412
331
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
413
332
|
try {
|
|
@@ -420,7 +339,6 @@ export class Frontend extends EventEmitter {
|
|
|
420
339
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
421
340
|
}
|
|
422
341
|
});
|
|
423
|
-
// Endpoint to view the matter.js log
|
|
424
342
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
425
343
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
426
344
|
try {
|
|
@@ -433,11 +351,9 @@ export class Frontend extends EventEmitter {
|
|
|
433
351
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
434
352
|
}
|
|
435
353
|
});
|
|
436
|
-
// Endpoint to view the diagnostic.log
|
|
437
354
|
this.expressApp.get('/api/view-diagnostic', async (req, res) => {
|
|
438
355
|
this.log.debug('The frontend sent /api/view-diagnostic');
|
|
439
356
|
const serverNodes = [];
|
|
440
|
-
// istanbul ignore else
|
|
441
357
|
if (this.matterbridge.bridgeMode === 'bridge') {
|
|
442
358
|
if (this.matterbridge.serverNode)
|
|
443
359
|
serverNodes.push(this.matterbridge.serverNode);
|
|
@@ -448,7 +364,6 @@ export class Frontend extends EventEmitter {
|
|
|
448
364
|
serverNodes.push(plugin.serverNode);
|
|
449
365
|
}
|
|
450
366
|
}
|
|
451
|
-
// istanbul ignore next
|
|
452
367
|
for (const device of this.matterbridge.getDevices()) {
|
|
453
368
|
if (device.serverNode)
|
|
454
369
|
serverNodes.push(device.serverNode);
|
|
@@ -471,20 +386,17 @@ export class Frontend extends EventEmitter {
|
|
|
471
386
|
values: [...serverNodes],
|
|
472
387
|
})));
|
|
473
388
|
delete Logger.destinations.diagnostic;
|
|
474
|
-
await wait(500);
|
|
389
|
+
await wait(500);
|
|
475
390
|
try {
|
|
476
391
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'), 'utf8');
|
|
477
392
|
res.type('text/plain');
|
|
478
393
|
res.send(data.slice(29));
|
|
479
394
|
}
|
|
480
395
|
catch (error) {
|
|
481
|
-
// istanbul ignore next
|
|
482
396
|
this.log.error(`Error reading diagnostic log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
483
|
-
// istanbul ignore next
|
|
484
397
|
res.status(500).send('Error reading diagnostic log file.');
|
|
485
398
|
}
|
|
486
399
|
});
|
|
487
|
-
// Endpoint to view the shelly log
|
|
488
400
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
489
401
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
490
402
|
try {
|
|
@@ -497,7 +409,6 @@ export class Frontend extends EventEmitter {
|
|
|
497
409
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
498
410
|
}
|
|
499
411
|
});
|
|
500
|
-
// Endpoint to download the matterbridge log
|
|
501
412
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
502
413
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
503
414
|
try {
|
|
@@ -511,14 +422,12 @@ export class Frontend extends EventEmitter {
|
|
|
511
422
|
}
|
|
512
423
|
res.type('text/plain');
|
|
513
424
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'matterbridge.log', (error) => {
|
|
514
|
-
/* istanbul ignore if */
|
|
515
425
|
if (error) {
|
|
516
426
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
517
427
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
518
428
|
}
|
|
519
429
|
});
|
|
520
430
|
});
|
|
521
|
-
// Endpoint to download the matter log
|
|
522
431
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
523
432
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
524
433
|
try {
|
|
@@ -532,14 +441,12 @@ export class Frontend extends EventEmitter {
|
|
|
532
441
|
}
|
|
533
442
|
res.type('text/plain');
|
|
534
443
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
|
|
535
|
-
/* istanbul ignore if */
|
|
536
444
|
if (error) {
|
|
537
445
|
this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
538
446
|
res.status(500).send('Error downloading the matter log file');
|
|
539
447
|
}
|
|
540
448
|
});
|
|
541
449
|
});
|
|
542
|
-
// Endpoint to download the shelly log
|
|
543
450
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
544
451
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
545
452
|
try {
|
|
@@ -553,90 +460,74 @@ export class Frontend extends EventEmitter {
|
|
|
553
460
|
}
|
|
554
461
|
res.type('text/plain');
|
|
555
462
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
556
|
-
/* istanbul ignore if */
|
|
557
463
|
if (error) {
|
|
558
464
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
559
465
|
res.status(500).send('Error downloading Shelly system log file');
|
|
560
466
|
}
|
|
561
467
|
});
|
|
562
468
|
});
|
|
563
|
-
// Endpoint to download the matterbridge storage directory
|
|
564
469
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
565
470
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
566
471
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
567
472
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), `matterbridge.${this.matterbridge.nodeStorageName}.zip`, (error) => {
|
|
568
|
-
/* istanbul ignore if */
|
|
569
473
|
if (error) {
|
|
570
474
|
this.log.error(`Error downloading file ${`matterbridge.${this.matterbridge.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
571
475
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
572
476
|
}
|
|
573
477
|
});
|
|
574
478
|
});
|
|
575
|
-
// Endpoint to download the matter storage file
|
|
576
479
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
577
480
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
578
481
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
579
482
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), `matterbridge.${this.matterbridge.matterStorageName}.zip`, (error) => {
|
|
580
|
-
/* istanbul ignore if */
|
|
581
483
|
if (error) {
|
|
582
484
|
this.log.error(`Error downloading the matter storage matterbridge.${this.matterbridge.matterStorageName}.zip: ${error instanceof Error ? error.message : error}`);
|
|
583
485
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
584
486
|
}
|
|
585
487
|
});
|
|
586
488
|
});
|
|
587
|
-
// Endpoint to download the matterbridge plugin directory
|
|
588
489
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
589
490
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
590
491
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
591
492
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
592
|
-
/* istanbul ignore if */
|
|
593
493
|
if (error) {
|
|
594
494
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
595
495
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
596
496
|
}
|
|
597
497
|
});
|
|
598
498
|
});
|
|
599
|
-
// Endpoint to download the matterbridge plugin config files
|
|
600
499
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
601
500
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
602
501
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
603
502
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
604
|
-
/* istanbul ignore if */
|
|
605
503
|
if (error) {
|
|
606
504
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
607
505
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
608
506
|
}
|
|
609
507
|
});
|
|
610
508
|
});
|
|
611
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
612
509
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
613
510
|
this.log.debug('The frontend sent /api/download-backup');
|
|
614
511
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
615
|
-
/* istanbul ignore if */
|
|
616
512
|
if (error) {
|
|
617
513
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
618
514
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
619
515
|
}
|
|
620
516
|
});
|
|
621
517
|
});
|
|
622
|
-
// Endpoint to upload a package
|
|
623
518
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
624
519
|
const { filename } = req.body;
|
|
625
520
|
const file = req.file;
|
|
626
|
-
/* istanbul ignore if */
|
|
627
521
|
if (!file || !filename) {
|
|
628
522
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
629
523
|
res.status(400).send('Invalid request: file and filename are required');
|
|
630
524
|
return;
|
|
631
525
|
}
|
|
632
526
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
633
|
-
// Define the path where the plugin file will be saved
|
|
634
527
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
635
528
|
try {
|
|
636
|
-
// Move the uploaded file to the specified path
|
|
637
529
|
await fs.rename(file.path, filePath);
|
|
638
530
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
639
|
-
// Install the plugin package
|
|
640
531
|
if (filename.endsWith('.tgz')) {
|
|
641
532
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
642
533
|
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], filename);
|
|
@@ -656,7 +547,6 @@ export class Frontend extends EventEmitter {
|
|
|
656
547
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
657
548
|
}
|
|
658
549
|
});
|
|
659
|
-
// Fallback for routing (must be the last route)
|
|
660
550
|
this.expressApp.use((req, res) => {
|
|
661
551
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
|
|
662
552
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -665,16 +555,13 @@ export class Frontend extends EventEmitter {
|
|
|
665
555
|
}
|
|
666
556
|
async stop() {
|
|
667
557
|
this.log.debug('Stopping the frontend...');
|
|
668
|
-
// Remove listeners from the express app
|
|
669
558
|
if (this.expressApp) {
|
|
670
559
|
this.expressApp.removeAllListeners();
|
|
671
560
|
this.expressApp = undefined;
|
|
672
561
|
this.log.debug('Frontend app closed successfully');
|
|
673
562
|
}
|
|
674
|
-
// Close the WebSocket server
|
|
675
563
|
if (this.webSocketServer) {
|
|
676
564
|
this.log.debug('Closing WebSocket server...');
|
|
677
|
-
// Close all active connections
|
|
678
565
|
this.webSocketServer.clients.forEach((client) => {
|
|
679
566
|
if (client.readyState === WebSocket.OPEN) {
|
|
680
567
|
client.close();
|
|
@@ -683,7 +570,6 @@ export class Frontend extends EventEmitter {
|
|
|
683
570
|
await withTimeout(new Promise((resolve) => {
|
|
684
571
|
this.webSocketServer?.close((error) => {
|
|
685
572
|
if (error) {
|
|
686
|
-
// istanbul ignore next
|
|
687
573
|
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
688
574
|
}
|
|
689
575
|
else {
|
|
@@ -696,27 +582,8 @@ export class Frontend extends EventEmitter {
|
|
|
696
582
|
this.webSocketServer.removeAllListeners();
|
|
697
583
|
this.webSocketServer = undefined;
|
|
698
584
|
}
|
|
699
|
-
// Close the http server
|
|
700
585
|
if (this.httpServer) {
|
|
701
586
|
this.log.debug('Closing http server...');
|
|
702
|
-
/*
|
|
703
|
-
await withTimeout(
|
|
704
|
-
new Promise<void>((resolve) => {
|
|
705
|
-
this.httpServer?.close((error) => {
|
|
706
|
-
if (error) {
|
|
707
|
-
// istanbul ignore next
|
|
708
|
-
this.log.error(`Error closing http server: ${error}`);
|
|
709
|
-
} else {
|
|
710
|
-
this.log.debug('Http server closed successfully');
|
|
711
|
-
this.emit('server_stopped');
|
|
712
|
-
}
|
|
713
|
-
resolve();
|
|
714
|
-
});
|
|
715
|
-
}),
|
|
716
|
-
5000,
|
|
717
|
-
false,
|
|
718
|
-
);
|
|
719
|
-
*/
|
|
720
587
|
this.httpServer.close();
|
|
721
588
|
this.log.debug('Http server closed successfully');
|
|
722
589
|
this.listening = false;
|
|
@@ -725,27 +592,8 @@ export class Frontend extends EventEmitter {
|
|
|
725
592
|
this.httpServer = undefined;
|
|
726
593
|
this.log.debug('Frontend http server closed successfully');
|
|
727
594
|
}
|
|
728
|
-
// Close the https server
|
|
729
595
|
if (this.httpsServer) {
|
|
730
596
|
this.log.debug('Closing https server...');
|
|
731
|
-
/*
|
|
732
|
-
await withTimeout(
|
|
733
|
-
new Promise<void>((resolve) => {
|
|
734
|
-
this.httpsServer?.close((error) => {
|
|
735
|
-
if (error) {
|
|
736
|
-
// istanbul ignore next
|
|
737
|
-
this.log.error(`Error closing https server: ${error}`);
|
|
738
|
-
} else {
|
|
739
|
-
this.log.debug('Https server closed successfully');
|
|
740
|
-
this.emit('server_stopped');
|
|
741
|
-
}
|
|
742
|
-
resolve();
|
|
743
|
-
});
|
|
744
|
-
}),
|
|
745
|
-
5000,
|
|
746
|
-
false,
|
|
747
|
-
);
|
|
748
|
-
*/
|
|
749
597
|
this.httpsServer.close();
|
|
750
598
|
this.log.debug('Https server closed successfully');
|
|
751
599
|
this.listening = false;
|
|
@@ -756,7 +604,6 @@ export class Frontend extends EventEmitter {
|
|
|
756
604
|
}
|
|
757
605
|
this.log.debug('Frontend stopped successfully');
|
|
758
606
|
}
|
|
759
|
-
// Function to format bytes to KB, MB, or GB
|
|
760
607
|
formatMemoryUsage = (bytes) => {
|
|
761
608
|
if (bytes >= 1024 ** 3) {
|
|
762
609
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -768,7 +615,6 @@ export class Frontend extends EventEmitter {
|
|
|
768
615
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
769
616
|
}
|
|
770
617
|
};
|
|
771
|
-
// Function to format system uptime with only the most significant unit
|
|
772
618
|
formatOsUpTime = (seconds) => {
|
|
773
619
|
if (seconds >= 86400) {
|
|
774
620
|
const days = Math.floor(seconds / 86400);
|
|
@@ -784,13 +630,7 @@ export class Frontend extends EventEmitter {
|
|
|
784
630
|
}
|
|
785
631
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
786
632
|
};
|
|
787
|
-
/**
|
|
788
|
-
* Retrieves the api settings data.
|
|
789
|
-
*
|
|
790
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
791
|
-
*/
|
|
792
633
|
async getApiSettings() {
|
|
793
|
-
// Update the system information
|
|
794
634
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
795
635
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
796
636
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -799,12 +639,11 @@ export class Frontend extends EventEmitter {
|
|
|
799
639
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
800
640
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
801
641
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
802
|
-
// Update the matterbridge information
|
|
803
642
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
804
643
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
805
644
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
806
645
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
807
|
-
this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.
|
|
646
|
+
this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.level;
|
|
808
647
|
this.matterbridge.matterbridgeInformation.matterMdnsInterface = this.matterbridge.mdnsInterface;
|
|
809
648
|
this.matterbridge.matterbridgeInformation.matterIpv4Address = this.matterbridge.ipv4address;
|
|
810
649
|
this.matterbridge.matterbridgeInformation.matterIpv6Address = this.matterbridge.ipv6address;
|
|
@@ -813,12 +652,6 @@ export class Frontend extends EventEmitter {
|
|
|
813
652
|
this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
|
|
814
653
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
815
654
|
}
|
|
816
|
-
/**
|
|
817
|
-
* Retrieves the reachable attribute.
|
|
818
|
-
*
|
|
819
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
820
|
-
* @returns {boolean} The reachable attribute.
|
|
821
|
-
*/
|
|
822
655
|
getReachability(device) {
|
|
823
656
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
824
657
|
return false;
|
|
@@ -830,12 +663,6 @@ export class Frontend extends EventEmitter {
|
|
|
830
663
|
return true;
|
|
831
664
|
return false;
|
|
832
665
|
}
|
|
833
|
-
/**
|
|
834
|
-
* Retrieves the power source attribute.
|
|
835
|
-
*
|
|
836
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
837
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
838
|
-
*/
|
|
839
666
|
getPowerSource(endpoint) {
|
|
840
667
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
841
668
|
return undefined;
|
|
@@ -851,22 +678,13 @@ export class Frontend extends EventEmitter {
|
|
|
851
678
|
}
|
|
852
679
|
return;
|
|
853
680
|
};
|
|
854
|
-
// Root endpoint
|
|
855
681
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
856
682
|
return powerSource(endpoint);
|
|
857
|
-
// Child endpoints
|
|
858
683
|
for (const child of endpoint.getChildEndpoints()) {
|
|
859
684
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
860
685
|
return powerSource(child);
|
|
861
686
|
}
|
|
862
687
|
}
|
|
863
|
-
/**
|
|
864
|
-
* Retrieves the cluster text description from a given device.
|
|
865
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
866
|
-
*
|
|
867
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
868
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
869
|
-
*/
|
|
870
688
|
getClusterTextFromDevice(device) {
|
|
871
689
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
872
690
|
return '';
|
|
@@ -877,7 +695,6 @@ export class Frontend extends EventEmitter {
|
|
|
877
695
|
if (composed)
|
|
878
696
|
return 'Composed: ' + composed.value;
|
|
879
697
|
}
|
|
880
|
-
// istanbul ignore next cause is not reachable
|
|
881
698
|
return '';
|
|
882
699
|
};
|
|
883
700
|
const getFixedLabel = (device) => {
|
|
@@ -887,13 +704,11 @@ export class Frontend extends EventEmitter {
|
|
|
887
704
|
if (composed)
|
|
888
705
|
return 'Composed: ' + composed.value;
|
|
889
706
|
}
|
|
890
|
-
// istanbul ignore next cause is not reacheable
|
|
891
707
|
return '';
|
|
892
708
|
};
|
|
893
709
|
let attributes = '';
|
|
894
710
|
let supportedModes = [];
|
|
895
711
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
896
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
897
712
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
898
713
|
return;
|
|
899
714
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -983,17 +798,11 @@ export class Frontend extends EventEmitter {
|
|
|
983
798
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
984
799
|
attributes += `${getUserLabel(device)} `;
|
|
985
800
|
});
|
|
986
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
987
801
|
return attributes.trimStart().trimEnd();
|
|
988
802
|
}
|
|
989
|
-
/**
|
|
990
|
-
* Retrieves the registered plugins sanitized for res.json().
|
|
991
|
-
*
|
|
992
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
993
|
-
*/
|
|
994
803
|
getPlugins() {
|
|
995
804
|
if (this.matterbridge.hasCleanupStarted)
|
|
996
|
-
return [];
|
|
805
|
+
return [];
|
|
997
806
|
const baseRegisteredPlugins = [];
|
|
998
807
|
for (const plugin of this.matterbridge.plugins) {
|
|
999
808
|
baseRegisteredPlugins.push({
|
|
@@ -1021,27 +830,18 @@ export class Frontend extends EventEmitter {
|
|
|
1021
830
|
schemaJson: plugin.schemaJson,
|
|
1022
831
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1023
832
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1024
|
-
// Childbridge mode specific data
|
|
1025
833
|
matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
|
|
1026
834
|
});
|
|
1027
835
|
}
|
|
1028
836
|
return baseRegisteredPlugins;
|
|
1029
837
|
}
|
|
1030
|
-
/**
|
|
1031
|
-
* Retrieves the devices from Matterbridge.
|
|
1032
|
-
*
|
|
1033
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1034
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
|
|
1035
|
-
*/
|
|
1036
838
|
async getDevices(pluginName) {
|
|
1037
839
|
if (this.matterbridge.hasCleanupStarted)
|
|
1038
|
-
return [];
|
|
840
|
+
return [];
|
|
1039
841
|
const devices = [];
|
|
1040
842
|
for (const device of this.matterbridge.devices.array()) {
|
|
1041
|
-
// Filter by pluginName if provided
|
|
1042
843
|
if (pluginName && pluginName !== device.plugin)
|
|
1043
844
|
continue;
|
|
1044
|
-
// Check if the device has the required properties
|
|
1045
845
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1046
846
|
continue;
|
|
1047
847
|
devices.push({
|
|
@@ -1061,37 +861,24 @@ export class Frontend extends EventEmitter {
|
|
|
1061
861
|
}
|
|
1062
862
|
return devices;
|
|
1063
863
|
}
|
|
1064
|
-
/**
|
|
1065
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1066
|
-
*
|
|
1067
|
-
* Response for /api/clusters
|
|
1068
|
-
*
|
|
1069
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1070
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1071
|
-
* @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1072
|
-
*/
|
|
1073
864
|
getClusters(pluginName, endpointNumber) {
|
|
865
|
+
if (this.matterbridge.hasCleanupStarted)
|
|
866
|
+
return;
|
|
1074
867
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1075
868
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.maybeId || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1076
869
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1077
870
|
return;
|
|
1078
871
|
}
|
|
1079
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1080
|
-
// Get the device types from the main endpoint
|
|
1081
872
|
const deviceTypes = [];
|
|
1082
873
|
const clusters = [];
|
|
1083
874
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1084
875
|
deviceTypes.push(d.deviceType);
|
|
1085
876
|
});
|
|
1086
|
-
// Get the clusters from the main endpoint
|
|
1087
877
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1088
878
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1089
879
|
return;
|
|
1090
880
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1091
881
|
return;
|
|
1092
|
-
// console.log(
|
|
1093
|
-
// `${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}`,
|
|
1094
|
-
// );
|
|
1095
882
|
clusters.push({
|
|
1096
883
|
endpoint: endpoint.number.toString(),
|
|
1097
884
|
number: endpoint.number,
|
|
@@ -1105,19 +892,12 @@ export class Frontend extends EventEmitter {
|
|
|
1105
892
|
attributeLocalValue: attributeValue,
|
|
1106
893
|
});
|
|
1107
894
|
});
|
|
1108
|
-
// Get the child endpoints
|
|
1109
895
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1110
|
-
// if (childEndpoints.length === 0) {
|
|
1111
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1112
|
-
// }
|
|
1113
896
|
childEndpoints.forEach((childEndpoint) => {
|
|
1114
|
-
// istanbul ignore if cause is not reachable: should never happen but ...
|
|
1115
897
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1116
898
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1117
899
|
return;
|
|
1118
900
|
}
|
|
1119
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1120
|
-
// Get the device types of the child endpoint
|
|
1121
901
|
const deviceTypes = [];
|
|
1122
902
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1123
903
|
deviceTypes.push(d.deviceType);
|
|
@@ -1127,9 +907,6 @@ export class Frontend extends EventEmitter {
|
|
|
1127
907
|
return;
|
|
1128
908
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1129
909
|
return;
|
|
1130
|
-
// console.log(
|
|
1131
|
-
// `${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}`,
|
|
1132
|
-
// );
|
|
1133
910
|
clusters.push({
|
|
1134
911
|
endpoint: childEndpoint.number.toString(),
|
|
1135
912
|
number: childEndpoint.number,
|
|
@@ -1146,13 +923,6 @@ export class Frontend extends EventEmitter {
|
|
|
1146
923
|
});
|
|
1147
924
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, number: endpoint.number, id: endpoint.id, deviceTypes, clusters };
|
|
1148
925
|
}
|
|
1149
|
-
/**
|
|
1150
|
-
* Handles incoming websocket api request messages from the Matterbridge frontend.
|
|
1151
|
-
*
|
|
1152
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1153
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1154
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1155
|
-
*/
|
|
1156
926
|
async wsMessageHandler(client, message) {
|
|
1157
927
|
let data;
|
|
1158
928
|
const sendResponse = (data) => {
|
|
@@ -1172,7 +942,7 @@ export class Frontend extends EventEmitter {
|
|
|
1172
942
|
};
|
|
1173
943
|
try {
|
|
1174
944
|
data = JSON.parse(message.toString());
|
|
1175
|
-
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method)
|
|
945
|
+
if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || data.dst !== 'Matterbridge') {
|
|
1176
946
|
this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
|
|
1177
947
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
|
|
1178
948
|
return;
|
|
@@ -1215,48 +985,36 @@ export class Frontend extends EventEmitter {
|
|
|
1215
985
|
this.wssSendSnackbarMessage(`Installed package ${localData.params.packageName}`, 5, 'success');
|
|
1216
986
|
const packageName = localData.params.packageName.replace(/@.*$/, '');
|
|
1217
987
|
if (localData.params.restart === false && packageName !== 'matterbridge') {
|
|
1218
|
-
// The install comes from InstallPlugins
|
|
1219
988
|
this.matterbridge.plugins
|
|
1220
989
|
.add(packageName)
|
|
1221
990
|
.then((plugin) => {
|
|
1222
|
-
// istanbul ignore next if
|
|
1223
991
|
if (plugin) {
|
|
1224
|
-
// The plugin is not registered
|
|
1225
992
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1226
|
-
// In childbridge mode the plugins server node is not started when added
|
|
1227
993
|
if (this.matterbridge.bridgeMode === 'childbridge')
|
|
1228
994
|
this.wssSendRestartRequired(true, true);
|
|
1229
995
|
this.matterbridge.plugins
|
|
1230
996
|
.load(plugin, true, 'The plugin has been added', true)
|
|
1231
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1232
997
|
.then(() => {
|
|
1233
998
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
1234
999
|
this.wssSendRefreshRequired('plugins');
|
|
1000
|
+
this.wssSendRefreshRequired('devices');
|
|
1235
1001
|
return;
|
|
1236
1002
|
})
|
|
1237
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1238
1003
|
.catch((_error) => {
|
|
1239
|
-
//
|
|
1240
1004
|
});
|
|
1241
1005
|
}
|
|
1242
1006
|
else {
|
|
1243
|
-
// The plugin is already registered
|
|
1244
1007
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1245
1008
|
this.wssSendRefreshRequired('plugins');
|
|
1246
1009
|
this.wssSendRestartRequired(true, true);
|
|
1247
1010
|
}
|
|
1248
1011
|
return;
|
|
1249
1012
|
})
|
|
1250
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1251
1013
|
.catch((_error) => {
|
|
1252
|
-
//
|
|
1253
1014
|
});
|
|
1254
1015
|
}
|
|
1255
1016
|
else {
|
|
1256
|
-
// The package is matterbridge
|
|
1257
|
-
// istanbul ignore next
|
|
1258
1017
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1259
|
-
// istanbul ignore next if
|
|
1260
1018
|
if (this.matterbridge.restartMode !== '') {
|
|
1261
1019
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1262
1020
|
this.matterbridge.shutdownProcess();
|
|
@@ -1279,9 +1037,7 @@ export class Frontend extends EventEmitter {
|
|
|
1279
1037
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' });
|
|
1280
1038
|
return;
|
|
1281
1039
|
}
|
|
1282
|
-
// The package is a plugin
|
|
1283
1040
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1284
|
-
// istanbul ignore next if
|
|
1285
1041
|
if (plugin) {
|
|
1286
1042
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
1287
1043
|
await this.matterbridge.plugins.remove(data.params.packageName);
|
|
@@ -1289,7 +1045,6 @@ export class Frontend extends EventEmitter {
|
|
|
1289
1045
|
this.wssSendRefreshRequired('plugins');
|
|
1290
1046
|
this.wssSendRefreshRequired('devices');
|
|
1291
1047
|
}
|
|
1292
|
-
// Uninstall the package
|
|
1293
1048
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1294
1049
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
1295
1050
|
spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'], data.params.packageName)
|
|
@@ -1332,7 +1087,6 @@ export class Frontend extends EventEmitter {
|
|
|
1332
1087
|
return;
|
|
1333
1088
|
})
|
|
1334
1089
|
.catch((_error) => {
|
|
1335
|
-
//
|
|
1336
1090
|
});
|
|
1337
1091
|
}
|
|
1338
1092
|
else {
|
|
@@ -1380,7 +1134,6 @@ export class Frontend extends EventEmitter {
|
|
|
1380
1134
|
return;
|
|
1381
1135
|
})
|
|
1382
1136
|
.catch((_error) => {
|
|
1383
|
-
//
|
|
1384
1137
|
});
|
|
1385
1138
|
}
|
|
1386
1139
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1406,7 +1159,6 @@ export class Frontend extends EventEmitter {
|
|
|
1406
1159
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1407
1160
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
|
|
1408
1161
|
if (plugin.serverNode) {
|
|
1409
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1410
1162
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1411
1163
|
plugin.serverNode = undefined;
|
|
1412
1164
|
}
|
|
@@ -1416,20 +1168,18 @@ export class Frontend extends EventEmitter {
|
|
|
1416
1168
|
this.matterbridge.devices.remove(device);
|
|
1417
1169
|
}
|
|
1418
1170
|
}
|
|
1419
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1420
1171
|
if (plugin.type === 'DynamicPlatform' && !plugin.locked)
|
|
1421
1172
|
await this.matterbridge.createDynamicPlugin(plugin);
|
|
1422
1173
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
|
|
1423
|
-
plugin.restartRequired = false;
|
|
1174
|
+
plugin.restartRequired = false;
|
|
1424
1175
|
let needRestart = 0;
|
|
1425
1176
|
for (const plugin of this.matterbridge.plugins) {
|
|
1426
1177
|
if (plugin.restartRequired)
|
|
1427
1178
|
needRestart++;
|
|
1428
1179
|
}
|
|
1429
1180
|
if (needRestart === 0) {
|
|
1430
|
-
this.wssSendRestartNotRequired(true);
|
|
1181
|
+
this.wssSendRestartNotRequired(true);
|
|
1431
1182
|
}
|
|
1432
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1433
1183
|
if (plugin.serverNode)
|
|
1434
1184
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1435
1185
|
this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
|
|
@@ -1685,22 +1435,22 @@ export class Frontend extends EventEmitter {
|
|
|
1685
1435
|
if (isValidString(data.params.value, 4)) {
|
|
1686
1436
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1687
1437
|
if (data.params.value === 'Debug') {
|
|
1688
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1438
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1689
1439
|
}
|
|
1690
1440
|
else if (data.params.value === 'Info') {
|
|
1691
|
-
await this.matterbridge.setLogLevel("info"
|
|
1441
|
+
await this.matterbridge.setLogLevel("info");
|
|
1692
1442
|
}
|
|
1693
1443
|
else if (data.params.value === 'Notice') {
|
|
1694
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1444
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1695
1445
|
}
|
|
1696
1446
|
else if (data.params.value === 'Warn') {
|
|
1697
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1447
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1698
1448
|
}
|
|
1699
1449
|
else if (data.params.value === 'Error') {
|
|
1700
|
-
await this.matterbridge.setLogLevel("error"
|
|
1450
|
+
await this.matterbridge.setLogLevel("error");
|
|
1701
1451
|
}
|
|
1702
1452
|
else if (data.params.value === 'Fatal') {
|
|
1703
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1453
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1704
1454
|
}
|
|
1705
1455
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1706
1456
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1711,7 +1461,6 @@ export class Frontend extends EventEmitter {
|
|
|
1711
1461
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1712
1462
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1713
1463
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1714
|
-
// Create the file logger for matterbridge
|
|
1715
1464
|
if (data.params.value)
|
|
1716
1465
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1717
1466
|
else
|
|
@@ -1790,7 +1539,6 @@ export class Frontend extends EventEmitter {
|
|
|
1790
1539
|
}
|
|
1791
1540
|
break;
|
|
1792
1541
|
case 'setmatterport':
|
|
1793
|
-
// eslint-disable-next-line no-case-declarations
|
|
1794
1542
|
const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1795
1543
|
if (isValidNumber(port, 5540, 5600)) {
|
|
1796
1544
|
this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
|
|
@@ -1808,7 +1556,6 @@ export class Frontend extends EventEmitter {
|
|
|
1808
1556
|
}
|
|
1809
1557
|
break;
|
|
1810
1558
|
case 'setmatterdiscriminator':
|
|
1811
|
-
// eslint-disable-next-line no-case-declarations
|
|
1812
1559
|
const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1813
1560
|
if (isValidNumber(discriminator, 0, 4095)) {
|
|
1814
1561
|
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
|
|
@@ -1826,7 +1573,6 @@ export class Frontend extends EventEmitter {
|
|
|
1826
1573
|
}
|
|
1827
1574
|
break;
|
|
1828
1575
|
case 'setmatterpasscode':
|
|
1829
|
-
// eslint-disable-next-line no-case-declarations
|
|
1830
1576
|
const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1831
1577
|
if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
|
|
1832
1578
|
this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
|
|
@@ -1870,19 +1616,15 @@ export class Frontend extends EventEmitter {
|
|
|
1870
1616
|
return;
|
|
1871
1617
|
}
|
|
1872
1618
|
const config = plugin.configJson;
|
|
1873
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1874
1619
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1875
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1876
1620
|
if (select === 'serial')
|
|
1877
1621
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1878
1622
|
if (select === 'name')
|
|
1879
1623
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1880
1624
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1881
|
-
// Remove postfix from the serial if it exists
|
|
1882
1625
|
if (config.postfix) {
|
|
1883
1626
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1884
1627
|
}
|
|
1885
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1886
1628
|
if (isValidArray(config.whiteList, 1)) {
|
|
1887
1629
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1888
1630
|
config.whiteList.push(data.params.serial);
|
|
@@ -1891,7 +1633,6 @@ export class Frontend extends EventEmitter {
|
|
|
1891
1633
|
config.whiteList.push(data.params.name);
|
|
1892
1634
|
}
|
|
1893
1635
|
}
|
|
1894
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1895
1636
|
if (isValidArray(config.blackList, 1)) {
|
|
1896
1637
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1897
1638
|
config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
|
|
@@ -1919,9 +1660,7 @@ export class Frontend extends EventEmitter {
|
|
|
1919
1660
|
return;
|
|
1920
1661
|
}
|
|
1921
1662
|
const config = plugin.configJson;
|
|
1922
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1923
1663
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1924
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1925
1664
|
if (select === 'serial')
|
|
1926
1665
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1927
1666
|
if (select === 'name')
|
|
@@ -1930,7 +1669,6 @@ export class Frontend extends EventEmitter {
|
|
|
1930
1669
|
if (config.postfix) {
|
|
1931
1670
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1932
1671
|
}
|
|
1933
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1934
1672
|
if (isValidArray(config.whiteList, 1)) {
|
|
1935
1673
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1936
1674
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
|
|
@@ -1939,7 +1677,6 @@ export class Frontend extends EventEmitter {
|
|
|
1939
1677
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
|
|
1940
1678
|
}
|
|
1941
1679
|
}
|
|
1942
|
-
// Add the serial to the blackList
|
|
1943
1680
|
if (isValidArray(config.blackList)) {
|
|
1944
1681
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1945
1682
|
config.blackList.push(data.params.serial);
|
|
@@ -1962,7 +1699,6 @@ export class Frontend extends EventEmitter {
|
|
|
1962
1699
|
}
|
|
1963
1700
|
}
|
|
1964
1701
|
else {
|
|
1965
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1966
1702
|
const localData = data;
|
|
1967
1703
|
this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
|
|
1968
1704
|
sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
|
|
@@ -1972,44 +1708,21 @@ export class Frontend extends EventEmitter {
|
|
|
1972
1708
|
inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
|
|
1973
1709
|
}
|
|
1974
1710
|
}
|
|
1975
|
-
/**
|
|
1976
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1977
|
-
*
|
|
1978
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1979
|
-
* @param {string} time - The time string of the message
|
|
1980
|
-
* @param {string} name - The logger name of the message
|
|
1981
|
-
* @param {string} message - The content of the message.
|
|
1982
|
-
*
|
|
1983
|
-
* @remarks
|
|
1984
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
1985
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
1986
|
-
* The function sends the message to all connected clients.
|
|
1987
|
-
*/
|
|
1988
1711
|
wssSendLogMessage(level, time, name, message) {
|
|
1989
1712
|
if (!level || !time || !name || !message)
|
|
1990
1713
|
return;
|
|
1991
|
-
// Remove ANSI escape codes from the message
|
|
1992
|
-
// eslint-disable-next-line no-control-regex
|
|
1993
1714
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1994
|
-
// Remove leading asterisks from the message
|
|
1995
1715
|
message = message.replace(/^\*+/, '');
|
|
1996
|
-
// Replace all occurrences of \t and \n
|
|
1997
1716
|
message = message.replace(/[\t\n]/g, '');
|
|
1998
|
-
// Remove non-printable characters
|
|
1999
|
-
// eslint-disable-next-line no-control-regex
|
|
2000
1717
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
2001
|
-
// Replace all occurrences of \" with "
|
|
2002
1718
|
message = message.replace(/\\"/g, '"');
|
|
2003
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
2004
1719
|
const maxContinuousLength = 100;
|
|
2005
1720
|
const keepStartLength = 20;
|
|
2006
1721
|
const keepEndLength = 20;
|
|
2007
|
-
// Split the message into words
|
|
2008
1722
|
if (level !== 'spawn') {
|
|
2009
1723
|
message = message
|
|
2010
1724
|
.split(' ')
|
|
2011
1725
|
.map((word) => {
|
|
2012
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2013
1726
|
if (word.length > maxContinuousLength) {
|
|
2014
1727
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2015
1728
|
}
|
|
@@ -2017,161 +1730,60 @@ export class Frontend extends EventEmitter {
|
|
|
2017
1730
|
})
|
|
2018
1731
|
.join(' ');
|
|
2019
1732
|
}
|
|
2020
|
-
// Send the message to all connected clients
|
|
2021
1733
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
|
|
2022
1734
|
}
|
|
2023
|
-
/**
|
|
2024
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2025
|
-
*
|
|
2026
|
-
* @param {string} changed - The changed value.
|
|
2027
|
-
* @param {Record<string, unknown>} params - Additional parameters to send with the message.
|
|
2028
|
-
* possible values for changed:
|
|
2029
|
-
* - 'settings'
|
|
2030
|
-
* - 'plugins'
|
|
2031
|
-
* - 'devices'
|
|
2032
|
-
* - 'matter' with param 'matter' (QRDiv component)
|
|
2033
|
-
* @param {ApiMatterResponse} params.matter - The matter device that has changed. Required if changed is 'matter'.
|
|
2034
|
-
*/
|
|
2035
1735
|
wssSendRefreshRequired(changed, params) {
|
|
2036
1736
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
2037
|
-
// Send the message to all connected clients
|
|
2038
1737
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
|
|
2039
1738
|
}
|
|
2040
|
-
/**
|
|
2041
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2042
|
-
*
|
|
2043
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
|
|
2044
|
-
* @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
|
|
2045
|
-
*/
|
|
2046
1739
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
2047
1740
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
2048
1741
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
2049
1742
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
|
|
2050
1743
|
if (snackbar === true)
|
|
2051
1744
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
2052
|
-
// Send the message to all connected clients
|
|
2053
1745
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
|
|
2054
1746
|
}
|
|
2055
|
-
/**
|
|
2056
|
-
* Sends a no need to restart WebSocket message to all connected clients.
|
|
2057
|
-
*
|
|
2058
|
-
* @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients. Default is true.
|
|
2059
|
-
*/
|
|
2060
1747
|
wssSendRestartNotRequired(snackbar = true) {
|
|
2061
1748
|
this.log.debug('Sending a restart not required message to all connected clients');
|
|
2062
1749
|
this.matterbridge.matterbridgeInformation.restartRequired = false;
|
|
2063
1750
|
if (snackbar === true)
|
|
2064
1751
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2065
|
-
// Send the message to all connected clients
|
|
2066
1752
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
|
|
2067
1753
|
}
|
|
2068
|
-
/**
|
|
2069
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
2070
|
-
*
|
|
2071
|
-
* @param {boolean} devVersion - If true, the update is for a development version. Default is false.
|
|
2072
|
-
*/
|
|
2073
1754
|
wssSendUpdateRequired(devVersion = false) {
|
|
2074
1755
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2075
1756
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
2076
|
-
// Send the message to all connected clients
|
|
2077
1757
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
|
|
2078
1758
|
}
|
|
2079
|
-
/**
|
|
2080
|
-
* Sends a cpu update message to all connected clients.
|
|
2081
|
-
*
|
|
2082
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2083
|
-
*/
|
|
2084
1759
|
wssSendCpuUpdate(cpuUsage) {
|
|
2085
1760
|
if (hasParameter('debug'))
|
|
2086
1761
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2087
|
-
// Send the message to all connected clients
|
|
2088
1762
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', success: true, response: { cpuUsage: Math.round(cpuUsage * 100) / 100 } });
|
|
2089
1763
|
}
|
|
2090
|
-
/**
|
|
2091
|
-
* Sends a memory update message to all connected clients.
|
|
2092
|
-
*
|
|
2093
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2094
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2095
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2096
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2097
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2098
|
-
* @param {string} external - The external memory in bytes.
|
|
2099
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2100
|
-
*/
|
|
2101
1764
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2102
1765
|
if (hasParameter('debug'))
|
|
2103
1766
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2104
|
-
// Send the message to all connected clients
|
|
2105
1767
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', success: true, response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
|
|
2106
1768
|
}
|
|
2107
|
-
/**
|
|
2108
|
-
* Sends an uptime update message to all connected clients.
|
|
2109
|
-
*
|
|
2110
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2111
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2112
|
-
*/
|
|
2113
1769
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2114
1770
|
if (hasParameter('debug'))
|
|
2115
1771
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2116
|
-
// Send the message to all connected clients
|
|
2117
1772
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
|
|
2118
1773
|
}
|
|
2119
|
-
/**
|
|
2120
|
-
* Sends an open snackbar message to all connected clients.
|
|
2121
|
-
*
|
|
2122
|
-
* @param {string} message - The message to send.
|
|
2123
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message. Default is 5 seconds.
|
|
2124
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the message.
|
|
2125
|
-
* possible values are: 'info', 'warning', 'error', 'success'. Default is 'info'.
|
|
2126
|
-
*
|
|
2127
|
-
* @remarks
|
|
2128
|
-
* If timeout is 0, the snackbar message will be displayed until closed by the user.
|
|
2129
|
-
*/
|
|
2130
1774
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2131
1775
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2132
|
-
// Send the message to all connected clients
|
|
2133
1776
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
|
|
2134
1777
|
}
|
|
2135
|
-
/**
|
|
2136
|
-
* Sends a close snackbar message to all connected clients.
|
|
2137
|
-
* It will close the snackbar message with the same message and timeout = 0.
|
|
2138
|
-
*
|
|
2139
|
-
* @param {string} message - The message to send.
|
|
2140
|
-
*/
|
|
2141
1778
|
wssSendCloseSnackbarMessage(message) {
|
|
2142
1779
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2143
|
-
// Send the message to all connected clients
|
|
2144
1780
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
|
|
2145
1781
|
}
|
|
2146
|
-
/**
|
|
2147
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2148
|
-
*
|
|
2149
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2150
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2151
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2152
|
-
* @param {EndpointNumber} number - The endpoint number where the attribute belongs.
|
|
2153
|
-
* @param {string} id - The endpoint id where the attribute belongs.
|
|
2154
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2155
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2156
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2157
|
-
*
|
|
2158
|
-
* @remarks
|
|
2159
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2160
|
-
* with the updated attribute information.
|
|
2161
|
-
*/
|
|
2162
1782
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
|
|
2163
1783
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2164
|
-
// Send the message to all connected clients
|
|
2165
1784
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', success: true, response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value } });
|
|
2166
1785
|
}
|
|
2167
|
-
/**
|
|
2168
|
-
* Sends a message to all connected clients.
|
|
2169
|
-
* This is an helper function to send a broadcast message to all connected clients.
|
|
2170
|
-
*
|
|
2171
|
-
* @param {WsMessageBroadcast} msg - The message to send.
|
|
2172
|
-
*/
|
|
2173
1786
|
wssBroadcastMessage(msg) {
|
|
2174
|
-
// Send the message to all connected clients
|
|
2175
1787
|
const stringifiedMsg = JSON.stringify(msg);
|
|
2176
1788
|
if (msg.method !== 'log')
|
|
2177
1789
|
this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
|
|
@@ -2182,4 +1794,3 @@ export class Frontend extends EventEmitter {
|
|
|
2182
1794
|
});
|
|
2183
1795
|
}
|
|
2184
1796
|
}
|
|
2185
|
-
//# sourceMappingURL=frontend.js.map
|