matterbridge 3.2.0 → 3.2.1-dev-20250802-516a522
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 -0
- package/dist/cli.js +2 -91
- package/dist/cliEmitter.js +0 -30
- package/dist/clusters/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -24
- package/dist/deviceManager.js +1 -94
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/dishwasher.js +3 -78
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.js +0 -2
- package/dist/devices/extractorHood.js +0 -42
- package/dist/devices/heatPump.js +2 -50
- package/dist/devices/laundryDryer.js +6 -83
- package/dist/devices/laundryWasher.js +7 -91
- package/dist/devices/roboticVacuumCleaner.js +7 -93
- package/dist/devices/solarPower.js +0 -38
- 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 +23 -448
- 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 +50 -805
- package/dist/matterbridgeAccessoryPlatform.js +0 -36
- package/dist/matterbridgeBehaviors.js +5 -65
- package/dist/matterbridgeDeviceTypes.js +15 -579
- package/dist/matterbridgeDynamicPlatform.js +0 -36
- package/dist/matterbridgeEndpoint.js +55 -1234
- package/dist/matterbridgeEndpointHelpers.js +12 -344
- package/dist/matterbridgePlatform.js +35 -248
- 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 -263
- 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 -123
- package/dist/utils/isvalid.js +0 -101
- package/dist/utils/network.js +5 -94
- package/dist/utils/spawn.js +0 -40
- package/dist/utils/wait.js +9 -62
- package/docs/404.html +107 -0
- package/docs/HomeAssistant.svg +2 -0
- package/docs/Shelly.svg +1 -0
- package/docs/Somfy.svg +1 -0
- package/docs/Zigbee2MQTT.svg +1 -0
- package/docs/_config.yml +86 -0
- package/docs/bmc-button.svg +22 -0
- package/docs/index.html +774 -0
- package/docs/matterbridge.svg +50 -0
- package/docs/robots.txt +4 -0
- package/npm-shrinkwrap.json +45 -45
- package/package.json +5 -3
- 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/dishwasher.d.ts +0 -91
- 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 -11
- 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 -87
- package/dist/devices/laundryDryer.d.ts.map +0 -1
- package/dist/devices/laundryDryer.js.map +0 -1
- package/dist/devices/laundryWasher.d.ts +0 -242
- package/dist/devices/laundryWasher.d.ts.map +0 -1
- package/dist/devices/laundryWasher.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/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 -463
- 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 -709
- 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 -1354
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -406
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -310
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -197
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -270
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -174
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts +0 -75
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -117
- 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 -56
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
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,41 +45,12 @@ 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
|
-
// Initialize multer with the upload directory
|
|
145
48
|
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
146
49
|
await fs.mkdir(uploadDir, { recursive: true });
|
|
147
50
|
const upload = multer({ dest: uploadDir });
|
|
148
|
-
// Create the express app that serves the frontend
|
|
149
51
|
this.expressApp = express();
|
|
150
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
151
|
-
/*
|
|
152
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
153
|
-
for (const method of methods) {
|
|
154
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
155
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
156
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
157
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
158
|
-
try {
|
|
159
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
160
|
-
return original(path, ...rest);
|
|
161
|
-
} catch (err) {
|
|
162
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
163
|
-
throw err;
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
*/
|
|
168
|
-
// Log all requests to the server for debugging
|
|
169
|
-
/*
|
|
170
|
-
this.expressApp.use((req, res, next) => {
|
|
171
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
172
|
-
next();
|
|
173
|
-
});
|
|
174
|
-
*/
|
|
175
|
-
// Serve static files from '/static' endpoint
|
|
176
52
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
177
53
|
if (!hasParameter('ssl')) {
|
|
178
|
-
// Create an HTTP server and attach the express app
|
|
179
54
|
try {
|
|
180
55
|
this.log.debug(`Creating HTTP server...`);
|
|
181
56
|
this.httpServer = createServer(this.expressApp);
|
|
@@ -185,7 +60,6 @@ export class Frontend extends EventEmitter {
|
|
|
185
60
|
this.emit('server_error', error);
|
|
186
61
|
return;
|
|
187
62
|
}
|
|
188
|
-
// Listen on the specified port
|
|
189
63
|
if (hasParameter('ingress')) {
|
|
190
64
|
this.httpServer.listen(this.port, '0.0.0.0', () => {
|
|
191
65
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -224,7 +98,6 @@ export class Frontend extends EventEmitter {
|
|
|
224
98
|
let passphrase;
|
|
225
99
|
let httpsServerOptions = {};
|
|
226
100
|
if (existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
|
|
227
|
-
// Load the p12 certificate and the passphrase
|
|
228
101
|
try {
|
|
229
102
|
pfx = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
|
|
230
103
|
this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
|
|
@@ -236,7 +109,7 @@ export class Frontend extends EventEmitter {
|
|
|
236
109
|
}
|
|
237
110
|
try {
|
|
238
111
|
passphrase = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
|
|
239
|
-
passphrase = passphrase.trim();
|
|
112
|
+
passphrase = passphrase.trim();
|
|
240
113
|
this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
|
|
241
114
|
}
|
|
242
115
|
catch (error) {
|
|
@@ -247,7 +120,6 @@ export class Frontend extends EventEmitter {
|
|
|
247
120
|
httpsServerOptions = { pfx, passphrase };
|
|
248
121
|
}
|
|
249
122
|
else {
|
|
250
|
-
// 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.
|
|
251
123
|
try {
|
|
252
124
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
253
125
|
this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
|
|
@@ -277,10 +149,9 @@ export class Frontend extends EventEmitter {
|
|
|
277
149
|
httpsServerOptions = { cert: fullChain ?? cert, key, ca };
|
|
278
150
|
}
|
|
279
151
|
if (hasParameter('mtls')) {
|
|
280
|
-
httpsServerOptions.requestCert = true;
|
|
281
|
-
httpsServerOptions.rejectUnauthorized = true;
|
|
152
|
+
httpsServerOptions.requestCert = true;
|
|
153
|
+
httpsServerOptions.rejectUnauthorized = true;
|
|
282
154
|
}
|
|
283
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
284
155
|
try {
|
|
285
156
|
this.log.debug(`Creating HTTPS server...`);
|
|
286
157
|
this.httpsServer = https.createServer(httpsServerOptions, this.expressApp);
|
|
@@ -290,7 +161,6 @@ export class Frontend extends EventEmitter {
|
|
|
290
161
|
this.emit('server_error', error);
|
|
291
162
|
return;
|
|
292
163
|
}
|
|
293
|
-
// Listen on the specified port
|
|
294
164
|
if (hasParameter('ingress')) {
|
|
295
165
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
296
166
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -320,19 +190,17 @@ export class Frontend extends EventEmitter {
|
|
|
320
190
|
return;
|
|
321
191
|
});
|
|
322
192
|
}
|
|
323
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
324
193
|
const wssPort = this.port;
|
|
325
194
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
326
195
|
this.log.debug(`Creating WebSocketServer on host ${CYAN}${wssHost}${db}...`);
|
|
327
196
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
328
197
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
329
198
|
const clientIp = request.socket.remoteAddress;
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
199
|
+
let callbackLogLevel = "notice";
|
|
200
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
201
|
+
callbackLogLevel = "info";
|
|
202
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
203
|
+
callbackLogLevel = "debug";
|
|
336
204
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
337
205
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
338
206
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -354,7 +222,6 @@ export class Frontend extends EventEmitter {
|
|
|
354
222
|
}
|
|
355
223
|
});
|
|
356
224
|
ws.on('error', (error) => {
|
|
357
|
-
// istanbul ignore next
|
|
358
225
|
this.log.error(`WebSocket client error: ${error}`);
|
|
359
226
|
});
|
|
360
227
|
});
|
|
@@ -368,7 +235,6 @@ export class Frontend extends EventEmitter {
|
|
|
368
235
|
this.webSocketServer.on('error', (ws, error) => {
|
|
369
236
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
370
237
|
});
|
|
371
|
-
// Subscribe to cli events
|
|
372
238
|
cliEmitter.removeAllListeners();
|
|
373
239
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
374
240
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -379,8 +245,6 @@ export class Frontend extends EventEmitter {
|
|
|
379
245
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
380
246
|
this.wssSendCpuUpdate(cpuUsage);
|
|
381
247
|
});
|
|
382
|
-
// Endpoint to validate login code
|
|
383
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
384
248
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
385
249
|
const { password } = req.body;
|
|
386
250
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -399,27 +263,23 @@ export class Frontend extends EventEmitter {
|
|
|
399
263
|
this.log.warn('/api/login error wrong password');
|
|
400
264
|
res.json({ valid: false });
|
|
401
265
|
}
|
|
402
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
403
266
|
}
|
|
404
267
|
catch (error) {
|
|
405
268
|
this.log.error('/api/login error getting password');
|
|
406
269
|
res.json({ valid: false });
|
|
407
270
|
}
|
|
408
271
|
});
|
|
409
|
-
// Endpoint to provide health check for docker
|
|
410
272
|
this.expressApp.get('/health', (req, res) => {
|
|
411
273
|
this.log.debug('Express received /health');
|
|
412
274
|
const healthStatus = {
|
|
413
|
-
status: 'ok',
|
|
414
|
-
uptime: process.uptime(),
|
|
415
|
-
timestamp: new Date().toISOString(),
|
|
275
|
+
status: 'ok',
|
|
276
|
+
uptime: process.uptime(),
|
|
277
|
+
timestamp: new Date().toISOString(),
|
|
416
278
|
};
|
|
417
279
|
res.status(200).json(healthStatus);
|
|
418
280
|
});
|
|
419
|
-
// Endpoint to provide memory usage details
|
|
420
281
|
this.expressApp.get('/memory', async (req, res) => {
|
|
421
282
|
this.log.debug('Express received /memory');
|
|
422
|
-
// Memory usage from process
|
|
423
283
|
const memoryUsageRaw = process.memoryUsage();
|
|
424
284
|
const memoryUsage = {
|
|
425
285
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -428,13 +288,10 @@ export class Frontend extends EventEmitter {
|
|
|
428
288
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
429
289
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
430
290
|
};
|
|
431
|
-
// V8 heap statistics
|
|
432
291
|
const { default: v8 } = await import('node:v8');
|
|
433
292
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
434
293
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
435
|
-
// Format heapStats
|
|
436
294
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
437
|
-
// Format heapSpaces
|
|
438
295
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
439
296
|
...space,
|
|
440
297
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -452,23 +309,19 @@ export class Frontend extends EventEmitter {
|
|
|
452
309
|
};
|
|
453
310
|
res.status(200).json(memoryReport);
|
|
454
311
|
});
|
|
455
|
-
// Endpoint to provide settings
|
|
456
312
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
457
313
|
this.log.debug('The frontend sent /api/settings');
|
|
458
314
|
res.json(await this.getApiSettings());
|
|
459
315
|
});
|
|
460
|
-
// Endpoint to provide plugins
|
|
461
316
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
462
317
|
this.log.debug('The frontend sent /api/plugins');
|
|
463
318
|
res.json(this.getPlugins());
|
|
464
319
|
});
|
|
465
|
-
// Endpoint to provide devices
|
|
466
320
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
467
321
|
this.log.debug('The frontend sent /api/devices');
|
|
468
322
|
const devices = await this.getDevices();
|
|
469
323
|
res.json(devices);
|
|
470
324
|
});
|
|
471
|
-
// Endpoint to view the matterbridge log
|
|
472
325
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
473
326
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
474
327
|
try {
|
|
@@ -481,7 +334,6 @@ export class Frontend extends EventEmitter {
|
|
|
481
334
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
482
335
|
}
|
|
483
336
|
});
|
|
484
|
-
// Endpoint to view the matter.js log
|
|
485
337
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
486
338
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
487
339
|
try {
|
|
@@ -494,7 +346,6 @@ export class Frontend extends EventEmitter {
|
|
|
494
346
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
495
347
|
}
|
|
496
348
|
});
|
|
497
|
-
// Endpoint to view the shelly log
|
|
498
349
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
499
350
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
500
351
|
try {
|
|
@@ -507,7 +358,6 @@ export class Frontend extends EventEmitter {
|
|
|
507
358
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
508
359
|
}
|
|
509
360
|
});
|
|
510
|
-
// Endpoint to download the matterbridge log
|
|
511
361
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
512
362
|
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
513
363
|
try {
|
|
@@ -521,14 +371,12 @@ export class Frontend extends EventEmitter {
|
|
|
521
371
|
}
|
|
522
372
|
res.type('text/plain');
|
|
523
373
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'matterbridge.log', (error) => {
|
|
524
|
-
/* istanbul ignore if */
|
|
525
374
|
if (error) {
|
|
526
375
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
527
376
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
528
377
|
}
|
|
529
378
|
});
|
|
530
379
|
});
|
|
531
|
-
// Endpoint to download the matter log
|
|
532
380
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
533
381
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
534
382
|
try {
|
|
@@ -542,14 +390,12 @@ export class Frontend extends EventEmitter {
|
|
|
542
390
|
}
|
|
543
391
|
res.type('text/plain');
|
|
544
392
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
|
|
545
|
-
/* istanbul ignore if */
|
|
546
393
|
if (error) {
|
|
547
394
|
this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
548
395
|
res.status(500).send('Error downloading the matter log file');
|
|
549
396
|
}
|
|
550
397
|
});
|
|
551
398
|
});
|
|
552
|
-
// Endpoint to download the shelly log
|
|
553
399
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
554
400
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
555
401
|
try {
|
|
@@ -563,90 +409,74 @@ export class Frontend extends EventEmitter {
|
|
|
563
409
|
}
|
|
564
410
|
res.type('text/plain');
|
|
565
411
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
566
|
-
/* istanbul ignore if */
|
|
567
412
|
if (error) {
|
|
568
413
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
569
414
|
res.status(500).send('Error downloading Shelly system log file');
|
|
570
415
|
}
|
|
571
416
|
});
|
|
572
417
|
});
|
|
573
|
-
// Endpoint to download the matterbridge storage directory
|
|
574
418
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
575
419
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
576
420
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
577
421
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), `matterbridge.${this.matterbridge.nodeStorageName}.zip`, (error) => {
|
|
578
|
-
/* istanbul ignore if */
|
|
579
422
|
if (error) {
|
|
580
423
|
this.log.error(`Error downloading file ${`matterbridge.${this.matterbridge.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
581
424
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
582
425
|
}
|
|
583
426
|
});
|
|
584
427
|
});
|
|
585
|
-
// Endpoint to download the matter storage file
|
|
586
428
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
587
429
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
588
430
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
589
431
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), `matterbridge.${this.matterbridge.matterStorageName}.zip`, (error) => {
|
|
590
|
-
/* istanbul ignore if */
|
|
591
432
|
if (error) {
|
|
592
433
|
this.log.error(`Error downloading the matter storage matterbridge.${this.matterbridge.matterStorageName}.zip: ${error instanceof Error ? error.message : error}`);
|
|
593
434
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
594
435
|
}
|
|
595
436
|
});
|
|
596
437
|
});
|
|
597
|
-
// Endpoint to download the matterbridge plugin directory
|
|
598
438
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
599
439
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
600
440
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
601
441
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
602
|
-
/* istanbul ignore if */
|
|
603
442
|
if (error) {
|
|
604
443
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
605
444
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
606
445
|
}
|
|
607
446
|
});
|
|
608
447
|
});
|
|
609
|
-
// Endpoint to download the matterbridge plugin config files
|
|
610
448
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
611
449
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
612
450
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
613
451
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
614
|
-
/* istanbul ignore if */
|
|
615
452
|
if (error) {
|
|
616
453
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
617
454
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
618
455
|
}
|
|
619
456
|
});
|
|
620
457
|
});
|
|
621
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
622
458
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
623
459
|
this.log.debug('The frontend sent /api/download-backup');
|
|
624
460
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
625
|
-
/* istanbul ignore if */
|
|
626
461
|
if (error) {
|
|
627
462
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
628
463
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
629
464
|
}
|
|
630
465
|
});
|
|
631
466
|
});
|
|
632
|
-
// Endpoint to upload a package
|
|
633
467
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
634
468
|
const { filename } = req.body;
|
|
635
469
|
const file = req.file;
|
|
636
|
-
/* istanbul ignore if */
|
|
637
470
|
if (!file || !filename) {
|
|
638
471
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
639
472
|
res.status(400).send('Invalid request: file and filename are required');
|
|
640
473
|
return;
|
|
641
474
|
}
|
|
642
475
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
643
|
-
// Define the path where the plugin file will be saved
|
|
644
476
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
645
477
|
try {
|
|
646
|
-
// Move the uploaded file to the specified path
|
|
647
478
|
await fs.rename(file.path, filePath);
|
|
648
479
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
649
|
-
// Install the plugin package
|
|
650
480
|
if (filename.endsWith('.tgz')) {
|
|
651
481
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
652
482
|
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
@@ -666,7 +496,6 @@ export class Frontend extends EventEmitter {
|
|
|
666
496
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
667
497
|
}
|
|
668
498
|
});
|
|
669
|
-
// Fallback for routing (must be the last route)
|
|
670
499
|
this.expressApp.use((req, res) => {
|
|
671
500
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
|
|
672
501
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -675,16 +504,13 @@ export class Frontend extends EventEmitter {
|
|
|
675
504
|
}
|
|
676
505
|
async stop() {
|
|
677
506
|
this.log.debug('Stopping the frontend...');
|
|
678
|
-
// Remove listeners from the express app
|
|
679
507
|
if (this.expressApp) {
|
|
680
508
|
this.expressApp.removeAllListeners();
|
|
681
509
|
this.expressApp = undefined;
|
|
682
510
|
this.log.debug('Frontend app closed successfully');
|
|
683
511
|
}
|
|
684
|
-
// Close the WebSocket server
|
|
685
512
|
if (this.webSocketServer) {
|
|
686
513
|
this.log.debug('Closing WebSocket server...');
|
|
687
|
-
// Close all active connections
|
|
688
514
|
this.webSocketServer.clients.forEach((client) => {
|
|
689
515
|
if (client.readyState === WebSocket.OPEN) {
|
|
690
516
|
client.close();
|
|
@@ -693,7 +519,6 @@ export class Frontend extends EventEmitter {
|
|
|
693
519
|
await withTimeout(new Promise((resolve) => {
|
|
694
520
|
this.webSocketServer?.close((error) => {
|
|
695
521
|
if (error) {
|
|
696
|
-
// istanbul ignore next
|
|
697
522
|
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
698
523
|
}
|
|
699
524
|
else {
|
|
@@ -706,13 +531,11 @@ export class Frontend extends EventEmitter {
|
|
|
706
531
|
this.webSocketServer.removeAllListeners();
|
|
707
532
|
this.webSocketServer = undefined;
|
|
708
533
|
}
|
|
709
|
-
// Close the http server
|
|
710
534
|
if (this.httpServer) {
|
|
711
535
|
this.log.debug('Closing http server...');
|
|
712
536
|
await withTimeout(new Promise((resolve) => {
|
|
713
537
|
this.httpServer?.close((error) => {
|
|
714
538
|
if (error) {
|
|
715
|
-
// istanbul ignore next
|
|
716
539
|
this.log.error(`Error closing http server: ${error}`);
|
|
717
540
|
}
|
|
718
541
|
else {
|
|
@@ -726,13 +549,11 @@ export class Frontend extends EventEmitter {
|
|
|
726
549
|
this.httpServer = undefined;
|
|
727
550
|
this.log.debug('Frontend http server closed successfully');
|
|
728
551
|
}
|
|
729
|
-
// Close the https server
|
|
730
552
|
if (this.httpsServer) {
|
|
731
553
|
this.log.debug('Closing https server...');
|
|
732
554
|
await withTimeout(new Promise((resolve) => {
|
|
733
555
|
this.httpsServer?.close((error) => {
|
|
734
556
|
if (error) {
|
|
735
|
-
// istanbul ignore next
|
|
736
557
|
this.log.error(`Error closing https server: ${error}`);
|
|
737
558
|
}
|
|
738
559
|
else {
|
|
@@ -748,7 +569,6 @@ export class Frontend extends EventEmitter {
|
|
|
748
569
|
}
|
|
749
570
|
this.log.debug('Frontend stopped successfully');
|
|
750
571
|
}
|
|
751
|
-
// Function to format bytes to KB, MB, or GB
|
|
752
572
|
formatMemoryUsage = (bytes) => {
|
|
753
573
|
if (bytes >= 1024 ** 3) {
|
|
754
574
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -760,7 +580,6 @@ export class Frontend extends EventEmitter {
|
|
|
760
580
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
761
581
|
}
|
|
762
582
|
};
|
|
763
|
-
// Function to format system uptime with only the most significant unit
|
|
764
583
|
formatOsUpTime = (seconds) => {
|
|
765
584
|
if (seconds >= 86400) {
|
|
766
585
|
const days = Math.floor(seconds / 86400);
|
|
@@ -776,13 +595,7 @@ export class Frontend extends EventEmitter {
|
|
|
776
595
|
}
|
|
777
596
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
778
597
|
};
|
|
779
|
-
/**
|
|
780
|
-
* Retrieves the api settings data.
|
|
781
|
-
*
|
|
782
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
783
|
-
*/
|
|
784
598
|
async getApiSettings() {
|
|
785
|
-
// Update the system information
|
|
786
599
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
787
600
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
788
601
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -791,7 +604,6 @@ export class Frontend extends EventEmitter {
|
|
|
791
604
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
792
605
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
793
606
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
794
|
-
// Update the matterbridge information
|
|
795
607
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
796
608
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
797
609
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
@@ -803,7 +615,6 @@ export class Frontend extends EventEmitter {
|
|
|
803
615
|
this.matterbridge.matterbridgeInformation.matterPort = (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540;
|
|
804
616
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = await this.matterbridge.nodeContext?.get('matterdiscriminator');
|
|
805
617
|
this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
|
|
806
|
-
// Update the matterbridge information in bridge mode
|
|
807
618
|
if (this.matterbridge.bridgeMode === 'bridge' && this.matterbridge.serverNode && !this.matterbridge.hasCleanupStarted) {
|
|
808
619
|
this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.serverNode.state.commissioning.commissioned;
|
|
809
620
|
this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.qrPairingCode;
|
|
@@ -813,12 +624,6 @@ export class Frontend extends EventEmitter {
|
|
|
813
624
|
}
|
|
814
625
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
815
626
|
}
|
|
816
|
-
/**
|
|
817
|
-
* Retrieves the reachable attribute.
|
|
818
|
-
*
|
|
819
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
820
|
-
* @returns {boolean} The reachable attribute.
|
|
821
|
-
*/
|
|
822
627
|
getReachability(device) {
|
|
823
628
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
824
629
|
return false;
|
|
@@ -830,12 +635,6 @@ export class Frontend extends EventEmitter {
|
|
|
830
635
|
return true;
|
|
831
636
|
return false;
|
|
832
637
|
}
|
|
833
|
-
/**
|
|
834
|
-
* Retrieves the power source attribute.
|
|
835
|
-
*
|
|
836
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
837
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
838
|
-
*/
|
|
839
638
|
getPowerSource(endpoint) {
|
|
840
639
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
841
640
|
return undefined;
|
|
@@ -851,21 +650,13 @@ export class Frontend extends EventEmitter {
|
|
|
851
650
|
}
|
|
852
651
|
return;
|
|
853
652
|
};
|
|
854
|
-
// Root endpoint
|
|
855
653
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
856
654
|
return powerSource(endpoint);
|
|
857
|
-
// Child endpoints
|
|
858
655
|
for (const child of endpoint.getChildEndpoints()) {
|
|
859
656
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
860
657
|
return powerSource(child);
|
|
861
658
|
}
|
|
862
659
|
}
|
|
863
|
-
/**
|
|
864
|
-
* Retrieves the commissioned status, matter pairing codes, fabrics and sessions from a given device in server mode.
|
|
865
|
-
*
|
|
866
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the data from.
|
|
867
|
-
* @returns {ApiDevicesMatter | undefined} An ApiDevicesMatter object or undefined if not found.
|
|
868
|
-
*/
|
|
869
660
|
getMatterDataFromDevice(device) {
|
|
870
661
|
if (device.mode === 'server' && device.serverNode) {
|
|
871
662
|
return {
|
|
@@ -877,13 +668,6 @@ export class Frontend extends EventEmitter {
|
|
|
877
668
|
};
|
|
878
669
|
}
|
|
879
670
|
}
|
|
880
|
-
/**
|
|
881
|
-
* Retrieves the cluster text description from a given device.
|
|
882
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
883
|
-
*
|
|
884
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
885
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
886
|
-
*/
|
|
887
671
|
getClusterTextFromDevice(device) {
|
|
888
672
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
889
673
|
return '';
|
|
@@ -894,7 +678,6 @@ export class Frontend extends EventEmitter {
|
|
|
894
678
|
if (composed)
|
|
895
679
|
return 'Composed: ' + composed.value;
|
|
896
680
|
}
|
|
897
|
-
// istanbul ignore next cause is not reachable
|
|
898
681
|
return '';
|
|
899
682
|
};
|
|
900
683
|
const getFixedLabel = (device) => {
|
|
@@ -904,13 +687,11 @@ export class Frontend extends EventEmitter {
|
|
|
904
687
|
if (composed)
|
|
905
688
|
return 'Composed: ' + composed.value;
|
|
906
689
|
}
|
|
907
|
-
// istanbul ignore next cause is not reacheable
|
|
908
690
|
return '';
|
|
909
691
|
};
|
|
910
692
|
let attributes = '';
|
|
911
693
|
let supportedModes = [];
|
|
912
694
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
913
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
914
695
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
915
696
|
return;
|
|
916
697
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1000,17 +781,11 @@ export class Frontend extends EventEmitter {
|
|
|
1000
781
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1001
782
|
attributes += `${getUserLabel(device)} `;
|
|
1002
783
|
});
|
|
1003
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1004
784
|
return attributes.trimStart().trimEnd();
|
|
1005
785
|
}
|
|
1006
|
-
/**
|
|
1007
|
-
* Retrieves the registered plugins sanitized for res.json().
|
|
1008
|
-
*
|
|
1009
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
1010
|
-
*/
|
|
1011
786
|
getPlugins() {
|
|
1012
787
|
if (this.matterbridge.hasCleanupStarted)
|
|
1013
|
-
return [];
|
|
788
|
+
return [];
|
|
1014
789
|
const baseRegisteredPlugins = [];
|
|
1015
790
|
for (const plugin of this.matterbridge.plugins) {
|
|
1016
791
|
baseRegisteredPlugins.push({
|
|
@@ -1040,7 +815,6 @@ export class Frontend extends EventEmitter {
|
|
|
1040
815
|
schemaJson: plugin.schemaJson,
|
|
1041
816
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1042
817
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1043
|
-
// Childbridge mode specific data
|
|
1044
818
|
paired: plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.commissioned : undefined,
|
|
1045
819
|
qrPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.pairingCodes.qrPairingCode : undefined,
|
|
1046
820
|
manualPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.pairingCodes.manualPairingCode : undefined,
|
|
@@ -1050,21 +824,13 @@ export class Frontend extends EventEmitter {
|
|
|
1050
824
|
}
|
|
1051
825
|
return baseRegisteredPlugins;
|
|
1052
826
|
}
|
|
1053
|
-
/**
|
|
1054
|
-
* Retrieves the devices from Matterbridge.
|
|
1055
|
-
*
|
|
1056
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1057
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
|
|
1058
|
-
*/
|
|
1059
827
|
async getDevices(pluginName) {
|
|
1060
828
|
if (this.matterbridge.hasCleanupStarted)
|
|
1061
|
-
return [];
|
|
829
|
+
return [];
|
|
1062
830
|
const devices = [];
|
|
1063
831
|
for (const device of this.matterbridge.devices.array()) {
|
|
1064
|
-
// Filter by pluginName if provided
|
|
1065
832
|
if (pluginName && pluginName !== device.plugin)
|
|
1066
833
|
continue;
|
|
1067
|
-
// Check if the device has the required properties
|
|
1068
834
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1069
835
|
continue;
|
|
1070
836
|
devices.push({
|
|
@@ -1084,37 +850,22 @@ export class Frontend extends EventEmitter {
|
|
|
1084
850
|
}
|
|
1085
851
|
return devices;
|
|
1086
852
|
}
|
|
1087
|
-
/**
|
|
1088
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1089
|
-
*
|
|
1090
|
-
* Response for /api/clusters
|
|
1091
|
-
*
|
|
1092
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1093
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1094
|
-
* @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1095
|
-
*/
|
|
1096
853
|
getClusters(pluginName, endpointNumber) {
|
|
1097
854
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1098
855
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1099
856
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1100
857
|
return;
|
|
1101
858
|
}
|
|
1102
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1103
|
-
// Get the device types from the main endpoint
|
|
1104
859
|
const deviceTypes = [];
|
|
1105
860
|
const clusters = [];
|
|
1106
861
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1107
862
|
deviceTypes.push(d.deviceType);
|
|
1108
863
|
});
|
|
1109
|
-
// Get the clusters from the main endpoint
|
|
1110
864
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1111
865
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1112
866
|
return;
|
|
1113
867
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1114
868
|
return;
|
|
1115
|
-
// console.log(
|
|
1116
|
-
// `${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}`,
|
|
1117
|
-
// );
|
|
1118
869
|
clusters.push({
|
|
1119
870
|
endpoint: endpoint.number.toString(),
|
|
1120
871
|
id: 'main',
|
|
@@ -1127,19 +878,12 @@ export class Frontend extends EventEmitter {
|
|
|
1127
878
|
attributeLocalValue: attributeValue,
|
|
1128
879
|
});
|
|
1129
880
|
});
|
|
1130
|
-
// Get the child endpoints
|
|
1131
881
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1132
|
-
// if (childEndpoints.length === 0) {
|
|
1133
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1134
|
-
// }
|
|
1135
882
|
childEndpoints.forEach((childEndpoint) => {
|
|
1136
|
-
// istanbul ignore if cause is not reachable: should never happen but ...
|
|
1137
883
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1138
884
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1139
885
|
return;
|
|
1140
886
|
}
|
|
1141
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1142
|
-
// Get the device types of the child endpoint
|
|
1143
887
|
const deviceTypes = [];
|
|
1144
888
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1145
889
|
deviceTypes.push(d.deviceType);
|
|
@@ -1149,12 +893,9 @@ export class Frontend extends EventEmitter {
|
|
|
1149
893
|
return;
|
|
1150
894
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1151
895
|
return;
|
|
1152
|
-
// console.log(
|
|
1153
|
-
// `${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}`,
|
|
1154
|
-
// );
|
|
1155
896
|
clusters.push({
|
|
1156
897
|
endpoint: childEndpoint.number.toString(),
|
|
1157
|
-
id: childEndpoint.maybeId ?? 'null',
|
|
898
|
+
id: childEndpoint.maybeId ?? 'null',
|
|
1158
899
|
deviceTypes,
|
|
1159
900
|
clusterName: capitalizeFirstLetter(clusterName),
|
|
1160
901
|
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
@@ -1167,13 +908,6 @@ export class Frontend extends EventEmitter {
|
|
|
1167
908
|
});
|
|
1168
909
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
|
|
1169
910
|
}
|
|
1170
|
-
/**
|
|
1171
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1172
|
-
*
|
|
1173
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1174
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1175
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1176
|
-
*/
|
|
1177
911
|
async wsMessageHandler(client, message) {
|
|
1178
912
|
let data;
|
|
1179
913
|
try {
|
|
@@ -1220,45 +954,33 @@ export class Frontend extends EventEmitter {
|
|
|
1220
954
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1221
955
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1222
956
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1223
|
-
// The install comes from InstallPlugins
|
|
1224
957
|
this.matterbridge.plugins
|
|
1225
958
|
.add(packageName)
|
|
1226
959
|
.then((plugin) => {
|
|
1227
|
-
// istanbul ignore next if
|
|
1228
960
|
if (plugin) {
|
|
1229
|
-
// The plugin is not registered
|
|
1230
961
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1231
962
|
this.matterbridge.plugins
|
|
1232
963
|
.load(plugin, true, 'The plugin has been added', true)
|
|
1233
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1234
964
|
.then(() => {
|
|
1235
965
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
1236
966
|
this.wssSendRefreshRequired('plugins');
|
|
1237
967
|
return;
|
|
1238
968
|
})
|
|
1239
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1240
969
|
.catch((_error) => {
|
|
1241
|
-
//
|
|
1242
970
|
});
|
|
1243
971
|
}
|
|
1244
972
|
else {
|
|
1245
|
-
// The plugin is already registered
|
|
1246
973
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1247
974
|
this.wssSendRefreshRequired('plugins');
|
|
1248
975
|
this.wssSendRestartRequired(true, true);
|
|
1249
976
|
}
|
|
1250
977
|
return;
|
|
1251
978
|
})
|
|
1252
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1253
979
|
.catch((_error) => {
|
|
1254
|
-
//
|
|
1255
980
|
});
|
|
1256
981
|
}
|
|
1257
982
|
else {
|
|
1258
|
-
// The package is matterbridge
|
|
1259
|
-
// istanbul ignore next
|
|
1260
983
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1261
|
-
// istanbul ignore next if
|
|
1262
984
|
if (this.matterbridge.restartMode !== '') {
|
|
1263
985
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1264
986
|
this.matterbridge.shutdownProcess();
|
|
@@ -1280,9 +1002,7 @@ export class Frontend extends EventEmitter {
|
|
|
1280
1002
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1281
1003
|
return;
|
|
1282
1004
|
}
|
|
1283
|
-
// The package is a plugin
|
|
1284
1005
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1285
|
-
// istanbul ignore next if
|
|
1286
1006
|
if (plugin) {
|
|
1287
1007
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
1288
1008
|
await this.matterbridge.plugins.remove(data.params.packageName);
|
|
@@ -1290,7 +1010,6 @@ export class Frontend extends EventEmitter {
|
|
|
1290
1010
|
this.wssSendRefreshRequired('plugins');
|
|
1291
1011
|
this.wssSendRefreshRequired('devices');
|
|
1292
1012
|
}
|
|
1293
|
-
// Uninstall the package
|
|
1294
1013
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1295
1014
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
1296
1015
|
spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1331,7 +1050,6 @@ export class Frontend extends EventEmitter {
|
|
|
1331
1050
|
return;
|
|
1332
1051
|
})
|
|
1333
1052
|
.catch((_error) => {
|
|
1334
|
-
//
|
|
1335
1053
|
});
|
|
1336
1054
|
}
|
|
1337
1055
|
else {
|
|
@@ -1378,7 +1096,6 @@ export class Frontend extends EventEmitter {
|
|
|
1378
1096
|
return;
|
|
1379
1097
|
})
|
|
1380
1098
|
.catch((_error) => {
|
|
1381
|
-
//
|
|
1382
1099
|
});
|
|
1383
1100
|
}
|
|
1384
1101
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1404,7 +1121,6 @@ export class Frontend extends EventEmitter {
|
|
|
1404
1121
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1405
1122
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
|
|
1406
1123
|
if (plugin.serverNode) {
|
|
1407
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1408
1124
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1409
1125
|
plugin.serverNode = undefined;
|
|
1410
1126
|
}
|
|
@@ -1415,16 +1131,15 @@ export class Frontend extends EventEmitter {
|
|
|
1415
1131
|
}
|
|
1416
1132
|
}
|
|
1417
1133
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
|
|
1418
|
-
plugin.restartRequired = false;
|
|
1134
|
+
plugin.restartRequired = false;
|
|
1419
1135
|
let needRestart = 0;
|
|
1420
1136
|
for (const plugin of this.matterbridge.plugins) {
|
|
1421
1137
|
if (plugin.restartRequired)
|
|
1422
1138
|
needRestart++;
|
|
1423
1139
|
}
|
|
1424
1140
|
if (needRestart === 0) {
|
|
1425
|
-
this.wssSendRestartNotRequired(true);
|
|
1141
|
+
this.wssSendRestartNotRequired(true);
|
|
1426
1142
|
}
|
|
1427
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1428
1143
|
if (plugin.serverNode)
|
|
1429
1144
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1430
1145
|
this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
|
|
@@ -1530,8 +1245,6 @@ export class Frontend extends EventEmitter {
|
|
|
1530
1245
|
else if (data.method === '/api/advertise') {
|
|
1531
1246
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
1532
1247
|
this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
|
|
1533
|
-
// this.matterbridge.matterbridgeQrPairingCode = pairingCodes?.qrPairingCode;
|
|
1534
|
-
// this.matterbridge.matterbridgeManualPairingCode = pairingCodes?.manualPairingCode;
|
|
1535
1248
|
this.wssSendRefreshRequired('matterbridgeAdvertise');
|
|
1536
1249
|
this.wssSendSnackbarMessage(`Started fabrics share`, 0);
|
|
1537
1250
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes, success: true }));
|
|
@@ -1654,22 +1367,22 @@ export class Frontend extends EventEmitter {
|
|
|
1654
1367
|
if (isValidString(data.params.value, 4)) {
|
|
1655
1368
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1656
1369
|
if (data.params.value === 'Debug') {
|
|
1657
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1370
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1658
1371
|
}
|
|
1659
1372
|
else if (data.params.value === 'Info') {
|
|
1660
|
-
await this.matterbridge.setLogLevel("info"
|
|
1373
|
+
await this.matterbridge.setLogLevel("info");
|
|
1661
1374
|
}
|
|
1662
1375
|
else if (data.params.value === 'Notice') {
|
|
1663
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1376
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1664
1377
|
}
|
|
1665
1378
|
else if (data.params.value === 'Warn') {
|
|
1666
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1379
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1667
1380
|
}
|
|
1668
1381
|
else if (data.params.value === 'Error') {
|
|
1669
|
-
await this.matterbridge.setLogLevel("error"
|
|
1382
|
+
await this.matterbridge.setLogLevel("error");
|
|
1670
1383
|
}
|
|
1671
1384
|
else if (data.params.value === 'Fatal') {
|
|
1672
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1385
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1673
1386
|
}
|
|
1674
1387
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1675
1388
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1680,7 +1393,6 @@ export class Frontend extends EventEmitter {
|
|
|
1680
1393
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1681
1394
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1682
1395
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1683
|
-
// Create the file logger for matterbridge
|
|
1684
1396
|
if (data.params.value)
|
|
1685
1397
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1686
1398
|
else
|
|
@@ -1727,7 +1439,6 @@ export class Frontend extends EventEmitter {
|
|
|
1727
1439
|
});
|
|
1728
1440
|
}
|
|
1729
1441
|
catch (error) {
|
|
1730
|
-
/* istanbul ignore next */
|
|
1731
1442
|
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}`);
|
|
1732
1443
|
}
|
|
1733
1444
|
}
|
|
@@ -1736,7 +1447,6 @@ export class Frontend extends EventEmitter {
|
|
|
1736
1447
|
Logger.removeLogger('matterfilelogger');
|
|
1737
1448
|
}
|
|
1738
1449
|
catch (error) {
|
|
1739
|
-
/* istanbul ignore next */
|
|
1740
1450
|
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}`);
|
|
1741
1451
|
}
|
|
1742
1452
|
}
|
|
@@ -1849,19 +1559,15 @@ export class Frontend extends EventEmitter {
|
|
|
1849
1559
|
return;
|
|
1850
1560
|
}
|
|
1851
1561
|
const config = plugin.configJson;
|
|
1852
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1853
1562
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1854
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1855
1563
|
if (select === 'serial')
|
|
1856
1564
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1857
1565
|
if (select === 'name')
|
|
1858
1566
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1859
1567
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1860
|
-
// Remove postfix from the serial if it exists
|
|
1861
1568
|
if (config.postfix) {
|
|
1862
1569
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1863
1570
|
}
|
|
1864
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1865
1571
|
if (isValidArray(config.whiteList, 1)) {
|
|
1866
1572
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1867
1573
|
config.whiteList.push(data.params.serial);
|
|
@@ -1870,7 +1576,6 @@ export class Frontend extends EventEmitter {
|
|
|
1870
1576
|
config.whiteList.push(data.params.name);
|
|
1871
1577
|
}
|
|
1872
1578
|
}
|
|
1873
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1874
1579
|
if (isValidArray(config.blackList, 1)) {
|
|
1875
1580
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1876
1581
|
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
@@ -1901,9 +1606,7 @@ export class Frontend extends EventEmitter {
|
|
|
1901
1606
|
return;
|
|
1902
1607
|
}
|
|
1903
1608
|
const config = plugin.configJson;
|
|
1904
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1905
1609
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1906
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1907
1610
|
if (select === 'serial')
|
|
1908
1611
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1909
1612
|
if (select === 'name')
|
|
@@ -1912,7 +1615,6 @@ export class Frontend extends EventEmitter {
|
|
|
1912
1615
|
if (config.postfix) {
|
|
1913
1616
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1914
1617
|
}
|
|
1915
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1916
1618
|
if (isValidArray(config.whiteList, 1)) {
|
|
1917
1619
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1918
1620
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
@@ -1921,7 +1623,6 @@ export class Frontend extends EventEmitter {
|
|
|
1921
1623
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1922
1624
|
}
|
|
1923
1625
|
}
|
|
1924
|
-
// Add the serial to the blackList
|
|
1925
1626
|
if (isValidArray(config.blackList)) {
|
|
1926
1627
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1927
1628
|
config.blackList.push(data.params.serial);
|
|
@@ -1955,251 +1656,126 @@ export class Frontend extends EventEmitter {
|
|
|
1955
1656
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1956
1657
|
}
|
|
1957
1658
|
}
|
|
1958
|
-
/**
|
|
1959
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1960
|
-
*
|
|
1961
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1962
|
-
* @param {string} time - The time string of the message
|
|
1963
|
-
* @param {string} name - The logger name of the message
|
|
1964
|
-
* @param {string} message - The content of the message.
|
|
1965
|
-
*
|
|
1966
|
-
* @remarks
|
|
1967
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
1968
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
1969
|
-
* The function sends the message to all connected clients.
|
|
1970
|
-
*/
|
|
1971
1659
|
wssSendMessage(level, time, name, message) {
|
|
1972
1660
|
if (!level || !time || !name || !message)
|
|
1973
1661
|
return;
|
|
1974
|
-
// Remove ANSI escape codes from the message
|
|
1975
|
-
// eslint-disable-next-line no-control-regex
|
|
1976
1662
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1977
|
-
// Remove leading asterisks from the message
|
|
1978
1663
|
message = message.replace(/^\*+/, '');
|
|
1979
|
-
// Replace all occurrences of \t and \n
|
|
1980
1664
|
message = message.replace(/[\t\n]/g, '');
|
|
1981
|
-
// Remove non-printable characters
|
|
1982
|
-
// eslint-disable-next-line no-control-regex
|
|
1983
1665
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1984
|
-
// Replace all occurrences of \" with "
|
|
1985
1666
|
message = message.replace(/\\"/g, '"');
|
|
1986
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1987
1667
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1988
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1989
1668
|
const maxContinuousLength = 100;
|
|
1990
1669
|
const keepStartLength = 20;
|
|
1991
1670
|
const keepEndLength = 20;
|
|
1992
|
-
// Split the message into words
|
|
1993
1671
|
message = message
|
|
1994
1672
|
.split(' ')
|
|
1995
1673
|
.map((word) => {
|
|
1996
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1997
1674
|
if (word.length > maxContinuousLength) {
|
|
1998
1675
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1999
1676
|
}
|
|
2000
1677
|
return word;
|
|
2001
1678
|
})
|
|
2002
1679
|
.join(' ');
|
|
2003
|
-
// Send the message to all connected clients
|
|
2004
1680
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2005
1681
|
if (client.readyState === WebSocket.OPEN) {
|
|
2006
1682
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
2007
1683
|
}
|
|
2008
1684
|
});
|
|
2009
1685
|
}
|
|
2010
|
-
/**
|
|
2011
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2012
|
-
*
|
|
2013
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
2014
|
-
* possible values:
|
|
2015
|
-
* - 'matterbridgeLatestVersion'
|
|
2016
|
-
* - 'matterbridgeDevVersion'
|
|
2017
|
-
* - 'matterbridgeAdvertise'
|
|
2018
|
-
* - 'online'
|
|
2019
|
-
* - 'offline'
|
|
2020
|
-
* - 'reachability'
|
|
2021
|
-
* - 'settings'
|
|
2022
|
-
* - 'plugins'
|
|
2023
|
-
* - 'pluginsRestart'
|
|
2024
|
-
* - 'devices'
|
|
2025
|
-
* - 'fabrics'
|
|
2026
|
-
* - 'sessions'
|
|
2027
|
-
*/
|
|
2028
1686
|
wssSendRefreshRequired(changed = null) {
|
|
2029
1687
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
2030
|
-
// Send the message to all connected clients
|
|
2031
1688
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2032
1689
|
if (client.readyState === WebSocket.OPEN) {
|
|
2033
1690
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
2034
1691
|
}
|
|
2035
1692
|
});
|
|
2036
1693
|
}
|
|
2037
|
-
/**
|
|
2038
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2039
|
-
*
|
|
2040
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
|
|
2041
|
-
* @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
|
|
2042
|
-
*/
|
|
2043
1694
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
2044
1695
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
2045
1696
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
2046
1697
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
|
|
2047
1698
|
if (snackbar === true)
|
|
2048
1699
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
2049
|
-
// Send the message to all connected clients
|
|
2050
1700
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2051
1701
|
if (client.readyState === WebSocket.OPEN) {
|
|
2052
1702
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } }));
|
|
2053
1703
|
}
|
|
2054
1704
|
});
|
|
2055
1705
|
}
|
|
2056
|
-
/**
|
|
2057
|
-
* Sends a no need to restart WebSocket message to all connected clients.
|
|
2058
|
-
*
|
|
2059
|
-
* @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients.
|
|
2060
|
-
*/
|
|
2061
1706
|
wssSendRestartNotRequired(snackbar = true) {
|
|
2062
1707
|
this.log.debug('Sending a restart not required message to all connected clients');
|
|
2063
1708
|
this.matterbridge.matterbridgeInformation.restartRequired = false;
|
|
2064
1709
|
if (snackbar === true)
|
|
2065
1710
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2066
|
-
// Send the message to all connected clients
|
|
2067
1711
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2068
1712
|
if (client.readyState === WebSocket.OPEN) {
|
|
2069
1713
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', params: {} }));
|
|
2070
1714
|
}
|
|
2071
1715
|
});
|
|
2072
1716
|
}
|
|
2073
|
-
/**
|
|
2074
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
2075
|
-
*
|
|
2076
|
-
* @param {boolean} devVersion - If true, the update is for a development version.
|
|
2077
|
-
*/
|
|
2078
1717
|
wssSendUpdateRequired(devVersion = false) {
|
|
2079
1718
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2080
1719
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
2081
|
-
// Send the message to all connected clients
|
|
2082
1720
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2083
1721
|
if (client.readyState === WebSocket.OPEN) {
|
|
2084
1722
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required', params: {} }));
|
|
2085
1723
|
}
|
|
2086
1724
|
});
|
|
2087
1725
|
}
|
|
2088
|
-
/**
|
|
2089
|
-
* Sends a cpu update message to all connected clients.
|
|
2090
|
-
*
|
|
2091
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2092
|
-
*/
|
|
2093
1726
|
wssSendCpuUpdate(cpuUsage) {
|
|
2094
1727
|
if (hasParameter('debug'))
|
|
2095
1728
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2096
|
-
// Send the message to all connected clients
|
|
2097
1729
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2098
1730
|
if (client.readyState === WebSocket.OPEN) {
|
|
2099
1731
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
2100
1732
|
}
|
|
2101
1733
|
});
|
|
2102
1734
|
}
|
|
2103
|
-
/**
|
|
2104
|
-
* Sends a memory update message to all connected clients.
|
|
2105
|
-
*
|
|
2106
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2107
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2108
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2109
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2110
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2111
|
-
* @param {string} external - The external memory in bytes.
|
|
2112
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2113
|
-
*/
|
|
2114
1735
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2115
1736
|
if (hasParameter('debug'))
|
|
2116
1737
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2117
|
-
// Send the message to all connected clients
|
|
2118
1738
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2119
1739
|
if (client.readyState === WebSocket.OPEN) {
|
|
2120
1740
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
2121
1741
|
}
|
|
2122
1742
|
});
|
|
2123
1743
|
}
|
|
2124
|
-
/**
|
|
2125
|
-
* Sends an uptime update message to all connected clients.
|
|
2126
|
-
*
|
|
2127
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2128
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2129
|
-
*/
|
|
2130
1744
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2131
1745
|
if (hasParameter('debug'))
|
|
2132
1746
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2133
|
-
// Send the message to all connected clients
|
|
2134
1747
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2135
1748
|
if (client.readyState === WebSocket.OPEN) {
|
|
2136
1749
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
2137
1750
|
}
|
|
2138
1751
|
});
|
|
2139
1752
|
}
|
|
2140
|
-
/**
|
|
2141
|
-
* Sends an open snackbar message to all connected clients.
|
|
2142
|
-
*
|
|
2143
|
-
* @param {string} message - The message to send.
|
|
2144
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
2145
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
2146
|
-
*/
|
|
2147
1753
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2148
1754
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2149
|
-
// Send the message to all connected clients
|
|
2150
1755
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2151
1756
|
if (client.readyState === WebSocket.OPEN) {
|
|
2152
1757
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
2153
1758
|
}
|
|
2154
1759
|
});
|
|
2155
1760
|
}
|
|
2156
|
-
/**
|
|
2157
|
-
* Sends a close snackbar message to all connected clients.
|
|
2158
|
-
*
|
|
2159
|
-
* @param {string} message - The message to send.
|
|
2160
|
-
*/
|
|
2161
1761
|
wssSendCloseSnackbarMessage(message) {
|
|
2162
1762
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2163
|
-
// Send the message to all connected clients
|
|
2164
1763
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2165
1764
|
if (client.readyState === WebSocket.OPEN) {
|
|
2166
1765
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
2167
1766
|
}
|
|
2168
1767
|
});
|
|
2169
1768
|
}
|
|
2170
|
-
/**
|
|
2171
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2172
|
-
*
|
|
2173
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2174
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2175
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2176
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2177
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2178
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2179
|
-
*
|
|
2180
|
-
* @remarks
|
|
2181
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2182
|
-
* with the updated attribute information.
|
|
2183
|
-
*/
|
|
2184
1769
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
2185
1770
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2186
|
-
// Send the message to all connected clients
|
|
2187
1771
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2188
1772
|
if (client.readyState === WebSocket.OPEN) {
|
|
2189
1773
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
2190
1774
|
}
|
|
2191
1775
|
});
|
|
2192
1776
|
}
|
|
2193
|
-
/**
|
|
2194
|
-
* Sends a message to all connected clients.
|
|
2195
|
-
*
|
|
2196
|
-
* @param {number} id - The message id.
|
|
2197
|
-
* @param {string} method - The message method.
|
|
2198
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
2199
|
-
*/
|
|
2200
1777
|
wssBroadcastMessage(id, method, params) {
|
|
2201
1778
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
2202
|
-
// Send the message to all connected clients
|
|
2203
1779
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2204
1780
|
if (client.readyState === WebSocket.OPEN) {
|
|
2205
1781
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -2207,4 +1783,3 @@ export class Frontend extends EventEmitter {
|
|
|
2207
1783
|
});
|
|
2208
1784
|
}
|
|
2209
1785
|
}
|
|
2210
|
-
//# sourceMappingURL=frontend.js.map
|