matterbridge 3.2.7 → 3.2.8-dev-20250916-967e0b8
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 +21 -2
- 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 +34 -459
- 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 +53 -789
- package/dist/matterbridgeAccessoryPlatform.js +0 -36
- package/dist/matterbridgeBehaviors.js +5 -65
- package/dist/matterbridgeDeviceTypes.js +17 -630
- package/dist/matterbridgeDynamicPlatform.js +0 -36
- package/dist/matterbridgeEndpoint.js +55 -1373
- package/dist/matterbridgeEndpointHelpers.js +12 -345
- package/dist/matterbridgePlatform.js +0 -304
- package/dist/matterbridgeTypes.js +0 -25
- package/dist/pluginManager.js +3 -249
- package/dist/shelly.js +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 -40
- package/dist/utils/wait.js +8 -60
- package/frontend/build/asset-manifest.json +6 -8
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/css/{main.a2f4846a.css → main.56c16a87.css} +2 -2
- package/frontend/build/static/css/main.56c16a87.css.map +1 -0
- package/frontend/build/static/js/main.c1ca9eaf.js +3 -0
- package/frontend/build/static/js/{main.ee68a4ae.js.map → main.c1ca9eaf.js.map} +1 -1
- package/frontend/package.json +8 -11
- package/npm-shrinkwrap.json +5 -5
- 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 -315
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.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 -465
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1747
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -761
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -1515
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -407
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -380
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -204
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -270
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -174
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts +0 -75
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -99
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/commandLine.d.ts +0 -59
- package/dist/utils/commandLine.d.ts.map +0 -1
- package/dist/utils/commandLine.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -33
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createDirectory.d.ts +0 -34
- package/dist/utils/createDirectory.d.ts.map +0 -1
- package/dist/utils/createDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -39
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -32
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -54
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/error.d.ts +0 -44
- package/dist/utils/error.d.ts.map +0 -1
- package/dist/utils/error.js.map +0 -1
- package/dist/utils/export.d.ts +0 -13
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/hex.d.ts +0 -89
- package/dist/utils/hex.d.ts.map +0 -1
- package/dist/utils/hex.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -103
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/jestHelpers.d.ts +0 -137
- package/dist/utils/jestHelpers.d.ts.map +0 -1
- package/dist/utils/jestHelpers.js.map +0 -1
- package/dist/utils/network.d.ts +0 -84
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/spawn.d.ts +0 -33
- package/dist/utils/spawn.d.ts.map +0 -1
- package/dist/utils/spawn.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -54
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
- package/frontend/build/static/css/main.a2f4846a.css.map +0 -1
- package/frontend/build/static/js/453.d855a71b.chunk.js +0 -2
- package/frontend/build/static/js/453.d855a71b.chunk.js.map +0 -1
- package/frontend/build/static/js/main.ee68a4ae.js +0 -3
- /package/frontend/build/static/js/{main.ee68a4ae.js.LICENSE.txt → main.c1ca9eaf.js.LICENSE.txt} +0 -0
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.2.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,100 +5,28 @@ 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
|
-
// Matterbridge
|
|
43
15
|
import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout, hasParameter, wait, inspectError } from './utils/export.js';
|
|
44
16
|
import { plg } from './matterbridgeTypes.js';
|
|
45
17
|
import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
|
|
46
18
|
import { cliEmitter, lastCpuUsage } from './cliEmitter.js';
|
|
47
|
-
/**
|
|
48
|
-
* Websocket message ID for logging.
|
|
49
|
-
*
|
|
50
|
-
* @constant {number}
|
|
51
|
-
*/
|
|
52
19
|
export const WS_ID_LOG = 0;
|
|
53
|
-
/**
|
|
54
|
-
* Websocket message ID indicating a refresh is needed.
|
|
55
|
-
*
|
|
56
|
-
* @constant {number}
|
|
57
|
-
*/
|
|
58
20
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
59
|
-
/**
|
|
60
|
-
* Websocket message ID indicating a restart is needed.
|
|
61
|
-
*
|
|
62
|
-
* @constant {number}
|
|
63
|
-
*/
|
|
64
21
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
65
|
-
/**
|
|
66
|
-
* Websocket message ID indicating a cpu update.
|
|
67
|
-
*
|
|
68
|
-
* @constant {number}
|
|
69
|
-
*/
|
|
70
22
|
export const WS_ID_CPU_UPDATE = 3;
|
|
71
|
-
/**
|
|
72
|
-
* Websocket message ID indicating a memory update.
|
|
73
|
-
*
|
|
74
|
-
* @constant {number}
|
|
75
|
-
*/
|
|
76
23
|
export const WS_ID_MEMORY_UPDATE = 4;
|
|
77
|
-
/**
|
|
78
|
-
* Websocket message ID indicating an uptime update.
|
|
79
|
-
*
|
|
80
|
-
* @constant {number}
|
|
81
|
-
*/
|
|
82
24
|
export const WS_ID_UPTIME_UPDATE = 5;
|
|
83
|
-
/**
|
|
84
|
-
* Websocket message ID indicating a snackbar message.
|
|
85
|
-
*
|
|
86
|
-
* @constant {number}
|
|
87
|
-
*/
|
|
88
25
|
export const WS_ID_SNACKBAR = 6;
|
|
89
|
-
/**
|
|
90
|
-
* Websocket message ID indicating matterbridge has un update available.
|
|
91
|
-
*
|
|
92
|
-
* @constant {number}
|
|
93
|
-
*/
|
|
94
26
|
export const WS_ID_UPDATE_NEEDED = 7;
|
|
95
|
-
/**
|
|
96
|
-
* Websocket message ID indicating a state update.
|
|
97
|
-
*
|
|
98
|
-
* @constant {number}
|
|
99
|
-
*/
|
|
100
27
|
export const WS_ID_STATEUPDATE = 8;
|
|
101
|
-
/**
|
|
102
|
-
* Websocket message ID indicating to close a permanent snackbar message.
|
|
103
|
-
*
|
|
104
|
-
* @constant {number}
|
|
105
|
-
*/
|
|
106
28
|
export const WS_ID_CLOSE_SNACKBAR = 9;
|
|
107
|
-
/**
|
|
108
|
-
* Websocket message ID indicating a shelly system update.
|
|
109
|
-
* check:
|
|
110
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/check
|
|
111
|
-
* perform:
|
|
112
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/perform
|
|
113
|
-
*
|
|
114
|
-
* @constant {number}
|
|
115
|
-
*/
|
|
116
29
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
117
|
-
/**
|
|
118
|
-
* Websocket message ID indicating a shelly main update.
|
|
119
|
-
* check:
|
|
120
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/check
|
|
121
|
-
* perform:
|
|
122
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/perform
|
|
123
|
-
*
|
|
124
|
-
* @constant {number}
|
|
125
|
-
*/
|
|
126
30
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
127
31
|
export class Frontend extends EventEmitter {
|
|
128
32
|
matterbridge;
|
|
@@ -135,7 +39,8 @@ export class Frontend extends EventEmitter {
|
|
|
135
39
|
constructor(matterbridge) {
|
|
136
40
|
super();
|
|
137
41
|
this.matterbridge = matterbridge;
|
|
138
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
42
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
43
|
+
this.log.logNameColor = '\x1b[38;5;97m';
|
|
139
44
|
}
|
|
140
45
|
set logLevel(logLevel) {
|
|
141
46
|
this.log.logLevel = logLevel;
|
|
@@ -143,39 +48,10 @@ export class Frontend extends EventEmitter {
|
|
|
143
48
|
async start(port = 8283) {
|
|
144
49
|
this.port = port;
|
|
145
50
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
146
|
-
|
|
147
|
-
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads'); // Is created by matterbridge initialize
|
|
51
|
+
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
148
52
|
const upload = multer({ dest: uploadDir });
|
|
149
|
-
// Create the express app that serves the frontend
|
|
150
53
|
this.expressApp = express();
|
|
151
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
152
|
-
/*
|
|
153
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
154
|
-
for (const method of methods) {
|
|
155
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
157
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
158
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
159
|
-
try {
|
|
160
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
161
|
-
return original(path, ...rest);
|
|
162
|
-
} catch (err) {
|
|
163
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
164
|
-
throw err;
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
*/
|
|
169
|
-
// Log all requests to the server for debugging
|
|
170
|
-
/*
|
|
171
|
-
this.expressApp.use((req, res, next) => {
|
|
172
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
173
|
-
next();
|
|
174
|
-
});
|
|
175
|
-
*/
|
|
176
|
-
// Serve static files from '/static' endpoint
|
|
177
54
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
178
|
-
// Read the package.json file to get the frontend version
|
|
179
55
|
try {
|
|
180
56
|
this.log.debug(`Reading frontend package.json...`);
|
|
181
57
|
const frontendJson = await fs.readFile(path.join(this.matterbridge.rootDirectory, 'frontend/package.json'), 'utf8');
|
|
@@ -183,11 +59,9 @@ export class Frontend extends EventEmitter {
|
|
|
183
59
|
this.log.debug(`Frontend version ${CYAN}${this.matterbridge.matterbridgeInformation.frontendVersion}${db}`);
|
|
184
60
|
}
|
|
185
61
|
catch (error) {
|
|
186
|
-
// istanbul ignore next
|
|
187
62
|
this.log.error(`Failed to read frontend package.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
188
63
|
}
|
|
189
64
|
if (!hasParameter('ssl')) {
|
|
190
|
-
// Create an HTTP server and attach the express app
|
|
191
65
|
try {
|
|
192
66
|
this.log.debug(`Creating HTTP server...`);
|
|
193
67
|
this.httpServer = createServer(this.expressApp);
|
|
@@ -197,7 +71,6 @@ export class Frontend extends EventEmitter {
|
|
|
197
71
|
this.emit('server_error', error);
|
|
198
72
|
return;
|
|
199
73
|
}
|
|
200
|
-
// Listen on the specified port
|
|
201
74
|
if (hasParameter('ingress')) {
|
|
202
75
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
203
76
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -236,7 +109,6 @@ export class Frontend extends EventEmitter {
|
|
|
236
109
|
let passphrase;
|
|
237
110
|
let httpsServerOptions = {};
|
|
238
111
|
if (existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
|
|
239
|
-
// Load the p12 certificate and the passphrase
|
|
240
112
|
try {
|
|
241
113
|
pfx = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
|
|
242
114
|
this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
|
|
@@ -248,7 +120,7 @@ export class Frontend extends EventEmitter {
|
|
|
248
120
|
}
|
|
249
121
|
try {
|
|
250
122
|
passphrase = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
|
|
251
|
-
passphrase = passphrase.trim();
|
|
123
|
+
passphrase = passphrase.trim();
|
|
252
124
|
this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
|
|
253
125
|
}
|
|
254
126
|
catch (error) {
|
|
@@ -259,7 +131,6 @@ export class Frontend extends EventEmitter {
|
|
|
259
131
|
httpsServerOptions = { pfx, passphrase };
|
|
260
132
|
}
|
|
261
133
|
else {
|
|
262
|
-
// 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.
|
|
263
134
|
try {
|
|
264
135
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
265
136
|
this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
|
|
@@ -289,10 +160,9 @@ export class Frontend extends EventEmitter {
|
|
|
289
160
|
httpsServerOptions = { cert: fullChain ?? cert, key, ca };
|
|
290
161
|
}
|
|
291
162
|
if (hasParameter('mtls')) {
|
|
292
|
-
httpsServerOptions.requestCert = true;
|
|
293
|
-
httpsServerOptions.rejectUnauthorized = true;
|
|
163
|
+
httpsServerOptions.requestCert = true;
|
|
164
|
+
httpsServerOptions.rejectUnauthorized = true;
|
|
294
165
|
}
|
|
295
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
296
166
|
try {
|
|
297
167
|
this.log.debug(`Creating HTTPS server...`);
|
|
298
168
|
this.httpsServer = https.createServer(httpsServerOptions, this.expressApp);
|
|
@@ -302,7 +172,6 @@ export class Frontend extends EventEmitter {
|
|
|
302
172
|
this.emit('server_error', error);
|
|
303
173
|
return;
|
|
304
174
|
}
|
|
305
|
-
// Listen on the specified port
|
|
306
175
|
if (hasParameter('ingress')) {
|
|
307
176
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
308
177
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -332,19 +201,17 @@ export class Frontend extends EventEmitter {
|
|
|
332
201
|
return;
|
|
333
202
|
});
|
|
334
203
|
}
|
|
335
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
336
204
|
const wssPort = this.port;
|
|
337
205
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
338
206
|
this.log.debug(`Creating WebSocketServer on host ${CYAN}${wssHost}${db}...`);
|
|
339
207
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
340
208
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
341
209
|
const clientIp = request.socket.remoteAddress;
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
210
|
+
let callbackLogLevel = "notice";
|
|
211
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
212
|
+
callbackLogLevel = "info";
|
|
213
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
214
|
+
callbackLogLevel = "debug";
|
|
348
215
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
349
216
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
350
217
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -366,7 +233,6 @@ export class Frontend extends EventEmitter {
|
|
|
366
233
|
}
|
|
367
234
|
});
|
|
368
235
|
ws.on('error', (error) => {
|
|
369
|
-
// istanbul ignore next
|
|
370
236
|
this.log.error(`WebSocket client error: ${error}`);
|
|
371
237
|
});
|
|
372
238
|
});
|
|
@@ -380,7 +246,6 @@ export class Frontend extends EventEmitter {
|
|
|
380
246
|
this.webSocketServer.on('error', (ws, error) => {
|
|
381
247
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
382
248
|
});
|
|
383
|
-
// Subscribe to cli events
|
|
384
249
|
cliEmitter.removeAllListeners();
|
|
385
250
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
386
251
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -391,8 +256,6 @@ export class Frontend extends EventEmitter {
|
|
|
391
256
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
392
257
|
this.wssSendCpuUpdate(cpuUsage);
|
|
393
258
|
});
|
|
394
|
-
// Endpoint to validate login code
|
|
395
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
396
259
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
397
260
|
const { password } = req.body;
|
|
398
261
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -411,27 +274,23 @@ export class Frontend extends EventEmitter {
|
|
|
411
274
|
this.log.warn('/api/login error wrong password');
|
|
412
275
|
res.json({ valid: false });
|
|
413
276
|
}
|
|
414
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
415
277
|
}
|
|
416
278
|
catch (error) {
|
|
417
279
|
this.log.error('/api/login error getting password');
|
|
418
280
|
res.json({ valid: false });
|
|
419
281
|
}
|
|
420
282
|
});
|
|
421
|
-
// Endpoint to provide health check for docker
|
|
422
283
|
this.expressApp.get('/health', (req, res) => {
|
|
423
284
|
this.log.debug('Express received /health');
|
|
424
285
|
const healthStatus = {
|
|
425
|
-
status: 'ok',
|
|
426
|
-
uptime: process.uptime(),
|
|
427
|
-
timestamp: new Date().toISOString(),
|
|
286
|
+
status: 'ok',
|
|
287
|
+
uptime: process.uptime(),
|
|
288
|
+
timestamp: new Date().toISOString(),
|
|
428
289
|
};
|
|
429
290
|
res.status(200).json(healthStatus);
|
|
430
291
|
});
|
|
431
|
-
// Endpoint to provide memory usage details
|
|
432
292
|
this.expressApp.get('/memory', async (req, res) => {
|
|
433
293
|
this.log.debug('Express received /memory');
|
|
434
|
-
// Memory usage from process
|
|
435
294
|
const memoryUsageRaw = process.memoryUsage();
|
|
436
295
|
const memoryUsage = {
|
|
437
296
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -440,13 +299,10 @@ export class Frontend extends EventEmitter {
|
|
|
440
299
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
441
300
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
442
301
|
};
|
|
443
|
-
// V8 heap statistics
|
|
444
302
|
const { default: v8 } = await import('node:v8');
|
|
445
303
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
446
304
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
447
|
-
// Format heapStats
|
|
448
305
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
449
|
-
// Format heapSpaces
|
|
450
306
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
451
307
|
...space,
|
|
452
308
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -464,23 +320,19 @@ export class Frontend extends EventEmitter {
|
|
|
464
320
|
};
|
|
465
321
|
res.status(200).json(memoryReport);
|
|
466
322
|
});
|
|
467
|
-
// Endpoint to provide settings
|
|
468
323
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
469
324
|
this.log.debug('The frontend sent /api/settings');
|
|
470
325
|
res.json(await this.getApiSettings());
|
|
471
326
|
});
|
|
472
|
-
// Endpoint to provide plugins
|
|
473
327
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
474
328
|
this.log.debug('The frontend sent /api/plugins');
|
|
475
329
|
res.json(this.getPlugins());
|
|
476
330
|
});
|
|
477
|
-
// Endpoint to provide devices
|
|
478
331
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
479
332
|
this.log.debug('The frontend sent /api/devices');
|
|
480
333
|
const devices = await this.getDevices();
|
|
481
334
|
res.json(devices);
|
|
482
335
|
});
|
|
483
|
-
// Endpoint to view the matterbridge log
|
|
484
336
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
485
337
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
486
338
|
try {
|
|
@@ -493,7 +345,6 @@ export class Frontend extends EventEmitter {
|
|
|
493
345
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
494
346
|
}
|
|
495
347
|
});
|
|
496
|
-
// Endpoint to view the matter.js log
|
|
497
348
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
498
349
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
499
350
|
try {
|
|
@@ -506,11 +357,9 @@ export class Frontend extends EventEmitter {
|
|
|
506
357
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
507
358
|
}
|
|
508
359
|
});
|
|
509
|
-
// Endpoint to view the diagnostic.log
|
|
510
360
|
this.expressApp.get('/api/view-diagnostic', async (req, res) => {
|
|
511
361
|
this.log.debug('The frontend sent /api/view-diagnostic');
|
|
512
362
|
const serverNodes = [];
|
|
513
|
-
// istanbul ignore else
|
|
514
363
|
if (this.matterbridge.bridgeMode === 'bridge') {
|
|
515
364
|
if (this.matterbridge.serverNode)
|
|
516
365
|
serverNodes.push(this.matterbridge.serverNode);
|
|
@@ -521,7 +370,6 @@ export class Frontend extends EventEmitter {
|
|
|
521
370
|
serverNodes.push(plugin.serverNode);
|
|
522
371
|
}
|
|
523
372
|
}
|
|
524
|
-
// istanbul ignore next
|
|
525
373
|
for (const device of this.matterbridge.getDevices()) {
|
|
526
374
|
if (device.serverNode)
|
|
527
375
|
serverNodes.push(device.serverNode);
|
|
@@ -544,20 +392,17 @@ export class Frontend extends EventEmitter {
|
|
|
544
392
|
values: [...serverNodes],
|
|
545
393
|
})));
|
|
546
394
|
delete Logger.destinations.diagnostic;
|
|
547
|
-
await wait(500);
|
|
395
|
+
await wait(500);
|
|
548
396
|
try {
|
|
549
397
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'), 'utf8');
|
|
550
398
|
res.type('text/plain');
|
|
551
399
|
res.send(data.slice(29));
|
|
552
400
|
}
|
|
553
401
|
catch (error) {
|
|
554
|
-
// istanbul ignore next
|
|
555
402
|
this.log.error(`Error reading diagnostic log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
556
|
-
// istanbul ignore next
|
|
557
403
|
res.status(500).send('Error reading diagnostic log file.');
|
|
558
404
|
}
|
|
559
405
|
});
|
|
560
|
-
// Endpoint to view the shelly log
|
|
561
406
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
562
407
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
563
408
|
try {
|
|
@@ -570,7 +415,6 @@ export class Frontend extends EventEmitter {
|
|
|
570
415
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
571
416
|
}
|
|
572
417
|
});
|
|
573
|
-
// Endpoint to download the matterbridge log
|
|
574
418
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
575
419
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
576
420
|
try {
|
|
@@ -584,14 +428,12 @@ export class Frontend extends EventEmitter {
|
|
|
584
428
|
}
|
|
585
429
|
res.type('text/plain');
|
|
586
430
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'matterbridge.log', (error) => {
|
|
587
|
-
/* istanbul ignore if */
|
|
588
431
|
if (error) {
|
|
589
432
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
590
433
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
591
434
|
}
|
|
592
435
|
});
|
|
593
436
|
});
|
|
594
|
-
// Endpoint to download the matter log
|
|
595
437
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
596
438
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
597
439
|
try {
|
|
@@ -605,14 +447,12 @@ export class Frontend extends EventEmitter {
|
|
|
605
447
|
}
|
|
606
448
|
res.type('text/plain');
|
|
607
449
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
|
|
608
|
-
/* istanbul ignore if */
|
|
609
450
|
if (error) {
|
|
610
451
|
this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
611
452
|
res.status(500).send('Error downloading the matter log file');
|
|
612
453
|
}
|
|
613
454
|
});
|
|
614
455
|
});
|
|
615
|
-
// Endpoint to download the shelly log
|
|
616
456
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
617
457
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
618
458
|
try {
|
|
@@ -626,90 +466,74 @@ export class Frontend extends EventEmitter {
|
|
|
626
466
|
}
|
|
627
467
|
res.type('text/plain');
|
|
628
468
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
629
|
-
/* istanbul ignore if */
|
|
630
469
|
if (error) {
|
|
631
470
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
632
471
|
res.status(500).send('Error downloading Shelly system log file');
|
|
633
472
|
}
|
|
634
473
|
});
|
|
635
474
|
});
|
|
636
|
-
// Endpoint to download the matterbridge storage directory
|
|
637
475
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
638
476
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
639
477
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
640
478
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), `matterbridge.${this.matterbridge.nodeStorageName}.zip`, (error) => {
|
|
641
|
-
/* istanbul ignore if */
|
|
642
479
|
if (error) {
|
|
643
480
|
this.log.error(`Error downloading file ${`matterbridge.${this.matterbridge.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
644
481
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
645
482
|
}
|
|
646
483
|
});
|
|
647
484
|
});
|
|
648
|
-
// Endpoint to download the matter storage file
|
|
649
485
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
650
486
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
651
487
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
652
488
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), `matterbridge.${this.matterbridge.matterStorageName}.zip`, (error) => {
|
|
653
|
-
/* istanbul ignore if */
|
|
654
489
|
if (error) {
|
|
655
490
|
this.log.error(`Error downloading the matter storage matterbridge.${this.matterbridge.matterStorageName}.zip: ${error instanceof Error ? error.message : error}`);
|
|
656
491
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
657
492
|
}
|
|
658
493
|
});
|
|
659
494
|
});
|
|
660
|
-
// Endpoint to download the matterbridge plugin directory
|
|
661
495
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
662
496
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
663
497
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
664
498
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
665
|
-
/* istanbul ignore if */
|
|
666
499
|
if (error) {
|
|
667
500
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
668
501
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
669
502
|
}
|
|
670
503
|
});
|
|
671
504
|
});
|
|
672
|
-
// Endpoint to download the matterbridge plugin config files
|
|
673
505
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
674
506
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
675
507
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
676
508
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
677
|
-
/* istanbul ignore if */
|
|
678
509
|
if (error) {
|
|
679
510
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
680
511
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
681
512
|
}
|
|
682
513
|
});
|
|
683
514
|
});
|
|
684
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
685
515
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
686
516
|
this.log.debug('The frontend sent /api/download-backup');
|
|
687
517
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
688
|
-
/* istanbul ignore if */
|
|
689
518
|
if (error) {
|
|
690
519
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
691
520
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
692
521
|
}
|
|
693
522
|
});
|
|
694
523
|
});
|
|
695
|
-
// Endpoint to upload a package
|
|
696
524
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
697
525
|
const { filename } = req.body;
|
|
698
526
|
const file = req.file;
|
|
699
|
-
/* istanbul ignore if */
|
|
700
527
|
if (!file || !filename) {
|
|
701
528
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
702
529
|
res.status(400).send('Invalid request: file and filename are required');
|
|
703
530
|
return;
|
|
704
531
|
}
|
|
705
532
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
706
|
-
// Define the path where the plugin file will be saved
|
|
707
533
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
708
534
|
try {
|
|
709
|
-
// Move the uploaded file to the specified path
|
|
710
535
|
await fs.rename(file.path, filePath);
|
|
711
536
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
712
|
-
// Install the plugin package
|
|
713
537
|
if (filename.endsWith('.tgz')) {
|
|
714
538
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
715
539
|
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
@@ -729,7 +553,6 @@ export class Frontend extends EventEmitter {
|
|
|
729
553
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
730
554
|
}
|
|
731
555
|
});
|
|
732
|
-
// Fallback for routing (must be the last route)
|
|
733
556
|
this.expressApp.use((req, res) => {
|
|
734
557
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
|
|
735
558
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -738,16 +561,13 @@ export class Frontend extends EventEmitter {
|
|
|
738
561
|
}
|
|
739
562
|
async stop() {
|
|
740
563
|
this.log.debug('Stopping the frontend...');
|
|
741
|
-
// Remove listeners from the express app
|
|
742
564
|
if (this.expressApp) {
|
|
743
565
|
this.expressApp.removeAllListeners();
|
|
744
566
|
this.expressApp = undefined;
|
|
745
567
|
this.log.debug('Frontend app closed successfully');
|
|
746
568
|
}
|
|
747
|
-
// Close the WebSocket server
|
|
748
569
|
if (this.webSocketServer) {
|
|
749
570
|
this.log.debug('Closing WebSocket server...');
|
|
750
|
-
// Close all active connections
|
|
751
571
|
this.webSocketServer.clients.forEach((client) => {
|
|
752
572
|
if (client.readyState === WebSocket.OPEN) {
|
|
753
573
|
client.close();
|
|
@@ -756,7 +576,6 @@ export class Frontend extends EventEmitter {
|
|
|
756
576
|
await withTimeout(new Promise((resolve) => {
|
|
757
577
|
this.webSocketServer?.close((error) => {
|
|
758
578
|
if (error) {
|
|
759
|
-
// istanbul ignore next
|
|
760
579
|
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
761
580
|
}
|
|
762
581
|
else {
|
|
@@ -769,13 +588,11 @@ export class Frontend extends EventEmitter {
|
|
|
769
588
|
this.webSocketServer.removeAllListeners();
|
|
770
589
|
this.webSocketServer = undefined;
|
|
771
590
|
}
|
|
772
|
-
// Close the http server
|
|
773
591
|
if (this.httpServer) {
|
|
774
592
|
this.log.debug('Closing http server...');
|
|
775
593
|
await withTimeout(new Promise((resolve) => {
|
|
776
594
|
this.httpServer?.close((error) => {
|
|
777
595
|
if (error) {
|
|
778
|
-
// istanbul ignore next
|
|
779
596
|
this.log.error(`Error closing http server: ${error}`);
|
|
780
597
|
}
|
|
781
598
|
else {
|
|
@@ -784,18 +601,16 @@ export class Frontend extends EventEmitter {
|
|
|
784
601
|
}
|
|
785
602
|
resolve();
|
|
786
603
|
});
|
|
787
|
-
}),
|
|
604
|
+
}), 5000, false);
|
|
788
605
|
this.httpServer.removeAllListeners();
|
|
789
606
|
this.httpServer = undefined;
|
|
790
607
|
this.log.debug('Frontend http server closed successfully');
|
|
791
608
|
}
|
|
792
|
-
// Close the https server
|
|
793
609
|
if (this.httpsServer) {
|
|
794
610
|
this.log.debug('Closing https server...');
|
|
795
611
|
await withTimeout(new Promise((resolve) => {
|
|
796
612
|
this.httpsServer?.close((error) => {
|
|
797
613
|
if (error) {
|
|
798
|
-
// istanbul ignore next
|
|
799
614
|
this.log.error(`Error closing https server: ${error}`);
|
|
800
615
|
}
|
|
801
616
|
else {
|
|
@@ -804,14 +619,13 @@ export class Frontend extends EventEmitter {
|
|
|
804
619
|
}
|
|
805
620
|
resolve();
|
|
806
621
|
});
|
|
807
|
-
}),
|
|
622
|
+
}), 5000, false);
|
|
808
623
|
this.httpsServer.removeAllListeners();
|
|
809
624
|
this.httpsServer = undefined;
|
|
810
625
|
this.log.debug('Frontend https server closed successfully');
|
|
811
626
|
}
|
|
812
627
|
this.log.debug('Frontend stopped successfully');
|
|
813
628
|
}
|
|
814
|
-
// Function to format bytes to KB, MB, or GB
|
|
815
629
|
formatMemoryUsage = (bytes) => {
|
|
816
630
|
if (bytes >= 1024 ** 3) {
|
|
817
631
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -823,7 +637,6 @@ export class Frontend extends EventEmitter {
|
|
|
823
637
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
824
638
|
}
|
|
825
639
|
};
|
|
826
|
-
// Function to format system uptime with only the most significant unit
|
|
827
640
|
formatOsUpTime = (seconds) => {
|
|
828
641
|
if (seconds >= 86400) {
|
|
829
642
|
const days = Math.floor(seconds / 86400);
|
|
@@ -839,13 +652,7 @@ export class Frontend extends EventEmitter {
|
|
|
839
652
|
}
|
|
840
653
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
841
654
|
};
|
|
842
|
-
/**
|
|
843
|
-
* Retrieves the api settings data.
|
|
844
|
-
*
|
|
845
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
846
|
-
*/
|
|
847
655
|
async getApiSettings() {
|
|
848
|
-
// Update the system information
|
|
849
656
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
850
657
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
851
658
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -854,7 +661,6 @@ export class Frontend extends EventEmitter {
|
|
|
854
661
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
855
662
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
856
663
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
857
|
-
// Update the matterbridge information
|
|
858
664
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
859
665
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
860
666
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
@@ -866,8 +672,8 @@ export class Frontend extends EventEmitter {
|
|
|
866
672
|
this.matterbridge.matterbridgeInformation.matterPort = (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540;
|
|
867
673
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = await this.matterbridge.nodeContext?.get('matterdiscriminator');
|
|
868
674
|
this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
|
|
869
|
-
// Update the matterbridge information in bridge mode
|
|
870
675
|
if (this.matterbridge.bridgeMode === 'bridge' && this.matterbridge.serverNode && !this.matterbridge.hasCleanupStarted) {
|
|
676
|
+
this.matterbridge.matterbridgeInformation.matter = this.matterbridge.getServerNodeData(this.matterbridge.serverNode);
|
|
871
677
|
this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.serverNode.state.commissioning.commissioned;
|
|
872
678
|
this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.qrPairingCode;
|
|
873
679
|
this.matterbridge.matterbridgeInformation.matterbridgeManualPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.manualPairingCode;
|
|
@@ -876,12 +682,6 @@ export class Frontend extends EventEmitter {
|
|
|
876
682
|
}
|
|
877
683
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
878
684
|
}
|
|
879
|
-
/**
|
|
880
|
-
* Retrieves the reachable attribute.
|
|
881
|
-
*
|
|
882
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
883
|
-
* @returns {boolean} The reachable attribute.
|
|
884
|
-
*/
|
|
885
685
|
getReachability(device) {
|
|
886
686
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
887
687
|
return false;
|
|
@@ -893,12 +693,6 @@ export class Frontend extends EventEmitter {
|
|
|
893
693
|
return true;
|
|
894
694
|
return false;
|
|
895
695
|
}
|
|
896
|
-
/**
|
|
897
|
-
* Retrieves the power source attribute.
|
|
898
|
-
*
|
|
899
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
900
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
901
|
-
*/
|
|
902
696
|
getPowerSource(endpoint) {
|
|
903
697
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
904
698
|
return undefined;
|
|
@@ -914,33 +708,18 @@ export class Frontend extends EventEmitter {
|
|
|
914
708
|
}
|
|
915
709
|
return;
|
|
916
710
|
};
|
|
917
|
-
// Root endpoint
|
|
918
711
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
919
712
|
return powerSource(endpoint);
|
|
920
|
-
// Child endpoints
|
|
921
713
|
for (const child of endpoint.getChildEndpoints()) {
|
|
922
714
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
923
715
|
return powerSource(child);
|
|
924
716
|
}
|
|
925
717
|
}
|
|
926
|
-
/**
|
|
927
|
-
* Retrieves the commissioned status, matter pairing codes, fabrics and sessions from a given device in server mode.
|
|
928
|
-
*
|
|
929
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the data from.
|
|
930
|
-
* @returns {ApiDevicesMatter | undefined} An ApiDevicesMatter object or undefined if not found.
|
|
931
|
-
*/
|
|
932
718
|
getMatterDataFromDevice(device) {
|
|
933
719
|
if (device.mode === 'server' && device.serverNode) {
|
|
934
720
|
return this.matterbridge.getServerNodeData(device.serverNode);
|
|
935
721
|
}
|
|
936
722
|
}
|
|
937
|
-
/**
|
|
938
|
-
* Retrieves the cluster text description from a given device.
|
|
939
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
940
|
-
*
|
|
941
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
942
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
943
|
-
*/
|
|
944
723
|
getClusterTextFromDevice(device) {
|
|
945
724
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
946
725
|
return '';
|
|
@@ -951,7 +730,6 @@ export class Frontend extends EventEmitter {
|
|
|
951
730
|
if (composed)
|
|
952
731
|
return 'Composed: ' + composed.value;
|
|
953
732
|
}
|
|
954
|
-
// istanbul ignore next cause is not reachable
|
|
955
733
|
return '';
|
|
956
734
|
};
|
|
957
735
|
const getFixedLabel = (device) => {
|
|
@@ -961,13 +739,11 @@ export class Frontend extends EventEmitter {
|
|
|
961
739
|
if (composed)
|
|
962
740
|
return 'Composed: ' + composed.value;
|
|
963
741
|
}
|
|
964
|
-
// istanbul ignore next cause is not reacheable
|
|
965
742
|
return '';
|
|
966
743
|
};
|
|
967
744
|
let attributes = '';
|
|
968
745
|
let supportedModes = [];
|
|
969
746
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
970
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
971
747
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
972
748
|
return;
|
|
973
749
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1057,17 +833,11 @@ export class Frontend extends EventEmitter {
|
|
|
1057
833
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1058
834
|
attributes += `${getUserLabel(device)} `;
|
|
1059
835
|
});
|
|
1060
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1061
836
|
return attributes.trimStart().trimEnd();
|
|
1062
837
|
}
|
|
1063
|
-
/**
|
|
1064
|
-
* Retrieves the registered plugins sanitized for res.json().
|
|
1065
|
-
*
|
|
1066
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
1067
|
-
*/
|
|
1068
838
|
getPlugins() {
|
|
1069
839
|
if (this.matterbridge.hasCleanupStarted)
|
|
1070
|
-
return [];
|
|
840
|
+
return [];
|
|
1071
841
|
const baseRegisteredPlugins = [];
|
|
1072
842
|
for (const plugin of this.matterbridge.plugins) {
|
|
1073
843
|
baseRegisteredPlugins.push({
|
|
@@ -1097,7 +867,7 @@ export class Frontend extends EventEmitter {
|
|
|
1097
867
|
schemaJson: plugin.schemaJson,
|
|
1098
868
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1099
869
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1100
|
-
|
|
870
|
+
matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
|
|
1101
871
|
paired: plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.commissioned : undefined,
|
|
1102
872
|
qrPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.pairingCodes.qrPairingCode : undefined,
|
|
1103
873
|
manualPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.pairingCodes.manualPairingCode : undefined,
|
|
@@ -1107,21 +877,13 @@ export class Frontend extends EventEmitter {
|
|
|
1107
877
|
}
|
|
1108
878
|
return baseRegisteredPlugins;
|
|
1109
879
|
}
|
|
1110
|
-
/**
|
|
1111
|
-
* Retrieves the devices from Matterbridge.
|
|
1112
|
-
*
|
|
1113
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1114
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
|
|
1115
|
-
*/
|
|
1116
880
|
async getDevices(pluginName) {
|
|
1117
881
|
if (this.matterbridge.hasCleanupStarted)
|
|
1118
|
-
return [];
|
|
882
|
+
return [];
|
|
1119
883
|
const devices = [];
|
|
1120
884
|
for (const device of this.matterbridge.devices.array()) {
|
|
1121
|
-
// Filter by pluginName if provided
|
|
1122
885
|
if (pluginName && pluginName !== device.plugin)
|
|
1123
886
|
continue;
|
|
1124
|
-
// Check if the device has the required properties
|
|
1125
887
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1126
888
|
continue;
|
|
1127
889
|
devices.push({
|
|
@@ -1141,37 +903,22 @@ export class Frontend extends EventEmitter {
|
|
|
1141
903
|
}
|
|
1142
904
|
return devices;
|
|
1143
905
|
}
|
|
1144
|
-
/**
|
|
1145
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1146
|
-
*
|
|
1147
|
-
* Response for /api/clusters
|
|
1148
|
-
*
|
|
1149
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1150
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1151
|
-
* @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1152
|
-
*/
|
|
1153
906
|
getClusters(pluginName, endpointNumber) {
|
|
1154
907
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1155
908
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1156
909
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1157
910
|
return;
|
|
1158
911
|
}
|
|
1159
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1160
|
-
// Get the device types from the main endpoint
|
|
1161
912
|
const deviceTypes = [];
|
|
1162
913
|
const clusters = [];
|
|
1163
914
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1164
915
|
deviceTypes.push(d.deviceType);
|
|
1165
916
|
});
|
|
1166
|
-
// Get the clusters from the main endpoint
|
|
1167
917
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1168
918
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1169
919
|
return;
|
|
1170
920
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1171
921
|
return;
|
|
1172
|
-
// console.log(
|
|
1173
|
-
// `${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}`,
|
|
1174
|
-
// );
|
|
1175
922
|
clusters.push({
|
|
1176
923
|
endpoint: endpoint.number.toString(),
|
|
1177
924
|
id: 'main',
|
|
@@ -1184,19 +931,12 @@ export class Frontend extends EventEmitter {
|
|
|
1184
931
|
attributeLocalValue: attributeValue,
|
|
1185
932
|
});
|
|
1186
933
|
});
|
|
1187
|
-
// Get the child endpoints
|
|
1188
934
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1189
|
-
// if (childEndpoints.length === 0) {
|
|
1190
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1191
|
-
// }
|
|
1192
935
|
childEndpoints.forEach((childEndpoint) => {
|
|
1193
|
-
// istanbul ignore if cause is not reachable: should never happen but ...
|
|
1194
936
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1195
937
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1196
938
|
return;
|
|
1197
939
|
}
|
|
1198
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1199
|
-
// Get the device types of the child endpoint
|
|
1200
940
|
const deviceTypes = [];
|
|
1201
941
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1202
942
|
deviceTypes.push(d.deviceType);
|
|
@@ -1206,12 +946,9 @@ export class Frontend extends EventEmitter {
|
|
|
1206
946
|
return;
|
|
1207
947
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1208
948
|
return;
|
|
1209
|
-
// console.log(
|
|
1210
|
-
// `${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}`,
|
|
1211
|
-
// );
|
|
1212
949
|
clusters.push({
|
|
1213
950
|
endpoint: childEndpoint.number.toString(),
|
|
1214
|
-
id: childEndpoint.maybeId ?? 'null',
|
|
951
|
+
id: childEndpoint.maybeId ?? 'null',
|
|
1215
952
|
deviceTypes,
|
|
1216
953
|
clusterName: capitalizeFirstLetter(clusterName),
|
|
1217
954
|
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
@@ -1224,13 +961,6 @@ export class Frontend extends EventEmitter {
|
|
|
1224
961
|
});
|
|
1225
962
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
|
|
1226
963
|
}
|
|
1227
|
-
/**
|
|
1228
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1229
|
-
*
|
|
1230
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1231
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1232
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1233
|
-
*/
|
|
1234
964
|
async wsMessageHandler(client, message) {
|
|
1235
965
|
let data;
|
|
1236
966
|
try {
|
|
@@ -1277,48 +1007,35 @@ export class Frontend extends EventEmitter {
|
|
|
1277
1007
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1278
1008
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1279
1009
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1280
|
-
// The install comes from InstallPlugins
|
|
1281
1010
|
this.matterbridge.plugins
|
|
1282
1011
|
.add(packageName)
|
|
1283
1012
|
.then((plugin) => {
|
|
1284
|
-
// istanbul ignore next if
|
|
1285
1013
|
if (plugin) {
|
|
1286
|
-
// The plugin is not registered
|
|
1287
1014
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1288
|
-
// In childbridge mode the plugins server node is not started when added
|
|
1289
1015
|
if (this.matterbridge.bridgeMode === 'childbridge')
|
|
1290
1016
|
this.wssSendRestartRequired(true, true);
|
|
1291
1017
|
this.matterbridge.plugins
|
|
1292
1018
|
.load(plugin, true, 'The plugin has been added', true)
|
|
1293
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1294
1019
|
.then(() => {
|
|
1295
1020
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
1296
1021
|
this.wssSendRefreshRequired('plugins');
|
|
1297
1022
|
return;
|
|
1298
1023
|
})
|
|
1299
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1300
1024
|
.catch((_error) => {
|
|
1301
|
-
//
|
|
1302
1025
|
});
|
|
1303
1026
|
}
|
|
1304
1027
|
else {
|
|
1305
|
-
// The plugin is already registered
|
|
1306
1028
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1307
1029
|
this.wssSendRefreshRequired('plugins');
|
|
1308
1030
|
this.wssSendRestartRequired(true, true);
|
|
1309
1031
|
}
|
|
1310
1032
|
return;
|
|
1311
1033
|
})
|
|
1312
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1313
1034
|
.catch((_error) => {
|
|
1314
|
-
//
|
|
1315
1035
|
});
|
|
1316
1036
|
}
|
|
1317
1037
|
else {
|
|
1318
|
-
// The package is matterbridge
|
|
1319
|
-
// istanbul ignore next
|
|
1320
1038
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1321
|
-
// istanbul ignore next if
|
|
1322
1039
|
if (this.matterbridge.restartMode !== '') {
|
|
1323
1040
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1324
1041
|
this.matterbridge.shutdownProcess();
|
|
@@ -1340,9 +1057,7 @@ export class Frontend extends EventEmitter {
|
|
|
1340
1057
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1341
1058
|
return;
|
|
1342
1059
|
}
|
|
1343
|
-
// The package is a plugin
|
|
1344
1060
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1345
|
-
// istanbul ignore next if
|
|
1346
1061
|
if (plugin) {
|
|
1347
1062
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
1348
1063
|
await this.matterbridge.plugins.remove(data.params.packageName);
|
|
@@ -1350,7 +1065,6 @@ export class Frontend extends EventEmitter {
|
|
|
1350
1065
|
this.wssSendRefreshRequired('plugins');
|
|
1351
1066
|
this.wssSendRefreshRequired('devices');
|
|
1352
1067
|
}
|
|
1353
|
-
// Uninstall the package
|
|
1354
1068
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1355
1069
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
1356
1070
|
spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1391,7 +1105,6 @@ export class Frontend extends EventEmitter {
|
|
|
1391
1105
|
return;
|
|
1392
1106
|
})
|
|
1393
1107
|
.catch((_error) => {
|
|
1394
|
-
//
|
|
1395
1108
|
});
|
|
1396
1109
|
}
|
|
1397
1110
|
else {
|
|
@@ -1438,7 +1151,6 @@ export class Frontend extends EventEmitter {
|
|
|
1438
1151
|
return;
|
|
1439
1152
|
})
|
|
1440
1153
|
.catch((_error) => {
|
|
1441
|
-
//
|
|
1442
1154
|
});
|
|
1443
1155
|
}
|
|
1444
1156
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1464,7 +1176,6 @@ export class Frontend extends EventEmitter {
|
|
|
1464
1176
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1465
1177
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
|
|
1466
1178
|
if (plugin.serverNode) {
|
|
1467
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1468
1179
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1469
1180
|
plugin.serverNode = undefined;
|
|
1470
1181
|
}
|
|
@@ -1474,17 +1185,19 @@ export class Frontend extends EventEmitter {
|
|
|
1474
1185
|
this.matterbridge.devices.remove(device);
|
|
1475
1186
|
}
|
|
1476
1187
|
}
|
|
1188
|
+
if (plugin.type === 'DynamicPlatform' && !plugin.locked) {
|
|
1189
|
+
await this.matterbridge.createDynamicPlugin(plugin);
|
|
1190
|
+
}
|
|
1477
1191
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
|
|
1478
|
-
plugin.restartRequired = false;
|
|
1192
|
+
plugin.restartRequired = false;
|
|
1479
1193
|
let needRestart = 0;
|
|
1480
1194
|
for (const plugin of this.matterbridge.plugins) {
|
|
1481
1195
|
if (plugin.restartRequired)
|
|
1482
1196
|
needRestart++;
|
|
1483
1197
|
}
|
|
1484
1198
|
if (needRestart === 0) {
|
|
1485
|
-
this.wssSendRestartNotRequired(true);
|
|
1199
|
+
this.wssSendRestartNotRequired(true);
|
|
1486
1200
|
}
|
|
1487
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1488
1201
|
if (plugin.serverNode)
|
|
1489
1202
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1490
1203
|
this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
|
|
@@ -1620,7 +1333,7 @@ export class Frontend extends EventEmitter {
|
|
|
1620
1333
|
this.log.debug(`*Sending data for node ${data.params.id}`);
|
|
1621
1334
|
this.wssSendRefreshRequired('matter', { matter: { ...this.matterbridge.getServerNodeData(serverNode) } });
|
|
1622
1335
|
}
|
|
1623
|
-
if (data.params.
|
|
1336
|
+
if (data.params.startCommission) {
|
|
1624
1337
|
await serverNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
1625
1338
|
this.matterbridge.advertisingNodes.set(serverNode.id, Date.now());
|
|
1626
1339
|
this.log.debug(`*Commissioning has been sent for node ${data.params.id}`);
|
|
@@ -1756,22 +1469,22 @@ export class Frontend extends EventEmitter {
|
|
|
1756
1469
|
if (isValidString(data.params.value, 4)) {
|
|
1757
1470
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1758
1471
|
if (data.params.value === 'Debug') {
|
|
1759
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1472
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1760
1473
|
}
|
|
1761
1474
|
else if (data.params.value === 'Info') {
|
|
1762
|
-
await this.matterbridge.setLogLevel("info"
|
|
1475
|
+
await this.matterbridge.setLogLevel("info");
|
|
1763
1476
|
}
|
|
1764
1477
|
else if (data.params.value === 'Notice') {
|
|
1765
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1478
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1766
1479
|
}
|
|
1767
1480
|
else if (data.params.value === 'Warn') {
|
|
1768
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1481
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1769
1482
|
}
|
|
1770
1483
|
else if (data.params.value === 'Error') {
|
|
1771
|
-
await this.matterbridge.setLogLevel("error"
|
|
1484
|
+
await this.matterbridge.setLogLevel("error");
|
|
1772
1485
|
}
|
|
1773
1486
|
else if (data.params.value === 'Fatal') {
|
|
1774
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1487
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1775
1488
|
}
|
|
1776
1489
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1777
1490
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1782,7 +1495,6 @@ export class Frontend extends EventEmitter {
|
|
|
1782
1495
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1783
1496
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1784
1497
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1785
|
-
// Create the file logger for matterbridge
|
|
1786
1498
|
if (data.params.value)
|
|
1787
1499
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1788
1500
|
else
|
|
@@ -1936,19 +1648,15 @@ export class Frontend extends EventEmitter {
|
|
|
1936
1648
|
return;
|
|
1937
1649
|
}
|
|
1938
1650
|
const config = plugin.configJson;
|
|
1939
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1940
1651
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1941
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1942
1652
|
if (select === 'serial')
|
|
1943
1653
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1944
1654
|
if (select === 'name')
|
|
1945
1655
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1946
1656
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1947
|
-
// Remove postfix from the serial if it exists
|
|
1948
1657
|
if (config.postfix) {
|
|
1949
1658
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1950
1659
|
}
|
|
1951
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1952
1660
|
if (isValidArray(config.whiteList, 1)) {
|
|
1953
1661
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1954
1662
|
config.whiteList.push(data.params.serial);
|
|
@@ -1957,7 +1665,6 @@ export class Frontend extends EventEmitter {
|
|
|
1957
1665
|
config.whiteList.push(data.params.name);
|
|
1958
1666
|
}
|
|
1959
1667
|
}
|
|
1960
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1961
1668
|
if (isValidArray(config.blackList, 1)) {
|
|
1962
1669
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1963
1670
|
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
@@ -1988,9 +1695,7 @@ export class Frontend extends EventEmitter {
|
|
|
1988
1695
|
return;
|
|
1989
1696
|
}
|
|
1990
1697
|
const config = plugin.configJson;
|
|
1991
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1992
1698
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1993
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1994
1699
|
if (select === 'serial')
|
|
1995
1700
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1996
1701
|
if (select === 'name')
|
|
@@ -1999,7 +1704,6 @@ export class Frontend extends EventEmitter {
|
|
|
1999
1704
|
if (config.postfix) {
|
|
2000
1705
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
2001
1706
|
}
|
|
2002
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
2003
1707
|
if (isValidArray(config.whiteList, 1)) {
|
|
2004
1708
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
2005
1709
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
@@ -2008,7 +1712,6 @@ export class Frontend extends EventEmitter {
|
|
|
2008
1712
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
2009
1713
|
}
|
|
2010
1714
|
}
|
|
2011
|
-
// Add the serial to the blackList
|
|
2012
1715
|
if (isValidArray(config.blackList)) {
|
|
2013
1716
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
2014
1717
|
config.blackList.push(data.params.serial);
|
|
@@ -2042,253 +1745,126 @@ export class Frontend extends EventEmitter {
|
|
|
2042
1745
|
inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
|
|
2043
1746
|
}
|
|
2044
1747
|
}
|
|
2045
|
-
/**
|
|
2046
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
2047
|
-
*
|
|
2048
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
2049
|
-
* @param {string} time - The time string of the message
|
|
2050
|
-
* @param {string} name - The logger name of the message
|
|
2051
|
-
* @param {string} message - The content of the message.
|
|
2052
|
-
*
|
|
2053
|
-
* @remarks
|
|
2054
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
2055
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
2056
|
-
* The function sends the message to all connected clients.
|
|
2057
|
-
*/
|
|
2058
1748
|
wssSendMessage(level, time, name, message) {
|
|
2059
1749
|
if (!level || !time || !name || !message)
|
|
2060
1750
|
return;
|
|
2061
|
-
// Remove ANSI escape codes from the message
|
|
2062
|
-
// eslint-disable-next-line no-control-regex
|
|
2063
1751
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
2064
|
-
// Remove leading asterisks from the message
|
|
2065
1752
|
message = message.replace(/^\*+/, '');
|
|
2066
|
-
// Replace all occurrences of \t and \n
|
|
2067
1753
|
message = message.replace(/[\t\n]/g, '');
|
|
2068
|
-
// Remove non-printable characters
|
|
2069
|
-
// eslint-disable-next-line no-control-regex
|
|
2070
1754
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
2071
|
-
// Replace all occurrences of \" with "
|
|
2072
1755
|
message = message.replace(/\\"/g, '"');
|
|
2073
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
2074
1756
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
2075
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
2076
1757
|
const maxContinuousLength = 100;
|
|
2077
1758
|
const keepStartLength = 20;
|
|
2078
1759
|
const keepEndLength = 20;
|
|
2079
|
-
// Split the message into words
|
|
2080
1760
|
message = message
|
|
2081
1761
|
.split(' ')
|
|
2082
1762
|
.map((word) => {
|
|
2083
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2084
1763
|
if (word.length > maxContinuousLength) {
|
|
2085
1764
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2086
1765
|
}
|
|
2087
1766
|
return word;
|
|
2088
1767
|
})
|
|
2089
1768
|
.join(' ');
|
|
2090
|
-
// Send the message to all connected clients
|
|
2091
1769
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2092
1770
|
if (client.readyState === WebSocket.OPEN) {
|
|
2093
1771
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
2094
1772
|
}
|
|
2095
1773
|
});
|
|
2096
1774
|
}
|
|
2097
|
-
/**
|
|
2098
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2099
|
-
*
|
|
2100
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
2101
|
-
* @param {Record<string, unknown>} params - Additional parameters to send with the message.
|
|
2102
|
-
* possible values for changed:
|
|
2103
|
-
* - 'matterbridgeLatestVersion'
|
|
2104
|
-
* - 'matterbridgeDevVersion'
|
|
2105
|
-
* - 'matterbridgeAdvertise'
|
|
2106
|
-
* - 'online'
|
|
2107
|
-
* - 'offline'
|
|
2108
|
-
* - 'reachability'
|
|
2109
|
-
* - 'settings'
|
|
2110
|
-
* - 'plugins'
|
|
2111
|
-
* - 'pluginsRestart'
|
|
2112
|
-
* - 'devices'
|
|
2113
|
-
* - 'fabrics'
|
|
2114
|
-
* - 'sessions'
|
|
2115
|
-
* - 'matter' with param 'matter' (QRDivDevice)
|
|
2116
|
-
*/
|
|
2117
1775
|
wssSendRefreshRequired(changed = null, params = {}) {
|
|
2118
1776
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
2119
|
-
// Send the message to all connected clients
|
|
2120
1777
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2121
1778
|
if (client.readyState === WebSocket.OPEN) {
|
|
2122
1779
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed, ...params } }));
|
|
2123
1780
|
}
|
|
2124
1781
|
});
|
|
2125
1782
|
}
|
|
2126
|
-
/**
|
|
2127
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2128
|
-
*
|
|
2129
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
|
|
2130
|
-
* @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
|
|
2131
|
-
*/
|
|
2132
1783
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
2133
1784
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
2134
1785
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
2135
1786
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
|
|
2136
1787
|
if (snackbar === true)
|
|
2137
1788
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
2138
|
-
// Send the message to all connected clients
|
|
2139
1789
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2140
1790
|
if (client.readyState === WebSocket.OPEN) {
|
|
2141
1791
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } }));
|
|
2142
1792
|
}
|
|
2143
1793
|
});
|
|
2144
1794
|
}
|
|
2145
|
-
/**
|
|
2146
|
-
* Sends a no need to restart WebSocket message to all connected clients.
|
|
2147
|
-
*
|
|
2148
|
-
* @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients.
|
|
2149
|
-
*/
|
|
2150
1795
|
wssSendRestartNotRequired(snackbar = true) {
|
|
2151
1796
|
this.log.debug('Sending a restart not required message to all connected clients');
|
|
2152
1797
|
this.matterbridge.matterbridgeInformation.restartRequired = false;
|
|
2153
1798
|
if (snackbar === true)
|
|
2154
1799
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2155
|
-
// Send the message to all connected clients
|
|
2156
1800
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2157
1801
|
if (client.readyState === WebSocket.OPEN) {
|
|
2158
1802
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', params: {} }));
|
|
2159
1803
|
}
|
|
2160
1804
|
});
|
|
2161
1805
|
}
|
|
2162
|
-
/**
|
|
2163
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
2164
|
-
*
|
|
2165
|
-
* @param {boolean} devVersion - If true, the update is for a development version.
|
|
2166
|
-
*/
|
|
2167
1806
|
wssSendUpdateRequired(devVersion = false) {
|
|
2168
1807
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2169
1808
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
2170
|
-
// Send the message to all connected clients
|
|
2171
1809
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2172
1810
|
if (client.readyState === WebSocket.OPEN) {
|
|
2173
1811
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required', params: {} }));
|
|
2174
1812
|
}
|
|
2175
1813
|
});
|
|
2176
1814
|
}
|
|
2177
|
-
/**
|
|
2178
|
-
* Sends a cpu update message to all connected clients.
|
|
2179
|
-
*
|
|
2180
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2181
|
-
*/
|
|
2182
1815
|
wssSendCpuUpdate(cpuUsage) {
|
|
2183
1816
|
if (hasParameter('debug'))
|
|
2184
1817
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2185
|
-
// Send the message to all connected clients
|
|
2186
1818
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2187
1819
|
if (client.readyState === WebSocket.OPEN) {
|
|
2188
1820
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
2189
1821
|
}
|
|
2190
1822
|
});
|
|
2191
1823
|
}
|
|
2192
|
-
/**
|
|
2193
|
-
* Sends a memory update message to all connected clients.
|
|
2194
|
-
*
|
|
2195
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2196
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2197
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2198
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2199
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2200
|
-
* @param {string} external - The external memory in bytes.
|
|
2201
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2202
|
-
*/
|
|
2203
1824
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2204
1825
|
if (hasParameter('debug'))
|
|
2205
1826
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2206
|
-
// Send the message to all connected clients
|
|
2207
1827
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2208
1828
|
if (client.readyState === WebSocket.OPEN) {
|
|
2209
1829
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
2210
1830
|
}
|
|
2211
1831
|
});
|
|
2212
1832
|
}
|
|
2213
|
-
/**
|
|
2214
|
-
* Sends an uptime update message to all connected clients.
|
|
2215
|
-
*
|
|
2216
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2217
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2218
|
-
*/
|
|
2219
1833
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2220
1834
|
if (hasParameter('debug'))
|
|
2221
1835
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2222
|
-
// Send the message to all connected clients
|
|
2223
1836
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2224
1837
|
if (client.readyState === WebSocket.OPEN) {
|
|
2225
1838
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
2226
1839
|
}
|
|
2227
1840
|
});
|
|
2228
1841
|
}
|
|
2229
|
-
/**
|
|
2230
|
-
* Sends an open snackbar message to all connected clients.
|
|
2231
|
-
*
|
|
2232
|
-
* @param {string} message - The message to send.
|
|
2233
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
2234
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
2235
|
-
*/
|
|
2236
1842
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2237
1843
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2238
|
-
// Send the message to all connected clients
|
|
2239
1844
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2240
1845
|
if (client.readyState === WebSocket.OPEN) {
|
|
2241
1846
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
2242
1847
|
}
|
|
2243
1848
|
});
|
|
2244
1849
|
}
|
|
2245
|
-
/**
|
|
2246
|
-
* Sends a close snackbar message to all connected clients.
|
|
2247
|
-
*
|
|
2248
|
-
* @param {string} message - The message to send.
|
|
2249
|
-
*/
|
|
2250
1850
|
wssSendCloseSnackbarMessage(message) {
|
|
2251
1851
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2252
|
-
// Send the message to all connected clients
|
|
2253
1852
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2254
1853
|
if (client.readyState === WebSocket.OPEN) {
|
|
2255
1854
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
2256
1855
|
}
|
|
2257
1856
|
});
|
|
2258
1857
|
}
|
|
2259
|
-
/**
|
|
2260
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2261
|
-
*
|
|
2262
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2263
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2264
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2265
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2266
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2267
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2268
|
-
*
|
|
2269
|
-
* @remarks
|
|
2270
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2271
|
-
* with the updated attribute information.
|
|
2272
|
-
*/
|
|
2273
1858
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
2274
1859
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2275
|
-
// Send the message to all connected clients
|
|
2276
1860
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2277
1861
|
if (client.readyState === WebSocket.OPEN) {
|
|
2278
1862
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
2279
1863
|
}
|
|
2280
1864
|
});
|
|
2281
1865
|
}
|
|
2282
|
-
/**
|
|
2283
|
-
* Sends a message to all connected clients.
|
|
2284
|
-
*
|
|
2285
|
-
* @param {number} id - The message id.
|
|
2286
|
-
* @param {string} method - The message method.
|
|
2287
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
2288
|
-
*/
|
|
2289
1866
|
wssBroadcastMessage(id, method, params) {
|
|
2290
1867
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
2291
|
-
// Send the message to all connected clients
|
|
2292
1868
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2293
1869
|
if (client.readyState === WebSocket.OPEN) {
|
|
2294
1870
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -2296,4 +1872,3 @@ export class Frontend extends EventEmitter {
|
|
|
2296
1872
|
});
|
|
2297
1873
|
}
|
|
2298
1874
|
}
|
|
2299
|
-
//# sourceMappingURL=frontend.js.map
|