matterbridge 2.0.0-edge.5 → 2.0.0

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 (91) 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 +98 -0
  19. package/dist/frontend.d.ts.map +1 -0
  20. package/dist/frontend.js +239 -22
  21. package/dist/frontend.js.map +1 -0
  22. package/dist/index.d.ts +34 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +29 -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/export.d.ts +10 -0
  31. package/dist/matter/export.d.ts.map +1 -0
  32. package/dist/matter/export.js +4 -0
  33. package/dist/matter/export.js.map +1 -0
  34. package/dist/matterbridge.d.ts +356 -0
  35. package/dist/matterbridge.d.ts.map +1 -0
  36. package/dist/matterbridge.js +687 -38
  37. package/dist/matterbridge.js.map +1 -0
  38. package/dist/matterbridgeAccessoryPlatform.d.ts +39 -0
  39. package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -0
  40. package/dist/matterbridgeAccessoryPlatform.js +33 -0
  41. package/dist/matterbridgeAccessoryPlatform.js.map +1 -0
  42. package/dist/matterbridgeBehaviors.d.ts +963 -0
  43. package/dist/matterbridgeBehaviors.d.ts.map +1 -0
  44. package/dist/matterbridgeBehaviors.js +31 -1
  45. package/dist/matterbridgeBehaviors.js.map +1 -0
  46. package/dist/matterbridgeDeviceTypes.d.ts +177 -0
  47. package/dist/matterbridgeDeviceTypes.d.ts.map +1 -0
  48. package/dist/matterbridgeDeviceTypes.js +112 -11
  49. package/dist/matterbridgeDeviceTypes.js.map +1 -0
  50. package/dist/matterbridgeDynamicPlatform.d.ts +39 -0
  51. package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -0
  52. package/dist/matterbridgeDynamicPlatform.js +33 -0
  53. package/dist/matterbridgeDynamicPlatform.js.map +1 -0
  54. package/dist/matterbridgeEndpoint.d.ts +10254 -0
  55. package/dist/matterbridgeEndpoint.d.ts.map +1 -0
  56. package/dist/matterbridgeEndpoint.js +1174 -10
  57. package/dist/matterbridgeEndpoint.js.map +1 -0
  58. package/dist/matterbridgeEndpointDefault.d.ts +2 -0
  59. package/dist/matterbridgeEndpointDefault.d.ts.map +1 -0
  60. package/dist/matterbridgeEndpointDefault.js +55 -0
  61. package/dist/matterbridgeEndpointDefault.js.map +1 -0
  62. package/dist/matterbridgePlatform.d.ts +164 -0
  63. package/dist/matterbridgePlatform.d.ts.map +1 -0
  64. package/dist/matterbridgePlatform.js +123 -3
  65. package/dist/matterbridgePlatform.js.map +1 -0
  66. package/dist/matterbridgeTypes.d.ts +167 -0
  67. package/dist/matterbridgeTypes.d.ts.map +1 -0
  68. package/dist/matterbridgeTypes.js +25 -0
  69. package/dist/matterbridgeTypes.js.map +1 -0
  70. package/dist/pluginManager.d.ts +238 -0
  71. package/dist/pluginManager.d.ts.map +1 -0
  72. package/dist/pluginManager.js +240 -3
  73. package/dist/pluginManager.js.map +1 -0
  74. package/dist/storage/export.d.ts +2 -0
  75. package/dist/storage/export.d.ts.map +1 -0
  76. package/dist/storage/export.js +1 -0
  77. package/dist/storage/export.js.map +1 -0
  78. package/dist/utils/colorUtils.d.ts +61 -0
  79. package/dist/utils/colorUtils.d.ts.map +1 -0
  80. package/dist/utils/colorUtils.js +205 -2
  81. package/dist/utils/colorUtils.js.map +1 -0
  82. package/dist/utils/export.d.ts +3 -0
  83. package/dist/utils/export.d.ts.map +1 -0
  84. package/dist/utils/export.js +1 -0
  85. package/dist/utils/export.js.map +1 -0
  86. package/dist/utils/utils.d.ts +221 -0
  87. package/dist/utils/utils.d.ts.map +1 -0
  88. package/dist/utils/utils.js +253 -9
  89. package/dist/utils/utils.js.map +1 -0
  90. package/npm-shrinkwrap.json +2 -2
  91. 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.0
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, 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;
@@ -24,15 +67,26 @@ export class Frontend {
24
67
  webSocketServer;
25
68
  constructor(matterbridge) {
26
69
  this.matterbridge = matterbridge;
27
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
70
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
28
71
  }
29
72
  async start(port = 8283) {
30
73
  this.port = port;
31
74
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
75
+ // Create the express app that serves the frontend
32
76
  this.expressApp = express();
77
+ // Log all requests to the server for debugging
78
+ /*
79
+ this.expressApp.use((req, res, next) => {
80
+ this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
81
+ next();
82
+ });
83
+ */
84
+ // Serve static files from '/static' endpoint
33
85
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
34
86
  if (!hasParameter('ssl')) {
87
+ // Create an HTTP server and attach the express app
35
88
  this.httpServer = createServer(this.expressApp);
89
+ // Listen on the specified port
36
90
  if (hasParameter('ingress')) {
37
91
  this.httpServer.listen(this.port, '0.0.0.0', () => {
38
92
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -46,6 +100,7 @@ export class Frontend {
46
100
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
47
101
  });
48
102
  }
103
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
104
  this.httpServer.on('error', (error) => {
50
105
  this.log.error(`Frontend http server error listening on ${this.port}`);
51
106
  switch (error.code) {
@@ -61,6 +116,7 @@ export class Frontend {
61
116
  });
62
117
  }
63
118
  else {
119
+ // Load the SSL certificate, the private key and optionally the CA certificate
64
120
  let cert;
65
121
  try {
66
122
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
@@ -88,7 +144,9 @@ export class Frontend {
88
144
  this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
89
145
  }
90
146
  const serverOptions = { cert, key, ca };
147
+ // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
91
148
  this.httpsServer = https.createServer(serverOptions, this.expressApp);
149
+ // Listen on the specified port
92
150
  if (hasParameter('ingress')) {
93
151
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
94
152
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -102,6 +160,7 @@ export class Frontend {
102
160
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
103
161
  });
104
162
  }
163
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
105
164
  this.httpsServer.on('error', (error) => {
106
165
  this.log.error(`Frontend https server error listening on ${this.port}`);
107
166
  switch (error.code) {
@@ -118,12 +177,13 @@ export class Frontend {
118
177
  }
119
178
  if (this.initializeError)
120
179
  return;
180
+ // Createe a WebSocket server and attach it to the http or https server
121
181
  const wssPort = this.port;
122
182
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
123
183
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
124
184
  this.webSocketServer.on('connection', (ws, request) => {
125
185
  const clientIp = request.socket.remoteAddress;
126
- AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug");
186
+ AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug" /* LogLevel.DEBUG */);
127
187
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
128
188
  ws.on('message', (message) => {
129
189
  this.wsMessageHandler(ws, message);
@@ -155,6 +215,7 @@ export class Frontend {
155
215
  this.webSocketServer.on('error', (ws, error) => {
156
216
  this.log.error(`WebSocketServer error: ${error}`);
157
217
  });
218
+ // Endpoint to validate login code
158
219
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
159
220
  const { password } = req.body;
160
221
  this.log.debug('The frontend sent /api/login', password);
@@ -173,21 +234,66 @@ export class Frontend {
173
234
  this.log.warn('/api/login error wrong password');
174
235
  res.json({ valid: false });
175
236
  }
237
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
176
238
  }
177
239
  catch (error) {
178
240
  this.log.error('/api/login error getting password');
179
241
  res.json({ valid: false });
180
242
  }
181
243
  });
244
+ // Endpoint to provide health check
182
245
  this.expressApp.get('/health', (req, res) => {
183
246
  this.log.debug('Express received /health');
184
247
  const healthStatus = {
185
- status: 'ok',
186
- uptime: process.uptime(),
187
- timestamp: new Date().toISOString(),
248
+ status: 'ok', // Indicate service is healthy
249
+ uptime: process.uptime(), // Server uptime in seconds
250
+ timestamp: new Date().toISOString(), // Current timestamp
188
251
  };
189
252
  res.status(200).json(healthStatus);
190
253
  });
254
+ // Endpoint to provide memory usage details
255
+ this.expressApp.get('/memory', async (req, res) => {
256
+ this.log.debug('Express received /memory');
257
+ // Function to format bytes to KB or MB
258
+ const formatMemoryUsage = (bytes) => {
259
+ const kb = bytes / 1024;
260
+ const mb = kb / 1024;
261
+ return mb >= 1 ? `${mb.toFixed(2)} MB` : `${kb.toFixed(2)} KB`;
262
+ };
263
+ // Memory usage from process
264
+ const memoryUsageRaw = process.memoryUsage();
265
+ const memoryUsage = {
266
+ rss: formatMemoryUsage(memoryUsageRaw.rss),
267
+ heapTotal: formatMemoryUsage(memoryUsageRaw.heapTotal),
268
+ heapUsed: formatMemoryUsage(memoryUsageRaw.heapUsed),
269
+ external: formatMemoryUsage(memoryUsageRaw.external),
270
+ arrayBuffers: formatMemoryUsage(memoryUsageRaw.arrayBuffers),
271
+ };
272
+ // V8 heap statistics
273
+ const { default: v8 } = await import('node:v8');
274
+ const heapStatsRaw = v8.getHeapStatistics();
275
+ const heapSpacesRaw = v8.getHeapSpaceStatistics();
276
+ // Format heapStats
277
+ const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, formatMemoryUsage(value)]));
278
+ // Format heapSpaces
279
+ const heapSpaces = heapSpacesRaw.map((space) => ({
280
+ ...space,
281
+ space_size: formatMemoryUsage(space.space_size),
282
+ space_used_size: formatMemoryUsage(space.space_used_size),
283
+ space_available_size: formatMemoryUsage(space.space_available_size),
284
+ physical_space_size: formatMemoryUsage(space.physical_space_size),
285
+ }));
286
+ const { default: module } = await import('module');
287
+ const loadedModules = module._cache ? Object.keys(module._cache).sort() : [];
288
+ const memoryReport = {
289
+ memoryUsage,
290
+ heapStats,
291
+ heapSpaces,
292
+ loadedModules,
293
+ };
294
+ res.status(200).json(memoryReport);
295
+ });
296
+ // Endpoint to provide settings
191
297
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
192
298
  this.log.debug('The frontend sent /api/settings');
193
299
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
@@ -207,17 +313,22 @@ export class Frontend {
207
313
  this.matterbridge.matterbridgeInformation.matterbridgeSessionInformations = Array.from(this.matterbridge.matterbridgeSessionInformations.values());
208
314
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
209
315
  const response = { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
316
+ // this.log.debug('Response:', debugStringify(response));
210
317
  res.json(response);
211
318
  });
319
+ // Endpoint to provide plugins
212
320
  this.expressApp.get('/api/plugins', async (req, res) => {
213
321
  this.log.debug('The frontend sent /api/plugins');
214
322
  const response = await this.getBaseRegisteredPlugins();
323
+ // this.log.debug('Response:', debugStringify(response));
215
324
  res.json(response);
216
325
  });
326
+ // Endpoint to provide devices
217
327
  this.expressApp.get('/api/devices', (req, res) => {
218
328
  this.log.debug('The frontend sent /api/devices');
219
329
  const devices = [];
220
330
  this.matterbridge.devices.forEach(async (device) => {
331
+ // Check if the device has the required properties
221
332
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
222
333
  return;
223
334
  const cluster = this.getClusterTextFromDevice(device);
@@ -233,8 +344,10 @@ export class Frontend {
233
344
  cluster: cluster,
234
345
  });
235
346
  });
347
+ // this.log.debug('Response:', debugStringify(data));
236
348
  res.json(devices);
237
349
  });
350
+ // Endpoint to provide the cluster servers of the devices
238
351
  this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
239
352
  const selectedPluginName = req.params.selectedPluginName;
240
353
  const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
@@ -307,6 +420,7 @@ export class Frontend {
307
420
  });
308
421
  res.json(data);
309
422
  });
423
+ // Endpoint to view the log
310
424
  this.expressApp.get('/api/view-log', async (req, res) => {
311
425
  this.log.debug('The frontend sent /api/log');
312
426
  try {
@@ -319,10 +433,12 @@ export class Frontend {
319
433
  res.status(500).send('Error reading log file');
320
434
  }
321
435
  });
436
+ // Endpoint to download the matterbridge log
322
437
  this.expressApp.get('/api/download-mblog', async (req, res) => {
323
438
  this.log.debug('The frontend sent /api/download-mblog');
324
439
  try {
325
440
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
441
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
326
442
  }
327
443
  catch (error) {
328
444
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -334,10 +450,12 @@ export class Frontend {
334
450
  }
335
451
  });
336
452
  });
453
+ // Endpoint to download the matter log
337
454
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
338
455
  this.log.debug('The frontend sent /api/download-mjlog');
339
456
  try {
340
457
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
458
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
341
459
  }
342
460
  catch (error) {
343
461
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -349,6 +467,7 @@ export class Frontend {
349
467
  }
350
468
  });
351
469
  });
470
+ // Endpoint to download the matter storage file
352
471
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
353
472
  this.log.debug('The frontend sent /api/download-mjstorage');
354
473
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
@@ -359,6 +478,7 @@ export class Frontend {
359
478
  }
360
479
  });
361
480
  });
481
+ // Endpoint to download the matterbridge storage directory
362
482
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
363
483
  this.log.debug('The frontend sent /api/download-mbstorage');
364
484
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
@@ -369,6 +489,7 @@ export class Frontend {
369
489
  }
370
490
  });
371
491
  });
492
+ // Endpoint to download the matterbridge plugin directory
372
493
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
373
494
  this.log.debug('The frontend sent /api/download-pluginstorage');
374
495
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
@@ -379,9 +500,11 @@ export class Frontend {
379
500
  }
380
501
  });
381
502
  });
503
+ // Endpoint to download the matterbridge plugin config files
382
504
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
383
505
  this.log.debug('The frontend sent /api/download-pluginconfig');
384
506
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
507
+ // 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')));
385
508
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
386
509
  if (error) {
387
510
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
@@ -389,6 +512,7 @@ export class Frontend {
389
512
  }
390
513
  });
391
514
  });
515
+ // Endpoint to download the matterbridge plugin config files
392
516
  this.expressApp.get('/api/download-backup', async (req, res) => {
393
517
  this.log.debug('The frontend sent /api/download-backup');
394
518
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -398,6 +522,7 @@ export class Frontend {
398
522
  }
399
523
  });
400
524
  });
525
+ // Endpoint to receive commands
401
526
  this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
402
527
  const command = req.params.command;
403
528
  let param = req.params.param;
@@ -407,13 +532,15 @@ export class Frontend {
407
532
  return;
408
533
  }
409
534
  this.log.debug(`Received frontend command: ${command}:${param}`);
535
+ // Handle the command setpassword from Settings
410
536
  if (command === 'setpassword') {
411
- const password = param.slice(1, -1);
537
+ const password = param.slice(1, -1); // Remove the first and last characters
412
538
  this.log.debug('setpassword', param, password);
413
539
  await this.matterbridge.nodeContext?.set('password', password);
414
540
  res.json({ message: 'Command received' });
415
541
  return;
416
542
  }
543
+ // Handle the command setbridgemode from Settings
417
544
  if (command === 'setbridgemode') {
418
545
  this.log.debug(`setbridgemode: ${param}`);
419
546
  this.wssSendRestartRequired();
@@ -421,6 +548,7 @@ export class Frontend {
421
548
  res.json({ message: 'Command received' });
422
549
  return;
423
550
  }
551
+ // Handle the command backup from Settings
424
552
  if (command === 'backup') {
425
553
  this.log.notice(`Prepairing the backup...`);
426
554
  await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
@@ -428,25 +556,26 @@ export class Frontend {
428
556
  res.json({ message: 'Command received' });
429
557
  return;
430
558
  }
559
+ // Handle the command setmbloglevel from Settings
431
560
  if (command === 'setmbloglevel') {
432
561
  this.log.debug('Matterbridge log level:', param);
433
562
  if (param === 'Debug') {
434
- this.log.logLevel = "debug";
563
+ this.log.logLevel = "debug" /* LogLevel.DEBUG */;
435
564
  }
436
565
  else if (param === 'Info') {
437
- this.log.logLevel = "info";
566
+ this.log.logLevel = "info" /* LogLevel.INFO */;
438
567
  }
439
568
  else if (param === 'Notice') {
440
- this.log.logLevel = "notice";
569
+ this.log.logLevel = "notice" /* LogLevel.NOTICE */;
441
570
  }
442
571
  else if (param === 'Warn') {
443
- this.log.logLevel = "warn";
572
+ this.log.logLevel = "warn" /* LogLevel.WARN */;
444
573
  }
445
574
  else if (param === 'Error') {
446
- this.log.logLevel = "error";
575
+ this.log.logLevel = "error" /* LogLevel.ERROR */;
447
576
  }
448
577
  else if (param === 'Fatal') {
449
- this.log.logLevel = "fatal";
578
+ this.log.logLevel = "fatal" /* LogLevel.FATAL */;
450
579
  }
451
580
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
452
581
  MatterbridgeEndpoint.logLevel = this.log.logLevel;
@@ -454,12 +583,13 @@ export class Frontend {
454
583
  for (const plugin of this.matterbridge.plugins) {
455
584
  if (!plugin.platform || !plugin.platform.config)
456
585
  continue;
457
- plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug" : this.log.logLevel;
458
- await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug" : this.log.logLevel);
586
+ plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel;
587
+ await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel);
459
588
  }
460
589
  res.json({ message: 'Command received' });
461
590
  return;
462
591
  }
592
+ // Handle the command setmbloglevel from Settings
463
593
  if (command === 'setmjloglevel') {
464
594
  this.log.debug('Matter.js log level:', param);
465
595
  if (param === 'Debug') {
@@ -484,30 +614,34 @@ export class Frontend {
484
614
  res.json({ message: 'Command received' });
485
615
  return;
486
616
  }
617
+ // Handle the command setmdnsinterface from Settings
487
618
  if (command === 'setmdnsinterface') {
488
- param = param.slice(1, -1);
619
+ param = param.slice(1, -1); // Remove the first and last characters *mdns*
489
620
  this.matterbridge.matterbridgeInformation.mattermdnsinterface = param;
490
621
  this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
491
622
  await this.matterbridge.nodeContext?.set('mattermdnsinterface', param);
492
623
  res.json({ message: 'Command received' });
493
624
  return;
494
625
  }
626
+ // Handle the command setipv4address from Settings
495
627
  if (command === 'setipv4address') {
496
- param = param.slice(1, -1);
628
+ param = param.slice(1, -1); // Remove the first and last characters *ip*
497
629
  this.matterbridge.matterbridgeInformation.matteripv4address = param;
498
630
  this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
499
631
  await this.matterbridge.nodeContext?.set('matteripv4address', param);
500
632
  res.json({ message: 'Command received' });
501
633
  return;
502
634
  }
635
+ // Handle the command setipv6address from Settings
503
636
  if (command === 'setipv6address') {
504
- param = param.slice(1, -1);
637
+ param = param.slice(1, -1); // Remove the first and last characters *ip*
505
638
  this.matterbridge.matterbridgeInformation.matteripv6address = param;
506
639
  this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
507
640
  await this.matterbridge.nodeContext?.set('matteripv6address', param);
508
641
  res.json({ message: 'Command received' });
509
642
  return;
510
643
  }
644
+ // Handle the command setmatterport from Settings
511
645
  if (command === 'setmatterport') {
512
646
  const port = Math.min(Math.max(parseInt(param), 5540), 5560);
513
647
  this.matterbridge.matterbridgeInformation.matterPort = port;
@@ -516,6 +650,7 @@ export class Frontend {
516
650
  res.json({ message: 'Command received' });
517
651
  return;
518
652
  }
653
+ // Handle the command setmatterdiscriminator from Settings
519
654
  if (command === 'setmatterdiscriminator') {
520
655
  const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
521
656
  this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
@@ -524,6 +659,7 @@ export class Frontend {
524
659
  res.json({ message: 'Command received' });
525
660
  return;
526
661
  }
662
+ // Handle the command setmatterpasscode from Settings
527
663
  if (command === 'setmatterpasscode') {
528
664
  const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
529
665
  this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
@@ -532,17 +668,20 @@ export class Frontend {
532
668
  res.json({ message: 'Command received' });
533
669
  return;
534
670
  }
671
+ // Handle the command setmbloglevel from Settings
535
672
  if (command === 'setmblogfile') {
536
673
  this.log.debug('Matterbridge file log:', param);
537
674
  this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
538
675
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
676
+ // Create the file logger for matterbridge
539
677
  if (param === 'true')
540
- AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
678
+ AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
541
679
  else
542
680
  AnsiLogger.setGlobalLogfile(undefined);
543
681
  res.json({ message: 'Command received' });
544
682
  return;
545
683
  }
684
+ // Handle the command setmbloglevel from Settings
546
685
  if (command === 'setmjlogfile') {
547
686
  this.log.debug('Matter file log:', param);
548
687
  this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
@@ -569,36 +708,43 @@ export class Frontend {
569
708
  res.json({ message: 'Command received' });
570
709
  return;
571
710
  }
711
+ // Handle the command unregister from Settings
572
712
  if (command === 'unregister') {
573
713
  await this.matterbridge.unregisterAndShutdownProcess();
574
714
  res.json({ message: 'Command received' });
575
715
  return;
576
716
  }
717
+ // Handle the command reset from Settings
577
718
  if (command === 'reset') {
578
719
  await this.matterbridge.shutdownProcessAndReset();
579
720
  res.json({ message: 'Command received' });
580
721
  return;
581
722
  }
723
+ // Handle the command factoryreset from Settings
582
724
  if (command === 'factoryreset') {
583
725
  await this.matterbridge.shutdownProcessAndFactoryReset();
584
726
  res.json({ message: 'Command received' });
585
727
  return;
586
728
  }
729
+ // Handle the command shutdown from Header
587
730
  if (command === 'shutdown') {
588
731
  await this.matterbridge.shutdownProcess();
589
732
  res.json({ message: 'Command received' });
590
733
  return;
591
734
  }
735
+ // Handle the command restart from Header
592
736
  if (command === 'restart') {
593
737
  await this.matterbridge.restartProcess();
594
738
  res.json({ message: 'Command received' });
595
739
  return;
596
740
  }
741
+ // Handle the command update from Header
597
742
  if (command === 'update') {
598
743
  this.log.info('Updating matterbridge...');
599
744
  try {
600
745
  await this.matterbridge.spawnCommand('npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose']);
601
746
  this.log.info('Matterbridge has been updated. Full restart required.');
747
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
602
748
  }
603
749
  catch (error) {
604
750
  this.log.error('Error updating matterbridge');
@@ -608,9 +754,11 @@ export class Frontend {
608
754
  res.json({ message: 'Command received' });
609
755
  return;
610
756
  }
757
+ // Handle the command saveconfig from Home
611
758
  if (command === 'saveconfig') {
612
759
  param = param.replace(/\*/g, '\\');
613
760
  this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
761
+ // console.log('Req.body:', JSON.stringify(req.body, null, 2));
614
762
  if (!this.matterbridge.plugins.has(param)) {
615
763
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
616
764
  }
@@ -624,49 +772,58 @@ export class Frontend {
624
772
  res.json({ message: 'Command received' });
625
773
  return;
626
774
  }
775
+ // Handle the command installplugin from Home
627
776
  if (command === 'installplugin') {
628
777
  param = param.replace(/\*/g, '\\');
629
778
  this.log.info(`Installing plugin ${plg}${param}${nf}...`);
630
779
  try {
631
780
  await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
632
781
  this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
782
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
633
783
  }
634
784
  catch (error) {
635
785
  this.log.error(`Error installing plugin ${plg}${param}${er}`);
636
786
  }
637
787
  this.wssSendRestartRequired();
638
788
  param = param.split('@')[0];
789
+ // Also add the plugin to matterbridge so no return!
639
790
  if (param === 'matterbridge') {
791
+ // 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
640
792
  res.json({ message: 'Command received' });
641
793
  return;
642
794
  }
643
795
  }
796
+ // Handle the command addplugin from Home
644
797
  if (command === 'addplugin' || command === 'installplugin') {
645
798
  param = param.replace(/\*/g, '\\');
646
799
  const plugin = await this.matterbridge.plugins.add(param);
647
800
  if (plugin) {
648
801
  if (this.matterbridge.bridgeMode === 'childbridge') {
802
+ // 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
803
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
649
804
  this.matterbridge.createDynamicPlugin(plugin, true);
650
805
  }
651
- this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true);
806
+ this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true); // No await do it in the background
652
807
  }
653
808
  res.json({ message: 'Command received' });
654
809
  this.wssSendRefreshRequired();
655
810
  return;
656
811
  }
812
+ // Handle the command removeplugin from Home
657
813
  if (command === 'removeplugin') {
658
814
  if (!this.matterbridge.plugins.has(param)) {
659
815
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
660
816
  }
661
817
  else {
662
818
  const plugin = this.matterbridge.plugins.get(param);
663
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
819
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true); // This will also close the server node in childbridge mode
664
820
  await this.matterbridge.plugins.remove(param);
665
821
  }
666
822
  res.json({ message: 'Command received' });
667
823
  this.wssSendRefreshRequired();
668
824
  return;
669
825
  }
826
+ // Handle the command enableplugin from Home
670
827
  if (command === 'enableplugin') {
671
828
  if (!this.matterbridge.plugins.has(param)) {
672
829
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -684,15 +841,17 @@ export class Frontend {
684
841
  plugin.addedDevices = undefined;
685
842
  await this.matterbridge.plugins.enable(param);
686
843
  if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
844
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
687
845
  this.matterbridge.createDynamicPlugin(plugin, true);
688
846
  }
689
- this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true);
847
+ 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
690
848
  }
691
849
  }
692
850
  res.json({ message: 'Command received' });
693
851
  this.wssSendRefreshRequired();
694
852
  return;
695
853
  }
854
+ // Handle the command disableplugin from Home
696
855
  if (command === 'disableplugin') {
697
856
  if (!this.matterbridge.plugins.has(param)) {
698
857
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -700,7 +859,7 @@ export class Frontend {
700
859
  else {
701
860
  const plugin = this.matterbridge.plugins.get(param);
702
861
  if (plugin && plugin.enabled) {
703
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
862
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true); // This will also close the server node in childbridge mode
704
863
  await this.matterbridge.plugins.disable(param);
705
864
  }
706
865
  }
@@ -709,6 +868,7 @@ export class Frontend {
709
868
  return;
710
869
  }
711
870
  });
871
+ // Fallback for routing (must be the last route)
712
872
  this.expressApp.get('*', (req, res) => {
713
873
  this.log.debug('The frontend sent:', req.url);
714
874
  this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -717,24 +877,29 @@ export class Frontend {
717
877
  this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
718
878
  }
719
879
  async stop() {
880
+ // Close the http server
720
881
  if (this.httpServer) {
721
882
  this.httpServer.close();
722
883
  this.httpServer.removeAllListeners();
723
884
  this.httpServer = undefined;
724
885
  this.log.debug('Frontend http server closed successfully');
725
886
  }
887
+ // Close the https server
726
888
  if (this.httpsServer) {
727
889
  this.httpsServer.close();
728
890
  this.httpsServer.removeAllListeners();
729
891
  this.httpsServer = undefined;
730
892
  this.log.debug('Frontend https server closed successfully');
731
893
  }
894
+ // Remove listeners from the express app
732
895
  if (this.expressApp) {
733
896
  this.expressApp.removeAllListeners();
734
897
  this.expressApp = undefined;
735
898
  this.log.debug('Frontend app closed successfully');
736
899
  }
900
+ // Close the WebSocket server
737
901
  if (this.webSocketServer) {
902
+ // Close all active connections
738
903
  this.webSocketServer.clients.forEach((client) => {
739
904
  if (client.readyState === WebSocket.OPEN) {
740
905
  client.close();
@@ -751,6 +916,11 @@ export class Frontend {
751
916
  this.webSocketServer = undefined;
752
917
  }
753
918
  }
919
+ /**
920
+ * Retrieves the cluster text description from a given device.
921
+ * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
922
+ * @returns {string} The attributes description of the cluster servers in the device.
923
+ */
754
924
  getClusterTextFromDevice(device) {
755
925
  const getAttribute = (device, cluster, attribute) => {
756
926
  let value = undefined;
@@ -788,8 +958,10 @@ export class Frontend {
788
958
  return '';
789
959
  };
790
960
  let attributes = '';
961
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
791
962
  Object.entries(device.state).forEach(([clusterName, clusterAttributes]) => {
792
963
  Object.entries(clusterAttributes).forEach(([attributeName, attributeValue]) => {
964
+ // console.log(`Cluster: ${clusterName} Attribute: ${attributeName} Value: ${attributeValue}`);
793
965
  if (clusterName === 'onOff' && attributeName === 'onOff')
794
966
  attributes += `OnOff: ${attributeValue} `;
795
967
  if (clusterName === 'switch' && attributeName === 'currentPosition')
@@ -860,6 +1032,10 @@ export class Frontend {
860
1032
  });
861
1033
  return attributes.trimStart().trimEnd();
862
1034
  }
1035
+ /**
1036
+ * Retrieves the base registered plugins sanitized for res.json().
1037
+ * @returns {BaseRegisteredPlugin[]} A promise that resolves to an array of BaseRegisteredPlugin objects.
1038
+ */
863
1039
  async getBaseRegisteredPlugins() {
864
1040
  const baseRegisteredPlugins = [];
865
1041
  for (const plugin of this.matterbridge.plugins) {
@@ -890,6 +1066,14 @@ export class Frontend {
890
1066
  }
891
1067
  return baseRegisteredPlugins;
892
1068
  }
1069
+ /**
1070
+ * Handles incoming websocket messages for the Matterbridge.
1071
+ *
1072
+ * @param {Matterbridge} this - The Matterbridge instance.
1073
+ * @param {WebSocket} client - The websocket client that sent the message.
1074
+ * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1075
+ * @returns {Promise<void>} A promise that resolves when the message has been handled.
1076
+ */
893
1077
  async wsMessageHandler(client, message) {
894
1078
  let data;
895
1079
  try {
@@ -989,8 +1173,10 @@ export class Frontend {
989
1173
  else if (data.method === '/api/devices') {
990
1174
  const devices = [];
991
1175
  this.matterbridge.devices.forEach(async (device) => {
1176
+ // Filter by pluginName if provided
992
1177
  if (data.params.pluginName && data.params.pluginName !== device.plugin)
993
1178
  return;
1179
+ // Check if the device has the required properties
994
1180
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
995
1181
  return;
996
1182
  const cluster = this.getClusterTextFromDevice(device);
@@ -1070,6 +1256,7 @@ export class Frontend {
1070
1256
  });
1071
1257
  endpointServer.getChildEndpoints().forEach((childEndpoint) => {
1072
1258
  deviceTypes = [];
1259
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1073
1260
  const name = childEndpoint.endpoint?.id;
1074
1261
  const clusterServers = childEndpoint.getAllClusterServers();
1075
1262
  clusterServers.forEach((clusterServer) => {
@@ -1152,44 +1339,73 @@ export class Frontend {
1152
1339
  return;
1153
1340
  }
1154
1341
  }
1342
+ /**
1343
+ * Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1344
+ *
1345
+ * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1346
+ * @param {string} time - The time string of the message
1347
+ * @param {string} name - The logger name of the message
1348
+ * @param {string} message - The content of the message.
1349
+ */
1155
1350
  wssSendMessage(level, time, name, message) {
1156
1351
  if (!level || !time || !name || !message)
1157
1352
  return;
1353
+ // Remove ANSI escape codes from the message
1354
+ // eslint-disable-next-line no-control-regex
1158
1355
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1356
+ // Remove leading asterisks from the message
1159
1357
  message = message.replace(/^\*+/, '');
1358
+ // Replace all occurrences of \t and \n
1160
1359
  message = message.replace(/[\t\n]/g, '');
1360
+ // Remove non-printable characters
1361
+ // eslint-disable-next-line no-control-regex
1161
1362
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1363
+ // Replace all occurrences of \" with "
1162
1364
  message = message.replace(/\\"/g, '"');
1365
+ // Define the maximum allowed length for continuous characters without a space
1163
1366
  const maxContinuousLength = 100;
1164
1367
  const keepStartLength = 20;
1165
1368
  const keepEndLength = 20;
1369
+ // Split the message into words
1166
1370
  message = message
1167
1371
  .split(' ')
1168
1372
  .map((word) => {
1373
+ // If the word length exceeds the max continuous length, insert spaces and truncate
1169
1374
  if (word.length > maxContinuousLength) {
1170
1375
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1171
1376
  }
1172
1377
  return word;
1173
1378
  })
1174
1379
  .join(' ');
1380
+ // Send the message to all connected clients
1175
1381
  this.webSocketServer?.clients.forEach((client) => {
1176
1382
  if (client.readyState === WebSocket.OPEN) {
1177
1383
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1178
1384
  }
1179
1385
  });
1180
1386
  }
1387
+ /**
1388
+ * Sends a need to refresh WebSocket message to all connected clients.
1389
+ *
1390
+ */
1181
1391
  wssSendRefreshRequired() {
1182
1392
  this.log.debug('Sending a refresh required message to all connected clients');
1183
1393
  this.matterbridge.matterbridgeInformation.refreshRequired = true;
1394
+ // Send the message to all connected clients
1184
1395
  this.webSocketServer?.clients.forEach((client) => {
1185
1396
  if (client.readyState === WebSocket.OPEN) {
1186
1397
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: {} }));
1187
1398
  }
1188
1399
  });
1189
1400
  }
1401
+ /**
1402
+ * Sends a need to restart WebSocket message to all connected clients.
1403
+ *
1404
+ */
1190
1405
  wssSendRestartRequired() {
1191
1406
  this.log.debug('Sending a restart required message to all connected clients');
1192
1407
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1408
+ // Send the message to all connected clients
1193
1409
  this.webSocketServer?.clients.forEach((client) => {
1194
1410
  if (client.readyState === WebSocket.OPEN) {
1195
1411
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
@@ -1197,3 +1413,4 @@ export class Frontend {
1197
1413
  });
1198
1414
  }
1199
1415
  }
1416
+ //# sourceMappingURL=frontend.js.map