matterbridge 3.1.5 → 3.1.6-dev-20250721-75fab6b

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.
Files changed (213) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cli.js +2 -91
  3. package/dist/cliEmitter.js +0 -30
  4. package/dist/clusters/export.js +0 -2
  5. package/dist/defaultConfigSchema.js +0 -24
  6. package/dist/deviceManager.js +1 -94
  7. package/dist/devices/batteryStorage.js +1 -48
  8. package/dist/devices/dishwasher.js +90 -0
  9. package/dist/devices/evse.js +10 -74
  10. package/dist/devices/export.js +2 -2
  11. package/dist/devices/extractorHood.js +35 -0
  12. package/dist/devices/heatPump.js +2 -50
  13. package/dist/devices/laundryDryer.js +6 -83
  14. package/dist/devices/laundryWasher.js +7 -91
  15. package/dist/devices/roboticVacuumCleaner.js +7 -93
  16. package/dist/devices/solarPower.js +0 -38
  17. package/dist/devices/waterHeater.js +2 -82
  18. package/dist/frontend.js +21 -429
  19. package/dist/globalMatterbridge.js +0 -47
  20. package/dist/helpers.js +0 -53
  21. package/dist/index.js +1 -30
  22. package/dist/logger/export.js +0 -1
  23. package/dist/matter/behaviors.js +0 -2
  24. package/dist/matter/clusters.js +0 -2
  25. package/dist/matter/devices.js +0 -2
  26. package/dist/matter/endpoints.js +0 -2
  27. package/dist/matter/export.js +0 -3
  28. package/dist/matter/types.js +0 -3
  29. package/dist/matterbridge.js +79 -802
  30. package/dist/matterbridgeAccessoryPlatform.js +0 -36
  31. package/dist/matterbridgeBehaviors.js +24 -61
  32. package/dist/matterbridgeDeviceTypes.js +15 -579
  33. package/dist/matterbridgeDynamicPlatform.js +0 -36
  34. package/dist/matterbridgeEndpoint.js +64 -1113
  35. package/dist/matterbridgeEndpointHelpers.js +12 -322
  36. package/dist/matterbridgePlatform.js +0 -233
  37. package/dist/matterbridgeTypes.js +0 -25
  38. package/dist/pluginManager.js +3 -249
  39. package/dist/shelly.js +7 -168
  40. package/dist/storage/export.js +0 -1
  41. package/dist/update.js +0 -54
  42. package/dist/utils/colorUtils.js +2 -263
  43. package/dist/utils/commandLine.js +0 -54
  44. package/dist/utils/copyDirectory.js +1 -38
  45. package/dist/utils/createDirectory.js +0 -33
  46. package/dist/utils/createZip.js +2 -47
  47. package/dist/utils/deepCopy.js +0 -39
  48. package/dist/utils/deepEqual.js +1 -72
  49. package/dist/utils/error.js +0 -41
  50. package/dist/utils/export.js +0 -1
  51. package/dist/utils/hex.js +0 -58
  52. package/dist/utils/isvalid.js +0 -101
  53. package/dist/utils/network.js +5 -81
  54. package/dist/utils/spawn.js +0 -40
  55. package/dist/utils/wait.js +9 -62
  56. package/npm-shrinkwrap.json +9 -9
  57. package/package.json +2 -3
  58. package/dist/cli.d.ts +0 -26
  59. package/dist/cli.d.ts.map +0 -1
  60. package/dist/cli.js.map +0 -1
  61. package/dist/cliEmitter.d.ts +0 -34
  62. package/dist/cliEmitter.d.ts.map +0 -1
  63. package/dist/cliEmitter.js.map +0 -1
  64. package/dist/clusters/export.d.ts +0 -2
  65. package/dist/clusters/export.d.ts.map +0 -1
  66. package/dist/clusters/export.js.map +0 -1
  67. package/dist/defaultConfigSchema.d.ts +0 -28
  68. package/dist/defaultConfigSchema.d.ts.map +0 -1
  69. package/dist/defaultConfigSchema.js.map +0 -1
  70. package/dist/deviceManager.d.ts +0 -112
  71. package/dist/deviceManager.d.ts.map +0 -1
  72. package/dist/deviceManager.js.map +0 -1
  73. package/dist/devices/batteryStorage.d.ts +0 -48
  74. package/dist/devices/batteryStorage.d.ts.map +0 -1
  75. package/dist/devices/batteryStorage.js.map +0 -1
  76. package/dist/devices/evse.d.ts +0 -75
  77. package/dist/devices/evse.d.ts.map +0 -1
  78. package/dist/devices/evse.js.map +0 -1
  79. package/dist/devices/export.d.ts +0 -9
  80. package/dist/devices/export.d.ts.map +0 -1
  81. package/dist/devices/export.js.map +0 -1
  82. package/dist/devices/heatPump.d.ts +0 -47
  83. package/dist/devices/heatPump.d.ts.map +0 -1
  84. package/dist/devices/heatPump.js.map +0 -1
  85. package/dist/devices/laundryDryer.d.ts +0 -87
  86. package/dist/devices/laundryDryer.d.ts.map +0 -1
  87. package/dist/devices/laundryDryer.js.map +0 -1
  88. package/dist/devices/laundryWasher.d.ts +0 -242
  89. package/dist/devices/laundryWasher.d.ts.map +0 -1
  90. package/dist/devices/laundryWasher.js.map +0 -1
  91. package/dist/devices/roboticVacuumCleaner.d.ts +0 -112
  92. package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
  93. package/dist/devices/roboticVacuumCleaner.js.map +0 -1
  94. package/dist/devices/solarPower.d.ts +0 -40
  95. package/dist/devices/solarPower.d.ts.map +0 -1
  96. package/dist/devices/solarPower.js.map +0 -1
  97. package/dist/devices/waterHeater.d.ts +0 -111
  98. package/dist/devices/waterHeater.d.ts.map +0 -1
  99. package/dist/devices/waterHeater.js.map +0 -1
  100. package/dist/frontend.d.ts +0 -304
  101. package/dist/frontend.d.ts.map +0 -1
  102. package/dist/frontend.js.map +0 -1
  103. package/dist/globalMatterbridge.d.ts +0 -59
  104. package/dist/globalMatterbridge.d.ts.map +0 -1
  105. package/dist/globalMatterbridge.js.map +0 -1
  106. package/dist/helpers.d.ts +0 -48
  107. package/dist/helpers.d.ts.map +0 -1
  108. package/dist/helpers.js.map +0 -1
  109. package/dist/index.d.ts +0 -33
  110. package/dist/index.d.ts.map +0 -1
  111. package/dist/index.js.map +0 -1
  112. package/dist/logger/export.d.ts +0 -2
  113. package/dist/logger/export.d.ts.map +0 -1
  114. package/dist/logger/export.js.map +0 -1
  115. package/dist/matter/behaviors.d.ts +0 -2
  116. package/dist/matter/behaviors.d.ts.map +0 -1
  117. package/dist/matter/behaviors.js.map +0 -1
  118. package/dist/matter/clusters.d.ts +0 -2
  119. package/dist/matter/clusters.d.ts.map +0 -1
  120. package/dist/matter/clusters.js.map +0 -1
  121. package/dist/matter/devices.d.ts +0 -2
  122. package/dist/matter/devices.d.ts.map +0 -1
  123. package/dist/matter/devices.js.map +0 -1
  124. package/dist/matter/endpoints.d.ts +0 -2
  125. package/dist/matter/endpoints.d.ts.map +0 -1
  126. package/dist/matter/endpoints.js.map +0 -1
  127. package/dist/matter/export.d.ts +0 -5
  128. package/dist/matter/export.d.ts.map +0 -1
  129. package/dist/matter/export.js.map +0 -1
  130. package/dist/matter/types.d.ts +0 -3
  131. package/dist/matter/types.d.ts.map +0 -1
  132. package/dist/matter/types.js.map +0 -1
  133. package/dist/matterbridge.d.ts +0 -447
  134. package/dist/matterbridge.d.ts.map +0 -1
  135. package/dist/matterbridge.js.map +0 -1
  136. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
  137. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  138. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  139. package/dist/matterbridgeBehaviors.d.ts +0 -1340
  140. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  141. package/dist/matterbridgeBehaviors.js.map +0 -1
  142. package/dist/matterbridgeDeviceTypes.d.ts +0 -709
  143. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  144. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  145. package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
  146. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  147. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  148. package/dist/matterbridgeEndpoint.d.ts +0 -1250
  149. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  150. package/dist/matterbridgeEndpoint.js.map +0 -1
  151. package/dist/matterbridgeEndpointHelpers.d.ts +0 -3198
  152. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  153. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  154. package/dist/matterbridgePlatform.d.ts +0 -310
  155. package/dist/matterbridgePlatform.d.ts.map +0 -1
  156. package/dist/matterbridgePlatform.js.map +0 -1
  157. package/dist/matterbridgeTypes.d.ts +0 -195
  158. package/dist/matterbridgeTypes.d.ts.map +0 -1
  159. package/dist/matterbridgeTypes.js.map +0 -1
  160. package/dist/pluginManager.d.ts +0 -270
  161. package/dist/pluginManager.d.ts.map +0 -1
  162. package/dist/pluginManager.js.map +0 -1
  163. package/dist/shelly.d.ts +0 -174
  164. package/dist/shelly.d.ts.map +0 -1
  165. package/dist/shelly.js.map +0 -1
  166. package/dist/storage/export.d.ts +0 -2
  167. package/dist/storage/export.d.ts.map +0 -1
  168. package/dist/storage/export.js.map +0 -1
  169. package/dist/update.d.ts +0 -59
  170. package/dist/update.d.ts.map +0 -1
  171. package/dist/update.js.map +0 -1
  172. package/dist/utils/colorUtils.d.ts +0 -117
  173. package/dist/utils/colorUtils.d.ts.map +0 -1
  174. package/dist/utils/colorUtils.js.map +0 -1
  175. package/dist/utils/commandLine.d.ts +0 -59
  176. package/dist/utils/commandLine.d.ts.map +0 -1
  177. package/dist/utils/commandLine.js.map +0 -1
  178. package/dist/utils/copyDirectory.d.ts +0 -33
  179. package/dist/utils/copyDirectory.d.ts.map +0 -1
  180. package/dist/utils/copyDirectory.js.map +0 -1
  181. package/dist/utils/createDirectory.d.ts +0 -34
  182. package/dist/utils/createDirectory.d.ts.map +0 -1
  183. package/dist/utils/createDirectory.js.map +0 -1
  184. package/dist/utils/createZip.d.ts +0 -39
  185. package/dist/utils/createZip.d.ts.map +0 -1
  186. package/dist/utils/createZip.js.map +0 -1
  187. package/dist/utils/deepCopy.d.ts +0 -32
  188. package/dist/utils/deepCopy.d.ts.map +0 -1
  189. package/dist/utils/deepCopy.js.map +0 -1
  190. package/dist/utils/deepEqual.d.ts +0 -54
  191. package/dist/utils/deepEqual.d.ts.map +0 -1
  192. package/dist/utils/deepEqual.js.map +0 -1
  193. package/dist/utils/error.d.ts +0 -44
  194. package/dist/utils/error.d.ts.map +0 -1
  195. package/dist/utils/error.js.map +0 -1
  196. package/dist/utils/export.d.ts +0 -12
  197. package/dist/utils/export.d.ts.map +0 -1
  198. package/dist/utils/export.js.map +0 -1
  199. package/dist/utils/hex.d.ts +0 -49
  200. package/dist/utils/hex.d.ts.map +0 -1
  201. package/dist/utils/hex.js.map +0 -1
  202. package/dist/utils/isvalid.d.ts +0 -103
  203. package/dist/utils/isvalid.d.ts.map +0 -1
  204. package/dist/utils/isvalid.js.map +0 -1
  205. package/dist/utils/network.d.ts +0 -74
  206. package/dist/utils/network.d.ts.map +0 -1
  207. package/dist/utils/network.js.map +0 -1
  208. package/dist/utils/spawn.d.ts +0 -33
  209. package/dist/utils/spawn.d.ts.map +0 -1
  210. package/dist/utils/spawn.js.map +0 -1
  211. package/dist/utils/wait.d.ts +0 -56
  212. package/dist/utils/wait.d.ts.map +0 -1
  213. 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 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
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(); // Ensure no extra characters
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; // Request client certificate
281
- httpsServerOptions.rejectUnauthorized = true; // Require client certificate validation
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
- // Set the global logger callback for the WebSocketServer
331
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
332
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
333
- callbackLogLevel = "info" /* LogLevel.INFO */;
334
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
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', // Indicate service is healthy
414
- uptime: process.uptime(), // Server uptime in seconds
415
- timestamp: new Date().toISOString(), // Current timestamp
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.getBaseRegisteredPlugins());
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();
@@ -708,7 +531,6 @@ export class Frontend extends EventEmitter {
708
531
  this.webSocketServer.removeAllListeners();
709
532
  this.webSocketServer = undefined;
710
533
  }
711
- // Close the http server
712
534
  if (this.httpServer) {
713
535
  this.log.debug('Closing http server...');
714
536
  await withTimeout(new Promise((resolve) => {
@@ -727,7 +549,6 @@ export class Frontend extends EventEmitter {
727
549
  this.httpServer = undefined;
728
550
  this.log.debug('Frontend http server closed successfully');
729
551
  }
730
- // Close the https server
731
552
  if (this.httpsServer) {
732
553
  this.log.debug('Closing https server...');
733
554
  await withTimeout(new Promise((resolve) => {
@@ -748,7 +569,6 @@ export class Frontend extends EventEmitter {
748
569
  }
749
570
  this.log.debug('Frontend stopped successfully');
750
571
  }
751
- // Function to format bytes to KB, MB, or GB
752
572
  formatMemoryUsage = (bytes) => {
753
573
  if (bytes >= 1024 ** 3) {
754
574
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -760,7 +580,6 @@ export class Frontend extends EventEmitter {
760
580
  return `${(bytes / 1024).toFixed(2)} KB`;
761
581
  }
762
582
  };
763
- // Function to format system uptime with only the most significant unit
764
583
  formatOsUpTime = (seconds) => {
765
584
  if (seconds >= 86400) {
766
585
  const days = Math.floor(seconds / 86400);
@@ -776,13 +595,7 @@ export class Frontend extends EventEmitter {
776
595
  }
777
596
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
778
597
  };
779
- /**
780
- * Retrieves the api settings data.
781
- *
782
- * @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
783
- */
784
598
  async getApiSettings() {
785
- // Update the system information
786
599
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
787
600
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
788
601
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -791,7 +604,6 @@ export class Frontend extends EventEmitter {
791
604
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
792
605
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
793
606
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
794
- // Update the matterbridge information
795
607
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
796
608
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
797
609
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
@@ -803,7 +615,6 @@ export class Frontend extends EventEmitter {
803
615
  this.matterbridge.matterbridgeInformation.matterPort = (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540;
804
616
  this.matterbridge.matterbridgeInformation.matterDiscriminator = await this.matterbridge.nodeContext?.get('matterdiscriminator');
805
617
  this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
806
- // Update the matterbridge information in bridge mode
807
618
  if (this.matterbridge.bridgeMode === 'bridge' && this.matterbridge.serverNode && !this.matterbridge.hasCleanupStarted) {
808
619
  this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.serverNode.state.commissioning.commissioned;
809
620
  this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.qrPairingCode;
@@ -813,12 +624,6 @@ export class Frontend extends EventEmitter {
813
624
  }
814
625
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
815
626
  }
816
- /**
817
- * Retrieves the reachable attribute.
818
- *
819
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
820
- * @returns {boolean} The reachable attribute.
821
- */
822
627
  getReachability(device) {
823
628
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
824
629
  return false;
@@ -830,12 +635,6 @@ export class Frontend extends EventEmitter {
830
635
  return true;
831
636
  return false;
832
637
  }
833
- /**
834
- * Retrieves the power source attribute.
835
- *
836
- * @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
837
- * @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
838
- */
839
638
  getPowerSource(endpoint) {
840
639
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
841
640
  return undefined;
@@ -851,21 +650,13 @@ export class Frontend extends EventEmitter {
851
650
  }
852
651
  return;
853
652
  };
854
- // Root endpoint
855
653
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
856
654
  return powerSource(endpoint);
857
- // Child endpoints
858
655
  for (const child of endpoint.getChildEndpoints()) {
859
656
  if (child.hasClusterServer(PowerSource.Cluster.id))
860
657
  return powerSource(child);
861
658
  }
862
659
  }
863
- /**
864
- * Retrieves the commissioned status, matter pairing codes, fabrics and sessions from a given device in server mode.
865
- *
866
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the data from.
867
- * @returns {ApiDevicesMatter | undefined} An ApiDevicesMatter object or undefined if not found.
868
- */
869
660
  getMatterDataFromDevice(device) {
870
661
  if (device.mode === 'server' && device.serverNode) {
871
662
  return {
@@ -877,13 +668,6 @@ export class Frontend extends EventEmitter {
877
668
  };
878
669
  }
879
670
  }
880
- /**
881
- * Retrieves the cluster text description from a given device.
882
- * The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
883
- *
884
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
885
- * @returns {string} The attributes description of the cluster servers in the device.
886
- */
887
671
  getClusterTextFromDevice(device) {
888
672
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
889
673
  return '';
@@ -908,7 +692,6 @@ export class Frontend extends EventEmitter {
908
692
  let attributes = '';
909
693
  let supportedModes = [];
910
694
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
911
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
912
695
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
913
696
  return;
914
697
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -998,17 +781,11 @@ export class Frontend extends EventEmitter {
998
781
  if (clusterName === 'userLabel' && attributeName === 'labelList')
999
782
  attributes += `${getUserLabel(device)} `;
1000
783
  });
1001
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
1002
784
  return attributes.trimStart().trimEnd();
1003
785
  }
1004
- /**
1005
- * Retrieves the base registered plugins sanitized for res.json().
1006
- *
1007
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
1008
- */
1009
786
  getBaseRegisteredPlugins() {
1010
787
  if (this.matterbridge.hasCleanupStarted)
1011
- return []; // Skip if cleanup has started
788
+ return [];
1012
789
  const baseRegisteredPlugins = [];
1013
790
  for (const plugin of this.matterbridge.plugins) {
1014
791
  baseRegisteredPlugins.push({
@@ -1037,7 +814,6 @@ export class Frontend extends EventEmitter {
1037
814
  schemaJson: plugin.schemaJson,
1038
815
  hasWhiteList: plugin.configJson?.whiteList !== undefined,
1039
816
  hasBlackList: plugin.configJson?.blackList !== undefined,
1040
- // Childbridge mode specific data
1041
817
  paired: plugin.serverNode?.state.commissioning.commissioned,
1042
818
  qrPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode?.state.commissioning.pairingCodes.qrPairingCode,
1043
819
  manualPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode?.state.commissioning.pairingCodes.manualPairingCode,
@@ -1047,21 +823,13 @@ export class Frontend extends EventEmitter {
1047
823
  }
1048
824
  return baseRegisteredPlugins;
1049
825
  }
1050
- /**
1051
- * Retrieves the devices from Matterbridge.
1052
- *
1053
- * @param {string} [pluginName] - The name of the plugin to filter devices by.
1054
- * @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
1055
- */
1056
826
  async getDevices(pluginName) {
1057
827
  if (this.matterbridge.hasCleanupStarted)
1058
- return []; // Skip if cleanup has started
828
+ return [];
1059
829
  const devices = [];
1060
830
  for (const device of this.matterbridge.devices.array()) {
1061
- // Filter by pluginName if provided
1062
831
  if (pluginName && pluginName !== device.plugin)
1063
832
  continue;
1064
- // Check if the device has the required properties
1065
833
  if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1066
834
  continue;
1067
835
  devices.push({
@@ -1081,37 +849,22 @@ export class Frontend extends EventEmitter {
1081
849
  }
1082
850
  return devices;
1083
851
  }
1084
- /**
1085
- * Retrieves the clusters from a given plugin and endpoint number.
1086
- *
1087
- * Response for /api/clusters
1088
- *
1089
- * @param {string} pluginName - The name of the plugin.
1090
- * @param {number} endpointNumber - The endpoint number.
1091
- * @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
1092
- */
1093
852
  getClusters(pluginName, endpointNumber) {
1094
853
  const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
1095
854
  if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
1096
855
  this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1097
856
  return;
1098
857
  }
1099
- // this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
1100
- // Get the device types from the main endpoint
1101
858
  const deviceTypes = [];
1102
859
  const clusters = [];
1103
860
  endpoint.state.descriptor.deviceTypeList.forEach((d) => {
1104
861
  deviceTypes.push(d.deviceType);
1105
862
  });
1106
- // Get the clusters from the main endpoint
1107
863
  endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1108
864
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1109
865
  return;
1110
866
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1111
867
  return;
1112
- // console.log(
1113
- // `${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}`,
1114
- // );
1115
868
  clusters.push({
1116
869
  endpoint: endpoint.number.toString(),
1117
870
  id: 'main',
@@ -1124,18 +877,12 @@ export class Frontend extends EventEmitter {
1124
877
  attributeLocalValue: attributeValue,
1125
878
  });
1126
879
  });
1127
- // Get the child endpoints
1128
880
  const childEndpoints = endpoint.getChildEndpoints();
1129
- // if (childEndpoints.length === 0) {
1130
- // this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
1131
- // }
1132
881
  childEndpoints.forEach((childEndpoint) => {
1133
882
  if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
1134
883
  this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1135
884
  return;
1136
885
  }
1137
- // this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
1138
- // Get the device types of the child endpoint
1139
886
  const deviceTypes = [];
1140
887
  childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
1141
888
  deviceTypes.push(d.deviceType);
@@ -1145,12 +892,9 @@ export class Frontend extends EventEmitter {
1145
892
  return;
1146
893
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1147
894
  return;
1148
- // console.log(
1149
- // `${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}`,
1150
- // );
1151
895
  clusters.push({
1152
896
  endpoint: childEndpoint.number.toString(),
1153
- id: childEndpoint.maybeId ?? 'null', // Never happens
897
+ id: childEndpoint.maybeId ?? 'null',
1154
898
  deviceTypes,
1155
899
  clusterName: capitalizeFirstLetter(clusterName),
1156
900
  clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
@@ -1163,13 +907,6 @@ export class Frontend extends EventEmitter {
1163
907
  });
1164
908
  return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
1165
909
  }
1166
- /**
1167
- * Handles incoming websocket messages for the Matterbridge frontend.
1168
- *
1169
- * @param {WebSocket} client - The websocket client that sent the message.
1170
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1171
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1172
- */
1173
910
  async wsMessageHandler(client, message) {
1174
911
  let data;
1175
912
  try {
@@ -1216,42 +953,32 @@ export class Frontend extends EventEmitter {
1216
953
  this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
1217
954
  const packageName = data.params.packageName.replace(/@.*$/, '');
1218
955
  if (data.params.restart === false && packageName !== 'matterbridge') {
1219
- // The install comes from InstallPlugins
1220
956
  this.matterbridge.plugins
1221
957
  .add(packageName)
1222
958
  .then((plugin) => {
1223
959
  if (plugin) {
1224
- // The plugin is not registered
1225
960
  this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
1226
961
  this.matterbridge.plugins
1227
962
  .load(plugin, true, 'The plugin has been added', true)
1228
- // eslint-disable-next-line promise/no-nesting
1229
963
  .then(() => {
1230
964
  this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
1231
965
  this.wssSendRefreshRequired('plugins');
1232
966
  return;
1233
967
  })
1234
- // eslint-disable-next-line promise/no-nesting
1235
968
  .catch((_error) => {
1236
- //
1237
969
  });
1238
970
  }
1239
971
  else {
1240
- // The plugin is already registered
1241
972
  this.wssSendSnackbarMessage(`Restart required`, 0);
1242
973
  this.wssSendRefreshRequired('plugins');
1243
974
  this.wssSendRestartRequired();
1244
975
  }
1245
976
  return;
1246
977
  })
1247
- // eslint-disable-next-line promise/no-nesting
1248
978
  .catch((_error) => {
1249
- //
1250
979
  });
1251
980
  }
1252
981
  else {
1253
- // The package is matterbridge
1254
- // istanbul ignore next if
1255
982
  if (this.matterbridge.restartMode !== '') {
1256
983
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1257
984
  this.matterbridge.shutdownProcess();
@@ -1274,7 +1001,6 @@ export class Frontend extends EventEmitter {
1274
1001
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
1275
1002
  return;
1276
1003
  }
1277
- // The package is a plugin
1278
1004
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
1279
1005
  if (plugin) {
1280
1006
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
@@ -1283,7 +1009,6 @@ export class Frontend extends EventEmitter {
1283
1009
  this.wssSendRefreshRequired('plugins');
1284
1010
  this.wssSendRefreshRequired('devices');
1285
1011
  }
1286
- // Uninstall the package
1287
1012
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1288
1013
  const { spawnCommand } = await import('./utils/spawn.js');
1289
1014
  spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
@@ -1324,7 +1049,6 @@ export class Frontend extends EventEmitter {
1324
1049
  return;
1325
1050
  })
1326
1051
  .catch((_error) => {
1327
- //
1328
1052
  });
1329
1053
  }
1330
1054
  else {
@@ -1371,7 +1095,6 @@ export class Frontend extends EventEmitter {
1371
1095
  return;
1372
1096
  })
1373
1097
  .catch((_error) => {
1374
- //
1375
1098
  });
1376
1099
  }
1377
1100
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1482,8 +1205,6 @@ export class Frontend extends EventEmitter {
1482
1205
  else if (data.method === '/api/advertise') {
1483
1206
  const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
1484
1207
  this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
1485
- // this.matterbridge.matterbridgeQrPairingCode = pairingCodes?.qrPairingCode;
1486
- // this.matterbridge.matterbridgeManualPairingCode = pairingCodes?.manualPairingCode;
1487
1208
  this.wssSendRefreshRequired('matterbridgeAdvertise');
1488
1209
  this.wssSendSnackbarMessage(`Started fabrics share`, 0);
1489
1210
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes, success: true }));
@@ -1606,22 +1327,22 @@ export class Frontend extends EventEmitter {
1606
1327
  if (isValidString(data.params.value, 4)) {
1607
1328
  this.log.debug('Matterbridge logger level:', data.params.value);
1608
1329
  if (data.params.value === 'Debug') {
1609
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1330
+ await this.matterbridge.setLogLevel("debug");
1610
1331
  }
1611
1332
  else if (data.params.value === 'Info') {
1612
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1333
+ await this.matterbridge.setLogLevel("info");
1613
1334
  }
1614
1335
  else if (data.params.value === 'Notice') {
1615
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1336
+ await this.matterbridge.setLogLevel("notice");
1616
1337
  }
1617
1338
  else if (data.params.value === 'Warn') {
1618
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1339
+ await this.matterbridge.setLogLevel("warn");
1619
1340
  }
1620
1341
  else if (data.params.value === 'Error') {
1621
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1342
+ await this.matterbridge.setLogLevel("error");
1622
1343
  }
1623
1344
  else if (data.params.value === 'Fatal') {
1624
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1345
+ await this.matterbridge.setLogLevel("fatal");
1625
1346
  }
1626
1347
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1627
1348
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1632,7 +1353,6 @@ export class Frontend extends EventEmitter {
1632
1353
  this.log.debug('Matterbridge file log:', data.params.value);
1633
1354
  this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
1634
1355
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1635
- // Create the file logger for matterbridge
1636
1356
  if (data.params.value)
1637
1357
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1638
1358
  else
@@ -1679,7 +1399,6 @@ export class Frontend extends EventEmitter {
1679
1399
  });
1680
1400
  }
1681
1401
  catch (error) {
1682
- /* istanbul ignore next */
1683
1402
  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}`);
1684
1403
  }
1685
1404
  }
@@ -1688,7 +1407,6 @@ export class Frontend extends EventEmitter {
1688
1407
  Logger.removeLogger('matterfilelogger');
1689
1408
  }
1690
1409
  catch (error) {
1691
- /* istanbul ignore next */
1692
1410
  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}`);
1693
1411
  }
1694
1412
  }
@@ -1801,19 +1519,15 @@ export class Frontend extends EventEmitter {
1801
1519
  return;
1802
1520
  }
1803
1521
  const config = plugin.configJson;
1804
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1805
1522
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1806
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1807
1523
  if (select === 'serial')
1808
1524
  this.log.info(`Selected device serial ${data.params.serial}`);
1809
1525
  if (select === 'name')
1810
1526
  this.log.info(`Selected device name ${data.params.name}`);
1811
1527
  if (config && select && (select === 'serial' || select === 'name')) {
1812
- // Remove postfix from the serial if it exists
1813
1528
  if (config.postfix) {
1814
1529
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1815
1530
  }
1816
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1817
1531
  if (isValidArray(config.whiteList, 1)) {
1818
1532
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1819
1533
  config.whiteList.push(data.params.serial);
@@ -1822,7 +1536,6 @@ export class Frontend extends EventEmitter {
1822
1536
  config.whiteList.push(data.params.name);
1823
1537
  }
1824
1538
  }
1825
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1826
1539
  if (isValidArray(config.blackList, 1)) {
1827
1540
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1828
1541
  config.blackList = config.blackList.filter((item) => item !== data.params.serial);
@@ -1853,9 +1566,7 @@ export class Frontend extends EventEmitter {
1853
1566
  return;
1854
1567
  }
1855
1568
  const config = plugin.configJson;
1856
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1857
1569
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1858
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1859
1570
  if (select === 'serial')
1860
1571
  this.log.info(`Unselected device serial ${data.params.serial}`);
1861
1572
  if (select === 'name')
@@ -1864,7 +1575,6 @@ export class Frontend extends EventEmitter {
1864
1575
  if (config.postfix) {
1865
1576
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1866
1577
  }
1867
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1868
1578
  if (isValidArray(config.whiteList, 1)) {
1869
1579
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1870
1580
  config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
@@ -1873,7 +1583,6 @@ export class Frontend extends EventEmitter {
1873
1583
  config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
1874
1584
  }
1875
1585
  }
1876
- // Add the serial to the blackList
1877
1586
  if (isValidArray(config.blackList)) {
1878
1587
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1879
1588
  config.blackList.push(data.params.serial);
@@ -1907,230 +1616,114 @@ export class Frontend extends EventEmitter {
1907
1616
  this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
1908
1617
  }
1909
1618
  }
1910
- /**
1911
- * Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1912
- *
1913
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1914
- * @param {string} time - The time string of the message
1915
- * @param {string} name - The logger name of the message
1916
- * @param {string} message - The content of the message.
1917
- *
1918
- * @remarks
1919
- * The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
1920
- * It also replaces all occurrences of \" with " and angle-brackets with &lt; and &gt;.
1921
- * The function sends the message to all connected clients.
1922
- */
1923
1619
  wssSendMessage(level, time, name, message) {
1924
1620
  if (!level || !time || !name || !message)
1925
1621
  return;
1926
- // Remove ANSI escape codes from the message
1927
- // eslint-disable-next-line no-control-regex
1928
1622
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1929
- // Remove leading asterisks from the message
1930
1623
  message = message.replace(/^\*+/, '');
1931
- // Replace all occurrences of \t and \n
1932
1624
  message = message.replace(/[\t\n]/g, '');
1933
- // Remove non-printable characters
1934
- // eslint-disable-next-line no-control-regex
1935
1625
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1936
- // Replace all occurrences of \" with "
1937
1626
  message = message.replace(/\\"/g, '"');
1938
- // Replace all occurrences of angle-brackets with &lt; and &gt;"
1939
1627
  message = message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1940
- // Define the maximum allowed length for continuous characters without a space
1941
1628
  const maxContinuousLength = 100;
1942
1629
  const keepStartLength = 20;
1943
1630
  const keepEndLength = 20;
1944
- // Split the message into words
1945
1631
  message = message
1946
1632
  .split(' ')
1947
1633
  .map((word) => {
1948
- // If the word length exceeds the max continuous length, insert spaces and truncate
1949
1634
  if (word.length > maxContinuousLength) {
1950
1635
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1951
1636
  }
1952
1637
  return word;
1953
1638
  })
1954
1639
  .join(' ');
1955
- // Send the message to all connected clients
1956
1640
  this.webSocketServer?.clients.forEach((client) => {
1957
1641
  if (client.readyState === WebSocket.OPEN) {
1958
1642
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1959
1643
  }
1960
1644
  });
1961
1645
  }
1962
- /**
1963
- * Sends a need to refresh WebSocket message to all connected clients.
1964
- *
1965
- * @param {string} changed - The changed value. If null, the whole page will be refreshed.
1966
- * possible values:
1967
- * - 'matterbridgeLatestVersion'
1968
- * - 'matterbridgeAdvertise'
1969
- * - 'online'
1970
- * - 'offline'
1971
- * - 'reachability'
1972
- * - 'settings'
1973
- * - 'plugins'
1974
- * - 'pluginsRestart'
1975
- * - 'devices'
1976
- * - 'fabrics'
1977
- * - 'sessions'
1978
- */
1979
1646
  wssSendRefreshRequired(changed = null) {
1980
1647
  this.log.debug('Sending a refresh required message to all connected clients');
1981
- // Send the message to all connected clients
1982
1648
  this.webSocketServer?.clients.forEach((client) => {
1983
1649
  if (client.readyState === WebSocket.OPEN) {
1984
1650
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
1985
1651
  }
1986
1652
  });
1987
1653
  }
1988
- /**
1989
- * Sends a need to restart WebSocket message to all connected clients.
1990
- *
1991
- * @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients.
1992
- */
1993
1654
  wssSendRestartRequired(snackbar = true) {
1994
1655
  this.log.debug('Sending a restart required message to all connected clients');
1995
1656
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1996
1657
  if (snackbar === true)
1997
1658
  this.wssSendSnackbarMessage(`Restart required`, 0);
1998
- // Send the message to all connected clients
1999
1659
  this.webSocketServer?.clients.forEach((client) => {
2000
1660
  if (client.readyState === WebSocket.OPEN) {
2001
1661
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
2002
1662
  }
2003
1663
  });
2004
1664
  }
2005
- /**
2006
- * Sends a need to update WebSocket message to all connected clients.
2007
- *
2008
- */
2009
1665
  wssSendUpdateRequired() {
2010
1666
  this.log.debug('Sending an update required message to all connected clients');
2011
1667
  this.matterbridge.matterbridgeInformation.updateRequired = true;
2012
- // Send the message to all connected clients
2013
1668
  this.webSocketServer?.clients.forEach((client) => {
2014
1669
  if (client.readyState === WebSocket.OPEN) {
2015
1670
  client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
2016
1671
  }
2017
1672
  });
2018
1673
  }
2019
- /**
2020
- * Sends a cpu update message to all connected clients.
2021
- *
2022
- * @param {number} cpuUsage - The CPU usage percentage to send.
2023
- */
2024
1674
  wssSendCpuUpdate(cpuUsage) {
2025
1675
  if (hasParameter('debug'))
2026
1676
  this.log.debug('Sending a cpu update message to all connected clients');
2027
- // Send the message to all connected clients
2028
1677
  this.webSocketServer?.clients.forEach((client) => {
2029
1678
  if (client.readyState === WebSocket.OPEN) {
2030
1679
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
2031
1680
  }
2032
1681
  });
2033
1682
  }
2034
- /**
2035
- * Sends a memory update message to all connected clients.
2036
- *
2037
- * @param {string} totalMemory - The total memory in bytes.
2038
- * @param {string} freeMemory - The free memory in bytes.
2039
- * @param {string} rss - The resident set size in bytes.
2040
- * @param {string} heapTotal - The total heap memory in bytes.
2041
- * @param {string} heapUsed - The used heap memory in bytes.
2042
- * @param {string} external - The external memory in bytes.
2043
- * @param {string} arrayBuffers - The array buffers memory in bytes.
2044
- */
2045
1683
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
2046
1684
  if (hasParameter('debug'))
2047
1685
  this.log.debug('Sending a memory update message to all connected clients');
2048
- // Send the message to all connected clients
2049
1686
  this.webSocketServer?.clients.forEach((client) => {
2050
1687
  if (client.readyState === WebSocket.OPEN) {
2051
1688
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
2052
1689
  }
2053
1690
  });
2054
1691
  }
2055
- /**
2056
- * Sends an uptime update message to all connected clients.
2057
- *
2058
- * @param {string} systemUptime - The system uptime in a human-readable format.
2059
- * @param {string} processUptime - The process uptime in a human-readable format.
2060
- */
2061
1692
  wssSendUptimeUpdate(systemUptime, processUptime) {
2062
1693
  if (hasParameter('debug'))
2063
1694
  this.log.debug('Sending a uptime update message to all connected clients');
2064
- // Send the message to all connected clients
2065
1695
  this.webSocketServer?.clients.forEach((client) => {
2066
1696
  if (client.readyState === WebSocket.OPEN) {
2067
1697
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
2068
1698
  }
2069
1699
  });
2070
1700
  }
2071
- /**
2072
- * Sends an open snackbar message to all connected clients.
2073
- *
2074
- * @param {string} message - The message to send.
2075
- * @param {number} timeout - The timeout in seconds for the snackbar message.
2076
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
2077
- */
2078
1701
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
2079
1702
  this.log.debug('Sending a snackbar message to all connected clients');
2080
- // Send the message to all connected clients
2081
1703
  this.webSocketServer?.clients.forEach((client) => {
2082
1704
  if (client.readyState === WebSocket.OPEN) {
2083
1705
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
2084
1706
  }
2085
1707
  });
2086
1708
  }
2087
- /**
2088
- * Sends a close snackbar message to all connected clients.
2089
- *
2090
- * @param {string} message - The message to send.
2091
- */
2092
1709
  wssSendCloseSnackbarMessage(message) {
2093
1710
  this.log.debug('Sending a close snackbar message to all connected clients');
2094
- // Send the message to all connected clients
2095
1711
  this.webSocketServer?.clients.forEach((client) => {
2096
1712
  if (client.readyState === WebSocket.OPEN) {
2097
1713
  client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
2098
1714
  }
2099
1715
  });
2100
1716
  }
2101
- /**
2102
- * Sends an attribute update message to all connected WebSocket clients.
2103
- *
2104
- * @param {string | undefined} plugin - The name of the plugin.
2105
- * @param {string | undefined} serialNumber - The serial number of the device.
2106
- * @param {string | undefined} uniqueId - The unique identifier of the device.
2107
- * @param {string} cluster - The cluster name where the attribute belongs.
2108
- * @param {string} attribute - The name of the attribute that changed.
2109
- * @param {number | string | boolean} value - The new value of the attribute.
2110
- *
2111
- * @remarks
2112
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
2113
- * with the updated attribute information.
2114
- */
2115
1717
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
2116
1718
  this.log.debug('Sending an attribute update message to all connected clients');
2117
- // Send the message to all connected clients
2118
1719
  this.webSocketServer?.clients.forEach((client) => {
2119
1720
  if (client.readyState === WebSocket.OPEN) {
2120
1721
  client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
2121
1722
  }
2122
1723
  });
2123
1724
  }
2124
- /**
2125
- * Sends a message to all connected clients.
2126
- *
2127
- * @param {number} id - The message id.
2128
- * @param {string} method - The message method.
2129
- * @param {Record<string, string | number | boolean>} params - The message parameters.
2130
- */
2131
1725
  wssBroadcastMessage(id, method, params) {
2132
1726
  this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
2133
- // Send the message to all connected clients
2134
1727
  this.webSocketServer?.clients.forEach((client) => {
2135
1728
  if (client.readyState === WebSocket.OPEN) {
2136
1729
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -2138,4 +1731,3 @@ export class Frontend extends EventEmitter {
2138
1731
  });
2139
1732
  }
2140
1733
  }
2141
- //# sourceMappingURL=frontend.js.map