matterbridge 2.2.0-dev.4 → 2.2.0-dev.5

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 (127) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/dist/cli.d.ts +28 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +37 -2
  5. package/dist/cli.js.map +1 -0
  6. package/dist/cluster/export.d.ts.map +1 -0
  7. package/dist/cluster/export.js +2 -0
  8. package/dist/cluster/export.js.map +1 -0
  9. package/dist/defaultConfigSchema.d.ts.map +1 -0
  10. package/dist/defaultConfigSchema.js +23 -0
  11. package/dist/defaultConfigSchema.js.map +1 -0
  12. package/dist/deviceManager.d.ts +109 -0
  13. package/dist/deviceManager.d.ts.map +1 -0
  14. package/dist/deviceManager.js +94 -1
  15. package/dist/deviceManager.js.map +1 -0
  16. package/dist/frontend.d.ts +172 -0
  17. package/dist/frontend.d.ts.map +1 -0
  18. package/dist/frontend.js +265 -22
  19. package/dist/frontend.js.map +1 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +29 -2
  22. package/dist/index.js.map +1 -0
  23. package/dist/logger/export.d.ts.map +1 -0
  24. package/dist/logger/export.js +1 -0
  25. package/dist/logger/export.js.map +1 -0
  26. package/dist/matter/behaviors.d.ts.map +1 -0
  27. package/dist/matter/behaviors.js +2 -0
  28. package/dist/matter/behaviors.js.map +1 -0
  29. package/dist/matter/clusters.d.ts.map +1 -0
  30. package/dist/matter/clusters.js +2 -0
  31. package/dist/matter/clusters.js.map +1 -0
  32. package/dist/matter/devices.d.ts.map +1 -0
  33. package/dist/matter/devices.js +2 -0
  34. package/dist/matter/devices.js.map +1 -0
  35. package/dist/matter/endpoints.d.ts.map +1 -0
  36. package/dist/matter/endpoints.js +2 -0
  37. package/dist/matter/endpoints.js.map +1 -0
  38. package/dist/matter/export.d.ts.map +1 -0
  39. package/dist/matter/export.js +2 -0
  40. package/dist/matter/export.js.map +1 -0
  41. package/dist/matter/types.d.ts.map +1 -0
  42. package/dist/matter/types.js +3 -1
  43. package/dist/matter/types.js.map +1 -0
  44. package/dist/matterbridge.d.ts +410 -0
  45. package/dist/matterbridge.d.ts.map +1 -0
  46. package/dist/matterbridge.js +718 -47
  47. package/dist/matterbridge.js.map +1 -0
  48. package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -0
  49. package/dist/matterbridgeAccessoryPlatform.js +33 -0
  50. package/dist/matterbridgeAccessoryPlatform.js.map +1 -0
  51. package/dist/matterbridgeBehaviors.d.ts +148 -0
  52. package/dist/matterbridgeBehaviors.d.ts.map +1 -0
  53. package/dist/matterbridgeBehaviors.js +32 -1
  54. package/dist/matterbridgeBehaviors.js.map +1 -0
  55. package/dist/matterbridgeDeviceTypes.d.ts.map +1 -0
  56. package/dist/matterbridgeDeviceTypes.js +112 -11
  57. package/dist/matterbridgeDeviceTypes.js.map +1 -0
  58. package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -0
  59. package/dist/matterbridgeDynamicPlatform.js +33 -0
  60. package/dist/matterbridgeDynamicPlatform.js.map +1 -0
  61. package/dist/matterbridgeEndpoint.d.ts +827 -0
  62. package/dist/matterbridgeEndpoint.d.ts.map +1 -0
  63. package/dist/matterbridgeEndpoint.js +690 -6
  64. package/dist/matterbridgeEndpoint.js.map +1 -0
  65. package/dist/matterbridgeEndpointHelpers.d.ts +123 -0
  66. package/dist/matterbridgeEndpointHelpers.d.ts.map +1 -0
  67. package/dist/matterbridgeEndpointHelpers.js +118 -9
  68. package/dist/matterbridgeEndpointHelpers.js.map +1 -0
  69. package/dist/matterbridgePlatform.d.ts +159 -0
  70. package/dist/matterbridgePlatform.d.ts.map +1 -0
  71. package/dist/matterbridgePlatform.js +162 -23
  72. package/dist/matterbridgePlatform.js.map +1 -0
  73. package/dist/matterbridgeTypes.d.ts.map +1 -0
  74. package/dist/matterbridgeTypes.js +24 -0
  75. package/dist/matterbridgeTypes.js.map +1 -0
  76. package/dist/pluginManager.d.ts +236 -0
  77. package/dist/pluginManager.d.ts.map +1 -0
  78. package/dist/pluginManager.js +229 -3
  79. package/dist/pluginManager.js.map +1 -0
  80. package/dist/shelly.d.ts.map +1 -0
  81. package/dist/shelly.js +131 -4
  82. package/dist/shelly.js.map +1 -0
  83. package/dist/storage/export.d.ts.map +1 -0
  84. package/dist/storage/export.js +1 -0
  85. package/dist/storage/export.js.map +1 -0
  86. package/dist/update.d.ts.map +1 -0
  87. package/dist/update.js +45 -0
  88. package/dist/update.js.map +1 -0
  89. package/dist/utils/colorUtils.d.ts.map +1 -0
  90. package/dist/utils/colorUtils.js +205 -2
  91. package/dist/utils/colorUtils.js.map +1 -0
  92. package/dist/utils/copyDirectory.d.ts.map +1 -0
  93. package/dist/utils/copyDirectory.js +37 -1
  94. package/dist/utils/copyDirectory.js.map +1 -0
  95. package/dist/utils/createZip.d.ts.map +1 -0
  96. package/dist/utils/createZip.js +42 -2
  97. package/dist/utils/createZip.js.map +1 -0
  98. package/dist/utils/deepCopy.d.ts.map +1 -0
  99. package/dist/utils/deepCopy.js +40 -0
  100. package/dist/utils/deepCopy.js.map +1 -0
  101. package/dist/utils/deepEqual.d.ts.map +1 -0
  102. package/dist/utils/deepEqual.js +65 -1
  103. package/dist/utils/deepEqual.js.map +1 -0
  104. package/dist/utils/export.d.ts.map +1 -0
  105. package/dist/utils/export.js +1 -0
  106. package/dist/utils/export.js.map +1 -0
  107. package/dist/utils/isvalid.d.ts.map +1 -0
  108. package/dist/utils/isvalid.js +86 -0
  109. package/dist/utils/isvalid.js.map +1 -0
  110. package/dist/utils/network.d.ts.map +1 -0
  111. package/dist/utils/network.js +77 -5
  112. package/dist/utils/network.js.map +1 -0
  113. package/dist/utils/parameter.d.ts.map +1 -0
  114. package/dist/utils/parameter.js +41 -0
  115. package/dist/utils/parameter.js.map +1 -0
  116. package/dist/utils/wait.d.ts +43 -0
  117. package/dist/utils/wait.d.ts.map +1 -0
  118. package/dist/utils/wait.js +48 -5
  119. package/dist/utils/wait.js.map +1 -0
  120. package/frontend/build/asset-manifest.json +3 -3
  121. package/frontend/build/index.html +1 -1
  122. package/frontend/build/static/js/{main.f87f07b6.js → main.f60aae10.js} +8 -8
  123. package/frontend/build/static/js/main.f60aae10.js.map +1 -0
  124. package/npm-shrinkwrap.json +44 -44
  125. package/package.json +2 -2
  126. package/frontend/build/static/js/main.f87f07b6.js.map +0 -1
  127. /package/frontend/build/static/js/{main.f87f07b6.js.LICENSE.txt → main.f60aae10.js.LICENSE.txt} +0 -0
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.2
8
+ *
9
+ * Copyright 2025, 2026, 2027 Luca Liguori.
10
+ *
11
+ * Licensed under the Apache License, Version 2.0 (the "License");
12
+ * you may not use this file except in compliance with the License.
13
+ * You may obtain a copy of the License at
14
+ *
15
+ * http://www.apache.org/licenses/LICENSE-2.0
16
+ *
17
+ * Unless required by applicable law or agreed to in writing, software
18
+ * distributed under the License is distributed on an "AS IS" BASIS,
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ * See the License for the specific language governing permissions and
21
+ * limitations under the License. *
22
+ */
23
+ // @matter
1
24
  import { EndpointServer, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat } from '@matter/main';
25
+ // Node modules
2
26
  import { createServer } from 'node:http';
3
27
  import https from 'https';
4
28
  import express from 'express';
@@ -6,19 +30,70 @@ import WebSocket, { WebSocketServer } from 'ws';
6
30
  import os from 'node:os';
7
31
  import path from 'node:path';
8
32
  import { promises as fs } from 'node:fs';
33
+ // AnsiLogger module
9
34
  import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';
35
+ // Matterbridge
10
36
  import { createZip, deepCopy, isValidNumber, isValidObject, isValidString } from './utils/export.js';
11
37
  import { plg } from './matterbridgeTypes.js';
12
38
  import { hasParameter } from './utils/export.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
+ * Websocket message ID indicating a cpu update.
56
+ * @constant {number}
57
+ */
16
58
  export const WS_ID_CPU_UPDATE = 3;
59
+ /**
60
+ * Websocket message ID indicating a memory update.
61
+ * @constant {number}
62
+ */
17
63
  export const WS_ID_MEMORY_UPDATE = 4;
64
+ /**
65
+ * Websocket message ID indicating an uptime update.
66
+ * @constant {number}
67
+ */
18
68
  export const WS_ID_UPTIME_UPDATE = 5;
69
+ /**
70
+ * Websocket message ID indicating a memory update.
71
+ * @constant {number}
72
+ */
19
73
  export const WS_ID_SNACKBAR = 6;
74
+ /**
75
+ * Websocket message ID indicating a shelly system update.
76
+ * check:
77
+ * curl -k http://127.0.0.1:8101/api/updates/sys/check
78
+ * perform:
79
+ * curl -k http://127.0.0.1:8101/api/updates/sys/perform
80
+ * @constant {number}
81
+ */
20
82
  export const WS_ID_SHELLY_SYS_UPDATE = 100;
83
+ /**
84
+ * Websocket message ID indicating a shelly main update.
85
+ * check:
86
+ * curl -k http://127.0.0.1:8101/api/updates/main/check
87
+ * perform:
88
+ * curl -k http://127.0.0.1:8101/api/updates/main/perform
89
+ * @constant {number}
90
+ */
21
91
  export const WS_ID_SHELLY_MAIN_UPDATE = 101;
92
+ /**
93
+ * Initializes the frontend of Matterbridge.
94
+ *
95
+ * @param port The port number to run the frontend server on. Default is 8283.
96
+ */
22
97
  export class Frontend {
23
98
  matterbridge;
24
99
  log;
@@ -35,7 +110,7 @@ export class Frontend {
35
110
  memoryTimeout;
36
111
  constructor(matterbridge) {
37
112
  this.matterbridge = matterbridge;
38
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
113
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
39
114
  }
40
115
  set logLevel(logLevel) {
41
116
  this.log.logLevel = logLevel;
@@ -43,10 +118,21 @@ export class Frontend {
43
118
  async start(port = 8283) {
44
119
  this.port = port;
45
120
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
121
+ // Create the express app that serves the frontend
46
122
  this.expressApp = express();
123
+ // Log all requests to the server for debugging
124
+ /*
125
+ this.expressApp.use((req, res, next) => {
126
+ this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
127
+ next();
128
+ });
129
+ */
130
+ // Serve static files from '/static' endpoint
47
131
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
48
132
  if (!hasParameter('ssl')) {
133
+ // Create an HTTP server and attach the express app
49
134
  this.httpServer = createServer(this.expressApp);
135
+ // Listen on the specified port
50
136
  if (hasParameter('ingress')) {
51
137
  this.httpServer.listen(this.port, '0.0.0.0', () => {
52
138
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -60,6 +146,7 @@ export class Frontend {
60
146
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
61
147
  });
62
148
  }
149
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
150
  this.httpServer.on('error', (error) => {
64
151
  this.log.error(`Frontend http server error listening on ${this.port}`);
65
152
  switch (error.code) {
@@ -75,6 +162,7 @@ export class Frontend {
75
162
  });
76
163
  }
77
164
  else {
165
+ // Load the SSL certificate, the private key and optionally the CA certificate
78
166
  let cert;
79
167
  try {
80
168
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
@@ -102,7 +190,9 @@ export class Frontend {
102
190
  this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
103
191
  }
104
192
  const serverOptions = { cert, key, ca };
193
+ // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
105
194
  this.httpsServer = https.createServer(serverOptions, this.expressApp);
195
+ // Listen on the specified port
106
196
  if (hasParameter('ingress')) {
107
197
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
108
198
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -116,6 +206,7 @@ export class Frontend {
116
206
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
117
207
  });
118
208
  }
209
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
119
210
  this.httpsServer.on('error', (error) => {
120
211
  this.log.error(`Frontend https server error listening on ${this.port}`);
121
212
  switch (error.code) {
@@ -132,16 +223,18 @@ export class Frontend {
132
223
  }
133
224
  if (this.initializeError)
134
225
  return;
226
+ // Create a WebSocket server and attach it to the http or https server
135
227
  const wssPort = this.port;
136
228
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
137
229
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
138
230
  this.webSocketServer.on('connection', (ws, request) => {
139
231
  const clientIp = request.socket.remoteAddress;
140
- let callbackLogLevel = "notice";
141
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
142
- callbackLogLevel = "info";
143
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
144
- callbackLogLevel = "debug";
232
+ // Set the global logger callback for the WebSocketServer
233
+ let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
234
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
235
+ callbackLogLevel = "info" /* LogLevel.INFO */;
236
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
237
+ callbackLogLevel = "debug" /* LogLevel.DEBUG */;
145
238
  AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
146
239
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
147
240
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -175,6 +268,7 @@ export class Frontend {
175
268
  this.webSocketServer.on('error', (ws, error) => {
176
269
  this.log.error(`WebSocketServer error: ${error}`);
177
270
  });
271
+ // Subscribe to cli events
178
272
  const { cliEmitter } = await import('./cli.js');
179
273
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
180
274
  this.wssSendUptimeUpdate(systemUptime, processUptime);
@@ -185,6 +279,7 @@ export class Frontend {
185
279
  cliEmitter.on('cpu', (cpuUsage) => {
186
280
  this.wssSendCpuUpdate(cpuUsage);
187
281
  });
282
+ // Endpoint to validate login code
188
283
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
189
284
  const { password } = req.body;
190
285
  this.log.debug('The frontend sent /api/login', password);
@@ -203,23 +298,27 @@ export class Frontend {
203
298
  this.log.warn('/api/login error wrong password');
204
299
  res.json({ valid: false });
205
300
  }
301
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
206
302
  }
207
303
  catch (error) {
208
304
  this.log.error('/api/login error getting password');
209
305
  res.json({ valid: false });
210
306
  }
211
307
  });
308
+ // Endpoint to provide health check
212
309
  this.expressApp.get('/health', (req, res) => {
213
310
  this.log.debug('Express received /health');
214
311
  const healthStatus = {
215
- status: 'ok',
216
- uptime: process.uptime(),
217
- timestamp: new Date().toISOString(),
312
+ status: 'ok', // Indicate service is healthy
313
+ uptime: process.uptime(), // Server uptime in seconds
314
+ timestamp: new Date().toISOString(), // Current timestamp
218
315
  };
219
316
  res.status(200).json(healthStatus);
220
317
  });
318
+ // Endpoint to provide memory usage details
221
319
  this.expressApp.get('/memory', async (req, res) => {
222
320
  this.log.debug('Express received /memory');
321
+ // Memory usage from process
223
322
  const memoryUsageRaw = process.memoryUsage();
224
323
  const memoryUsage = {
225
324
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -228,10 +327,13 @@ export class Frontend {
228
327
  external: this.formatMemoryUsage(memoryUsageRaw.external),
229
328
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
230
329
  };
330
+ // V8 heap statistics
231
331
  const { default: v8 } = await import('node:v8');
232
332
  const heapStatsRaw = v8.getHeapStatistics();
233
333
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
334
+ // Format heapStats
234
335
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
336
+ // Format heapSpaces
235
337
  const heapSpaces = heapSpacesRaw.map((space) => ({
236
338
  ...space,
237
339
  space_size: this.formatMemoryUsage(space.space_size),
@@ -249,6 +351,7 @@ export class Frontend {
249
351
  };
250
352
  res.status(200).json(memoryReport);
251
353
  });
354
+ // Endpoint to start advertising the server node
252
355
  this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
253
356
  const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
254
357
  if (pairingCodes) {
@@ -259,18 +362,22 @@ export class Frontend {
259
362
  res.status(500).json({ error: 'Failed to generate pairing codes' });
260
363
  }
261
364
  });
365
+ // Endpoint to provide settings
262
366
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
263
367
  this.log.debug('The frontend sent /api/settings');
264
368
  res.json(await this.getApiSettings());
265
369
  });
370
+ // Endpoint to provide plugins
266
371
  this.expressApp.get('/api/plugins', async (req, res) => {
267
372
  this.log.debug('The frontend sent /api/plugins');
268
373
  res.json(this.getBaseRegisteredPlugins());
269
374
  });
375
+ // Endpoint to provide devices
270
376
  this.expressApp.get('/api/devices', (req, res) => {
271
377
  this.log.debug('The frontend sent /api/devices');
272
378
  const devices = [];
273
379
  this.matterbridge.devices.forEach(async (device) => {
380
+ // Check if the device has the required properties
274
381
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
275
382
  return;
276
383
  const cluster = this.getClusterTextFromDevice(device);
@@ -288,6 +395,7 @@ export class Frontend {
288
395
  });
289
396
  res.json(devices);
290
397
  });
398
+ // Endpoint to provide the cluster servers of the devices
291
399
  this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
292
400
  const selectedPluginName = req.params.selectedPluginName;
293
401
  const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
@@ -360,6 +468,7 @@ export class Frontend {
360
468
  });
361
469
  res.json(data);
362
470
  });
471
+ // Endpoint to view the log
363
472
  this.expressApp.get('/api/view-log', async (req, res) => {
364
473
  this.log.debug('The frontend sent /api/log');
365
474
  try {
@@ -372,10 +481,12 @@ export class Frontend {
372
481
  res.status(500).send('Error reading log file');
373
482
  }
374
483
  });
484
+ // Endpoint to download the matterbridge log
375
485
  this.expressApp.get('/api/download-mblog', async (req, res) => {
376
486
  this.log.debug('The frontend sent /api/download-mblog');
377
487
  try {
378
488
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
489
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
379
490
  }
380
491
  catch (error) {
381
492
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -387,10 +498,12 @@ export class Frontend {
387
498
  }
388
499
  });
389
500
  });
501
+ // Endpoint to download the matter log
390
502
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
391
503
  this.log.debug('The frontend sent /api/download-mjlog');
392
504
  try {
393
505
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
506
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
394
507
  }
395
508
  catch (error) {
396
509
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -402,6 +515,7 @@ export class Frontend {
402
515
  }
403
516
  });
404
517
  });
518
+ // Endpoint to download the matter storage file
405
519
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
406
520
  this.log.debug('The frontend sent /api/download-mjstorage');
407
521
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
@@ -412,6 +526,7 @@ export class Frontend {
412
526
  }
413
527
  });
414
528
  });
529
+ // Endpoint to download the matterbridge storage directory
415
530
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
416
531
  this.log.debug('The frontend sent /api/download-mbstorage');
417
532
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
@@ -422,6 +537,7 @@ export class Frontend {
422
537
  }
423
538
  });
424
539
  });
540
+ // Endpoint to download the matterbridge plugin directory
425
541
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
426
542
  this.log.debug('The frontend sent /api/download-pluginstorage');
427
543
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
@@ -432,9 +548,11 @@ export class Frontend {
432
548
  }
433
549
  });
434
550
  });
551
+ // Endpoint to download the matterbridge plugin config files
435
552
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
436
553
  this.log.debug('The frontend sent /api/download-pluginconfig');
437
554
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
555
+ // 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')));
438
556
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
439
557
  if (error) {
440
558
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
@@ -442,6 +560,7 @@ export class Frontend {
442
560
  }
443
561
  });
444
562
  });
563
+ // Endpoint to download the matterbridge plugin config files
445
564
  this.expressApp.get('/api/download-backup', async (req, res) => {
446
565
  this.log.debug('The frontend sent /api/download-backup');
447
566
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -451,6 +570,7 @@ export class Frontend {
451
570
  }
452
571
  });
453
572
  });
573
+ // Endpoint to receive commands
454
574
  this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
455
575
  const command = req.params.command;
456
576
  let param = req.params.param;
@@ -460,13 +580,15 @@ export class Frontend {
460
580
  return;
461
581
  }
462
582
  this.log.debug(`Received frontend command: ${command}:${param}`);
583
+ // Handle the command setpassword from Settings
463
584
  if (command === 'setpassword') {
464
- const password = param.slice(1, -1);
585
+ const password = param.slice(1, -1); // Remove the first and last characters
465
586
  this.log.debug('setpassword', param, password);
466
587
  await this.matterbridge.nodeContext?.set('password', password);
467
588
  res.json({ message: 'Command received' });
468
589
  return;
469
590
  }
591
+ // Handle the command setbridgemode from Settings
470
592
  if (command === 'setbridgemode') {
471
593
  this.log.debug(`setbridgemode: ${param}`);
472
594
  this.wssSendRestartRequired();
@@ -474,6 +596,7 @@ export class Frontend {
474
596
  res.json({ message: 'Command received' });
475
597
  return;
476
598
  }
599
+ // Handle the command backup from Settings
477
600
  if (command === 'backup') {
478
601
  this.log.notice(`Prepairing the backup...`);
479
602
  await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
@@ -482,31 +605,33 @@ export class Frontend {
482
605
  res.json({ message: 'Command received' });
483
606
  return;
484
607
  }
608
+ // Handle the command setmbloglevel from Settings
485
609
  if (command === 'setmbloglevel') {
486
610
  this.log.debug('Matterbridge log level:', param);
487
611
  if (param === 'Debug') {
488
- this.log.logLevel = "debug";
612
+ this.log.logLevel = "debug" /* LogLevel.DEBUG */;
489
613
  }
490
614
  else if (param === 'Info') {
491
- this.log.logLevel = "info";
615
+ this.log.logLevel = "info" /* LogLevel.INFO */;
492
616
  }
493
617
  else if (param === 'Notice') {
494
- this.log.logLevel = "notice";
618
+ this.log.logLevel = "notice" /* LogLevel.NOTICE */;
495
619
  }
496
620
  else if (param === 'Warn') {
497
- this.log.logLevel = "warn";
621
+ this.log.logLevel = "warn" /* LogLevel.WARN */;
498
622
  }
499
623
  else if (param === 'Error') {
500
- this.log.logLevel = "error";
624
+ this.log.logLevel = "error" /* LogLevel.ERROR */;
501
625
  }
502
626
  else if (param === 'Fatal') {
503
- this.log.logLevel = "fatal";
627
+ this.log.logLevel = "fatal" /* LogLevel.FATAL */;
504
628
  }
505
629
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
506
630
  await this.matterbridge.setLogLevel(this.log.logLevel);
507
631
  res.json({ message: 'Command received' });
508
632
  return;
509
633
  }
634
+ // Handle the command setmbloglevel from Settings
510
635
  if (command === 'setmjloglevel') {
511
636
  this.log.debug('Matter.js log level:', param);
512
637
  if (param === 'Debug') {
@@ -531,30 +656,34 @@ export class Frontend {
531
656
  res.json({ message: 'Command received' });
532
657
  return;
533
658
  }
659
+ // Handle the command setmdnsinterface from Settings
534
660
  if (command === 'setmdnsinterface') {
535
- param = param.slice(1, -1);
661
+ param = param.slice(1, -1); // Remove the first and last characters *mdns*
536
662
  this.matterbridge.matterbridgeInformation.mattermdnsinterface = param;
537
663
  this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
538
664
  await this.matterbridge.nodeContext?.set('mattermdnsinterface', param);
539
665
  res.json({ message: 'Command received' });
540
666
  return;
541
667
  }
668
+ // Handle the command setipv4address from Settings
542
669
  if (command === 'setipv4address') {
543
- param = param.slice(1, -1);
670
+ param = param.slice(1, -1); // Remove the first and last characters *ip*
544
671
  this.matterbridge.matterbridgeInformation.matteripv4address = param;
545
672
  this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
546
673
  await this.matterbridge.nodeContext?.set('matteripv4address', param);
547
674
  res.json({ message: 'Command received' });
548
675
  return;
549
676
  }
677
+ // Handle the command setipv6address from Settings
550
678
  if (command === 'setipv6address') {
551
- param = param.slice(1, -1);
679
+ param = param.slice(1, -1); // Remove the first and last characters *ip*
552
680
  this.matterbridge.matterbridgeInformation.matteripv6address = param;
553
681
  this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
554
682
  await this.matterbridge.nodeContext?.set('matteripv6address', param);
555
683
  res.json({ message: 'Command received' });
556
684
  return;
557
685
  }
686
+ // Handle the command setmatterport from Settings
558
687
  if (command === 'setmatterport') {
559
688
  const port = Math.min(Math.max(parseInt(param), 5540), 5560);
560
689
  this.matterbridge.matterbridgeInformation.matterPort = port;
@@ -563,6 +692,7 @@ export class Frontend {
563
692
  res.json({ message: 'Command received' });
564
693
  return;
565
694
  }
695
+ // Handle the command setmatterdiscriminator from Settings
566
696
  if (command === 'setmatterdiscriminator') {
567
697
  const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
568
698
  this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
@@ -571,6 +701,7 @@ export class Frontend {
571
701
  res.json({ message: 'Command received' });
572
702
  return;
573
703
  }
704
+ // Handle the command setmatterpasscode from Settings
574
705
  if (command === 'setmatterpasscode') {
575
706
  const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
576
707
  this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
@@ -579,17 +710,20 @@ export class Frontend {
579
710
  res.json({ message: 'Command received' });
580
711
  return;
581
712
  }
713
+ // Handle the command setmbloglevel from Settings
582
714
  if (command === 'setmblogfile') {
583
715
  this.log.debug('Matterbridge file log:', param);
584
716
  this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
585
717
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
718
+ // Create the file logger for matterbridge
586
719
  if (param === 'true')
587
- AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
720
+ AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
588
721
  else
589
722
  AnsiLogger.setGlobalLogfile(undefined);
590
723
  res.json({ message: 'Command received' });
591
724
  return;
592
725
  }
726
+ // Handle the command setmbloglevel from Settings
593
727
  if (command === 'setmjlogfile') {
594
728
  this.log.debug('Matter file log:', param);
595
729
  this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
@@ -616,40 +750,48 @@ export class Frontend {
616
750
  res.json({ message: 'Command received' });
617
751
  return;
618
752
  }
753
+ // Handle the command unregister from Settings
619
754
  if (command === 'unregister') {
620
755
  await this.matterbridge.unregisterAndShutdownProcess();
621
756
  res.json({ message: 'Command received' });
622
757
  return;
623
758
  }
759
+ // Handle the command reset from Settings
624
760
  if (command === 'reset') {
625
761
  await this.matterbridge.shutdownProcessAndReset();
626
762
  res.json({ message: 'Command received' });
627
763
  return;
628
764
  }
765
+ // Handle the command factoryreset from Settings
629
766
  if (command === 'factoryreset') {
630
767
  await this.matterbridge.shutdownProcessAndFactoryReset();
631
768
  res.json({ message: 'Command received' });
632
769
  return;
633
770
  }
771
+ // Handle the command shutdown from Header
634
772
  if (command === 'shutdown') {
635
773
  await this.matterbridge.shutdownProcess();
636
774
  res.json({ message: 'Command received' });
637
775
  return;
638
776
  }
777
+ // Handle the command restart from Header
639
778
  if (command === 'restart') {
640
779
  await this.matterbridge.restartProcess();
641
780
  res.json({ message: 'Command received' });
642
781
  return;
643
782
  }
783
+ // Handle the command update from Header
644
784
  if (command === 'update') {
645
785
  await this.matterbridge.updateProcess();
646
786
  this.wssSendRestartRequired();
647
787
  res.json({ message: 'Command received' });
648
788
  return;
649
789
  }
790
+ // Handle the command saveconfig from Home
650
791
  if (command === 'saveconfig') {
651
792
  param = param.replace(/\*/g, '\\');
652
793
  this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
794
+ // console.log('Req.body:', JSON.stringify(req.body, null, 2));
653
795
  if (!this.matterbridge.plugins.has(param)) {
654
796
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
655
797
  }
@@ -664,6 +806,7 @@ export class Frontend {
664
806
  res.json({ message: 'Command received' });
665
807
  return;
666
808
  }
809
+ // Handle the command installplugin from Home
667
810
  if (command === 'installplugin') {
668
811
  param = param.replace(/\*/g, '\\');
669
812
  this.log.info(`Installing plugin ${plg}${param}${nf}...`);
@@ -672,6 +815,7 @@ export class Frontend {
672
815
  await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
673
816
  this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
674
817
  this.wssSendSnackbarMessage(`Installed package ${param}`);
818
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
675
819
  }
676
820
  catch (error) {
677
821
  this.log.error(`Error installing plugin ${plg}${param}${er}`);
@@ -680,16 +824,21 @@ export class Frontend {
680
824
  this.wssSendSnackbarMessage(`Restart required`, 0);
681
825
  this.wssSendRestartRequired();
682
826
  param = param.split('@')[0];
827
+ // Also add the plugin to matterbridge so no return!
683
828
  if (param === 'matterbridge') {
829
+ // 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
684
830
  res.json({ message: 'Command received' });
685
831
  return;
686
832
  }
687
833
  }
834
+ // Handle the command addplugin from Home
688
835
  if (command === 'addplugin' || command === 'installplugin') {
689
836
  param = param.replace(/\*/g, '\\');
690
837
  const plugin = await this.matterbridge.plugins.add(param);
691
838
  if (plugin) {
692
839
  if (this.matterbridge.bridgeMode === 'childbridge') {
840
+ // 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
841
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
693
842
  this.matterbridge.createDynamicPlugin(plugin, true);
694
843
  }
695
844
  this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
@@ -699,19 +848,21 @@ export class Frontend {
699
848
  res.json({ message: 'Command received' });
700
849
  return;
701
850
  }
851
+ // Handle the command removeplugin from Home
702
852
  if (command === 'removeplugin') {
703
853
  if (!this.matterbridge.plugins.has(param)) {
704
854
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
705
855
  }
706
856
  else {
707
857
  const plugin = this.matterbridge.plugins.get(param);
708
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
858
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true); // This will also close the server node in childbridge mode
709
859
  await this.matterbridge.plugins.remove(param);
710
860
  }
711
861
  res.json({ message: 'Command received' });
712
862
  this.wssSendRefreshRequired();
713
863
  return;
714
864
  }
865
+ // Handle the command enableplugin from Home
715
866
  if (command === 'enableplugin') {
716
867
  if (!this.matterbridge.plugins.has(param)) {
717
868
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -729,6 +880,7 @@ export class Frontend {
729
880
  plugin.addedDevices = undefined;
730
881
  await this.matterbridge.plugins.enable(param);
731
882
  if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
883
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
732
884
  this.matterbridge.createDynamicPlugin(plugin, true);
733
885
  }
734
886
  this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
@@ -740,6 +892,7 @@ export class Frontend {
740
892
  this.wssSendRefreshRequired();
741
893
  return;
742
894
  }
895
+ // Handle the command disableplugin from Home
743
896
  if (command === 'disableplugin') {
744
897
  if (!this.matterbridge.plugins.has(param)) {
745
898
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -747,7 +900,7 @@ export class Frontend {
747
900
  else {
748
901
  const plugin = this.matterbridge.plugins.get(param);
749
902
  if (plugin && plugin.enabled) {
750
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
903
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true); // This will also close the server node in childbridge mode
751
904
  await this.matterbridge.plugins.disable(param);
752
905
  }
753
906
  }
@@ -756,6 +909,7 @@ export class Frontend {
756
909
  return;
757
910
  }
758
911
  });
912
+ // Fallback for routing (must be the last route)
759
913
  this.expressApp.get('*', (req, res) => {
760
914
  this.log.debug('The frontend sent:', req.url);
761
915
  this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -764,24 +918,29 @@ export class Frontend {
764
918
  this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
765
919
  }
766
920
  async stop() {
921
+ // Close the http server
767
922
  if (this.httpServer) {
768
923
  this.httpServer.close();
769
924
  this.httpServer.removeAllListeners();
770
925
  this.httpServer = undefined;
771
926
  this.log.debug('Frontend http server closed successfully');
772
927
  }
928
+ // Close the https server
773
929
  if (this.httpsServer) {
774
930
  this.httpsServer.close();
775
931
  this.httpsServer.removeAllListeners();
776
932
  this.httpsServer = undefined;
777
933
  this.log.debug('Frontend https server closed successfully');
778
934
  }
935
+ // Remove listeners from the express app
779
936
  if (this.expressApp) {
780
937
  this.expressApp.removeAllListeners();
781
938
  this.expressApp = undefined;
782
939
  this.log.debug('Frontend app closed successfully');
783
940
  }
941
+ // Close the WebSocket server
784
942
  if (this.webSocketServer) {
943
+ // Close all active connections
785
944
  this.webSocketServer.clients.forEach((client) => {
786
945
  if (client.readyState === WebSocket.OPEN) {
787
946
  client.close();
@@ -798,6 +957,7 @@ export class Frontend {
798
957
  this.webSocketServer = undefined;
799
958
  }
800
959
  }
960
+ // Function to format bytes to KB, MB, or GB
801
961
  formatMemoryUsage = (bytes) => {
802
962
  if (bytes >= 1024 ** 3) {
803
963
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -809,6 +969,7 @@ export class Frontend {
809
969
  return `${(bytes / 1024).toFixed(2)} KB`;
810
970
  }
811
971
  };
972
+ // Function to format system uptime with only the most significant unit
812
973
  formatOsUpTime = (seconds) => {
813
974
  if (seconds >= 86400) {
814
975
  const days = Math.floor(seconds / 86400);
@@ -824,8 +985,13 @@ export class Frontend {
824
985
  }
825
986
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
826
987
  };
988
+ /**
989
+ * Retrieves the api settings data.
990
+ * @returns {Promise<object>} A promise that resolve in the api settings object.
991
+ */
827
992
  async getApiSettings() {
828
993
  const { lastCpuUsage } = await import('./cli.js');
994
+ // Update the system information
829
995
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
830
996
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
831
997
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -834,6 +1000,7 @@ export class Frontend {
834
1000
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
835
1001
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
836
1002
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
1003
+ // Update the matterbridge information
837
1004
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
838
1005
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
839
1006
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
@@ -852,6 +1019,11 @@ export class Frontend {
852
1019
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
853
1020
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
854
1021
  }
1022
+ /**
1023
+ * Retrieves the cluster text description from a given device.
1024
+ * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
1025
+ * @returns {string} The attributes description of the cluster servers in the device.
1026
+ */
855
1027
  getClusterTextFromDevice(device) {
856
1028
  const getAttribute = (device, cluster, attribute) => {
857
1029
  let value = undefined;
@@ -890,6 +1062,7 @@ export class Frontend {
890
1062
  };
891
1063
  let attributes = '';
892
1064
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1065
+ // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
893
1066
  if (typeof attributeValue === 'undefined')
894
1067
  return;
895
1068
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -967,8 +1140,13 @@ export class Frontend {
967
1140
  if (clusterName === 'userLabel' && attributeName === 'labelList')
968
1141
  attributes += `${getUserLabel(device)} `;
969
1142
  });
1143
+ // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
970
1144
  return attributes.trimStart().trimEnd();
971
1145
  }
1146
+ /**
1147
+ * Retrieves the base registered plugins sanitized for res.json().
1148
+ * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
1149
+ */
972
1150
  getBaseRegisteredPlugins() {
973
1151
  const baseRegisteredPlugins = [];
974
1152
  for (const plugin of this.matterbridge.plugins) {
@@ -999,6 +1177,13 @@ export class Frontend {
999
1177
  }
1000
1178
  return baseRegisteredPlugins;
1001
1179
  }
1180
+ /**
1181
+ * Handles incoming websocket messages for the Matterbridge frontend.
1182
+ *
1183
+ * @param {WebSocket} client - The websocket client that sent the message.
1184
+ * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1185
+ * @returns {Promise<void>} A promise that resolves when the message has been handled.
1186
+ */
1002
1187
  async wsMessageHandler(client, message) {
1003
1188
  let data;
1004
1189
  try {
@@ -1139,8 +1324,10 @@ export class Frontend {
1139
1324
  else if (data.method === '/api/devices') {
1140
1325
  const devices = [];
1141
1326
  this.matterbridge.devices.forEach(async (device) => {
1327
+ // Filter by pluginName if provided
1142
1328
  if (data.params.pluginName && data.params.pluginName !== device.plugin)
1143
1329
  return;
1330
+ // Check if the device has the required properties
1144
1331
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1145
1332
  return;
1146
1333
  const cluster = this.getClusterTextFromDevice(device);
@@ -1223,6 +1410,7 @@ export class Frontend {
1223
1410
  });
1224
1411
  endpointServer.getChildEndpoints().forEach((childEndpoint) => {
1225
1412
  deviceTypes = [];
1413
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1226
1414
  const name = childEndpoint.endpoint?.id;
1227
1415
  const clusterServers = childEndpoint.getAllClusterServers();
1228
1416
  clusterServers.forEach((clusterServer) => {
@@ -1305,85 +1493,139 @@ export class Frontend {
1305
1493
  return;
1306
1494
  }
1307
1495
  }
1496
+ /**
1497
+ * Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1498
+ *
1499
+ * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1500
+ * @param {string} time - The time string of the message
1501
+ * @param {string} name - The logger name of the message
1502
+ * @param {string} message - The content of the message.
1503
+ */
1308
1504
  wssSendMessage(level, time, name, message) {
1309
1505
  if (!level || !time || !name || !message)
1310
1506
  return;
1507
+ // Remove ANSI escape codes from the message
1508
+ // eslint-disable-next-line no-control-regex
1311
1509
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1510
+ // Remove leading asterisks from the message
1312
1511
  message = message.replace(/^\*+/, '');
1512
+ // Replace all occurrences of \t and \n
1313
1513
  message = message.replace(/[\t\n]/g, '');
1514
+ // Remove non-printable characters
1515
+ // eslint-disable-next-line no-control-regex
1314
1516
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1517
+ // Replace all occurrences of \" with "
1315
1518
  message = message.replace(/\\"/g, '"');
1519
+ // Define the maximum allowed length for continuous characters without a space
1316
1520
  const maxContinuousLength = 100;
1317
1521
  const keepStartLength = 20;
1318
1522
  const keepEndLength = 20;
1523
+ // Split the message into words
1319
1524
  message = message
1320
1525
  .split(' ')
1321
1526
  .map((word) => {
1527
+ // If the word length exceeds the max continuous length, insert spaces and truncate
1322
1528
  if (word.length > maxContinuousLength) {
1323
1529
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1324
1530
  }
1325
1531
  return word;
1326
1532
  })
1327
1533
  .join(' ');
1534
+ // Send the message to all connected clients
1328
1535
  this.webSocketServer?.clients.forEach((client) => {
1329
1536
  if (client.readyState === WebSocket.OPEN) {
1330
1537
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1331
1538
  }
1332
1539
  });
1333
1540
  }
1541
+ /**
1542
+ * Sends a need to refresh WebSocket message to all connected clients.
1543
+ *
1544
+ */
1334
1545
  wssSendRefreshRequired() {
1335
1546
  this.log.debug('Sending a refresh required message to all connected clients');
1336
1547
  this.matterbridge.matterbridgeInformation.refreshRequired = true;
1548
+ // Send the message to all connected clients
1337
1549
  this.webSocketServer?.clients.forEach((client) => {
1338
1550
  if (client.readyState === WebSocket.OPEN) {
1339
1551
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: {} }));
1340
1552
  }
1341
1553
  });
1342
1554
  }
1555
+ /**
1556
+ * Sends a need to restart WebSocket message to all connected clients.
1557
+ *
1558
+ */
1343
1559
  wssSendRestartRequired() {
1344
1560
  this.log.debug('Sending a restart required message to all connected clients');
1345
1561
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1346
1562
  this.wssSendSnackbarMessage(`Restart required`, 0);
1563
+ // Send the message to all connected clients
1347
1564
  this.webSocketServer?.clients.forEach((client) => {
1348
1565
  if (client.readyState === WebSocket.OPEN) {
1349
1566
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
1350
1567
  }
1351
1568
  });
1352
1569
  }
1570
+ /**
1571
+ * Sends a memory update message to all connected clients.
1572
+ *
1573
+ */
1353
1574
  wssSendCpuUpdate(cpuUsage) {
1354
1575
  this.log.debug('Sending a cpu update message to all connected clients');
1576
+ // Send the message to all connected clients
1355
1577
  this.webSocketServer?.clients.forEach((client) => {
1356
1578
  if (client.readyState === WebSocket.OPEN) {
1357
1579
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
1358
1580
  }
1359
1581
  });
1360
1582
  }
1583
+ /**
1584
+ * Sends a cpu update message to all connected clients.
1585
+ *
1586
+ */
1361
1587
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
1362
1588
  this.log.debug('Sending a memory update message to all connected clients');
1589
+ // Send the message to all connected clients
1363
1590
  this.webSocketServer?.clients.forEach((client) => {
1364
1591
  if (client.readyState === WebSocket.OPEN) {
1365
1592
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
1366
1593
  }
1367
1594
  });
1368
1595
  }
1596
+ /**
1597
+ * Sends a memory update message to all connected clients.
1598
+ *
1599
+ */
1369
1600
  wssSendUptimeUpdate(systemUptime, processUptime) {
1370
1601
  this.log.debug('Sending a uptime update message to all connected clients');
1602
+ // Send the message to all connected clients
1371
1603
  this.webSocketServer?.clients.forEach((client) => {
1372
1604
  if (client.readyState === WebSocket.OPEN) {
1373
1605
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
1374
1606
  }
1375
1607
  });
1376
1608
  }
1609
+ /**
1610
+ * Sends a cpu update message to all connected clients.
1611
+ *
1612
+ */
1377
1613
  wssSendSnackbarMessage(message, timeout = 5) {
1378
1614
  this.log.debug('Sending a snackbar message to all connected clients');
1615
+ // Send the message to all connected clients
1379
1616
  this.webSocketServer?.clients.forEach((client) => {
1380
1617
  if (client.readyState === WebSocket.OPEN) {
1381
1618
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { message, timeout } }));
1382
1619
  }
1383
1620
  });
1384
1621
  }
1622
+ /**
1623
+ * Sends a message to all connected clients.
1624
+ *
1625
+ */
1385
1626
  wssBroadcastMessage(id, method, params) {
1386
1627
  this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
1628
+ // Send the message to all connected clients
1387
1629
  this.webSocketServer?.clients.forEach((client) => {
1388
1630
  if (client.readyState === WebSocket.OPEN) {
1389
1631
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -1391,3 +1633,4 @@ export class Frontend {
1391
1633
  });
1392
1634
  }
1393
1635
  }
1636
+ //# sourceMappingURL=frontend.js.map