matterbridge 3.1.1 → 3.1.2-dev-20250706-6c6481e
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 +19 -0
- package/README-DEV.md +6 -2
- package/dist/cli.js +2 -91
- package/dist/cliEmitter.js +0 -30
- package/dist/clusters/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -24
- package/dist/deviceManager.js +1 -94
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.js +0 -2
- 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 +8 -84
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/waterHeater.js +2 -82
- package/dist/frontend.js +53 -474
- package/dist/globalMatterbridge.js +0 -47
- package/dist/helpers.js +0 -53
- package/dist/index.js +1 -39
- 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 +52 -804
- package/dist/matterbridgeAccessoryPlatform.js +0 -36
- package/dist/matterbridgeBehaviors.js +1 -61
- package/dist/matterbridgeDeviceTypes.js +15 -579
- package/dist/matterbridgeDynamicPlatform.js +0 -36
- package/dist/matterbridgeEndpoint.js +42 -1027
- package/dist/matterbridgeEndpointHelpers.js +12 -322
- package/dist/matterbridgePlatform.js +0 -233
- package/dist/matterbridgeTypes.js +0 -25
- package/dist/pluginManager.js +3 -269
- package/dist/shelly.js +7 -168
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -54
- 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/export.js +0 -1
- package/dist/utils/hex.js +0 -58
- package/dist/utils/isvalid.js +0 -101
- package/dist/utils/network.js +5 -83
- package/dist/utils/spawn.js +0 -18
- package/dist/utils/wait.js +9 -62
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- package/dist/cli.d.ts +0 -26
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cliEmitter.d.ts +0 -34
- package/dist/cliEmitter.d.ts.map +0 -1
- package/dist/cliEmitter.js.map +0 -1
- package/dist/clusters/export.d.ts +0 -2
- package/dist/clusters/export.d.ts.map +0 -1
- package/dist/clusters/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -28
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -112
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/devices/batteryStorage.d.ts +0 -48
- package/dist/devices/batteryStorage.d.ts.map +0 -1
- package/dist/devices/batteryStorage.js.map +0 -1
- package/dist/devices/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 -9
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.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 -103
- 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/frontend.d.ts +0 -302
- 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 -41
- 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 -450
- 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 -1340
- 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 -1179
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -3198
- 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 -192
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -291
- 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 -59
- 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/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 -49
- 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 -76
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/spawn.d.ts +0 -11
- 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.1.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 { 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
|
-
|
|
36
|
-
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from 'node-ansi-logger';
|
|
37
|
-
// @matter
|
|
10
|
+
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt } from 'node-ansi-logger';
|
|
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
|
-
import { capitalizeFirstLetter } from './matterbridgeEndpointHelpers.js';
|
|
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;
|
|
@@ -134,7 +38,7 @@ export class Frontend extends EventEmitter {
|
|
|
134
38
|
constructor(matterbridge) {
|
|
135
39
|
super();
|
|
136
40
|
this.matterbridge = matterbridge;
|
|
137
|
-
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4
|
|
41
|
+
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
138
42
|
}
|
|
139
43
|
set logLevel(logLevel) {
|
|
140
44
|
this.log.logLevel = logLevel;
|
|
@@ -142,41 +46,12 @@ export class Frontend extends EventEmitter {
|
|
|
142
46
|
async start(port = 8283) {
|
|
143
47
|
this.port = port;
|
|
144
48
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
|
|
145
|
-
// Initialize multer with the upload directory
|
|
146
49
|
const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
|
|
147
50
|
await fs.mkdir(uploadDir, { recursive: true });
|
|
148
51
|
const upload = multer({ dest: uploadDir });
|
|
149
|
-
// Create the express app that serves the frontend
|
|
150
52
|
this.expressApp = express();
|
|
151
|
-
// Inject logging/debug wrapper for route/middleware registration
|
|
152
|
-
/*
|
|
153
|
-
const methods = ['get', 'post', 'put', 'delete', 'use'];
|
|
154
|
-
for (const method of methods) {
|
|
155
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
|
-
const original = (this.expressApp as any)[method].bind(this.expressApp);
|
|
157
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
158
|
-
(this.expressApp as any)[method] = (path: any, ...rest: any) => {
|
|
159
|
-
try {
|
|
160
|
-
console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
|
|
161
|
-
return original(path, ...rest);
|
|
162
|
-
} catch (err) {
|
|
163
|
-
console.error(`[ERROR] Failed to register route: ${path}`);
|
|
164
|
-
throw err;
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
*/
|
|
169
|
-
// Log all requests to the server for debugging
|
|
170
|
-
/*
|
|
171
|
-
this.expressApp.use((req, res, next) => {
|
|
172
|
-
this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
|
|
173
|
-
next();
|
|
174
|
-
});
|
|
175
|
-
*/
|
|
176
|
-
// Serve static files from '/static' endpoint
|
|
177
53
|
this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
|
|
178
54
|
if (!hasParameter('ssl')) {
|
|
179
|
-
// Create an HTTP server and attach the express app
|
|
180
55
|
try {
|
|
181
56
|
this.httpServer = createServer(this.expressApp);
|
|
182
57
|
}
|
|
@@ -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}`);
|
|
@@ -217,7 +91,6 @@ export class Frontend extends EventEmitter {
|
|
|
217
91
|
});
|
|
218
92
|
}
|
|
219
93
|
else {
|
|
220
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
221
94
|
let cert;
|
|
222
95
|
try {
|
|
223
96
|
cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -247,7 +120,6 @@ export class Frontend extends EventEmitter {
|
|
|
247
120
|
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
248
121
|
}
|
|
249
122
|
const serverOptions = { cert, key, ca };
|
|
250
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
251
123
|
try {
|
|
252
124
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
253
125
|
}
|
|
@@ -256,7 +128,6 @@ export class Frontend extends EventEmitter {
|
|
|
256
128
|
this.emit('server_error', error);
|
|
257
129
|
return;
|
|
258
130
|
}
|
|
259
|
-
// Listen on the specified port
|
|
260
131
|
if (hasParameter('ingress')) {
|
|
261
132
|
this.httpsServer.listen(this.port, '0.0.0.0', () => {
|
|
262
133
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
|
|
@@ -289,18 +160,16 @@ export class Frontend extends EventEmitter {
|
|
|
289
160
|
}
|
|
290
161
|
if (this.initializeError)
|
|
291
162
|
return;
|
|
292
|
-
// Create a WebSocket server and attach it to the http or https server
|
|
293
163
|
const wssPort = this.port;
|
|
294
164
|
const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
|
|
295
165
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
296
166
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
297
167
|
const clientIp = request.socket.remoteAddress;
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
168
|
+
let callbackLogLevel = "notice";
|
|
169
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
170
|
+
callbackLogLevel = "info";
|
|
171
|
+
if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
172
|
+
callbackLogLevel = "debug";
|
|
304
173
|
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
|
|
305
174
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
306
175
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
@@ -335,7 +204,6 @@ export class Frontend extends EventEmitter {
|
|
|
335
204
|
this.webSocketServer.on('error', (ws, error) => {
|
|
336
205
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
337
206
|
});
|
|
338
|
-
// Subscribe to cli events
|
|
339
207
|
cliEmitter.removeAllListeners();
|
|
340
208
|
cliEmitter.on('uptime', (systemUptime, processUptime) => {
|
|
341
209
|
this.wssSendUptimeUpdate(systemUptime, processUptime);
|
|
@@ -346,8 +214,6 @@ export class Frontend extends EventEmitter {
|
|
|
346
214
|
cliEmitter.on('cpu', (cpuUsage) => {
|
|
347
215
|
this.wssSendCpuUpdate(cpuUsage);
|
|
348
216
|
});
|
|
349
|
-
// Endpoint to validate login code
|
|
350
|
-
// curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
|
|
351
217
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
352
218
|
const { password } = req.body;
|
|
353
219
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -366,27 +232,23 @@ export class Frontend extends EventEmitter {
|
|
|
366
232
|
this.log.warn('/api/login error wrong password');
|
|
367
233
|
res.json({ valid: false });
|
|
368
234
|
}
|
|
369
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
370
235
|
}
|
|
371
236
|
catch (error) {
|
|
372
237
|
this.log.error('/api/login error getting password');
|
|
373
238
|
res.json({ valid: false });
|
|
374
239
|
}
|
|
375
240
|
});
|
|
376
|
-
// Endpoint to provide health check for docker
|
|
377
241
|
this.expressApp.get('/health', (req, res) => {
|
|
378
242
|
this.log.debug('Express received /health');
|
|
379
243
|
const healthStatus = {
|
|
380
|
-
status: 'ok',
|
|
381
|
-
uptime: process.uptime(),
|
|
382
|
-
timestamp: new Date().toISOString(),
|
|
244
|
+
status: 'ok',
|
|
245
|
+
uptime: process.uptime(),
|
|
246
|
+
timestamp: new Date().toISOString(),
|
|
383
247
|
};
|
|
384
248
|
res.status(200).json(healthStatus);
|
|
385
249
|
});
|
|
386
|
-
// Endpoint to provide memory usage details
|
|
387
250
|
this.expressApp.get('/memory', async (req, res) => {
|
|
388
251
|
this.log.debug('Express received /memory');
|
|
389
|
-
// Memory usage from process
|
|
390
252
|
const memoryUsageRaw = process.memoryUsage();
|
|
391
253
|
const memoryUsage = {
|
|
392
254
|
rss: this.formatMemoryUsage(memoryUsageRaw.rss),
|
|
@@ -395,13 +257,10 @@ export class Frontend extends EventEmitter {
|
|
|
395
257
|
external: this.formatMemoryUsage(memoryUsageRaw.external),
|
|
396
258
|
arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
|
|
397
259
|
};
|
|
398
|
-
// V8 heap statistics
|
|
399
260
|
const { default: v8 } = await import('node:v8');
|
|
400
261
|
const heapStatsRaw = v8.getHeapStatistics();
|
|
401
262
|
const heapSpacesRaw = v8.getHeapSpaceStatistics();
|
|
402
|
-
// Format heapStats
|
|
403
263
|
const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
|
|
404
|
-
// Format heapSpaces
|
|
405
264
|
const heapSpaces = heapSpacesRaw.map((space) => ({
|
|
406
265
|
...space,
|
|
407
266
|
space_size: this.formatMemoryUsage(space.space_size),
|
|
@@ -419,36 +278,31 @@ export class Frontend extends EventEmitter {
|
|
|
419
278
|
};
|
|
420
279
|
res.status(200).json(memoryReport);
|
|
421
280
|
});
|
|
422
|
-
// Endpoint to provide settings
|
|
423
281
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
424
282
|
this.log.debug('The frontend sent /api/settings');
|
|
425
283
|
res.json(await this.getApiSettings());
|
|
426
284
|
});
|
|
427
|
-
// Endpoint to provide plugins
|
|
428
285
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
429
286
|
this.log.debug('The frontend sent /api/plugins');
|
|
430
287
|
res.json(this.getBaseRegisteredPlugins());
|
|
431
288
|
});
|
|
432
|
-
// Endpoint to provide devices
|
|
433
289
|
this.expressApp.get('/api/devices', async (req, res) => {
|
|
434
290
|
this.log.debug('The frontend sent /api/devices');
|
|
435
291
|
const devices = await this.getDevices();
|
|
436
292
|
res.json(devices);
|
|
437
293
|
});
|
|
438
|
-
// Endpoint to view the matterbridge log
|
|
439
294
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
440
295
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
441
296
|
try {
|
|
442
|
-
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.
|
|
297
|
+
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), 'utf8');
|
|
443
298
|
res.type('text/plain');
|
|
444
299
|
res.send(data);
|
|
445
300
|
}
|
|
446
301
|
catch (error) {
|
|
447
|
-
this.log.error(`Error reading matterbridge log file ${this.matterbridge.
|
|
302
|
+
this.log.error(`Error reading matterbridge log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
448
303
|
res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
|
|
449
304
|
}
|
|
450
305
|
});
|
|
451
|
-
// Endpoint to view the matter.js log
|
|
452
306
|
this.expressApp.get('/api/view-mjlog', async (req, res) => {
|
|
453
307
|
this.log.debug('The frontend sent /api/view-mjlog');
|
|
454
308
|
try {
|
|
@@ -461,7 +315,6 @@ export class Frontend extends EventEmitter {
|
|
|
461
315
|
res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
|
|
462
316
|
}
|
|
463
317
|
});
|
|
464
|
-
// Endpoint to view the shelly log
|
|
465
318
|
this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
|
|
466
319
|
this.log.debug('The frontend sent /api/shellyviewsystemlog');
|
|
467
320
|
try {
|
|
@@ -470,38 +323,32 @@ export class Frontend extends EventEmitter {
|
|
|
470
323
|
res.send(data);
|
|
471
324
|
}
|
|
472
325
|
catch (error) {
|
|
473
|
-
this.log.error(`Error reading shelly log file ${this.matterbridge.
|
|
326
|
+
this.log.error(`Error reading shelly log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
474
327
|
res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
|
|
475
328
|
}
|
|
476
329
|
});
|
|
477
|
-
// Endpoint to download the matterbridge log
|
|
478
330
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
479
|
-
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.
|
|
331
|
+
this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
480
332
|
try {
|
|
481
|
-
|
|
482
|
-
await fs.
|
|
483
|
-
|
|
484
|
-
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), data, 'utf-8');
|
|
333
|
+
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), fs.constants.F_OK);
|
|
334
|
+
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), 'utf8');
|
|
335
|
+
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), data, 'utf-8');
|
|
485
336
|
}
|
|
486
337
|
catch (error) {
|
|
487
|
-
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.
|
|
338
|
+
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'Enable the matterbridge log on file in the settings to download the matterbridge log.', 'utf-8');
|
|
488
339
|
this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
|
|
489
340
|
}
|
|
490
341
|
res.type('text/plain');
|
|
491
|
-
|
|
492
|
-
res.download(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
|
|
493
|
-
/* istanbul ignore if */
|
|
342
|
+
res.download(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'matterbridge.log', (error) => {
|
|
494
343
|
if (error) {
|
|
495
|
-
this.log.error(`Error downloading log file ${this.matterbridge.
|
|
344
|
+
this.log.error(`Error downloading log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
496
345
|
res.status(500).send('Error downloading the matterbridge log file');
|
|
497
346
|
}
|
|
498
347
|
});
|
|
499
348
|
});
|
|
500
|
-
// Endpoint to download the matter log
|
|
501
349
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
502
|
-
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.
|
|
350
|
+
this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
|
|
503
351
|
try {
|
|
504
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
505
352
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
|
|
506
353
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'utf8');
|
|
507
354
|
await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), data, 'utf-8');
|
|
@@ -512,18 +359,15 @@ export class Frontend extends EventEmitter {
|
|
|
512
359
|
}
|
|
513
360
|
res.type('text/plain');
|
|
514
361
|
res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
|
|
515
|
-
/* istanbul ignore if */
|
|
516
362
|
if (error) {
|
|
517
363
|
this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
|
|
518
364
|
res.status(500).send('Error downloading the matter log file');
|
|
519
365
|
}
|
|
520
366
|
});
|
|
521
367
|
});
|
|
522
|
-
// Endpoint to download the shelly log
|
|
523
368
|
this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
|
|
524
369
|
this.log.debug('The frontend sent /api/shellydownloadsystemlog');
|
|
525
370
|
try {
|
|
526
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
527
371
|
await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
|
|
528
372
|
const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
|
|
529
373
|
await fs.writeFile(path.join(os.tmpdir(), 'shelly.log'), data, 'utf-8');
|
|
@@ -534,14 +378,12 @@ export class Frontend extends EventEmitter {
|
|
|
534
378
|
}
|
|
535
379
|
res.type('text/plain');
|
|
536
380
|
res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
|
|
537
|
-
/* istanbul ignore if */
|
|
538
381
|
if (error) {
|
|
539
382
|
this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
|
|
540
383
|
res.status(500).send('Error downloading Shelly system log file');
|
|
541
384
|
}
|
|
542
385
|
});
|
|
543
386
|
});
|
|
544
|
-
// Endpoint to download the matterbridge storage directory
|
|
545
387
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
546
388
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
547
389
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
|
|
@@ -552,7 +394,6 @@ export class Frontend extends EventEmitter {
|
|
|
552
394
|
}
|
|
553
395
|
});
|
|
554
396
|
});
|
|
555
|
-
// Endpoint to download the matter storage file
|
|
556
397
|
this.expressApp.get('/api/download-mjstorage', async (req, res) => {
|
|
557
398
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
558
399
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
|
|
@@ -563,7 +404,6 @@ export class Frontend extends EventEmitter {
|
|
|
563
404
|
}
|
|
564
405
|
});
|
|
565
406
|
});
|
|
566
|
-
// Endpoint to download the matterbridge plugin directory
|
|
567
407
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
568
408
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
569
409
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
|
|
@@ -574,18 +414,16 @@ export class Frontend extends EventEmitter {
|
|
|
574
414
|
}
|
|
575
415
|
});
|
|
576
416
|
});
|
|
577
|
-
// Endpoint to download the matterbridge plugin config files
|
|
578
417
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
579
418
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
580
419
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
|
|
581
420
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
582
421
|
if (error) {
|
|
583
|
-
this.log.error(`Error downloading file matterbridge.
|
|
584
|
-
res.status(500).send('Error downloading the matterbridge plugin
|
|
422
|
+
this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
|
|
423
|
+
res.status(500).send('Error downloading the matterbridge plugin config file');
|
|
585
424
|
}
|
|
586
425
|
});
|
|
587
426
|
});
|
|
588
|
-
// Endpoint to download the matterbridge backup (created with the backup command)
|
|
589
427
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
590
428
|
this.log.debug('The frontend sent /api/download-backup');
|
|
591
429
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -595,7 +433,6 @@ export class Frontend extends EventEmitter {
|
|
|
595
433
|
}
|
|
596
434
|
});
|
|
597
435
|
});
|
|
598
|
-
// Endpoint to upload a package
|
|
599
436
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
600
437
|
const { filename } = req.body;
|
|
601
438
|
const file = req.file;
|
|
@@ -605,13 +442,10 @@ export class Frontend extends EventEmitter {
|
|
|
605
442
|
return;
|
|
606
443
|
}
|
|
607
444
|
this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
|
|
608
|
-
// Define the path where the plugin file will be saved
|
|
609
445
|
const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
|
|
610
446
|
try {
|
|
611
|
-
// Move the uploaded file to the specified path
|
|
612
447
|
await fs.rename(file.path, filePath);
|
|
613
448
|
this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
|
|
614
|
-
// Install the plugin package
|
|
615
449
|
if (filename.endsWith('.tgz')) {
|
|
616
450
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
617
451
|
await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
|
|
@@ -631,7 +465,6 @@ export class Frontend extends EventEmitter {
|
|
|
631
465
|
res.status(500).send(`Error uploading or installing plugin package ${filename}`);
|
|
632
466
|
}
|
|
633
467
|
});
|
|
634
|
-
// Fallback for routing (must be the last route)
|
|
635
468
|
this.expressApp.use((req, res) => {
|
|
636
469
|
this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
|
|
637
470
|
res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -640,15 +473,13 @@ export class Frontend extends EventEmitter {
|
|
|
640
473
|
}
|
|
641
474
|
async stop() {
|
|
642
475
|
this.log.debug('Stopping the frontend...');
|
|
643
|
-
// Remove listeners from the express app
|
|
644
476
|
if (this.expressApp) {
|
|
645
477
|
this.expressApp.removeAllListeners();
|
|
646
478
|
this.expressApp = undefined;
|
|
647
479
|
this.log.debug('Frontend app closed successfully');
|
|
648
480
|
}
|
|
649
|
-
// Close the WebSocket server
|
|
650
481
|
if (this.webSocketServer) {
|
|
651
|
-
|
|
482
|
+
this.log.debug('Closing WebSocket server...');
|
|
652
483
|
this.webSocketServer.clients.forEach((client) => {
|
|
653
484
|
if (client.readyState === WebSocket.OPEN) {
|
|
654
485
|
client.close();
|
|
@@ -668,8 +499,8 @@ export class Frontend extends EventEmitter {
|
|
|
668
499
|
this.webSocketServer.removeAllListeners();
|
|
669
500
|
this.webSocketServer = undefined;
|
|
670
501
|
}
|
|
671
|
-
// Close the http server
|
|
672
502
|
if (this.httpServer) {
|
|
503
|
+
this.log.debug('Closing http server...');
|
|
673
504
|
await withTimeout(new Promise((resolve) => {
|
|
674
505
|
this.httpServer?.close((error) => {
|
|
675
506
|
if (error) {
|
|
@@ -685,8 +516,8 @@ export class Frontend extends EventEmitter {
|
|
|
685
516
|
this.httpServer = undefined;
|
|
686
517
|
this.log.debug('Frontend http server closed successfully');
|
|
687
518
|
}
|
|
688
|
-
// Close the https server
|
|
689
519
|
if (this.httpsServer) {
|
|
520
|
+
this.log.debug('Closing https server...');
|
|
690
521
|
await withTimeout(new Promise((resolve) => {
|
|
691
522
|
this.httpsServer?.close((error) => {
|
|
692
523
|
if (error) {
|
|
@@ -704,7 +535,6 @@ export class Frontend extends EventEmitter {
|
|
|
704
535
|
}
|
|
705
536
|
this.log.debug('Frontend stopped successfully');
|
|
706
537
|
}
|
|
707
|
-
// Function to format bytes to KB, MB, or GB
|
|
708
538
|
formatMemoryUsage = (bytes) => {
|
|
709
539
|
if (bytes >= 1024 ** 3) {
|
|
710
540
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
@@ -716,7 +546,6 @@ export class Frontend extends EventEmitter {
|
|
|
716
546
|
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
717
547
|
}
|
|
718
548
|
};
|
|
719
|
-
// Function to format system uptime with only the most significant unit
|
|
720
549
|
formatOsUpTime = (seconds) => {
|
|
721
550
|
if (seconds >= 86400) {
|
|
722
551
|
const days = Math.floor(seconds / 86400);
|
|
@@ -732,13 +561,7 @@ export class Frontend extends EventEmitter {
|
|
|
732
561
|
}
|
|
733
562
|
return `${seconds} second${seconds !== 1 ? 's' : ''}`;
|
|
734
563
|
};
|
|
735
|
-
/**
|
|
736
|
-
* Retrieves the api settings data.
|
|
737
|
-
*
|
|
738
|
-
* @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
|
|
739
|
-
*/
|
|
740
564
|
async getApiSettings() {
|
|
741
|
-
// Update the system information
|
|
742
565
|
this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
|
|
743
566
|
this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
|
|
744
567
|
this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
|
|
@@ -747,7 +570,6 @@ export class Frontend extends EventEmitter {
|
|
|
747
570
|
this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
|
|
748
571
|
this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
749
572
|
this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
750
|
-
// Update the matterbridge information
|
|
751
573
|
this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
|
|
752
574
|
this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
|
|
753
575
|
this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
|
|
@@ -766,12 +588,6 @@ export class Frontend extends EventEmitter {
|
|
|
766
588
|
this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
|
|
767
589
|
return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
|
|
768
590
|
}
|
|
769
|
-
/**
|
|
770
|
-
* Retrieves the reachable attribute.
|
|
771
|
-
*
|
|
772
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
|
|
773
|
-
* @returns {boolean} The reachable attribute.
|
|
774
|
-
*/
|
|
775
591
|
getReachability(device) {
|
|
776
592
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
777
593
|
return false;
|
|
@@ -783,12 +599,6 @@ export class Frontend extends EventEmitter {
|
|
|
783
599
|
return true;
|
|
784
600
|
return false;
|
|
785
601
|
}
|
|
786
|
-
/**
|
|
787
|
-
* Retrieves the power source attribute.
|
|
788
|
-
*
|
|
789
|
-
* @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice object.
|
|
790
|
-
* @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
|
|
791
|
-
*/
|
|
792
602
|
getPowerSource(endpoint) {
|
|
793
603
|
if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
|
|
794
604
|
return undefined;
|
|
@@ -804,21 +614,13 @@ export class Frontend extends EventEmitter {
|
|
|
804
614
|
}
|
|
805
615
|
return;
|
|
806
616
|
};
|
|
807
|
-
// Root endpoint
|
|
808
617
|
if (endpoint.hasClusterServer(PowerSource.Cluster.id))
|
|
809
618
|
return powerSource(endpoint);
|
|
810
|
-
// Child endpoints
|
|
811
619
|
for (const child of endpoint.getChildEndpoints()) {
|
|
812
620
|
if (child.hasClusterServer(PowerSource.Cluster.id))
|
|
813
621
|
return powerSource(child);
|
|
814
622
|
}
|
|
815
623
|
}
|
|
816
|
-
/**
|
|
817
|
-
* Retrieves the matter pairing code from a given device.
|
|
818
|
-
*
|
|
819
|
-
* @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object to retrieve the QR pairing code from.
|
|
820
|
-
* @returns {ApiDevicesMatter | undefined} An ApiDevicesMatter object or undefined if not found.
|
|
821
|
-
*/
|
|
822
624
|
getMatterDataFromDevice(device) {
|
|
823
625
|
if (device.mode === 'server' && device.serverNode && device.serverContext) {
|
|
824
626
|
return {
|
|
@@ -830,65 +632,30 @@ export class Frontend extends EventEmitter {
|
|
|
830
632
|
};
|
|
831
633
|
}
|
|
832
634
|
}
|
|
833
|
-
/**
|
|
834
|
-
* Retrieves the cluster text description from a given device.
|
|
835
|
-
*
|
|
836
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
837
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
838
|
-
*/
|
|
839
635
|
getClusterTextFromDevice(device) {
|
|
840
636
|
if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
|
|
841
637
|
return '';
|
|
842
|
-
const getAttribute = (device, cluster, attribute) => {
|
|
843
|
-
let value = undefined;
|
|
844
|
-
Object.entries(device.state)
|
|
845
|
-
.filter(([clusterName]) => clusterName.toLowerCase() === cluster.toLowerCase())
|
|
846
|
-
.forEach(([, clusterAttributes]) => {
|
|
847
|
-
Object.entries(clusterAttributes)
|
|
848
|
-
.filter(([attributeName]) => attributeName.toLowerCase() === attribute.toLowerCase())
|
|
849
|
-
.forEach(([, attributeValue]) => {
|
|
850
|
-
value = attributeValue;
|
|
851
|
-
});
|
|
852
|
-
});
|
|
853
|
-
if (value === undefined)
|
|
854
|
-
this.log.error(`Cluster ${cluster} or attribute ${attribute} not found in device ${device.deviceName}`);
|
|
855
|
-
return value;
|
|
856
|
-
};
|
|
857
638
|
const getUserLabel = (device) => {
|
|
858
639
|
const labelList = getAttribute(device, 'userLabel', 'labelList');
|
|
859
|
-
if (
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
return '';
|
|
640
|
+
if (labelList) {
|
|
641
|
+
const composed = labelList.find((entry) => entry.label === 'composed');
|
|
642
|
+
if (composed)
|
|
643
|
+
return 'Composed: ' + composed.value;
|
|
644
|
+
}
|
|
645
|
+
return '';
|
|
866
646
|
};
|
|
867
647
|
const getFixedLabel = (device) => {
|
|
868
648
|
const labelList = getAttribute(device, 'fixedLabel', 'labelList');
|
|
869
|
-
if (
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
return '';
|
|
649
|
+
if (labelList) {
|
|
650
|
+
const composed = labelList.find((entry) => entry.label === 'composed');
|
|
651
|
+
if (composed)
|
|
652
|
+
return 'Composed: ' + composed.value;
|
|
653
|
+
}
|
|
654
|
+
return '';
|
|
876
655
|
};
|
|
877
656
|
let attributes = '';
|
|
878
657
|
let supportedModes = [];
|
|
879
|
-
/*
|
|
880
|
-
Object.keys(device.behaviors.supported).forEach((clusterName) => {
|
|
881
|
-
const clusterBehavior = device.behaviors.supported[lowercaseFirstLetter(clusterName)] as ClusterBehavior.Type | undefined;
|
|
882
|
-
// console.log(`Device: ${device.deviceName} => Cluster: ${clusterName} Behavior: ${clusterBehavior?.id}`, clusterBehavior);
|
|
883
|
-
if (clusterBehavior && clusterBehavior.cluster && clusterBehavior.cluster.attributes) {
|
|
884
|
-
Object.entries(clusterBehavior.cluster.attributes).forEach(([attributeName, attribute]) => {
|
|
885
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName} Attribute: ${attributeName}`, attribute);
|
|
886
|
-
});
|
|
887
|
-
}
|
|
888
|
-
});
|
|
889
|
-
*/
|
|
890
658
|
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
891
|
-
// console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
|
|
892
659
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
893
660
|
return;
|
|
894
661
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -913,8 +680,6 @@ export class Frontend extends EventEmitter {
|
|
|
913
680
|
const supportedMode = supportedModes.find((mode) => mode.mode === attributeValue);
|
|
914
681
|
if (supportedMode)
|
|
915
682
|
attributes += `Mode: ${supportedMode.label} `;
|
|
916
|
-
else
|
|
917
|
-
attributes += `Mode: ${attributeValue} `;
|
|
918
683
|
}
|
|
919
684
|
const operationalStateClusters = ['operationalState', 'rvcOperationalState'];
|
|
920
685
|
if (operationalStateClusters.includes(clusterName) && attributeName === 'operationalState')
|
|
@@ -980,14 +745,8 @@ export class Frontend extends EventEmitter {
|
|
|
980
745
|
if (clusterName === 'userLabel' && attributeName === 'labelList')
|
|
981
746
|
attributes += `${getUserLabel(device)} `;
|
|
982
747
|
});
|
|
983
|
-
// console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
|
|
984
748
|
return attributes.trimStart().trimEnd();
|
|
985
749
|
}
|
|
986
|
-
/**
|
|
987
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
988
|
-
*
|
|
989
|
-
* @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
|
|
990
|
-
*/
|
|
991
750
|
getBaseRegisteredPlugins() {
|
|
992
751
|
const baseRegisteredPlugins = [];
|
|
993
752
|
for (const plugin of this.matterbridge.plugins) {
|
|
@@ -1026,19 +785,11 @@ export class Frontend extends EventEmitter {
|
|
|
1026
785
|
}
|
|
1027
786
|
return baseRegisteredPlugins;
|
|
1028
787
|
}
|
|
1029
|
-
/**
|
|
1030
|
-
* Retrieves the devices from Matterbridge.
|
|
1031
|
-
*
|
|
1032
|
-
* @param {string} [pluginName] - The name of the plugin to filter devices by.
|
|
1033
|
-
* @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
|
|
1034
|
-
*/
|
|
1035
788
|
async getDevices(pluginName) {
|
|
1036
789
|
const devices = [];
|
|
1037
790
|
for (const device of this.matterbridge.devices.array()) {
|
|
1038
|
-
// Filter by pluginName if provided
|
|
1039
791
|
if (pluginName && pluginName !== device.plugin)
|
|
1040
792
|
continue;
|
|
1041
|
-
// Check if the device has the required properties
|
|
1042
793
|
if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1043
794
|
continue;
|
|
1044
795
|
devices.push({
|
|
@@ -1058,37 +809,22 @@ export class Frontend extends EventEmitter {
|
|
|
1058
809
|
}
|
|
1059
810
|
return devices;
|
|
1060
811
|
}
|
|
1061
|
-
/**
|
|
1062
|
-
* Retrieves the clusters from a given plugin and endpoint number.
|
|
1063
|
-
*
|
|
1064
|
-
* Response for /api/clusters
|
|
1065
|
-
*
|
|
1066
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1067
|
-
* @param {number} endpointNumber - The endpoint number.
|
|
1068
|
-
* @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
|
|
1069
|
-
*/
|
|
1070
812
|
getClusters(pluginName, endpointNumber) {
|
|
1071
813
|
const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
|
|
1072
814
|
if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
|
|
1073
815
|
this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1074
816
|
return;
|
|
1075
817
|
}
|
|
1076
|
-
// this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
|
|
1077
|
-
// Get the device types from the main endpoint
|
|
1078
818
|
const deviceTypes = [];
|
|
1079
819
|
const clusters = [];
|
|
1080
820
|
endpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1081
821
|
deviceTypes.push(d.deviceType);
|
|
1082
822
|
});
|
|
1083
|
-
// Get the clusters from the main endpoint
|
|
1084
823
|
endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
1085
824
|
if (typeof attributeValue === 'undefined' || attributeValue === undefined)
|
|
1086
825
|
return;
|
|
1087
826
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1088
827
|
return;
|
|
1089
|
-
// console.log(
|
|
1090
|
-
// `${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}`,
|
|
1091
|
-
// );
|
|
1092
828
|
clusters.push({
|
|
1093
829
|
endpoint: endpoint.number.toString(),
|
|
1094
830
|
id: 'main',
|
|
@@ -1101,18 +837,12 @@ export class Frontend extends EventEmitter {
|
|
|
1101
837
|
attributeLocalValue: attributeValue,
|
|
1102
838
|
});
|
|
1103
839
|
});
|
|
1104
|
-
// Get the child endpoints
|
|
1105
840
|
const childEndpoints = endpoint.getChildEndpoints();
|
|
1106
|
-
// if (childEndpoints.length === 0) {
|
|
1107
|
-
// this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1108
|
-
// }
|
|
1109
841
|
childEndpoints.forEach((childEndpoint) => {
|
|
1110
842
|
if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
|
|
1111
843
|
this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
|
|
1112
844
|
return;
|
|
1113
845
|
}
|
|
1114
|
-
// this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
|
|
1115
|
-
// Get the device types of the child endpoint
|
|
1116
846
|
const deviceTypes = [];
|
|
1117
847
|
childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
|
|
1118
848
|
deviceTypes.push(d.deviceType);
|
|
@@ -1122,12 +852,9 @@ export class Frontend extends EventEmitter {
|
|
|
1122
852
|
return;
|
|
1123
853
|
if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
|
|
1124
854
|
return;
|
|
1125
|
-
// console.log(
|
|
1126
|
-
// `${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}`,
|
|
1127
|
-
// );
|
|
1128
855
|
clusters.push({
|
|
1129
856
|
endpoint: childEndpoint.number.toString(),
|
|
1130
|
-
id: childEndpoint.maybeId ?? 'null',
|
|
857
|
+
id: childEndpoint.maybeId ?? 'null',
|
|
1131
858
|
deviceTypes,
|
|
1132
859
|
clusterName: capitalizeFirstLetter(clusterName),
|
|
1133
860
|
clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
|
|
@@ -1140,13 +867,6 @@ export class Frontend extends EventEmitter {
|
|
|
1140
867
|
});
|
|
1141
868
|
return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
|
|
1142
869
|
}
|
|
1143
|
-
/**
|
|
1144
|
-
* Handles incoming websocket messages for the Matterbridge frontend.
|
|
1145
|
-
*
|
|
1146
|
-
* @param {WebSocket} client - The websocket client that sent the message.
|
|
1147
|
-
* @param {WebSocket.RawData} message - The raw data of the message received from the client.
|
|
1148
|
-
* @returns {Promise<void>} A promise that resolves when the message has been handled.
|
|
1149
|
-
*/
|
|
1150
870
|
async wsMessageHandler(client, message) {
|
|
1151
871
|
let data;
|
|
1152
872
|
try {
|
|
@@ -1193,41 +913,32 @@ export class Frontend extends EventEmitter {
|
|
|
1193
913
|
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
1194
914
|
const packageName = data.params.packageName.replace(/@.*$/, '');
|
|
1195
915
|
if (data.params.restart === false && packageName !== 'matterbridge') {
|
|
1196
|
-
// The install comes from InstallPlugins
|
|
1197
916
|
this.matterbridge.plugins
|
|
1198
917
|
.add(packageName)
|
|
1199
918
|
.then((plugin) => {
|
|
1200
919
|
if (plugin) {
|
|
1201
|
-
// The plugin is not registered
|
|
1202
920
|
this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
|
|
1203
921
|
this.matterbridge.plugins
|
|
1204
922
|
.load(plugin, true, 'The plugin has been added', true)
|
|
1205
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1206
923
|
.then(() => {
|
|
1207
924
|
this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
|
|
1208
925
|
this.wssSendRefreshRequired('plugins');
|
|
1209
926
|
return;
|
|
1210
927
|
})
|
|
1211
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1212
928
|
.catch((_error) => {
|
|
1213
|
-
//
|
|
1214
929
|
});
|
|
1215
930
|
}
|
|
1216
931
|
else {
|
|
1217
|
-
// The plugin is already registered
|
|
1218
932
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1219
933
|
this.wssSendRefreshRequired('plugins');
|
|
1220
934
|
this.wssSendRestartRequired();
|
|
1221
935
|
}
|
|
1222
936
|
return;
|
|
1223
937
|
})
|
|
1224
|
-
// eslint-disable-next-line promise/no-nesting
|
|
1225
938
|
.catch((_error) => {
|
|
1226
|
-
//
|
|
1227
939
|
});
|
|
1228
940
|
}
|
|
1229
941
|
else {
|
|
1230
|
-
// The package is matterbridge
|
|
1231
942
|
if (this.matterbridge.restartMode !== '') {
|
|
1232
943
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1233
944
|
this.matterbridge.shutdownProcess();
|
|
@@ -1250,7 +961,6 @@ export class Frontend extends EventEmitter {
|
|
|
1250
961
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1251
962
|
return;
|
|
1252
963
|
}
|
|
1253
|
-
// The package is a plugin
|
|
1254
964
|
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1255
965
|
if (plugin) {
|
|
1256
966
|
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
@@ -1259,7 +969,6 @@ export class Frontend extends EventEmitter {
|
|
|
1259
969
|
this.wssSendRefreshRequired('plugins');
|
|
1260
970
|
this.wssSendRefreshRequired('devices');
|
|
1261
971
|
}
|
|
1262
|
-
// Uninstall the package
|
|
1263
972
|
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1264
973
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
1265
974
|
spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
@@ -1300,7 +1009,6 @@ export class Frontend extends EventEmitter {
|
|
|
1300
1009
|
return;
|
|
1301
1010
|
})
|
|
1302
1011
|
.catch((_error) => {
|
|
1303
|
-
//
|
|
1304
1012
|
});
|
|
1305
1013
|
}
|
|
1306
1014
|
else {
|
|
@@ -1347,7 +1055,6 @@ export class Frontend extends EventEmitter {
|
|
|
1347
1055
|
return;
|
|
1348
1056
|
})
|
|
1349
1057
|
.catch((_error) => {
|
|
1350
|
-
//
|
|
1351
1058
|
});
|
|
1352
1059
|
}
|
|
1353
1060
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1376,10 +1083,7 @@ export class Frontend extends EventEmitter {
|
|
|
1376
1083
|
}
|
|
1377
1084
|
this.log.info(`Saving config for plugin ${plg}${data.params.pluginName}${nf}...`);
|
|
1378
1085
|
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1379
|
-
if (
|
|
1380
|
-
this.log.warn(`Plugin ${plg}${data.params.pluginName}${wr} not found in matterbridge`);
|
|
1381
|
-
}
|
|
1382
|
-
else {
|
|
1086
|
+
if (plugin) {
|
|
1383
1087
|
this.matterbridge.plugins.saveConfigFromJson(plugin, data.params.formData, true);
|
|
1384
1088
|
this.wssSendSnackbarMessage(`Saved config for plugin ${data.params.pluginName}`);
|
|
1385
1089
|
this.wssSendRefreshRequired('pluginsRestart');
|
|
@@ -1465,7 +1169,7 @@ export class Frontend extends EventEmitter {
|
|
|
1465
1169
|
this.matterbridge.matterbridgeManualPairingCode = pairingCodes?.manualPairingCode;
|
|
1466
1170
|
this.wssSendRefreshRequired('matterbridgeAdvertise');
|
|
1467
1171
|
this.wssSendSnackbarMessage(`Started fabrics share`, 0);
|
|
1468
|
-
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes }));
|
|
1172
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes, success: true }));
|
|
1469
1173
|
}
|
|
1470
1174
|
else if (data.method === '/api/stopadvertise') {
|
|
1471
1175
|
await this.matterbridge.stopAdvertiseServerNode(this.matterbridge.serverNode);
|
|
@@ -1585,22 +1289,22 @@ export class Frontend extends EventEmitter {
|
|
|
1585
1289
|
if (isValidString(data.params.value, 4)) {
|
|
1586
1290
|
this.log.debug('Matterbridge logger level:', data.params.value);
|
|
1587
1291
|
if (data.params.value === 'Debug') {
|
|
1588
|
-
await this.matterbridge.setLogLevel("debug"
|
|
1292
|
+
await this.matterbridge.setLogLevel("debug");
|
|
1589
1293
|
}
|
|
1590
1294
|
else if (data.params.value === 'Info') {
|
|
1591
|
-
await this.matterbridge.setLogLevel("info"
|
|
1295
|
+
await this.matterbridge.setLogLevel("info");
|
|
1592
1296
|
}
|
|
1593
1297
|
else if (data.params.value === 'Notice') {
|
|
1594
|
-
await this.matterbridge.setLogLevel("notice"
|
|
1298
|
+
await this.matterbridge.setLogLevel("notice");
|
|
1595
1299
|
}
|
|
1596
1300
|
else if (data.params.value === 'Warn') {
|
|
1597
|
-
await this.matterbridge.setLogLevel("warn"
|
|
1301
|
+
await this.matterbridge.setLogLevel("warn");
|
|
1598
1302
|
}
|
|
1599
1303
|
else if (data.params.value === 'Error') {
|
|
1600
|
-
await this.matterbridge.setLogLevel("error"
|
|
1304
|
+
await this.matterbridge.setLogLevel("error");
|
|
1601
1305
|
}
|
|
1602
1306
|
else if (data.params.value === 'Fatal') {
|
|
1603
|
-
await this.matterbridge.setLogLevel("fatal"
|
|
1307
|
+
await this.matterbridge.setLogLevel("fatal");
|
|
1604
1308
|
}
|
|
1605
1309
|
await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
1606
1310
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1611,9 +1315,8 @@ export class Frontend extends EventEmitter {
|
|
|
1611
1315
|
this.log.debug('Matterbridge file log:', data.params.value);
|
|
1612
1316
|
this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
|
|
1613
1317
|
await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
|
|
1614
|
-
// Create the file logger for matterbridge
|
|
1615
1318
|
if (data.params.value)
|
|
1616
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.
|
|
1319
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
|
|
1617
1320
|
else
|
|
1618
1321
|
AnsiLogger.setGlobalLogfile(undefined);
|
|
1619
1322
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
@@ -1725,14 +1428,15 @@ export class Frontend extends EventEmitter {
|
|
|
1725
1428
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = data.params.value;
|
|
1726
1429
|
await this.matterbridge.nodeContext?.set('matterdiscriminator', data.params.value);
|
|
1727
1430
|
this.wssSendRestartRequired();
|
|
1431
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
1728
1432
|
}
|
|
1729
1433
|
else {
|
|
1730
1434
|
this.log.debug(`Reset matter commissioning discriminator to ${CYAN}undefined${db}`);
|
|
1731
1435
|
this.matterbridge.matterbridgeInformation.matterDiscriminator = undefined;
|
|
1732
1436
|
await this.matterbridge.nodeContext?.remove('matterdiscriminator');
|
|
1733
1437
|
this.wssSendRestartRequired();
|
|
1438
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: false }));
|
|
1734
1439
|
}
|
|
1735
|
-
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
1736
1440
|
break;
|
|
1737
1441
|
case 'setmatterpasscode':
|
|
1738
1442
|
data.params.value = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
|
|
@@ -1741,14 +1445,15 @@ export class Frontend extends EventEmitter {
|
|
|
1741
1445
|
this.log.debug(`Set matter commissioning passcode to ${CYAN}${data.params.value}${db}`);
|
|
1742
1446
|
await this.matterbridge.nodeContext?.set('matterpasscode', data.params.value);
|
|
1743
1447
|
this.wssSendRestartRequired();
|
|
1448
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
1744
1449
|
}
|
|
1745
1450
|
else {
|
|
1746
1451
|
this.log.debug(`Reset matter commissioning passcode to ${CYAN}undefined${db}`);
|
|
1747
1452
|
this.matterbridge.matterbridgeInformation.matterPasscode = undefined;
|
|
1748
1453
|
await this.matterbridge.nodeContext?.remove('matterpasscode');
|
|
1749
1454
|
this.wssSendRestartRequired();
|
|
1455
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: false }));
|
|
1750
1456
|
}
|
|
1751
|
-
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
|
|
1752
1457
|
break;
|
|
1753
1458
|
case 'setvirtualmode':
|
|
1754
1459
|
if (isValidString(data.params.value, 1) && ['disabled', 'light', 'outlet', 'switch', 'mounted_switch'].includes(data.params.value)) {
|
|
@@ -1776,19 +1481,15 @@ export class Frontend extends EventEmitter {
|
|
|
1776
1481
|
return;
|
|
1777
1482
|
}
|
|
1778
1483
|
const config = plugin.configJson;
|
|
1779
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1780
1484
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1781
|
-
// this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1782
1485
|
if (select === 'serial')
|
|
1783
1486
|
this.log.info(`Selected device serial ${data.params.serial}`);
|
|
1784
1487
|
if (select === 'name')
|
|
1785
1488
|
this.log.info(`Selected device name ${data.params.name}`);
|
|
1786
1489
|
if (config && select && (select === 'serial' || select === 'name')) {
|
|
1787
|
-
// Remove postfix from the serial if it exists
|
|
1788
1490
|
if (config.postfix) {
|
|
1789
1491
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1790
1492
|
}
|
|
1791
|
-
// Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
|
|
1792
1493
|
if (isValidArray(config.whiteList, 1)) {
|
|
1793
1494
|
if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
|
|
1794
1495
|
config.whiteList.push(data.params.serial);
|
|
@@ -1797,7 +1498,6 @@ export class Frontend extends EventEmitter {
|
|
|
1797
1498
|
config.whiteList.push(data.params.name);
|
|
1798
1499
|
}
|
|
1799
1500
|
}
|
|
1800
|
-
// Remove the serial from the blackList if the blackList exists and the serial or name is in it
|
|
1801
1501
|
if (isValidArray(config.blackList, 1)) {
|
|
1802
1502
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1803
1503
|
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
@@ -1827,9 +1527,7 @@ export class Frontend extends EventEmitter {
|
|
|
1827
1527
|
return;
|
|
1828
1528
|
}
|
|
1829
1529
|
const config = plugin.configJson;
|
|
1830
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1831
1530
|
const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
|
|
1832
|
-
// this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
|
|
1833
1531
|
if (select === 'serial')
|
|
1834
1532
|
this.log.info(`Unselected device serial ${data.params.serial}`);
|
|
1835
1533
|
if (select === 'name')
|
|
@@ -1838,7 +1536,6 @@ export class Frontend extends EventEmitter {
|
|
|
1838
1536
|
if (config.postfix) {
|
|
1839
1537
|
data.params.serial = data.params.serial.replace('-' + config.postfix, '');
|
|
1840
1538
|
}
|
|
1841
|
-
// Remove the serial from the whiteList if the whiteList exists and the serial is in it
|
|
1842
1539
|
if (isValidArray(config.whiteList, 1)) {
|
|
1843
1540
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1844
1541
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
@@ -1847,7 +1544,6 @@ export class Frontend extends EventEmitter {
|
|
|
1847
1544
|
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1848
1545
|
}
|
|
1849
1546
|
}
|
|
1850
|
-
// Add the serial to the blackList
|
|
1851
1547
|
if (isValidArray(config.blackList)) {
|
|
1852
1548
|
if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
|
|
1853
1549
|
config.blackList.push(data.params.serial);
|
|
@@ -1880,230 +1576,114 @@ export class Frontend extends EventEmitter {
|
|
|
1880
1576
|
this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
|
|
1881
1577
|
}
|
|
1882
1578
|
}
|
|
1883
|
-
/**
|
|
1884
|
-
* Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
|
|
1885
|
-
*
|
|
1886
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
1887
|
-
* @param {string} time - The time string of the message
|
|
1888
|
-
* @param {string} name - The logger name of the message
|
|
1889
|
-
* @param {string} message - The content of the message.
|
|
1890
|
-
*
|
|
1891
|
-
* @remarks
|
|
1892
|
-
* The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
|
|
1893
|
-
* It also replaces all occurrences of \" with " and angle-brackets with < and >.
|
|
1894
|
-
* The function sends the message to all connected clients.
|
|
1895
|
-
*/
|
|
1896
1579
|
wssSendMessage(level, time, name, message) {
|
|
1897
1580
|
if (!level || !time || !name || !message)
|
|
1898
1581
|
return;
|
|
1899
|
-
// Remove ANSI escape codes from the message
|
|
1900
|
-
// eslint-disable-next-line no-control-regex
|
|
1901
1582
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
1902
|
-
// Remove leading asterisks from the message
|
|
1903
1583
|
message = message.replace(/^\*+/, '');
|
|
1904
|
-
// Replace all occurrences of \t and \n
|
|
1905
1584
|
message = message.replace(/[\t\n]/g, '');
|
|
1906
|
-
// Remove non-printable characters
|
|
1907
|
-
// eslint-disable-next-line no-control-regex
|
|
1908
1585
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
1909
|
-
// Replace all occurrences of \" with "
|
|
1910
1586
|
message = message.replace(/\\"/g, '"');
|
|
1911
|
-
// Replace all occurrences of angle-brackets with < and >"
|
|
1912
1587
|
message = message.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1913
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
1914
1588
|
const maxContinuousLength = 100;
|
|
1915
1589
|
const keepStartLength = 20;
|
|
1916
1590
|
const keepEndLength = 20;
|
|
1917
|
-
// Split the message into words
|
|
1918
1591
|
message = message
|
|
1919
1592
|
.split(' ')
|
|
1920
1593
|
.map((word) => {
|
|
1921
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
1922
1594
|
if (word.length > maxContinuousLength) {
|
|
1923
1595
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
1924
1596
|
}
|
|
1925
1597
|
return word;
|
|
1926
1598
|
})
|
|
1927
1599
|
.join(' ');
|
|
1928
|
-
// Send the message to all connected clients
|
|
1929
1600
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1930
1601
|
if (client.readyState === WebSocket.OPEN) {
|
|
1931
1602
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
1932
1603
|
}
|
|
1933
1604
|
});
|
|
1934
1605
|
}
|
|
1935
|
-
/**
|
|
1936
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
1937
|
-
*
|
|
1938
|
-
* @param {string} changed - The changed value. If null, the whole page will be refreshed.
|
|
1939
|
-
* possible values:
|
|
1940
|
-
* - 'matterbridgeLatestVersion'
|
|
1941
|
-
* - 'matterbridgeAdvertise'
|
|
1942
|
-
* - 'online'
|
|
1943
|
-
* - 'offline'
|
|
1944
|
-
* - 'reachability'
|
|
1945
|
-
* - 'settings'
|
|
1946
|
-
* - 'plugins'
|
|
1947
|
-
* - 'pluginsRestart'
|
|
1948
|
-
* - 'devices'
|
|
1949
|
-
* - 'fabrics'
|
|
1950
|
-
* - 'sessions'
|
|
1951
|
-
*/
|
|
1952
1606
|
wssSendRefreshRequired(changed = null) {
|
|
1953
1607
|
this.log.debug('Sending a refresh required message to all connected clients');
|
|
1954
|
-
// Send the message to all connected clients
|
|
1955
1608
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1956
1609
|
if (client.readyState === WebSocket.OPEN) {
|
|
1957
1610
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
|
|
1958
1611
|
}
|
|
1959
1612
|
});
|
|
1960
1613
|
}
|
|
1961
|
-
/**
|
|
1962
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
1963
|
-
*
|
|
1964
|
-
* @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients.
|
|
1965
|
-
*/
|
|
1966
1614
|
wssSendRestartRequired(snackbar = true) {
|
|
1967
1615
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1968
1616
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1969
1617
|
if (snackbar === true)
|
|
1970
1618
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1971
|
-
// Send the message to all connected clients
|
|
1972
1619
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1973
1620
|
if (client.readyState === WebSocket.OPEN) {
|
|
1974
1621
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
1975
1622
|
}
|
|
1976
1623
|
});
|
|
1977
1624
|
}
|
|
1978
|
-
/**
|
|
1979
|
-
* Sends a need to update WebSocket message to all connected clients.
|
|
1980
|
-
*
|
|
1981
|
-
*/
|
|
1982
1625
|
wssSendUpdateRequired() {
|
|
1983
1626
|
this.log.debug('Sending an update required message to all connected clients');
|
|
1984
1627
|
this.matterbridge.matterbridgeInformation.updateRequired = true;
|
|
1985
|
-
// Send the message to all connected clients
|
|
1986
1628
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1987
1629
|
if (client.readyState === WebSocket.OPEN) {
|
|
1988
1630
|
client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
|
|
1989
1631
|
}
|
|
1990
1632
|
});
|
|
1991
1633
|
}
|
|
1992
|
-
/**
|
|
1993
|
-
* Sends a cpu update message to all connected clients.
|
|
1994
|
-
*
|
|
1995
|
-
* @param {number} cpuUsage - The CPU usage percentage to send.
|
|
1996
|
-
*/
|
|
1997
1634
|
wssSendCpuUpdate(cpuUsage) {
|
|
1998
1635
|
if (hasParameter('debug'))
|
|
1999
1636
|
this.log.debug('Sending a cpu update message to all connected clients');
|
|
2000
|
-
// Send the message to all connected clients
|
|
2001
1637
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2002
1638
|
if (client.readyState === WebSocket.OPEN) {
|
|
2003
1639
|
client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
|
|
2004
1640
|
}
|
|
2005
1641
|
});
|
|
2006
1642
|
}
|
|
2007
|
-
/**
|
|
2008
|
-
* Sends a memory update message to all connected clients.
|
|
2009
|
-
*
|
|
2010
|
-
* @param {string} totalMemory - The total memory in bytes.
|
|
2011
|
-
* @param {string} freeMemory - The free memory in bytes.
|
|
2012
|
-
* @param {string} rss - The resident set size in bytes.
|
|
2013
|
-
* @param {string} heapTotal - The total heap memory in bytes.
|
|
2014
|
-
* @param {string} heapUsed - The used heap memory in bytes.
|
|
2015
|
-
* @param {string} external - The external memory in bytes.
|
|
2016
|
-
* @param {string} arrayBuffers - The array buffers memory in bytes.
|
|
2017
|
-
*/
|
|
2018
1643
|
wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
|
|
2019
1644
|
if (hasParameter('debug'))
|
|
2020
1645
|
this.log.debug('Sending a memory update message to all connected clients');
|
|
2021
|
-
// Send the message to all connected clients
|
|
2022
1646
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2023
1647
|
if (client.readyState === WebSocket.OPEN) {
|
|
2024
1648
|
client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
|
|
2025
1649
|
}
|
|
2026
1650
|
});
|
|
2027
1651
|
}
|
|
2028
|
-
/**
|
|
2029
|
-
* Sends an uptime update message to all connected clients.
|
|
2030
|
-
*
|
|
2031
|
-
* @param {string} systemUptime - The system uptime in a human-readable format.
|
|
2032
|
-
* @param {string} processUptime - The process uptime in a human-readable format.
|
|
2033
|
-
*/
|
|
2034
1652
|
wssSendUptimeUpdate(systemUptime, processUptime) {
|
|
2035
1653
|
if (hasParameter('debug'))
|
|
2036
1654
|
this.log.debug('Sending a uptime update message to all connected clients');
|
|
2037
|
-
// Send the message to all connected clients
|
|
2038
1655
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2039
1656
|
if (client.readyState === WebSocket.OPEN) {
|
|
2040
1657
|
client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
|
|
2041
1658
|
}
|
|
2042
1659
|
});
|
|
2043
1660
|
}
|
|
2044
|
-
/**
|
|
2045
|
-
* Sends an open snackbar message to all connected clients.
|
|
2046
|
-
*
|
|
2047
|
-
* @param {string} message - The message to send.
|
|
2048
|
-
* @param {number} timeout - The timeout in seconds for the snackbar message.
|
|
2049
|
-
* @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
|
|
2050
|
-
*/
|
|
2051
1661
|
wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
|
|
2052
1662
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
2053
|
-
// Send the message to all connected clients
|
|
2054
1663
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2055
1664
|
if (client.readyState === WebSocket.OPEN) {
|
|
2056
1665
|
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
2057
1666
|
}
|
|
2058
1667
|
});
|
|
2059
1668
|
}
|
|
2060
|
-
/**
|
|
2061
|
-
* Sends a close snackbar message to all connected clients.
|
|
2062
|
-
*
|
|
2063
|
-
* @param {string} message - The message to send.
|
|
2064
|
-
*/
|
|
2065
1669
|
wssSendCloseSnackbarMessage(message) {
|
|
2066
1670
|
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
2067
|
-
// Send the message to all connected clients
|
|
2068
1671
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2069
1672
|
if (client.readyState === WebSocket.OPEN) {
|
|
2070
1673
|
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
2071
1674
|
}
|
|
2072
1675
|
});
|
|
2073
1676
|
}
|
|
2074
|
-
/**
|
|
2075
|
-
* Sends an attribute update message to all connected WebSocket clients.
|
|
2076
|
-
*
|
|
2077
|
-
* @param {string | undefined} plugin - The name of the plugin.
|
|
2078
|
-
* @param {string | undefined} serialNumber - The serial number of the device.
|
|
2079
|
-
* @param {string | undefined} uniqueId - The unique identifier of the device.
|
|
2080
|
-
* @param {string} cluster - The cluster name where the attribute belongs.
|
|
2081
|
-
* @param {string} attribute - The name of the attribute that changed.
|
|
2082
|
-
* @param {number | string | boolean} value - The new value of the attribute.
|
|
2083
|
-
*
|
|
2084
|
-
* @remarks
|
|
2085
|
-
* This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
|
|
2086
|
-
* with the updated attribute information.
|
|
2087
|
-
*/
|
|
2088
1677
|
wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
|
|
2089
1678
|
this.log.debug('Sending an attribute update message to all connected clients');
|
|
2090
|
-
// Send the message to all connected clients
|
|
2091
1679
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2092
1680
|
if (client.readyState === WebSocket.OPEN) {
|
|
2093
1681
|
client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
|
|
2094
1682
|
}
|
|
2095
1683
|
});
|
|
2096
1684
|
}
|
|
2097
|
-
/**
|
|
2098
|
-
* Sends a message to all connected clients.
|
|
2099
|
-
*
|
|
2100
|
-
* @param {number} id - The message id.
|
|
2101
|
-
* @param {string} method - The message method.
|
|
2102
|
-
* @param {Record<string, string | number | boolean>} params - The message parameters.
|
|
2103
|
-
*/
|
|
2104
1685
|
wssBroadcastMessage(id, method, params) {
|
|
2105
1686
|
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
2106
|
-
// Send the message to all connected clients
|
|
2107
1687
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2108
1688
|
if (client.readyState === WebSocket.OPEN) {
|
|
2109
1689
|
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
@@ -2111,4 +1691,3 @@ export class Frontend extends EventEmitter {
|
|
|
2111
1691
|
});
|
|
2112
1692
|
}
|
|
2113
1693
|
}
|
|
2114
|
-
//# sourceMappingURL=frontend.js.map
|