matterbridge 3.2.9 → 3.2.10-dev-20250928-30c21de
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 +18 -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 +27 -417
- package/dist/frontendTypes.js +0 -45
- 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 +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 +0 -304
- 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_node_modules.js +19 -19
- package/frontend/package-lock.json +115 -100
- 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,7 +639,6 @@ 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;
|
|
@@ -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,35 @@ 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');
|
|
1235
1000
|
return;
|
|
1236
1001
|
})
|
|
1237
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1238
1002
|
.catch((_error) => {
|
|
1239
|
-
//
|
|
1240
1003
|
});
|
|
1241
1004
|
}
|
|
1242
1005
|
else {
|
|
1243
|
-
// The plugin is already registered
|
|
1244
1006
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1245
1007
|
this.wssSendRefreshRequired('plugins');
|
|
1246
1008
|
this.wssSendRestartRequired(true, true);
|
|
1247
1009
|
}
|
|
1248
1010
|
return;
|
|
1249
1011
|
})
|
|
1250
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1251
1012
|
.catch((_error) => {
|
|
1252
|
-
//
|
|
1253
1013
|
});
|
|
1254
1014
|
}
|
|
1255
1015
|
else {
|
|
1256
|
-
// The package is matterbridge
|
|
1257
|
-
// istanbul ignore next
|
|
1258
1016
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1259
|
-
// istanbul ignore next if
|
|
1260
1017
|
if (this.matterbridge.restartMode !== '') {
|
|
1261
1018
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1262
1019
|
this.matterbridge.shutdownProcess();
|
|
@@ -1279,9 +1036,7 @@ export class Frontend extends EventEmitter {
|
|
|
1279
1036
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' });
|
|
1280
1037
|
return;
|
|
1281
1038
|
}
|
|
1282
|
-
// The package is a plugin
|
|
1283
1039
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1284
|
-
// istanbul ignore next if
|
|
1285
1040
|
if (plugin) {
|
|
1286
1041
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
1287
1042
|
await this.matterbridge.plugins.remove(data.params.packageName);
|
|
@@ -1289,7 +1044,6 @@ export class Frontend extends EventEmitter {
|
|
|
1289
1044
|
this.wssSendRefreshRequired('plugins');
|
|
1290
1045
|
this.wssSendRefreshRequired('devices');
|
|
1291
1046
|
}
|
|
1292
|
-
// Uninstall the package
|
|
1293
1047
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1294
1048
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
1295
1049
|
spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'], data.params.packageName)
|
|
@@ -1332,7 +1086,6 @@ export class Frontend extends EventEmitter {
|
|
|
1332
1086
|
return;
|
|
1333
1087
|
})
|
|
1334
1088
|
.catch((_error) => {
|
|
1335
|
-
//
|
|
1336
1089
|
});
|
|
1337
1090
|
}
|
|
1338
1091
|
else {
|
|
@@ -1380,7 +1133,6 @@ export class Frontend extends EventEmitter {
|
|
|
1380
1133
|
return;
|
|
1381
1134
|
})
|
|
1382
1135
|
.catch((_error) => {
|
|
1383
|
-
//
|
|
1384
1136
|
});
|
|
1385
1137
|
}
|
|
1386
1138
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1406,7 +1158,6 @@ export class Frontend extends EventEmitter {
|
|
|
1406
1158
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1407
1159
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
|
|
1408
1160
|
if (plugin.serverNode) {
|
|
1409
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1410
1161
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1411
1162
|
plugin.serverNode = undefined;
|
|
1412
1163
|
}
|
|
@@ -1416,20 +1167,18 @@ export class Frontend extends EventEmitter {
|
|
|
1416
1167
|
this.matterbridge.devices.remove(device);
|
|
1417
1168
|
}
|
|
1418
1169
|
}
|
|
1419
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1420
1170
|
if (plugin.type === 'DynamicPlatform' && !plugin.locked)
|
|
1421
1171
|
await this.matterbridge.createDynamicPlugin(plugin);
|
|
1422
1172
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
|
|
1423
|
-
plugin.restartRequired = false;
|
|
1173
|
+
plugin.restartRequired = false;
|
|
1424
1174
|
let needRestart = 0;
|
|
1425
1175
|
for (const plugin of this.matterbridge.plugins) {
|
|
1426
1176
|
if (plugin.restartRequired)
|
|
1427
1177
|
needRestart++;
|
|
1428
1178
|
}
|
|
1429
1179
|
if (needRestart === 0) {
|
|
1430
|
-
this.wssSendRestartNotRequired(true);
|
|
1180
|
+
this.wssSendRestartNotRequired(true);
|
|
1431
1181
|
}
|
|
1432
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1433
1182
|
if (plugin.serverNode)
|
|
1434
1183
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1435
1184
|
this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
|
|
@@ -1685,22 +1434,22 @@ export class Frontend extends EventEmitter {
|
|
|
1685
1434
|
if (isValidString(data.params.value, 4)) {
|
|
1686
1435
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1687
1436
|
if (data.params.value === 'Debug') {
|
|
1688
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1437
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1689
1438
|
}
|
|
1690
1439
|
else if (data.params.value === 'Info') {
|
|
1691
|
-
await this.matterbridge.setLogLevel("info"
|
|
1440
|
+
await this.matterbridge.setLogLevel("info");
|
|
1692
1441
|
}
|
|
1693
1442
|
else if (data.params.value === 'Notice') {
|
|
1694
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1443
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1695
1444
|
}
|
|
1696
1445
|
else if (data.params.value === 'Warn') {
|
|
1697
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1446
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1698
1447
|
}
|
|
1699
1448
|
else if (data.params.value === 'Error') {
|
|
1700
|
-
await this.matterbridge.setLogLevel("error"
|
|
1449
|
+
await this.matterbridge.setLogLevel("error");
|
|
1701
1450
|
}
|
|
1702
1451
|
else if (data.params.value === 'Fatal') {
|
|
1703
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1452
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1704
1453
|
}
|
|
1705
1454
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1706
1455
|
sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
|
|
@@ -1711,7 +1460,6 @@ export class Frontend extends EventEmitter {
|
|
|
1711
1460
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1712
1461
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1713
1462
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1714
|
-
// Create the file logger for matterbridge
|
|
1715
1463
|
if (data.params.value)
|
|
1716
1464
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1717
1465
|
else
|
|
@@ -1790,7 +1538,6 @@ export class Frontend extends EventEmitter {
|
|
|
1790
1538
|
}
|
|
1791
1539
|
break;
|
|
1792
1540
|
case 'setmatterport':
|
|
1793
|
-
// eslint-disable-next-line no-case-declarations
|
|
1794
1541
|
const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1795
1542
|
if (isValidNumber(port, 5540, 5600)) {
|
|
1796
1543
|
this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
|
|
@@ -1808,7 +1555,6 @@ export class Frontend extends EventEmitter {
|
|
|
1808
1555
|
}
|
|
1809
1556
|
break;
|
|
1810
1557
|
case 'setmatterdiscriminator':
|
|
1811
|
-
// eslint-disable-next-line no-case-declarations
|
|
1812
1558
|
const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1813
1559
|
if (isValidNumber(discriminator, 0, 4095)) {
|
|
1814
1560
|
this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
|
|
@@ -1826,7 +1572,6 @@ export class Frontend extends EventEmitter {
|
|
|
1826
1572
|
}
|
|
1827
1573
|
break;
|
|
1828
1574
|
case 'setmatterpasscode':
|
|
1829
|
-
// eslint-disable-next-line no-case-declarations
|
|
1830
1575
|
const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
1831
1576
|
if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
|
|
1832
1577
|
this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
|
|
@@ -1870,19 +1615,15 @@ export class Frontend extends EventEmitter {
|
|
|
1870
1615
|
return;
|
|
1871
1616
|
}
|
|
1872
1617
|
const config = plugin.configJson;
|
|
1873
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1874
1618
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1875
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1876
1619
|
if (select === 'serial')
|
|
1877
1620
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1878
1621
|
if (select === 'name')
|
|
1879
1622
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1880
1623
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1881
|
-
// Remove postfix from the serial if it exists
|
|
1882
1624
|
if (config.postfix) {
|
|
1883
1625
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1884
1626
|
}
|
|
1885
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1886
1627
|
if (isValidArray(config.whiteList, 1)) {
|
|
1887
1628
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1888
1629
|
config.whiteList.push(data.params.serial);
|
|
@@ -1891,7 +1632,6 @@ export class Frontend extends EventEmitter {
|
|
|
1891
1632
|
config.whiteList.push(data.params.name);
|
|
1892
1633
|
}
|
|
1893
1634
|
}
|
|
1894
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1895
1635
|
if (isValidArray(config.blackList, 1)) {
|
|
1896
1636
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1897
1637
|
config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
|
|
@@ -1919,9 +1659,7 @@ export class Frontend extends EventEmitter {
|
|
|
1919
1659
|
return;
|
|
1920
1660
|
}
|
|
1921
1661
|
const config = plugin.configJson;
|
|
1922
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1923
1662
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1924
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1925
1663
|
if (select === 'serial')
|
|
1926
1664
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1927
1665
|
if (select === 'name')
|
|
@@ -1930,7 +1668,6 @@ export class Frontend extends EventEmitter {
|
|
|
1930
1668
|
if (config.postfix) {
|
|
1931
1669
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1932
1670
|
}
|
|
1933
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1934
1671
|
if (isValidArray(config.whiteList, 1)) {
|
|
1935
1672
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1936
1673
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
|
|
@@ -1939,7 +1676,6 @@ export class Frontend extends EventEmitter {
|
|
|
1939
1676
|
config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
|
|
1940
1677
|
}
|
|
1941
1678
|
}
|
|
1942
|
-
// Add the serial to the blackList
|
|
1943
1679
|
if (isValidArray(config.blackList)) {
|
|
1944
1680
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1945
1681
|
config.blackList.push(data.params.serial);
|
|
@@ -1962,7 +1698,6 @@ export class Frontend extends EventEmitter {
|
|
|
1962
1698
|
}
|
|
1963
1699
|
}
|
|
1964
1700
|
else {
|
|
1965
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1966
1701
|
const localData = data;
|
|
1967
1702
|
this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
|
|
1968
1703
|
sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
|
|
@@ -1972,44 +1707,21 @@ export class Frontend extends EventEmitter {
|
|
|
1972
1707
|
inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
|
|
1973
1708
|
}
|
|
1974
1709
|
}
|
|
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
1710
|
wssSendLogMessage(level, time, name, message) {
|
|
1989
1711
|
if (!level || !time || !name || !message)
|
|
1990
1712
|
return;
|
|
1991
|
-
// Remove ANSI escape codes from the message
|
|
1992
|
-
// eslint-disable-next-line no-control-regex
|
|
1993
1713
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1994
|
-
// Remove leading asterisks from the message
|
|
1995
1714
|
message = message.replace(/^\*+/, '');
|
|
1996
|
-
// Replace all occurrences of \t and \n
|
|
1997
1715
|
message = message.replace(/[\t\n]/g, '');
|
|
1998
|
-
// Remove non-printable characters
|
|
1999
|
-
// eslint-disable-next-line no-control-regex
|
|
2000
1716
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
2001
|
-
// Replace all occurrences of \" with "
|
|
2002
1717
|
message = message.replace(/\\"/g, '"');
|
|
2003
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
2004
1718
|
const maxContinuousLength = 100;
|
|
2005
1719
|
const keepStartLength = 20;
|
|
2006
1720
|
const keepEndLength = 20;
|
|
2007
|
-
// Split the message into words
|
|
2008
1721
|
if (level !== 'spawn') {
|
|
2009
1722
|
message = message
|
|
2010
1723
|
.split(' ')
|
|
2011
1724
|
.map((word) => {
|
|
2012
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2013
1725
|
if (word.length > maxContinuousLength) {
|
|
2014
1726
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2015
1727
|
}
|
|
@@ -2017,161 +1729,60 @@ export class Frontend extends EventEmitter {
|
|
|
2017
1729
|
})
|
|
2018
1730
|
.join(' ');
|
|
2019
1731
|
}
|
|
2020
|
-
// Send the message to all connected clients
|
|
2021
1732
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
|
|
2022
1733
|
}
|
|
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
1734
|
wssSendRefreshRequired(changed, params) {
|
|
2036
1735
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
2037
|
-
// Send the message to all connected clients
|
|
2038
1736
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
|
|
2039
1737
|
}
|
|
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
1738
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
2047
1739
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
2048
1740
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
2049
1741
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
|
|
2050
1742
|
if (snackbar === true)
|
|
2051
1743
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
2052
|
-
// Send the message to all connected clients
|
|
2053
1744
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
|
|
2054
1745
|
}
|
|
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
1746
|
wssSendRestartNotRequired(snackbar = true) {
|
|
2061
1747
|
this.log.debug('Sending a restart not required message to all connected clients');
|
|
2062
1748
|
this.matterbridge.matterbridgeInformation.restartRequired = false;
|
|
2063
1749
|
if (snackbar === true)
|
|
2064
1750
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2065
|
-
// Send the message to all connected clients
|
|
2066
1751
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
|
|
2067
1752
|
}
|
|
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
1753
|
wssSendUpdateRequired(devVersion = false) {
|
|
2074
1754
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2075
1755
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
2076
|
-
// Send the message to all connected clients
|
|
2077
1756
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
|
|
2078
1757
|
}
|
|
2079
|
-
/**
|
|
2080
|
-
* Sends a cpu update message to all connected clients.
|
|
2081
|
-
*
|
|
2082
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2083
|
-
*/
|
|
2084
1758
|
wssSendCpuUpdate(cpuUsage) {
|
|
2085
1759
|
if (hasParameter('debug'))
|
|
2086
1760
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2087
|
-
// Send the message to all connected clients
|
|
2088
1761
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', success: true, response: { cpuUsage: Math.round(cpuUsage * 100) / 100 } });
|
|
2089
1762
|
}
|
|
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
1763
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2102
1764
|
if (hasParameter('debug'))
|
|
2103
1765
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2104
|
-
// Send the message to all connected clients
|
|
2105
1766
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', success: true, response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
|
|
2106
1767
|
}
|
|
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
1768
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2114
1769
|
if (hasParameter('debug'))
|
|
2115
1770
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2116
|
-
// Send the message to all connected clients
|
|
2117
1771
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
|
|
2118
1772
|
}
|
|
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
1773
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2131
1774
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2132
|
-
// Send the message to all connected clients
|
|
2133
1775
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
|
|
2134
1776
|
}
|
|
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
1777
|
wssSendCloseSnackbarMessage(message) {
|
|
2142
1778
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2143
|
-
// Send the message to all connected clients
|
|
2144
1779
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
|
|
2145
1780
|
}
|
|
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
1781
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
|
|
2163
1782
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2164
|
-
// Send the message to all connected clients
|
|
2165
1783
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', success: true, response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value } });
|
|
2166
1784
|
}
|
|
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
1785
|
wssBroadcastMessage(msg) {
|
|
2174
|
-
// Send the message to all connected clients
|
|
2175
1786
|
const stringifiedMsg = JSON.stringify(msg);
|
|
2176
1787
|
if (msg.method !== 'log')
|
|
2177
1788
|
this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
|
|
@@ -2182,4 +1793,3 @@ export class Frontend extends EventEmitter {
|
|
|
2182
1793
|
});
|
|
2183
1794
|
}
|
|
2184
1795
|
}
|
|
2185
|
-
//# sourceMappingURL=frontend.js.map
|