matterbridge 3.0.1 → 3.0.2-dev-20250508-7214e17

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 (150) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +1 -1
  3. package/dist/cli.js +2 -37
  4. package/dist/cluster/export.js +0 -2
  5. package/dist/defaultConfigSchema.js +0 -23
  6. package/dist/deviceManager.js +1 -94
  7. package/dist/frontend.js +15 -328
  8. package/dist/index.js +2 -28
  9. package/dist/logger/export.js +0 -1
  10. package/dist/matter/behaviors.js +0 -2
  11. package/dist/matter/clusters.js +0 -2
  12. package/dist/matter/devices.js +0 -2
  13. package/dist/matter/endpoints.js +0 -2
  14. package/dist/matter/export.js +0 -2
  15. package/dist/matter/types.js +0 -2
  16. package/dist/matterbridge.js +80 -747
  17. package/dist/matterbridgeAccessoryPlatform.js +0 -34
  18. package/dist/matterbridgeBehaviors.js +109 -48
  19. package/dist/matterbridgeDeviceTypes.js +12 -431
  20. package/dist/matterbridgeDynamicPlatform.js +0 -34
  21. package/dist/matterbridgeEndpoint.js +13 -814
  22. package/dist/matterbridgeEndpointHelpers.js +29 -148
  23. package/dist/matterbridgePlatform.js +7 -225
  24. package/dist/matterbridgeTypes.js +0 -24
  25. package/dist/pluginManager.js +3 -264
  26. package/dist/roboticVacuumCleaner.js +87 -0
  27. package/dist/shelly.js +6 -146
  28. package/dist/storage/export.js +0 -1
  29. package/dist/update.js +1 -53
  30. package/dist/utils/colorUtils.js +2 -205
  31. package/dist/utils/{parameter.js → commandLine.js} +1 -54
  32. package/dist/utils/copyDirectory.js +1 -37
  33. package/dist/utils/createZip.js +2 -42
  34. package/dist/utils/deepCopy.js +8 -43
  35. package/dist/utils/deepEqual.js +7 -69
  36. package/dist/utils/export.js +2 -2
  37. package/dist/utils/hex.js +27 -0
  38. package/dist/utils/isvalid.js +3 -93
  39. package/dist/utils/network.js +7 -78
  40. package/dist/utils/wait.js +5 -48
  41. package/npm-shrinkwrap.json +2 -2
  42. package/package.json +1 -2
  43. package/dist/cli.d.ts +0 -29
  44. package/dist/cli.d.ts.map +0 -1
  45. package/dist/cli.js.map +0 -1
  46. package/dist/cluster/export.d.ts +0 -2
  47. package/dist/cluster/export.d.ts.map +0 -1
  48. package/dist/cluster/export.js.map +0 -1
  49. package/dist/defaultConfigSchema.d.ts +0 -27
  50. package/dist/defaultConfigSchema.d.ts.map +0 -1
  51. package/dist/defaultConfigSchema.js.map +0 -1
  52. package/dist/deviceManager.d.ts +0 -114
  53. package/dist/deviceManager.d.ts.map +0 -1
  54. package/dist/deviceManager.js.map +0 -1
  55. package/dist/frontend.d.ts +0 -240
  56. package/dist/frontend.d.ts.map +0 -1
  57. package/dist/frontend.js.map +0 -1
  58. package/dist/index.d.ts +0 -35
  59. package/dist/index.d.ts.map +0 -1
  60. package/dist/index.js.map +0 -1
  61. package/dist/logger/export.d.ts +0 -2
  62. package/dist/logger/export.d.ts.map +0 -1
  63. package/dist/logger/export.js.map +0 -1
  64. package/dist/matter/behaviors.d.ts +0 -2
  65. package/dist/matter/behaviors.d.ts.map +0 -1
  66. package/dist/matter/behaviors.js.map +0 -1
  67. package/dist/matter/clusters.d.ts +0 -2
  68. package/dist/matter/clusters.d.ts.map +0 -1
  69. package/dist/matter/clusters.js.map +0 -1
  70. package/dist/matter/devices.d.ts +0 -2
  71. package/dist/matter/devices.d.ts.map +0 -1
  72. package/dist/matter/devices.js.map +0 -1
  73. package/dist/matter/endpoints.d.ts +0 -2
  74. package/dist/matter/endpoints.d.ts.map +0 -1
  75. package/dist/matter/endpoints.js.map +0 -1
  76. package/dist/matter/export.d.ts +0 -5
  77. package/dist/matter/export.d.ts.map +0 -1
  78. package/dist/matter/export.js.map +0 -1
  79. package/dist/matter/types.d.ts +0 -3
  80. package/dist/matter/types.d.ts.map +0 -1
  81. package/dist/matter/types.js.map +0 -1
  82. package/dist/matterbridge.d.ts +0 -433
  83. package/dist/matterbridge.d.ts.map +0 -1
  84. package/dist/matterbridge.js.map +0 -1
  85. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -40
  86. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  87. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  88. package/dist/matterbridgeBehaviors.d.ts +0 -1166
  89. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  90. package/dist/matterbridgeBehaviors.js.map +0 -1
  91. package/dist/matterbridgeDeviceTypes.d.ts +0 -494
  92. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  93. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  94. package/dist/matterbridgeDynamicPlatform.d.ts +0 -40
  95. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  96. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  97. package/dist/matterbridgeEndpoint.d.ts +0 -956
  98. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  99. package/dist/matterbridgeEndpoint.js.map +0 -1
  100. package/dist/matterbridgeEndpointHelpers.d.ts +0 -2706
  101. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  102. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  103. package/dist/matterbridgePlatform.d.ts +0 -294
  104. package/dist/matterbridgePlatform.d.ts.map +0 -1
  105. package/dist/matterbridgePlatform.js.map +0 -1
  106. package/dist/matterbridgeTypes.d.ts +0 -187
  107. package/dist/matterbridgeTypes.d.ts.map +0 -1
  108. package/dist/matterbridgeTypes.js.map +0 -1
  109. package/dist/pluginManager.d.ts +0 -273
  110. package/dist/pluginManager.d.ts.map +0 -1
  111. package/dist/pluginManager.js.map +0 -1
  112. package/dist/shelly.d.ts +0 -92
  113. package/dist/shelly.d.ts.map +0 -1
  114. package/dist/shelly.js.map +0 -1
  115. package/dist/storage/export.d.ts +0 -2
  116. package/dist/storage/export.d.ts.map +0 -1
  117. package/dist/storage/export.js.map +0 -1
  118. package/dist/update.d.ts +0 -32
  119. package/dist/update.d.ts.map +0 -1
  120. package/dist/update.js.map +0 -1
  121. package/dist/utils/colorUtils.d.ts +0 -61
  122. package/dist/utils/colorUtils.d.ts.map +0 -1
  123. package/dist/utils/colorUtils.js.map +0 -1
  124. package/dist/utils/copyDirectory.d.ts +0 -32
  125. package/dist/utils/copyDirectory.d.ts.map +0 -1
  126. package/dist/utils/copyDirectory.js.map +0 -1
  127. package/dist/utils/createZip.d.ts +0 -38
  128. package/dist/utils/createZip.d.ts.map +0 -1
  129. package/dist/utils/createZip.js.map +0 -1
  130. package/dist/utils/deepCopy.d.ts +0 -31
  131. package/dist/utils/deepCopy.d.ts.map +0 -1
  132. package/dist/utils/deepCopy.js.map +0 -1
  133. package/dist/utils/deepEqual.d.ts +0 -53
  134. package/dist/utils/deepEqual.d.ts.map +0 -1
  135. package/dist/utils/deepEqual.js.map +0 -1
  136. package/dist/utils/export.d.ts +0 -10
  137. package/dist/utils/export.d.ts.map +0 -1
  138. package/dist/utils/export.js.map +0 -1
  139. package/dist/utils/isvalid.d.ts +0 -95
  140. package/dist/utils/isvalid.d.ts.map +0 -1
  141. package/dist/utils/isvalid.js.map +0 -1
  142. package/dist/utils/network.d.ts +0 -69
  143. package/dist/utils/network.d.ts.map +0 -1
  144. package/dist/utils/network.js.map +0 -1
  145. package/dist/utils/parameter.d.ts +0 -58
  146. package/dist/utils/parameter.d.ts.map +0 -1
  147. package/dist/utils/parameter.js.map +0 -1
  148. package/dist/utils/wait.d.ts +0 -43
  149. package/dist/utils/wait.d.ts.map +0 -1
  150. package/dist/utils/wait.js.map +0 -1
package/dist/frontend.js CHANGED
@@ -1,111 +1,28 @@
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
1
  import { EndpointServer, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
25
- // Node modules
26
2
  import { createServer } from 'node:http';
27
3
  import https from 'node:https';
28
4
  import os from 'node:os';
29
5
  import path from 'node:path';
30
6
  import { promises as fs } from 'node:fs';
31
- // Third-party modules
32
7
  import express from 'express';
33
8
  import WebSocket, { WebSocketServer } from 'ws';
34
9
  import multer from 'multer';
35
- // AnsiLogger module
36
10
  import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from './logger/export.js';
37
- // Matterbridge
38
11
  import { createZip, deepCopy, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean } from './utils/export.js';
39
12
  import { plg } from './matterbridgeTypes.js';
40
13
  import { hasParameter } from './utils/export.js';
41
14
  import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
42
- /**
43
- * Websocket message ID for logging.
44
- * @constant {number}
45
- */
46
15
  export const WS_ID_LOG = 0;
47
- /**
48
- * Websocket message ID indicating a refresh is needed.
49
- * @constant {number}
50
- */
51
16
  export const WS_ID_REFRESH_NEEDED = 1;
52
- /**
53
- * Websocket message ID indicating a restart is needed.
54
- * @constant {number}
55
- */
56
17
  export const WS_ID_RESTART_NEEDED = 2;
57
- /**
58
- * Websocket message ID indicating a cpu update.
59
- * @constant {number}
60
- */
61
18
  export const WS_ID_CPU_UPDATE = 3;
62
- /**
63
- * Websocket message ID indicating a memory update.
64
- * @constant {number}
65
- */
66
19
  export const WS_ID_MEMORY_UPDATE = 4;
67
- /**
68
- * Websocket message ID indicating an uptime update.
69
- * @constant {number}
70
- */
71
20
  export const WS_ID_UPTIME_UPDATE = 5;
72
- /**
73
- * Websocket message ID indicating a snackbar message.
74
- * @constant {number}
75
- */
76
21
  export const WS_ID_SNACKBAR = 6;
77
- /**
78
- * Websocket message ID indicating matterbridge has un update available.
79
- * @constant {number}
80
- */
81
22
  export const WS_ID_UPDATE_NEEDED = 7;
82
- /**
83
- * Websocket message ID indicating a state update.
84
- * @constant {number}
85
- */
86
23
  export const WS_ID_STATEUPDATE = 8;
87
- /**
88
- * Websocket message ID indicating to close a permanent snackbar message.
89
- * @constant {number}
90
- */
91
24
  export const WS_ID_CLOSE_SNACKBAR = 9;
92
- /**
93
- * Websocket message ID indicating a shelly system update.
94
- * check:
95
- * curl -k http://127.0.0.1:8101/api/updates/sys/check
96
- * perform:
97
- * curl -k http://127.0.0.1:8101/api/updates/sys/perform
98
- * @constant {number}
99
- */
100
25
  export const WS_ID_SHELLY_SYS_UPDATE = 100;
101
- /**
102
- * Websocket message ID indicating a shelly main update.
103
- * check:
104
- * curl -k http://127.0.0.1:8101/api/updates/main/check
105
- * perform:
106
- * curl -k http://127.0.0.1:8101/api/updates/main/perform
107
- * @constant {number}
108
- */
109
26
  export const WS_ID_SHELLY_MAIN_UPDATE = 101;
110
27
  export class Frontend {
111
28
  matterbridge;
@@ -123,7 +40,7 @@ export class Frontend {
123
40
  memoryTimeout;
124
41
  constructor(matterbridge) {
125
42
  this.matterbridge = matterbridge;
126
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
43
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
127
44
  }
128
45
  set logLevel(logLevel) {
129
46
  this.log.logLevel = logLevel;
@@ -131,43 +48,13 @@ export class Frontend {
131
48
  async start(port = 8283) {
132
49
  this.port = port;
133
50
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
134
- // Initialize multer with the upload directory
135
51
  const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
136
52
  await fs.mkdir(uploadDir, { recursive: true });
137
53
  const upload = multer({ dest: uploadDir });
138
- // Create the express app that serves the frontend
139
54
  this.expressApp = express();
140
- // Inject logging/debug wrapper for route/middleware registration
141
- /*
142
- const methods = ['get', 'post', 'put', 'delete', 'use'];
143
- for (const method of methods) {
144
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
145
- const original = (this.expressApp as any)[method].bind(this.expressApp);
146
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
147
- (this.expressApp as any)[method] = (path: any, ...rest: any) => {
148
- try {
149
- console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
150
- return original(path, ...rest);
151
- } catch (err) {
152
- console.error(`[ERROR] Failed to register route: ${path}`);
153
- throw err;
154
- }
155
- };
156
- }
157
- */
158
- // Log all requests to the server for debugging
159
- /*
160
- this.expressApp.use((req, res, next) => {
161
- this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
162
- next();
163
- });
164
- */
165
- // Serve static files from '/static' endpoint
166
55
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
167
56
  if (!hasParameter('ssl')) {
168
- // Create an HTTP server and attach the express app
169
57
  this.httpServer = createServer(this.expressApp);
170
- // Listen on the specified port
171
58
  if (hasParameter('ingress')) {
172
59
  this.httpServer.listen(this.port, '0.0.0.0', () => {
173
60
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -181,7 +68,6 @@ export class Frontend {
181
68
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
182
69
  });
183
70
  }
184
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
185
71
  this.httpServer.on('error', (error) => {
186
72
  this.log.error(`Frontend http server error listening on ${this.port}`);
187
73
  switch (error.code) {
@@ -197,7 +83,6 @@ export class Frontend {
197
83
  });
198
84
  }
199
85
  else {
200
- // Load the SSL certificate, the private key and optionally the CA certificate
201
86
  let cert;
202
87
  try {
203
88
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
@@ -225,9 +110,7 @@ export class Frontend {
225
110
  this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
226
111
  }
227
112
  const serverOptions = { cert, key, ca };
228
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
229
113
  this.httpsServer = https.createServer(serverOptions, this.expressApp);
230
- // Listen on the specified port
231
114
  if (hasParameter('ingress')) {
232
115
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
233
116
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -241,7 +124,6 @@ export class Frontend {
241
124
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
242
125
  });
243
126
  }
244
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
245
127
  this.httpsServer.on('error', (error) => {
246
128
  this.log.error(`Frontend https server error listening on ${this.port}`);
247
129
  switch (error.code) {
@@ -258,18 +140,16 @@ export class Frontend {
258
140
  }
259
141
  if (this.initializeError)
260
142
  return;
261
- // Create a WebSocket server and attach it to the http or https server
262
143
  const wssPort = this.port;
263
144
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
264
145
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
265
146
  this.webSocketServer.on('connection', (ws, request) => {
266
147
  const clientIp = request.socket.remoteAddress;
267
- // Set the global logger callback for the WebSocketServer
268
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
269
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
270
- callbackLogLevel = "info" /* LogLevel.INFO */;
271
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
272
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
148
+ let callbackLogLevel = "notice";
149
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
150
+ callbackLogLevel = "info";
151
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
152
+ callbackLogLevel = "debug";
273
153
  AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
274
154
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
275
155
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -303,7 +183,6 @@ export class Frontend {
303
183
  this.webSocketServer.on('error', (ws, error) => {
304
184
  this.log.error(`WebSocketServer error: ${error}`);
305
185
  });
306
- // Subscribe to cli events
307
186
  const { cliEmitter } = await import('./cli.js');
308
187
  cliEmitter.removeAllListeners();
309
188
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
@@ -315,7 +194,6 @@ export class Frontend {
315
194
  cliEmitter.on('cpu', (cpuUsage) => {
316
195
  this.wssSendCpuUpdate(cpuUsage);
317
196
  });
318
- // Endpoint to validate login code
319
197
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
320
198
  const { password } = req.body;
321
199
  this.log.debug('The frontend sent /api/login', password);
@@ -334,27 +212,23 @@ export class Frontend {
334
212
  this.log.warn('/api/login error wrong password');
335
213
  res.json({ valid: false });
336
214
  }
337
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
338
215
  }
339
216
  catch (error) {
340
217
  this.log.error('/api/login error getting password');
341
218
  res.json({ valid: false });
342
219
  }
343
220
  });
344
- // Endpoint to provide health check for docker
345
221
  this.expressApp.get('/health', (req, res) => {
346
222
  this.log.debug('Express received /health');
347
223
  const healthStatus = {
348
- status: 'ok', // Indicate service is healthy
349
- uptime: process.uptime(), // Server uptime in seconds
350
- timestamp: new Date().toISOString(), // Current timestamp
224
+ status: 'ok',
225
+ uptime: process.uptime(),
226
+ timestamp: new Date().toISOString(),
351
227
  };
352
228
  res.status(200).json(healthStatus);
353
229
  });
354
- // Endpoint to provide memory usage details
355
230
  this.expressApp.get('/memory', async (req, res) => {
356
231
  this.log.debug('Express received /memory');
357
- // Memory usage from process
358
232
  const memoryUsageRaw = process.memoryUsage();
359
233
  const memoryUsage = {
360
234
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -363,13 +237,10 @@ export class Frontend {
363
237
  external: this.formatMemoryUsage(memoryUsageRaw.external),
364
238
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
365
239
  };
366
- // V8 heap statistics
367
240
  const { default: v8 } = await import('node:v8');
368
241
  const heapStatsRaw = v8.getHeapStatistics();
369
242
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
370
- // Format heapStats
371
243
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
372
- // Format heapSpaces
373
244
  const heapSpaces = heapSpacesRaw.map((space) => ({
374
245
  ...space,
375
246
  space_size: this.formatMemoryUsage(space.space_size),
@@ -387,23 +258,19 @@ export class Frontend {
387
258
  };
388
259
  res.status(200).json(memoryReport);
389
260
  });
390
- // Endpoint to provide settings
391
261
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
392
262
  this.log.debug('The frontend sent /api/settings');
393
263
  res.json(await this.getApiSettings());
394
264
  });
395
- // Endpoint to provide plugins
396
265
  this.expressApp.get('/api/plugins', async (req, res) => {
397
266
  this.log.debug('The frontend sent /api/plugins');
398
267
  res.json(this.getBaseRegisteredPlugins());
399
268
  });
400
- // Endpoint to provide devices
401
269
  this.expressApp.get('/api/devices', async (req, res) => {
402
270
  this.log.debug('The frontend sent /api/devices');
403
271
  const devices = await this.getDevices();
404
272
  res.json(devices);
405
273
  });
406
- // Endpoint to view the matterbridge log
407
274
  this.expressApp.get('/api/view-mblog', async (req, res) => {
408
275
  this.log.debug('The frontend sent /api/view-mblog');
409
276
  try {
@@ -416,7 +283,6 @@ export class Frontend {
416
283
  res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
417
284
  }
418
285
  });
419
- // Endpoint to view the matter.js log
420
286
  this.expressApp.get('/api/view-mjlog', async (req, res) => {
421
287
  this.log.debug('The frontend sent /api/view-mjlog');
422
288
  try {
@@ -429,7 +295,6 @@ export class Frontend {
429
295
  res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
430
296
  }
431
297
  });
432
- // Endpoint to view the shelly log
433
298
  this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
434
299
  this.log.debug('The frontend sent /api/shellyviewsystemlog');
435
300
  try {
@@ -442,7 +307,6 @@ export class Frontend {
442
307
  res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
443
308
  }
444
309
  });
445
- // Endpoint to download the matterbridge log
446
310
  this.expressApp.get('/api/download-mblog', async (req, res) => {
447
311
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
448
312
  try {
@@ -455,7 +319,6 @@ export class Frontend {
455
319
  this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
456
320
  }
457
321
  res.type('text/plain');
458
- // res.download(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
459
322
  res.download(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
460
323
  if (error) {
461
324
  this.log.error(`Error downloading log file ${this.matterbridge.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
@@ -463,7 +326,6 @@ export class Frontend {
463
326
  }
464
327
  });
465
328
  });
466
- // Endpoint to download the matter log
467
329
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
468
330
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
469
331
  try {
@@ -483,7 +345,6 @@ export class Frontend {
483
345
  }
484
346
  });
485
347
  });
486
- // Endpoint to download the shelly log
487
348
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
488
349
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
489
350
  try {
@@ -503,7 +364,6 @@ export class Frontend {
503
364
  }
504
365
  });
505
366
  });
506
- // Endpoint to download the matterbridge storage directory
507
367
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
508
368
  this.log.debug('The frontend sent /api/download-mbstorage');
509
369
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
@@ -514,7 +374,6 @@ export class Frontend {
514
374
  }
515
375
  });
516
376
  });
517
- // Endpoint to download the matter storage file
518
377
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
519
378
  this.log.debug('The frontend sent /api/download-mjstorage');
520
379
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
@@ -525,7 +384,6 @@ export class Frontend {
525
384
  }
526
385
  });
527
386
  });
528
- // Endpoint to download the matterbridge plugin directory
529
387
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
530
388
  this.log.debug('The frontend sent /api/download-pluginstorage');
531
389
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
@@ -536,11 +394,9 @@ export class Frontend {
536
394
  }
537
395
  });
538
396
  });
539
- // Endpoint to download the matterbridge plugin config files
540
397
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
541
398
  this.log.debug('The frontend sent /api/download-pluginconfig');
542
399
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
543
- // await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, 'certs', '*.*')), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
544
400
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
545
401
  if (error) {
546
402
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
@@ -548,7 +404,6 @@ export class Frontend {
548
404
  }
549
405
  });
550
406
  });
551
- // Endpoint to download the matterbridge plugin config files
552
407
  this.expressApp.get('/api/download-backup', async (req, res) => {
553
408
  this.log.debug('The frontend sent /api/download-backup');
554
409
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -558,7 +413,6 @@ export class Frontend {
558
413
  }
559
414
  });
560
415
  });
561
- // Endpoint to upload a package
562
416
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
563
417
  const { filename } = req.body;
564
418
  const file = req.file;
@@ -568,13 +422,10 @@ export class Frontend {
568
422
  return;
569
423
  }
570
424
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
571
- // Define the path where the plugin file will be saved
572
425
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
573
426
  try {
574
- // Move the uploaded file to the specified path
575
427
  await fs.rename(file.path, filePath);
576
428
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
577
- // Install the plugin package
578
429
  if (filename.endsWith('.tgz')) {
579
430
  await this.matterbridge.spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
580
431
  this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
@@ -593,7 +444,6 @@ export class Frontend {
593
444
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
594
445
  }
595
446
  });
596
- // Fallback for routing (must be the last route)
597
447
  this.expressApp.use((req, res) => {
598
448
  this.log.debug('The frontend sent:', req.url);
599
449
  res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -601,29 +451,24 @@ export class Frontend {
601
451
  this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
602
452
  }
603
453
  async stop() {
604
- // Close the http server
605
454
  if (this.httpServer) {
606
455
  this.httpServer.close();
607
456
  this.httpServer.removeAllListeners();
608
457
  this.httpServer = undefined;
609
458
  this.log.debug('Frontend http server closed successfully');
610
459
  }
611
- // Close the https server
612
460
  if (this.httpsServer) {
613
461
  this.httpsServer.close();
614
462
  this.httpsServer.removeAllListeners();
615
463
  this.httpsServer = undefined;
616
464
  this.log.debug('Frontend https server closed successfully');
617
465
  }
618
- // Remove listeners from the express app
619
466
  if (this.expressApp) {
620
467
  this.expressApp.removeAllListeners();
621
468
  this.expressApp = undefined;
622
469
  this.log.debug('Frontend app closed successfully');
623
470
  }
624
- // Close the WebSocket server
625
471
  if (this.webSocketServer) {
626
- // Close all active connections
627
472
  this.webSocketServer.clients.forEach((client) => {
628
473
  if (client.readyState === WebSocket.OPEN) {
629
474
  client.close();
@@ -640,7 +485,6 @@ export class Frontend {
640
485
  this.webSocketServer = undefined;
641
486
  }
642
487
  }
643
- // Function to format bytes to KB, MB, or GB
644
488
  formatMemoryUsage = (bytes) => {
645
489
  if (bytes >= 1024 ** 3) {
646
490
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -652,7 +496,6 @@ export class Frontend {
652
496
  return `${(bytes / 1024).toFixed(2)} KB`;
653
497
  }
654
498
  };
655
- // Function to format system uptime with only the most significant unit
656
499
  formatOsUpTime = (seconds) => {
657
500
  if (seconds >= 86400) {
658
501
  const days = Math.floor(seconds / 86400);
@@ -668,13 +511,8 @@ export class Frontend {
668
511
  }
669
512
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
670
513
  };
671
- /**
672
- * Retrieves the api settings data.
673
- * @returns {Promise<object>} A promise that resolve in the api settings object.
674
- */
675
514
  async getApiSettings() {
676
515
  const { lastCpuUsage } = await import('./cli.js');
677
- // Update the system information
678
516
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
679
517
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
680
518
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -683,7 +521,6 @@ export class Frontend {
683
521
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
684
522
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
685
523
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
686
- // Update the matterbridge information
687
524
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
688
525
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
689
526
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
@@ -702,11 +539,6 @@ export class Frontend {
702
539
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
703
540
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
704
541
  }
705
- /**
706
- * Retrieves the reachable attribute.
707
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
708
- * @returns {boolean} The reachable attribute.
709
- */
710
542
  getReachability(device) {
711
543
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
712
544
  return false;
@@ -731,20 +563,13 @@ export class Frontend {
731
563
  }
732
564
  return;
733
565
  };
734
- // Root endpoint
735
566
  if (device.hasClusterServer(PowerSource.Cluster.id))
736
567
  return powerSource(device);
737
- // Child endpoints
738
568
  for (const child of device.getChildEndpoints()) {
739
569
  if (child.hasClusterServer(PowerSource.Cluster.id))
740
570
  return powerSource(child);
741
571
  }
742
572
  }
743
- /**
744
- * Retrieves the cluster text description from a given device.
745
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
746
- * @returns {string} The attributes description of the cluster servers in the device.
747
- */
748
573
  getClusterTextFromDevice(device) {
749
574
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
750
575
  return '';
@@ -786,7 +611,6 @@ export class Frontend {
786
611
  let attributes = '';
787
612
  let supportedModes = [];
788
613
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
789
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
790
614
  if (typeof attributeValue === 'undefined')
791
615
  return;
792
616
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -878,13 +702,8 @@ export class Frontend {
878
702
  if (clusterName === 'userLabel' && attributeName === 'labelList')
879
703
  attributes += `${getUserLabel(device)} `;
880
704
  });
881
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
882
705
  return attributes.trimStart().trimEnd();
883
706
  }
884
- /**
885
- * Retrieves the base registered plugins sanitized for res.json().
886
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
887
- */
888
707
  getBaseRegisteredPlugins() {
889
708
  const baseRegisteredPlugins = [];
890
709
  for (const plugin of this.matterbridge.plugins) {
@@ -923,18 +742,11 @@ export class Frontend {
923
742
  }
924
743
  return baseRegisteredPlugins;
925
744
  }
926
- /**
927
- * Retrieves the devices from Matterbridge.
928
- * @param {string} [pluginName] - The name of the plugin to filter devices by.
929
- * @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices.
930
- */
931
745
  async getDevices(pluginName) {
932
746
  const devices = [];
933
747
  this.matterbridge.devices.forEach(async (device) => {
934
- // Filter by pluginName if provided
935
748
  if (pluginName && pluginName !== device.plugin)
936
749
  return;
937
- // Check if the device has the required properties
938
750
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
939
751
  return;
940
752
  const cluster = this.getClusterTextFromDevice(device);
@@ -954,13 +766,6 @@ export class Frontend {
954
766
  });
955
767
  return devices;
956
768
  }
957
- /**
958
- * Handles incoming websocket messages for the Matterbridge frontend.
959
- *
960
- * @param {WebSocket} client - The websocket client that sent the message.
961
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
962
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
963
- */
964
769
  async wsMessageHandler(client, message) {
965
770
  let data;
966
771
  try {
@@ -1007,10 +812,8 @@ export class Frontend {
1007
812
  this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
1008
813
  const packageName = data.params.packageName.replace(/@.*$/, '');
1009
814
  if (data.params.restart === false && packageName !== 'matterbridge') {
1010
- // The install comes from InstallPlugins
1011
815
  this.matterbridge.plugins.add(packageName).then((plugin) => {
1012
816
  if (plugin) {
1013
- // The plugin is not registered
1014
817
  this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
1015
818
  this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
1016
819
  this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
@@ -1018,7 +821,6 @@ export class Frontend {
1018
821
  });
1019
822
  }
1020
823
  else {
1021
- // The plugin is already registered
1022
824
  this.wssSendSnackbarMessage(`Restart required`, 0);
1023
825
  this.wssSendRefreshRequired('plugins');
1024
826
  this.wssSendRestartRequired();
@@ -1026,7 +828,6 @@ export class Frontend {
1026
828
  });
1027
829
  }
1028
830
  else {
1029
- // The package is matterbridge
1030
831
  if (this.matterbridge.restartMode !== '') {
1031
832
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1032
833
  this.matterbridge.shutdownProcess();
@@ -1048,7 +849,6 @@ export class Frontend {
1048
849
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
1049
850
  return;
1050
851
  }
1051
- // The package is a plugin
1052
852
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
1053
853
  if (plugin) {
1054
854
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
@@ -1057,7 +857,6 @@ export class Frontend {
1057
857
  this.wssSendRefreshRequired('plugins');
1058
858
  this.wssSendRefreshRequired('devices');
1059
859
  }
1060
- // Uninstall the package
1061
860
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1062
861
  this.matterbridge
1063
862
  .spawnCommand('npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
@@ -1334,7 +1133,6 @@ export class Frontend {
1334
1133
  });
1335
1134
  endpointServer.getChildEndpoints().forEach((childEndpoint) => {
1336
1135
  deviceTypes = [];
1337
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1338
1136
  const name = childEndpoint.endpoint?.id;
1339
1137
  const clusterServers = childEndpoint.getAllClusterServers();
1340
1138
  clusterServers.forEach((clusterServer) => {
@@ -1448,22 +1246,22 @@ export class Frontend {
1448
1246
  if (isValidString(data.params.value, 4)) {
1449
1247
  this.log.debug('Matterbridge logger level:', data.params.value);
1450
1248
  if (data.params.value === 'Debug') {
1451
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1249
+ await this.matterbridge.setLogLevel("debug");
1452
1250
  }
1453
1251
  else if (data.params.value === 'Info') {
1454
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1252
+ await this.matterbridge.setLogLevel("info");
1455
1253
  }
1456
1254
  else if (data.params.value === 'Notice') {
1457
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1255
+ await this.matterbridge.setLogLevel("notice");
1458
1256
  }
1459
1257
  else if (data.params.value === 'Warn') {
1460
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1258
+ await this.matterbridge.setLogLevel("warn");
1461
1259
  }
1462
1260
  else if (data.params.value === 'Error') {
1463
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1261
+ await this.matterbridge.setLogLevel("error");
1464
1262
  }
1465
1263
  else if (data.params.value === 'Fatal') {
1466
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1264
+ await this.matterbridge.setLogLevel("fatal");
1467
1265
  }
1468
1266
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1469
1267
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1474,7 +1272,6 @@ export class Frontend {
1474
1272
  this.log.debug('Matterbridge file log:', data.params.value);
1475
1273
  this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
1476
1274
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1477
- // Create the file logger for matterbridge
1478
1275
  if (data.params.value)
1479
1276
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1480
1277
  else
@@ -1630,19 +1427,15 @@ export class Frontend {
1630
1427
  return;
1631
1428
  }
1632
1429
  const config = plugin.configJson;
1633
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1634
1430
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1635
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1636
1431
  if (select === 'serial')
1637
1432
  this.log.info(`Selected device serial ${data.params.serial}`);
1638
1433
  if (select === 'name')
1639
1434
  this.log.info(`Selected device name ${data.params.name}`);
1640
1435
  if (config && select && (select === 'serial' || select === 'name')) {
1641
- // Remove postfix from the serial if it exists
1642
1436
  if (config.postfix) {
1643
1437
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1644
1438
  }
1645
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1646
1439
  if (isValidArray(config.whiteList, 1)) {
1647
1440
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1648
1441
  config.whiteList.push(data.params.serial);
@@ -1651,7 +1444,6 @@ export class Frontend {
1651
1444
  config.whiteList.push(data.params.name);
1652
1445
  }
1653
1446
  }
1654
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1655
1447
  if (isValidArray(config.blackList, 1)) {
1656
1448
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1657
1449
  config.blackList = config.blackList.filter((serial) => serial !== data.params.serial);
@@ -1681,9 +1473,7 @@ export class Frontend {
1681
1473
  return;
1682
1474
  }
1683
1475
  const config = plugin.configJson;
1684
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1685
1476
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1686
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1687
1477
  if (select === 'serial')
1688
1478
  this.log.info(`Unselected device serial ${data.params.serial}`);
1689
1479
  if (select === 'name')
@@ -1692,7 +1482,6 @@ export class Frontend {
1692
1482
  if (config.postfix) {
1693
1483
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1694
1484
  }
1695
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1696
1485
  if (isValidArray(config.whiteList, 1)) {
1697
1486
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1698
1487
  config.whiteList = config.whiteList.filter((serial) => serial !== data.params.serial);
@@ -1701,7 +1490,6 @@ export class Frontend {
1701
1490
  config.whiteList = config.whiteList.filter((name) => name !== data.params.name);
1702
1491
  }
1703
1492
  }
1704
- // Add the serial to the blackList
1705
1493
  if (isValidArray(config.blackList)) {
1706
1494
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1707
1495
  config.blackList.push(data.params.serial);
@@ -1734,214 +1522,114 @@ export class Frontend {
1734
1522
  this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
1735
1523
  }
1736
1524
  }
1737
- /**
1738
- * Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1739
- *
1740
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1741
- * @param {string} time - The time string of the message
1742
- * @param {string} name - The logger name of the message
1743
- * @param {string} message - The content of the message.
1744
- */
1745
1525
  wssSendMessage(level, time, name, message) {
1746
1526
  if (!level || !time || !name || !message)
1747
1527
  return;
1748
- // Remove ANSI escape codes from the message
1749
- // eslint-disable-next-line no-control-regex
1750
1528
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1751
- // Remove leading asterisks from the message
1752
1529
  message = message.replace(/^\*+/, '');
1753
- // Replace all occurrences of \t and \n
1754
1530
  message = message.replace(/[\t\n]/g, '');
1755
- // Remove non-printable characters
1756
- // eslint-disable-next-line no-control-regex
1757
1531
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1758
- // Replace all occurrences of \" with "
1759
1532
  message = message.replace(/\\"/g, '"');
1760
- // Replace all occurrences of angle-brackets with &lt; and &gt;"
1761
1533
  message = message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1762
- // Define the maximum allowed length for continuous characters without a space
1763
1534
  const maxContinuousLength = 100;
1764
1535
  const keepStartLength = 20;
1765
1536
  const keepEndLength = 20;
1766
- // Split the message into words
1767
1537
  message = message
1768
1538
  .split(' ')
1769
1539
  .map((word) => {
1770
- // If the word length exceeds the max continuous length, insert spaces and truncate
1771
1540
  if (word.length > maxContinuousLength) {
1772
1541
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1773
1542
  }
1774
1543
  return word;
1775
1544
  })
1776
1545
  .join(' ');
1777
- // Send the message to all connected clients
1778
1546
  this.webSocketServer?.clients.forEach((client) => {
1779
1547
  if (client.readyState === WebSocket.OPEN) {
1780
1548
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1781
1549
  }
1782
1550
  });
1783
1551
  }
1784
- /**
1785
- * Sends a need to refresh WebSocket message to all connected clients.
1786
- *
1787
- * @param {string} changed - The changed value. If null, the whole page will be refreshed.
1788
- * possible values:
1789
- * - 'matterbridgeLatestVersion'
1790
- * - 'matterbridgeAdvertise'
1791
- * - 'online'
1792
- * - 'offline'
1793
- * - 'reachability'
1794
- * - 'settings'
1795
- * - 'plugins'
1796
- * - 'pluginsRestart'
1797
- * - 'devices'
1798
- * - 'fabrics'
1799
- * - 'sessions'
1800
- */
1801
1552
  wssSendRefreshRequired(changed = null) {
1802
1553
  this.log.debug('Sending a refresh required message to all connected clients');
1803
- // Send the message to all connected clients
1804
1554
  this.webSocketServer?.clients.forEach((client) => {
1805
1555
  if (client.readyState === WebSocket.OPEN) {
1806
1556
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
1807
1557
  }
1808
1558
  });
1809
1559
  }
1810
- /**
1811
- * Sends a need to restart WebSocket message to all connected clients.
1812
- *
1813
- */
1814
1560
  wssSendRestartRequired(snackbar = true) {
1815
1561
  this.log.debug('Sending a restart required message to all connected clients');
1816
1562
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1817
1563
  if (snackbar === true)
1818
1564
  this.wssSendSnackbarMessage(`Restart required`, 0);
1819
- // Send the message to all connected clients
1820
1565
  this.webSocketServer?.clients.forEach((client) => {
1821
1566
  if (client.readyState === WebSocket.OPEN) {
1822
1567
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
1823
1568
  }
1824
1569
  });
1825
1570
  }
1826
- /**
1827
- * Sends a need to update WebSocket message to all connected clients.
1828
- *
1829
- */
1830
1571
  wssSendUpdateRequired() {
1831
1572
  this.log.debug('Sending an update required message to all connected clients');
1832
1573
  this.matterbridge.matterbridgeInformation.updateRequired = true;
1833
- // Send the message to all connected clients
1834
1574
  this.webSocketServer?.clients.forEach((client) => {
1835
1575
  if (client.readyState === WebSocket.OPEN) {
1836
1576
  client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
1837
1577
  }
1838
1578
  });
1839
1579
  }
1840
- /**
1841
- * Sends a cpu update message to all connected clients.
1842
- *
1843
- */
1844
1580
  wssSendCpuUpdate(cpuUsage) {
1845
1581
  if (hasParameter('debug'))
1846
1582
  this.log.debug('Sending a cpu update message to all connected clients');
1847
- // Send the message to all connected clients
1848
1583
  this.webSocketServer?.clients.forEach((client) => {
1849
1584
  if (client.readyState === WebSocket.OPEN) {
1850
1585
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
1851
1586
  }
1852
1587
  });
1853
1588
  }
1854
- /**
1855
- * Sends a memory update message to all connected clients.
1856
- *
1857
- */
1858
1589
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
1859
1590
  if (hasParameter('debug'))
1860
1591
  this.log.debug('Sending a memory update message to all connected clients');
1861
- // Send the message to all connected clients
1862
1592
  this.webSocketServer?.clients.forEach((client) => {
1863
1593
  if (client.readyState === WebSocket.OPEN) {
1864
1594
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
1865
1595
  }
1866
1596
  });
1867
1597
  }
1868
- /**
1869
- * Sends an uptime update message to all connected clients.
1870
- *
1871
- */
1872
1598
  wssSendUptimeUpdate(systemUptime, processUptime) {
1873
1599
  if (hasParameter('debug'))
1874
1600
  this.log.debug('Sending a uptime update message to all connected clients');
1875
- // Send the message to all connected clients
1876
1601
  this.webSocketServer?.clients.forEach((client) => {
1877
1602
  if (client.readyState === WebSocket.OPEN) {
1878
1603
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
1879
1604
  }
1880
1605
  });
1881
1606
  }
1882
- /**
1883
- * Sends an open snackbar message to all connected clients.
1884
- * @param {string} message - The message to send.
1885
- * @param {number} timeout - The timeout in seconds for the snackbar message.
1886
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
1887
- *
1888
- */
1889
1607
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
1890
1608
  this.log.debug('Sending a snackbar message to all connected clients');
1891
- // Send the message to all connected clients
1892
1609
  this.webSocketServer?.clients.forEach((client) => {
1893
1610
  if (client.readyState === WebSocket.OPEN) {
1894
1611
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
1895
1612
  }
1896
1613
  });
1897
1614
  }
1898
- /**
1899
- * Sends a close snackbar message to all connected clients.
1900
- * @param {string} message - The message to send.
1901
- *
1902
- */
1903
1615
  wssSendCloseSnackbarMessage(message) {
1904
1616
  this.log.debug('Sending a close snackbar message to all connected clients');
1905
- // Send the message to all connected clients
1906
1617
  this.webSocketServer?.clients.forEach((client) => {
1907
1618
  if (client.readyState === WebSocket.OPEN) {
1908
1619
  client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
1909
1620
  }
1910
1621
  });
1911
1622
  }
1912
- /**
1913
- * Sends an attribute update message to all connected WebSocket clients.
1914
- *
1915
- * @param {string | undefined} plugin - The name of the plugin.
1916
- * @param {string | undefined} serialNumber - The serial number of the device.
1917
- * @param {string | undefined} uniqueId - The unique identifier of the device.
1918
- * @param {string} cluster - The cluster name where the attribute belongs.
1919
- * @param {string} attribute - The name of the attribute that changed.
1920
- * @param {number | string | boolean} value - The new value of the attribute.
1921
- *
1922
- * @remarks
1923
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
1924
- * with the updated attribute information.
1925
- */
1926
1623
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
1927
1624
  this.log.debug('Sending an attribute update message to all connected clients');
1928
- // Send the message to all connected clients
1929
1625
  this.webSocketServer?.clients.forEach((client) => {
1930
1626
  if (client.readyState === WebSocket.OPEN) {
1931
1627
  client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
1932
1628
  }
1933
1629
  });
1934
1630
  }
1935
- /**
1936
- * Sends a message to all connected clients.
1937
- * @param {number} id - The message id.
1938
- * @param {string} method - The message method.
1939
- * @param {Record<string, string | number | boolean>} params - The message parameters.
1940
- *
1941
- */
1942
1631
  wssBroadcastMessage(id, method, params) {
1943
1632
  this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
1944
- // Send the message to all connected clients
1945
1633
  this.webSocketServer?.clients.forEach((client) => {
1946
1634
  if (client.readyState === WebSocket.OPEN) {
1947
1635
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -1949,4 +1637,3 @@ export class Frontend {
1949
1637
  });
1950
1638
  }
1951
1639
  }
1952
- //# sourceMappingURL=frontend.js.map