matterbridge 3.2.5 → 3.2.6-dev-20250903-6ab5022
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +1 -43
- 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/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 -4
- 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 +10 -96
- package/dist/devices/solarPower.js +0 -38
- 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 -113
- package/dist/dgram/mb_coap.js +3 -41
- package/dist/dgram/mb_mdns.js +13 -51
- package/dist/dgram/mdns.js +137 -298
- package/dist/dgram/multicast.js +1 -60
- package/dist/dgram/unicast.js +0 -54
- package/dist/frontend.js +26 -451
- 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 -581
- package/dist/matterbridgeDynamicPlatform.js +0 -36
- package/dist/matterbridgeEndpoint.js +54 -1223
- package/dist/matterbridgeEndpointHelpers.js +12 -345
- package/dist/matterbridgePlatform.js +0 -256
- 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/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 -6
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/css/{main.944b63c3.css → main.a2f4846a.css} +2 -2
- package/frontend/build/static/css/main.a2f4846a.css.map +1 -0
- package/frontend/build/static/js/main.710b8f9f.js +3 -0
- package/frontend/build/static/js/{main.ae006df6.js.LICENSE.txt → main.710b8f9f.js.LICENSE.txt} +11 -0
- package/frontend/build/static/js/{main.ae006df6.js.map → main.710b8f9f.js.map} +1 -1
- package/frontend/package-lock.json +5 -5
- package/frontend/package.json +1 -1
- package/npm-shrinkwrap.json +8 -7
- 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/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 -15
- 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/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 -140
- 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 -288
- package/dist/dgram/mdns.d.ts.map +0 -1
- package/dist/dgram/mdns.js.map +0 -1
- package/dist/dgram/multicast.d.ts +0 -65
- 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 -313
- 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 -462
- 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 -1351
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -710
- 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 -1359
- 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 -331
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -198
- 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 -12
- 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/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.944b63c3.css.map +0 -1
- package/frontend/build/static/js/main.ae006df6.js +0 -115
package/dist/frontend.js
CHANGED
|
@@ -1,126 +1,30 @@
|
|
|
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';
|
|
28
4
|
import path from 'node:path';
|
|
29
5
|
import { existsSync, promises as fs } from 'node:fs';
|
|
30
6
|
import EventEmitter from 'node:events';
|
|
31
|
-
// Third-party modules
|
|
32
7
|
import express from 'express';
|
|
33
8
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
34
9
|
import multer from 'multer';
|
|
35
|
-
// AnsiLogger module
|
|
36
10
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt } from 'node-ansi-logger';
|
|
37
|
-
// @matter
|
|
38
11
|
import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
|
|
39
12
|
import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
|
|
40
|
-
// Matterbridge
|
|
41
13
|
import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout, hasParameter } from './utils/export.js';
|
|
42
14
|
import { plg } from './matterbridgeTypes.js';
|
|
43
15
|
import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
|
|
44
16
|
import { cliEmitter, lastCpuUsage } from './cliEmitter.js';
|
|
45
|
-
/**
|
|
46
|
-
* Websocket message ID for logging.
|
|
47
|
-
*
|
|
48
|
-
* @constant {number}
|
|
49
|
-
*/
|
|
50
17
|
export const WS_ID_LOG = 0;
|
|
51
|
-
/**
|
|
52
|
-
* Websocket message ID indicating a refresh is needed.
|
|
53
|
-
*
|
|
54
|
-
* @constant {number}
|
|
55
|
-
*/
|
|
56
18
|
export const WS_ID_REFRESH_NEEDED = 1;
|
|
57
|
-
/**
|
|
58
|
-
* Websocket message ID indicating a restart is needed.
|
|
59
|
-
*
|
|
60
|
-
* @constant {number}
|
|
61
|
-
*/
|
|
62
19
|
export const WS_ID_RESTART_NEEDED = 2;
|
|
63
|
-
/**
|
|
64
|
-
* Websocket message ID indicating a cpu update.
|
|
65
|
-
*
|
|
66
|
-
* @constant {number}
|
|
67
|
-
*/
|
|
68
20
|
export const WS_ID_CPU_UPDATE = 3;
|
|
69
|
-
/**
|
|
70
|
-
* Websocket message ID indicating a memory update.
|
|
71
|
-
*
|
|
72
|
-
* @constant {number}
|
|
73
|
-
*/
|
|
74
21
|
export const WS_ID_MEMORY_UPDATE = 4;
|
|
75
|
-
/**
|
|
76
|
-
* Websocket message ID indicating an uptime update.
|
|
77
|
-
*
|
|
78
|
-
* @constant {number}
|
|
79
|
-
*/
|
|
80
22
|
export const WS_ID_UPTIME_UPDATE = 5;
|
|
81
|
-
/**
|
|
82
|
-
* Websocket message ID indicating a snackbar message.
|
|
83
|
-
*
|
|
84
|
-
* @constant {number}
|
|
85
|
-
*/
|
|
86
23
|
export const WS_ID_SNACKBAR = 6;
|
|
87
|
-
/**
|
|
88
|
-
* Websocket message ID indicating matterbridge has un update available.
|
|
89
|
-
*
|
|
90
|
-
* @constant {number}
|
|
91
|
-
*/
|
|
92
24
|
export const WS_ID_UPDATE_NEEDED = 7;
|
|
93
|
-
/**
|
|
94
|
-
* Websocket message ID indicating a state update.
|
|
95
|
-
*
|
|
96
|
-
* @constant {number}
|
|
97
|
-
*/
|
|
98
25
|
export const WS_ID_STATEUPDATE = 8;
|
|
99
|
-
/**
|
|
100
|
-
* Websocket message ID indicating to close a permanent snackbar message.
|
|
101
|
-
*
|
|
102
|
-
* @constant {number}
|
|
103
|
-
*/
|
|
104
26
|
export const WS_ID_CLOSE_SNACKBAR = 9;
|
|
105
|
-
/**
|
|
106
|
-
* Websocket message ID indicating a shelly system update.
|
|
107
|
-
* check:
|
|
108
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/check
|
|
109
|
-
* perform:
|
|
110
|
-
* curl -k http://127.0.0.1:8101/api/updates/sys/perform
|
|
111
|
-
*
|
|
112
|
-
* @constant {number}
|
|
113
|
-
*/
|
|
114
27
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
115
|
-
/**
|
|
116
|
-
* Websocket message ID indicating a shelly main update.
|
|
117
|
-
* check:
|
|
118
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/check
|
|
119
|
-
* perform:
|
|
120
|
-
* curl -k http://127.0.0.1:8101/api/updates/main/perform
|
|
121
|
-
*
|
|
122
|
-
* @constant {number}
|
|
123
|
-
*/
|
|
124
28
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
125
29
|
export class Frontend extends EventEmitter {
|
|
126
30
|
matterbridge;
|
|
@@ -133,7 +37,7 @@ export class Frontend extends EventEmitter {
|
|
|
133
37
|
constructor(matterbridge) {
|
|
134
38
|
super();
|
|
135
39
|
this.matterbridge = matterbridge;
|
|
136
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
40
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
137
41
|
}
|
|
138
42
|
set logLevel(logLevel) {
|
|
139
43
|
this.log.logLevel = logLevel;
|
|
@@ -141,39 +45,10 @@ export class Frontend extends EventEmitter {
|
|
|
141
45
|
async start(port = 8283) {
|
|
142
46
|
this.port = port;
|
|
143
47
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
144
|
-
|
|
145
|
-
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads'); // Is created by matterbridge initialize
|
|
48
|
+
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
146
49
|
const upload = multer({ dest: uploadDir });
|
|
147
|
-
// Create the express app that serves the frontend
|
|
148
50
|
this.expressApp = express();
|
|
149
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
150
|
-
/*
|
|
151
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
152
|
-
for (const method of methods) {
|
|
153
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
154
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
155
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
157
|
-
try {
|
|
158
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
159
|
-
return original(path, ...rest);
|
|
160
|
-
} catch (err) {
|
|
161
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
162
|
-
throw err;
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
*/
|
|
167
|
-
// Log all requests to the server for debugging
|
|
168
|
-
/*
|
|
169
|
-
this.expressApp.use((req, res, next) => {
|
|
170
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
171
|
-
next();
|
|
172
|
-
});
|
|
173
|
-
*/
|
|
174
|
-
// Serve static files from '/static' endpoint
|
|
175
51
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
176
|
-
// Read the package.json file to get the frontend version
|
|
177
52
|
try {
|
|
178
53
|
this.log.debug(`Reading frontend package.json...`);
|
|
179
54
|
const frontendJson = await fs.readFile(path.join(this.matterbridge.rootDirectory, 'frontend/package.json'), 'utf8');
|
|
@@ -181,11 +56,9 @@ export class Frontend extends EventEmitter {
|
|
|
181
56
|
this.log.debug(`Frontend version ${CYAN}${this.matterbridge.matterbridgeInformation.frontendVersion}${db}`);
|
|
182
57
|
}
|
|
183
58
|
catch (error) {
|
|
184
|
-
// istanbul ignore next
|
|
185
59
|
this.log.error(`Failed to read frontend package.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
186
60
|
}
|
|
187
61
|
if (!hasParameter('ssl')) {
|
|
188
|
-
// Create an HTTP server and attach the express app
|
|
189
62
|
try {
|
|
190
63
|
this.log.debug(`Creating HTTP server...`);
|
|
191
64
|
this.httpServer = createServer(this.expressApp);
|
|
@@ -195,7 +68,6 @@ export class Frontend extends EventEmitter {
|
|
|
195
68
|
this.emit('server_error', error);
|
|
196
69
|
return;
|
|
197
70
|
}
|
|
198
|
-
// Listen on the specified port
|
|
199
71
|
if (hasParameter('ingress')) {
|
|
200
72
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
201
73
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -234,7 +106,6 @@ export class Frontend extends EventEmitter {
|
|
|
234
106
|
let passphrase;
|
|
235
107
|
let httpsServerOptions = {};
|
|
236
108
|
if (existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
|
|
237
|
-
// Load the p12 certificate and the passphrase
|
|
238
109
|
try {
|
|
239
110
|
pfx = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
|
|
240
111
|
this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
|
|
@@ -246,7 +117,7 @@ export class Frontend extends EventEmitter {
|
|
|
246
117
|
}
|
|
247
118
|
try {
|
|
248
119
|
passphrase = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
|
|
249
|
-
passphrase = passphrase.trim();
|
|
120
|
+
passphrase = passphrase.trim();
|
|
250
121
|
this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
|
|
251
122
|
}
|
|
252
123
|
catch (error) {
|
|
@@ -257,7 +128,6 @@ export class Frontend extends EventEmitter {
|
|
|
257
128
|
httpsServerOptions = { pfx, passphrase };
|
|
258
129
|
}
|
|
259
130
|
else {
|
|
260
|
-
// 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.
|
|
261
131
|
try {
|
|
262
132
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
263
133
|
this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
|
|
@@ -287,10 +157,9 @@ export class Frontend extends EventEmitter {
|
|
|
287
157
|
httpsServerOptions = { cert: fullChain ?? cert, key, ca };
|
|
288
158
|
}
|
|
289
159
|
if (hasParameter('mtls')) {
|
|
290
|
-
httpsServerOptions.requestCert = true;
|
|
291
|
-
httpsServerOptions.rejectUnauthorized = true;
|
|
160
|
+
httpsServerOptions.requestCert = true;
|
|
161
|
+
httpsServerOptions.rejectUnauthorized = true;
|
|
292
162
|
}
|
|
293
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
294
163
|
try {
|
|
295
164
|
this.log.debug(`Creating HTTPS server...`);
|
|
296
165
|
this.httpsServer = https.createServer(httpsServerOptions, this.expressApp);
|
|
@@ -300,7 +169,6 @@ export class Frontend extends EventEmitter {
|
|
|
300
169
|
this.emit('server_error', error);
|
|
301
170
|
return;
|
|
302
171
|
}
|
|
303
|
-
// Listen on the specified port
|
|
304
172
|
if (hasParameter('ingress')) {
|
|
305
173
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
306
174
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -330,19 +198,17 @@ export class Frontend extends EventEmitter {
|
|
|
330
198
|
return;
|
|
331
199
|
});
|
|
332
200
|
}
|
|
333
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
334
201
|
const wssPort = this.port;
|
|
335
202
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
336
203
|
this.log.debug(`Creating WebSocketServer on host ${CYAN}${wssHost}${db}...`);
|
|
337
204
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
338
205
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
339
206
|
const clientIp = request.socket.remoteAddress;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
207
|
+
let callbackLogLevel = "notice";
|
|
208
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
209
|
+
callbackLogLevel = "info";
|
|
210
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
211
|
+
callbackLogLevel = "debug";
|
|
346
212
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
347
213
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
348
214
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -364,7 +230,6 @@ export class Frontend extends EventEmitter {
|
|
|
364
230
|
}
|
|
365
231
|
});
|
|
366
232
|
ws.on('error', (error) => {
|
|
367
|
-
// istanbul ignore next
|
|
368
233
|
this.log.error(`WebSocket client error: ${error}`);
|
|
369
234
|
});
|
|
370
235
|
});
|
|
@@ -378,7 +243,6 @@ export class Frontend extends EventEmitter {
|
|
|
378
243
|
this.webSocketServer.on('error', (ws, error) => {
|
|
379
244
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
380
245
|
});
|
|
381
|
-
// Subscribe to cli events
|
|
382
246
|
cliEmitter.removeAllListeners();
|
|
383
247
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
384
248
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -389,8 +253,6 @@ export class Frontend extends EventEmitter {
|
|
|
389
253
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
390
254
|
this.wssSendCpuUpdate(cpuUsage);
|
|
391
255
|
});
|
|
392
|
-
// Endpoint to validate login code
|
|
393
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
394
256
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
395
257
|
const { password } = req.body;
|
|
396
258
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -409,27 +271,23 @@ export class Frontend extends EventEmitter {
|
|
|
409
271
|
this.log.warn('/api/login error wrong password');
|
|
410
272
|
res.json({ valid: false });
|
|
411
273
|
}
|
|
412
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
413
274
|
}
|
|
414
275
|
catch (error) {
|
|
415
276
|
this.log.error('/api/login error getting password');
|
|
416
277
|
res.json({ valid: false });
|
|
417
278
|
}
|
|
418
279
|
});
|
|
419
|
-
// Endpoint to provide health check for docker
|
|
420
280
|
this.expressApp.get('/health', (req, res) => {
|
|
421
281
|
this.log.debug('Express received /health');
|
|
422
282
|
const healthStatus = {
|
|
423
|
-
status: 'ok',
|
|
424
|
-
uptime: process.uptime(),
|
|
425
|
-
timestamp: new Date().toISOString(),
|
|
283
|
+
status: 'ok',
|
|
284
|
+
uptime: process.uptime(),
|
|
285
|
+
timestamp: new Date().toISOString(),
|
|
426
286
|
};
|
|
427
287
|
res.status(200).json(healthStatus);
|
|
428
288
|
});
|
|
429
|
-
// Endpoint to provide memory usage details
|
|
430
289
|
this.expressApp.get('/memory', async (req, res) => {
|
|
431
290
|
this.log.debug('Express received /memory');
|
|
432
|
-
// Memory usage from process
|
|
433
291
|
const memoryUsageRaw = process.memoryUsage();
|
|
434
292
|
const memoryUsage = {
|
|
435
293
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -438,13 +296,10 @@ export class Frontend extends EventEmitter {
|
|
|
438
296
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
439
297
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
440
298
|
};
|
|
441
|
-
// V8 heap statistics
|
|
442
299
|
const { default: v8 } = await import('node:v8');
|
|
443
300
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
444
301
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
445
|
-
// Format heapStats
|
|
446
302
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
447
|
-
// Format heapSpaces
|
|
448
303
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
449
304
|
...space,
|
|
450
305
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -462,23 +317,19 @@ export class Frontend extends EventEmitter {
|
|
|
462
317
|
};
|
|
463
318
|
res.status(200).json(memoryReport);
|
|
464
319
|
});
|
|
465
|
-
// Endpoint to provide settings
|
|
466
320
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
467
321
|
this.log.debug('The frontend sent /api/settings');
|
|
468
322
|
res.json(await this.getApiSettings());
|
|
469
323
|
});
|
|
470
|
-
// Endpoint to provide plugins
|
|
471
324
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
472
325
|
this.log.debug('The frontend sent /api/plugins');
|
|
473
326
|
res.json(this.getPlugins());
|
|
474
327
|
});
|
|
475
|
-
// Endpoint to provide devices
|
|
476
328
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
477
329
|
this.log.debug('The frontend sent /api/devices');
|
|
478
330
|
const devices = await this.getDevices();
|
|
479
331
|
res.json(devices);
|
|
480
332
|
});
|
|
481
|
-
// Endpoint to view the matterbridge log
|
|
482
333
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
483
334
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
484
335
|
try {
|
|
@@ -491,7 +342,6 @@ export class Frontend extends EventEmitter {
|
|
|
491
342
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
492
343
|
}
|
|
493
344
|
});
|
|
494
|
-
// Endpoint to view the matter.js log
|
|
495
345
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
496
346
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
497
347
|
try {
|
|
@@ -504,7 +354,6 @@ export class Frontend extends EventEmitter {
|
|
|
504
354
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
505
355
|
}
|
|
506
356
|
});
|
|
507
|
-
// Endpoint to view the shelly log
|
|
508
357
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
509
358
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
510
359
|
try {
|
|
@@ -517,7 +366,6 @@ export class Frontend extends EventEmitter {
|
|
|
517
366
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
518
367
|
}
|
|
519
368
|
});
|
|
520
|
-
// Endpoint to download the matterbridge log
|
|
521
369
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
522
370
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
523
371
|
try {
|
|
@@ -531,14 +379,12 @@ export class Frontend extends EventEmitter {
|
|
|
531
379
|
}
|
|
532
380
|
res.type('text/plain');
|
|
533
381
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'matterbridge.log', (error) => {
|
|
534
|
-
/* istanbul ignore if */
|
|
535
382
|
if (error) {
|
|
536
383
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
537
384
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
538
385
|
}
|
|
539
386
|
});
|
|
540
387
|
});
|
|
541
|
-
// Endpoint to download the matter log
|
|
542
388
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
543
389
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
544
390
|
try {
|
|
@@ -552,14 +398,12 @@ export class Frontend extends EventEmitter {
|
|
|
552
398
|
}
|
|
553
399
|
res.type('text/plain');
|
|
554
400
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
|
|
555
|
-
/* istanbul ignore if */
|
|
556
401
|
if (error) {
|
|
557
402
|
this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
558
403
|
res.status(500).send('Error downloading the matter log file');
|
|
559
404
|
}
|
|
560
405
|
});
|
|
561
406
|
});
|
|
562
|
-
// Endpoint to download the shelly log
|
|
563
407
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
564
408
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
565
409
|
try {
|
|
@@ -573,90 +417,74 @@ export class Frontend extends EventEmitter {
|
|
|
573
417
|
}
|
|
574
418
|
res.type('text/plain');
|
|
575
419
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
576
|
-
/* istanbul ignore if */
|
|
577
420
|
if (error) {
|
|
578
421
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
579
422
|
res.status(500).send('Error downloading Shelly system log file');
|
|
580
423
|
}
|
|
581
424
|
});
|
|
582
425
|
});
|
|
583
|
-
// Endpoint to download the matterbridge storage directory
|
|
584
426
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
585
427
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
586
428
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
587
429
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), `matterbridge.${this.matterbridge.nodeStorageName}.zip`, (error) => {
|
|
588
|
-
/* istanbul ignore if */
|
|
589
430
|
if (error) {
|
|
590
431
|
this.log.error(`Error downloading file ${`matterbridge.${this.matterbridge.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
591
432
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
592
433
|
}
|
|
593
434
|
});
|
|
594
435
|
});
|
|
595
|
-
// Endpoint to download the matter storage file
|
|
596
436
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
597
437
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
598
438
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
599
439
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), `matterbridge.${this.matterbridge.matterStorageName}.zip`, (error) => {
|
|
600
|
-
/* istanbul ignore if */
|
|
601
440
|
if (error) {
|
|
602
441
|
this.log.error(`Error downloading the matter storage matterbridge.${this.matterbridge.matterStorageName}.zip: ${error instanceof Error ? error.message : error}`);
|
|
603
442
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
604
443
|
}
|
|
605
444
|
});
|
|
606
445
|
});
|
|
607
|
-
// Endpoint to download the matterbridge plugin directory
|
|
608
446
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
609
447
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
610
448
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
611
449
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
612
|
-
/* istanbul ignore if */
|
|
613
450
|
if (error) {
|
|
614
451
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
615
452
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
616
453
|
}
|
|
617
454
|
});
|
|
618
455
|
});
|
|
619
|
-
// Endpoint to download the matterbridge plugin config files
|
|
620
456
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
621
457
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
622
458
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
623
459
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
624
|
-
/* istanbul ignore if */
|
|
625
460
|
if (error) {
|
|
626
461
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
627
462
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
628
463
|
}
|
|
629
464
|
});
|
|
630
465
|
});
|
|
631
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
632
466
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
633
467
|
this.log.debug('The frontend sent /api/download-backup');
|
|
634
468
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
635
|
-
/* istanbul ignore if */
|
|
636
469
|
if (error) {
|
|
637
470
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
638
471
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
639
472
|
}
|
|
640
473
|
});
|
|
641
474
|
});
|
|
642
|
-
// Endpoint to upload a package
|
|
643
475
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
644
476
|
const { filename } = req.body;
|
|
645
477
|
const file = req.file;
|
|
646
|
-
/* istanbul ignore if */
|
|
647
478
|
if (!file || !filename) {
|
|
648
479
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
649
480
|
res.status(400).send('Invalid request: file and filename are required');
|
|
650
481
|
return;
|
|
651
482
|
}
|
|
652
483
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
653
|
-
// Define the path where the plugin file will be saved
|
|
654
484
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
655
485
|
try {
|
|
656
|
-
// Move the uploaded file to the specified path
|
|
657
486
|
await fs.rename(file.path, filePath);
|
|
658
487
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
659
|
-
// Install the plugin package
|
|
660
488
|
if (filename.endsWith('.tgz')) {
|
|
661
489
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
662
490
|
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
@@ -676,7 +504,6 @@ export class Frontend extends EventEmitter {
|
|
|
676
504
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
677
505
|
}
|
|
678
506
|
});
|
|
679
|
-
// Fallback for routing (must be the last route)
|
|
680
507
|
this.expressApp.use((req, res) => {
|
|
681
508
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
|
|
682
509
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -685,16 +512,13 @@ export class Frontend extends EventEmitter {
|
|
|
685
512
|
}
|
|
686
513
|
async stop() {
|
|
687
514
|
this.log.debug('Stopping the frontend...');
|
|
688
|
-
// Remove listeners from the express app
|
|
689
515
|
if (this.expressApp) {
|
|
690
516
|
this.expressApp.removeAllListeners();
|
|
691
517
|
this.expressApp = undefined;
|
|
692
518
|
this.log.debug('Frontend app closed successfully');
|
|
693
519
|
}
|
|
694
|
-
// Close the WebSocket server
|
|
695
520
|
if (this.webSocketServer) {
|
|
696
521
|
this.log.debug('Closing WebSocket server...');
|
|
697
|
-
// Close all active connections
|
|
698
522
|
this.webSocketServer.clients.forEach((client) => {
|
|
699
523
|
if (client.readyState === WebSocket.OPEN) {
|
|
700
524
|
client.close();
|
|
@@ -703,7 +527,6 @@ export class Frontend extends EventEmitter {
|
|
|
703
527
|
await withTimeout(new Promise((resolve) => {
|
|
704
528
|
this.webSocketServer?.close((error) => {
|
|
705
529
|
if (error) {
|
|
706
|
-
// istanbul ignore next
|
|
707
530
|
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
708
531
|
}
|
|
709
532
|
else {
|
|
@@ -716,13 +539,11 @@ export class Frontend extends EventEmitter {
|
|
|
716
539
|
this.webSocketServer.removeAllListeners();
|
|
717
540
|
this.webSocketServer = undefined;
|
|
718
541
|
}
|
|
719
|
-
// Close the http server
|
|
720
542
|
if (this.httpServer) {
|
|
721
543
|
this.log.debug('Closing http server...');
|
|
722
544
|
await withTimeout(new Promise((resolve) => {
|
|
723
545
|
this.httpServer?.close((error) => {
|
|
724
546
|
if (error) {
|
|
725
|
-
// istanbul ignore next
|
|
726
547
|
this.log.error(`Error closing http server: ${error}`);
|
|
727
548
|
}
|
|
728
549
|
else {
|
|
@@ -736,13 +557,11 @@ export class Frontend extends EventEmitter {
|
|
|
736
557
|
this.httpServer = undefined;
|
|
737
558
|
this.log.debug('Frontend http server closed successfully');
|
|
738
559
|
}
|
|
739
|
-
// Close the https server
|
|
740
560
|
if (this.httpsServer) {
|
|
741
561
|
this.log.debug('Closing https server...');
|
|
742
562
|
await withTimeout(new Promise((resolve) => {
|
|
743
563
|
this.httpsServer?.close((error) => {
|
|
744
564
|
if (error) {
|
|
745
|
-
// istanbul ignore next
|
|
746
565
|
this.log.error(`Error closing https server: ${error}`);
|
|
747
566
|
}
|
|
748
567
|
else {
|
|
@@ -758,7 +577,6 @@ export class Frontend extends EventEmitter {
|
|
|
758
577
|
}
|
|
759
578
|
this.log.debug('Frontend stopped successfully');
|
|
760
579
|
}
|
|
761
|
-
// Function to format bytes to KB, MB, or GB
|
|
762
580
|
formatMemoryUsage = (bytes) => {
|
|
763
581
|
if (bytes >= 1024 ** 3) {
|
|
764
582
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -770,7 +588,6 @@ export class Frontend extends EventEmitter {
|
|
|
770
588
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
771
589
|
}
|
|
772
590
|
};
|
|
773
|
-
// Function to format system uptime with only the most significant unit
|
|
774
591
|
formatOsUpTime = (seconds) => {
|
|
775
592
|
if (seconds >= 86400) {
|
|
776
593
|
const days = Math.floor(seconds / 86400);
|
|
@@ -786,13 +603,7 @@ export class Frontend extends EventEmitter {
|
|
|
786
603
|
}
|
|
787
604
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
788
605
|
};
|
|
789
|
-
/**
|
|
790
|
-
* Retrieves the api settings data.
|
|
791
|
-
*
|
|
792
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
793
|
-
*/
|
|
794
606
|
async getApiSettings() {
|
|
795
|
-
// Update the system information
|
|
796
607
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
797
608
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
798
609
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -801,7 +612,6 @@ export class Frontend extends EventEmitter {
|
|
|
801
612
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
802
613
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
803
614
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
804
|
-
// Update the matterbridge information
|
|
805
615
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
806
616
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
807
617
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
@@ -813,7 +623,6 @@ export class Frontend extends EventEmitter {
|
|
|
813
623
|
this.matterbridge.matterbridgeInformation.matterPort = (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540;
|
|
814
624
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = await this.matterbridge.nodeContext?.get('matterdiscriminator');
|
|
815
625
|
this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
|
|
816
|
-
// Update the matterbridge information in bridge mode
|
|
817
626
|
if (this.matterbridge.bridgeMode === 'bridge' && this.matterbridge.serverNode && !this.matterbridge.hasCleanupStarted) {
|
|
818
627
|
this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.serverNode.state.commissioning.commissioned;
|
|
819
628
|
this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.qrPairingCode;
|
|
@@ -823,12 +632,6 @@ export class Frontend extends EventEmitter {
|
|
|
823
632
|
}
|
|
824
633
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
825
634
|
}
|
|
826
|
-
/**
|
|
827
|
-
* Retrieves the reachable attribute.
|
|
828
|
-
*
|
|
829
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
830
|
-
* @returns {boolean} The reachable attribute.
|
|
831
|
-
*/
|
|
832
635
|
getReachability(device) {
|
|
833
636
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
834
637
|
return false;
|
|
@@ -840,12 +643,6 @@ export class Frontend extends EventEmitter {
|
|
|
840
643
|
return true;
|
|
841
644
|
return false;
|
|
842
645
|
}
|
|
843
|
-
/**
|
|
844
|
-
* Retrieves the power source attribute.
|
|
845
|
-
*
|
|
846
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
847
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
848
|
-
*/
|
|
849
646
|
getPowerSource(endpoint) {
|
|
850
647
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
851
648
|
return undefined;
|
|
@@ -861,21 +658,13 @@ export class Frontend extends EventEmitter {
|
|
|
861
658
|
}
|
|
862
659
|
return;
|
|
863
660
|
};
|
|
864
|
-
// Root endpoint
|
|
865
661
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
866
662
|
return powerSource(endpoint);
|
|
867
|
-
// Child endpoints
|
|
868
663
|
for (const child of endpoint.getChildEndpoints()) {
|
|
869
664
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
870
665
|
return powerSource(child);
|
|
871
666
|
}
|
|
872
667
|
}
|
|
873
|
-
/**
|
|
874
|
-
* Retrieves the commissioned status, matter pairing codes, fabrics and sessions from a given device in server mode.
|
|
875
|
-
*
|
|
876
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the data from.
|
|
877
|
-
* @returns {ApiDevicesMatter | undefined} An ApiDevicesMatter object or undefined if not found.
|
|
878
|
-
*/
|
|
879
668
|
getMatterDataFromDevice(device) {
|
|
880
669
|
if (device.mode === 'server' && device.serverNode) {
|
|
881
670
|
return {
|
|
@@ -887,13 +676,6 @@ export class Frontend extends EventEmitter {
|
|
|
887
676
|
};
|
|
888
677
|
}
|
|
889
678
|
}
|
|
890
|
-
/**
|
|
891
|
-
* Retrieves the cluster text description from a given device.
|
|
892
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
893
|
-
*
|
|
894
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
895
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
896
|
-
*/
|
|
897
679
|
getClusterTextFromDevice(device) {
|
|
898
680
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
899
681
|
return '';
|
|
@@ -904,7 +686,6 @@ export class Frontend extends EventEmitter {
|
|
|
904
686
|
if (composed)
|
|
905
687
|
return 'Composed: ' + composed.value;
|
|
906
688
|
}
|
|
907
|
-
// istanbul ignore next cause is not reachable
|
|
908
689
|
return '';
|
|
909
690
|
};
|
|
910
691
|
const getFixedLabel = (device) => {
|
|
@@ -914,13 +695,11 @@ export class Frontend extends EventEmitter {
|
|
|
914
695
|
if (composed)
|
|
915
696
|
return 'Composed: ' + composed.value;
|
|
916
697
|
}
|
|
917
|
-
// istanbul ignore next cause is not reacheable
|
|
918
698
|
return '';
|
|
919
699
|
};
|
|
920
700
|
let attributes = '';
|
|
921
701
|
let supportedModes = [];
|
|
922
702
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
923
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
924
703
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
925
704
|
return;
|
|
926
705
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1010,17 +789,11 @@ export class Frontend extends EventEmitter {
|
|
|
1010
789
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1011
790
|
attributes += `${getUserLabel(device)} `;
|
|
1012
791
|
});
|
|
1013
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1014
792
|
return attributes.trimStart().trimEnd();
|
|
1015
793
|
}
|
|
1016
|
-
/**
|
|
1017
|
-
* Retrieves the registered plugins sanitized for res.json().
|
|
1018
|
-
*
|
|
1019
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
1020
|
-
*/
|
|
1021
794
|
getPlugins() {
|
|
1022
795
|
if (this.matterbridge.hasCleanupStarted)
|
|
1023
|
-
return [];
|
|
796
|
+
return [];
|
|
1024
797
|
const baseRegisteredPlugins = [];
|
|
1025
798
|
for (const plugin of this.matterbridge.plugins) {
|
|
1026
799
|
baseRegisteredPlugins.push({
|
|
@@ -1050,7 +823,6 @@ export class Frontend extends EventEmitter {
|
|
|
1050
823
|
schemaJson: plugin.schemaJson,
|
|
1051
824
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1052
825
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1053
|
-
// Childbridge mode specific data
|
|
1054
826
|
paired: plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.commissioned : undefined,
|
|
1055
827
|
qrPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.pairingCodes.qrPairingCode : undefined,
|
|
1056
828
|
manualPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.pairingCodes.manualPairingCode : undefined,
|
|
@@ -1060,21 +832,13 @@ export class Frontend extends EventEmitter {
|
|
|
1060
832
|
}
|
|
1061
833
|
return baseRegisteredPlugins;
|
|
1062
834
|
}
|
|
1063
|
-
/**
|
|
1064
|
-
* Retrieves the devices from Matterbridge.
|
|
1065
|
-
*
|
|
1066
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1067
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
|
|
1068
|
-
*/
|
|
1069
835
|
async getDevices(pluginName) {
|
|
1070
836
|
if (this.matterbridge.hasCleanupStarted)
|
|
1071
|
-
return [];
|
|
837
|
+
return [];
|
|
1072
838
|
const devices = [];
|
|
1073
839
|
for (const device of this.matterbridge.devices.array()) {
|
|
1074
|
-
// Filter by pluginName if provided
|
|
1075
840
|
if (pluginName && pluginName !== device.plugin)
|
|
1076
841
|
continue;
|
|
1077
|
-
// Check if the device has the required properties
|
|
1078
842
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1079
843
|
continue;
|
|
1080
844
|
devices.push({
|
|
@@ -1094,37 +858,22 @@ export class Frontend extends EventEmitter {
|
|
|
1094
858
|
}
|
|
1095
859
|
return devices;
|
|
1096
860
|
}
|
|
1097
|
-
/**
|
|
1098
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1099
|
-
*
|
|
1100
|
-
* Response for /api/clusters
|
|
1101
|
-
*
|
|
1102
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1103
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1104
|
-
* @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1105
|
-
*/
|
|
1106
861
|
getClusters(pluginName, endpointNumber) {
|
|
1107
862
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1108
863
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1109
864
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1110
865
|
return;
|
|
1111
866
|
}
|
|
1112
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1113
|
-
// Get the device types from the main endpoint
|
|
1114
867
|
const deviceTypes = [];
|
|
1115
868
|
const clusters = [];
|
|
1116
869
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1117
870
|
deviceTypes.push(d.deviceType);
|
|
1118
871
|
});
|
|
1119
|
-
// Get the clusters from the main endpoint
|
|
1120
872
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1121
873
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1122
874
|
return;
|
|
1123
875
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1124
876
|
return;
|
|
1125
|
-
// console.log(
|
|
1126
|
-
// `${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}`,
|
|
1127
|
-
// );
|
|
1128
877
|
clusters.push({
|
|
1129
878
|
endpoint: endpoint.number.toString(),
|
|
1130
879
|
id: 'main',
|
|
@@ -1137,19 +886,12 @@ export class Frontend extends EventEmitter {
|
|
|
1137
886
|
attributeLocalValue: attributeValue,
|
|
1138
887
|
});
|
|
1139
888
|
});
|
|
1140
|
-
// Get the child endpoints
|
|
1141
889
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1142
|
-
// if (childEndpoints.length === 0) {
|
|
1143
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1144
|
-
// }
|
|
1145
890
|
childEndpoints.forEach((childEndpoint) => {
|
|
1146
|
-
// istanbul ignore if cause is not reachable: should never happen but ...
|
|
1147
891
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1148
892
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1149
893
|
return;
|
|
1150
894
|
}
|
|
1151
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1152
|
-
// Get the device types of the child endpoint
|
|
1153
895
|
const deviceTypes = [];
|
|
1154
896
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1155
897
|
deviceTypes.push(d.deviceType);
|
|
@@ -1159,12 +901,9 @@ export class Frontend extends EventEmitter {
|
|
|
1159
901
|
return;
|
|
1160
902
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1161
903
|
return;
|
|
1162
|
-
// console.log(
|
|
1163
|
-
// `${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}`,
|
|
1164
|
-
// );
|
|
1165
904
|
clusters.push({
|
|
1166
905
|
endpoint: childEndpoint.number.toString(),
|
|
1167
|
-
id: childEndpoint.maybeId ?? 'null',
|
|
906
|
+
id: childEndpoint.maybeId ?? 'null',
|
|
1168
907
|
deviceTypes,
|
|
1169
908
|
clusterName: capitalizeFirstLetter(clusterName),
|
|
1170
909
|
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
@@ -1177,13 +916,6 @@ export class Frontend extends EventEmitter {
|
|
|
1177
916
|
});
|
|
1178
917
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
|
|
1179
918
|
}
|
|
1180
|
-
/**
|
|
1181
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1182
|
-
*
|
|
1183
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1184
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1185
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1186
|
-
*/
|
|
1187
919
|
async wsMessageHandler(client, message) {
|
|
1188
920
|
let data;
|
|
1189
921
|
try {
|
|
@@ -1230,45 +962,35 @@ export class Frontend extends EventEmitter {
|
|
|
1230
962
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1231
963
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1232
964
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1233
|
-
// The install comes from InstallPlugins
|
|
1234
965
|
this.matterbridge.plugins
|
|
1235
966
|
.add(packageName)
|
|
1236
967
|
.then((plugin) => {
|
|
1237
|
-
// istanbul ignore next if
|
|
1238
968
|
if (plugin) {
|
|
1239
|
-
// The plugin is not registered
|
|
1240
969
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
970
|
+
if (this.matterbridge.bridgeMode === 'childbridge')
|
|
971
|
+
this.wssSendRestartRequired(true, true);
|
|
1241
972
|
this.matterbridge.plugins
|
|
1242
973
|
.load(plugin, true, 'The plugin has been added', true)
|
|
1243
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1244
974
|
.then(() => {
|
|
1245
975
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
1246
976
|
this.wssSendRefreshRequired('plugins');
|
|
1247
977
|
return;
|
|
1248
978
|
})
|
|
1249
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1250
979
|
.catch((_error) => {
|
|
1251
|
-
//
|
|
1252
980
|
});
|
|
1253
981
|
}
|
|
1254
982
|
else {
|
|
1255
|
-
// The plugin is already registered
|
|
1256
983
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1257
984
|
this.wssSendRefreshRequired('plugins');
|
|
1258
985
|
this.wssSendRestartRequired(true, true);
|
|
1259
986
|
}
|
|
1260
987
|
return;
|
|
1261
988
|
})
|
|
1262
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1263
989
|
.catch((_error) => {
|
|
1264
|
-
//
|
|
1265
990
|
});
|
|
1266
991
|
}
|
|
1267
992
|
else {
|
|
1268
|
-
// The package is matterbridge
|
|
1269
|
-
// istanbul ignore next
|
|
1270
993
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1271
|
-
// istanbul ignore next if
|
|
1272
994
|
if (this.matterbridge.restartMode !== '') {
|
|
1273
995
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1274
996
|
this.matterbridge.shutdownProcess();
|
|
@@ -1290,9 +1012,7 @@ export class Frontend extends EventEmitter {
|
|
|
1290
1012
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1291
1013
|
return;
|
|
1292
1014
|
}
|
|
1293
|
-
// The package is a plugin
|
|
1294
1015
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1295
|
-
// istanbul ignore next if
|
|
1296
1016
|
if (plugin) {
|
|
1297
1017
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
1298
1018
|
await this.matterbridge.plugins.remove(data.params.packageName);
|
|
@@ -1300,7 +1020,6 @@ export class Frontend extends EventEmitter {
|
|
|
1300
1020
|
this.wssSendRefreshRequired('plugins');
|
|
1301
1021
|
this.wssSendRefreshRequired('devices');
|
|
1302
1022
|
}
|
|
1303
|
-
// Uninstall the package
|
|
1304
1023
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1305
1024
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
1306
1025
|
spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1341,7 +1060,6 @@ export class Frontend extends EventEmitter {
|
|
|
1341
1060
|
return;
|
|
1342
1061
|
})
|
|
1343
1062
|
.catch((_error) => {
|
|
1344
|
-
//
|
|
1345
1063
|
});
|
|
1346
1064
|
}
|
|
1347
1065
|
else {
|
|
@@ -1388,7 +1106,6 @@ export class Frontend extends EventEmitter {
|
|
|
1388
1106
|
return;
|
|
1389
1107
|
})
|
|
1390
1108
|
.catch((_error) => {
|
|
1391
|
-
//
|
|
1392
1109
|
});
|
|
1393
1110
|
}
|
|
1394
1111
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1414,7 +1131,6 @@ export class Frontend extends EventEmitter {
|
|
|
1414
1131
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1415
1132
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
|
|
1416
1133
|
if (plugin.serverNode) {
|
|
1417
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1418
1134
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1419
1135
|
plugin.serverNode = undefined;
|
|
1420
1136
|
}
|
|
@@ -1425,16 +1141,15 @@ export class Frontend extends EventEmitter {
|
|
|
1425
1141
|
}
|
|
1426
1142
|
}
|
|
1427
1143
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
|
|
1428
|
-
plugin.restartRequired = false;
|
|
1144
|
+
plugin.restartRequired = false;
|
|
1429
1145
|
let needRestart = 0;
|
|
1430
1146
|
for (const plugin of this.matterbridge.plugins) {
|
|
1431
1147
|
if (plugin.restartRequired)
|
|
1432
1148
|
needRestart++;
|
|
1433
1149
|
}
|
|
1434
1150
|
if (needRestart === 0) {
|
|
1435
|
-
this.wssSendRestartNotRequired(true);
|
|
1151
|
+
this.wssSendRestartNotRequired(true);
|
|
1436
1152
|
}
|
|
1437
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1438
1153
|
if (plugin.serverNode)
|
|
1439
1154
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1440
1155
|
this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
|
|
@@ -1540,8 +1255,6 @@ export class Frontend extends EventEmitter {
|
|
|
1540
1255
|
else if (data.method === '/api/advertise') {
|
|
1541
1256
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
1542
1257
|
this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
|
|
1543
|
-
// this.matterbridge.matterbridgeQrPairingCode = pairingCodes?.qrPairingCode;
|
|
1544
|
-
// this.matterbridge.matterbridgeManualPairingCode = pairingCodes?.manualPairingCode;
|
|
1545
1258
|
this.wssSendRefreshRequired('matterbridgeAdvertise');
|
|
1546
1259
|
this.wssSendSnackbarMessage(`Started fabrics share`, 0);
|
|
1547
1260
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes, success: true }));
|
|
@@ -1664,22 +1377,22 @@ export class Frontend extends EventEmitter {
|
|
|
1664
1377
|
if (isValidString(data.params.value, 4)) {
|
|
1665
1378
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1666
1379
|
if (data.params.value === 'Debug') {
|
|
1667
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1380
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1668
1381
|
}
|
|
1669
1382
|
else if (data.params.value === 'Info') {
|
|
1670
|
-
await this.matterbridge.setLogLevel("info"
|
|
1383
|
+
await this.matterbridge.setLogLevel("info");
|
|
1671
1384
|
}
|
|
1672
1385
|
else if (data.params.value === 'Notice') {
|
|
1673
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1386
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1674
1387
|
}
|
|
1675
1388
|
else if (data.params.value === 'Warn') {
|
|
1676
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1389
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1677
1390
|
}
|
|
1678
1391
|
else if (data.params.value === 'Error') {
|
|
1679
|
-
await this.matterbridge.setLogLevel("error"
|
|
1392
|
+
await this.matterbridge.setLogLevel("error");
|
|
1680
1393
|
}
|
|
1681
1394
|
else if (data.params.value === 'Fatal') {
|
|
1682
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1395
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1683
1396
|
}
|
|
1684
1397
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1685
1398
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1690,7 +1403,6 @@ export class Frontend extends EventEmitter {
|
|
|
1690
1403
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1691
1404
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1692
1405
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1693
|
-
// Create the file logger for matterbridge
|
|
1694
1406
|
if (data.params.value)
|
|
1695
1407
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1696
1408
|
else
|
|
@@ -1737,7 +1449,6 @@ export class Frontend extends EventEmitter {
|
|
|
1737
1449
|
});
|
|
1738
1450
|
}
|
|
1739
1451
|
catch (error) {
|
|
1740
|
-
/* istanbul ignore next */
|
|
1741
1452
|
this.log.debug(`Error adding the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1742
1453
|
}
|
|
1743
1454
|
}
|
|
@@ -1746,7 +1457,6 @@ export class Frontend extends EventEmitter {
|
|
|
1746
1457
|
Logger.removeLogger('matterfilelogger');
|
|
1747
1458
|
}
|
|
1748
1459
|
catch (error) {
|
|
1749
|
-
/* istanbul ignore next */
|
|
1750
1460
|
this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1751
1461
|
}
|
|
1752
1462
|
}
|
|
@@ -1859,19 +1569,15 @@ export class Frontend extends EventEmitter {
|
|
|
1859
1569
|
return;
|
|
1860
1570
|
}
|
|
1861
1571
|
const config = plugin.configJson;
|
|
1862
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1863
1572
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1864
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1865
1573
|
if (select === 'serial')
|
|
1866
1574
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1867
1575
|
if (select === 'name')
|
|
1868
1576
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1869
1577
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1870
|
-
// Remove postfix from the serial if it exists
|
|
1871
1578
|
if (config.postfix) {
|
|
1872
1579
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1873
1580
|
}
|
|
1874
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1875
1581
|
if (isValidArray(config.whiteList, 1)) {
|
|
1876
1582
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1877
1583
|
config.whiteList.push(data.params.serial);
|
|
@@ -1880,7 +1586,6 @@ export class Frontend extends EventEmitter {
|
|
|
1880
1586
|
config.whiteList.push(data.params.name);
|
|
1881
1587
|
}
|
|
1882
1588
|
}
|
|
1883
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1884
1589
|
if (isValidArray(config.blackList, 1)) {
|
|
1885
1590
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1886
1591
|
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
@@ -1911,9 +1616,7 @@ export class Frontend extends EventEmitter {
|
|
|
1911
1616
|
return;
|
|
1912
1617
|
}
|
|
1913
1618
|
const config = plugin.configJson;
|
|
1914
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1915
1619
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1916
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1917
1620
|
if (select === 'serial')
|
|
1918
1621
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1919
1622
|
if (select === 'name')
|
|
@@ -1922,7 +1625,6 @@ export class Frontend extends EventEmitter {
|
|
|
1922
1625
|
if (config.postfix) {
|
|
1923
1626
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1924
1627
|
}
|
|
1925
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1926
1628
|
if (isValidArray(config.whiteList, 1)) {
|
|
1927
1629
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1928
1630
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
@@ -1931,7 +1633,6 @@ export class Frontend extends EventEmitter {
|
|
|
1931
1633
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1932
1634
|
}
|
|
1933
1635
|
}
|
|
1934
|
-
// Add the serial to the blackList
|
|
1935
1636
|
if (isValidArray(config.blackList)) {
|
|
1936
1637
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1937
1638
|
config.blackList.push(data.params.serial);
|
|
@@ -1965,251 +1666,126 @@ export class Frontend extends EventEmitter {
|
|
|
1965
1666
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1966
1667
|
}
|
|
1967
1668
|
}
|
|
1968
|
-
/**
|
|
1969
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1970
|
-
*
|
|
1971
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1972
|
-
* @param {string} time - The time string of the message
|
|
1973
|
-
* @param {string} name - The logger name of the message
|
|
1974
|
-
* @param {string} message - The content of the message.
|
|
1975
|
-
*
|
|
1976
|
-
* @remarks
|
|
1977
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
1978
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
1979
|
-
* The function sends the message to all connected clients.
|
|
1980
|
-
*/
|
|
1981
1669
|
wssSendMessage(level, time, name, message) {
|
|
1982
1670
|
if (!level || !time || !name || !message)
|
|
1983
1671
|
return;
|
|
1984
|
-
// Remove ANSI escape codes from the message
|
|
1985
|
-
// eslint-disable-next-line no-control-regex
|
|
1986
1672
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1987
|
-
// Remove leading asterisks from the message
|
|
1988
1673
|
message = message.replace(/^\*+/, '');
|
|
1989
|
-
// Replace all occurrences of \t and \n
|
|
1990
1674
|
message = message.replace(/[\t\n]/g, '');
|
|
1991
|
-
// Remove non-printable characters
|
|
1992
|
-
// eslint-disable-next-line no-control-regex
|
|
1993
1675
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1994
|
-
// Replace all occurrences of \" with "
|
|
1995
1676
|
message = message.replace(/\\"/g, '"');
|
|
1996
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1997
1677
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1998
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1999
1678
|
const maxContinuousLength = 100;
|
|
2000
1679
|
const keepStartLength = 20;
|
|
2001
1680
|
const keepEndLength = 20;
|
|
2002
|
-
// Split the message into words
|
|
2003
1681
|
message = message
|
|
2004
1682
|
.split(' ')
|
|
2005
1683
|
.map((word) => {
|
|
2006
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2007
1684
|
if (word.length > maxContinuousLength) {
|
|
2008
1685
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2009
1686
|
}
|
|
2010
1687
|
return word;
|
|
2011
1688
|
})
|
|
2012
1689
|
.join(' ');
|
|
2013
|
-
// Send the message to all connected clients
|
|
2014
1690
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2015
1691
|
if (client.readyState === WebSocket.OPEN) {
|
|
2016
1692
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
2017
1693
|
}
|
|
2018
1694
|
});
|
|
2019
1695
|
}
|
|
2020
|
-
/**
|
|
2021
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2022
|
-
*
|
|
2023
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
2024
|
-
* possible values:
|
|
2025
|
-
* - 'matterbridgeLatestVersion'
|
|
2026
|
-
* - 'matterbridgeDevVersion'
|
|
2027
|
-
* - 'matterbridgeAdvertise'
|
|
2028
|
-
* - 'online'
|
|
2029
|
-
* - 'offline'
|
|
2030
|
-
* - 'reachability'
|
|
2031
|
-
* - 'settings'
|
|
2032
|
-
* - 'plugins'
|
|
2033
|
-
* - 'pluginsRestart'
|
|
2034
|
-
* - 'devices'
|
|
2035
|
-
* - 'fabrics'
|
|
2036
|
-
* - 'sessions'
|
|
2037
|
-
*/
|
|
2038
1696
|
wssSendRefreshRequired(changed = null) {
|
|
2039
1697
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
2040
|
-
// Send the message to all connected clients
|
|
2041
1698
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2042
1699
|
if (client.readyState === WebSocket.OPEN) {
|
|
2043
1700
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
2044
1701
|
}
|
|
2045
1702
|
});
|
|
2046
1703
|
}
|
|
2047
|
-
/**
|
|
2048
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2049
|
-
*
|
|
2050
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
|
|
2051
|
-
* @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
|
|
2052
|
-
*/
|
|
2053
1704
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
2054
1705
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
2055
1706
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
2056
1707
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
|
|
2057
1708
|
if (snackbar === true)
|
|
2058
1709
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
2059
|
-
// Send the message to all connected clients
|
|
2060
1710
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2061
1711
|
if (client.readyState === WebSocket.OPEN) {
|
|
2062
1712
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } }));
|
|
2063
1713
|
}
|
|
2064
1714
|
});
|
|
2065
1715
|
}
|
|
2066
|
-
/**
|
|
2067
|
-
* Sends a no need to restart WebSocket message to all connected clients.
|
|
2068
|
-
*
|
|
2069
|
-
* @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients.
|
|
2070
|
-
*/
|
|
2071
1716
|
wssSendRestartNotRequired(snackbar = true) {
|
|
2072
1717
|
this.log.debug('Sending a restart not required message to all connected clients');
|
|
2073
1718
|
this.matterbridge.matterbridgeInformation.restartRequired = false;
|
|
2074
1719
|
if (snackbar === true)
|
|
2075
1720
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2076
|
-
// Send the message to all connected clients
|
|
2077
1721
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2078
1722
|
if (client.readyState === WebSocket.OPEN) {
|
|
2079
1723
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', params: {} }));
|
|
2080
1724
|
}
|
|
2081
1725
|
});
|
|
2082
1726
|
}
|
|
2083
|
-
/**
|
|
2084
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
2085
|
-
*
|
|
2086
|
-
* @param {boolean} devVersion - If true, the update is for a development version.
|
|
2087
|
-
*/
|
|
2088
1727
|
wssSendUpdateRequired(devVersion = false) {
|
|
2089
1728
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2090
1729
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
2091
|
-
// Send the message to all connected clients
|
|
2092
1730
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2093
1731
|
if (client.readyState === WebSocket.OPEN) {
|
|
2094
1732
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required', params: {} }));
|
|
2095
1733
|
}
|
|
2096
1734
|
});
|
|
2097
1735
|
}
|
|
2098
|
-
/**
|
|
2099
|
-
* Sends a cpu update message to all connected clients.
|
|
2100
|
-
*
|
|
2101
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2102
|
-
*/
|
|
2103
1736
|
wssSendCpuUpdate(cpuUsage) {
|
|
2104
1737
|
if (hasParameter('debug'))
|
|
2105
1738
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2106
|
-
// Send the message to all connected clients
|
|
2107
1739
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2108
1740
|
if (client.readyState === WebSocket.OPEN) {
|
|
2109
1741
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
2110
1742
|
}
|
|
2111
1743
|
});
|
|
2112
1744
|
}
|
|
2113
|
-
/**
|
|
2114
|
-
* Sends a memory update message to all connected clients.
|
|
2115
|
-
*
|
|
2116
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2117
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2118
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2119
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2120
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2121
|
-
* @param {string} external - The external memory in bytes.
|
|
2122
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2123
|
-
*/
|
|
2124
1745
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2125
1746
|
if (hasParameter('debug'))
|
|
2126
1747
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2127
|
-
// Send the message to all connected clients
|
|
2128
1748
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2129
1749
|
if (client.readyState === WebSocket.OPEN) {
|
|
2130
1750
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
2131
1751
|
}
|
|
2132
1752
|
});
|
|
2133
1753
|
}
|
|
2134
|
-
/**
|
|
2135
|
-
* Sends an uptime update message to all connected clients.
|
|
2136
|
-
*
|
|
2137
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2138
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2139
|
-
*/
|
|
2140
1754
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2141
1755
|
if (hasParameter('debug'))
|
|
2142
1756
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2143
|
-
// Send the message to all connected clients
|
|
2144
1757
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2145
1758
|
if (client.readyState === WebSocket.OPEN) {
|
|
2146
1759
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
2147
1760
|
}
|
|
2148
1761
|
});
|
|
2149
1762
|
}
|
|
2150
|
-
/**
|
|
2151
|
-
* Sends an open snackbar message to all connected clients.
|
|
2152
|
-
*
|
|
2153
|
-
* @param {string} message - The message to send.
|
|
2154
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
2155
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
2156
|
-
*/
|
|
2157
1763
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2158
1764
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2159
|
-
// Send the message to all connected clients
|
|
2160
1765
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2161
1766
|
if (client.readyState === WebSocket.OPEN) {
|
|
2162
1767
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
2163
1768
|
}
|
|
2164
1769
|
});
|
|
2165
1770
|
}
|
|
2166
|
-
/**
|
|
2167
|
-
* Sends a close snackbar message to all connected clients.
|
|
2168
|
-
*
|
|
2169
|
-
* @param {string} message - The message to send.
|
|
2170
|
-
*/
|
|
2171
1771
|
wssSendCloseSnackbarMessage(message) {
|
|
2172
1772
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2173
|
-
// Send the message to all connected clients
|
|
2174
1773
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2175
1774
|
if (client.readyState === WebSocket.OPEN) {
|
|
2176
1775
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
2177
1776
|
}
|
|
2178
1777
|
});
|
|
2179
1778
|
}
|
|
2180
|
-
/**
|
|
2181
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2182
|
-
*
|
|
2183
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2184
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2185
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2186
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2187
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2188
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2189
|
-
*
|
|
2190
|
-
* @remarks
|
|
2191
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2192
|
-
* with the updated attribute information.
|
|
2193
|
-
*/
|
|
2194
1779
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
2195
1780
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2196
|
-
// Send the message to all connected clients
|
|
2197
1781
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2198
1782
|
if (client.readyState === WebSocket.OPEN) {
|
|
2199
1783
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
2200
1784
|
}
|
|
2201
1785
|
});
|
|
2202
1786
|
}
|
|
2203
|
-
/**
|
|
2204
|
-
* Sends a message to all connected clients.
|
|
2205
|
-
*
|
|
2206
|
-
* @param {number} id - The message id.
|
|
2207
|
-
* @param {string} method - The message method.
|
|
2208
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
2209
|
-
*/
|
|
2210
1787
|
wssBroadcastMessage(id, method, params) {
|
|
2211
1788
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
2212
|
-
// Send the message to all connected clients
|
|
2213
1789
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2214
1790
|
if (client.readyState === WebSocket.OPEN) {
|
|
2215
1791
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -2217,4 +1793,3 @@ export class Frontend extends EventEmitter {
|
|
|
2217
1793
|
});
|
|
2218
1794
|
}
|
|
2219
1795
|
}
|
|
2220
|
-
//# sourceMappingURL=frontend.js.map
|