matterbridge 3.1.1 → 3.1.2-dev-20250705-7da1eac

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 +17 -0
  2. package/README-DEV.md +6 -2
  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 +8 -84
  15. package/dist/devices/solarPower.js +0 -38
  16. package/dist/devices/waterHeater.js +2 -82
  17. package/dist/frontend.js +19 -420
  18. package/dist/globalMatterbridge.js +0 -47
  19. package/dist/helpers.js +0 -53
  20. package/dist/index.js +1 -39
  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 +50 -802
  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 +42 -1027
  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 -269
  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 +2 -2
  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 -103
  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 -302
  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 -41
  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 -1179
  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, wr, 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 } 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.matterbrideLoggerFile)}`);
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.matterbrideLoggerFile), fs.constants.F_OK);
483
334
  const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'utf8');
484
335
  await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), data, 'utf-8');
@@ -488,20 +339,16 @@ export class Frontend extends EventEmitter {
488
339
  this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
489
340
  }
490
341
  res.type('text/plain');
491
- // res.download(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
492
342
  res.download(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
493
- /* istanbul ignore if */
494
343
  if (error) {
495
344
  this.log.error(`Error downloading log file ${this.matterbridge.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
496
345
  res.status(500).send('Error downloading the matterbridge log file');
497
346
  }
498
347
  });
499
348
  });
500
- // Endpoint to download the matter log
501
349
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
502
350
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
503
351
  try {
504
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
505
352
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
506
353
  const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'utf8');
507
354
  await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), data, 'utf-8');
@@ -512,18 +359,15 @@ export class Frontend extends EventEmitter {
512
359
  }
513
360
  res.type('text/plain');
514
361
  res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
515
- /* istanbul ignore if */
516
362
  if (error) {
517
363
  this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
518
364
  res.status(500).send('Error downloading the matter log file');
519
365
  }
520
366
  });
521
367
  });
522
- // Endpoint to download the shelly log
523
368
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
524
369
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
525
370
  try {
526
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
527
371
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
528
372
  const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
529
373
  await fs.writeFile(path.join(os.tmpdir(), 'shelly.log'), data, 'utf-8');
@@ -534,14 +378,12 @@ export class Frontend extends EventEmitter {
534
378
  }
535
379
  res.type('text/plain');
536
380
  res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
537
- /* istanbul ignore if */
538
381
  if (error) {
539
382
  this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
540
383
  res.status(500).send('Error downloading Shelly system log file');
541
384
  }
542
385
  });
543
386
  });
544
- // Endpoint to download the matterbridge storage directory
545
387
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
546
388
  this.log.debug('The frontend sent /api/download-mbstorage');
547
389
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
@@ -552,7 +394,6 @@ export class Frontend extends EventEmitter {
552
394
  }
553
395
  });
554
396
  });
555
- // Endpoint to download the matter storage file
556
397
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
557
398
  this.log.debug('The frontend sent /api/download-mjstorage');
558
399
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
@@ -563,7 +404,6 @@ export class Frontend extends EventEmitter {
563
404
  }
564
405
  });
565
406
  });
566
- // Endpoint to download the matterbridge plugin directory
567
407
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
568
408
  this.log.debug('The frontend sent /api/download-pluginstorage');
569
409
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
@@ -574,7 +414,6 @@ export class Frontend extends EventEmitter {
574
414
  }
575
415
  });
576
416
  });
577
- // Endpoint to download the matterbridge plugin config files
578
417
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
579
418
  this.log.debug('The frontend sent /api/download-pluginconfig');
580
419
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
@@ -585,7 +424,6 @@ export class Frontend extends EventEmitter {
585
424
  }
586
425
  });
587
426
  });
588
- // Endpoint to download the matterbridge backup (created with the backup command)
589
427
  this.expressApp.get('/api/download-backup', async (req, res) => {
590
428
  this.log.debug('The frontend sent /api/download-backup');
591
429
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -595,7 +433,6 @@ export class Frontend extends EventEmitter {
595
433
  }
596
434
  });
597
435
  });
598
- // Endpoint to upload a package
599
436
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
600
437
  const { filename } = req.body;
601
438
  const file = req.file;
@@ -605,13 +442,10 @@ export class Frontend extends EventEmitter {
605
442
  return;
606
443
  }
607
444
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
608
- // Define the path where the plugin file will be saved
609
445
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
610
446
  try {
611
- // Move the uploaded file to the specified path
612
447
  await fs.rename(file.path, filePath);
613
448
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
614
- // Install the plugin package
615
449
  if (filename.endsWith('.tgz')) {
616
450
  const { spawnCommand } = await import('./utils/spawn.js');
617
451
  await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
@@ -631,7 +465,6 @@ export class Frontend extends EventEmitter {
631
465
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
632
466
  }
633
467
  });
634
- // Fallback for routing (must be the last route)
635
468
  this.expressApp.use((req, res) => {
636
469
  this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
637
470
  res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -640,15 +473,13 @@ export class Frontend extends EventEmitter {
640
473
  }
641
474
  async stop() {
642
475
  this.log.debug('Stopping the frontend...');
643
- // Remove listeners from the express app
644
476
  if (this.expressApp) {
645
477
  this.expressApp.removeAllListeners();
646
478
  this.expressApp = undefined;
647
479
  this.log.debug('Frontend app closed successfully');
648
480
  }
649
- // Close the WebSocket server
650
481
  if (this.webSocketServer) {
651
- // Close all active connections
482
+ this.log.debug('Closing WebSocket server...');
652
483
  this.webSocketServer.clients.forEach((client) => {
653
484
  if (client.readyState === WebSocket.OPEN) {
654
485
  client.close();
@@ -668,8 +499,8 @@ export class Frontend extends EventEmitter {
668
499
  this.webSocketServer.removeAllListeners();
669
500
  this.webSocketServer = undefined;
670
501
  }
671
- // Close the http server
672
502
  if (this.httpServer) {
503
+ this.log.debug('Closing http server...');
673
504
  await withTimeout(new Promise((resolve) => {
674
505
  this.httpServer?.close((error) => {
675
506
  if (error) {
@@ -685,8 +516,8 @@ export class Frontend extends EventEmitter {
685
516
  this.httpServer = undefined;
686
517
  this.log.debug('Frontend http server closed successfully');
687
518
  }
688
- // Close the https server
689
519
  if (this.httpsServer) {
520
+ this.log.debug('Closing https server...');
690
521
  await withTimeout(new Promise((resolve) => {
691
522
  this.httpsServer?.close((error) => {
692
523
  if (error) {
@@ -704,7 +535,6 @@ export class Frontend extends EventEmitter {
704
535
  }
705
536
  this.log.debug('Frontend stopped successfully');
706
537
  }
707
- // Function to format bytes to KB, MB, or GB
708
538
  formatMemoryUsage = (bytes) => {
709
539
  if (bytes >= 1024 ** 3) {
710
540
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -716,7 +546,6 @@ export class Frontend extends EventEmitter {
716
546
  return `${(bytes / 1024).toFixed(2)} KB`;
717
547
  }
718
548
  };
719
- // Function to format system uptime with only the most significant unit
720
549
  formatOsUpTime = (seconds) => {
721
550
  if (seconds >= 86400) {
722
551
  const days = Math.floor(seconds / 86400);
@@ -732,13 +561,7 @@ export class Frontend extends EventEmitter {
732
561
  }
733
562
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
734
563
  };
735
- /**
736
- * Retrieves the api settings data.
737
- *
738
- * @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
739
- */
740
564
  async getApiSettings() {
741
- // Update the system information
742
565
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
743
566
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
744
567
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -747,7 +570,6 @@ export class Frontend extends EventEmitter {
747
570
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
748
571
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
749
572
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
750
- // Update the matterbridge information
751
573
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
752
574
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
753
575
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
@@ -766,12 +588,6 @@ export class Frontend extends EventEmitter {
766
588
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
767
589
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
768
590
  }
769
- /**
770
- * Retrieves the reachable attribute.
771
- *
772
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
773
- * @returns {boolean} The reachable attribute.
774
- */
775
591
  getReachability(device) {
776
592
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
777
593
  return false;
@@ -783,12 +599,6 @@ export class Frontend extends EventEmitter {
783
599
  return true;
784
600
  return false;
785
601
  }
786
- /**
787
- * Retrieves the power source attribute.
788
- *
789
- * @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice object.
790
- * @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
791
- */
792
602
  getPowerSource(endpoint) {
793
603
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
794
604
  return undefined;
@@ -804,21 +614,13 @@ export class Frontend extends EventEmitter {
804
614
  }
805
615
  return;
806
616
  };
807
- // Root endpoint
808
617
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
809
618
  return powerSource(endpoint);
810
- // Child endpoints
811
619
  for (const child of endpoint.getChildEndpoints()) {
812
620
  if (child.hasClusterServer(PowerSource.Cluster.id))
813
621
  return powerSource(child);
814
622
  }
815
623
  }
816
- /**
817
- * Retrieves the matter pairing code from a given device.
818
- *
819
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object to retrieve the QR pairing code from.
820
- * @returns {ApiDevicesMatter | undefined} An ApiDevicesMatter object or undefined if not found.
821
- */
822
624
  getMatterDataFromDevice(device) {
823
625
  if (device.mode === 'server' && device.serverNode && device.serverContext) {
824
626
  return {
@@ -830,12 +632,6 @@ export class Frontend extends EventEmitter {
830
632
  };
831
633
  }
832
634
  }
833
- /**
834
- * Retrieves the cluster text description from a given device.
835
- *
836
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
837
- * @returns {string} The attributes description of the cluster servers in the device.
838
- */
839
635
  getClusterTextFromDevice(device) {
840
636
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
841
637
  return '';
@@ -876,19 +672,7 @@ export class Frontend extends EventEmitter {
876
672
  };
877
673
  let attributes = '';
878
674
  let supportedModes = [];
879
- /*
880
- Object.keys(device.behaviors.supported).forEach((clusterName) => {
881
- const clusterBehavior = device.behaviors.supported[lowercaseFirstLetter(clusterName)] as ClusterBehavior.Type | undefined;
882
- // console.log(`Device: ${device.deviceName} => Cluster: ${clusterName} Behavior: ${clusterBehavior?.id}`, clusterBehavior);
883
- if (clusterBehavior && clusterBehavior.cluster && clusterBehavior.cluster.attributes) {
884
- Object.entries(clusterBehavior.cluster.attributes).forEach(([attributeName, attribute]) => {
885
- // console.log(`${device.deviceName} => Cluster: ${clusterName} Attribute: ${attributeName}`, attribute);
886
- });
887
- }
888
- });
889
- */
890
675
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
891
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
892
676
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
893
677
  return;
894
678
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -980,14 +764,8 @@ export class Frontend extends EventEmitter {
980
764
  if (clusterName === 'userLabel' && attributeName === 'labelList')
981
765
  attributes += `${getUserLabel(device)} `;
982
766
  });
983
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
984
767
  return attributes.trimStart().trimEnd();
985
768
  }
986
- /**
987
- * Retrieves the base registered plugins sanitized for res.json().
988
- *
989
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
990
- */
991
769
  getBaseRegisteredPlugins() {
992
770
  const baseRegisteredPlugins = [];
993
771
  for (const plugin of this.matterbridge.plugins) {
@@ -1026,19 +804,11 @@ export class Frontend extends EventEmitter {
1026
804
  }
1027
805
  return baseRegisteredPlugins;
1028
806
  }
1029
- /**
1030
- * Retrieves the devices from Matterbridge.
1031
- *
1032
- * @param {string} [pluginName] - The name of the plugin to filter devices by.
1033
- * @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
1034
- */
1035
807
  async getDevices(pluginName) {
1036
808
  const devices = [];
1037
809
  for (const device of this.matterbridge.devices.array()) {
1038
- // Filter by pluginName if provided
1039
810
  if (pluginName && pluginName !== device.plugin)
1040
811
  continue;
1041
- // Check if the device has the required properties
1042
812
  if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1043
813
  continue;
1044
814
  devices.push({
@@ -1058,37 +828,22 @@ export class Frontend extends EventEmitter {
1058
828
  }
1059
829
  return devices;
1060
830
  }
1061
- /**
1062
- * Retrieves the clusters from a given plugin and endpoint number.
1063
- *
1064
- * Response for /api/clusters
1065
- *
1066
- * @param {string} pluginName - The name of the plugin.
1067
- * @param {number} endpointNumber - The endpoint number.
1068
- * @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
1069
- */
1070
831
  getClusters(pluginName, endpointNumber) {
1071
832
  const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
1072
833
  if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
1073
834
  this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1074
835
  return;
1075
836
  }
1076
- // this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
1077
- // Get the device types from the main endpoint
1078
837
  const deviceTypes = [];
1079
838
  const clusters = [];
1080
839
  endpoint.state.descriptor.deviceTypeList.forEach((d) => {
1081
840
  deviceTypes.push(d.deviceType);
1082
841
  });
1083
- // Get the clusters from the main endpoint
1084
842
  endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1085
843
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1086
844
  return;
1087
845
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1088
846
  return;
1089
- // console.log(
1090
- // `${idn}${endpoint.deviceName}${rs}${nf} => Cluster: ${CYAN}${clusterName} (0x${clusterId.toString(16).padStart(2, '0')})${nf} Attribute: ${CYAN}${attributeName} (0x${attributeId.toString(16).padStart(2, '0')})${nf} Value: ${YELLOW}${typeof attributeValue === 'object' ? stringify(attributeValue as object) : attributeValue}${nf}`,
1091
- // );
1092
847
  clusters.push({
1093
848
  endpoint: endpoint.number.toString(),
1094
849
  id: 'main',
@@ -1101,18 +856,12 @@ export class Frontend extends EventEmitter {
1101
856
  attributeLocalValue: attributeValue,
1102
857
  });
1103
858
  });
1104
- // Get the child endpoints
1105
859
  const childEndpoints = endpoint.getChildEndpoints();
1106
- // if (childEndpoints.length === 0) {
1107
- // this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
1108
- // }
1109
860
  childEndpoints.forEach((childEndpoint) => {
1110
861
  if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
1111
862
  this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1112
863
  return;
1113
864
  }
1114
- // this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
1115
- // Get the device types of the child endpoint
1116
865
  const deviceTypes = [];
1117
866
  childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
1118
867
  deviceTypes.push(d.deviceType);
@@ -1122,12 +871,9 @@ export class Frontend extends EventEmitter {
1122
871
  return;
1123
872
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1124
873
  return;
1125
- // console.log(
1126
- // `${idn}${childEndpoint.deviceName}${rs}${nf} => Cluster: ${CYAN}${clusterName} (0x${clusterId.toString(16).padStart(2, '0')})${nf} Attribute: ${CYAN}${attributeName} (0x${attributeId.toString(16).padStart(2, '0')})${nf} Value: ${YELLOW}${typeof attributeValue === 'object' ? stringify(attributeValue as object) : attributeValue}${nf}`,
1127
- // );
1128
874
  clusters.push({
1129
875
  endpoint: childEndpoint.number.toString(),
1130
- id: childEndpoint.maybeId ?? 'null', // Never happens
876
+ id: childEndpoint.maybeId ?? 'null',
1131
877
  deviceTypes,
1132
878
  clusterName: capitalizeFirstLetter(clusterName),
1133
879
  clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
@@ -1140,13 +886,6 @@ export class Frontend extends EventEmitter {
1140
886
  });
1141
887
  return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
1142
888
  }
1143
- /**
1144
- * Handles incoming websocket messages for the Matterbridge frontend.
1145
- *
1146
- * @param {WebSocket} client - The websocket client that sent the message.
1147
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1148
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1149
- */
1150
889
  async wsMessageHandler(client, message) {
1151
890
  let data;
1152
891
  try {
@@ -1193,41 +932,32 @@ export class Frontend extends EventEmitter {
1193
932
  this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
1194
933
  const packageName = data.params.packageName.replace(/@.*$/, '');
1195
934
  if (data.params.restart === false && packageName !== 'matterbridge') {
1196
- // The install comes from InstallPlugins
1197
935
  this.matterbridge.plugins
1198
936
  .add(packageName)
1199
937
  .then((plugin) => {
1200
938
  if (plugin) {
1201
- // The plugin is not registered
1202
939
  this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
1203
940
  this.matterbridge.plugins
1204
941
  .load(plugin, true, 'The plugin has been added', true)
1205
- // eslint-disable-next-line promise/no-nesting
1206
942
  .then(() => {
1207
943
  this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
1208
944
  this.wssSendRefreshRequired('plugins');
1209
945
  return;
1210
946
  })
1211
- // eslint-disable-next-line promise/no-nesting
1212
947
  .catch((_error) => {
1213
- //
1214
948
  });
1215
949
  }
1216
950
  else {
1217
- // The plugin is already registered
1218
951
  this.wssSendSnackbarMessage(`Restart required`, 0);
1219
952
  this.wssSendRefreshRequired('plugins');
1220
953
  this.wssSendRestartRequired();
1221
954
  }
1222
955
  return;
1223
956
  })
1224
- // eslint-disable-next-line promise/no-nesting
1225
957
  .catch((_error) => {
1226
- //
1227
958
  });
1228
959
  }
1229
960
  else {
1230
- // The package is matterbridge
1231
961
  if (this.matterbridge.restartMode !== '') {
1232
962
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1233
963
  this.matterbridge.shutdownProcess();
@@ -1250,7 +980,6 @@ export class Frontend extends EventEmitter {
1250
980
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
1251
981
  return;
1252
982
  }
1253
- // The package is a plugin
1254
983
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
1255
984
  if (plugin) {
1256
985
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
@@ -1259,7 +988,6 @@ export class Frontend extends EventEmitter {
1259
988
  this.wssSendRefreshRequired('plugins');
1260
989
  this.wssSendRefreshRequired('devices');
1261
990
  }
1262
- // Uninstall the package
1263
991
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1264
992
  const { spawnCommand } = await import('./utils/spawn.js');
1265
993
  spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
@@ -1300,7 +1028,6 @@ export class Frontend extends EventEmitter {
1300
1028
  return;
1301
1029
  })
1302
1030
  .catch((_error) => {
1303
- //
1304
1031
  });
1305
1032
  }
1306
1033
  else {
@@ -1347,7 +1074,6 @@ export class Frontend extends EventEmitter {
1347
1074
  return;
1348
1075
  })
1349
1076
  .catch((_error) => {
1350
- //
1351
1077
  });
1352
1078
  }
1353
1079
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1585,22 +1311,22 @@ export class Frontend extends EventEmitter {
1585
1311
  if (isValidString(data.params.value, 4)) {
1586
1312
  this.log.debug('Matterbridge logger level:', data.params.value);
1587
1313
  if (data.params.value === 'Debug') {
1588
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1314
+ await this.matterbridge.setLogLevel("debug");
1589
1315
  }
1590
1316
  else if (data.params.value === 'Info') {
1591
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1317
+ await this.matterbridge.setLogLevel("info");
1592
1318
  }
1593
1319
  else if (data.params.value === 'Notice') {
1594
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1320
+ await this.matterbridge.setLogLevel("notice");
1595
1321
  }
1596
1322
  else if (data.params.value === 'Warn') {
1597
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1323
+ await this.matterbridge.setLogLevel("warn");
1598
1324
  }
1599
1325
  else if (data.params.value === 'Error') {
1600
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1326
+ await this.matterbridge.setLogLevel("error");
1601
1327
  }
1602
1328
  else if (data.params.value === 'Fatal') {
1603
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1329
+ await this.matterbridge.setLogLevel("fatal");
1604
1330
  }
1605
1331
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1606
1332
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1611,7 +1337,6 @@ export class Frontend extends EventEmitter {
1611
1337
  this.log.debug('Matterbridge file log:', data.params.value);
1612
1338
  this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
1613
1339
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1614
- // Create the file logger for matterbridge
1615
1340
  if (data.params.value)
1616
1341
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1617
1342
  else
@@ -1776,19 +1501,15 @@ export class Frontend extends EventEmitter {
1776
1501
  return;
1777
1502
  }
1778
1503
  const config = plugin.configJson;
1779
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1780
1504
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1781
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1782
1505
  if (select === 'serial')
1783
1506
  this.log.info(`Selected device serial ${data.params.serial}`);
1784
1507
  if (select === 'name')
1785
1508
  this.log.info(`Selected device name ${data.params.name}`);
1786
1509
  if (config && select && (select === 'serial' || select === 'name')) {
1787
- // Remove postfix from the serial if it exists
1788
1510
  if (config.postfix) {
1789
1511
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1790
1512
  }
1791
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1792
1513
  if (isValidArray(config.whiteList, 1)) {
1793
1514
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1794
1515
  config.whiteList.push(data.params.serial);
@@ -1797,7 +1518,6 @@ export class Frontend extends EventEmitter {
1797
1518
  config.whiteList.push(data.params.name);
1798
1519
  }
1799
1520
  }
1800
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1801
1521
  if (isValidArray(config.blackList, 1)) {
1802
1522
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1803
1523
  config.blackList = config.blackList.filter((item) => item !== data.params.serial);
@@ -1827,9 +1547,7 @@ export class Frontend extends EventEmitter {
1827
1547
  return;
1828
1548
  }
1829
1549
  const config = plugin.configJson;
1830
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1831
1550
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1832
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1833
1551
  if (select === 'serial')
1834
1552
  this.log.info(`Unselected device serial ${data.params.serial}`);
1835
1553
  if (select === 'name')
@@ -1838,7 +1556,6 @@ export class Frontend extends EventEmitter {
1838
1556
  if (config.postfix) {
1839
1557
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1840
1558
  }
1841
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1842
1559
  if (isValidArray(config.whiteList, 1)) {
1843
1560
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1844
1561
  config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
@@ -1847,7 +1564,6 @@ export class Frontend extends EventEmitter {
1847
1564
  config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
1848
1565
  }
1849
1566
  }
1850
- // Add the serial to the blackList
1851
1567
  if (isValidArray(config.blackList)) {
1852
1568
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1853
1569
  config.blackList.push(data.params.serial);
@@ -1880,230 +1596,114 @@ export class Frontend extends EventEmitter {
1880
1596
  this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
1881
1597
  }
1882
1598
  }
1883
- /**
1884
- * Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1885
- *
1886
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1887
- * @param {string} time - The time string of the message
1888
- * @param {string} name - The logger name of the message
1889
- * @param {string} message - The content of the message.
1890
- *
1891
- * @remarks
1892
- * The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
1893
- * It also replaces all occurrences of \" with " and angle-brackets with &lt; and &gt;.
1894
- * The function sends the message to all connected clients.
1895
- */
1896
1599
  wssSendMessage(level, time, name, message) {
1897
1600
  if (!level || !time || !name || !message)
1898
1601
  return;
1899
- // Remove ANSI escape codes from the message
1900
- // eslint-disable-next-line no-control-regex
1901
1602
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1902
- // Remove leading asterisks from the message
1903
1603
  message = message.replace(/^\*+/, '');
1904
- // Replace all occurrences of \t and \n
1905
1604
  message = message.replace(/[\t\n]/g, '');
1906
- // Remove non-printable characters
1907
- // eslint-disable-next-line no-control-regex
1908
1605
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1909
- // Replace all occurrences of \" with "
1910
1606
  message = message.replace(/\\"/g, '"');
1911
- // Replace all occurrences of angle-brackets with &lt; and &gt;"
1912
1607
  message = message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1913
- // Define the maximum allowed length for continuous characters without a space
1914
1608
  const maxContinuousLength = 100;
1915
1609
  const keepStartLength = 20;
1916
1610
  const keepEndLength = 20;
1917
- // Split the message into words
1918
1611
  message = message
1919
1612
  .split(' ')
1920
1613
  .map((word) => {
1921
- // If the word length exceeds the max continuous length, insert spaces and truncate
1922
1614
  if (word.length > maxContinuousLength) {
1923
1615
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1924
1616
  }
1925
1617
  return word;
1926
1618
  })
1927
1619
  .join(' ');
1928
- // Send the message to all connected clients
1929
1620
  this.webSocketServer?.clients.forEach((client) => {
1930
1621
  if (client.readyState === WebSocket.OPEN) {
1931
1622
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1932
1623
  }
1933
1624
  });
1934
1625
  }
1935
- /**
1936
- * Sends a need to refresh WebSocket message to all connected clients.
1937
- *
1938
- * @param {string} changed - The changed value. If null, the whole page will be refreshed.
1939
- * possible values:
1940
- * - 'matterbridgeLatestVersion'
1941
- * - 'matterbridgeAdvertise'
1942
- * - 'online'
1943
- * - 'offline'
1944
- * - 'reachability'
1945
- * - 'settings'
1946
- * - 'plugins'
1947
- * - 'pluginsRestart'
1948
- * - 'devices'
1949
- * - 'fabrics'
1950
- * - 'sessions'
1951
- */
1952
1626
  wssSendRefreshRequired(changed = null) {
1953
1627
  this.log.debug('Sending a refresh required message to all connected clients');
1954
- // Send the message to all connected clients
1955
1628
  this.webSocketServer?.clients.forEach((client) => {
1956
1629
  if (client.readyState === WebSocket.OPEN) {
1957
1630
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
1958
1631
  }
1959
1632
  });
1960
1633
  }
1961
- /**
1962
- * Sends a need to restart WebSocket message to all connected clients.
1963
- *
1964
- * @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients.
1965
- */
1966
1634
  wssSendRestartRequired(snackbar = true) {
1967
1635
  this.log.debug('Sending a restart required message to all connected clients');
1968
1636
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1969
1637
  if (snackbar === true)
1970
1638
  this.wssSendSnackbarMessage(`Restart required`, 0);
1971
- // Send the message to all connected clients
1972
1639
  this.webSocketServer?.clients.forEach((client) => {
1973
1640
  if (client.readyState === WebSocket.OPEN) {
1974
1641
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
1975
1642
  }
1976
1643
  });
1977
1644
  }
1978
- /**
1979
- * Sends a need to update WebSocket message to all connected clients.
1980
- *
1981
- */
1982
1645
  wssSendUpdateRequired() {
1983
1646
  this.log.debug('Sending an update required message to all connected clients');
1984
1647
  this.matterbridge.matterbridgeInformation.updateRequired = true;
1985
- // Send the message to all connected clients
1986
1648
  this.webSocketServer?.clients.forEach((client) => {
1987
1649
  if (client.readyState === WebSocket.OPEN) {
1988
1650
  client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
1989
1651
  }
1990
1652
  });
1991
1653
  }
1992
- /**
1993
- * Sends a cpu update message to all connected clients.
1994
- *
1995
- * @param {number} cpuUsage - The CPU usage percentage to send.
1996
- */
1997
1654
  wssSendCpuUpdate(cpuUsage) {
1998
1655
  if (hasParameter('debug'))
1999
1656
  this.log.debug('Sending a cpu update message to all connected clients');
2000
- // Send the message to all connected clients
2001
1657
  this.webSocketServer?.clients.forEach((client) => {
2002
1658
  if (client.readyState === WebSocket.OPEN) {
2003
1659
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
2004
1660
  }
2005
1661
  });
2006
1662
  }
2007
- /**
2008
- * Sends a memory update message to all connected clients.
2009
- *
2010
- * @param {string} totalMemory - The total memory in bytes.
2011
- * @param {string} freeMemory - The free memory in bytes.
2012
- * @param {string} rss - The resident set size in bytes.
2013
- * @param {string} heapTotal - The total heap memory in bytes.
2014
- * @param {string} heapUsed - The used heap memory in bytes.
2015
- * @param {string} external - The external memory in bytes.
2016
- * @param {string} arrayBuffers - The array buffers memory in bytes.
2017
- */
2018
1663
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
2019
1664
  if (hasParameter('debug'))
2020
1665
  this.log.debug('Sending a memory update message to all connected clients');
2021
- // Send the message to all connected clients
2022
1666
  this.webSocketServer?.clients.forEach((client) => {
2023
1667
  if (client.readyState === WebSocket.OPEN) {
2024
1668
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
2025
1669
  }
2026
1670
  });
2027
1671
  }
2028
- /**
2029
- * Sends an uptime update message to all connected clients.
2030
- *
2031
- * @param {string} systemUptime - The system uptime in a human-readable format.
2032
- * @param {string} processUptime - The process uptime in a human-readable format.
2033
- */
2034
1672
  wssSendUptimeUpdate(systemUptime, processUptime) {
2035
1673
  if (hasParameter('debug'))
2036
1674
  this.log.debug('Sending a uptime update message to all connected clients');
2037
- // Send the message to all connected clients
2038
1675
  this.webSocketServer?.clients.forEach((client) => {
2039
1676
  if (client.readyState === WebSocket.OPEN) {
2040
1677
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
2041
1678
  }
2042
1679
  });
2043
1680
  }
2044
- /**
2045
- * Sends an open snackbar message to all connected clients.
2046
- *
2047
- * @param {string} message - The message to send.
2048
- * @param {number} timeout - The timeout in seconds for the snackbar message.
2049
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
2050
- */
2051
1681
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
2052
1682
  this.log.debug('Sending a snackbar message to all connected clients');
2053
- // Send the message to all connected clients
2054
1683
  this.webSocketServer?.clients.forEach((client) => {
2055
1684
  if (client.readyState === WebSocket.OPEN) {
2056
1685
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
2057
1686
  }
2058
1687
  });
2059
1688
  }
2060
- /**
2061
- * Sends a close snackbar message to all connected clients.
2062
- *
2063
- * @param {string} message - The message to send.
2064
- */
2065
1689
  wssSendCloseSnackbarMessage(message) {
2066
1690
  this.log.debug('Sending a close snackbar message to all connected clients');
2067
- // Send the message to all connected clients
2068
1691
  this.webSocketServer?.clients.forEach((client) => {
2069
1692
  if (client.readyState === WebSocket.OPEN) {
2070
1693
  client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
2071
1694
  }
2072
1695
  });
2073
1696
  }
2074
- /**
2075
- * Sends an attribute update message to all connected WebSocket clients.
2076
- *
2077
- * @param {string | undefined} plugin - The name of the plugin.
2078
- * @param {string | undefined} serialNumber - The serial number of the device.
2079
- * @param {string | undefined} uniqueId - The unique identifier of the device.
2080
- * @param {string} cluster - The cluster name where the attribute belongs.
2081
- * @param {string} attribute - The name of the attribute that changed.
2082
- * @param {number | string | boolean} value - The new value of the attribute.
2083
- *
2084
- * @remarks
2085
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
2086
- * with the updated attribute information.
2087
- */
2088
1697
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
2089
1698
  this.log.debug('Sending an attribute update message to all connected clients');
2090
- // Send the message to all connected clients
2091
1699
  this.webSocketServer?.clients.forEach((client) => {
2092
1700
  if (client.readyState === WebSocket.OPEN) {
2093
1701
  client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
2094
1702
  }
2095
1703
  });
2096
1704
  }
2097
- /**
2098
- * Sends a message to all connected clients.
2099
- *
2100
- * @param {number} id - The message id.
2101
- * @param {string} method - The message method.
2102
- * @param {Record<string, string | number | boolean>} params - The message parameters.
2103
- */
2104
1705
  wssBroadcastMessage(id, method, params) {
2105
1706
  this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
2106
- // Send the message to all connected clients
2107
1707
  this.webSocketServer?.clients.forEach((client) => {
2108
1708
  if (client.readyState === WebSocket.OPEN) {
2109
1709
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -2111,4 +1711,3 @@ export class Frontend extends EventEmitter {
2111
1711
  });
2112
1712
  }
2113
1713
  }
2114
- //# sourceMappingURL=frontend.js.map