matterbridge 2.2.2 → 2.2.4-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/cli.js +2 -37
  3. package/dist/cluster/export.js +0 -2
  4. package/dist/defaultConfigSchema.js +0 -23
  5. package/dist/deviceManager.js +1 -94
  6. package/dist/frontend.js +31 -306
  7. package/dist/index.js +1 -28
  8. package/dist/logger/export.js +0 -1
  9. package/dist/matter/behaviors.js +0 -2
  10. package/dist/matter/clusters.js +0 -2
  11. package/dist/matter/devices.js +0 -2
  12. package/dist/matter/endpoints.js +0 -2
  13. package/dist/matter/export.js +0 -2
  14. package/dist/matter/types.js +0 -2
  15. package/dist/matterbridge.js +69 -727
  16. package/dist/matterbridgeAccessoryPlatform.js +0 -33
  17. package/dist/matterbridgeBehaviors.js +1 -32
  18. package/dist/matterbridgeDeviceTypes.js +11 -112
  19. package/dist/matterbridgeDynamicPlatform.js +0 -33
  20. package/dist/matterbridgeEndpoint.js +6 -690
  21. package/dist/matterbridgeEndpointHelpers.js +9 -118
  22. package/dist/matterbridgePlatform.js +7 -185
  23. package/dist/matterbridgeTypes.js +0 -24
  24. package/dist/pluginManager.js +3 -229
  25. package/dist/shelly.js +6 -121
  26. package/dist/storage/export.js +0 -1
  27. package/dist/update.js +0 -45
  28. package/dist/utils/colorUtils.js +2 -205
  29. package/dist/utils/copyDirectory.js +1 -37
  30. package/dist/utils/createZip.js +2 -42
  31. package/dist/utils/deepCopy.js +0 -40
  32. package/dist/utils/deepEqual.js +1 -65
  33. package/dist/utils/export.js +0 -1
  34. package/dist/utils/isvalid.js +0 -86
  35. package/dist/utils/network.js +5 -77
  36. package/dist/utils/parameter.js +0 -41
  37. package/dist/utils/wait.js +5 -48
  38. package/frontend/build/asset-manifest.json +3 -3
  39. package/frontend/build/index.html +1 -1
  40. package/frontend/build/static/js/{main.6bd178dd.js → main.819c0908.js} +4 -4
  41. package/frontend/build/static/js/{main.6bd178dd.js.map → main.819c0908.js.map} +1 -1
  42. package/npm-shrinkwrap.json +2 -2
  43. package/package.json +1 -2
  44. package/dist/cli.d.ts +0 -29
  45. package/dist/cli.d.ts.map +0 -1
  46. package/dist/cli.js.map +0 -1
  47. package/dist/cluster/export.d.ts +0 -2
  48. package/dist/cluster/export.d.ts.map +0 -1
  49. package/dist/cluster/export.js.map +0 -1
  50. package/dist/defaultConfigSchema.d.ts +0 -27
  51. package/dist/defaultConfigSchema.d.ts.map +0 -1
  52. package/dist/defaultConfigSchema.js.map +0 -1
  53. package/dist/deviceManager.d.ts +0 -114
  54. package/dist/deviceManager.d.ts.map +0 -1
  55. package/dist/deviceManager.js.map +0 -1
  56. package/dist/frontend.d.ts +0 -201
  57. package/dist/frontend.d.ts.map +0 -1
  58. package/dist/frontend.js.map +0 -1
  59. package/dist/index.d.ts +0 -35
  60. package/dist/index.d.ts.map +0 -1
  61. package/dist/index.js.map +0 -1
  62. package/dist/logger/export.d.ts +0 -2
  63. package/dist/logger/export.d.ts.map +0 -1
  64. package/dist/logger/export.js.map +0 -1
  65. package/dist/matter/behaviors.d.ts +0 -2
  66. package/dist/matter/behaviors.d.ts.map +0 -1
  67. package/dist/matter/behaviors.js.map +0 -1
  68. package/dist/matter/clusters.d.ts +0 -2
  69. package/dist/matter/clusters.d.ts.map +0 -1
  70. package/dist/matter/clusters.js.map +0 -1
  71. package/dist/matter/devices.d.ts +0 -2
  72. package/dist/matter/devices.d.ts.map +0 -1
  73. package/dist/matter/devices.js.map +0 -1
  74. package/dist/matter/endpoints.d.ts +0 -2
  75. package/dist/matter/endpoints.d.ts.map +0 -1
  76. package/dist/matter/endpoints.js.map +0 -1
  77. package/dist/matter/export.d.ts +0 -5
  78. package/dist/matter/export.d.ts.map +0 -1
  79. package/dist/matter/export.js.map +0 -1
  80. package/dist/matter/types.d.ts +0 -3
  81. package/dist/matter/types.d.ts.map +0 -1
  82. package/dist/matter/types.js.map +0 -1
  83. package/dist/matterbridge.d.ts +0 -412
  84. package/dist/matterbridge.d.ts.map +0 -1
  85. package/dist/matterbridge.js.map +0 -1
  86. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
  87. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  88. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  89. package/dist/matterbridgeBehaviors.d.ts +0 -1056
  90. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  91. package/dist/matterbridgeBehaviors.js.map +0 -1
  92. package/dist/matterbridgeDeviceTypes.d.ts +0 -177
  93. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  94. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  95. package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
  96. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  97. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  98. package/dist/matterbridgeEndpoint.d.ts +0 -835
  99. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  100. package/dist/matterbridgeEndpoint.js.map +0 -1
  101. package/dist/matterbridgeEndpointHelpers.d.ts +0 -2275
  102. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  103. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  104. package/dist/matterbridgePlatform.d.ts +0 -251
  105. package/dist/matterbridgePlatform.d.ts.map +0 -1
  106. package/dist/matterbridgePlatform.js.map +0 -1
  107. package/dist/matterbridgeTypes.d.ts +0 -177
  108. package/dist/matterbridgeTypes.d.ts.map +0 -1
  109. package/dist/matterbridgeTypes.js.map +0 -1
  110. package/dist/pluginManager.d.ts +0 -236
  111. package/dist/pluginManager.d.ts.map +0 -1
  112. package/dist/pluginManager.js.map +0 -1
  113. package/dist/shelly.d.ts +0 -77
  114. package/dist/shelly.d.ts.map +0 -1
  115. package/dist/shelly.js.map +0 -1
  116. package/dist/storage/export.d.ts +0 -2
  117. package/dist/storage/export.d.ts.map +0 -1
  118. package/dist/storage/export.js.map +0 -1
  119. package/dist/update.d.ts +0 -32
  120. package/dist/update.d.ts.map +0 -1
  121. package/dist/update.js.map +0 -1
  122. package/dist/utils/colorUtils.d.ts +0 -61
  123. package/dist/utils/colorUtils.d.ts.map +0 -1
  124. package/dist/utils/colorUtils.js.map +0 -1
  125. package/dist/utils/copyDirectory.d.ts +0 -32
  126. package/dist/utils/copyDirectory.d.ts.map +0 -1
  127. package/dist/utils/copyDirectory.js.map +0 -1
  128. package/dist/utils/createZip.d.ts +0 -38
  129. package/dist/utils/createZip.d.ts.map +0 -1
  130. package/dist/utils/createZip.js.map +0 -1
  131. package/dist/utils/deepCopy.d.ts +0 -31
  132. package/dist/utils/deepCopy.d.ts.map +0 -1
  133. package/dist/utils/deepCopy.js.map +0 -1
  134. package/dist/utils/deepEqual.d.ts +0 -53
  135. package/dist/utils/deepEqual.d.ts.map +0 -1
  136. package/dist/utils/deepEqual.js.map +0 -1
  137. package/dist/utils/export.d.ts +0 -10
  138. package/dist/utils/export.d.ts.map +0 -1
  139. package/dist/utils/export.js.map +0 -1
  140. package/dist/utils/isvalid.d.ts +0 -87
  141. package/dist/utils/isvalid.d.ts.map +0 -1
  142. package/dist/utils/isvalid.js.map +0 -1
  143. package/dist/utils/network.d.ts +0 -70
  144. package/dist/utils/network.d.ts.map +0 -1
  145. package/dist/utils/network.js.map +0 -1
  146. package/dist/utils/parameter.d.ts +0 -44
  147. package/dist/utils/parameter.d.ts.map +0 -1
  148. package/dist/utils/parameter.js.map +0 -1
  149. package/dist/utils/wait.d.ts +0 -43
  150. package/dist/utils/wait.d.ts.map +0 -1
  151. package/dist/utils/wait.js.map +0 -1
  152. /package/frontend/build/static/js/{main.6bd178dd.js.LICENSE.txt → main.819c0908.js.LICENSE.txt} +0 -0
package/dist/frontend.js CHANGED
@@ -1,28 +1,4 @@
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 os from 'node:os';
28
4
  import path from 'node:path';
@@ -31,70 +7,21 @@ import https from 'https';
31
7
  import express from 'express';
32
8
  import WebSocket, { WebSocketServer } from 'ws';
33
9
  import multer from 'multer';
34
- // AnsiLogger module
35
10
  import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';
36
- // Matterbridge
37
11
  import { createZip, deepCopy, isValidArray, isValidNumber, isValidObject, isValidString } from './utils/export.js';
38
12
  import { plg } from './matterbridgeTypes.js';
39
13
  import { hasParameter } from './utils/export.js';
40
14
  import { BridgedDeviceBasicInformation } from '@matter/main/clusters';
41
- /**
42
- * Websocket message ID for logging.
43
- * @constant {number}
44
- */
45
15
  export const WS_ID_LOG = 0;
46
- /**
47
- * Websocket message ID indicating a refresh is needed.
48
- * @constant {number}
49
- */
50
16
  export const WS_ID_REFRESH_NEEDED = 1;
51
- /**
52
- * Websocket message ID indicating a restart is needed.
53
- * @constant {number}
54
- */
55
17
  export const WS_ID_RESTART_NEEDED = 2;
56
- /**
57
- * Websocket message ID indicating a cpu update.
58
- * @constant {number}
59
- */
60
18
  export const WS_ID_CPU_UPDATE = 3;
61
- /**
62
- * Websocket message ID indicating a memory update.
63
- * @constant {number}
64
- */
65
19
  export const WS_ID_MEMORY_UPDATE = 4;
66
- /**
67
- * Websocket message ID indicating an uptime update.
68
- * @constant {number}
69
- */
70
20
  export const WS_ID_UPTIME_UPDATE = 5;
71
- /**
72
- * Websocket message ID indicating a memory update.
73
- * @constant {number}
74
- */
75
21
  export const WS_ID_SNACKBAR = 6;
76
- /**
77
- * Websocket message ID indicating a memory update.
78
- * @constant {number}
79
- */
80
22
  export const WS_ID_UPDATE_NEEDED = 7;
81
- /**
82
- * Websocket message ID indicating a shelly system update.
83
- * check:
84
- * curl -k http://127.0.0.1:8101/api/updates/sys/check
85
- * perform:
86
- * curl -k http://127.0.0.1:8101/api/updates/sys/perform
87
- * @constant {number}
88
- */
23
+ export const WS_ID_STATEUPDATE = 8;
89
24
  export const WS_ID_SHELLY_SYS_UPDATE = 100;
90
- /**
91
- * Websocket message ID indicating a shelly main update.
92
- * check:
93
- * curl -k http://127.0.0.1:8101/api/updates/main/check
94
- * perform:
95
- * curl -k http://127.0.0.1:8101/api/updates/main/perform
96
- * @constant {number}
97
- */
98
25
  export const WS_ID_SHELLY_MAIN_UPDATE = 101;
99
26
  export class Frontend {
100
27
  matterbridge;
@@ -112,7 +39,7 @@ export class Frontend {
112
39
  memoryTimeout;
113
40
  constructor(matterbridge) {
114
41
  this.matterbridge = matterbridge;
115
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
42
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
116
43
  }
117
44
  set logLevel(logLevel) {
118
45
  this.log.logLevel = logLevel;
@@ -120,25 +47,13 @@ export class Frontend {
120
47
  async start(port = 8283) {
121
48
  this.port = port;
122
49
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
123
- // Initialize multer with the upload directory
124
50
  const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
125
51
  await fs.mkdir(uploadDir, { recursive: true });
126
52
  const upload = multer({ dest: uploadDir });
127
- // Create the express app that serves the frontend
128
53
  this.expressApp = express();
129
- // Log all requests to the server for debugging
130
- /*
131
- this.expressApp.use((req, res, next) => {
132
- this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
133
- next();
134
- });
135
- */
136
- // Serve static files from '/static' endpoint
137
54
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
138
55
  if (!hasParameter('ssl')) {
139
- // Create an HTTP server and attach the express app
140
56
  this.httpServer = createServer(this.expressApp);
141
- // Listen on the specified port
142
57
  if (hasParameter('ingress')) {
143
58
  this.httpServer.listen(this.port, '0.0.0.0', () => {
144
59
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -152,7 +67,6 @@ export class Frontend {
152
67
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
153
68
  });
154
69
  }
155
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
70
  this.httpServer.on('error', (error) => {
157
71
  this.log.error(`Frontend http server error listening on ${this.port}`);
158
72
  switch (error.code) {
@@ -168,7 +82,6 @@ export class Frontend {
168
82
  });
169
83
  }
170
84
  else {
171
- // Load the SSL certificate, the private key and optionally the CA certificate
172
85
  let cert;
173
86
  try {
174
87
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
@@ -196,9 +109,7 @@ export class Frontend {
196
109
  this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
197
110
  }
198
111
  const serverOptions = { cert, key, ca };
199
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
200
112
  this.httpsServer = https.createServer(serverOptions, this.expressApp);
201
- // Listen on the specified port
202
113
  if (hasParameter('ingress')) {
203
114
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
204
115
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -212,7 +123,6 @@ export class Frontend {
212
123
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
213
124
  });
214
125
  }
215
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
216
126
  this.httpsServer.on('error', (error) => {
217
127
  this.log.error(`Frontend https server error listening on ${this.port}`);
218
128
  switch (error.code) {
@@ -229,18 +139,16 @@ export class Frontend {
229
139
  }
230
140
  if (this.initializeError)
231
141
  return;
232
- // Create a WebSocket server and attach it to the http or https server
233
142
  const wssPort = this.port;
234
143
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
235
144
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
236
145
  this.webSocketServer.on('connection', (ws, request) => {
237
146
  const clientIp = request.socket.remoteAddress;
238
- // Set the global logger callback for the WebSocketServer
239
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
240
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
241
- callbackLogLevel = "info" /* LogLevel.INFO */;
242
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
243
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
147
+ let callbackLogLevel = "notice";
148
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
149
+ callbackLogLevel = "info";
150
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
151
+ callbackLogLevel = "debug";
244
152
  AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
245
153
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
246
154
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -274,7 +182,6 @@ export class Frontend {
274
182
  this.webSocketServer.on('error', (ws, error) => {
275
183
  this.log.error(`WebSocketServer error: ${error}`);
276
184
  });
277
- // Subscribe to cli events
278
185
  const { cliEmitter } = await import('./cli.js');
279
186
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
280
187
  this.wssSendUptimeUpdate(systemUptime, processUptime);
@@ -285,7 +192,6 @@ export class Frontend {
285
192
  cliEmitter.on('cpu', (cpuUsage) => {
286
193
  this.wssSendCpuUpdate(cpuUsage);
287
194
  });
288
- // Endpoint to validate login code
289
195
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
290
196
  const { password } = req.body;
291
197
  this.log.debug('The frontend sent /api/login', password);
@@ -304,27 +210,23 @@ export class Frontend {
304
210
  this.log.warn('/api/login error wrong password');
305
211
  res.json({ valid: false });
306
212
  }
307
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
308
213
  }
309
214
  catch (error) {
310
215
  this.log.error('/api/login error getting password');
311
216
  res.json({ valid: false });
312
217
  }
313
218
  });
314
- // Endpoint to provide health check
315
219
  this.expressApp.get('/health', (req, res) => {
316
220
  this.log.debug('Express received /health');
317
221
  const healthStatus = {
318
- status: 'ok', // Indicate service is healthy
319
- uptime: process.uptime(), // Server uptime in seconds
320
- timestamp: new Date().toISOString(), // Current timestamp
222
+ status: 'ok',
223
+ uptime: process.uptime(),
224
+ timestamp: new Date().toISOString(),
321
225
  };
322
226
  res.status(200).json(healthStatus);
323
227
  });
324
- // Endpoint to provide memory usage details
325
228
  this.expressApp.get('/memory', async (req, res) => {
326
229
  this.log.debug('Express received /memory');
327
- // Memory usage from process
328
230
  const memoryUsageRaw = process.memoryUsage();
329
231
  const memoryUsage = {
330
232
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -333,13 +235,10 @@ export class Frontend {
333
235
  external: this.formatMemoryUsage(memoryUsageRaw.external),
334
236
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
335
237
  };
336
- // V8 heap statistics
337
238
  const { default: v8 } = await import('node:v8');
338
239
  const heapStatsRaw = v8.getHeapStatistics();
339
240
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
340
- // Format heapStats
341
241
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
342
- // Format heapSpaces
343
242
  const heapSpaces = heapSpacesRaw.map((space) => ({
344
243
  ...space,
345
244
  space_size: this.formatMemoryUsage(space.space_size),
@@ -357,7 +256,6 @@ export class Frontend {
357
256
  };
358
257
  res.status(200).json(memoryReport);
359
258
  });
360
- // Endpoint to start advertising the server node
361
259
  this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
362
260
  const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
363
261
  if (pairingCodes) {
@@ -368,22 +266,18 @@ export class Frontend {
368
266
  res.status(500).json({ error: 'Failed to generate pairing codes' });
369
267
  }
370
268
  });
371
- // Endpoint to provide settings
372
269
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
373
270
  this.log.debug('The frontend sent /api/settings');
374
271
  res.json(await this.getApiSettings());
375
272
  });
376
- // Endpoint to provide plugins
377
273
  this.expressApp.get('/api/plugins', async (req, res) => {
378
274
  this.log.debug('The frontend sent /api/plugins');
379
275
  res.json(this.getBaseRegisteredPlugins());
380
276
  });
381
- // Endpoint to provide devices
382
277
  this.expressApp.get('/api/devices', (req, res) => {
383
278
  this.log.debug('The frontend sent /api/devices');
384
279
  const devices = [];
385
280
  this.matterbridge.devices.forEach(async (device) => {
386
- // Check if the device has the required properties
387
281
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
388
282
  return;
389
283
  const cluster = this.getClusterTextFromDevice(device);
@@ -402,7 +296,6 @@ export class Frontend {
402
296
  });
403
297
  res.json(devices);
404
298
  });
405
- // Endpoint to provide the cluster servers of the devices
406
299
  this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
407
300
  const selectedPluginName = req.params.selectedPluginName;
408
301
  const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
@@ -475,7 +368,6 @@ export class Frontend {
475
368
  });
476
369
  res.json(data);
477
370
  });
478
- // Endpoint to view the log
479
371
  this.expressApp.get('/api/view-log', async (req, res) => {
480
372
  this.log.debug('The frontend sent /api/log');
481
373
  try {
@@ -488,12 +380,10 @@ export class Frontend {
488
380
  res.status(500).send('Error reading log file');
489
381
  }
490
382
  });
491
- // Endpoint to download the matterbridge log
492
383
  this.expressApp.get('/api/download-mblog', async (req, res) => {
493
384
  this.log.debug('The frontend sent /api/download-mblog');
494
385
  try {
495
386
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
496
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
497
387
  }
498
388
  catch (error) {
499
389
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -505,12 +395,10 @@ export class Frontend {
505
395
  }
506
396
  });
507
397
  });
508
- // Endpoint to download the matter log
509
398
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
510
399
  this.log.debug('The frontend sent /api/download-mjlog');
511
400
  try {
512
401
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
513
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
514
402
  }
515
403
  catch (error) {
516
404
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -522,12 +410,10 @@ export class Frontend {
522
410
  }
523
411
  });
524
412
  });
525
- // Endpoint to download the matter log
526
413
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
527
414
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
528
415
  try {
529
416
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
530
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
531
417
  }
532
418
  catch (error) {
533
419
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'Create the Shelly system log before downloading it.');
@@ -539,7 +425,6 @@ export class Frontend {
539
425
  }
540
426
  });
541
427
  });
542
- // Endpoint to download the matter storage file
543
428
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
544
429
  this.log.debug('The frontend sent /api/download-mjstorage');
545
430
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
@@ -550,7 +435,6 @@ export class Frontend {
550
435
  }
551
436
  });
552
437
  });
553
- // Endpoint to download the matterbridge storage directory
554
438
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
555
439
  this.log.debug('The frontend sent /api/download-mbstorage');
556
440
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
@@ -561,7 +445,6 @@ export class Frontend {
561
445
  }
562
446
  });
563
447
  });
564
- // Endpoint to download the matterbridge plugin directory
565
448
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
566
449
  this.log.debug('The frontend sent /api/download-pluginstorage');
567
450
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
@@ -572,11 +455,9 @@ export class Frontend {
572
455
  }
573
456
  });
574
457
  });
575
- // Endpoint to download the matterbridge plugin config files
576
458
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
577
459
  this.log.debug('The frontend sent /api/download-pluginconfig');
578
460
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
579
- // 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')));
580
461
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
581
462
  if (error) {
582
463
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
@@ -584,7 +465,6 @@ export class Frontend {
584
465
  }
585
466
  });
586
467
  });
587
- // Endpoint to download the matterbridge plugin config files
588
468
  this.expressApp.get('/api/download-backup', async (req, res) => {
589
469
  this.log.debug('The frontend sent /api/download-backup');
590
470
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -594,7 +474,6 @@ export class Frontend {
594
474
  }
595
475
  });
596
476
  });
597
- // Endpoint to receive commands
598
477
  this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
599
478
  const command = req.params.command;
600
479
  let param = req.params.param;
@@ -604,15 +483,13 @@ export class Frontend {
604
483
  return;
605
484
  }
606
485
  this.log.debug(`Received frontend command: ${command}:${param}`);
607
- // Handle the command setpassword from Settings
608
486
  if (command === 'setpassword') {
609
- const password = param.slice(1, -1); // Remove the first and last characters
487
+ const password = param.slice(1, -1);
610
488
  this.log.debug('setpassword', param, password);
611
489
  await this.matterbridge.nodeContext?.set('password', password);
612
490
  res.json({ message: 'Command received' });
613
491
  return;
614
492
  }
615
- // Handle the command setbridgemode from Settings
616
493
  if (command === 'setbridgemode') {
617
494
  this.log.debug(`setbridgemode: ${param}`);
618
495
  this.wssSendRestartRequired();
@@ -620,7 +497,6 @@ export class Frontend {
620
497
  res.json({ message: 'Command received' });
621
498
  return;
622
499
  }
623
- // Handle the command backup from Settings
624
500
  if (command === 'backup') {
625
501
  this.log.notice(`Prepairing the backup...`);
626
502
  await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
@@ -629,33 +505,31 @@ export class Frontend {
629
505
  res.json({ message: 'Command received' });
630
506
  return;
631
507
  }
632
- // Handle the command setmbloglevel from Settings
633
508
  if (command === 'setmbloglevel') {
634
509
  this.log.debug('Matterbridge log level:', param);
635
510
  if (param === 'Debug') {
636
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
511
+ this.log.logLevel = "debug";
637
512
  }
638
513
  else if (param === 'Info') {
639
- this.log.logLevel = "info" /* LogLevel.INFO */;
514
+ this.log.logLevel = "info";
640
515
  }
641
516
  else if (param === 'Notice') {
642
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
517
+ this.log.logLevel = "notice";
643
518
  }
644
519
  else if (param === 'Warn') {
645
- this.log.logLevel = "warn" /* LogLevel.WARN */;
520
+ this.log.logLevel = "warn";
646
521
  }
647
522
  else if (param === 'Error') {
648
- this.log.logLevel = "error" /* LogLevel.ERROR */;
523
+ this.log.logLevel = "error";
649
524
  }
650
525
  else if (param === 'Fatal') {
651
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
526
+ this.log.logLevel = "fatal";
652
527
  }
653
528
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
654
529
  await this.matterbridge.setLogLevel(this.log.logLevel);
655
530
  res.json({ message: 'Command received' });
656
531
  return;
657
532
  }
658
- // Handle the command setmbloglevel from Settings
659
533
  if (command === 'setmjloglevel') {
660
534
  this.log.debug('Matter.js log level:', param);
661
535
  if (param === 'Debug') {
@@ -680,34 +554,30 @@ export class Frontend {
680
554
  res.json({ message: 'Command received' });
681
555
  return;
682
556
  }
683
- // Handle the command setmdnsinterface from Settings
684
557
  if (command === 'setmdnsinterface') {
685
- param = param.slice(1, -1); // Remove the first and last characters *mdns*
558
+ param = param.slice(1, -1);
686
559
  this.matterbridge.matterbridgeInformation.mattermdnsinterface = param;
687
560
  this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
688
561
  await this.matterbridge.nodeContext?.set('mattermdnsinterface', param);
689
562
  res.json({ message: 'Command received' });
690
563
  return;
691
564
  }
692
- // Handle the command setipv4address from Settings
693
565
  if (command === 'setipv4address') {
694
- param = param.slice(1, -1); // Remove the first and last characters *ip*
566
+ param = param.slice(1, -1);
695
567
  this.matterbridge.matterbridgeInformation.matteripv4address = param;
696
568
  this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
697
569
  await this.matterbridge.nodeContext?.set('matteripv4address', param);
698
570
  res.json({ message: 'Command received' });
699
571
  return;
700
572
  }
701
- // Handle the command setipv6address from Settings
702
573
  if (command === 'setipv6address') {
703
- param = param.slice(1, -1); // Remove the first and last characters *ip*
574
+ param = param.slice(1, -1);
704
575
  this.matterbridge.matterbridgeInformation.matteripv6address = param;
705
576
  this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
706
577
  await this.matterbridge.nodeContext?.set('matteripv6address', param);
707
578
  res.json({ message: 'Command received' });
708
579
  return;
709
580
  }
710
- // Handle the command setmatterport from Settings
711
581
  if (command === 'setmatterport') {
712
582
  const port = Math.min(Math.max(parseInt(param), 5540), 5560);
713
583
  this.matterbridge.matterbridgeInformation.matterPort = port;
@@ -716,7 +586,6 @@ export class Frontend {
716
586
  res.json({ message: 'Command received' });
717
587
  return;
718
588
  }
719
- // Handle the command setmatterdiscriminator from Settings
720
589
  if (command === 'setmatterdiscriminator') {
721
590
  const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
722
591
  this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
@@ -725,7 +594,6 @@ export class Frontend {
725
594
  res.json({ message: 'Command received' });
726
595
  return;
727
596
  }
728
- // Handle the command setmatterpasscode from Settings
729
597
  if (command === 'setmatterpasscode') {
730
598
  const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
731
599
  this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
@@ -734,20 +602,17 @@ export class Frontend {
734
602
  res.json({ message: 'Command received' });
735
603
  return;
736
604
  }
737
- // Handle the command setmbloglevel from Settings
738
605
  if (command === 'setmblogfile') {
739
606
  this.log.debug('Matterbridge file log:', param);
740
607
  this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
741
608
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
742
- // Create the file logger for matterbridge
743
609
  if (param === 'true')
744
- AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
610
+ AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
745
611
  else
746
612
  AnsiLogger.setGlobalLogfile(undefined);
747
613
  res.json({ message: 'Command received' });
748
614
  return;
749
615
  }
750
- // Handle the command setmbloglevel from Settings
751
616
  if (command === 'setmjlogfile') {
752
617
  this.log.debug('Matter file log:', param);
753
618
  this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
@@ -774,48 +639,40 @@ export class Frontend {
774
639
  res.json({ message: 'Command received' });
775
640
  return;
776
641
  }
777
- // Handle the command unregister from Settings
778
642
  if (command === 'unregister') {
779
643
  await this.matterbridge.unregisterAndShutdownProcess();
780
644
  res.json({ message: 'Command received' });
781
645
  return;
782
646
  }
783
- // Handle the command reset from Settings
784
647
  if (command === 'reset') {
785
648
  await this.matterbridge.shutdownProcessAndReset();
786
649
  res.json({ message: 'Command received' });
787
650
  return;
788
651
  }
789
- // Handle the command factoryreset from Settings
790
652
  if (command === 'factoryreset') {
791
653
  await this.matterbridge.shutdownProcessAndFactoryReset();
792
654
  res.json({ message: 'Command received' });
793
655
  return;
794
656
  }
795
- // Handle the command shutdown from Header
796
657
  if (command === 'shutdown') {
797
658
  await this.matterbridge.shutdownProcess();
798
659
  res.json({ message: 'Command received' });
799
660
  return;
800
661
  }
801
- // Handle the command restart from Header
802
662
  if (command === 'restart') {
803
663
  await this.matterbridge.restartProcess();
804
664
  res.json({ message: 'Command received' });
805
665
  return;
806
666
  }
807
- // Handle the command update from Header
808
667
  if (command === 'update') {
809
668
  await this.matterbridge.updateProcess();
810
669
  this.wssSendRestartRequired();
811
670
  res.json({ message: 'Command received' });
812
671
  return;
813
672
  }
814
- // Handle the command saveconfig from Home
815
673
  if (command === 'saveconfig') {
816
674
  param = param.replace(/\*/g, '\\');
817
675
  this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
818
- // console.log('Req.body:', JSON.stringify(req.body, null, 2));
819
676
  if (!this.matterbridge.plugins.has(param)) {
820
677
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
821
678
  }
@@ -830,7 +687,6 @@ export class Frontend {
830
687
  res.json({ message: 'Command received' });
831
688
  return;
832
689
  }
833
- // Handle the command installplugin from Home
834
690
  if (command === 'installplugin') {
835
691
  param = param.replace(/\*/g, '\\');
836
692
  this.log.info(`Installing plugin ${plg}${param}${nf}...`);
@@ -839,7 +695,6 @@ export class Frontend {
839
695
  await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
840
696
  this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
841
697
  this.wssSendSnackbarMessage(`Installed package ${param}`);
842
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
843
698
  }
844
699
  catch (error) {
845
700
  this.log.error(`Error installing plugin ${plg}${param}${er}`);
@@ -847,22 +702,17 @@ export class Frontend {
847
702
  }
848
703
  this.wssSendRestartRequired();
849
704
  param = param.split('@')[0];
850
- // Also add the plugin to matterbridge so no return!
851
705
  if (param === 'matterbridge') {
852
- // 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
853
706
  res.json({ message: 'Command received' });
854
707
  return;
855
708
  }
856
709
  }
857
- // Handle the command addplugin from Home
858
710
  if (command === 'addplugin' || command === 'installplugin') {
859
711
  param = param.replace(/\*/g, '\\');
860
712
  const plugin = await this.matterbridge.plugins.add(param);
861
713
  if (plugin) {
862
714
  this.wssSendSnackbarMessage(`Added plugin ${param}`);
863
715
  if (this.matterbridge.bridgeMode === 'childbridge') {
864
- // 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
865
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
866
716
  this.matterbridge.createDynamicPlugin(plugin, true);
867
717
  }
868
718
  this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
@@ -872,14 +722,13 @@ export class Frontend {
872
722
  res.json({ message: 'Command received' });
873
723
  return;
874
724
  }
875
- // Handle the command removeplugin from Home
876
725
  if (command === 'removeplugin') {
877
726
  if (!this.matterbridge.plugins.has(param)) {
878
727
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
879
728
  }
880
729
  else {
881
730
  const plugin = this.matterbridge.plugins.get(param);
882
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true); // This will also close the server node in childbridge mode
731
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
883
732
  await this.matterbridge.plugins.remove(param);
884
733
  this.wssSendSnackbarMessage(`Removed plugin ${param}`);
885
734
  this.wssSendRefreshRequired('plugins');
@@ -887,7 +736,6 @@ export class Frontend {
887
736
  res.json({ message: 'Command received' });
888
737
  return;
889
738
  }
890
- // Handle the command enableplugin from Home
891
739
  if (command === 'enableplugin') {
892
740
  if (!this.matterbridge.plugins.has(param)) {
893
741
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -906,7 +754,6 @@ export class Frontend {
906
754
  await this.matterbridge.plugins.enable(param);
907
755
  this.wssSendSnackbarMessage(`Enabled plugin ${param}`);
908
756
  if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
909
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
910
757
  this.matterbridge.createDynamicPlugin(plugin, true);
911
758
  }
912
759
  this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
@@ -917,7 +764,6 @@ export class Frontend {
917
764
  res.json({ message: 'Command received' });
918
765
  return;
919
766
  }
920
- // Handle the command disableplugin from Home
921
767
  if (command === 'disableplugin') {
922
768
  if (!this.matterbridge.plugins.has(param)) {
923
769
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -925,7 +771,7 @@ export class Frontend {
925
771
  else {
926
772
  const plugin = this.matterbridge.plugins.get(param);
927
773
  if (plugin && plugin.enabled) {
928
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true); // This will also close the server node in childbridge mode
774
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
929
775
  await this.matterbridge.plugins.disable(param);
930
776
  this.wssSendSnackbarMessage(`Disabled plugin ${param}`);
931
777
  this.wssSendRefreshRequired('plugins');
@@ -943,13 +789,10 @@ export class Frontend {
943
789
  res.status(400).send('Invalid request: file and filename are required');
944
790
  return;
945
791
  }
946
- // Define the path where the plugin file will be saved
947
792
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
948
793
  try {
949
- // Move the uploaded file to the specified path
950
794
  await fs.rename(file.path, filePath);
951
795
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
952
- // Install the plugin package
953
796
  if (filename.endsWith('.tgz')) {
954
797
  await this.matterbridge.spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
955
798
  this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
@@ -965,7 +808,6 @@ export class Frontend {
965
808
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
966
809
  }
967
810
  });
968
- // Fallback for routing (must be the last route)
969
811
  this.expressApp.get('*', (req, res) => {
970
812
  this.log.debug('The frontend sent:', req.url);
971
813
  this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -974,31 +816,24 @@ export class Frontend {
974
816
  this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
975
817
  }
976
818
  async stop() {
977
- // Remove all listeners from the cliEmitter
978
- // cliEmitter.removeAllListeners();
979
- // Close the http server
980
819
  if (this.httpServer) {
981
820
  this.httpServer.close();
982
821
  this.httpServer.removeAllListeners();
983
822
  this.httpServer = undefined;
984
823
  this.log.debug('Frontend http server closed successfully');
985
824
  }
986
- // Close the https server
987
825
  if (this.httpsServer) {
988
826
  this.httpsServer.close();
989
827
  this.httpsServer.removeAllListeners();
990
828
  this.httpsServer = undefined;
991
829
  this.log.debug('Frontend https server closed successfully');
992
830
  }
993
- // Remove listeners from the express app
994
831
  if (this.expressApp) {
995
832
  this.expressApp.removeAllListeners();
996
833
  this.expressApp = undefined;
997
834
  this.log.debug('Frontend app closed successfully');
998
835
  }
999
- // Close the WebSocket server
1000
836
  if (this.webSocketServer) {
1001
- // Close all active connections
1002
837
  this.webSocketServer.clients.forEach((client) => {
1003
838
  if (client.readyState === WebSocket.OPEN) {
1004
839
  client.close();
@@ -1015,7 +850,6 @@ export class Frontend {
1015
850
  this.webSocketServer = undefined;
1016
851
  }
1017
852
  }
1018
- // Function to format bytes to KB, MB, or GB
1019
853
  formatMemoryUsage = (bytes) => {
1020
854
  if (bytes >= 1024 ** 3) {
1021
855
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -1027,7 +861,6 @@ export class Frontend {
1027
861
  return `${(bytes / 1024).toFixed(2)} KB`;
1028
862
  }
1029
863
  };
1030
- // Function to format system uptime with only the most significant unit
1031
864
  formatOsUpTime = (seconds) => {
1032
865
  if (seconds >= 86400) {
1033
866
  const days = Math.floor(seconds / 86400);
@@ -1043,13 +876,8 @@ export class Frontend {
1043
876
  }
1044
877
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
1045
878
  };
1046
- /**
1047
- * Retrieves the api settings data.
1048
- * @returns {Promise<object>} A promise that resolve in the api settings object.
1049
- */
1050
879
  async getApiSettings() {
1051
880
  const { lastCpuUsage } = await import('./cli.js');
1052
- // Update the system information
1053
881
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
1054
882
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
1055
883
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -1058,7 +886,6 @@ export class Frontend {
1058
886
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
1059
887
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
1060
888
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
1061
- // Update the matterbridge information
1062
889
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
1063
890
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
1064
891
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
@@ -1077,11 +904,6 @@ export class Frontend {
1077
904
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
1078
905
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
1079
906
  }
1080
- /**
1081
- * Retrieves the reachable attribute.
1082
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
1083
- * @returns {boolean} The reachable attribute.
1084
- */
1085
907
  getReachability(device) {
1086
908
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
1087
909
  return false;
@@ -1091,11 +913,6 @@ export class Frontend {
1091
913
  return true;
1092
914
  return false;
1093
915
  }
1094
- /**
1095
- * Retrieves the cluster text description from a given device.
1096
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
1097
- * @returns {string} The attributes description of the cluster servers in the device.
1098
- */
1099
916
  getClusterTextFromDevice(device) {
1100
917
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
1101
918
  return '';
@@ -1136,7 +953,6 @@ export class Frontend {
1136
953
  };
1137
954
  let attributes = '';
1138
955
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1139
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
1140
956
  if (typeof attributeValue === 'undefined')
1141
957
  return;
1142
958
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -1214,13 +1030,8 @@ export class Frontend {
1214
1030
  if (clusterName === 'userLabel' && attributeName === 'labelList')
1215
1031
  attributes += `${getUserLabel(device)} `;
1216
1032
  });
1217
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
1218
1033
  return attributes.trimStart().trimEnd();
1219
1034
  }
1220
- /**
1221
- * Retrieves the base registered plugins sanitized for res.json().
1222
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
1223
- */
1224
1035
  getBaseRegisteredPlugins() {
1225
1036
  const baseRegisteredPlugins = [];
1226
1037
  for (const plugin of this.matterbridge.plugins) {
@@ -1253,13 +1064,6 @@ export class Frontend {
1253
1064
  }
1254
1065
  return baseRegisteredPlugins;
1255
1066
  }
1256
- /**
1257
- * Handles incoming websocket messages for the Matterbridge frontend.
1258
- *
1259
- * @param {WebSocket} client - The websocket client that sent the message.
1260
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1261
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1262
- */
1263
1067
  async wsMessageHandler(client, message) {
1264
1068
  let data;
1265
1069
  try {
@@ -1405,10 +1209,8 @@ export class Frontend {
1405
1209
  else if (data.method === '/api/devices') {
1406
1210
  const devices = [];
1407
1211
  this.matterbridge.devices.forEach(async (device) => {
1408
- // Filter by pluginName if provided
1409
1212
  if (data.params.pluginName && data.params.pluginName !== device.plugin)
1410
1213
  return;
1411
- // Check if the device has the required properties
1412
1214
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1413
1215
  return;
1414
1216
  const cluster = this.getClusterTextFromDevice(device);
@@ -1492,7 +1294,6 @@ export class Frontend {
1492
1294
  });
1493
1295
  endpointServer.getChildEndpoints().forEach((childEndpoint) => {
1494
1296
  deviceTypes = [];
1495
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1496
1297
  const name = childEndpoint.endpoint?.id;
1497
1298
  const clusterServers = childEndpoint.getAllClusterServers();
1498
1299
  clusterServers.forEach((clusterServer) => {
@@ -1546,7 +1347,6 @@ export class Frontend {
1546
1347
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select' }));
1547
1348
  return;
1548
1349
  }
1549
- // const selectDeviceValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectDevice.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
1550
1350
  const selectDeviceValues = plugin.platform?.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1551
1351
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectDeviceValues }));
1552
1352
  return;
@@ -1561,7 +1361,6 @@ export class Frontend {
1561
1361
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' }));
1562
1362
  return;
1563
1363
  }
1564
- // const selectEntityValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectEntity.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
1565
1364
  const selectEntityValues = plugin.platform?.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1566
1365
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectEntityValues }));
1567
1366
  return;
@@ -1582,13 +1381,11 @@ export class Frontend {
1582
1381
  if (config.postfix) {
1583
1382
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1584
1383
  }
1585
- // Add the serial to the whiteList if the whiteList exists and the serial is not already in it
1586
1384
  if (isValidArray(config.whiteList, 1)) {
1587
1385
  if (!config.whiteList.includes(data.params.serial)) {
1588
1386
  config.whiteList.push(data.params.serial);
1589
1387
  }
1590
1388
  }
1591
- // Remove the serial from the blackList if the blackList exists and the serial is in it
1592
1389
  if (isValidArray(config.blackList, 1)) {
1593
1390
  if (config.blackList.includes(data.params.serial)) {
1594
1391
  config.blackList = config.blackList.filter((serial) => serial !== data.params.serial);
@@ -1611,13 +1408,11 @@ export class Frontend {
1611
1408
  if (config.postfix) {
1612
1409
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1613
1410
  }
1614
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1615
1411
  if (isValidArray(config.whiteList, 1)) {
1616
1412
  if (config.whiteList.includes(data.params.serial)) {
1617
1413
  config.whiteList = config.whiteList.filter((serial) => serial !== data.params.serial);
1618
1414
  }
1619
1415
  }
1620
- // Add the serial to the blackList
1621
1416
  if (isValidArray(config.blackList)) {
1622
1417
  if (!config.blackList.includes(data.params.serial)) {
1623
1418
  config.blackList.push(data.params.serial);
@@ -1641,171 +1436,102 @@ export class Frontend {
1641
1436
  return;
1642
1437
  }
1643
1438
  }
1644
- /**
1645
- * Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1646
- *
1647
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1648
- * @param {string} time - The time string of the message
1649
- * @param {string} name - The logger name of the message
1650
- * @param {string} message - The content of the message.
1651
- */
1652
1439
  wssSendMessage(level, time, name, message) {
1653
1440
  if (!level || !time || !name || !message)
1654
1441
  return;
1655
- // Remove ANSI escape codes from the message
1656
- // eslint-disable-next-line no-control-regex
1657
1442
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1658
- // Remove leading asterisks from the message
1659
1443
  message = message.replace(/^\*+/, '');
1660
- // Replace all occurrences of \t and \n
1661
1444
  message = message.replace(/[\t\n]/g, '');
1662
- // Remove non-printable characters
1663
- // eslint-disable-next-line no-control-regex
1664
1445
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1665
- // Replace all occurrences of \" with "
1666
1446
  message = message.replace(/\\"/g, '"');
1667
- // Define the maximum allowed length for continuous characters without a space
1668
1447
  const maxContinuousLength = 100;
1669
1448
  const keepStartLength = 20;
1670
1449
  const keepEndLength = 20;
1671
- // Split the message into words
1672
1450
  message = message
1673
1451
  .split(' ')
1674
1452
  .map((word) => {
1675
- // If the word length exceeds the max continuous length, insert spaces and truncate
1676
1453
  if (word.length > maxContinuousLength) {
1677
1454
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1678
1455
  }
1679
1456
  return word;
1680
1457
  })
1681
1458
  .join(' ');
1682
- // Send the message to all connected clients
1683
1459
  this.webSocketServer?.clients.forEach((client) => {
1684
1460
  if (client.readyState === WebSocket.OPEN) {
1685
1461
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1686
1462
  }
1687
1463
  });
1688
1464
  }
1689
- /**
1690
- * Sends a need to refresh WebSocket message to all connected clients.
1691
- *
1692
- * @param {string} changed - The changed value. If null, the whole page will be refreshed.
1693
- * possible values:
1694
- * - 'matterbridgeLatestVersion'
1695
- * - 'matterbridgeAdvertise'
1696
- * - 'online'
1697
- * - 'offline'
1698
- * - 'reachability'
1699
- * - 'settings'
1700
- * - 'plugins'
1701
- * - 'devices'
1702
- * - 'fabrics'
1703
- * - 'sessions'
1704
- */
1705
1465
  wssSendRefreshRequired(changed = null) {
1706
1466
  this.log.debug('Sending a refresh required message to all connected clients');
1707
- // Send the message to all connected clients
1708
1467
  this.webSocketServer?.clients.forEach((client) => {
1709
1468
  if (client.readyState === WebSocket.OPEN) {
1710
1469
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
1711
1470
  }
1712
1471
  });
1713
1472
  }
1714
- /**
1715
- * Sends a need to restart WebSocket message to all connected clients.
1716
- *
1717
- */
1718
1473
  wssSendRestartRequired(snackbar = true) {
1719
1474
  this.log.debug('Sending a restart required message to all connected clients');
1720
1475
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1721
1476
  if (snackbar === true)
1722
1477
  this.wssSendSnackbarMessage(`Restart required`, 0);
1723
- // Send the message to all connected clients
1724
1478
  this.webSocketServer?.clients.forEach((client) => {
1725
1479
  if (client.readyState === WebSocket.OPEN) {
1726
1480
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
1727
1481
  }
1728
1482
  });
1729
1483
  }
1730
- /**
1731
- * Sends a need to update WebSocket message to all connected clients.
1732
- *
1733
- */
1734
1484
  wssSendUpdateRequired() {
1735
1485
  this.log.debug('Sending an update required message to all connected clients');
1736
1486
  this.matterbridge.matterbridgeInformation.updateRequired = true;
1737
- // Send the message to all connected clients
1738
1487
  this.webSocketServer?.clients.forEach((client) => {
1739
1488
  if (client.readyState === WebSocket.OPEN) {
1740
1489
  client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
1741
1490
  }
1742
1491
  });
1743
1492
  }
1744
- /**
1745
- * Sends a memory update message to all connected clients.
1746
- *
1747
- */
1748
1493
  wssSendCpuUpdate(cpuUsage) {
1749
1494
  this.log.debug('Sending a cpu update message to all connected clients');
1750
- // Send the message to all connected clients
1751
1495
  this.webSocketServer?.clients.forEach((client) => {
1752
1496
  if (client.readyState === WebSocket.OPEN) {
1753
1497
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
1754
1498
  }
1755
1499
  });
1756
1500
  }
1757
- /**
1758
- * Sends a cpu update message to all connected clients.
1759
- *
1760
- */
1761
1501
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
1762
1502
  this.log.debug('Sending a memory update message to all connected clients');
1763
- // Send the message to all connected clients
1764
1503
  this.webSocketServer?.clients.forEach((client) => {
1765
1504
  if (client.readyState === WebSocket.OPEN) {
1766
1505
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
1767
1506
  }
1768
1507
  });
1769
1508
  }
1770
- /**
1771
- * Sends a memory update message to all connected clients.
1772
- *
1773
- */
1774
1509
  wssSendUptimeUpdate(systemUptime, processUptime) {
1775
1510
  this.log.debug('Sending a uptime update message to all connected clients');
1776
- // Send the message to all connected clients
1777
1511
  this.webSocketServer?.clients.forEach((client) => {
1778
1512
  if (client.readyState === WebSocket.OPEN) {
1779
1513
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
1780
1514
  }
1781
1515
  });
1782
1516
  }
1783
- /**
1784
- * Sends a cpu update message to all connected clients.
1785
- * @param {string} message - The message to send.
1786
- * @param {number} timeout - The timeout in seconds for the snackbar message.
1787
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
1788
- *
1789
- */
1790
1517
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
1791
1518
  this.log.debug('Sending a snackbar message to all connected clients');
1792
- // Send the message to all connected clients
1793
1519
  this.webSocketServer?.clients.forEach((client) => {
1794
1520
  if (client.readyState === WebSocket.OPEN) {
1795
1521
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { message, timeout, severity } }));
1796
1522
  }
1797
1523
  });
1798
1524
  }
1799
- /**
1800
- * Sends a message to all connected clients.
1801
- * @param {number} id - The message id.
1802
- * @param {string} method - The message method.
1803
- * @param {Record<string, string | number | boolean>} params - The message parameters.
1804
- *
1805
- */
1525
+ wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
1526
+ this.log.debug('Sending an attribute update message to all connected clients');
1527
+ this.webSocketServer?.clients.forEach((client) => {
1528
+ if (client.readyState === WebSocket.OPEN) {
1529
+ client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
1530
+ }
1531
+ });
1532
+ }
1806
1533
  wssBroadcastMessage(id, method, params) {
1807
1534
  this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
1808
- // Send the message to all connected clients
1809
1535
  this.webSocketServer?.clients.forEach((client) => {
1810
1536
  if (client.readyState === WebSocket.OPEN) {
1811
1537
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -1813,4 +1539,3 @@ export class Frontend {
1813
1539
  });
1814
1540
  }
1815
1541
  }
1816
- //# sourceMappingURL=frontend.js.map