matterbridge 3.1.2 → 3.1.3-dev-20250714-c9b85b3

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 (208) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +8 -0
  3. package/dist/cli.js +2 -91
  4. package/dist/cliEmitter.js +0 -30
  5. package/dist/clusters/export.js +0 -2
  6. package/dist/defaultConfigSchema.js +0 -24
  7. package/dist/deviceManager.js +1 -94
  8. package/dist/devices/batteryStorage.js +1 -48
  9. package/dist/devices/evse.js +10 -74
  10. package/dist/devices/export.js +0 -2
  11. package/dist/devices/heatPump.js +2 -50
  12. package/dist/devices/laundryDryer.js +6 -83
  13. package/dist/devices/laundryWasher.js +7 -91
  14. package/dist/devices/roboticVacuumCleaner.js +6 -89
  15. package/dist/devices/solarPower.js +0 -38
  16. package/dist/devices/waterHeater.js +2 -82
  17. package/dist/frontend.js +33 -430
  18. package/dist/globalMatterbridge.js +0 -47
  19. package/dist/helpers.js +0 -53
  20. package/dist/index.js +1 -30
  21. package/dist/logger/export.js +0 -1
  22. package/dist/matter/behaviors.js +0 -2
  23. package/dist/matter/clusters.js +0 -2
  24. package/dist/matter/devices.js +0 -2
  25. package/dist/matter/endpoints.js +0 -2
  26. package/dist/matter/export.js +0 -3
  27. package/dist/matter/types.js +0 -3
  28. package/dist/matterbridge.js +77 -911
  29. package/dist/matterbridgeAccessoryPlatform.js +0 -36
  30. package/dist/matterbridgeBehaviors.js +1 -61
  31. package/dist/matterbridgeDeviceTypes.js +15 -579
  32. package/dist/matterbridgeDynamicPlatform.js +0 -36
  33. package/dist/matterbridgeEndpoint.js +51 -1053
  34. package/dist/matterbridgeEndpointHelpers.js +12 -322
  35. package/dist/matterbridgePlatform.js +0 -233
  36. package/dist/matterbridgeTypes.js +0 -25
  37. package/dist/pluginManager.js +3 -271
  38. package/dist/shelly.js +7 -168
  39. package/dist/storage/export.js +0 -1
  40. package/dist/update.js +0 -54
  41. package/dist/utils/colorUtils.js +2 -263
  42. package/dist/utils/commandLine.js +0 -54
  43. package/dist/utils/copyDirectory.js +1 -38
  44. package/dist/utils/createDirectory.js +0 -33
  45. package/dist/utils/createZip.js +2 -47
  46. package/dist/utils/deepCopy.js +0 -39
  47. package/dist/utils/deepEqual.js +1 -72
  48. package/dist/utils/export.js +0 -1
  49. package/dist/utils/hex.js +0 -58
  50. package/dist/utils/isvalid.js +0 -101
  51. package/dist/utils/network.js +5 -83
  52. package/dist/utils/spawn.js +0 -18
  53. package/dist/utils/wait.js +9 -62
  54. package/npm-shrinkwrap.json +5 -5
  55. package/package.json +1 -2
  56. package/dist/cli.d.ts +0 -26
  57. package/dist/cli.d.ts.map +0 -1
  58. package/dist/cli.js.map +0 -1
  59. package/dist/cliEmitter.d.ts +0 -34
  60. package/dist/cliEmitter.d.ts.map +0 -1
  61. package/dist/cliEmitter.js.map +0 -1
  62. package/dist/clusters/export.d.ts +0 -2
  63. package/dist/clusters/export.d.ts.map +0 -1
  64. package/dist/clusters/export.js.map +0 -1
  65. package/dist/defaultConfigSchema.d.ts +0 -28
  66. package/dist/defaultConfigSchema.d.ts.map +0 -1
  67. package/dist/defaultConfigSchema.js.map +0 -1
  68. package/dist/deviceManager.d.ts +0 -112
  69. package/dist/deviceManager.d.ts.map +0 -1
  70. package/dist/deviceManager.js.map +0 -1
  71. package/dist/devices/batteryStorage.d.ts +0 -48
  72. package/dist/devices/batteryStorage.d.ts.map +0 -1
  73. package/dist/devices/batteryStorage.js.map +0 -1
  74. package/dist/devices/evse.d.ts +0 -75
  75. package/dist/devices/evse.d.ts.map +0 -1
  76. package/dist/devices/evse.js.map +0 -1
  77. package/dist/devices/export.d.ts +0 -9
  78. package/dist/devices/export.d.ts.map +0 -1
  79. package/dist/devices/export.js.map +0 -1
  80. package/dist/devices/heatPump.d.ts +0 -47
  81. package/dist/devices/heatPump.d.ts.map +0 -1
  82. package/dist/devices/heatPump.js.map +0 -1
  83. package/dist/devices/laundryDryer.d.ts +0 -87
  84. package/dist/devices/laundryDryer.d.ts.map +0 -1
  85. package/dist/devices/laundryDryer.js.map +0 -1
  86. package/dist/devices/laundryWasher.d.ts +0 -242
  87. package/dist/devices/laundryWasher.d.ts.map +0 -1
  88. package/dist/devices/laundryWasher.js.map +0 -1
  89. package/dist/devices/roboticVacuumCleaner.d.ts +0 -110
  90. package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
  91. package/dist/devices/roboticVacuumCleaner.js.map +0 -1
  92. package/dist/devices/solarPower.d.ts +0 -40
  93. package/dist/devices/solarPower.d.ts.map +0 -1
  94. package/dist/devices/solarPower.js.map +0 -1
  95. package/dist/devices/waterHeater.d.ts +0 -111
  96. package/dist/devices/waterHeater.d.ts.map +0 -1
  97. package/dist/devices/waterHeater.js.map +0 -1
  98. package/dist/frontend.d.ts +0 -303
  99. package/dist/frontend.d.ts.map +0 -1
  100. package/dist/frontend.js.map +0 -1
  101. package/dist/globalMatterbridge.d.ts +0 -59
  102. package/dist/globalMatterbridge.d.ts.map +0 -1
  103. package/dist/globalMatterbridge.js.map +0 -1
  104. package/dist/helpers.d.ts +0 -48
  105. package/dist/helpers.d.ts.map +0 -1
  106. package/dist/helpers.js.map +0 -1
  107. package/dist/index.d.ts +0 -33
  108. package/dist/index.d.ts.map +0 -1
  109. package/dist/index.js.map +0 -1
  110. package/dist/logger/export.d.ts +0 -2
  111. package/dist/logger/export.d.ts.map +0 -1
  112. package/dist/logger/export.js.map +0 -1
  113. package/dist/matter/behaviors.d.ts +0 -2
  114. package/dist/matter/behaviors.d.ts.map +0 -1
  115. package/dist/matter/behaviors.js.map +0 -1
  116. package/dist/matter/clusters.d.ts +0 -2
  117. package/dist/matter/clusters.d.ts.map +0 -1
  118. package/dist/matter/clusters.js.map +0 -1
  119. package/dist/matter/devices.d.ts +0 -2
  120. package/dist/matter/devices.d.ts.map +0 -1
  121. package/dist/matter/devices.js.map +0 -1
  122. package/dist/matter/endpoints.d.ts +0 -2
  123. package/dist/matter/endpoints.d.ts.map +0 -1
  124. package/dist/matter/endpoints.js.map +0 -1
  125. package/dist/matter/export.d.ts +0 -5
  126. package/dist/matter/export.d.ts.map +0 -1
  127. package/dist/matter/export.js.map +0 -1
  128. package/dist/matter/types.d.ts +0 -3
  129. package/dist/matter/types.d.ts.map +0 -1
  130. package/dist/matter/types.js.map +0 -1
  131. package/dist/matterbridge.d.ts +0 -450
  132. package/dist/matterbridge.d.ts.map +0 -1
  133. package/dist/matterbridge.js.map +0 -1
  134. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
  135. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  136. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  137. package/dist/matterbridgeBehaviors.d.ts +0 -1340
  138. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  139. package/dist/matterbridgeBehaviors.js.map +0 -1
  140. package/dist/matterbridgeDeviceTypes.d.ts +0 -709
  141. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  142. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  143. package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
  144. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  145. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  146. package/dist/matterbridgeEndpoint.d.ts +0 -1196
  147. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  148. package/dist/matterbridgeEndpoint.js.map +0 -1
  149. package/dist/matterbridgeEndpointHelpers.d.ts +0 -3198
  150. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  151. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  152. package/dist/matterbridgePlatform.d.ts +0 -310
  153. package/dist/matterbridgePlatform.d.ts.map +0 -1
  154. package/dist/matterbridgePlatform.js.map +0 -1
  155. package/dist/matterbridgeTypes.d.ts +0 -192
  156. package/dist/matterbridgeTypes.d.ts.map +0 -1
  157. package/dist/matterbridgeTypes.js.map +0 -1
  158. package/dist/pluginManager.d.ts +0 -291
  159. package/dist/pluginManager.d.ts.map +0 -1
  160. package/dist/pluginManager.js.map +0 -1
  161. package/dist/shelly.d.ts +0 -174
  162. package/dist/shelly.d.ts.map +0 -1
  163. package/dist/shelly.js.map +0 -1
  164. package/dist/storage/export.d.ts +0 -2
  165. package/dist/storage/export.d.ts.map +0 -1
  166. package/dist/storage/export.js.map +0 -1
  167. package/dist/update.d.ts +0 -59
  168. package/dist/update.d.ts.map +0 -1
  169. package/dist/update.js.map +0 -1
  170. package/dist/utils/colorUtils.d.ts +0 -117
  171. package/dist/utils/colorUtils.d.ts.map +0 -1
  172. package/dist/utils/colorUtils.js.map +0 -1
  173. package/dist/utils/commandLine.d.ts +0 -59
  174. package/dist/utils/commandLine.d.ts.map +0 -1
  175. package/dist/utils/commandLine.js.map +0 -1
  176. package/dist/utils/copyDirectory.d.ts +0 -33
  177. package/dist/utils/copyDirectory.d.ts.map +0 -1
  178. package/dist/utils/copyDirectory.js.map +0 -1
  179. package/dist/utils/createDirectory.d.ts +0 -34
  180. package/dist/utils/createDirectory.d.ts.map +0 -1
  181. package/dist/utils/createDirectory.js.map +0 -1
  182. package/dist/utils/createZip.d.ts +0 -39
  183. package/dist/utils/createZip.d.ts.map +0 -1
  184. package/dist/utils/createZip.js.map +0 -1
  185. package/dist/utils/deepCopy.d.ts +0 -32
  186. package/dist/utils/deepCopy.d.ts.map +0 -1
  187. package/dist/utils/deepCopy.js.map +0 -1
  188. package/dist/utils/deepEqual.d.ts +0 -54
  189. package/dist/utils/deepEqual.d.ts.map +0 -1
  190. package/dist/utils/deepEqual.js.map +0 -1
  191. package/dist/utils/export.d.ts +0 -12
  192. package/dist/utils/export.d.ts.map +0 -1
  193. package/dist/utils/export.js.map +0 -1
  194. package/dist/utils/hex.d.ts +0 -49
  195. package/dist/utils/hex.d.ts.map +0 -1
  196. package/dist/utils/hex.js.map +0 -1
  197. package/dist/utils/isvalid.d.ts +0 -103
  198. package/dist/utils/isvalid.d.ts.map +0 -1
  199. package/dist/utils/isvalid.js.map +0 -1
  200. package/dist/utils/network.d.ts +0 -76
  201. package/dist/utils/network.d.ts.map +0 -1
  202. package/dist/utils/network.js.map +0 -1
  203. package/dist/utils/spawn.d.ts +0 -11
  204. package/dist/utils/spawn.d.ts.map +0 -1
  205. package/dist/utils/spawn.js.map +0 -1
  206. package/dist/utils/wait.d.ts +0 -56
  207. package/dist/utils/wait.d.ts.map +0 -1
  208. 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
- // 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;
@@ -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 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
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
- // Set the global logger callback for the WebSocketServer
299
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
300
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
301
- callbackLogLevel = "info" /* LogLevel.INFO */;
302
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
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', // Indicate service is healthy
381
- uptime: process.uptime(), // Server uptime in seconds
382
- timestamp: new Date().toISOString(), // Current timestamp
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,23 +278,19 @@ 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 {
@@ -448,7 +303,6 @@ export class Frontend extends EventEmitter {
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 {
@@ -474,11 +327,9 @@ export class Frontend extends EventEmitter {
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
331
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
480
332
  try {
481
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
482
333
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), fs.constants.F_OK);
483
334
  const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), 'utf8');
484
335
  await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), data, 'utf-8');
@@ -489,18 +340,15 @@ export class Frontend extends EventEmitter {
489
340
  }
490
341
  res.type('text/plain');
491
342
  res.download(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'matterbridge.log', (error) => {
492
- /* istanbul ignore if */
493
343
  if (error) {
494
344
  this.log.error(`Error downloading log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
495
345
  res.status(500).send('Error downloading the matterbridge log file');
496
346
  }
497
347
  });
498
348
  });
499
- // Endpoint to download the matter log
500
349
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
501
350
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
502
351
  try {
503
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
504
352
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
505
353
  const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'utf8');
506
354
  await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), data, 'utf-8');
@@ -511,18 +359,15 @@ export class Frontend extends EventEmitter {
511
359
  }
512
360
  res.type('text/plain');
513
361
  res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
514
- /* istanbul ignore if */
515
362
  if (error) {
516
363
  this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
517
364
  res.status(500).send('Error downloading the matter log file');
518
365
  }
519
366
  });
520
367
  });
521
- // Endpoint to download the shelly log
522
368
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
523
369
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
524
370
  try {
525
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
526
371
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
527
372
  const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
528
373
  await fs.writeFile(path.join(os.tmpdir(), 'shelly.log'), data, 'utf-8');
@@ -533,90 +378,74 @@ export class Frontend extends EventEmitter {
533
378
  }
534
379
  res.type('text/plain');
535
380
  res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
536
- /* istanbul ignore if */
537
381
  if (error) {
538
382
  this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
539
383
  res.status(500).send('Error downloading Shelly system log file');
540
384
  }
541
385
  });
542
386
  });
543
- // Endpoint to download the matterbridge storage directory
544
387
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
545
388
  this.log.debug('The frontend sent /api/download-mbstorage');
546
389
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
547
390
  res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), `matterbridge.${this.matterbridge.nodeStorageName}.zip`, (error) => {
548
- /* istanbul ignore if */
549
391
  if (error) {
550
392
  this.log.error(`Error downloading file ${`matterbridge.${this.matterbridge.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
551
393
  res.status(500).send('Error downloading the matterbridge storage file');
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));
559
400
  res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), `matterbridge.${this.matterbridge.matterStorageName}.zip`, (error) => {
560
- /* istanbul ignore if */
561
401
  if (error) {
562
402
  this.log.error(`Error downloading the matter storage matterbridge.${this.matterbridge.matterStorageName}.zip: ${error instanceof Error ? error.message : error}`);
563
403
  res.status(500).send('Error downloading the matter storage zip file');
564
404
  }
565
405
  });
566
406
  });
567
- // Endpoint to download the matterbridge plugin directory
568
407
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
569
408
  this.log.debug('The frontend sent /api/download-pluginstorage');
570
409
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
571
410
  res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
572
- /* istanbul ignore if */
573
411
  if (error) {
574
412
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
575
413
  res.status(500).send('Error downloading the matterbridge plugin storage file');
576
414
  }
577
415
  });
578
416
  });
579
- // Endpoint to download the matterbridge plugin config files
580
417
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
581
418
  this.log.debug('The frontend sent /api/download-pluginconfig');
582
419
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
583
420
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
584
- /* istanbul ignore if */
585
421
  if (error) {
586
422
  this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
587
423
  res.status(500).send('Error downloading the matterbridge plugin config file');
588
424
  }
589
425
  });
590
426
  });
591
- // Endpoint to download the matterbridge backup (created with the backup command)
592
427
  this.expressApp.get('/api/download-backup', async (req, res) => {
593
428
  this.log.debug('The frontend sent /api/download-backup');
594
429
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
595
- /* istanbul ignore if */
596
430
  if (error) {
597
431
  this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
598
432
  res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
599
433
  }
600
434
  });
601
435
  });
602
- // Endpoint to upload a package
603
436
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
604
437
  const { filename } = req.body;
605
438
  const file = req.file;
606
- /* istanbul ignore if */
607
439
  if (!file || !filename) {
608
440
  this.log.error(`uploadpackage: invalid request: file and filename are required`);
609
441
  res.status(400).send('Invalid request: file and filename are required');
610
442
  return;
611
443
  }
612
444
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
613
- // Define the path where the plugin file will be saved
614
445
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
615
446
  try {
616
- // Move the uploaded file to the specified path
617
447
  await fs.rename(file.path, filePath);
618
448
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
619
- // Install the plugin package
620
449
  if (filename.endsWith('.tgz')) {
621
450
  const { spawnCommand } = await import('./utils/spawn.js');
622
451
  await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
@@ -636,7 +465,6 @@ export class Frontend extends EventEmitter {
636
465
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
637
466
  }
638
467
  });
639
- // Fallback for routing (must be the last route)
640
468
  this.expressApp.use((req, res) => {
641
469
  this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
642
470
  res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -645,16 +473,13 @@ export class Frontend extends EventEmitter {
645
473
  }
646
474
  async stop() {
647
475
  this.log.debug('Stopping the frontend...');
648
- // Remove listeners from the express app
649
476
  if (this.expressApp) {
650
477
  this.expressApp.removeAllListeners();
651
478
  this.expressApp = undefined;
652
479
  this.log.debug('Frontend app closed successfully');
653
480
  }
654
- // Close the WebSocket server
655
481
  if (this.webSocketServer) {
656
482
  this.log.debug('Closing WebSocket server...');
657
- // Close all active connections
658
483
  this.webSocketServer.clients.forEach((client) => {
659
484
  if (client.readyState === WebSocket.OPEN) {
660
485
  client.close();
@@ -674,7 +499,6 @@ export class Frontend extends EventEmitter {
674
499
  this.webSocketServer.removeAllListeners();
675
500
  this.webSocketServer = undefined;
676
501
  }
677
- // Close the http server
678
502
  if (this.httpServer) {
679
503
  this.log.debug('Closing http server...');
680
504
  await withTimeout(new Promise((resolve) => {
@@ -692,7 +516,6 @@ export class Frontend extends EventEmitter {
692
516
  this.httpServer = undefined;
693
517
  this.log.debug('Frontend http server closed successfully');
694
518
  }
695
- // Close the https server
696
519
  if (this.httpsServer) {
697
520
  this.log.debug('Closing https server...');
698
521
  await withTimeout(new Promise((resolve) => {
@@ -712,7 +535,6 @@ export class Frontend extends EventEmitter {
712
535
  }
713
536
  this.log.debug('Frontend stopped successfully');
714
537
  }
715
- // Function to format bytes to KB, MB, or GB
716
538
  formatMemoryUsage = (bytes) => {
717
539
  if (bytes >= 1024 ** 3) {
718
540
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -724,7 +546,6 @@ export class Frontend extends EventEmitter {
724
546
  return `${(bytes / 1024).toFixed(2)} KB`;
725
547
  }
726
548
  };
727
- // Function to format system uptime with only the most significant unit
728
549
  formatOsUpTime = (seconds) => {
729
550
  if (seconds >= 86400) {
730
551
  const days = Math.floor(seconds / 86400);
@@ -740,13 +561,7 @@ export class Frontend extends EventEmitter {
740
561
  }
741
562
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
742
563
  };
743
- /**
744
- * Retrieves the api settings data.
745
- *
746
- * @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
747
- */
748
564
  async getApiSettings() {
749
- // Update the system information
750
565
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
751
566
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
752
567
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -755,9 +570,9 @@ export class Frontend extends EventEmitter {
755
570
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
756
571
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
757
572
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
758
- // Update the matterbridge information
759
573
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
760
574
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
575
+ this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
761
576
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
762
577
  this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
763
578
  this.matterbridge.matterbridgeInformation.mattermdnsinterface = this.matterbridge.mdnsInterface;
@@ -766,20 +581,15 @@ export class Frontend extends EventEmitter {
766
581
  this.matterbridge.matterbridgeInformation.matterPort = (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540;
767
582
  this.matterbridge.matterbridgeInformation.matterDiscriminator = await this.matterbridge.nodeContext?.get('matterdiscriminator');
768
583
  this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
769
- this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.matterbridgePaired;
770
- this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeQrPairingCode;
771
- this.matterbridge.matterbridgeInformation.matterbridgeManualPairingCode = this.matterbridge.matterbridgeManualPairingCode;
772
- this.matterbridge.matterbridgeInformation.matterbridgeFabricInformations = this.matterbridge.matterbridgeFabricInformations;
773
- this.matterbridge.matterbridgeInformation.matterbridgeSessionInformations = this.matterbridge.matterbridgeSessionInformations;
774
- this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
584
+ if (this.matterbridge.bridgeMode === 'bridge' && this.matterbridge.serverNode) {
585
+ this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.serverNode.state.commissioning.commissioned;
586
+ this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.qrPairingCode;
587
+ this.matterbridge.matterbridgeInformation.matterbridgeManualPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.manualPairingCode;
588
+ this.matterbridge.matterbridgeInformation.matterbridgeFabricInformations = this.matterbridge.sanitizeFabricInformations(Object.values(this.matterbridge.serverNode.state.commissioning.fabrics));
589
+ this.matterbridge.matterbridgeInformation.matterbridgeSessionInformations = this.matterbridge.sanitizeSessionInformation(Object.values(this.matterbridge.serverNode.state.sessions.sessions));
590
+ }
775
591
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
776
592
  }
777
- /**
778
- * Retrieves the reachable attribute.
779
- *
780
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
781
- * @returns {boolean} The reachable attribute.
782
- */
783
593
  getReachability(device) {
784
594
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
785
595
  return false;
@@ -791,12 +601,6 @@ export class Frontend extends EventEmitter {
791
601
  return true;
792
602
  return false;
793
603
  }
794
- /**
795
- * Retrieves the power source attribute.
796
- *
797
- * @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
798
- * @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
799
- */
800
604
  getPowerSource(endpoint) {
801
605
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
802
606
  return undefined;
@@ -812,21 +616,13 @@ export class Frontend extends EventEmitter {
812
616
  }
813
617
  return;
814
618
  };
815
- // Root endpoint
816
619
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
817
620
  return powerSource(endpoint);
818
- // Child endpoints
819
621
  for (const child of endpoint.getChildEndpoints()) {
820
622
  if (child.hasClusterServer(PowerSource.Cluster.id))
821
623
  return powerSource(child);
822
624
  }
823
625
  }
824
- /**
825
- * Retrieves the commissioned status, matter pairing codes, fabrics and sessions from a given device.
826
- *
827
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the data from.
828
- * @returns {ApiDevicesMatter | undefined} An ApiDevicesMatter object or undefined if not found.
829
- */
830
626
  getMatterDataFromDevice(device) {
831
627
  if (device.mode === 'server' && device.serverNode) {
832
628
  return {
@@ -838,13 +634,6 @@ export class Frontend extends EventEmitter {
838
634
  };
839
635
  }
840
636
  }
841
- /**
842
- * Retrieves the cluster text description from a given device.
843
- * The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
844
- *
845
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
846
- * @returns {string} The attributes description of the cluster servers in the device.
847
- */
848
637
  getClusterTextFromDevice(device) {
849
638
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
850
639
  return '';
@@ -869,7 +658,6 @@ export class Frontend extends EventEmitter {
869
658
  let attributes = '';
870
659
  let supportedModes = [];
871
660
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
872
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
873
661
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
874
662
  return;
875
663
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -959,15 +747,11 @@ export class Frontend extends EventEmitter {
959
747
  if (clusterName === 'userLabel' && attributeName === 'labelList')
960
748
  attributes += `${getUserLabel(device)} `;
961
749
  });
962
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
963
750
  return attributes.trimStart().trimEnd();
964
751
  }
965
- /**
966
- * Retrieves the base registered plugins sanitized for res.json().
967
- *
968
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
969
- */
970
752
  getBaseRegisteredPlugins() {
753
+ if (this.matterbridge.hasCleanupStarted)
754
+ return [];
971
755
  const baseRegisteredPlugins = [];
972
756
  for (const plugin of this.matterbridge.plugins) {
973
757
  baseRegisteredPlugins.push({
@@ -989,35 +773,29 @@ export class Frontend extends EventEmitter {
989
773
  loaded: plugin.loaded,
990
774
  started: plugin.started,
991
775
  configured: plugin.configured,
992
- paired: plugin.paired,
993
776
  restartRequired: plugin.restartRequired,
994
- fabricInformations: plugin.fabricInformations,
995
- sessionInformations: plugin.sessionInformations,
996
777
  registeredDevices: plugin.registeredDevices,
997
778
  addedDevices: plugin.addedDevices,
998
- qrPairingCode: plugin.qrPairingCode,
999
- manualPairingCode: plugin.manualPairingCode,
1000
779
  configJson: plugin.configJson,
1001
780
  schemaJson: plugin.schemaJson,
1002
781
  hasWhiteList: plugin.configJson?.whiteList !== undefined,
1003
782
  hasBlackList: plugin.configJson?.blackList !== undefined,
783
+ paired: plugin.serverNode?.state.commissioning.commissioned,
784
+ qrPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode?.state.commissioning.pairingCodes.qrPairingCode,
785
+ manualPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode?.state.commissioning.pairingCodes.manualPairingCode,
786
+ fabricInformations: plugin.serverNode ? this.matterbridge.sanitizeFabricInformations(Object.values(plugin.serverNode?.state.commissioning.fabrics)) : undefined,
787
+ sessionInformations: plugin.serverNode ? this.matterbridge.sanitizeSessionInformation(Object.values(plugin.serverNode?.state.sessions.sessions)) : undefined,
1004
788
  });
1005
789
  }
1006
790
  return baseRegisteredPlugins;
1007
791
  }
1008
- /**
1009
- * Retrieves the devices from Matterbridge.
1010
- *
1011
- * @param {string} [pluginName] - The name of the plugin to filter devices by.
1012
- * @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
1013
- */
1014
792
  async getDevices(pluginName) {
793
+ if (this.matterbridge.hasCleanupStarted)
794
+ return [];
1015
795
  const devices = [];
1016
796
  for (const device of this.matterbridge.devices.array()) {
1017
- // Filter by pluginName if provided
1018
797
  if (pluginName && pluginName !== device.plugin)
1019
798
  continue;
1020
- // Check if the device has the required properties
1021
799
  if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1022
800
  continue;
1023
801
  devices.push({
@@ -1037,37 +815,22 @@ export class Frontend extends EventEmitter {
1037
815
  }
1038
816
  return devices;
1039
817
  }
1040
- /**
1041
- * Retrieves the clusters from a given plugin and endpoint number.
1042
- *
1043
- * Response for /api/clusters
1044
- *
1045
- * @param {string} pluginName - The name of the plugin.
1046
- * @param {number} endpointNumber - The endpoint number.
1047
- * @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
1048
- */
1049
818
  getClusters(pluginName, endpointNumber) {
1050
819
  const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
1051
820
  if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
1052
821
  this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1053
822
  return;
1054
823
  }
1055
- // this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
1056
- // Get the device types from the main endpoint
1057
824
  const deviceTypes = [];
1058
825
  const clusters = [];
1059
826
  endpoint.state.descriptor.deviceTypeList.forEach((d) => {
1060
827
  deviceTypes.push(d.deviceType);
1061
828
  });
1062
- // Get the clusters from the main endpoint
1063
829
  endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1064
830
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1065
831
  return;
1066
832
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1067
833
  return;
1068
- // console.log(
1069
- // `${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}`,
1070
- // );
1071
834
  clusters.push({
1072
835
  endpoint: endpoint.number.toString(),
1073
836
  id: 'main',
@@ -1080,18 +843,12 @@ export class Frontend extends EventEmitter {
1080
843
  attributeLocalValue: attributeValue,
1081
844
  });
1082
845
  });
1083
- // Get the child endpoints
1084
846
  const childEndpoints = endpoint.getChildEndpoints();
1085
- // if (childEndpoints.length === 0) {
1086
- // this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
1087
- // }
1088
847
  childEndpoints.forEach((childEndpoint) => {
1089
848
  if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
1090
849
  this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1091
850
  return;
1092
851
  }
1093
- // this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
1094
- // Get the device types of the child endpoint
1095
852
  const deviceTypes = [];
1096
853
  childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
1097
854
  deviceTypes.push(d.deviceType);
@@ -1101,12 +858,9 @@ export class Frontend extends EventEmitter {
1101
858
  return;
1102
859
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1103
860
  return;
1104
- // console.log(
1105
- // `${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}`,
1106
- // );
1107
861
  clusters.push({
1108
862
  endpoint: childEndpoint.number.toString(),
1109
- id: childEndpoint.maybeId ?? 'null', // Never happens
863
+ id: childEndpoint.maybeId ?? 'null',
1110
864
  deviceTypes,
1111
865
  clusterName: capitalizeFirstLetter(clusterName),
1112
866
  clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
@@ -1119,13 +873,6 @@ export class Frontend extends EventEmitter {
1119
873
  });
1120
874
  return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
1121
875
  }
1122
- /**
1123
- * Handles incoming websocket messages for the Matterbridge frontend.
1124
- *
1125
- * @param {WebSocket} client - The websocket client that sent the message.
1126
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1127
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1128
- */
1129
876
  async wsMessageHandler(client, message) {
1130
877
  let data;
1131
878
  try {
@@ -1172,41 +919,32 @@ export class Frontend extends EventEmitter {
1172
919
  this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
1173
920
  const packageName = data.params.packageName.replace(/@.*$/, '');
1174
921
  if (data.params.restart === false && packageName !== 'matterbridge') {
1175
- // The install comes from InstallPlugins
1176
922
  this.matterbridge.plugins
1177
923
  .add(packageName)
1178
924
  .then((plugin) => {
1179
925
  if (plugin) {
1180
- // The plugin is not registered
1181
926
  this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
1182
927
  this.matterbridge.plugins
1183
928
  .load(plugin, true, 'The plugin has been added', true)
1184
- // eslint-disable-next-line promise/no-nesting
1185
929
  .then(() => {
1186
930
  this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
1187
931
  this.wssSendRefreshRequired('plugins');
1188
932
  return;
1189
933
  })
1190
- // eslint-disable-next-line promise/no-nesting
1191
934
  .catch((_error) => {
1192
- //
1193
935
  });
1194
936
  }
1195
937
  else {
1196
- // The plugin is already registered
1197
938
  this.wssSendSnackbarMessage(`Restart required`, 0);
1198
939
  this.wssSendRefreshRequired('plugins');
1199
940
  this.wssSendRestartRequired();
1200
941
  }
1201
942
  return;
1202
943
  })
1203
- // eslint-disable-next-line promise/no-nesting
1204
944
  .catch((_error) => {
1205
- //
1206
945
  });
1207
946
  }
1208
947
  else {
1209
- // The package is matterbridge
1210
948
  if (this.matterbridge.restartMode !== '') {
1211
949
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1212
950
  this.matterbridge.shutdownProcess();
@@ -1229,7 +967,6 @@ export class Frontend extends EventEmitter {
1229
967
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
1230
968
  return;
1231
969
  }
1232
- // The package is a plugin
1233
970
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
1234
971
  if (plugin) {
1235
972
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
@@ -1238,7 +975,6 @@ export class Frontend extends EventEmitter {
1238
975
  this.wssSendRefreshRequired('plugins');
1239
976
  this.wssSendRefreshRequired('devices');
1240
977
  }
1241
- // Uninstall the package
1242
978
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1243
979
  const { spawnCommand } = await import('./utils/spawn.js');
1244
980
  spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
@@ -1279,7 +1015,6 @@ export class Frontend extends EventEmitter {
1279
1015
  return;
1280
1016
  })
1281
1017
  .catch((_error) => {
1282
- //
1283
1018
  });
1284
1019
  }
1285
1020
  else {
@@ -1326,7 +1061,6 @@ export class Frontend extends EventEmitter {
1326
1061
  return;
1327
1062
  })
1328
1063
  .catch((_error) => {
1329
- //
1330
1064
  });
1331
1065
  }
1332
1066
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1437,8 +1171,6 @@ export class Frontend extends EventEmitter {
1437
1171
  else if (data.method === '/api/advertise') {
1438
1172
  const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
1439
1173
  this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
1440
- this.matterbridge.matterbridgeQrPairingCode = pairingCodes?.qrPairingCode;
1441
- this.matterbridge.matterbridgeManualPairingCode = pairingCodes?.manualPairingCode;
1442
1174
  this.wssSendRefreshRequired('matterbridgeAdvertise');
1443
1175
  this.wssSendSnackbarMessage(`Started fabrics share`, 0);
1444
1176
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes, success: true }));
@@ -1561,22 +1293,22 @@ export class Frontend extends EventEmitter {
1561
1293
  if (isValidString(data.params.value, 4)) {
1562
1294
  this.log.debug('Matterbridge logger level:', data.params.value);
1563
1295
  if (data.params.value === 'Debug') {
1564
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1296
+ await this.matterbridge.setLogLevel("debug");
1565
1297
  }
1566
1298
  else if (data.params.value === 'Info') {
1567
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1299
+ await this.matterbridge.setLogLevel("info");
1568
1300
  }
1569
1301
  else if (data.params.value === 'Notice') {
1570
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1302
+ await this.matterbridge.setLogLevel("notice");
1571
1303
  }
1572
1304
  else if (data.params.value === 'Warn') {
1573
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1305
+ await this.matterbridge.setLogLevel("warn");
1574
1306
  }
1575
1307
  else if (data.params.value === 'Error') {
1576
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1308
+ await this.matterbridge.setLogLevel("error");
1577
1309
  }
1578
1310
  else if (data.params.value === 'Fatal') {
1579
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1311
+ await this.matterbridge.setLogLevel("fatal");
1580
1312
  }
1581
1313
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1582
1314
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1587,7 +1319,6 @@ export class Frontend extends EventEmitter {
1587
1319
  this.log.debug('Matterbridge file log:', data.params.value);
1588
1320
  this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
1589
1321
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1590
- // Create the file logger for matterbridge
1591
1322
  if (data.params.value)
1592
1323
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1593
1324
  else
@@ -1634,7 +1365,6 @@ export class Frontend extends EventEmitter {
1634
1365
  });
1635
1366
  }
1636
1367
  catch (error) {
1637
- /* istanbul ignore next */
1638
1368
  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}`);
1639
1369
  }
1640
1370
  }
@@ -1643,7 +1373,6 @@ export class Frontend extends EventEmitter {
1643
1373
  Logger.removeLogger('matterfilelogger');
1644
1374
  }
1645
1375
  catch (error) {
1646
- /* istanbul ignore next */
1647
1376
  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}`);
1648
1377
  }
1649
1378
  }
@@ -1756,19 +1485,15 @@ export class Frontend extends EventEmitter {
1756
1485
  return;
1757
1486
  }
1758
1487
  const config = plugin.configJson;
1759
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1760
1488
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1761
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1762
1489
  if (select === 'serial')
1763
1490
  this.log.info(`Selected device serial ${data.params.serial}`);
1764
1491
  if (select === 'name')
1765
1492
  this.log.info(`Selected device name ${data.params.name}`);
1766
1493
  if (config && select && (select === 'serial' || select === 'name')) {
1767
- // Remove postfix from the serial if it exists
1768
1494
  if (config.postfix) {
1769
1495
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1770
1496
  }
1771
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1772
1497
  if (isValidArray(config.whiteList, 1)) {
1773
1498
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1774
1499
  config.whiteList.push(data.params.serial);
@@ -1777,7 +1502,6 @@ export class Frontend extends EventEmitter {
1777
1502
  config.whiteList.push(data.params.name);
1778
1503
  }
1779
1504
  }
1780
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1781
1505
  if (isValidArray(config.blackList, 1)) {
1782
1506
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1783
1507
  config.blackList = config.blackList.filter((item) => item !== data.params.serial);
@@ -1808,9 +1532,7 @@ export class Frontend extends EventEmitter {
1808
1532
  return;
1809
1533
  }
1810
1534
  const config = plugin.configJson;
1811
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1812
1535
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1813
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1814
1536
  if (select === 'serial')
1815
1537
  this.log.info(`Unselected device serial ${data.params.serial}`);
1816
1538
  if (select === 'name')
@@ -1819,7 +1541,6 @@ export class Frontend extends EventEmitter {
1819
1541
  if (config.postfix) {
1820
1542
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1821
1543
  }
1822
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1823
1544
  if (isValidArray(config.whiteList, 1)) {
1824
1545
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1825
1546
  config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
@@ -1828,7 +1549,6 @@ export class Frontend extends EventEmitter {
1828
1549
  config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
1829
1550
  }
1830
1551
  }
1831
- // Add the serial to the blackList
1832
1552
  if (isValidArray(config.blackList)) {
1833
1553
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1834
1554
  config.blackList.push(data.params.serial);
@@ -1862,230 +1582,114 @@ export class Frontend extends EventEmitter {
1862
1582
  this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
1863
1583
  }
1864
1584
  }
1865
- /**
1866
- * Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1867
- *
1868
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1869
- * @param {string} time - The time string of the message
1870
- * @param {string} name - The logger name of the message
1871
- * @param {string} message - The content of the message.
1872
- *
1873
- * @remarks
1874
- * The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
1875
- * It also replaces all occurrences of \" with " and angle-brackets with &lt; and &gt;.
1876
- * The function sends the message to all connected clients.
1877
- */
1878
1585
  wssSendMessage(level, time, name, message) {
1879
1586
  if (!level || !time || !name || !message)
1880
1587
  return;
1881
- // Remove ANSI escape codes from the message
1882
- // eslint-disable-next-line no-control-regex
1883
1588
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1884
- // Remove leading asterisks from the message
1885
1589
  message = message.replace(/^\*+/, '');
1886
- // Replace all occurrences of \t and \n
1887
1590
  message = message.replace(/[\t\n]/g, '');
1888
- // Remove non-printable characters
1889
- // eslint-disable-next-line no-control-regex
1890
1591
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1891
- // Replace all occurrences of \" with "
1892
1592
  message = message.replace(/\\"/g, '"');
1893
- // Replace all occurrences of angle-brackets with &lt; and &gt;"
1894
1593
  message = message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1895
- // Define the maximum allowed length for continuous characters without a space
1896
1594
  const maxContinuousLength = 100;
1897
1595
  const keepStartLength = 20;
1898
1596
  const keepEndLength = 20;
1899
- // Split the message into words
1900
1597
  message = message
1901
1598
  .split(' ')
1902
1599
  .map((word) => {
1903
- // If the word length exceeds the max continuous length, insert spaces and truncate
1904
1600
  if (word.length > maxContinuousLength) {
1905
1601
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1906
1602
  }
1907
1603
  return word;
1908
1604
  })
1909
1605
  .join(' ');
1910
- // Send the message to all connected clients
1911
1606
  this.webSocketServer?.clients.forEach((client) => {
1912
1607
  if (client.readyState === WebSocket.OPEN) {
1913
1608
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1914
1609
  }
1915
1610
  });
1916
1611
  }
1917
- /**
1918
- * Sends a need to refresh WebSocket message to all connected clients.
1919
- *
1920
- * @param {string} changed - The changed value. If null, the whole page will be refreshed.
1921
- * possible values:
1922
- * - 'matterbridgeLatestVersion'
1923
- * - 'matterbridgeAdvertise'
1924
- * - 'online'
1925
- * - 'offline'
1926
- * - 'reachability'
1927
- * - 'settings'
1928
- * - 'plugins'
1929
- * - 'pluginsRestart'
1930
- * - 'devices'
1931
- * - 'fabrics'
1932
- * - 'sessions'
1933
- */
1934
1612
  wssSendRefreshRequired(changed = null) {
1935
1613
  this.log.debug('Sending a refresh required message to all connected clients');
1936
- // Send the message to all connected clients
1937
1614
  this.webSocketServer?.clients.forEach((client) => {
1938
1615
  if (client.readyState === WebSocket.OPEN) {
1939
1616
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
1940
1617
  }
1941
1618
  });
1942
1619
  }
1943
- /**
1944
- * Sends a need to restart WebSocket message to all connected clients.
1945
- *
1946
- * @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients.
1947
- */
1948
1620
  wssSendRestartRequired(snackbar = true) {
1949
1621
  this.log.debug('Sending a restart required message to all connected clients');
1950
1622
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1951
1623
  if (snackbar === true)
1952
1624
  this.wssSendSnackbarMessage(`Restart required`, 0);
1953
- // Send the message to all connected clients
1954
1625
  this.webSocketServer?.clients.forEach((client) => {
1955
1626
  if (client.readyState === WebSocket.OPEN) {
1956
1627
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
1957
1628
  }
1958
1629
  });
1959
1630
  }
1960
- /**
1961
- * Sends a need to update WebSocket message to all connected clients.
1962
- *
1963
- */
1964
1631
  wssSendUpdateRequired() {
1965
1632
  this.log.debug('Sending an update required message to all connected clients');
1966
1633
  this.matterbridge.matterbridgeInformation.updateRequired = true;
1967
- // Send the message to all connected clients
1968
1634
  this.webSocketServer?.clients.forEach((client) => {
1969
1635
  if (client.readyState === WebSocket.OPEN) {
1970
1636
  client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
1971
1637
  }
1972
1638
  });
1973
1639
  }
1974
- /**
1975
- * Sends a cpu update message to all connected clients.
1976
- *
1977
- * @param {number} cpuUsage - The CPU usage percentage to send.
1978
- */
1979
1640
  wssSendCpuUpdate(cpuUsage) {
1980
1641
  if (hasParameter('debug'))
1981
1642
  this.log.debug('Sending a cpu update message to all connected clients');
1982
- // Send the message to all connected clients
1983
1643
  this.webSocketServer?.clients.forEach((client) => {
1984
1644
  if (client.readyState === WebSocket.OPEN) {
1985
1645
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
1986
1646
  }
1987
1647
  });
1988
1648
  }
1989
- /**
1990
- * Sends a memory update message to all connected clients.
1991
- *
1992
- * @param {string} totalMemory - The total memory in bytes.
1993
- * @param {string} freeMemory - The free memory in bytes.
1994
- * @param {string} rss - The resident set size in bytes.
1995
- * @param {string} heapTotal - The total heap memory in bytes.
1996
- * @param {string} heapUsed - The used heap memory in bytes.
1997
- * @param {string} external - The external memory in bytes.
1998
- * @param {string} arrayBuffers - The array buffers memory in bytes.
1999
- */
2000
1649
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
2001
1650
  if (hasParameter('debug'))
2002
1651
  this.log.debug('Sending a memory update message to all connected clients');
2003
- // Send the message to all connected clients
2004
1652
  this.webSocketServer?.clients.forEach((client) => {
2005
1653
  if (client.readyState === WebSocket.OPEN) {
2006
1654
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
2007
1655
  }
2008
1656
  });
2009
1657
  }
2010
- /**
2011
- * Sends an uptime update message to all connected clients.
2012
- *
2013
- * @param {string} systemUptime - The system uptime in a human-readable format.
2014
- * @param {string} processUptime - The process uptime in a human-readable format.
2015
- */
2016
1658
  wssSendUptimeUpdate(systemUptime, processUptime) {
2017
1659
  if (hasParameter('debug'))
2018
1660
  this.log.debug('Sending a uptime update message to all connected clients');
2019
- // Send the message to all connected clients
2020
1661
  this.webSocketServer?.clients.forEach((client) => {
2021
1662
  if (client.readyState === WebSocket.OPEN) {
2022
1663
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
2023
1664
  }
2024
1665
  });
2025
1666
  }
2026
- /**
2027
- * Sends an open snackbar message to all connected clients.
2028
- *
2029
- * @param {string} message - The message to send.
2030
- * @param {number} timeout - The timeout in seconds for the snackbar message.
2031
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
2032
- */
2033
1667
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
2034
1668
  this.log.debug('Sending a snackbar message to all connected clients');
2035
- // Send the message to all connected clients
2036
1669
  this.webSocketServer?.clients.forEach((client) => {
2037
1670
  if (client.readyState === WebSocket.OPEN) {
2038
1671
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
2039
1672
  }
2040
1673
  });
2041
1674
  }
2042
- /**
2043
- * Sends a close snackbar message to all connected clients.
2044
- *
2045
- * @param {string} message - The message to send.
2046
- */
2047
1675
  wssSendCloseSnackbarMessage(message) {
2048
1676
  this.log.debug('Sending a close snackbar message to all connected clients');
2049
- // Send the message to all connected clients
2050
1677
  this.webSocketServer?.clients.forEach((client) => {
2051
1678
  if (client.readyState === WebSocket.OPEN) {
2052
1679
  client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
2053
1680
  }
2054
1681
  });
2055
1682
  }
2056
- /**
2057
- * Sends an attribute update message to all connected WebSocket clients.
2058
- *
2059
- * @param {string | undefined} plugin - The name of the plugin.
2060
- * @param {string | undefined} serialNumber - The serial number of the device.
2061
- * @param {string | undefined} uniqueId - The unique identifier of the device.
2062
- * @param {string} cluster - The cluster name where the attribute belongs.
2063
- * @param {string} attribute - The name of the attribute that changed.
2064
- * @param {number | string | boolean} value - The new value of the attribute.
2065
- *
2066
- * @remarks
2067
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
2068
- * with the updated attribute information.
2069
- */
2070
1683
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
2071
1684
  this.log.debug('Sending an attribute update message to all connected clients');
2072
- // Send the message to all connected clients
2073
1685
  this.webSocketServer?.clients.forEach((client) => {
2074
1686
  if (client.readyState === WebSocket.OPEN) {
2075
1687
  client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
2076
1688
  }
2077
1689
  });
2078
1690
  }
2079
- /**
2080
- * Sends a message to all connected clients.
2081
- *
2082
- * @param {number} id - The message id.
2083
- * @param {string} method - The message method.
2084
- * @param {Record<string, string | number | boolean>} params - The message parameters.
2085
- */
2086
1691
  wssBroadcastMessage(id, method, params) {
2087
1692
  this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
2088
- // Send the message to all connected clients
2089
1693
  this.webSocketServer?.clients.forEach((client) => {
2090
1694
  if (client.readyState === WebSocket.OPEN) {
2091
1695
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -2093,4 +1697,3 @@ export class Frontend extends EventEmitter {
2093
1697
  });
2094
1698
  }
2095
1699
  }
2096
- //# sourceMappingURL=frontend.js.map