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