matterbridge 3.0.7 → 3.0.8-dev-20250622-f9e44a2

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 +24 -0
  2. package/bin/matterbridge +2 -0
  3. package/dist/cli.js +5 -66
  4. package/dist/clusters/export.js +0 -2
  5. package/dist/defaultConfigSchema.js +0 -23
  6. package/dist/deviceManager.js +2 -95
  7. package/dist/devices/export.js +0 -2
  8. package/dist/evse.js +9 -65
  9. package/dist/frontend.js +47 -383
  10. package/dist/globalMatterbridge.js +0 -20
  11. package/dist/helpers.js +4 -52
  12. package/dist/index.js +2 -29
  13. package/dist/laundryWasher.js +7 -92
  14. package/dist/logger/export.js +0 -1
  15. package/dist/matter/behaviors.js +0 -2
  16. package/dist/matter/clusters.js +0 -2
  17. package/dist/matter/devices.js +0 -2
  18. package/dist/matter/endpoints.js +0 -2
  19. package/dist/matter/export.js +0 -2
  20. package/dist/matter/types.js +0 -2
  21. package/dist/matterbridge.js +53 -755
  22. package/dist/matterbridgeAccessoryPlatform.js +0 -34
  23. package/dist/matterbridgeBehaviors.js +1 -54
  24. package/dist/matterbridgeDeviceTypes.js +15 -578
  25. package/dist/matterbridgeDynamicPlatform.js +0 -34
  26. package/dist/matterbridgeEndpoint.js +45 -1000
  27. package/dist/matterbridgeEndpointHelpers.js +12 -206
  28. package/dist/matterbridgePlatform.js +13 -224
  29. package/dist/matterbridgeTypes.js +0 -24
  30. package/dist/pluginManager.js +4 -270
  31. package/dist/roboticVacuumCleaner.js +9 -84
  32. package/dist/shelly.js +9 -156
  33. package/dist/storage/export.js +0 -1
  34. package/dist/update.js +0 -53
  35. package/dist/utils/colorUtils.js +2 -205
  36. package/dist/utils/commandLine.js +0 -53
  37. package/dist/utils/copyDirectory.js +1 -37
  38. package/dist/utils/createDirectory.js +0 -31
  39. package/dist/utils/createZip.js +2 -42
  40. package/dist/utils/deepCopy.js +0 -38
  41. package/dist/utils/deepEqual.js +1 -71
  42. package/dist/utils/export.js +0 -1
  43. package/dist/utils/hex.js +0 -57
  44. package/dist/utils/isvalid.js +0 -100
  45. package/dist/utils/network.js +6 -77
  46. package/dist/utils/spawn.js +0 -16
  47. package/dist/utils/wait.js +10 -58
  48. package/dist/waterHeater.js +5 -65
  49. package/frontend/build/matterbridge 1250x1250.png +0 -0
  50. package/frontend/build/matterbridge 624x624.png +0 -0
  51. package/frontend/package-lock.json +19176 -0
  52. package/frontend/package.json +79 -0
  53. package/frontend/public/Shelly.svg +1 -0
  54. package/frontend/public/bmc-button.svg +22 -0
  55. package/frontend/public/discord.svg +5 -0
  56. package/frontend/public/favicon.ico +0 -0
  57. package/frontend/public/index.html +19 -0
  58. package/frontend/public/manifest.json +15 -0
  59. package/frontend/public/matter.png +0 -0
  60. package/frontend/public/matterbridge 1250x1250.png +0 -0
  61. package/frontend/public/matterbridge 32x32.png +0 -0
  62. package/frontend/public/matterbridge 624x624.png +0 -0
  63. package/frontend/public/matterbridge 64x64.png +0 -0
  64. package/frontend/public/matterbridge.svg +50 -0
  65. package/frontend/public/robots.txt +3 -0
  66. package/npm-shrinkwrap.json +27 -36
  67. package/package.json +2 -3
  68. package/dist/cli.d.ts +0 -29
  69. package/dist/cli.d.ts.map +0 -1
  70. package/dist/cli.js.map +0 -1
  71. package/dist/clusters/export.d.ts +0 -2
  72. package/dist/clusters/export.d.ts.map +0 -1
  73. package/dist/clusters/export.js.map +0 -1
  74. package/dist/defaultConfigSchema.d.ts +0 -27
  75. package/dist/defaultConfigSchema.d.ts.map +0 -1
  76. package/dist/defaultConfigSchema.js.map +0 -1
  77. package/dist/deviceManager.d.ts +0 -114
  78. package/dist/deviceManager.d.ts.map +0 -1
  79. package/dist/deviceManager.js.map +0 -1
  80. package/dist/devices/export.d.ts +0 -5
  81. package/dist/devices/export.d.ts.map +0 -1
  82. package/dist/devices/export.js.map +0 -1
  83. package/dist/evse.d.ts +0 -67
  84. package/dist/evse.d.ts.map +0 -1
  85. package/dist/evse.js.map +0 -1
  86. package/dist/frontend.d.ts +0 -256
  87. package/dist/frontend.d.ts.map +0 -1
  88. package/dist/frontend.js.map +0 -1
  89. package/dist/globalMatterbridge.d.ts +0 -32
  90. package/dist/globalMatterbridge.d.ts.map +0 -1
  91. package/dist/globalMatterbridge.js.map +0 -1
  92. package/dist/helpers.d.ts +0 -47
  93. package/dist/helpers.d.ts.map +0 -1
  94. package/dist/helpers.js.map +0 -1
  95. package/dist/index.d.ts +0 -37
  96. package/dist/index.d.ts.map +0 -1
  97. package/dist/index.js.map +0 -1
  98. package/dist/laundryWasher.d.ts +0 -243
  99. package/dist/laundryWasher.d.ts.map +0 -1
  100. package/dist/laundryWasher.js.map +0 -1
  101. package/dist/logger/export.d.ts +0 -2
  102. package/dist/logger/export.d.ts.map +0 -1
  103. package/dist/logger/export.js.map +0 -1
  104. package/dist/matter/behaviors.d.ts +0 -2
  105. package/dist/matter/behaviors.d.ts.map +0 -1
  106. package/dist/matter/behaviors.js.map +0 -1
  107. package/dist/matter/clusters.d.ts +0 -2
  108. package/dist/matter/clusters.d.ts.map +0 -1
  109. package/dist/matter/clusters.js.map +0 -1
  110. package/dist/matter/devices.d.ts +0 -2
  111. package/dist/matter/devices.d.ts.map +0 -1
  112. package/dist/matter/devices.js.map +0 -1
  113. package/dist/matter/endpoints.d.ts +0 -2
  114. package/dist/matter/endpoints.d.ts.map +0 -1
  115. package/dist/matter/endpoints.js.map +0 -1
  116. package/dist/matter/export.d.ts +0 -5
  117. package/dist/matter/export.d.ts.map +0 -1
  118. package/dist/matter/export.js.map +0 -1
  119. package/dist/matter/types.d.ts +0 -3
  120. package/dist/matter/types.d.ts.map +0 -1
  121. package/dist/matter/types.js.map +0 -1
  122. package/dist/matterbridge.d.ts +0 -445
  123. package/dist/matterbridge.d.ts.map +0 -1
  124. package/dist/matterbridge.js.map +0 -1
  125. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -40
  126. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  127. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  128. package/dist/matterbridgeBehaviors.d.ts +0 -1333
  129. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  130. package/dist/matterbridgeBehaviors.js.map +0 -1
  131. package/dist/matterbridgeDeviceTypes.d.ts +0 -644
  132. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  133. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  134. package/dist/matterbridgeDynamicPlatform.d.ts +0 -40
  135. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  136. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  137. package/dist/matterbridgeEndpoint.d.ts +0 -1145
  138. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  139. package/dist/matterbridgeEndpoint.js.map +0 -1
  140. package/dist/matterbridgeEndpointHelpers.d.ts +0 -3083
  141. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  142. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  143. package/dist/matterbridgePlatform.d.ts +0 -290
  144. package/dist/matterbridgePlatform.d.ts.map +0 -1
  145. package/dist/matterbridgePlatform.js.map +0 -1
  146. package/dist/matterbridgeTypes.d.ts +0 -196
  147. package/dist/matterbridgeTypes.d.ts.map +0 -1
  148. package/dist/matterbridgeTypes.js.map +0 -1
  149. package/dist/pluginManager.d.ts +0 -273
  150. package/dist/pluginManager.d.ts.map +0 -1
  151. package/dist/pluginManager.js.map +0 -1
  152. package/dist/roboticVacuumCleaner.d.ts +0 -102
  153. package/dist/roboticVacuumCleaner.d.ts.map +0 -1
  154. package/dist/roboticVacuumCleaner.js.map +0 -1
  155. package/dist/shelly.d.ts +0 -161
  156. package/dist/shelly.d.ts.map +0 -1
  157. package/dist/shelly.js.map +0 -1
  158. package/dist/storage/export.d.ts +0 -2
  159. package/dist/storage/export.d.ts.map +0 -1
  160. package/dist/storage/export.js.map +0 -1
  161. package/dist/update.d.ts +0 -58
  162. package/dist/update.d.ts.map +0 -1
  163. package/dist/update.js.map +0 -1
  164. package/dist/utils/colorUtils.d.ts +0 -61
  165. package/dist/utils/colorUtils.d.ts.map +0 -1
  166. package/dist/utils/colorUtils.js.map +0 -1
  167. package/dist/utils/commandLine.d.ts +0 -58
  168. package/dist/utils/commandLine.d.ts.map +0 -1
  169. package/dist/utils/commandLine.js.map +0 -1
  170. package/dist/utils/copyDirectory.d.ts +0 -32
  171. package/dist/utils/copyDirectory.d.ts.map +0 -1
  172. package/dist/utils/copyDirectory.js.map +0 -1
  173. package/dist/utils/createDirectory.d.ts +0 -32
  174. package/dist/utils/createDirectory.d.ts.map +0 -1
  175. package/dist/utils/createDirectory.js.map +0 -1
  176. package/dist/utils/createZip.d.ts +0 -38
  177. package/dist/utils/createZip.d.ts.map +0 -1
  178. package/dist/utils/createZip.js.map +0 -1
  179. package/dist/utils/deepCopy.d.ts +0 -31
  180. package/dist/utils/deepCopy.d.ts.map +0 -1
  181. package/dist/utils/deepCopy.js.map +0 -1
  182. package/dist/utils/deepEqual.d.ts +0 -53
  183. package/dist/utils/deepEqual.d.ts.map +0 -1
  184. package/dist/utils/deepEqual.js.map +0 -1
  185. package/dist/utils/export.d.ts +0 -12
  186. package/dist/utils/export.d.ts.map +0 -1
  187. package/dist/utils/export.js.map +0 -1
  188. package/dist/utils/hex.d.ts +0 -48
  189. package/dist/utils/hex.d.ts.map +0 -1
  190. package/dist/utils/hex.js.map +0 -1
  191. package/dist/utils/isvalid.d.ts +0 -102
  192. package/dist/utils/isvalid.d.ts.map +0 -1
  193. package/dist/utils/isvalid.js.map +0 -1
  194. package/dist/utils/network.d.ts +0 -69
  195. package/dist/utils/network.d.ts.map +0 -1
  196. package/dist/utils/network.js.map +0 -1
  197. package/dist/utils/spawn.d.ts +0 -12
  198. package/dist/utils/spawn.d.ts.map +0 -1
  199. package/dist/utils/spawn.js.map +0 -1
  200. package/dist/utils/wait.d.ts +0 -52
  201. package/dist/utils/wait.d.ts.map +0 -1
  202. package/dist/utils/wait.js.map +0 -1
  203. package/dist/waterHeater.d.ts +0 -90
  204. package/dist/waterHeater.d.ts.map +0 -1
  205. package/dist/waterHeater.js.map +0 -1
  206. package/tsconfig.jest.json +0 -8
  207. package/tsconfig.production.json +0 -13
package/dist/frontend.js CHANGED
@@ -1,113 +1,29 @@
1
- /**
2
- * This file contains the class Frontend.
3
- *
4
- * @file frontend.ts
5
- * @author Luca Liguori
6
- * @date 2025-01-13
7
- * @version 1.0.2
8
- *
9
- * Copyright 2025, 2026, 2027 Luca Liguori.
10
- *
11
- * Licensed under the Apache License, Version 2.0 (the "License");
12
- * you may not use this file except in compliance with the License.
13
- * You may obtain a copy of the License at
14
- *
15
- * http://www.apache.org/licenses/LICENSE-2.0
16
- *
17
- * Unless required by applicable law or agreed to in writing, software
18
- * distributed under the License is distributed on an "AS IS" BASIS,
19
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
- * See the License for the specific language governing permissions and
21
- * limitations under the License. *
22
- */
23
- // @matter
24
- import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
25
- import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
26
- // Node modules
27
1
  import { createServer } from 'node:http';
28
2
  import https from 'node:https';
29
3
  import os from 'node:os';
30
4
  import path from 'node:path';
31
5
  import { promises as fs } from 'node:fs';
32
- // Third-party modules
33
6
  import express from 'express';
34
7
  import WebSocket, { WebSocketServer } from 'ws';
35
8
  import multer from 'multer';
36
- // AnsiLogger module
37
- import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from './logger/export.js';
38
- // Matterbridge
39
- import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout } from './utils/export.js';
9
+ import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from 'node-ansi-logger';
10
+ import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
11
+ import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
12
+ import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout, hasParameter } from './utils/export.js';
40
13
  import { plg } from './matterbridgeTypes.js';
41
- import { hasParameter } from './utils/export.js';
42
14
  import { capitalizeFirstLetter } from './matterbridgeEndpointHelpers.js';
43
15
  import spawn from './utils/spawn.js';
44
- /**
45
- * Websocket message ID for logging.
46
- * @constant {number}
47
- */
48
16
  export const WS_ID_LOG = 0;
49
- /**
50
- * Websocket message ID indicating a refresh is needed.
51
- * @constant {number}
52
- */
53
17
  export const WS_ID_REFRESH_NEEDED = 1;
54
- /**
55
- * Websocket message ID indicating a restart is needed.
56
- * @constant {number}
57
- */
58
18
  export const WS_ID_RESTART_NEEDED = 2;
59
- /**
60
- * Websocket message ID indicating a cpu update.
61
- * @constant {number}
62
- */
63
19
  export const WS_ID_CPU_UPDATE = 3;
64
- /**
65
- * Websocket message ID indicating a memory update.
66
- * @constant {number}
67
- */
68
20
  export const WS_ID_MEMORY_UPDATE = 4;
69
- /**
70
- * Websocket message ID indicating an uptime update.
71
- * @constant {number}
72
- */
73
21
  export const WS_ID_UPTIME_UPDATE = 5;
74
- /**
75
- * Websocket message ID indicating a snackbar message.
76
- * @constant {number}
77
- */
78
22
  export const WS_ID_SNACKBAR = 6;
79
- /**
80
- * Websocket message ID indicating matterbridge has un update available.
81
- * @constant {number}
82
- */
83
23
  export const WS_ID_UPDATE_NEEDED = 7;
84
- /**
85
- * Websocket message ID indicating a state update.
86
- * @constant {number}
87
- */
88
24
  export const WS_ID_STATEUPDATE = 8;
89
- /**
90
- * Websocket message ID indicating to close a permanent snackbar message.
91
- * @constant {number}
92
- */
93
25
  export const WS_ID_CLOSE_SNACKBAR = 9;
94
- /**
95
- * Websocket message ID indicating a shelly system update.
96
- * check:
97
- * curl -k http://127.0.0.1:8101/api/updates/sys/check
98
- * perform:
99
- * curl -k http://127.0.0.1:8101/api/updates/sys/perform
100
- * @constant {number}
101
- */
102
26
  export const WS_ID_SHELLY_SYS_UPDATE = 100;
103
- /**
104
- * Websocket message ID indicating a shelly main update.
105
- * check:
106
- * curl -k http://127.0.0.1:8101/api/updates/main/check
107
- * perform:
108
- * curl -k http://127.0.0.1:8101/api/updates/main/perform
109
- * @constant {number}
110
- */
111
27
  export const WS_ID_SHELLY_MAIN_UPDATE = 101;
112
28
  export class Frontend {
113
29
  matterbridge;
@@ -120,7 +36,7 @@ export class Frontend {
120
36
  webSocketServer;
121
37
  constructor(matterbridge) {
122
38
  this.matterbridge = matterbridge;
123
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
39
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
124
40
  }
125
41
  set logLevel(logLevel) {
126
42
  this.log.logLevel = logLevel;
@@ -128,43 +44,13 @@ export class Frontend {
128
44
  async start(port = 8283) {
129
45
  this.port = port;
130
46
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
131
- // Initialize multer with the upload directory
132
47
  const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
133
48
  await fs.mkdir(uploadDir, { recursive: true });
134
49
  const upload = multer({ dest: uploadDir });
135
- // Create the express app that serves the frontend
136
50
  this.expressApp = express();
137
- // Inject logging/debug wrapper for route/middleware registration
138
- /*
139
- const methods = ['get', 'post', 'put', 'delete', 'use'];
140
- for (const method of methods) {
141
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
142
- const original = (this.expressApp as any)[method].bind(this.expressApp);
143
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
144
- (this.expressApp as any)[method] = (path: any, ...rest: any) => {
145
- try {
146
- console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
147
- return original(path, ...rest);
148
- } catch (err) {
149
- console.error(`[ERROR] Failed to register route: ${path}`);
150
- throw err;
151
- }
152
- };
153
- }
154
- */
155
- // Log all requests to the server for debugging
156
- /*
157
- this.expressApp.use((req, res, next) => {
158
- this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
159
- next();
160
- });
161
- */
162
- // Serve static files from '/static' endpoint
163
51
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
164
52
  if (!hasParameter('ssl')) {
165
- // Create an HTTP server and attach the express app
166
53
  this.httpServer = createServer(this.expressApp);
167
- // Listen on the specified port
168
54
  if (hasParameter('ingress')) {
169
55
  this.httpServer.listen(this.port, '0.0.0.0', () => {
170
56
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -178,7 +64,6 @@ export class Frontend {
178
64
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
179
65
  });
180
66
  }
181
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
67
  this.httpServer.on('error', (error) => {
183
68
  this.log.error(`Frontend http server error listening on ${this.port}`);
184
69
  switch (error.code) {
@@ -194,7 +79,6 @@ export class Frontend {
194
79
  });
195
80
  }
196
81
  else {
197
- // Load the SSL certificate, the private key and optionally the CA certificate
198
82
  let cert;
199
83
  try {
200
84
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
@@ -222,9 +106,7 @@ export class Frontend {
222
106
  this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
223
107
  }
224
108
  const serverOptions = { cert, key, ca };
225
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
226
109
  this.httpsServer = https.createServer(serverOptions, this.expressApp);
227
- // Listen on the specified port
228
110
  if (hasParameter('ingress')) {
229
111
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
230
112
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -238,7 +120,6 @@ export class Frontend {
238
120
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
239
121
  });
240
122
  }
241
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
242
123
  this.httpsServer.on('error', (error) => {
243
124
  this.log.error(`Frontend https server error listening on ${this.port}`);
244
125
  switch (error.code) {
@@ -255,18 +136,16 @@ export class Frontend {
255
136
  }
256
137
  if (this.initializeError)
257
138
  return;
258
- // Create a WebSocket server and attach it to the http or https server
259
139
  const wssPort = this.port;
260
140
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
261
141
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
262
142
  this.webSocketServer.on('connection', (ws, request) => {
263
143
  const clientIp = request.socket.remoteAddress;
264
- // Set the global logger callback for the WebSocketServer
265
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
266
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
267
- callbackLogLevel = "info" /* LogLevel.INFO */;
268
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
269
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
144
+ let callbackLogLevel = "notice";
145
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
146
+ callbackLogLevel = "info";
147
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
148
+ callbackLogLevel = "debug";
270
149
  AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
271
150
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
272
151
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -300,7 +179,6 @@ export class Frontend {
300
179
  this.webSocketServer.on('error', (ws, error) => {
301
180
  this.log.error(`WebSocketServer error: ${error}`);
302
181
  });
303
- // Subscribe to cli events
304
182
  const { cliEmitter } = await import('./cli.js');
305
183
  cliEmitter.removeAllListeners();
306
184
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
@@ -312,8 +190,6 @@ export class Frontend {
312
190
  cliEmitter.on('cpu', (cpuUsage) => {
313
191
  this.wssSendCpuUpdate(cpuUsage);
314
192
  });
315
- // Endpoint to validate login code
316
- // curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
317
193
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
318
194
  const { password } = req.body;
319
195
  this.log.debug('The frontend sent /api/login', password);
@@ -332,27 +208,23 @@ export class Frontend {
332
208
  this.log.warn('/api/login error wrong password');
333
209
  res.json({ valid: false });
334
210
  }
335
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
336
211
  }
337
212
  catch (error) {
338
213
  this.log.error('/api/login error getting password');
339
214
  res.json({ valid: false });
340
215
  }
341
216
  });
342
- // Endpoint to provide health check for docker
343
217
  this.expressApp.get('/health', (req, res) => {
344
218
  this.log.debug('Express received /health');
345
219
  const healthStatus = {
346
- status: 'ok', // Indicate service is healthy
347
- uptime: process.uptime(), // Server uptime in seconds
348
- timestamp: new Date().toISOString(), // Current timestamp
220
+ status: 'ok',
221
+ uptime: process.uptime(),
222
+ timestamp: new Date().toISOString(),
349
223
  };
350
224
  res.status(200).json(healthStatus);
351
225
  });
352
- // Endpoint to provide memory usage details
353
226
  this.expressApp.get('/memory', async (req, res) => {
354
227
  this.log.debug('Express received /memory');
355
- // Memory usage from process
356
228
  const memoryUsageRaw = process.memoryUsage();
357
229
  const memoryUsage = {
358
230
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -361,13 +233,10 @@ export class Frontend {
361
233
  external: this.formatMemoryUsage(memoryUsageRaw.external),
362
234
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
363
235
  };
364
- // V8 heap statistics
365
236
  const { default: v8 } = await import('node:v8');
366
237
  const heapStatsRaw = v8.getHeapStatistics();
367
238
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
368
- // Format heapStats
369
239
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
370
- // Format heapSpaces
371
240
  const heapSpaces = heapSpacesRaw.map((space) => ({
372
241
  ...space,
373
242
  space_size: this.formatMemoryUsage(space.space_size),
@@ -385,23 +254,19 @@ export class Frontend {
385
254
  };
386
255
  res.status(200).json(memoryReport);
387
256
  });
388
- // Endpoint to provide settings
389
257
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
390
258
  this.log.debug('The frontend sent /api/settings');
391
259
  res.json(await this.getApiSettings());
392
260
  });
393
- // Endpoint to provide plugins
394
261
  this.expressApp.get('/api/plugins', async (req, res) => {
395
262
  this.log.debug('The frontend sent /api/plugins');
396
263
  res.json(this.getBaseRegisteredPlugins());
397
264
  });
398
- // Endpoint to provide devices
399
265
  this.expressApp.get('/api/devices', async (req, res) => {
400
266
  this.log.debug('The frontend sent /api/devices');
401
267
  const devices = await this.getDevices();
402
268
  res.json(devices);
403
269
  });
404
- // Endpoint to view the matterbridge log
405
270
  this.expressApp.get('/api/view-mblog', async (req, res) => {
406
271
  this.log.debug('The frontend sent /api/view-mblog');
407
272
  try {
@@ -414,7 +279,6 @@ export class Frontend {
414
279
  res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
415
280
  }
416
281
  });
417
- // Endpoint to view the matter.js log
418
282
  this.expressApp.get('/api/view-mjlog', async (req, res) => {
419
283
  this.log.debug('The frontend sent /api/view-mjlog');
420
284
  try {
@@ -427,7 +291,6 @@ export class Frontend {
427
291
  res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
428
292
  }
429
293
  });
430
- // Endpoint to view the shelly log
431
294
  this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
432
295
  this.log.debug('The frontend sent /api/shellyviewsystemlog');
433
296
  try {
@@ -440,7 +303,6 @@ export class Frontend {
440
303
  res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
441
304
  }
442
305
  });
443
- // Endpoint to download the matterbridge log
444
306
  this.expressApp.get('/api/download-mblog', async (req, res) => {
445
307
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
446
308
  try {
@@ -453,7 +315,6 @@ export class Frontend {
453
315
  this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
454
316
  }
455
317
  res.type('text/plain');
456
- // res.download(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
457
318
  res.download(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
458
319
  if (error) {
459
320
  this.log.error(`Error downloading log file ${this.matterbridge.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
@@ -461,7 +322,6 @@ export class Frontend {
461
322
  }
462
323
  });
463
324
  });
464
- // Endpoint to download the matter log
465
325
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
466
326
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
467
327
  try {
@@ -481,7 +341,6 @@ export class Frontend {
481
341
  }
482
342
  });
483
343
  });
484
- // Endpoint to download the shelly log
485
344
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
486
345
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
487
346
  try {
@@ -501,7 +360,6 @@ export class Frontend {
501
360
  }
502
361
  });
503
362
  });
504
- // Endpoint to download the matterbridge storage directory
505
363
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
506
364
  this.log.debug('The frontend sent /api/download-mbstorage');
507
365
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
@@ -512,7 +370,6 @@ export class Frontend {
512
370
  }
513
371
  });
514
372
  });
515
- // Endpoint to download the matter storage file
516
373
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
517
374
  this.log.debug('The frontend sent /api/download-mjstorage');
518
375
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
@@ -523,7 +380,6 @@ export class Frontend {
523
380
  }
524
381
  });
525
382
  });
526
- // Endpoint to download the matterbridge plugin directory
527
383
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
528
384
  this.log.debug('The frontend sent /api/download-pluginstorage');
529
385
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
@@ -534,7 +390,6 @@ export class Frontend {
534
390
  }
535
391
  });
536
392
  });
537
- // Endpoint to download the matterbridge plugin config files
538
393
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
539
394
  this.log.debug('The frontend sent /api/download-pluginconfig');
540
395
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
@@ -545,7 +400,6 @@ export class Frontend {
545
400
  }
546
401
  });
547
402
  });
548
- // Endpoint to download the matterbridge backup (created with the backup command)
549
403
  this.expressApp.get('/api/download-backup', async (req, res) => {
550
404
  this.log.debug('The frontend sent /api/download-backup');
551
405
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -555,7 +409,6 @@ export class Frontend {
555
409
  }
556
410
  });
557
411
  });
558
- // Endpoint to upload a package
559
412
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
560
413
  const { filename } = req.body;
561
414
  const file = req.file;
@@ -565,13 +418,10 @@ export class Frontend {
565
418
  return;
566
419
  }
567
420
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
568
- // Define the path where the plugin file will be saved
569
421
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
570
422
  try {
571
- // Move the uploaded file to the specified path
572
423
  await fs.rename(file.path, filePath);
573
424
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
574
- // Install the plugin package
575
425
  if (filename.endsWith('.tgz')) {
576
426
  await spawn.spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
577
427
  this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
@@ -590,7 +440,6 @@ export class Frontend {
590
440
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
591
441
  }
592
442
  });
593
- // Fallback for routing (must be the last route)
594
443
  this.expressApp.use((req, res) => {
595
444
  this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
596
445
  res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -599,15 +448,12 @@ export class Frontend {
599
448
  }
600
449
  async stop() {
601
450
  this.log.debug('Stopping the frontend...');
602
- // Remove listeners from the express app
603
451
  if (this.expressApp) {
604
452
  this.expressApp.removeAllListeners();
605
453
  this.expressApp = undefined;
606
454
  this.log.debug('Frontend app closed successfully');
607
455
  }
608
- // Close the WebSocket server
609
456
  if (this.webSocketServer) {
610
- // Close all active connections
611
457
  this.webSocketServer.clients.forEach((client) => {
612
458
  if (client.readyState === WebSocket.OPEN) {
613
459
  client.close();
@@ -627,7 +473,6 @@ export class Frontend {
627
473
  this.webSocketServer.removeAllListeners();
628
474
  this.webSocketServer = undefined;
629
475
  }
630
- // Close the http server
631
476
  if (this.httpServer) {
632
477
  await withTimeout(new Promise((resolve) => {
633
478
  this.httpServer?.close((error) => {
@@ -644,7 +489,6 @@ export class Frontend {
644
489
  this.httpServer = undefined;
645
490
  this.log.debug('Frontend http server closed successfully');
646
491
  }
647
- // Close the https server
648
492
  if (this.httpsServer) {
649
493
  await withTimeout(new Promise((resolve) => {
650
494
  this.httpsServer?.close((error) => {
@@ -663,7 +507,6 @@ export class Frontend {
663
507
  }
664
508
  this.log.debug('Frontend stopped successfully');
665
509
  }
666
- // Function to format bytes to KB, MB, or GB
667
510
  formatMemoryUsage = (bytes) => {
668
511
  if (bytes >= 1024 ** 3) {
669
512
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -675,7 +518,6 @@ export class Frontend {
675
518
  return `${(bytes / 1024).toFixed(2)} KB`;
676
519
  }
677
520
  };
678
- // Function to format system uptime with only the most significant unit
679
521
  formatOsUpTime = (seconds) => {
680
522
  if (seconds >= 86400) {
681
523
  const days = Math.floor(seconds / 86400);
@@ -691,14 +533,8 @@ export class Frontend {
691
533
  }
692
534
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
693
535
  };
694
- /**
695
- * Retrieves the api settings data.
696
- *
697
- * @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
698
- */
699
536
  async getApiSettings() {
700
537
  const { lastCpuUsage } = await import('./cli.js');
701
- // Update the system information
702
538
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
703
539
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
704
540
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -707,7 +543,6 @@ export class Frontend {
707
543
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
708
544
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
709
545
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
710
- // Update the matterbridge information
711
546
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
712
547
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
713
548
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
@@ -726,11 +561,6 @@ export class Frontend {
726
561
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
727
562
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
728
563
  }
729
- /**
730
- * Retrieves the reachable attribute.
731
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
732
- * @returns {boolean} The reachable attribute.
733
- */
734
564
  getReachability(device) {
735
565
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
736
566
  return false;
@@ -740,11 +570,6 @@ export class Frontend {
740
570
  return true;
741
571
  return false;
742
572
  }
743
- /**
744
- * Retrieves the power source attribute.
745
- * @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice object.
746
- * @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
747
- */
748
573
  getPowerSource(endpoint) {
749
574
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
750
575
  return undefined;
@@ -760,20 +585,13 @@ export class Frontend {
760
585
  }
761
586
  return;
762
587
  };
763
- // Root endpoint
764
588
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
765
589
  return powerSource(endpoint);
766
- // Child endpoints
767
590
  for (const child of endpoint.getChildEndpoints()) {
768
591
  if (child.hasClusterServer(PowerSource.Cluster.id))
769
592
  return powerSource(child);
770
593
  }
771
594
  }
772
- /**
773
- * Retrieves the cluster text description from a given device.
774
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
775
- * @returns {string} The attributes description of the cluster servers in the device.
776
- */
777
595
  getClusterTextFromDevice(device) {
778
596
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
779
597
  return '';
@@ -814,19 +632,7 @@ export class Frontend {
814
632
  };
815
633
  let attributes = '';
816
634
  let supportedModes = [];
817
- /*
818
- Object.keys(device.behaviors.supported).forEach((clusterName) => {
819
- const clusterBehavior = device.behaviors.supported[lowercaseFirstLetter(clusterName)] as ClusterBehavior.Type | undefined;
820
- // console.log(`Device: ${device.deviceName} => Cluster: ${clusterName} Behavior: ${clusterBehavior?.id}`, clusterBehavior);
821
- if (clusterBehavior && clusterBehavior.cluster && clusterBehavior.cluster.attributes) {
822
- Object.entries(clusterBehavior.cluster.attributes).forEach(([attributeName, attribute]) => {
823
- // console.log(`${device.deviceName} => Cluster: ${clusterName} Attribute: ${attributeName}`, attribute);
824
- });
825
- }
826
- });
827
- */
828
635
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
829
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
830
636
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
831
637
  return;
832
638
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -918,13 +724,8 @@ export class Frontend {
918
724
  if (clusterName === 'userLabel' && attributeName === 'labelList')
919
725
  attributes += `${getUserLabel(device)} `;
920
726
  });
921
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
922
727
  return attributes.trimStart().trimEnd();
923
728
  }
924
- /**
925
- * Retrieves the base registered plugins sanitized for res.json().
926
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
927
- */
928
729
  getBaseRegisteredPlugins() {
929
730
  const baseRegisteredPlugins = [];
930
731
  for (const plugin of this.matterbridge.plugins) {
@@ -963,18 +764,11 @@ export class Frontend {
963
764
  }
964
765
  return baseRegisteredPlugins;
965
766
  }
966
- /**
967
- * Retrieves the devices from Matterbridge.
968
- * @param {string} [pluginName] - The name of the plugin to filter devices by.
969
- * @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices.
970
- */
971
767
  async getDevices(pluginName) {
972
768
  const devices = [];
973
769
  this.matterbridge.devices.forEach(async (device) => {
974
- // Filter by pluginName if provided
975
770
  if (pluginName && pluginName !== device.plugin)
976
771
  return;
977
- // Check if the device has the required properties
978
772
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
979
773
  return;
980
774
  const cluster = this.getClusterTextFromDevice(device);
@@ -994,37 +788,22 @@ export class Frontend {
994
788
  });
995
789
  return devices;
996
790
  }
997
- /**
998
- * Retrieves the clusters from a given plugin and endpoint number.
999
- *
1000
- * Response for /api/clusters
1001
- *
1002
- * @param {string} pluginName - The name of the plugin.
1003
- * @param {number} endpointNumber - The endpoint number.
1004
- * @returns {Promise<ApiClustersResponse | undefined>} A promise that resolves to the clusters or undefined if not found.
1005
- */
1006
791
  getClusters(pluginName, endpointNumber) {
1007
792
  const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
1008
793
  if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
1009
794
  this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1010
795
  return;
1011
796
  }
1012
- // this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
1013
- // Get the device types from the main endpoint
1014
797
  const deviceTypes = [];
1015
798
  const clusters = [];
1016
799
  endpoint.state.descriptor.deviceTypeList.forEach((d) => {
1017
800
  deviceTypes.push(d.deviceType);
1018
801
  });
1019
- // Get the clusters from the main endpoint
1020
802
  endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1021
803
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1022
804
  return;
1023
805
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1024
806
  return;
1025
- // console.log(
1026
- // `${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}`,
1027
- // );
1028
807
  clusters.push({
1029
808
  endpoint: endpoint.number.toString(),
1030
809
  id: 'main',
@@ -1037,18 +816,12 @@ export class Frontend {
1037
816
  attributeLocalValue: attributeValue,
1038
817
  });
1039
818
  });
1040
- // Get the child endpoints
1041
819
  const childEndpoints = endpoint.getChildEndpoints();
1042
- // if (childEndpoints.length === 0) {
1043
- // this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
1044
- // }
1045
820
  childEndpoints.forEach((childEndpoint) => {
1046
821
  if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
1047
822
  this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1048
823
  return;
1049
824
  }
1050
- // this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
1051
- // Get the device types of the child endpoint
1052
825
  const deviceTypes = [];
1053
826
  childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
1054
827
  deviceTypes.push(d.deviceType);
@@ -1058,12 +831,9 @@ export class Frontend {
1058
831
  return;
1059
832
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1060
833
  return;
1061
- // console.log(
1062
- // `${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}`,
1063
- // );
1064
834
  clusters.push({
1065
835
  endpoint: childEndpoint.number.toString(),
1066
- id: childEndpoint.maybeId ?? 'null', // Never happens
836
+ id: childEndpoint.maybeId ?? 'null',
1067
837
  deviceTypes,
1068
838
  clusterName: capitalizeFirstLetter(clusterName),
1069
839
  clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
@@ -1076,13 +846,6 @@ export class Frontend {
1076
846
  });
1077
847
  return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
1078
848
  }
1079
- /**
1080
- * Handles incoming websocket messages for the Matterbridge frontend.
1081
- *
1082
- * @param {WebSocket} client - The websocket client that sent the message.
1083
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1084
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1085
- */
1086
849
  async wsMessageHandler(client, message) {
1087
850
  let data;
1088
851
  try {
@@ -1129,26 +892,32 @@ export class Frontend {
1129
892
  this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
1130
893
  const packageName = data.params.packageName.replace(/@.*$/, '');
1131
894
  if (data.params.restart === false && packageName !== 'matterbridge') {
1132
- // The install comes from InstallPlugins
1133
- this.matterbridge.plugins.add(packageName).then((plugin) => {
895
+ this.matterbridge.plugins
896
+ .add(packageName)
897
+ .then((plugin) => {
1134
898
  if (plugin) {
1135
- // The plugin is not registered
1136
899
  this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
1137
- this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
900
+ this.matterbridge.plugins
901
+ .load(plugin, true, 'The plugin has been added', true)
902
+ .then(() => {
1138
903
  this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
1139
904
  this.wssSendRefreshRequired('plugins');
905
+ return;
906
+ })
907
+ .catch((_error) => {
1140
908
  });
1141
909
  }
1142
910
  else {
1143
- // The plugin is already registered
1144
911
  this.wssSendSnackbarMessage(`Restart required`, 0);
1145
912
  this.wssSendRefreshRequired('plugins');
1146
913
  this.wssSendRestartRequired();
1147
914
  }
915
+ return;
916
+ })
917
+ .catch((_error) => {
1148
918
  });
1149
919
  }
1150
920
  else {
1151
- // The package is matterbridge
1152
921
  if (this.matterbridge.restartMode !== '') {
1153
922
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1154
923
  this.matterbridge.shutdownProcess();
@@ -1158,6 +927,7 @@ export class Frontend {
1158
927
  this.wssSendRestartRequired();
1159
928
  }
1160
929
  }
930
+ return;
1161
931
  })
1162
932
  .catch((error) => {
1163
933
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error }));
@@ -1170,7 +940,6 @@ export class Frontend {
1170
940
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
1171
941
  return;
1172
942
  }
1173
- // The package is a plugin
1174
943
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
1175
944
  if (plugin) {
1176
945
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
@@ -1179,7 +948,6 @@ export class Frontend {
1179
948
  this.wssSendRefreshRequired('plugins');
1180
949
  this.wssSendRefreshRequired('devices');
1181
950
  }
1182
- // Uninstall the package
1183
951
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1184
952
  spawn
1185
953
  .spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
@@ -1187,6 +955,7 @@ export class Frontend {
1187
955
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
1188
956
  this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
1189
957
  this.wssSendSnackbarMessage(`Uninstalled package ${data.params.packageName}`, 5, 'success');
958
+ return;
1190
959
  })
1191
960
  .catch((error) => {
1192
961
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error }));
@@ -1210,10 +979,15 @@ export class Frontend {
1210
979
  if (plugin) {
1211
980
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: plugin.name }));
1212
981
  this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
1213
- this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
982
+ this.matterbridge.plugins
983
+ .load(plugin, true, 'The plugin has been added', true)
984
+ .then(() => {
1214
985
  this.wssSendRefreshRequired('plugins');
1215
986
  this.wssSendRefreshRequired('devices');
1216
987
  this.wssSendSnackbarMessage(`Started plugin ${data.params.pluginNameOrPath}`, 5, 'success');
988
+ return;
989
+ })
990
+ .catch((_error) => {
1217
991
  });
1218
992
  }
1219
993
  else {
@@ -1251,10 +1025,15 @@ export class Frontend {
1251
1025
  plugin.addedDevices = undefined;
1252
1026
  await this.matterbridge.plugins.enable(data.params.pluginName);
1253
1027
  this.wssSendSnackbarMessage(`Enabled plugin ${data.params.pluginName}`, 5, 'success');
1254
- this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
1028
+ this.matterbridge.plugins
1029
+ .load(plugin, true, 'The plugin has been enabled', true)
1030
+ .then(() => {
1255
1031
  this.wssSendRefreshRequired('plugins');
1256
1032
  this.wssSendRefreshRequired('devices');
1257
1033
  this.wssSendSnackbarMessage(`Started plugin ${data.params.pluginName}`, 5, 'success');
1034
+ return;
1035
+ })
1036
+ .catch((_error) => {
1258
1037
  });
1259
1038
  }
1260
1039
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1461,6 +1240,7 @@ export class Frontend {
1461
1240
  ?.onAction(data.params.action, data.params.value, data.params.id, data.params.formData)
1462
1241
  .then(() => {
1463
1242
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
1243
+ return;
1464
1244
  })
1465
1245
  .catch((error) => {
1466
1246
  this.log.error(`Error in plugin ${plugin.name} action ${data.params.action}: ${error}`);
@@ -1491,22 +1271,22 @@ export class Frontend {
1491
1271
  if (isValidString(data.params.value, 4)) {
1492
1272
  this.log.debug('Matterbridge logger level:', data.params.value);
1493
1273
  if (data.params.value === 'Debug') {
1494
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1274
+ await this.matterbridge.setLogLevel("debug");
1495
1275
  }
1496
1276
  else if (data.params.value === 'Info') {
1497
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1277
+ await this.matterbridge.setLogLevel("info");
1498
1278
  }
1499
1279
  else if (data.params.value === 'Notice') {
1500
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1280
+ await this.matterbridge.setLogLevel("notice");
1501
1281
  }
1502
1282
  else if (data.params.value === 'Warn') {
1503
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1283
+ await this.matterbridge.setLogLevel("warn");
1504
1284
  }
1505
1285
  else if (data.params.value === 'Error') {
1506
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1286
+ await this.matterbridge.setLogLevel("error");
1507
1287
  }
1508
1288
  else if (data.params.value === 'Fatal') {
1509
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1289
+ await this.matterbridge.setLogLevel("fatal");
1510
1290
  }
1511
1291
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1512
1292
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1517,7 +1297,6 @@ export class Frontend {
1517
1297
  this.log.debug('Matterbridge file log:', data.params.value);
1518
1298
  this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
1519
1299
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1520
- // Create the file logger for matterbridge
1521
1300
  if (data.params.value)
1522
1301
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1523
1302
  else
@@ -1682,19 +1461,15 @@ export class Frontend {
1682
1461
  return;
1683
1462
  }
1684
1463
  const config = plugin.configJson;
1685
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1686
1464
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1687
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1688
1465
  if (select === 'serial')
1689
1466
  this.log.info(`Selected device serial ${data.params.serial}`);
1690
1467
  if (select === 'name')
1691
1468
  this.log.info(`Selected device name ${data.params.name}`);
1692
1469
  if (config && select && (select === 'serial' || select === 'name')) {
1693
- // Remove postfix from the serial if it exists
1694
1470
  if (config.postfix) {
1695
1471
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1696
1472
  }
1697
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1698
1473
  if (isValidArray(config.whiteList, 1)) {
1699
1474
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1700
1475
  config.whiteList.push(data.params.serial);
@@ -1703,7 +1478,6 @@ export class Frontend {
1703
1478
  config.whiteList.push(data.params.name);
1704
1479
  }
1705
1480
  }
1706
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1707
1481
  if (isValidArray(config.blackList, 1)) {
1708
1482
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1709
1483
  config.blackList = config.blackList.filter((item) => item !== data.params.serial);
@@ -1733,9 +1507,7 @@ export class Frontend {
1733
1507
  return;
1734
1508
  }
1735
1509
  const config = plugin.configJson;
1736
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1737
1510
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1738
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1739
1511
  if (select === 'serial')
1740
1512
  this.log.info(`Unselected device serial ${data.params.serial}`);
1741
1513
  if (select === 'name')
@@ -1744,7 +1516,6 @@ export class Frontend {
1744
1516
  if (config.postfix) {
1745
1517
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1746
1518
  }
1747
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1748
1519
  if (isValidArray(config.whiteList, 1)) {
1749
1520
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1750
1521
  config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
@@ -1753,7 +1524,6 @@ export class Frontend {
1753
1524
  config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
1754
1525
  }
1755
1526
  }
1756
- // Add the serial to the blackList
1757
1527
  if (isValidArray(config.blackList)) {
1758
1528
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1759
1529
  config.blackList.push(data.params.serial);
@@ -1786,219 +1556,114 @@ export class Frontend {
1786
1556
  this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
1787
1557
  }
1788
1558
  }
1789
- /**
1790
- * Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1791
- *
1792
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1793
- * @param {string} time - The time string of the message
1794
- * @param {string} name - The logger name of the message
1795
- * @param {string} message - The content of the message.
1796
- *
1797
- * @remark
1798
- * The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
1799
- * It also replaces all occurrences of \" with " and angle-brackets with &lt; and &gt;.
1800
- * The function sends the message to all connected clients.
1801
- */
1802
1559
  wssSendMessage(level, time, name, message) {
1803
1560
  if (!level || !time || !name || !message)
1804
1561
  return;
1805
- // Remove ANSI escape codes from the message
1806
- // eslint-disable-next-line no-control-regex
1807
1562
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1808
- // Remove leading asterisks from the message
1809
1563
  message = message.replace(/^\*+/, '');
1810
- // Replace all occurrences of \t and \n
1811
1564
  message = message.replace(/[\t\n]/g, '');
1812
- // Remove non-printable characters
1813
- // eslint-disable-next-line no-control-regex
1814
1565
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1815
- // Replace all occurrences of \" with "
1816
1566
  message = message.replace(/\\"/g, '"');
1817
- // Replace all occurrences of angle-brackets with &lt; and &gt;"
1818
1567
  message = message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1819
- // Define the maximum allowed length for continuous characters without a space
1820
1568
  const maxContinuousLength = 100;
1821
1569
  const keepStartLength = 20;
1822
1570
  const keepEndLength = 20;
1823
- // Split the message into words
1824
1571
  message = message
1825
1572
  .split(' ')
1826
1573
  .map((word) => {
1827
- // If the word length exceeds the max continuous length, insert spaces and truncate
1828
1574
  if (word.length > maxContinuousLength) {
1829
1575
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1830
1576
  }
1831
1577
  return word;
1832
1578
  })
1833
1579
  .join(' ');
1834
- // Send the message to all connected clients
1835
1580
  this.webSocketServer?.clients.forEach((client) => {
1836
1581
  if (client.readyState === WebSocket.OPEN) {
1837
1582
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1838
1583
  }
1839
1584
  });
1840
1585
  }
1841
- /**
1842
- * Sends a need to refresh WebSocket message to all connected clients.
1843
- *
1844
- * @param {string} changed - The changed value. If null, the whole page will be refreshed.
1845
- * possible values:
1846
- * - 'matterbridgeLatestVersion'
1847
- * - 'matterbridgeAdvertise'
1848
- * - 'online'
1849
- * - 'offline'
1850
- * - 'reachability'
1851
- * - 'settings'
1852
- * - 'plugins'
1853
- * - 'pluginsRestart'
1854
- * - 'devices'
1855
- * - 'fabrics'
1856
- * - 'sessions'
1857
- */
1858
1586
  wssSendRefreshRequired(changed = null) {
1859
1587
  this.log.debug('Sending a refresh required message to all connected clients');
1860
- // Send the message to all connected clients
1861
1588
  this.webSocketServer?.clients.forEach((client) => {
1862
1589
  if (client.readyState === WebSocket.OPEN) {
1863
1590
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
1864
1591
  }
1865
1592
  });
1866
1593
  }
1867
- /**
1868
- * Sends a need to restart WebSocket message to all connected clients.
1869
- *
1870
- */
1871
1594
  wssSendRestartRequired(snackbar = true) {
1872
1595
  this.log.debug('Sending a restart required message to all connected clients');
1873
1596
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1874
1597
  if (snackbar === true)
1875
1598
  this.wssSendSnackbarMessage(`Restart required`, 0);
1876
- // Send the message to all connected clients
1877
1599
  this.webSocketServer?.clients.forEach((client) => {
1878
1600
  if (client.readyState === WebSocket.OPEN) {
1879
1601
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
1880
1602
  }
1881
1603
  });
1882
1604
  }
1883
- /**
1884
- * Sends a need to update WebSocket message to all connected clients.
1885
- *
1886
- */
1887
1605
  wssSendUpdateRequired() {
1888
1606
  this.log.debug('Sending an update required message to all connected clients');
1889
1607
  this.matterbridge.matterbridgeInformation.updateRequired = true;
1890
- // Send the message to all connected clients
1891
1608
  this.webSocketServer?.clients.forEach((client) => {
1892
1609
  if (client.readyState === WebSocket.OPEN) {
1893
1610
  client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
1894
1611
  }
1895
1612
  });
1896
1613
  }
1897
- /**
1898
- * Sends a cpu update message to all connected clients.
1899
- *
1900
- */
1901
1614
  wssSendCpuUpdate(cpuUsage) {
1902
1615
  if (hasParameter('debug'))
1903
1616
  this.log.debug('Sending a cpu update message to all connected clients');
1904
- // Send the message to all connected clients
1905
1617
  this.webSocketServer?.clients.forEach((client) => {
1906
1618
  if (client.readyState === WebSocket.OPEN) {
1907
1619
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
1908
1620
  }
1909
1621
  });
1910
1622
  }
1911
- /**
1912
- * Sends a memory update message to all connected clients.
1913
- *
1914
- */
1915
1623
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
1916
1624
  if (hasParameter('debug'))
1917
1625
  this.log.debug('Sending a memory update message to all connected clients');
1918
- // Send the message to all connected clients
1919
1626
  this.webSocketServer?.clients.forEach((client) => {
1920
1627
  if (client.readyState === WebSocket.OPEN) {
1921
1628
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
1922
1629
  }
1923
1630
  });
1924
1631
  }
1925
- /**
1926
- * Sends an uptime update message to all connected clients.
1927
- *
1928
- */
1929
1632
  wssSendUptimeUpdate(systemUptime, processUptime) {
1930
1633
  if (hasParameter('debug'))
1931
1634
  this.log.debug('Sending a uptime update message to all connected clients');
1932
- // Send the message to all connected clients
1933
1635
  this.webSocketServer?.clients.forEach((client) => {
1934
1636
  if (client.readyState === WebSocket.OPEN) {
1935
1637
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
1936
1638
  }
1937
1639
  });
1938
1640
  }
1939
- /**
1940
- * Sends an open snackbar message to all connected clients.
1941
- * @param {string} message - The message to send.
1942
- * @param {number} timeout - The timeout in seconds for the snackbar message.
1943
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
1944
- *
1945
- */
1946
1641
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
1947
1642
  this.log.debug('Sending a snackbar message to all connected clients');
1948
- // Send the message to all connected clients
1949
1643
  this.webSocketServer?.clients.forEach((client) => {
1950
1644
  if (client.readyState === WebSocket.OPEN) {
1951
1645
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
1952
1646
  }
1953
1647
  });
1954
1648
  }
1955
- /**
1956
- * Sends a close snackbar message to all connected clients.
1957
- * @param {string} message - The message to send.
1958
- *
1959
- */
1960
1649
  wssSendCloseSnackbarMessage(message) {
1961
1650
  this.log.debug('Sending a close snackbar message to all connected clients');
1962
- // Send the message to all connected clients
1963
1651
  this.webSocketServer?.clients.forEach((client) => {
1964
1652
  if (client.readyState === WebSocket.OPEN) {
1965
1653
  client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
1966
1654
  }
1967
1655
  });
1968
1656
  }
1969
- /**
1970
- * Sends an attribute update message to all connected WebSocket clients.
1971
- *
1972
- * @param {string | undefined} plugin - The name of the plugin.
1973
- * @param {string | undefined} serialNumber - The serial number of the device.
1974
- * @param {string | undefined} uniqueId - The unique identifier of the device.
1975
- * @param {string} cluster - The cluster name where the attribute belongs.
1976
- * @param {string} attribute - The name of the attribute that changed.
1977
- * @param {number | string | boolean} value - The new value of the attribute.
1978
- *
1979
- * @remarks
1980
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
1981
- * with the updated attribute information.
1982
- */
1983
1657
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
1984
1658
  this.log.debug('Sending an attribute update message to all connected clients');
1985
- // Send the message to all connected clients
1986
1659
  this.webSocketServer?.clients.forEach((client) => {
1987
1660
  if (client.readyState === WebSocket.OPEN) {
1988
1661
  client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
1989
1662
  }
1990
1663
  });
1991
1664
  }
1992
- /**
1993
- * Sends a message to all connected clients.
1994
- * @param {number} id - The message id.
1995
- * @param {string} method - The message method.
1996
- * @param {Record<string, string | number | boolean>} params - The message parameters.
1997
- *
1998
- */
1999
1665
  wssBroadcastMessage(id, method, params) {
2000
1666
  this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
2001
- // Send the message to all connected clients
2002
1667
  this.webSocketServer?.clients.forEach((client) => {
2003
1668
  if (client.readyState === WebSocket.OPEN) {
2004
1669
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -2006,4 +1671,3 @@ export class Frontend {
2006
1671
  });
2007
1672
  }
2008
1673
  }
2009
- //# sourceMappingURL=frontend.js.map