matterbridge 3.1.2 → 3.1.3-dev-20250712-616f7ed

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 (207) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/cli.js +2 -91
  3. package/dist/cliEmitter.js +0 -30
  4. package/dist/clusters/export.js +0 -2
  5. package/dist/defaultConfigSchema.js +0 -24
  6. package/dist/deviceManager.js +1 -94
  7. package/dist/devices/batteryStorage.js +1 -48
  8. package/dist/devices/evse.js +10 -74
  9. package/dist/devices/export.js +0 -2
  10. package/dist/devices/heatPump.js +2 -50
  11. package/dist/devices/laundryDryer.js +6 -83
  12. package/dist/devices/laundryWasher.js +7 -91
  13. package/dist/devices/roboticVacuumCleaner.js +6 -89
  14. package/dist/devices/solarPower.js +0 -38
  15. package/dist/devices/waterHeater.js +2 -82
  16. package/dist/frontend.js +16 -417
  17. package/dist/globalMatterbridge.js +0 -47
  18. package/dist/helpers.js +0 -53
  19. package/dist/index.js +1 -30
  20. package/dist/logger/export.js +0 -1
  21. package/dist/matter/behaviors.js +0 -2
  22. package/dist/matter/clusters.js +0 -2
  23. package/dist/matter/devices.js +0 -2
  24. package/dist/matter/endpoints.js +0 -2
  25. package/dist/matter/export.js +0 -3
  26. package/dist/matter/types.js +0 -3
  27. package/dist/matterbridge.js +54 -803
  28. package/dist/matterbridgeAccessoryPlatform.js +0 -36
  29. package/dist/matterbridgeBehaviors.js +1 -61
  30. package/dist/matterbridgeDeviceTypes.js +15 -579
  31. package/dist/matterbridgeDynamicPlatform.js +0 -36
  32. package/dist/matterbridgeEndpoint.js +51 -1053
  33. package/dist/matterbridgeEndpointHelpers.js +12 -322
  34. package/dist/matterbridgePlatform.js +0 -233
  35. package/dist/matterbridgeTypes.js +0 -25
  36. package/dist/pluginManager.js +3 -269
  37. package/dist/shelly.js +7 -168
  38. package/dist/storage/export.js +0 -1
  39. package/dist/update.js +0 -54
  40. package/dist/utils/colorUtils.js +2 -263
  41. package/dist/utils/commandLine.js +0 -54
  42. package/dist/utils/copyDirectory.js +1 -38
  43. package/dist/utils/createDirectory.js +0 -33
  44. package/dist/utils/createZip.js +2 -47
  45. package/dist/utils/deepCopy.js +0 -39
  46. package/dist/utils/deepEqual.js +1 -72
  47. package/dist/utils/export.js +0 -1
  48. package/dist/utils/hex.js +0 -58
  49. package/dist/utils/isvalid.js +0 -101
  50. package/dist/utils/network.js +5 -83
  51. package/dist/utils/spawn.js +0 -18
  52. package/dist/utils/wait.js +9 -62
  53. package/npm-shrinkwrap.json +5 -5
  54. package/package.json +1 -2
  55. package/dist/cli.d.ts +0 -26
  56. package/dist/cli.d.ts.map +0 -1
  57. package/dist/cli.js.map +0 -1
  58. package/dist/cliEmitter.d.ts +0 -34
  59. package/dist/cliEmitter.d.ts.map +0 -1
  60. package/dist/cliEmitter.js.map +0 -1
  61. package/dist/clusters/export.d.ts +0 -2
  62. package/dist/clusters/export.d.ts.map +0 -1
  63. package/dist/clusters/export.js.map +0 -1
  64. package/dist/defaultConfigSchema.d.ts +0 -28
  65. package/dist/defaultConfigSchema.d.ts.map +0 -1
  66. package/dist/defaultConfigSchema.js.map +0 -1
  67. package/dist/deviceManager.d.ts +0 -112
  68. package/dist/deviceManager.d.ts.map +0 -1
  69. package/dist/deviceManager.js.map +0 -1
  70. package/dist/devices/batteryStorage.d.ts +0 -48
  71. package/dist/devices/batteryStorage.d.ts.map +0 -1
  72. package/dist/devices/batteryStorage.js.map +0 -1
  73. package/dist/devices/evse.d.ts +0 -75
  74. package/dist/devices/evse.d.ts.map +0 -1
  75. package/dist/devices/evse.js.map +0 -1
  76. package/dist/devices/export.d.ts +0 -9
  77. package/dist/devices/export.d.ts.map +0 -1
  78. package/dist/devices/export.js.map +0 -1
  79. package/dist/devices/heatPump.d.ts +0 -47
  80. package/dist/devices/heatPump.d.ts.map +0 -1
  81. package/dist/devices/heatPump.js.map +0 -1
  82. package/dist/devices/laundryDryer.d.ts +0 -87
  83. package/dist/devices/laundryDryer.d.ts.map +0 -1
  84. package/dist/devices/laundryDryer.js.map +0 -1
  85. package/dist/devices/laundryWasher.d.ts +0 -242
  86. package/dist/devices/laundryWasher.d.ts.map +0 -1
  87. package/dist/devices/laundryWasher.js.map +0 -1
  88. package/dist/devices/roboticVacuumCleaner.d.ts +0 -110
  89. package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
  90. package/dist/devices/roboticVacuumCleaner.js.map +0 -1
  91. package/dist/devices/solarPower.d.ts +0 -40
  92. package/dist/devices/solarPower.d.ts.map +0 -1
  93. package/dist/devices/solarPower.js.map +0 -1
  94. package/dist/devices/waterHeater.d.ts +0 -111
  95. package/dist/devices/waterHeater.d.ts.map +0 -1
  96. package/dist/devices/waterHeater.js.map +0 -1
  97. package/dist/frontend.d.ts +0 -303
  98. package/dist/frontend.d.ts.map +0 -1
  99. package/dist/frontend.js.map +0 -1
  100. package/dist/globalMatterbridge.d.ts +0 -59
  101. package/dist/globalMatterbridge.d.ts.map +0 -1
  102. package/dist/globalMatterbridge.js.map +0 -1
  103. package/dist/helpers.d.ts +0 -48
  104. package/dist/helpers.d.ts.map +0 -1
  105. package/dist/helpers.js.map +0 -1
  106. package/dist/index.d.ts +0 -33
  107. package/dist/index.d.ts.map +0 -1
  108. package/dist/index.js.map +0 -1
  109. package/dist/logger/export.d.ts +0 -2
  110. package/dist/logger/export.d.ts.map +0 -1
  111. package/dist/logger/export.js.map +0 -1
  112. package/dist/matter/behaviors.d.ts +0 -2
  113. package/dist/matter/behaviors.d.ts.map +0 -1
  114. package/dist/matter/behaviors.js.map +0 -1
  115. package/dist/matter/clusters.d.ts +0 -2
  116. package/dist/matter/clusters.d.ts.map +0 -1
  117. package/dist/matter/clusters.js.map +0 -1
  118. package/dist/matter/devices.d.ts +0 -2
  119. package/dist/matter/devices.d.ts.map +0 -1
  120. package/dist/matter/devices.js.map +0 -1
  121. package/dist/matter/endpoints.d.ts +0 -2
  122. package/dist/matter/endpoints.d.ts.map +0 -1
  123. package/dist/matter/endpoints.js.map +0 -1
  124. package/dist/matter/export.d.ts +0 -5
  125. package/dist/matter/export.d.ts.map +0 -1
  126. package/dist/matter/export.js.map +0 -1
  127. package/dist/matter/types.d.ts +0 -3
  128. package/dist/matter/types.d.ts.map +0 -1
  129. package/dist/matter/types.js.map +0 -1
  130. package/dist/matterbridge.d.ts +0 -450
  131. package/dist/matterbridge.d.ts.map +0 -1
  132. package/dist/matterbridge.js.map +0 -1
  133. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
  134. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  135. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  136. package/dist/matterbridgeBehaviors.d.ts +0 -1340
  137. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  138. package/dist/matterbridgeBehaviors.js.map +0 -1
  139. package/dist/matterbridgeDeviceTypes.d.ts +0 -709
  140. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  141. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  142. package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
  143. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  144. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  145. package/dist/matterbridgeEndpoint.d.ts +0 -1196
  146. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  147. package/dist/matterbridgeEndpoint.js.map +0 -1
  148. package/dist/matterbridgeEndpointHelpers.d.ts +0 -3198
  149. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  150. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  151. package/dist/matterbridgePlatform.d.ts +0 -310
  152. package/dist/matterbridgePlatform.d.ts.map +0 -1
  153. package/dist/matterbridgePlatform.js.map +0 -1
  154. package/dist/matterbridgeTypes.d.ts +0 -192
  155. package/dist/matterbridgeTypes.d.ts.map +0 -1
  156. package/dist/matterbridgeTypes.js.map +0 -1
  157. package/dist/pluginManager.d.ts +0 -291
  158. package/dist/pluginManager.d.ts.map +0 -1
  159. package/dist/pluginManager.js.map +0 -1
  160. package/dist/shelly.d.ts +0 -174
  161. package/dist/shelly.d.ts.map +0 -1
  162. package/dist/shelly.js.map +0 -1
  163. package/dist/storage/export.d.ts +0 -2
  164. package/dist/storage/export.d.ts.map +0 -1
  165. package/dist/storage/export.js.map +0 -1
  166. package/dist/update.d.ts +0 -59
  167. package/dist/update.d.ts.map +0 -1
  168. package/dist/update.js.map +0 -1
  169. package/dist/utils/colorUtils.d.ts +0 -117
  170. package/dist/utils/colorUtils.d.ts.map +0 -1
  171. package/dist/utils/colorUtils.js.map +0 -1
  172. package/dist/utils/commandLine.d.ts +0 -59
  173. package/dist/utils/commandLine.d.ts.map +0 -1
  174. package/dist/utils/commandLine.js.map +0 -1
  175. package/dist/utils/copyDirectory.d.ts +0 -33
  176. package/dist/utils/copyDirectory.d.ts.map +0 -1
  177. package/dist/utils/copyDirectory.js.map +0 -1
  178. package/dist/utils/createDirectory.d.ts +0 -34
  179. package/dist/utils/createDirectory.d.ts.map +0 -1
  180. package/dist/utils/createDirectory.js.map +0 -1
  181. package/dist/utils/createZip.d.ts +0 -39
  182. package/dist/utils/createZip.d.ts.map +0 -1
  183. package/dist/utils/createZip.js.map +0 -1
  184. package/dist/utils/deepCopy.d.ts +0 -32
  185. package/dist/utils/deepCopy.d.ts.map +0 -1
  186. package/dist/utils/deepCopy.js.map +0 -1
  187. package/dist/utils/deepEqual.d.ts +0 -54
  188. package/dist/utils/deepEqual.d.ts.map +0 -1
  189. package/dist/utils/deepEqual.js.map +0 -1
  190. package/dist/utils/export.d.ts +0 -12
  191. package/dist/utils/export.d.ts.map +0 -1
  192. package/dist/utils/export.js.map +0 -1
  193. package/dist/utils/hex.d.ts +0 -49
  194. package/dist/utils/hex.d.ts.map +0 -1
  195. package/dist/utils/hex.js.map +0 -1
  196. package/dist/utils/isvalid.d.ts +0 -103
  197. package/dist/utils/isvalid.d.ts.map +0 -1
  198. package/dist/utils/isvalid.js.map +0 -1
  199. package/dist/utils/network.d.ts +0 -76
  200. package/dist/utils/network.d.ts.map +0 -1
  201. package/dist/utils/network.js.map +0 -1
  202. package/dist/utils/spawn.d.ts +0 -11
  203. package/dist/utils/spawn.d.ts.map +0 -1
  204. package/dist/utils/spawn.js.map +0 -1
  205. package/dist/utils/wait.d.ts +0 -56
  206. package/dist/utils/wait.d.ts.map +0 -1
  207. 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,7 +570,6 @@ 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;
761
575
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
@@ -774,12 +588,6 @@ export class Frontend extends EventEmitter {
774
588
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
775
589
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
776
590
  }
777
- /**
778
- * Retrieves the reachable attribute.
779
- *
780
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
781
- * @returns {boolean} The reachable attribute.
782
- */
783
591
  getReachability(device) {
784
592
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
785
593
  return false;
@@ -791,12 +599,6 @@ export class Frontend extends EventEmitter {
791
599
  return true;
792
600
  return false;
793
601
  }
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
602
  getPowerSource(endpoint) {
801
603
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
802
604
  return undefined;
@@ -812,21 +614,13 @@ export class Frontend extends EventEmitter {
812
614
  }
813
615
  return;
814
616
  };
815
- // Root endpoint
816
617
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
817
618
  return powerSource(endpoint);
818
- // Child endpoints
819
619
  for (const child of endpoint.getChildEndpoints()) {
820
620
  if (child.hasClusterServer(PowerSource.Cluster.id))
821
621
  return powerSource(child);
822
622
  }
823
623
  }
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
624
  getMatterDataFromDevice(device) {
831
625
  if (device.mode === 'server' && device.serverNode) {
832
626
  return {
@@ -838,13 +632,6 @@ export class Frontend extends EventEmitter {
838
632
  };
839
633
  }
840
634
  }
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
635
  getClusterTextFromDevice(device) {
849
636
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
850
637
  return '';
@@ -869,7 +656,6 @@ export class Frontend extends EventEmitter {
869
656
  let attributes = '';
870
657
  let supportedModes = [];
871
658
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
872
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
873
659
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
874
660
  return;
875
661
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -959,14 +745,8 @@ export class Frontend extends EventEmitter {
959
745
  if (clusterName === 'userLabel' && attributeName === 'labelList')
960
746
  attributes += `${getUserLabel(device)} `;
961
747
  });
962
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
963
748
  return attributes.trimStart().trimEnd();
964
749
  }
965
- /**
966
- * Retrieves the base registered plugins sanitized for res.json().
967
- *
968
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
969
- */
970
750
  getBaseRegisteredPlugins() {
971
751
  const baseRegisteredPlugins = [];
972
752
  for (const plugin of this.matterbridge.plugins) {
@@ -1005,19 +785,11 @@ export class Frontend extends EventEmitter {
1005
785
  }
1006
786
  return baseRegisteredPlugins;
1007
787
  }
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
788
  async getDevices(pluginName) {
1015
789
  const devices = [];
1016
790
  for (const device of this.matterbridge.devices.array()) {
1017
- // Filter by pluginName if provided
1018
791
  if (pluginName && pluginName !== device.plugin)
1019
792
  continue;
1020
- // Check if the device has the required properties
1021
793
  if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1022
794
  continue;
1023
795
  devices.push({
@@ -1037,37 +809,22 @@ export class Frontend extends EventEmitter {
1037
809
  }
1038
810
  return devices;
1039
811
  }
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
812
  getClusters(pluginName, endpointNumber) {
1050
813
  const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
1051
814
  if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
1052
815
  this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1053
816
  return;
1054
817
  }
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
818
  const deviceTypes = [];
1058
819
  const clusters = [];
1059
820
  endpoint.state.descriptor.deviceTypeList.forEach((d) => {
1060
821
  deviceTypes.push(d.deviceType);
1061
822
  });
1062
- // Get the clusters from the main endpoint
1063
823
  endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1064
824
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1065
825
  return;
1066
826
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1067
827
  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
828
  clusters.push({
1072
829
  endpoint: endpoint.number.toString(),
1073
830
  id: 'main',
@@ -1080,18 +837,12 @@ export class Frontend extends EventEmitter {
1080
837
  attributeLocalValue: attributeValue,
1081
838
  });
1082
839
  });
1083
- // Get the child endpoints
1084
840
  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
841
  childEndpoints.forEach((childEndpoint) => {
1089
842
  if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
1090
843
  this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1091
844
  return;
1092
845
  }
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
846
  const deviceTypes = [];
1096
847
  childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
1097
848
  deviceTypes.push(d.deviceType);
@@ -1101,12 +852,9 @@ export class Frontend extends EventEmitter {
1101
852
  return;
1102
853
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1103
854
  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
855
  clusters.push({
1108
856
  endpoint: childEndpoint.number.toString(),
1109
- id: childEndpoint.maybeId ?? 'null', // Never happens
857
+ id: childEndpoint.maybeId ?? 'null',
1110
858
  deviceTypes,
1111
859
  clusterName: capitalizeFirstLetter(clusterName),
1112
860
  clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
@@ -1119,13 +867,6 @@ export class Frontend extends EventEmitter {
1119
867
  });
1120
868
  return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
1121
869
  }
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
870
  async wsMessageHandler(client, message) {
1130
871
  let data;
1131
872
  try {
@@ -1172,41 +913,32 @@ export class Frontend extends EventEmitter {
1172
913
  this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
1173
914
  const packageName = data.params.packageName.replace(/@.*$/, '');
1174
915
  if (data.params.restart === false && packageName !== 'matterbridge') {
1175
- // The install comes from InstallPlugins
1176
916
  this.matterbridge.plugins
1177
917
  .add(packageName)
1178
918
  .then((plugin) => {
1179
919
  if (plugin) {
1180
- // The plugin is not registered
1181
920
  this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
1182
921
  this.matterbridge.plugins
1183
922
  .load(plugin, true, 'The plugin has been added', true)
1184
- // eslint-disable-next-line promise/no-nesting
1185
923
  .then(() => {
1186
924
  this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
1187
925
  this.wssSendRefreshRequired('plugins');
1188
926
  return;
1189
927
  })
1190
- // eslint-disable-next-line promise/no-nesting
1191
928
  .catch((_error) => {
1192
- //
1193
929
  });
1194
930
  }
1195
931
  else {
1196
- // The plugin is already registered
1197
932
  this.wssSendSnackbarMessage(`Restart required`, 0);
1198
933
  this.wssSendRefreshRequired('plugins');
1199
934
  this.wssSendRestartRequired();
1200
935
  }
1201
936
  return;
1202
937
  })
1203
- // eslint-disable-next-line promise/no-nesting
1204
938
  .catch((_error) => {
1205
- //
1206
939
  });
1207
940
  }
1208
941
  else {
1209
- // The package is matterbridge
1210
942
  if (this.matterbridge.restartMode !== '') {
1211
943
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1212
944
  this.matterbridge.shutdownProcess();
@@ -1229,7 +961,6 @@ export class Frontend extends EventEmitter {
1229
961
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
1230
962
  return;
1231
963
  }
1232
- // The package is a plugin
1233
964
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
1234
965
  if (plugin) {
1235
966
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
@@ -1238,7 +969,6 @@ export class Frontend extends EventEmitter {
1238
969
  this.wssSendRefreshRequired('plugins');
1239
970
  this.wssSendRefreshRequired('devices');
1240
971
  }
1241
- // Uninstall the package
1242
972
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1243
973
  const { spawnCommand } = await import('./utils/spawn.js');
1244
974
  spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
@@ -1279,7 +1009,6 @@ export class Frontend extends EventEmitter {
1279
1009
  return;
1280
1010
  })
1281
1011
  .catch((_error) => {
1282
- //
1283
1012
  });
1284
1013
  }
1285
1014
  else {
@@ -1326,7 +1055,6 @@ export class Frontend extends EventEmitter {
1326
1055
  return;
1327
1056
  })
1328
1057
  .catch((_error) => {
1329
- //
1330
1058
  });
1331
1059
  }
1332
1060
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1561,22 +1289,22 @@ export class Frontend extends EventEmitter {
1561
1289
  if (isValidString(data.params.value, 4)) {
1562
1290
  this.log.debug('Matterbridge logger level:', data.params.value);
1563
1291
  if (data.params.value === 'Debug') {
1564
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1292
+ await this.matterbridge.setLogLevel("debug");
1565
1293
  }
1566
1294
  else if (data.params.value === 'Info') {
1567
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1295
+ await this.matterbridge.setLogLevel("info");
1568
1296
  }
1569
1297
  else if (data.params.value === 'Notice') {
1570
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1298
+ await this.matterbridge.setLogLevel("notice");
1571
1299
  }
1572
1300
  else if (data.params.value === 'Warn') {
1573
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1301
+ await this.matterbridge.setLogLevel("warn");
1574
1302
  }
1575
1303
  else if (data.params.value === 'Error') {
1576
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1304
+ await this.matterbridge.setLogLevel("error");
1577
1305
  }
1578
1306
  else if (data.params.value === 'Fatal') {
1579
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1307
+ await this.matterbridge.setLogLevel("fatal");
1580
1308
  }
1581
1309
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1582
1310
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1587,7 +1315,6 @@ export class Frontend extends EventEmitter {
1587
1315
  this.log.debug('Matterbridge file log:', data.params.value);
1588
1316
  this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
1589
1317
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1590
- // Create the file logger for matterbridge
1591
1318
  if (data.params.value)
1592
1319
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1593
1320
  else
@@ -1634,7 +1361,6 @@ export class Frontend extends EventEmitter {
1634
1361
  });
1635
1362
  }
1636
1363
  catch (error) {
1637
- /* istanbul ignore next */
1638
1364
  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
1365
  }
1640
1366
  }
@@ -1643,7 +1369,6 @@ export class Frontend extends EventEmitter {
1643
1369
  Logger.removeLogger('matterfilelogger');
1644
1370
  }
1645
1371
  catch (error) {
1646
- /* istanbul ignore next */
1647
1372
  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
1373
  }
1649
1374
  }
@@ -1756,19 +1481,15 @@ export class Frontend extends EventEmitter {
1756
1481
  return;
1757
1482
  }
1758
1483
  const config = plugin.configJson;
1759
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1760
1484
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1761
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1762
1485
  if (select === 'serial')
1763
1486
  this.log.info(`Selected device serial ${data.params.serial}`);
1764
1487
  if (select === 'name')
1765
1488
  this.log.info(`Selected device name ${data.params.name}`);
1766
1489
  if (config && select && (select === 'serial' || select === 'name')) {
1767
- // Remove postfix from the serial if it exists
1768
1490
  if (config.postfix) {
1769
1491
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1770
1492
  }
1771
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1772
1493
  if (isValidArray(config.whiteList, 1)) {
1773
1494
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1774
1495
  config.whiteList.push(data.params.serial);
@@ -1777,7 +1498,6 @@ export class Frontend extends EventEmitter {
1777
1498
  config.whiteList.push(data.params.name);
1778
1499
  }
1779
1500
  }
1780
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1781
1501
  if (isValidArray(config.blackList, 1)) {
1782
1502
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1783
1503
  config.blackList = config.blackList.filter((item) => item !== data.params.serial);
@@ -1808,9 +1528,7 @@ export class Frontend extends EventEmitter {
1808
1528
  return;
1809
1529
  }
1810
1530
  const config = plugin.configJson;
1811
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1812
1531
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1813
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1814
1532
  if (select === 'serial')
1815
1533
  this.log.info(`Unselected device serial ${data.params.serial}`);
1816
1534
  if (select === 'name')
@@ -1819,7 +1537,6 @@ export class Frontend extends EventEmitter {
1819
1537
  if (config.postfix) {
1820
1538
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1821
1539
  }
1822
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1823
1540
  if (isValidArray(config.whiteList, 1)) {
1824
1541
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1825
1542
  config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
@@ -1828,7 +1545,6 @@ export class Frontend extends EventEmitter {
1828
1545
  config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
1829
1546
  }
1830
1547
  }
1831
- // Add the serial to the blackList
1832
1548
  if (isValidArray(config.blackList)) {
1833
1549
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1834
1550
  config.blackList.push(data.params.serial);
@@ -1862,230 +1578,114 @@ export class Frontend extends EventEmitter {
1862
1578
  this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
1863
1579
  }
1864
1580
  }
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
1581
  wssSendMessage(level, time, name, message) {
1879
1582
  if (!level || !time || !name || !message)
1880
1583
  return;
1881
- // Remove ANSI escape codes from the message
1882
- // eslint-disable-next-line no-control-regex
1883
1584
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1884
- // Remove leading asterisks from the message
1885
1585
  message = message.replace(/^\*+/, '');
1886
- // Replace all occurrences of \t and \n
1887
1586
  message = message.replace(/[\t\n]/g, '');
1888
- // Remove non-printable characters
1889
- // eslint-disable-next-line no-control-regex
1890
1587
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1891
- // Replace all occurrences of \" with "
1892
1588
  message = message.replace(/\\"/g, '"');
1893
- // Replace all occurrences of angle-brackets with &lt; and &gt;"
1894
1589
  message = message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1895
- // Define the maximum allowed length for continuous characters without a space
1896
1590
  const maxContinuousLength = 100;
1897
1591
  const keepStartLength = 20;
1898
1592
  const keepEndLength = 20;
1899
- // Split the message into words
1900
1593
  message = message
1901
1594
  .split(' ')
1902
1595
  .map((word) => {
1903
- // If the word length exceeds the max continuous length, insert spaces and truncate
1904
1596
  if (word.length > maxContinuousLength) {
1905
1597
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1906
1598
  }
1907
1599
  return word;
1908
1600
  })
1909
1601
  .join(' ');
1910
- // Send the message to all connected clients
1911
1602
  this.webSocketServer?.clients.forEach((client) => {
1912
1603
  if (client.readyState === WebSocket.OPEN) {
1913
1604
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1914
1605
  }
1915
1606
  });
1916
1607
  }
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
1608
  wssSendRefreshRequired(changed = null) {
1935
1609
  this.log.debug('Sending a refresh required message to all connected clients');
1936
- // Send the message to all connected clients
1937
1610
  this.webSocketServer?.clients.forEach((client) => {
1938
1611
  if (client.readyState === WebSocket.OPEN) {
1939
1612
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
1940
1613
  }
1941
1614
  });
1942
1615
  }
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
1616
  wssSendRestartRequired(snackbar = true) {
1949
1617
  this.log.debug('Sending a restart required message to all connected clients');
1950
1618
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1951
1619
  if (snackbar === true)
1952
1620
  this.wssSendSnackbarMessage(`Restart required`, 0);
1953
- // Send the message to all connected clients
1954
1621
  this.webSocketServer?.clients.forEach((client) => {
1955
1622
  if (client.readyState === WebSocket.OPEN) {
1956
1623
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
1957
1624
  }
1958
1625
  });
1959
1626
  }
1960
- /**
1961
- * Sends a need to update WebSocket message to all connected clients.
1962
- *
1963
- */
1964
1627
  wssSendUpdateRequired() {
1965
1628
  this.log.debug('Sending an update required message to all connected clients');
1966
1629
  this.matterbridge.matterbridgeInformation.updateRequired = true;
1967
- // Send the message to all connected clients
1968
1630
  this.webSocketServer?.clients.forEach((client) => {
1969
1631
  if (client.readyState === WebSocket.OPEN) {
1970
1632
  client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
1971
1633
  }
1972
1634
  });
1973
1635
  }
1974
- /**
1975
- * Sends a cpu update message to all connected clients.
1976
- *
1977
- * @param {number} cpuUsage - The CPU usage percentage to send.
1978
- */
1979
1636
  wssSendCpuUpdate(cpuUsage) {
1980
1637
  if (hasParameter('debug'))
1981
1638
  this.log.debug('Sending a cpu update message to all connected clients');
1982
- // Send the message to all connected clients
1983
1639
  this.webSocketServer?.clients.forEach((client) => {
1984
1640
  if (client.readyState === WebSocket.OPEN) {
1985
1641
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
1986
1642
  }
1987
1643
  });
1988
1644
  }
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
1645
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
2001
1646
  if (hasParameter('debug'))
2002
1647
  this.log.debug('Sending a memory update message to all connected clients');
2003
- // Send the message to all connected clients
2004
1648
  this.webSocketServer?.clients.forEach((client) => {
2005
1649
  if (client.readyState === WebSocket.OPEN) {
2006
1650
  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
1651
  }
2008
1652
  });
2009
1653
  }
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
1654
  wssSendUptimeUpdate(systemUptime, processUptime) {
2017
1655
  if (hasParameter('debug'))
2018
1656
  this.log.debug('Sending a uptime update message to all connected clients');
2019
- // Send the message to all connected clients
2020
1657
  this.webSocketServer?.clients.forEach((client) => {
2021
1658
  if (client.readyState === WebSocket.OPEN) {
2022
1659
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
2023
1660
  }
2024
1661
  });
2025
1662
  }
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
1663
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
2034
1664
  this.log.debug('Sending a snackbar message to all connected clients');
2035
- // Send the message to all connected clients
2036
1665
  this.webSocketServer?.clients.forEach((client) => {
2037
1666
  if (client.readyState === WebSocket.OPEN) {
2038
1667
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
2039
1668
  }
2040
1669
  });
2041
1670
  }
2042
- /**
2043
- * Sends a close snackbar message to all connected clients.
2044
- *
2045
- * @param {string} message - The message to send.
2046
- */
2047
1671
  wssSendCloseSnackbarMessage(message) {
2048
1672
  this.log.debug('Sending a close snackbar message to all connected clients');
2049
- // Send the message to all connected clients
2050
1673
  this.webSocketServer?.clients.forEach((client) => {
2051
1674
  if (client.readyState === WebSocket.OPEN) {
2052
1675
  client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
2053
1676
  }
2054
1677
  });
2055
1678
  }
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
1679
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
2071
1680
  this.log.debug('Sending an attribute update message to all connected clients');
2072
- // Send the message to all connected clients
2073
1681
  this.webSocketServer?.clients.forEach((client) => {
2074
1682
  if (client.readyState === WebSocket.OPEN) {
2075
1683
  client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
2076
1684
  }
2077
1685
  });
2078
1686
  }
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
1687
  wssBroadcastMessage(id, method, params) {
2087
1688
  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
1689
  this.webSocketServer?.clients.forEach((client) => {
2090
1690
  if (client.readyState === WebSocket.OPEN) {
2091
1691
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -2093,4 +1693,3 @@ export class Frontend extends EventEmitter {
2093
1693
  });
2094
1694
  }
2095
1695
  }
2096
- //# sourceMappingURL=frontend.js.map