matterbridge 3.0.1 → 3.0.2-dev-20250509-ae61aa7

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