matterbridge 3.1.8 → 3.1.9-dev-20250729-d64715f
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 +13 -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 -449
- 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 -803
- 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 -1215
- package/dist/matterbridgeEndpointHelpers.js +12 -344
- package/dist/matterbridgePlatform.js +0 -233
- 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 -63
- 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 -81
- package/dist/utils/spawn.js +0 -40
- package/dist/utils/wait.js +9 -62
- package/npm-shrinkwrap.json +48 -48
- package/package.json +2 -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 -1348
- 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 -68
- 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 -74
- 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,11 +358,9 @@ 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 {
|
|
514
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
515
364
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), fs.constants.F_OK);
|
|
516
365
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), 'utf8');
|
|
517
366
|
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), data, 'utf-8');
|
|
@@ -522,18 +371,15 @@ export class Frontend extends EventEmitter {
|
|
|
522
371
|
}
|
|
523
372
|
res.type('text/plain');
|
|
524
373
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'matterbridge.log', (error) => {
|
|
525
|
-
/* istanbul ignore if */
|
|
526
374
|
if (error) {
|
|
527
375
|
this.log.error(`Error downloading log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
528
376
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
529
377
|
}
|
|
530
378
|
});
|
|
531
379
|
});
|
|
532
|
-
// Endpoint to download the matter log
|
|
533
380
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
534
381
|
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
535
382
|
try {
|
|
536
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
537
383
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
538
384
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'utf8');
|
|
539
385
|
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), data, 'utf-8');
|
|
@@ -544,18 +390,15 @@ export class Frontend extends EventEmitter {
|
|
|
544
390
|
}
|
|
545
391
|
res.type('text/plain');
|
|
546
392
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
|
|
547
|
-
/* istanbul ignore if */
|
|
548
393
|
if (error) {
|
|
549
394
|
this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
550
395
|
res.status(500).send('Error downloading the matter log file');
|
|
551
396
|
}
|
|
552
397
|
});
|
|
553
398
|
});
|
|
554
|
-
// Endpoint to download the shelly log
|
|
555
399
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
556
400
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
557
401
|
try {
|
|
558
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
559
402
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
|
|
560
403
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
|
|
561
404
|
await fs.writeFile(path.join(os.tmpdir(), 'shelly.log'), data, 'utf-8');
|
|
@@ -566,90 +409,74 @@ export class Frontend extends EventEmitter {
|
|
|
566
409
|
}
|
|
567
410
|
res.type('text/plain');
|
|
568
411
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
569
|
-
/* istanbul ignore if */
|
|
570
412
|
if (error) {
|
|
571
413
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
572
414
|
res.status(500).send('Error downloading Shelly system log file');
|
|
573
415
|
}
|
|
574
416
|
});
|
|
575
417
|
});
|
|
576
|
-
// Endpoint to download the matterbridge storage directory
|
|
577
418
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
578
419
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
579
420
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
580
421
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), `matterbridge.${this.matterbridge.nodeStorageName}.zip`, (error) => {
|
|
581
|
-
/* istanbul ignore if */
|
|
582
422
|
if (error) {
|
|
583
423
|
this.log.error(`Error downloading file ${`matterbridge.${this.matterbridge.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
|
|
584
424
|
res.status(500).send('Error downloading the matterbridge storage file');
|
|
585
425
|
}
|
|
586
426
|
});
|
|
587
427
|
});
|
|
588
|
-
// Endpoint to download the matter storage file
|
|
589
428
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
590
429
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
591
430
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
592
431
|
res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), `matterbridge.${this.matterbridge.matterStorageName}.zip`, (error) => {
|
|
593
|
-
/* istanbul ignore if */
|
|
594
432
|
if (error) {
|
|
595
433
|
this.log.error(`Error downloading the matter storage matterbridge.${this.matterbridge.matterStorageName}.zip: ${error instanceof Error ? error.message : error}`);
|
|
596
434
|
res.status(500).send('Error downloading the matter storage zip file');
|
|
597
435
|
}
|
|
598
436
|
});
|
|
599
437
|
});
|
|
600
|
-
// Endpoint to download the matterbridge plugin directory
|
|
601
438
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
602
439
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
603
440
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
604
441
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
|
|
605
|
-
/* istanbul ignore if */
|
|
606
442
|
if (error) {
|
|
607
443
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
608
444
|
res.status(500).send('Error downloading the matterbridge plugin storage file');
|
|
609
445
|
}
|
|
610
446
|
});
|
|
611
447
|
});
|
|
612
|
-
// Endpoint to download the matterbridge plugin config files
|
|
613
448
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
614
449
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
615
450
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
616
451
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
617
|
-
/* istanbul ignore if */
|
|
618
452
|
if (error) {
|
|
619
453
|
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
620
454
|
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
621
455
|
}
|
|
622
456
|
});
|
|
623
457
|
});
|
|
624
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
625
458
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
626
459
|
this.log.debug('The frontend sent /api/download-backup');
|
|
627
460
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
628
|
-
/* istanbul ignore if */
|
|
629
461
|
if (error) {
|
|
630
462
|
this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
631
463
|
res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
|
|
632
464
|
}
|
|
633
465
|
});
|
|
634
466
|
});
|
|
635
|
-
// Endpoint to upload a package
|
|
636
467
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
637
468
|
const { filename } = req.body;
|
|
638
469
|
const file = req.file;
|
|
639
|
-
/* istanbul ignore if */
|
|
640
470
|
if (!file || !filename) {
|
|
641
471
|
this.log.error(`uploadpackage: invalid request: file and filename are required`);
|
|
642
472
|
res.status(400).send('Invalid request: file and filename are required');
|
|
643
473
|
return;
|
|
644
474
|
}
|
|
645
475
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
646
|
-
// Define the path where the plugin file will be saved
|
|
647
476
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
648
477
|
try {
|
|
649
|
-
// Move the uploaded file to the specified path
|
|
650
478
|
await fs.rename(file.path, filePath);
|
|
651
479
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
652
|
-
// Install the plugin package
|
|
653
480
|
if (filename.endsWith('.tgz')) {
|
|
654
481
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
655
482
|
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
@@ -669,7 +496,6 @@ export class Frontend extends EventEmitter {
|
|
|
669
496
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
670
497
|
}
|
|
671
498
|
});
|
|
672
|
-
// Fallback for routing (must be the last route)
|
|
673
499
|
this.expressApp.use((req, res) => {
|
|
674
500
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
|
|
675
501
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -678,16 +504,13 @@ export class Frontend extends EventEmitter {
|
|
|
678
504
|
}
|
|
679
505
|
async stop() {
|
|
680
506
|
this.log.debug('Stopping the frontend...');
|
|
681
|
-
// Remove listeners from the express app
|
|
682
507
|
if (this.expressApp) {
|
|
683
508
|
this.expressApp.removeAllListeners();
|
|
684
509
|
this.expressApp = undefined;
|
|
685
510
|
this.log.debug('Frontend app closed successfully');
|
|
686
511
|
}
|
|
687
|
-
// Close the WebSocket server
|
|
688
512
|
if (this.webSocketServer) {
|
|
689
513
|
this.log.debug('Closing WebSocket server...');
|
|
690
|
-
// Close all active connections
|
|
691
514
|
this.webSocketServer.clients.forEach((client) => {
|
|
692
515
|
if (client.readyState === WebSocket.OPEN) {
|
|
693
516
|
client.close();
|
|
@@ -696,7 +519,6 @@ export class Frontend extends EventEmitter {
|
|
|
696
519
|
await withTimeout(new Promise((resolve) => {
|
|
697
520
|
this.webSocketServer?.close((error) => {
|
|
698
521
|
if (error) {
|
|
699
|
-
// istanbul ignore next
|
|
700
522
|
this.log.error(`Error closing WebSocket server: ${error}`);
|
|
701
523
|
}
|
|
702
524
|
else {
|
|
@@ -709,13 +531,11 @@ export class Frontend extends EventEmitter {
|
|
|
709
531
|
this.webSocketServer.removeAllListeners();
|
|
710
532
|
this.webSocketServer = undefined;
|
|
711
533
|
}
|
|
712
|
-
// Close the http server
|
|
713
534
|
if (this.httpServer) {
|
|
714
535
|
this.log.debug('Closing http server...');
|
|
715
536
|
await withTimeout(new Promise((resolve) => {
|
|
716
537
|
this.httpServer?.close((error) => {
|
|
717
538
|
if (error) {
|
|
718
|
-
// istanbul ignore next
|
|
719
539
|
this.log.error(`Error closing http server: ${error}`);
|
|
720
540
|
}
|
|
721
541
|
else {
|
|
@@ -729,13 +549,11 @@ export class Frontend extends EventEmitter {
|
|
|
729
549
|
this.httpServer = undefined;
|
|
730
550
|
this.log.debug('Frontend http server closed successfully');
|
|
731
551
|
}
|
|
732
|
-
// Close the https server
|
|
733
552
|
if (this.httpsServer) {
|
|
734
553
|
this.log.debug('Closing https server...');
|
|
735
554
|
await withTimeout(new Promise((resolve) => {
|
|
736
555
|
this.httpsServer?.close((error) => {
|
|
737
556
|
if (error) {
|
|
738
|
-
// istanbul ignore next
|
|
739
557
|
this.log.error(`Error closing https server: ${error}`);
|
|
740
558
|
}
|
|
741
559
|
else {
|
|
@@ -751,7 +569,6 @@ export class Frontend extends EventEmitter {
|
|
|
751
569
|
}
|
|
752
570
|
this.log.debug('Frontend stopped successfully');
|
|
753
571
|
}
|
|
754
|
-
// Function to format bytes to KB, MB, or GB
|
|
755
572
|
formatMemoryUsage = (bytes) => {
|
|
756
573
|
if (bytes >= 1024 ** 3) {
|
|
757
574
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -763,7 +580,6 @@ export class Frontend extends EventEmitter {
|
|
|
763
580
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
764
581
|
}
|
|
765
582
|
};
|
|
766
|
-
// Function to format system uptime with only the most significant unit
|
|
767
583
|
formatOsUpTime = (seconds) => {
|
|
768
584
|
if (seconds >= 86400) {
|
|
769
585
|
const days = Math.floor(seconds / 86400);
|
|
@@ -779,13 +595,7 @@ export class Frontend extends EventEmitter {
|
|
|
779
595
|
}
|
|
780
596
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
781
597
|
};
|
|
782
|
-
/**
|
|
783
|
-
* Retrieves the api settings data.
|
|
784
|
-
*
|
|
785
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
786
|
-
*/
|
|
787
598
|
async getApiSettings() {
|
|
788
|
-
// Update the system information
|
|
789
599
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
790
600
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
791
601
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -794,7 +604,6 @@ export class Frontend extends EventEmitter {
|
|
|
794
604
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
795
605
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
796
606
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
797
|
-
// Update the matterbridge information
|
|
798
607
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
799
608
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
800
609
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
@@ -806,7 +615,6 @@ export class Frontend extends EventEmitter {
|
|
|
806
615
|
this.matterbridge.matterbridgeInformation.matterPort = (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540;
|
|
807
616
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = await this.matterbridge.nodeContext?.get('matterdiscriminator');
|
|
808
617
|
this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
|
|
809
|
-
// Update the matterbridge information in bridge mode
|
|
810
618
|
if (this.matterbridge.bridgeMode === 'bridge' && this.matterbridge.serverNode && !this.matterbridge.hasCleanupStarted) {
|
|
811
619
|
this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.serverNode.state.commissioning.commissioned;
|
|
812
620
|
this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.qrPairingCode;
|
|
@@ -816,12 +624,6 @@ export class Frontend extends EventEmitter {
|
|
|
816
624
|
}
|
|
817
625
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
818
626
|
}
|
|
819
|
-
/**
|
|
820
|
-
* Retrieves the reachable attribute.
|
|
821
|
-
*
|
|
822
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
823
|
-
* @returns {boolean} The reachable attribute.
|
|
824
|
-
*/
|
|
825
627
|
getReachability(device) {
|
|
826
628
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
827
629
|
return false;
|
|
@@ -833,12 +635,6 @@ export class Frontend extends EventEmitter {
|
|
|
833
635
|
return true;
|
|
834
636
|
return false;
|
|
835
637
|
}
|
|
836
|
-
/**
|
|
837
|
-
* Retrieves the power source attribute.
|
|
838
|
-
*
|
|
839
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
|
|
840
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
841
|
-
*/
|
|
842
638
|
getPowerSource(endpoint) {
|
|
843
639
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
844
640
|
return undefined;
|
|
@@ -854,21 +650,13 @@ export class Frontend extends EventEmitter {
|
|
|
854
650
|
}
|
|
855
651
|
return;
|
|
856
652
|
};
|
|
857
|
-
// Root endpoint
|
|
858
653
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
859
654
|
return powerSource(endpoint);
|
|
860
|
-
// Child endpoints
|
|
861
655
|
for (const child of endpoint.getChildEndpoints()) {
|
|
862
656
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
863
657
|
return powerSource(child);
|
|
864
658
|
}
|
|
865
659
|
}
|
|
866
|
-
/**
|
|
867
|
-
* Retrieves the commissioned status, matter pairing codes, fabrics and sessions from a given device in server mode.
|
|
868
|
-
*
|
|
869
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the data from.
|
|
870
|
-
* @returns {ApiDevicesMatter | undefined} An ApiDevicesMatter object or undefined if not found.
|
|
871
|
-
*/
|
|
872
660
|
getMatterDataFromDevice(device) {
|
|
873
661
|
if (device.mode === 'server' && device.serverNode) {
|
|
874
662
|
return {
|
|
@@ -880,13 +668,6 @@ export class Frontend extends EventEmitter {
|
|
|
880
668
|
};
|
|
881
669
|
}
|
|
882
670
|
}
|
|
883
|
-
/**
|
|
884
|
-
* Retrieves the cluster text description from a given device.
|
|
885
|
-
* The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
|
|
886
|
-
*
|
|
887
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
|
|
888
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
889
|
-
*/
|
|
890
671
|
getClusterTextFromDevice(device) {
|
|
891
672
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
892
673
|
return '';
|
|
@@ -897,7 +678,6 @@ export class Frontend extends EventEmitter {
|
|
|
897
678
|
if (composed)
|
|
898
679
|
return 'Composed: ' + composed.value;
|
|
899
680
|
}
|
|
900
|
-
// istanbul ignore next cause is not reachable
|
|
901
681
|
return '';
|
|
902
682
|
};
|
|
903
683
|
const getFixedLabel = (device) => {
|
|
@@ -907,13 +687,11 @@ export class Frontend extends EventEmitter {
|
|
|
907
687
|
if (composed)
|
|
908
688
|
return 'Composed: ' + composed.value;
|
|
909
689
|
}
|
|
910
|
-
// istanbul ignore next cause is not reacheable
|
|
911
690
|
return '';
|
|
912
691
|
};
|
|
913
692
|
let attributes = '';
|
|
914
693
|
let supportedModes = [];
|
|
915
694
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
916
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
917
695
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
918
696
|
return;
|
|
919
697
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -1003,17 +781,11 @@ export class Frontend extends EventEmitter {
|
|
|
1003
781
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
1004
782
|
attributes += `${getUserLabel(device)} `;
|
|
1005
783
|
});
|
|
1006
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
1007
784
|
return attributes.trimStart().trimEnd();
|
|
1008
785
|
}
|
|
1009
|
-
/**
|
|
1010
|
-
* Retrieves the registered plugins sanitized for res.json().
|
|
1011
|
-
*
|
|
1012
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
1013
|
-
*/
|
|
1014
786
|
getPlugins() {
|
|
1015
787
|
if (this.matterbridge.hasCleanupStarted)
|
|
1016
|
-
return [];
|
|
788
|
+
return [];
|
|
1017
789
|
const baseRegisteredPlugins = [];
|
|
1018
790
|
for (const plugin of this.matterbridge.plugins) {
|
|
1019
791
|
baseRegisteredPlugins.push({
|
|
@@ -1043,7 +815,6 @@ export class Frontend extends EventEmitter {
|
|
|
1043
815
|
schemaJson: plugin.schemaJson,
|
|
1044
816
|
hasWhiteList: plugin.configJson?.whiteList !== undefined,
|
|
1045
817
|
hasBlackList: plugin.configJson?.blackList !== undefined,
|
|
1046
|
-
// Childbridge mode specific data
|
|
1047
818
|
paired: plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.commissioned : undefined,
|
|
1048
819
|
qrPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.pairingCodes.qrPairingCode : undefined,
|
|
1049
820
|
manualPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.pairingCodes.manualPairingCode : undefined,
|
|
@@ -1053,21 +824,13 @@ export class Frontend extends EventEmitter {
|
|
|
1053
824
|
}
|
|
1054
825
|
return baseRegisteredPlugins;
|
|
1055
826
|
}
|
|
1056
|
-
/**
|
|
1057
|
-
* Retrieves the devices from Matterbridge.
|
|
1058
|
-
*
|
|
1059
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1060
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
|
|
1061
|
-
*/
|
|
1062
827
|
async getDevices(pluginName) {
|
|
1063
828
|
if (this.matterbridge.hasCleanupStarted)
|
|
1064
|
-
return [];
|
|
829
|
+
return [];
|
|
1065
830
|
const devices = [];
|
|
1066
831
|
for (const device of this.matterbridge.devices.array()) {
|
|
1067
|
-
// Filter by pluginName if provided
|
|
1068
832
|
if (pluginName && pluginName !== device.plugin)
|
|
1069
833
|
continue;
|
|
1070
|
-
// Check if the device has the required properties
|
|
1071
834
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1072
835
|
continue;
|
|
1073
836
|
devices.push({
|
|
@@ -1087,37 +850,22 @@ export class Frontend extends EventEmitter {
|
|
|
1087
850
|
}
|
|
1088
851
|
return devices;
|
|
1089
852
|
}
|
|
1090
|
-
/**
|
|
1091
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1092
|
-
*
|
|
1093
|
-
* Response for /api/clusters
|
|
1094
|
-
*
|
|
1095
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1096
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1097
|
-
* @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1098
|
-
*/
|
|
1099
853
|
getClusters(pluginName, endpointNumber) {
|
|
1100
854
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1101
855
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1102
856
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1103
857
|
return;
|
|
1104
858
|
}
|
|
1105
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1106
|
-
// Get the device types from the main endpoint
|
|
1107
859
|
const deviceTypes = [];
|
|
1108
860
|
const clusters = [];
|
|
1109
861
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1110
862
|
deviceTypes.push(d.deviceType);
|
|
1111
863
|
});
|
|
1112
|
-
// Get the clusters from the main endpoint
|
|
1113
864
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1114
865
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1115
866
|
return;
|
|
1116
867
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1117
868
|
return;
|
|
1118
|
-
// console.log(
|
|
1119
|
-
// `${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}`,
|
|
1120
|
-
// );
|
|
1121
869
|
clusters.push({
|
|
1122
870
|
endpoint: endpoint.number.toString(),
|
|
1123
871
|
id: 'main',
|
|
@@ -1130,19 +878,12 @@ export class Frontend extends EventEmitter {
|
|
|
1130
878
|
attributeLocalValue: attributeValue,
|
|
1131
879
|
});
|
|
1132
880
|
});
|
|
1133
|
-
// Get the child endpoints
|
|
1134
881
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1135
|
-
// if (childEndpoints.length === 0) {
|
|
1136
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1137
|
-
// }
|
|
1138
882
|
childEndpoints.forEach((childEndpoint) => {
|
|
1139
|
-
// istanbul ignore if cause is not reachable: should never happen but ...
|
|
1140
883
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1141
884
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1142
885
|
return;
|
|
1143
886
|
}
|
|
1144
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1145
|
-
// Get the device types of the child endpoint
|
|
1146
887
|
const deviceTypes = [];
|
|
1147
888
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1148
889
|
deviceTypes.push(d.deviceType);
|
|
@@ -1152,12 +893,9 @@ export class Frontend extends EventEmitter {
|
|
|
1152
893
|
return;
|
|
1153
894
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1154
895
|
return;
|
|
1155
|
-
// console.log(
|
|
1156
|
-
// `${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}`,
|
|
1157
|
-
// );
|
|
1158
896
|
clusters.push({
|
|
1159
897
|
endpoint: childEndpoint.number.toString(),
|
|
1160
|
-
id: childEndpoint.maybeId ?? 'null',
|
|
898
|
+
id: childEndpoint.maybeId ?? 'null',
|
|
1161
899
|
deviceTypes,
|
|
1162
900
|
clusterName: capitalizeFirstLetter(clusterName),
|
|
1163
901
|
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
@@ -1170,13 +908,6 @@ export class Frontend extends EventEmitter {
|
|
|
1170
908
|
});
|
|
1171
909
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
|
|
1172
910
|
}
|
|
1173
|
-
/**
|
|
1174
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1175
|
-
*
|
|
1176
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1177
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1178
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1179
|
-
*/
|
|
1180
911
|
async wsMessageHandler(client, message) {
|
|
1181
912
|
let data;
|
|
1182
913
|
try {
|
|
@@ -1223,44 +954,33 @@ export class Frontend extends EventEmitter {
|
|
|
1223
954
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1224
955
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1225
956
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1226
|
-
// The install comes from InstallPlugins
|
|
1227
957
|
this.matterbridge.plugins
|
|
1228
958
|
.add(packageName)
|
|
1229
959
|
.then((plugin) => {
|
|
1230
960
|
if (plugin) {
|
|
1231
|
-
// The plugin is not registered
|
|
1232
961
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1233
962
|
this.matterbridge.plugins
|
|
1234
963
|
.load(plugin, true, 'The plugin has been added', true)
|
|
1235
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1236
964
|
.then(() => {
|
|
1237
965
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
1238
966
|
this.wssSendRefreshRequired('plugins');
|
|
1239
967
|
return;
|
|
1240
968
|
})
|
|
1241
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1242
969
|
.catch((_error) => {
|
|
1243
|
-
//
|
|
1244
970
|
});
|
|
1245
971
|
}
|
|
1246
972
|
else {
|
|
1247
|
-
// The plugin is already registered
|
|
1248
973
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1249
974
|
this.wssSendRefreshRequired('plugins');
|
|
1250
975
|
this.wssSendRestartRequired(true, true);
|
|
1251
976
|
}
|
|
1252
977
|
return;
|
|
1253
978
|
})
|
|
1254
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1255
979
|
.catch((_error) => {
|
|
1256
|
-
//
|
|
1257
980
|
});
|
|
1258
981
|
}
|
|
1259
982
|
else {
|
|
1260
|
-
// The package is matterbridge
|
|
1261
|
-
// istanbul ignore next
|
|
1262
983
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
|
|
1263
|
-
// istanbul ignore next if
|
|
1264
984
|
if (this.matterbridge.restartMode !== '') {
|
|
1265
985
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1266
986
|
this.matterbridge.shutdownProcess();
|
|
@@ -1282,7 +1002,6 @@ export class Frontend extends EventEmitter {
|
|
|
1282
1002
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1283
1003
|
return;
|
|
1284
1004
|
}
|
|
1285
|
-
// The package is a plugin
|
|
1286
1005
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1287
1006
|
if (plugin) {
|
|
1288
1007
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
@@ -1291,7 +1010,6 @@ export class Frontend extends EventEmitter {
|
|
|
1291
1010
|
this.wssSendRefreshRequired('plugins');
|
|
1292
1011
|
this.wssSendRefreshRequired('devices');
|
|
1293
1012
|
}
|
|
1294
|
-
// Uninstall the package
|
|
1295
1013
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1296
1014
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
1297
1015
|
spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1332,7 +1050,6 @@ export class Frontend extends EventEmitter {
|
|
|
1332
1050
|
return;
|
|
1333
1051
|
})
|
|
1334
1052
|
.catch((_error) => {
|
|
1335
|
-
//
|
|
1336
1053
|
});
|
|
1337
1054
|
}
|
|
1338
1055
|
else {
|
|
@@ -1379,7 +1096,6 @@ export class Frontend extends EventEmitter {
|
|
|
1379
1096
|
return;
|
|
1380
1097
|
})
|
|
1381
1098
|
.catch((_error) => {
|
|
1382
|
-
//
|
|
1383
1099
|
});
|
|
1384
1100
|
}
|
|
1385
1101
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1405,7 +1121,6 @@ export class Frontend extends EventEmitter {
|
|
|
1405
1121
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1406
1122
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
|
|
1407
1123
|
if (plugin.serverNode) {
|
|
1408
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1409
1124
|
await this.matterbridge.stopServerNode(plugin.serverNode);
|
|
1410
1125
|
plugin.serverNode = undefined;
|
|
1411
1126
|
}
|
|
@@ -1416,16 +1131,15 @@ export class Frontend extends EventEmitter {
|
|
|
1416
1131
|
}
|
|
1417
1132
|
}
|
|
1418
1133
|
await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
|
|
1419
|
-
plugin.restartRequired = false;
|
|
1134
|
+
plugin.restartRequired = false;
|
|
1420
1135
|
let needRestart = 0;
|
|
1421
1136
|
for (const plugin of this.matterbridge.plugins) {
|
|
1422
1137
|
if (plugin.restartRequired)
|
|
1423
1138
|
needRestart++;
|
|
1424
1139
|
}
|
|
1425
1140
|
if (needRestart === 0) {
|
|
1426
|
-
this.wssSendRestartNotRequired(true);
|
|
1141
|
+
this.wssSendRestartNotRequired(true);
|
|
1427
1142
|
}
|
|
1428
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1429
1143
|
if (plugin.serverNode)
|
|
1430
1144
|
await this.matterbridge.startServerNode(plugin.serverNode);
|
|
1431
1145
|
this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
|
|
@@ -1531,8 +1245,6 @@ export class Frontend extends EventEmitter {
|
|
|
1531
1245
|
else if (data.method === '/api/advertise') {
|
|
1532
1246
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
1533
1247
|
this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
|
|
1534
|
-
// this.matterbridge.matterbridgeQrPairingCode = pairingCodes?.qrPairingCode;
|
|
1535
|
-
// this.matterbridge.matterbridgeManualPairingCode = pairingCodes?.manualPairingCode;
|
|
1536
1248
|
this.wssSendRefreshRequired('matterbridgeAdvertise');
|
|
1537
1249
|
this.wssSendSnackbarMessage(`Started fabrics share`, 0);
|
|
1538
1250
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes, success: true }));
|
|
@@ -1655,22 +1367,22 @@ export class Frontend extends EventEmitter {
|
|
|
1655
1367
|
if (isValidString(data.params.value, 4)) {
|
|
1656
1368
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1657
1369
|
if (data.params.value === 'Debug') {
|
|
1658
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1370
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1659
1371
|
}
|
|
1660
1372
|
else if (data.params.value === 'Info') {
|
|
1661
|
-
await this.matterbridge.setLogLevel("info"
|
|
1373
|
+
await this.matterbridge.setLogLevel("info");
|
|
1662
1374
|
}
|
|
1663
1375
|
else if (data.params.value === 'Notice') {
|
|
1664
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1376
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1665
1377
|
}
|
|
1666
1378
|
else if (data.params.value === 'Warn') {
|
|
1667
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1379
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1668
1380
|
}
|
|
1669
1381
|
else if (data.params.value === 'Error') {
|
|
1670
|
-
await this.matterbridge.setLogLevel("error"
|
|
1382
|
+
await this.matterbridge.setLogLevel("error");
|
|
1671
1383
|
}
|
|
1672
1384
|
else if (data.params.value === 'Fatal') {
|
|
1673
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1385
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1674
1386
|
}
|
|
1675
1387
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1676
1388
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1681,7 +1393,6 @@ export class Frontend extends EventEmitter {
|
|
|
1681
1393
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1682
1394
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1683
1395
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1684
|
-
// Create the file logger for matterbridge
|
|
1685
1396
|
if (data.params.value)
|
|
1686
1397
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1687
1398
|
else
|
|
@@ -1728,7 +1439,6 @@ export class Frontend extends EventEmitter {
|
|
|
1728
1439
|
});
|
|
1729
1440
|
}
|
|
1730
1441
|
catch (error) {
|
|
1731
|
-
/* istanbul ignore next */
|
|
1732
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}`);
|
|
1733
1443
|
}
|
|
1734
1444
|
}
|
|
@@ -1737,7 +1447,6 @@ export class Frontend extends EventEmitter {
|
|
|
1737
1447
|
Logger.removeLogger('matterfilelogger');
|
|
1738
1448
|
}
|
|
1739
1449
|
catch (error) {
|
|
1740
|
-
/* istanbul ignore next */
|
|
1741
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}`);
|
|
1742
1451
|
}
|
|
1743
1452
|
}
|
|
@@ -1850,19 +1559,15 @@ export class Frontend extends EventEmitter {
|
|
|
1850
1559
|
return;
|
|
1851
1560
|
}
|
|
1852
1561
|
const config = plugin.configJson;
|
|
1853
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1854
1562
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1855
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1856
1563
|
if (select === 'serial')
|
|
1857
1564
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1858
1565
|
if (select === 'name')
|
|
1859
1566
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1860
1567
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1861
|
-
// Remove postfix from the serial if it exists
|
|
1862
1568
|
if (config.postfix) {
|
|
1863
1569
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1864
1570
|
}
|
|
1865
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1866
1571
|
if (isValidArray(config.whiteList, 1)) {
|
|
1867
1572
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1868
1573
|
config.whiteList.push(data.params.serial);
|
|
@@ -1871,7 +1576,6 @@ export class Frontend extends EventEmitter {
|
|
|
1871
1576
|
config.whiteList.push(data.params.name);
|
|
1872
1577
|
}
|
|
1873
1578
|
}
|
|
1874
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1875
1579
|
if (isValidArray(config.blackList, 1)) {
|
|
1876
1580
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1877
1581
|
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
@@ -1902,9 +1606,7 @@ export class Frontend extends EventEmitter {
|
|
|
1902
1606
|
return;
|
|
1903
1607
|
}
|
|
1904
1608
|
const config = plugin.configJson;
|
|
1905
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1906
1609
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1907
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1908
1610
|
if (select === 'serial')
|
|
1909
1611
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1910
1612
|
if (select === 'name')
|
|
@@ -1913,7 +1615,6 @@ export class Frontend extends EventEmitter {
|
|
|
1913
1615
|
if (config.postfix) {
|
|
1914
1616
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1915
1617
|
}
|
|
1916
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1917
1618
|
if (isValidArray(config.whiteList, 1)) {
|
|
1918
1619
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1919
1620
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
@@ -1922,7 +1623,6 @@ export class Frontend extends EventEmitter {
|
|
|
1922
1623
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1923
1624
|
}
|
|
1924
1625
|
}
|
|
1925
|
-
// Add the serial to the blackList
|
|
1926
1626
|
if (isValidArray(config.blackList)) {
|
|
1927
1627
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1928
1628
|
config.blackList.push(data.params.serial);
|
|
@@ -1956,251 +1656,126 @@ export class Frontend extends EventEmitter {
|
|
|
1956
1656
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1957
1657
|
}
|
|
1958
1658
|
}
|
|
1959
|
-
/**
|
|
1960
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1961
|
-
*
|
|
1962
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1963
|
-
* @param {string} time - The time string of the message
|
|
1964
|
-
* @param {string} name - The logger name of the message
|
|
1965
|
-
* @param {string} message - The content of the message.
|
|
1966
|
-
*
|
|
1967
|
-
* @remarks
|
|
1968
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
1969
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
1970
|
-
* The function sends the message to all connected clients.
|
|
1971
|
-
*/
|
|
1972
1659
|
wssSendMessage(level, time, name, message) {
|
|
1973
1660
|
if (!level || !time || !name || !message)
|
|
1974
1661
|
return;
|
|
1975
|
-
// Remove ANSI escape codes from the message
|
|
1976
|
-
// eslint-disable-next-line no-control-regex
|
|
1977
1662
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1978
|
-
// Remove leading asterisks from the message
|
|
1979
1663
|
message = message.replace(/^\*+/, '');
|
|
1980
|
-
// Replace all occurrences of \t and \n
|
|
1981
1664
|
message = message.replace(/[\t\n]/g, '');
|
|
1982
|
-
// Remove non-printable characters
|
|
1983
|
-
// eslint-disable-next-line no-control-regex
|
|
1984
1665
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1985
|
-
// Replace all occurrences of \" with "
|
|
1986
1666
|
message = message.replace(/\\"/g, '"');
|
|
1987
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1988
1667
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1989
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1990
1668
|
const maxContinuousLength = 100;
|
|
1991
1669
|
const keepStartLength = 20;
|
|
1992
1670
|
const keepEndLength = 20;
|
|
1993
|
-
// Split the message into words
|
|
1994
1671
|
message = message
|
|
1995
1672
|
.split(' ')
|
|
1996
1673
|
.map((word) => {
|
|
1997
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1998
1674
|
if (word.length > maxContinuousLength) {
|
|
1999
1675
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2000
1676
|
}
|
|
2001
1677
|
return word;
|
|
2002
1678
|
})
|
|
2003
1679
|
.join(' ');
|
|
2004
|
-
// Send the message to all connected clients
|
|
2005
1680
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2006
1681
|
if (client.readyState === WebSocket.OPEN) {
|
|
2007
1682
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
2008
1683
|
}
|
|
2009
1684
|
});
|
|
2010
1685
|
}
|
|
2011
|
-
/**
|
|
2012
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2013
|
-
*
|
|
2014
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
2015
|
-
* possible values:
|
|
2016
|
-
* - 'matterbridgeLatestVersion'
|
|
2017
|
-
* - 'matterbridgeDevVersion'
|
|
2018
|
-
* - 'matterbridgeAdvertise'
|
|
2019
|
-
* - 'online'
|
|
2020
|
-
* - 'offline'
|
|
2021
|
-
* - 'reachability'
|
|
2022
|
-
* - 'settings'
|
|
2023
|
-
* - 'plugins'
|
|
2024
|
-
* - 'pluginsRestart'
|
|
2025
|
-
* - 'devices'
|
|
2026
|
-
* - 'fabrics'
|
|
2027
|
-
* - 'sessions'
|
|
2028
|
-
*/
|
|
2029
1686
|
wssSendRefreshRequired(changed = null) {
|
|
2030
1687
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
2031
|
-
// Send the message to all connected clients
|
|
2032
1688
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2033
1689
|
if (client.readyState === WebSocket.OPEN) {
|
|
2034
1690
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
2035
1691
|
}
|
|
2036
1692
|
});
|
|
2037
1693
|
}
|
|
2038
|
-
/**
|
|
2039
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2040
|
-
*
|
|
2041
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
|
|
2042
|
-
* @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
|
|
2043
|
-
*/
|
|
2044
1694
|
wssSendRestartRequired(snackbar = true, fixed = false) {
|
|
2045
1695
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
2046
1696
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
2047
1697
|
this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
|
|
2048
1698
|
if (snackbar === true)
|
|
2049
1699
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
2050
|
-
// Send the message to all connected clients
|
|
2051
1700
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2052
1701
|
if (client.readyState === WebSocket.OPEN) {
|
|
2053
1702
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } }));
|
|
2054
1703
|
}
|
|
2055
1704
|
});
|
|
2056
1705
|
}
|
|
2057
|
-
/**
|
|
2058
|
-
* Sends a no need to restart WebSocket message to all connected clients.
|
|
2059
|
-
*
|
|
2060
|
-
* @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients.
|
|
2061
|
-
*/
|
|
2062
1706
|
wssSendRestartNotRequired(snackbar = true) {
|
|
2063
1707
|
this.log.debug('Sending a restart not required message to all connected clients');
|
|
2064
1708
|
this.matterbridge.matterbridgeInformation.restartRequired = false;
|
|
2065
1709
|
if (snackbar === true)
|
|
2066
1710
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2067
|
-
// Send the message to all connected clients
|
|
2068
1711
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2069
1712
|
if (client.readyState === WebSocket.OPEN) {
|
|
2070
1713
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', params: {} }));
|
|
2071
1714
|
}
|
|
2072
1715
|
});
|
|
2073
1716
|
}
|
|
2074
|
-
/**
|
|
2075
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
2076
|
-
*
|
|
2077
|
-
* @param {boolean} devVersion - If true, the update is for a development version.
|
|
2078
|
-
*/
|
|
2079
1717
|
wssSendUpdateRequired(devVersion = false) {
|
|
2080
1718
|
this.log.debug('Sending an update required message to all connected clients');
|
|
2081
1719
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
2082
|
-
// Send the message to all connected clients
|
|
2083
1720
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2084
1721
|
if (client.readyState === WebSocket.OPEN) {
|
|
2085
1722
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required', params: {} }));
|
|
2086
1723
|
}
|
|
2087
1724
|
});
|
|
2088
1725
|
}
|
|
2089
|
-
/**
|
|
2090
|
-
* Sends a cpu update message to all connected clients.
|
|
2091
|
-
*
|
|
2092
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
2093
|
-
*/
|
|
2094
1726
|
wssSendCpuUpdate(cpuUsage) {
|
|
2095
1727
|
if (hasParameter('debug'))
|
|
2096
1728
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2097
|
-
// Send the message to all connected clients
|
|
2098
1729
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2099
1730
|
if (client.readyState === WebSocket.OPEN) {
|
|
2100
1731
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
2101
1732
|
}
|
|
2102
1733
|
});
|
|
2103
1734
|
}
|
|
2104
|
-
/**
|
|
2105
|
-
* Sends a memory update message to all connected clients.
|
|
2106
|
-
*
|
|
2107
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2108
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2109
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2110
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2111
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2112
|
-
* @param {string} external - The external memory in bytes.
|
|
2113
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2114
|
-
*/
|
|
2115
1735
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2116
1736
|
if (hasParameter('debug'))
|
|
2117
1737
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2118
|
-
// Send the message to all connected clients
|
|
2119
1738
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2120
1739
|
if (client.readyState === WebSocket.OPEN) {
|
|
2121
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 } }));
|
|
2122
1741
|
}
|
|
2123
1742
|
});
|
|
2124
1743
|
}
|
|
2125
|
-
/**
|
|
2126
|
-
* Sends an uptime update message to all connected clients.
|
|
2127
|
-
*
|
|
2128
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2129
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2130
|
-
*/
|
|
2131
1744
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2132
1745
|
if (hasParameter('debug'))
|
|
2133
1746
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2134
|
-
// Send the message to all connected clients
|
|
2135
1747
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2136
1748
|
if (client.readyState === WebSocket.OPEN) {
|
|
2137
1749
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
2138
1750
|
}
|
|
2139
1751
|
});
|
|
2140
1752
|
}
|
|
2141
|
-
/**
|
|
2142
|
-
* Sends an open snackbar message to all connected clients.
|
|
2143
|
-
*
|
|
2144
|
-
* @param {string} message - The message to send.
|
|
2145
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
2146
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
2147
|
-
*/
|
|
2148
1753
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2149
1754
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2150
|
-
// Send the message to all connected clients
|
|
2151
1755
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2152
1756
|
if (client.readyState === WebSocket.OPEN) {
|
|
2153
1757
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
2154
1758
|
}
|
|
2155
1759
|
});
|
|
2156
1760
|
}
|
|
2157
|
-
/**
|
|
2158
|
-
* Sends a close snackbar message to all connected clients.
|
|
2159
|
-
*
|
|
2160
|
-
* @param {string} message - The message to send.
|
|
2161
|
-
*/
|
|
2162
1761
|
wssSendCloseSnackbarMessage(message) {
|
|
2163
1762
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2164
|
-
// Send the message to all connected clients
|
|
2165
1763
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2166
1764
|
if (client.readyState === WebSocket.OPEN) {
|
|
2167
1765
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
2168
1766
|
}
|
|
2169
1767
|
});
|
|
2170
1768
|
}
|
|
2171
|
-
/**
|
|
2172
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2173
|
-
*
|
|
2174
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2175
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2176
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2177
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2178
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2179
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2180
|
-
*
|
|
2181
|
-
* @remarks
|
|
2182
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2183
|
-
* with the updated attribute information.
|
|
2184
|
-
*/
|
|
2185
1769
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
2186
1770
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2187
|
-
// Send the message to all connected clients
|
|
2188
1771
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2189
1772
|
if (client.readyState === WebSocket.OPEN) {
|
|
2190
1773
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
2191
1774
|
}
|
|
2192
1775
|
});
|
|
2193
1776
|
}
|
|
2194
|
-
/**
|
|
2195
|
-
* Sends a message to all connected clients.
|
|
2196
|
-
*
|
|
2197
|
-
* @param {number} id - The message id.
|
|
2198
|
-
* @param {string} method - The message method.
|
|
2199
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
2200
|
-
*/
|
|
2201
1777
|
wssBroadcastMessage(id, method, params) {
|
|
2202
1778
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
2203
|
-
// Send the message to all connected clients
|
|
2204
1779
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2205
1780
|
if (client.readyState === WebSocket.OPEN) {
|
|
2206
1781
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -2208,4 +1783,3 @@ export class Frontend extends EventEmitter {
|
|
|
2208
1783
|
});
|
|
2209
1784
|
}
|
|
2210
1785
|
}
|
|
2211
|
-
//# sourceMappingURL=frontend.js.map
|