matterbridge 2.1.1-dev.2 → 2.1.1

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 (111) hide show
  1. package/CHANGELOG.md +3 -2
  2. package/dist/cli.d.ts +25 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +26 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/cluster/export.d.ts +2 -0
  7. package/dist/cluster/export.d.ts.map +1 -0
  8. package/dist/cluster/export.js +2 -0
  9. package/dist/cluster/export.js.map +1 -0
  10. package/dist/defaultConfigSchema.d.ts +27 -0
  11. package/dist/defaultConfigSchema.d.ts.map +1 -0
  12. package/dist/defaultConfigSchema.js +23 -0
  13. package/dist/defaultConfigSchema.js.map +1 -0
  14. package/dist/deviceManager.d.ts +46 -0
  15. package/dist/deviceManager.d.ts.map +1 -0
  16. package/dist/deviceManager.js +26 -1
  17. package/dist/deviceManager.js.map +1 -0
  18. package/dist/frontend.d.ts +109 -0
  19. package/dist/frontend.d.ts.map +1 -0
  20. package/dist/frontend.js +227 -22
  21. package/dist/frontend.js.map +1 -0
  22. package/dist/index.d.ts +35 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +28 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/logger/export.d.ts +2 -0
  27. package/dist/logger/export.d.ts.map +1 -0
  28. package/dist/logger/export.js +1 -0
  29. package/dist/logger/export.js.map +1 -0
  30. package/dist/matter/behaviors.d.ts +2 -0
  31. package/dist/matter/behaviors.d.ts.map +1 -0
  32. package/dist/matter/behaviors.js +2 -0
  33. package/dist/matter/behaviors.js.map +1 -0
  34. package/dist/matter/clusters.d.ts +2 -0
  35. package/dist/matter/clusters.d.ts.map +1 -0
  36. package/dist/matter/clusters.js +2 -0
  37. package/dist/matter/clusters.js.map +1 -0
  38. package/dist/matter/devices.d.ts +2 -0
  39. package/dist/matter/devices.d.ts.map +1 -0
  40. package/dist/matter/devices.js +2 -0
  41. package/dist/matter/devices.js.map +1 -0
  42. package/dist/matter/endpoints.d.ts +2 -0
  43. package/dist/matter/endpoints.d.ts.map +1 -0
  44. package/dist/matter/endpoints.js +2 -0
  45. package/dist/matter/endpoints.js.map +1 -0
  46. package/dist/matter/export.d.ts +4 -0
  47. package/dist/matter/export.d.ts.map +1 -0
  48. package/dist/matter/export.js +2 -0
  49. package/dist/matter/export.js.map +1 -0
  50. package/dist/matter/types.d.ts +3 -0
  51. package/dist/matter/types.d.ts.map +1 -0
  52. package/dist/matter/types.js +2 -0
  53. package/dist/matter/types.js.map +1 -0
  54. package/dist/matterbridge.d.ts +409 -0
  55. package/dist/matterbridge.d.ts.map +1 -0
  56. package/dist/matterbridge.js +754 -40
  57. package/dist/matterbridge.js.map +1 -0
  58. package/dist/matterbridgeAccessoryPlatform.d.ts +39 -0
  59. package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -0
  60. package/dist/matterbridgeAccessoryPlatform.js +33 -0
  61. package/dist/matterbridgeAccessoryPlatform.js.map +1 -0
  62. package/dist/matterbridgeBehaviors.d.ts +1056 -0
  63. package/dist/matterbridgeBehaviors.d.ts.map +1 -0
  64. package/dist/matterbridgeBehaviors.js +32 -1
  65. package/dist/matterbridgeBehaviors.js.map +1 -0
  66. package/dist/matterbridgeDeviceTypes.d.ts +177 -0
  67. package/dist/matterbridgeDeviceTypes.d.ts.map +1 -0
  68. package/dist/matterbridgeDeviceTypes.js +112 -11
  69. package/dist/matterbridgeDeviceTypes.js.map +1 -0
  70. package/dist/matterbridgeDynamicPlatform.d.ts +39 -0
  71. package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -0
  72. package/dist/matterbridgeDynamicPlatform.js +33 -0
  73. package/dist/matterbridgeDynamicPlatform.js.map +1 -0
  74. package/dist/matterbridgeEndpoint.d.ts +834 -0
  75. package/dist/matterbridgeEndpoint.d.ts.map +1 -0
  76. package/dist/matterbridgeEndpoint.js +690 -6
  77. package/dist/matterbridgeEndpoint.js.map +1 -0
  78. package/dist/matterbridgeEndpointHelpers.d.ts +2262 -0
  79. package/dist/matterbridgeEndpointHelpers.d.ts.map +1 -0
  80. package/dist/matterbridgeEndpointHelpers.js +96 -0
  81. package/dist/matterbridgeEndpointHelpers.js.map +1 -0
  82. package/dist/matterbridgePlatform.d.ts +164 -0
  83. package/dist/matterbridgePlatform.d.ts.map +1 -0
  84. package/dist/matterbridgePlatform.js +123 -3
  85. package/dist/matterbridgePlatform.js.map +1 -0
  86. package/dist/matterbridgeTypes.d.ts +165 -0
  87. package/dist/matterbridgeTypes.d.ts.map +1 -0
  88. package/dist/matterbridgeTypes.js +24 -0
  89. package/dist/matterbridgeTypes.js.map +1 -0
  90. package/dist/pluginManager.d.ts +238 -0
  91. package/dist/pluginManager.d.ts.map +1 -0
  92. package/dist/pluginManager.js +240 -3
  93. package/dist/pluginManager.js.map +1 -0
  94. package/dist/storage/export.d.ts +2 -0
  95. package/dist/storage/export.d.ts.map +1 -0
  96. package/dist/storage/export.js +1 -0
  97. package/dist/storage/export.js.map +1 -0
  98. package/dist/utils/colorUtils.d.ts +61 -0
  99. package/dist/utils/colorUtils.d.ts.map +1 -0
  100. package/dist/utils/colorUtils.js +205 -2
  101. package/dist/utils/colorUtils.js.map +1 -0
  102. package/dist/utils/export.d.ts +3 -0
  103. package/dist/utils/export.d.ts.map +1 -0
  104. package/dist/utils/export.js +1 -0
  105. package/dist/utils/export.js.map +1 -0
  106. package/dist/utils/utils.d.ts +221 -0
  107. package/dist/utils/utils.d.ts.map +1 -0
  108. package/dist/utils/utils.js +251 -7
  109. package/dist/utils/utils.js.map +1 -0
  110. package/npm-shrinkwrap.json +2 -2
  111. package/package.json +2 -1
package/dist/frontend.js CHANGED
@@ -1,4 +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.1
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
1
24
  import { EndpointServer, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat } from '@matter/main';
25
+ // Node modules
2
26
  import { createServer } from 'http';
3
27
  import https from 'https';
4
28
  import express from 'express';
@@ -6,13 +30,32 @@ import WebSocket, { WebSocketServer } from 'ws';
6
30
  import os from 'os';
7
31
  import path from 'path';
8
32
  import { promises as fs } from 'fs';
33
+ // AnsiLogger module
9
34
  import { AnsiLogger, CYAN, db, debugStringify, er, nf, rs, stringify, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';
35
+ // Matterbridge
10
36
  import { createZip, getIntParameter, hasParameter, isValidNumber, isValidObject, isValidString } from './utils/utils.js';
11
37
  import { plg } from './matterbridgeTypes.js';
12
38
  import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
39
+ /**
40
+ * Websocket message ID for logging.
41
+ * @constant {number}
42
+ */
13
43
  export const WS_ID_LOG = 0;
44
+ /**
45
+ * Websocket message ID indicating a refresh is needed.
46
+ * @constant {number}
47
+ */
14
48
  export const WS_ID_REFRESH_NEEDED = 1;
49
+ /**
50
+ * Websocket message ID indicating a restart is needed.
51
+ * @constant {number}
52
+ */
15
53
  export const WS_ID_RESTART_NEEDED = 2;
54
+ /**
55
+ * Initializes the frontend of Matterbridge.
56
+ *
57
+ * @param port The port number to run the frontend server on. Default is 8283.
58
+ */
16
59
  export class Frontend {
17
60
  matterbridge;
18
61
  log;
@@ -27,7 +70,7 @@ export class Frontend {
27
70
  memoryTimeout;
28
71
  constructor(matterbridge) {
29
72
  this.matterbridge = matterbridge;
30
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
73
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
31
74
  }
32
75
  set logLevel(logLevel) {
33
76
  this.log.logLevel = logLevel;
@@ -35,10 +78,21 @@ export class Frontend {
35
78
  async start(port = 8283) {
36
79
  this.port = port;
37
80
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
81
+ // Create the express app that serves the frontend
38
82
  this.expressApp = express();
83
+ // Log all requests to the server for debugging
84
+ /*
85
+ this.expressApp.use((req, res, next) => {
86
+ this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
87
+ next();
88
+ });
89
+ */
90
+ // Serve static files from '/static' endpoint
39
91
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
40
92
  if (!hasParameter('ssl')) {
93
+ // Create an HTTP server and attach the express app
41
94
  this.httpServer = createServer(this.expressApp);
95
+ // Listen on the specified port
42
96
  if (hasParameter('ingress')) {
43
97
  this.httpServer.listen(this.port, '0.0.0.0', () => {
44
98
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -52,6 +106,7 @@ export class Frontend {
52
106
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
53
107
  });
54
108
  }
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
110
  this.httpServer.on('error', (error) => {
56
111
  this.log.error(`Frontend http server error listening on ${this.port}`);
57
112
  switch (error.code) {
@@ -67,6 +122,7 @@ export class Frontend {
67
122
  });
68
123
  }
69
124
  else {
125
+ // Load the SSL certificate, the private key and optionally the CA certificate
70
126
  let cert;
71
127
  try {
72
128
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
@@ -94,7 +150,9 @@ export class Frontend {
94
150
  this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
95
151
  }
96
152
  const serverOptions = { cert, key, ca };
153
+ // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
97
154
  this.httpsServer = https.createServer(serverOptions, this.expressApp);
155
+ // Listen on the specified port
98
156
  if (hasParameter('ingress')) {
99
157
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
100
158
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -108,6 +166,7 @@ export class Frontend {
108
166
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
109
167
  });
110
168
  }
169
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
170
  this.httpsServer.on('error', (error) => {
112
171
  this.log.error(`Frontend https server error listening on ${this.port}`);
113
172
  switch (error.code) {
@@ -124,12 +183,13 @@ export class Frontend {
124
183
  }
125
184
  if (this.initializeError)
126
185
  return;
186
+ // Createe a WebSocket server and attach it to the http or https server
127
187
  const wssPort = this.port;
128
188
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
129
189
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
130
190
  this.webSocketServer.on('connection', (ws, request) => {
131
191
  const clientIp = request.socket.remoteAddress;
132
- AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug");
192
+ AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug" /* LogLevel.DEBUG */);
133
193
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
134
194
  ws.on('message', (message) => {
135
195
  this.wsMessageHandler(ws, message);
@@ -161,9 +221,11 @@ export class Frontend {
161
221
  this.webSocketServer.on('error', (ws, error) => {
162
222
  this.log.error(`WebSocketServer error: ${error}`);
163
223
  });
224
+ // Start the memory dump interval
164
225
  if (hasParameter('memorydump')) {
165
226
  this.startMemoryDump();
166
227
  }
228
+ // Endpoint to validate login code
167
229
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
168
230
  const { password } = req.body;
169
231
  this.log.debug('The frontend sent /api/login', password);
@@ -182,23 +244,27 @@ export class Frontend {
182
244
  this.log.warn('/api/login error wrong password');
183
245
  res.json({ valid: false });
184
246
  }
247
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
185
248
  }
186
249
  catch (error) {
187
250
  this.log.error('/api/login error getting password');
188
251
  res.json({ valid: false });
189
252
  }
190
253
  });
254
+ // Endpoint to provide health check
191
255
  this.expressApp.get('/health', (req, res) => {
192
256
  this.log.debug('Express received /health');
193
257
  const healthStatus = {
194
- status: 'ok',
195
- uptime: process.uptime(),
196
- timestamp: new Date().toISOString(),
258
+ status: 'ok', // Indicate service is healthy
259
+ uptime: process.uptime(), // Server uptime in seconds
260
+ timestamp: new Date().toISOString(), // Current timestamp
197
261
  };
198
262
  res.status(200).json(healthStatus);
199
263
  });
264
+ // Endpoint to provide memory usage details
200
265
  this.expressApp.get('/memory', async (req, res) => {
201
266
  this.log.debug('Express received /memory');
267
+ // Memory usage from process
202
268
  const memoryUsageRaw = process.memoryUsage();
203
269
  const memoryUsage = {
204
270
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -207,10 +273,13 @@ export class Frontend {
207
273
  external: this.formatMemoryUsage(memoryUsageRaw.external),
208
274
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
209
275
  };
276
+ // V8 heap statistics
210
277
  const { default: v8 } = await import('node:v8');
211
278
  const heapStatsRaw = v8.getHeapStatistics();
212
279
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
280
+ // Format heapStats
213
281
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
282
+ // Format heapSpaces
214
283
  const heapSpaces = heapSpacesRaw.map((space) => ({
215
284
  ...space,
216
285
  space_size: this.formatMemoryUsage(space.space_size),
@@ -220,6 +289,23 @@ export class Frontend {
220
289
  }));
221
290
  const { default: module } = await import('module');
222
291
  const loadedModules = module._cache ? Object.keys(module._cache).sort() : [];
292
+ /*
293
+ if (req.query.heapdump === 'true') {
294
+ const { default: heapdump } = await import('heapdump');
295
+ const filename = `heapdump-${Date.now()}.heapsnapshot`;
296
+
297
+ heapdump.writeSnapshot(filename, (err, dumpFilename) => {
298
+ if (err) {
299
+ this.log.error(`Heap dump error: ${err.message}`);
300
+ return res.status(500).json({ error: 'Heap dump failed', details: err.message });
301
+ }
302
+
303
+ this.log.info(`Heap dump written to ${dumpFilename}`);
304
+ return res.status(200).json({ ...memoryReport, heapdump: dumpFilename });
305
+ });
306
+ return; // Exit early since heapdump response is handled inside callback
307
+ }
308
+ */
223
309
  const memoryReport = {
224
310
  memoryUsage,
225
311
  heapStats,
@@ -228,6 +314,7 @@ export class Frontend {
228
314
  };
229
315
  res.status(200).json(memoryReport);
230
316
  });
317
+ // Endpoint to start advertising the server node
231
318
  this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
232
319
  const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
233
320
  if (pairingCodes) {
@@ -238,19 +325,24 @@ export class Frontend {
238
325
  res.status(500).json({ error: 'Failed to generate pairing codes' });
239
326
  }
240
327
  });
328
+ // Endpoint to provide settings
241
329
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
242
330
  this.log.debug('The frontend sent /api/settings');
243
331
  res.json(await this.getApiSettings());
244
332
  });
333
+ // Endpoint to provide plugins
245
334
  this.expressApp.get('/api/plugins', async (req, res) => {
246
335
  this.log.debug('The frontend sent /api/plugins');
247
336
  const response = this.getBaseRegisteredPlugins();
337
+ // this.log.debug('Response:', debugStringify(response));
248
338
  res.json(response);
249
339
  });
340
+ // Endpoint to provide devices
250
341
  this.expressApp.get('/api/devices', (req, res) => {
251
342
  this.log.debug('The frontend sent /api/devices');
252
343
  const devices = [];
253
344
  this.matterbridge.devices.forEach(async (device) => {
345
+ // Check if the device has the required properties
254
346
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
255
347
  return;
256
348
  const cluster = this.getClusterTextFromDevice(device);
@@ -266,8 +358,10 @@ export class Frontend {
266
358
  cluster: cluster,
267
359
  });
268
360
  });
361
+ // this.log.debug('Response:', debugStringify(data));
269
362
  res.json(devices);
270
363
  });
364
+ // Endpoint to provide the cluster servers of the devices
271
365
  this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
272
366
  const selectedPluginName = req.params.selectedPluginName;
273
367
  const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
@@ -340,6 +434,7 @@ export class Frontend {
340
434
  });
341
435
  res.json(data);
342
436
  });
437
+ // Endpoint to view the log
343
438
  this.expressApp.get('/api/view-log', async (req, res) => {
344
439
  this.log.debug('The frontend sent /api/log');
345
440
  try {
@@ -352,10 +447,12 @@ export class Frontend {
352
447
  res.status(500).send('Error reading log file');
353
448
  }
354
449
  });
450
+ // Endpoint to download the matterbridge log
355
451
  this.expressApp.get('/api/download-mblog', async (req, res) => {
356
452
  this.log.debug('The frontend sent /api/download-mblog');
357
453
  try {
358
454
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
455
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
359
456
  }
360
457
  catch (error) {
361
458
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -367,10 +464,12 @@ export class Frontend {
367
464
  }
368
465
  });
369
466
  });
467
+ // Endpoint to download the matter log
370
468
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
371
469
  this.log.debug('The frontend sent /api/download-mjlog');
372
470
  try {
373
471
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
472
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
374
473
  }
375
474
  catch (error) {
376
475
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -382,6 +481,7 @@ export class Frontend {
382
481
  }
383
482
  });
384
483
  });
484
+ // Endpoint to download the matter storage file
385
485
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
386
486
  this.log.debug('The frontend sent /api/download-mjstorage');
387
487
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
@@ -392,6 +492,7 @@ export class Frontend {
392
492
  }
393
493
  });
394
494
  });
495
+ // Endpoint to download the matterbridge storage directory
395
496
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
396
497
  this.log.debug('The frontend sent /api/download-mbstorage');
397
498
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
@@ -402,6 +503,7 @@ export class Frontend {
402
503
  }
403
504
  });
404
505
  });
506
+ // Endpoint to download the matterbridge plugin directory
405
507
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
406
508
  this.log.debug('The frontend sent /api/download-pluginstorage');
407
509
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
@@ -412,9 +514,11 @@ export class Frontend {
412
514
  }
413
515
  });
414
516
  });
517
+ // Endpoint to download the matterbridge plugin config files
415
518
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
416
519
  this.log.debug('The frontend sent /api/download-pluginconfig');
417
520
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
521
+ // 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')));
418
522
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
419
523
  if (error) {
420
524
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
@@ -422,6 +526,7 @@ export class Frontend {
422
526
  }
423
527
  });
424
528
  });
529
+ // Endpoint to download the matterbridge plugin config files
425
530
  this.expressApp.get('/api/download-backup', async (req, res) => {
426
531
  this.log.debug('The frontend sent /api/download-backup');
427
532
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -431,6 +536,7 @@ export class Frontend {
431
536
  }
432
537
  });
433
538
  });
539
+ // Endpoint to receive commands
434
540
  this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
435
541
  const command = req.params.command;
436
542
  let param = req.params.param;
@@ -440,13 +546,15 @@ export class Frontend {
440
546
  return;
441
547
  }
442
548
  this.log.debug(`Received frontend command: ${command}:${param}`);
549
+ // Handle the command setpassword from Settings
443
550
  if (command === 'setpassword') {
444
- const password = param.slice(1, -1);
551
+ const password = param.slice(1, -1); // Remove the first and last characters
445
552
  this.log.debug('setpassword', param, password);
446
553
  await this.matterbridge.nodeContext?.set('password', password);
447
554
  res.json({ message: 'Command received' });
448
555
  return;
449
556
  }
557
+ // Handle the command setbridgemode from Settings
450
558
  if (command === 'setbridgemode') {
451
559
  this.log.debug(`setbridgemode: ${param}`);
452
560
  this.wssSendRestartRequired();
@@ -454,6 +562,7 @@ export class Frontend {
454
562
  res.json({ message: 'Command received' });
455
563
  return;
456
564
  }
565
+ // Handle the command backup from Settings
457
566
  if (command === 'backup') {
458
567
  this.log.notice(`Prepairing the backup...`);
459
568
  await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
@@ -461,25 +570,26 @@ export class Frontend {
461
570
  res.json({ message: 'Command received' });
462
571
  return;
463
572
  }
573
+ // Handle the command setmbloglevel from Settings
464
574
  if (command === 'setmbloglevel') {
465
575
  this.log.debug('Matterbridge log level:', param);
466
576
  if (param === 'Debug') {
467
- this.log.logLevel = "debug";
577
+ this.log.logLevel = "debug" /* LogLevel.DEBUG */;
468
578
  }
469
579
  else if (param === 'Info') {
470
- this.log.logLevel = "info";
580
+ this.log.logLevel = "info" /* LogLevel.INFO */;
471
581
  }
472
582
  else if (param === 'Notice') {
473
- this.log.logLevel = "notice";
583
+ this.log.logLevel = "notice" /* LogLevel.NOTICE */;
474
584
  }
475
585
  else if (param === 'Warn') {
476
- this.log.logLevel = "warn";
586
+ this.log.logLevel = "warn" /* LogLevel.WARN */;
477
587
  }
478
588
  else if (param === 'Error') {
479
- this.log.logLevel = "error";
589
+ this.log.logLevel = "error" /* LogLevel.ERROR */;
480
590
  }
481
591
  else if (param === 'Fatal') {
482
- this.log.logLevel = "fatal";
592
+ this.log.logLevel = "fatal" /* LogLevel.FATAL */;
483
593
  }
484
594
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
485
595
  this.matterbridge.log.logLevel = this.log.logLevel;
@@ -489,12 +599,13 @@ export class Frontend {
489
599
  for (const plugin of this.matterbridge.plugins) {
490
600
  if (!plugin.platform || !plugin.platform.config)
491
601
  continue;
492
- plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug" : this.log.logLevel;
493
- await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug" : this.log.logLevel);
602
+ plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel;
603
+ await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel);
494
604
  }
495
605
  res.json({ message: 'Command received' });
496
606
  return;
497
607
  }
608
+ // Handle the command setmbloglevel from Settings
498
609
  if (command === 'setmjloglevel') {
499
610
  this.log.debug('Matter.js log level:', param);
500
611
  if (param === 'Debug') {
@@ -519,30 +630,34 @@ export class Frontend {
519
630
  res.json({ message: 'Command received' });
520
631
  return;
521
632
  }
633
+ // Handle the command setmdnsinterface from Settings
522
634
  if (command === 'setmdnsinterface') {
523
- param = param.slice(1, -1);
635
+ param = param.slice(1, -1); // Remove the first and last characters *mdns*
524
636
  this.matterbridge.matterbridgeInformation.mattermdnsinterface = param;
525
637
  this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
526
638
  await this.matterbridge.nodeContext?.set('mattermdnsinterface', param);
527
639
  res.json({ message: 'Command received' });
528
640
  return;
529
641
  }
642
+ // Handle the command setipv4address from Settings
530
643
  if (command === 'setipv4address') {
531
- param = param.slice(1, -1);
644
+ param = param.slice(1, -1); // Remove the first and last characters *ip*
532
645
  this.matterbridge.matterbridgeInformation.matteripv4address = param;
533
646
  this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
534
647
  await this.matterbridge.nodeContext?.set('matteripv4address', param);
535
648
  res.json({ message: 'Command received' });
536
649
  return;
537
650
  }
651
+ // Handle the command setipv6address from Settings
538
652
  if (command === 'setipv6address') {
539
- param = param.slice(1, -1);
653
+ param = param.slice(1, -1); // Remove the first and last characters *ip*
540
654
  this.matterbridge.matterbridgeInformation.matteripv6address = param;
541
655
  this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
542
656
  await this.matterbridge.nodeContext?.set('matteripv6address', param);
543
657
  res.json({ message: 'Command received' });
544
658
  return;
545
659
  }
660
+ // Handle the command setmatterport from Settings
546
661
  if (command === 'setmatterport') {
547
662
  const port = Math.min(Math.max(parseInt(param), 5540), 5560);
548
663
  this.matterbridge.matterbridgeInformation.matterPort = port;
@@ -551,6 +666,7 @@ export class Frontend {
551
666
  res.json({ message: 'Command received' });
552
667
  return;
553
668
  }
669
+ // Handle the command setmatterdiscriminator from Settings
554
670
  if (command === 'setmatterdiscriminator') {
555
671
  const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
556
672
  this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
@@ -559,6 +675,7 @@ export class Frontend {
559
675
  res.json({ message: 'Command received' });
560
676
  return;
561
677
  }
678
+ // Handle the command setmatterpasscode from Settings
562
679
  if (command === 'setmatterpasscode') {
563
680
  const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
564
681
  this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
@@ -567,17 +684,20 @@ export class Frontend {
567
684
  res.json({ message: 'Command received' });
568
685
  return;
569
686
  }
687
+ // Handle the command setmbloglevel from Settings
570
688
  if (command === 'setmblogfile') {
571
689
  this.log.debug('Matterbridge file log:', param);
572
690
  this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
573
691
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
692
+ // Create the file logger for matterbridge
574
693
  if (param === 'true')
575
- AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
694
+ AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
576
695
  else
577
696
  AnsiLogger.setGlobalLogfile(undefined);
578
697
  res.json({ message: 'Command received' });
579
698
  return;
580
699
  }
700
+ // Handle the command setmbloglevel from Settings
581
701
  if (command === 'setmjlogfile') {
582
702
  this.log.debug('Matter file log:', param);
583
703
  this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
@@ -604,36 +724,43 @@ export class Frontend {
604
724
  res.json({ message: 'Command received' });
605
725
  return;
606
726
  }
727
+ // Handle the command unregister from Settings
607
728
  if (command === 'unregister') {
608
729
  await this.matterbridge.unregisterAndShutdownProcess();
609
730
  res.json({ message: 'Command received' });
610
731
  return;
611
732
  }
733
+ // Handle the command reset from Settings
612
734
  if (command === 'reset') {
613
735
  await this.matterbridge.shutdownProcessAndReset();
614
736
  res.json({ message: 'Command received' });
615
737
  return;
616
738
  }
739
+ // Handle the command factoryreset from Settings
617
740
  if (command === 'factoryreset') {
618
741
  await this.matterbridge.shutdownProcessAndFactoryReset();
619
742
  res.json({ message: 'Command received' });
620
743
  return;
621
744
  }
745
+ // Handle the command shutdown from Header
622
746
  if (command === 'shutdown') {
623
747
  await this.matterbridge.shutdownProcess();
624
748
  res.json({ message: 'Command received' });
625
749
  return;
626
750
  }
751
+ // Handle the command restart from Header
627
752
  if (command === 'restart') {
628
753
  await this.matterbridge.restartProcess();
629
754
  res.json({ message: 'Command received' });
630
755
  return;
631
756
  }
757
+ // Handle the command update from Header
632
758
  if (command === 'update') {
633
759
  this.log.info('Updating matterbridge...');
634
760
  try {
635
761
  await this.matterbridge.spawnCommand('npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose']);
636
762
  this.log.info('Matterbridge has been updated. Full restart required.');
763
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
637
764
  }
638
765
  catch (error) {
639
766
  this.log.error('Error updating matterbridge');
@@ -643,9 +770,11 @@ export class Frontend {
643
770
  res.json({ message: 'Command received' });
644
771
  return;
645
772
  }
773
+ // Handle the command saveconfig from Home
646
774
  if (command === 'saveconfig') {
647
775
  param = param.replace(/\*/g, '\\');
648
776
  this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
777
+ // console.log('Req.body:', JSON.stringify(req.body, null, 2));
649
778
  if (!this.matterbridge.plugins.has(param)) {
650
779
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
651
780
  }
@@ -659,49 +788,58 @@ export class Frontend {
659
788
  res.json({ message: 'Command received' });
660
789
  return;
661
790
  }
791
+ // Handle the command installplugin from Home
662
792
  if (command === 'installplugin') {
663
793
  param = param.replace(/\*/g, '\\');
664
794
  this.log.info(`Installing plugin ${plg}${param}${nf}...`);
665
795
  try {
666
796
  await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
667
797
  this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
798
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
668
799
  }
669
800
  catch (error) {
670
801
  this.log.error(`Error installing plugin ${plg}${param}${er}`);
671
802
  }
672
803
  this.wssSendRestartRequired();
673
804
  param = param.split('@')[0];
805
+ // Also add the plugin to matterbridge so no return!
674
806
  if (param === 'matterbridge') {
807
+ // If we used the command installplugin to install a dev or a specific version of matterbridge we don't want to add it to matterbridge
675
808
  res.json({ message: 'Command received' });
676
809
  return;
677
810
  }
678
811
  }
812
+ // Handle the command addplugin from Home
679
813
  if (command === 'addplugin' || command === 'installplugin') {
680
814
  param = param.replace(/\*/g, '\\');
681
815
  const plugin = await this.matterbridge.plugins.add(param);
682
816
  if (plugin) {
683
817
  if (this.matterbridge.bridgeMode === 'childbridge') {
818
+ // We don't know now if the plugin is a dynamic platform or an accessory platform so we create the server node and the aggregator node
819
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
684
820
  this.matterbridge.createDynamicPlugin(plugin, true);
685
821
  }
686
- this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true);
822
+ this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true); // No await do it in the background
687
823
  }
688
824
  res.json({ message: 'Command received' });
689
825
  this.wssSendRefreshRequired();
690
826
  return;
691
827
  }
828
+ // Handle the command removeplugin from Home
692
829
  if (command === 'removeplugin') {
693
830
  if (!this.matterbridge.plugins.has(param)) {
694
831
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
695
832
  }
696
833
  else {
697
834
  const plugin = this.matterbridge.plugins.get(param);
698
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
835
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true); // This will also close the server node in childbridge mode
699
836
  await this.matterbridge.plugins.remove(param);
700
837
  }
701
838
  res.json({ message: 'Command received' });
702
839
  this.wssSendRefreshRequired();
703
840
  return;
704
841
  }
842
+ // Handle the command enableplugin from Home
705
843
  if (command === 'enableplugin') {
706
844
  if (!this.matterbridge.plugins.has(param)) {
707
845
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -719,15 +857,17 @@ export class Frontend {
719
857
  plugin.addedDevices = undefined;
720
858
  await this.matterbridge.plugins.enable(param);
721
859
  if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
860
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
722
861
  this.matterbridge.createDynamicPlugin(plugin, true);
723
862
  }
724
- this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true);
863
+ this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true); // No await do it in the background since the server node is already started
725
864
  }
726
865
  }
727
866
  res.json({ message: 'Command received' });
728
867
  this.wssSendRefreshRequired();
729
868
  return;
730
869
  }
870
+ // Handle the command disableplugin from Home
731
871
  if (command === 'disableplugin') {
732
872
  if (!this.matterbridge.plugins.has(param)) {
733
873
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -735,7 +875,7 @@ export class Frontend {
735
875
  else {
736
876
  const plugin = this.matterbridge.plugins.get(param);
737
877
  if (plugin && plugin.enabled) {
738
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
878
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true); // This will also close the server node in childbridge mode
739
879
  await this.matterbridge.plugins.disable(param);
740
880
  }
741
881
  }
@@ -744,6 +884,7 @@ export class Frontend {
744
884
  return;
745
885
  }
746
886
  });
887
+ // Fallback for routing (must be the last route)
747
888
  this.expressApp.get('*', (req, res) => {
748
889
  this.log.debug('The frontend sent:', req.url);
749
890
  this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -752,24 +893,29 @@ export class Frontend {
752
893
  this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
753
894
  }
754
895
  async stop() {
896
+ // Close the http server
755
897
  if (this.httpServer) {
756
898
  this.httpServer.close();
757
899
  this.httpServer.removeAllListeners();
758
900
  this.httpServer = undefined;
759
901
  this.log.debug('Frontend http server closed successfully');
760
902
  }
903
+ // Close the https server
761
904
  if (this.httpsServer) {
762
905
  this.httpsServer.close();
763
906
  this.httpsServer.removeAllListeners();
764
907
  this.httpsServer = undefined;
765
908
  this.log.debug('Frontend https server closed successfully');
766
909
  }
910
+ // Remove listeners from the express app
767
911
  if (this.expressApp) {
768
912
  this.expressApp.removeAllListeners();
769
913
  this.expressApp = undefined;
770
914
  this.log.debug('Frontend app closed successfully');
771
915
  }
916
+ // Close the WebSocket server
772
917
  if (this.webSocketServer) {
918
+ // Close all active connections
773
919
  this.webSocketServer.clients.forEach((client) => {
774
920
  if (client.readyState === WebSocket.OPEN) {
775
921
  client.close();
@@ -785,10 +931,12 @@ export class Frontend {
785
931
  });
786
932
  this.webSocketServer = undefined;
787
933
  }
934
+ // Stop the memory dump interval
788
935
  if (hasParameter('memorydump')) {
789
936
  this.stopMemoryDump();
790
937
  }
791
938
  }
939
+ // Function to format bytes to KB or MB
792
940
  formatMemoryUsage = (bytes) => {
793
941
  const kb = bytes / 1024;
794
942
  const mb = kb / 1024;
@@ -830,9 +978,14 @@ export class Frontend {
830
978
  external: this.formatMemoryUsage(memory.external),
831
979
  arrayBuffers: this.formatMemoryUsage(memory.arrayBuffers),
832
980
  };
981
+ // eslint-disable-next-line no-console
833
982
  console.log(`${YELLOW}Memory usage${db} rss ${CYAN}${memoryUsage.rss}${db} heapTotal ${CYAN}${memoryUsage.heapTotal}${db} heapUsed ${CYAN}${memoryUsage.heapUsed}${db} external ${memoryUsage.external} arrayBuffers ${memoryUsage.arrayBuffers}${rs}`);
834
983
  }
835
984
  }
985
+ /**
986
+ * Retrieves the api settings.
987
+ * @returns {Promise<object>} A promise that resolve in the api settings object.
988
+ */
836
989
  async getApiSettings() {
837
990
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
838
991
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
@@ -852,6 +1005,11 @@ export class Frontend {
852
1005
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
853
1006
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
854
1007
  }
1008
+ /**
1009
+ * Retrieves the cluster text description from a given device.
1010
+ * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
1011
+ * @returns {string} The attributes description of the cluster servers in the device.
1012
+ */
855
1013
  getClusterTextFromDevice(device) {
856
1014
  const getAttribute = (device, cluster, attribute) => {
857
1015
  let value = undefined;
@@ -890,6 +1048,7 @@ export class Frontend {
890
1048
  };
891
1049
  let attributes = '';
892
1050
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1051
+ // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
893
1052
  if (typeof attributeValue === 'undefined')
894
1053
  return;
895
1054
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -967,8 +1126,13 @@ export class Frontend {
967
1126
  if (clusterName === 'userLabel' && attributeName === 'labelList')
968
1127
  attributes += `${getUserLabel(device)} `;
969
1128
  });
1129
+ // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
970
1130
  return attributes.trimStart().trimEnd();
971
1131
  }
1132
+ /**
1133
+ * Retrieves the base registered plugins sanitized for res.json().
1134
+ * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
1135
+ */
972
1136
  getBaseRegisteredPlugins() {
973
1137
  const baseRegisteredPlugins = [];
974
1138
  for (const plugin of this.matterbridge.plugins) {
@@ -999,6 +1163,14 @@ export class Frontend {
999
1163
  }
1000
1164
  return baseRegisteredPlugins;
1001
1165
  }
1166
+ /**
1167
+ * Handles incoming websocket messages for the Matterbridge.
1168
+ *
1169
+ * @param {Matterbridge} this - The Matterbridge instance.
1170
+ * @param {WebSocket} client - The websocket client that sent the message.
1171
+ * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1172
+ * @returns {Promise<void>} A promise that resolves when the message has been handled.
1173
+ */
1002
1174
  async wsMessageHandler(client, message) {
1003
1175
  let data;
1004
1176
  try {
@@ -1086,8 +1258,10 @@ export class Frontend {
1086
1258
  else if (data.method === '/api/devices') {
1087
1259
  const devices = [];
1088
1260
  this.matterbridge.devices.forEach(async (device) => {
1261
+ // Filter by pluginName if provided
1089
1262
  if (data.params.pluginName && data.params.pluginName !== device.plugin)
1090
1263
  return;
1264
+ // Check if the device has the required properties
1091
1265
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
1092
1266
  return;
1093
1267
  const cluster = this.getClusterTextFromDevice(device);
@@ -1170,6 +1344,7 @@ export class Frontend {
1170
1344
  });
1171
1345
  endpointServer.getChildEndpoints().forEach((childEndpoint) => {
1172
1346
  deviceTypes = [];
1347
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1173
1348
  const name = childEndpoint.endpoint?.id;
1174
1349
  const clusterServers = childEndpoint.getAllClusterServers();
1175
1350
  clusterServers.forEach((clusterServer) => {
@@ -1252,44 +1427,73 @@ export class Frontend {
1252
1427
  return;
1253
1428
  }
1254
1429
  }
1430
+ /**
1431
+ * Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1432
+ *
1433
+ * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1434
+ * @param {string} time - The time string of the message
1435
+ * @param {string} name - The logger name of the message
1436
+ * @param {string} message - The content of the message.
1437
+ */
1255
1438
  wssSendMessage(level, time, name, message) {
1256
1439
  if (!level || !time || !name || !message)
1257
1440
  return;
1441
+ // Remove ANSI escape codes from the message
1442
+ // eslint-disable-next-line no-control-regex
1258
1443
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1444
+ // Remove leading asterisks from the message
1259
1445
  message = message.replace(/^\*+/, '');
1446
+ // Replace all occurrences of \t and \n
1260
1447
  message = message.replace(/[\t\n]/g, '');
1448
+ // Remove non-printable characters
1449
+ // eslint-disable-next-line no-control-regex
1261
1450
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1451
+ // Replace all occurrences of \" with "
1262
1452
  message = message.replace(/\\"/g, '"');
1453
+ // Define the maximum allowed length for continuous characters without a space
1263
1454
  const maxContinuousLength = 100;
1264
1455
  const keepStartLength = 20;
1265
1456
  const keepEndLength = 20;
1457
+ // Split the message into words
1266
1458
  message = message
1267
1459
  .split(' ')
1268
1460
  .map((word) => {
1461
+ // If the word length exceeds the max continuous length, insert spaces and truncate
1269
1462
  if (word.length > maxContinuousLength) {
1270
1463
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1271
1464
  }
1272
1465
  return word;
1273
1466
  })
1274
1467
  .join(' ');
1468
+ // Send the message to all connected clients
1275
1469
  this.webSocketServer?.clients.forEach((client) => {
1276
1470
  if (client.readyState === WebSocket.OPEN) {
1277
1471
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1278
1472
  }
1279
1473
  });
1280
1474
  }
1475
+ /**
1476
+ * Sends a need to refresh WebSocket message to all connected clients.
1477
+ *
1478
+ */
1281
1479
  wssSendRefreshRequired() {
1282
1480
  this.log.debug('Sending a refresh required message to all connected clients');
1283
1481
  this.matterbridge.matterbridgeInformation.refreshRequired = true;
1482
+ // Send the message to all connected clients
1284
1483
  this.webSocketServer?.clients.forEach((client) => {
1285
1484
  if (client.readyState === WebSocket.OPEN) {
1286
1485
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: {} }));
1287
1486
  }
1288
1487
  });
1289
1488
  }
1489
+ /**
1490
+ * Sends a need to restart WebSocket message to all connected clients.
1491
+ *
1492
+ */
1290
1493
  wssSendRestartRequired() {
1291
1494
  this.log.debug('Sending a restart required message to all connected clients');
1292
1495
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1496
+ // Send the message to all connected clients
1293
1497
  this.webSocketServer?.clients.forEach((client) => {
1294
1498
  if (client.readyState === WebSocket.OPEN) {
1295
1499
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
@@ -1297,3 +1501,4 @@ export class Frontend {
1297
1501
  });
1298
1502
  }
1299
1503
  }
1504
+ //# sourceMappingURL=frontend.js.map