matterbridge 2.2.8 → 2.2.9-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 +20 -1
  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 +19 -326
  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 +51 -747
  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 -720
  21. package/dist/matterbridgeEndpointHelpers.js +9 -118
  22. package/dist/matterbridgePlatform.js +7 -216
  23. package/dist/matterbridgeTypes.js +0 -24
  24. package/dist/pluginManager.js +3 -262
  25. package/dist/shelly.js +6 -146
  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 -76
  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.e11d6bb4.js → main.ff47208e.js} +3 -3
  41. package/frontend/build/static/js/{main.e11d6bb4.js.map → main.ff47208e.js.map} +1 -1
  42. package/npm-shrinkwrap.json +9 -9
  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 -221
  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 -425
  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 -867
  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 -285
  105. package/dist/matterbridgePlatform.d.ts.map +0 -1
  106. package/dist/matterbridgePlatform.js.map +0 -1
  107. package/dist/matterbridgeTypes.d.ts +0 -183
  108. package/dist/matterbridgeTypes.d.ts.map +0 -1
  109. package/dist/matterbridgeTypes.js.map +0 -1
  110. package/dist/pluginManager.d.ts +0 -271
  111. package/dist/pluginManager.d.ts.map +0 -1
  112. package/dist/pluginManager.js.map +0 -1
  113. package/dist/shelly.d.ts +0 -92
  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 -69
  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.e11d6bb4.js.LICENSE.txt → main.ff47208e.js.LICENSE.txt} +0 -0
package/dist/frontend.js CHANGED
@@ -1,106 +1,27 @@
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
11
  import { createZip, deepCopy, isValidArray, isValidNumber, isValidObject, isValidString } from './utils/export.js';
39
12
  import { plg } from './matterbridgeTypes.js';
40
13
  import { hasParameter } from './utils/export.js';
41
14
  import { BridgedDeviceBasicInformation } 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 a shelly system update.
89
- * check:
90
- * curl -k http://127.0.0.1:8101/api/updates/sys/check
91
- * perform:
92
- * curl -k http://127.0.0.1:8101/api/updates/sys/perform
93
- * @constant {number}
94
- */
95
24
  export const WS_ID_SHELLY_SYS_UPDATE = 100;
96
- /**
97
- * Websocket message ID indicating a shelly main update.
98
- * check:
99
- * curl -k http://127.0.0.1:8101/api/updates/main/check
100
- * perform:
101
- * curl -k http://127.0.0.1:8101/api/updates/main/perform
102
- * @constant {number}
103
- */
104
25
  export const WS_ID_SHELLY_MAIN_UPDATE = 101;
105
26
  export class Frontend {
106
27
  matterbridge;
@@ -118,7 +39,7 @@ export class Frontend {
118
39
  memoryTimeout;
119
40
  constructor(matterbridge) {
120
41
  this.matterbridge = matterbridge;
121
- 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" });
122
43
  }
123
44
  set logLevel(logLevel) {
124
45
  this.log.logLevel = logLevel;
@@ -126,25 +47,13 @@ export class Frontend {
126
47
  async start(port = 8283) {
127
48
  this.port = port;
128
49
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
129
- // Initialize multer with the upload directory
130
50
  const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
131
51
  await fs.mkdir(uploadDir, { recursive: true });
132
52
  const upload = multer({ dest: uploadDir });
133
- // Create the express app that serves the frontend
134
53
  this.expressApp = express();
135
- // Log all requests to the server for debugging
136
- /*
137
- this.expressApp.use((req, res, next) => {
138
- this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
139
- next();
140
- });
141
- */
142
- // Serve static files from '/static' endpoint
143
54
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
144
55
  if (!hasParameter('ssl')) {
145
- // Create an HTTP server and attach the express app
146
56
  this.httpServer = createServer(this.expressApp);
147
- // Listen on the specified port
148
57
  if (hasParameter('ingress')) {
149
58
  this.httpServer.listen(this.port, '0.0.0.0', () => {
150
59
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -158,7 +67,6 @@ export class Frontend {
158
67
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
159
68
  });
160
69
  }
161
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
162
70
  this.httpServer.on('error', (error) => {
163
71
  this.log.error(`Frontend http server error listening on ${this.port}`);
164
72
  switch (error.code) {
@@ -174,7 +82,6 @@ export class Frontend {
174
82
  });
175
83
  }
176
84
  else {
177
- // Load the SSL certificate, the private key and optionally the CA certificate
178
85
  let cert;
179
86
  try {
180
87
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
@@ -202,9 +109,7 @@ export class Frontend {
202
109
  this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
203
110
  }
204
111
  const serverOptions = { cert, key, ca };
205
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
206
112
  this.httpsServer = https.createServer(serverOptions, this.expressApp);
207
- // Listen on the specified port
208
113
  if (hasParameter('ingress')) {
209
114
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
210
115
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -218,7 +123,6 @@ export class Frontend {
218
123
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
219
124
  });
220
125
  }
221
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
222
126
  this.httpsServer.on('error', (error) => {
223
127
  this.log.error(`Frontend https server error listening on ${this.port}`);
224
128
  switch (error.code) {
@@ -235,18 +139,16 @@ export class Frontend {
235
139
  }
236
140
  if (this.initializeError)
237
141
  return;
238
- // Create a WebSocket server and attach it to the http or https server
239
142
  const wssPort = this.port;
240
143
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
241
144
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
242
145
  this.webSocketServer.on('connection', (ws, request) => {
243
146
  const clientIp = request.socket.remoteAddress;
244
- // Set the global logger callback for the WebSocketServer
245
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
246
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
247
- callbackLogLevel = "info" /* LogLevel.INFO */;
248
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
249
- 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";
250
152
  AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
251
153
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
252
154
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -280,7 +182,6 @@ export class Frontend {
280
182
  this.webSocketServer.on('error', (ws, error) => {
281
183
  this.log.error(`WebSocketServer error: ${error}`);
282
184
  });
283
- // Subscribe to cli events
284
185
  const { cliEmitter } = await import('./cli.js');
285
186
  cliEmitter.removeAllListeners();
286
187
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
@@ -292,7 +193,6 @@ export class Frontend {
292
193
  cliEmitter.on('cpu', (cpuUsage) => {
293
194
  this.wssSendCpuUpdate(cpuUsage);
294
195
  });
295
- // Endpoint to validate login code
296
196
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
297
197
  const { password } = req.body;
298
198
  this.log.debug('The frontend sent /api/login', password);
@@ -311,27 +211,23 @@ export class Frontend {
311
211
  this.log.warn('/api/login error wrong password');
312
212
  res.json({ valid: false });
313
213
  }
314
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
315
214
  }
316
215
  catch (error) {
317
216
  this.log.error('/api/login error getting password');
318
217
  res.json({ valid: false });
319
218
  }
320
219
  });
321
- // Endpoint to provide health check
322
220
  this.expressApp.get('/health', (req, res) => {
323
221
  this.log.debug('Express received /health');
324
222
  const healthStatus = {
325
- status: 'ok', // Indicate service is healthy
326
- uptime: process.uptime(), // Server uptime in seconds
327
- timestamp: new Date().toISOString(), // Current timestamp
223
+ status: 'ok',
224
+ uptime: process.uptime(),
225
+ timestamp: new Date().toISOString(),
328
226
  };
329
227
  res.status(200).json(healthStatus);
330
228
  });
331
- // Endpoint to provide memory usage details
332
229
  this.expressApp.get('/memory', async (req, res) => {
333
230
  this.log.debug('Express received /memory');
334
- // Memory usage from process
335
231
  const memoryUsageRaw = process.memoryUsage();
336
232
  const memoryUsage = {
337
233
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -340,13 +236,10 @@ export class Frontend {
340
236
  external: this.formatMemoryUsage(memoryUsageRaw.external),
341
237
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
342
238
  };
343
- // V8 heap statistics
344
239
  const { default: v8 } = await import('node:v8');
345
240
  const heapStatsRaw = v8.getHeapStatistics();
346
241
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
347
- // Format heapStats
348
242
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
349
- // Format heapSpaces
350
243
  const heapSpaces = heapSpacesRaw.map((space) => ({
351
244
  ...space,
352
245
  space_size: this.formatMemoryUsage(space.space_size),
@@ -364,7 +257,6 @@ export class Frontend {
364
257
  };
365
258
  res.status(200).json(memoryReport);
366
259
  });
367
- // Endpoint to start advertising the server node
368
260
  this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
369
261
  const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
370
262
  if (pairingCodes) {
@@ -375,22 +267,18 @@ export class Frontend {
375
267
  res.status(500).json({ error: 'Failed to generate pairing codes' });
376
268
  }
377
269
  });
378
- // Endpoint to provide settings
379
270
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
380
271
  this.log.debug('The frontend sent /api/settings');
381
272
  res.json(await this.getApiSettings());
382
273
  });
383
- // Endpoint to provide plugins
384
274
  this.expressApp.get('/api/plugins', async (req, res) => {
385
275
  this.log.debug('The frontend sent /api/plugins');
386
276
  res.json(this.getBaseRegisteredPlugins());
387
277
  });
388
- // Endpoint to provide devices
389
278
  this.expressApp.get('/api/devices', (req, res) => {
390
279
  this.log.debug('The frontend sent /api/devices');
391
280
  const devices = [];
392
281
  this.matterbridge.devices.forEach(async (device) => {
393
- // Check if the device has the required properties
394
282
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
395
283
  return;
396
284
  const cluster = this.getClusterTextFromDevice(device);
@@ -409,7 +297,6 @@ export class Frontend {
409
297
  });
410
298
  res.json(devices);
411
299
  });
412
- // Endpoint to provide the cluster servers of the devices
413
300
  this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
414
301
  const selectedPluginName = req.params.selectedPluginName;
415
302
  const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
@@ -482,7 +369,6 @@ export class Frontend {
482
369
  });
483
370
  res.json(data);
484
371
  });
485
- // Endpoint to view the log
486
372
  this.expressApp.get('/api/view-log', async (req, res) => {
487
373
  this.log.debug('The frontend sent /api/log');
488
374
  try {
@@ -495,12 +381,10 @@ export class Frontend {
495
381
  res.status(500).send('Error reading log file');
496
382
  }
497
383
  });
498
- // Endpoint to download the matterbridge log
499
384
  this.expressApp.get('/api/download-mblog', async (req, res) => {
500
385
  this.log.debug('The frontend sent /api/download-mblog');
501
386
  try {
502
387
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
503
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
504
388
  }
505
389
  catch (error) {
506
390
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -512,12 +396,10 @@ export class Frontend {
512
396
  }
513
397
  });
514
398
  });
515
- // Endpoint to download the matter log
516
399
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
517
400
  this.log.debug('The frontend sent /api/download-mjlog');
518
401
  try {
519
402
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
520
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
521
403
  }
522
404
  catch (error) {
523
405
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -529,12 +411,10 @@ export class Frontend {
529
411
  }
530
412
  });
531
413
  });
532
- // Endpoint to download the matter log
533
414
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
534
415
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
535
416
  try {
536
417
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
537
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
538
418
  }
539
419
  catch (error) {
540
420
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'Create the Shelly system log before downloading it.');
@@ -546,7 +426,6 @@ export class Frontend {
546
426
  }
547
427
  });
548
428
  });
549
- // Endpoint to download the matter storage file
550
429
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
551
430
  this.log.debug('The frontend sent /api/download-mjstorage');
552
431
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
@@ -557,7 +436,6 @@ export class Frontend {
557
436
  }
558
437
  });
559
438
  });
560
- // Endpoint to download the matterbridge storage directory
561
439
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
562
440
  this.log.debug('The frontend sent /api/download-mbstorage');
563
441
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
@@ -568,7 +446,6 @@ export class Frontend {
568
446
  }
569
447
  });
570
448
  });
571
- // Endpoint to download the matterbridge plugin directory
572
449
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
573
450
  this.log.debug('The frontend sent /api/download-pluginstorage');
574
451
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
@@ -579,11 +456,9 @@ export class Frontend {
579
456
  }
580
457
  });
581
458
  });
582
- // Endpoint to download the matterbridge plugin config files
583
459
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
584
460
  this.log.debug('The frontend sent /api/download-pluginconfig');
585
461
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
586
- // 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')));
587
462
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
588
463
  if (error) {
589
464
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
@@ -591,7 +466,6 @@ export class Frontend {
591
466
  }
592
467
  });
593
468
  });
594
- // Endpoint to download the matterbridge plugin config files
595
469
  this.expressApp.get('/api/download-backup', async (req, res) => {
596
470
  this.log.debug('The frontend sent /api/download-backup');
597
471
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -601,7 +475,6 @@ export class Frontend {
601
475
  }
602
476
  });
603
477
  });
604
- // Endpoint to receive commands
605
478
  this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
606
479
  const command = req.params.command;
607
480
  let param = req.params.param;
@@ -611,15 +484,13 @@ export class Frontend {
611
484
  return;
612
485
  }
613
486
  this.log.debug(`Received frontend command: ${command}:${param}`);
614
- // Handle the command setpassword from Settings
615
487
  if (command === 'setpassword') {
616
- const password = param.slice(1, -1); // Remove the first and last characters
488
+ const password = param.slice(1, -1);
617
489
  this.log.debug('setpassword', param, password);
618
490
  await this.matterbridge.nodeContext?.set('password', password);
619
491
  res.json({ message: 'Command received' });
620
492
  return;
621
493
  }
622
- // Handle the command setbridgemode from Settings
623
494
  if (command === 'setbridgemode') {
624
495
  this.log.debug(`setbridgemode: ${param}`);
625
496
  this.wssSendRestartRequired();
@@ -627,7 +498,6 @@ export class Frontend {
627
498
  res.json({ message: 'Command received' });
628
499
  return;
629
500
  }
630
- // Handle the command backup from Settings
631
501
  if (command === 'backup') {
632
502
  this.log.notice(`Prepairing the backup...`);
633
503
  await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
@@ -636,33 +506,31 @@ export class Frontend {
636
506
  res.json({ message: 'Command received' });
637
507
  return;
638
508
  }
639
- // Handle the command setmbloglevel from Settings
640
509
  if (command === 'setmbloglevel') {
641
510
  this.log.debug('Matterbridge log level:', param);
642
511
  if (param === 'Debug') {
643
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
512
+ this.log.logLevel = "debug";
644
513
  }
645
514
  else if (param === 'Info') {
646
- this.log.logLevel = "info" /* LogLevel.INFO */;
515
+ this.log.logLevel = "info";
647
516
  }
648
517
  else if (param === 'Notice') {
649
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
518
+ this.log.logLevel = "notice";
650
519
  }
651
520
  else if (param === 'Warn') {
652
- this.log.logLevel = "warn" /* LogLevel.WARN */;
521
+ this.log.logLevel = "warn";
653
522
  }
654
523
  else if (param === 'Error') {
655
- this.log.logLevel = "error" /* LogLevel.ERROR */;
524
+ this.log.logLevel = "error";
656
525
  }
657
526
  else if (param === 'Fatal') {
658
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
527
+ this.log.logLevel = "fatal";
659
528
  }
660
529
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
661
530
  await this.matterbridge.setLogLevel(this.log.logLevel);
662
531
  res.json({ message: 'Command received' });
663
532
  return;
664
533
  }
665
- // Handle the command setmbloglevel from Settings
666
534
  if (command === 'setmjloglevel') {
667
535
  this.log.debug('Matter.js log level:', param);
668
536
  if (param === 'Debug') {
@@ -687,7 +555,6 @@ export class Frontend {
687
555
  res.json({ message: 'Command received' });
688
556
  return;
689
557
  }
690
- // Handle the command setmdnsinterface from Settings
691
558
  if (command === 'setmdnsinterface') {
692
559
  if (param === 'json' && isValidString(req.body.value)) {
693
560
  this.matterbridge.matterbridgeInformation.mattermdnsinterface = req.body.value;
@@ -697,7 +564,6 @@ export class Frontend {
697
564
  res.json({ message: 'Command received' });
698
565
  return;
699
566
  }
700
- // Handle the command setipv4address from Settings
701
567
  if (command === 'setipv4address') {
702
568
  if (param === 'json' && isValidString(req.body.value)) {
703
569
  this.log.debug(`Matter.js ipv4 address: ${req.body.value === '' ? 'all ipv4 addresses' : req.body.value}`);
@@ -707,7 +573,6 @@ export class Frontend {
707
573
  res.json({ message: 'Command received' });
708
574
  return;
709
575
  }
710
- // Handle the command setipv6address from Settings
711
576
  if (command === 'setipv6address') {
712
577
  if (param === 'json' && isValidString(req.body.value)) {
713
578
  this.log.debug(`Matter.js ipv6 address: ${req.body.value === '' ? 'all ipv6 addresses' : req.body.value}`);
@@ -717,7 +582,6 @@ export class Frontend {
717
582
  res.json({ message: 'Command received' });
718
583
  return;
719
584
  }
720
- // Handle the command setmatterport from Settings
721
585
  if (command === 'setmatterport') {
722
586
  const port = Math.min(Math.max(parseInt(req.body.value), 5540), 5560);
723
587
  this.matterbridge.matterbridgeInformation.matterPort = port;
@@ -726,7 +590,6 @@ export class Frontend {
726
590
  res.json({ message: 'Command received' });
727
591
  return;
728
592
  }
729
- // Handle the command setmatterdiscriminator from Settings
730
593
  if (command === 'setmatterdiscriminator') {
731
594
  const discriminator = Math.min(Math.max(parseInt(req.body.value), 1000), 4095);
732
595
  this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
@@ -735,7 +598,6 @@ export class Frontend {
735
598
  res.json({ message: 'Command received' });
736
599
  return;
737
600
  }
738
- // Handle the command setmatterpasscode from Settings
739
601
  if (command === 'setmatterpasscode') {
740
602
  const passcode = Math.min(Math.max(parseInt(req.body.value), 10000000), 90000000);
741
603
  this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
@@ -744,20 +606,17 @@ export class Frontend {
744
606
  res.json({ message: 'Command received' });
745
607
  return;
746
608
  }
747
- // Handle the command setmbloglevel from Settings
748
609
  if (command === 'setmblogfile') {
749
610
  this.log.debug('Matterbridge file log:', param);
750
611
  this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
751
612
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
752
- // Create the file logger for matterbridge
753
613
  if (param === 'true')
754
- AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
614
+ AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
755
615
  else
756
616
  AnsiLogger.setGlobalLogfile(undefined);
757
617
  res.json({ message: 'Command received' });
758
618
  return;
759
619
  }
760
- // Handle the command setmbloglevel from Settings
761
620
  if (command === 'setmjlogfile') {
762
621
  this.log.debug('Matter file log:', param);
763
622
  this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
@@ -784,48 +643,40 @@ export class Frontend {
784
643
  res.json({ message: 'Command received' });
785
644
  return;
786
645
  }
787
- // Handle the command unregister from Settings
788
646
  if (command === 'unregister') {
789
647
  await this.matterbridge.unregisterAndShutdownProcess();
790
648
  res.json({ message: 'Command received' });
791
649
  return;
792
650
  }
793
- // Handle the command reset from Settings
794
651
  if (command === 'reset') {
795
652
  await this.matterbridge.shutdownProcessAndReset();
796
653
  res.json({ message: 'Command received' });
797
654
  return;
798
655
  }
799
- // Handle the command factoryreset from Settings
800
656
  if (command === 'factoryreset') {
801
657
  await this.matterbridge.shutdownProcessAndFactoryReset();
802
658
  res.json({ message: 'Command received' });
803
659
  return;
804
660
  }
805
- // Handle the command shutdown from Header
806
661
  if (command === 'shutdown') {
807
662
  await this.matterbridge.shutdownProcess();
808
663
  res.json({ message: 'Command received' });
809
664
  return;
810
665
  }
811
- // Handle the command restart from Header
812
666
  if (command === 'restart') {
813
667
  await this.matterbridge.restartProcess();
814
668
  res.json({ message: 'Command received' });
815
669
  return;
816
670
  }
817
- // Handle the command update from Header
818
671
  if (command === 'update') {
819
672
  await this.matterbridge.updateProcess();
820
673
  this.wssSendRestartRequired();
821
674
  res.json({ message: 'Command received' });
822
675
  return;
823
676
  }
824
- // Handle the command saveconfig from Home
825
677
  if (command === 'saveconfig') {
826
678
  param = param.replace(/\*/g, '\\');
827
679
  this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
828
- // console.log('Req.body:', JSON.stringify(req.body, null, 2));
829
680
  if (!this.matterbridge.plugins.has(param)) {
830
681
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
831
682
  }
@@ -840,7 +691,6 @@ export class Frontend {
840
691
  res.json({ message: 'Command received' });
841
692
  return;
842
693
  }
843
- // Handle the command installplugin from Home
844
694
  if (command === 'installplugin') {
845
695
  param = param.replace(/\*/g, '\\');
846
696
  this.log.info(`Installing plugin ${plg}${param}${nf}...`);
@@ -849,7 +699,6 @@ export class Frontend {
849
699
  await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
850
700
  this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
851
701
  this.wssSendSnackbarMessage(`Installed package ${param}`, 10, 'success');
852
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
853
702
  }
854
703
  catch (error) {
855
704
  this.log.error(`Error installing plugin ${plg}${param}${er}`);
@@ -857,22 +706,17 @@ export class Frontend {
857
706
  }
858
707
  this.wssSendRestartRequired();
859
708
  param = param.split('@')[0];
860
- // Also add the plugin to matterbridge so no return!
861
709
  if (param === 'matterbridge') {
862
- // 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
863
710
  res.json({ message: 'Command received' });
864
711
  return;
865
712
  }
866
713
  }
867
- // Handle the command addplugin from Home
868
714
  if (command === 'addplugin' || command === 'installplugin') {
869
715
  param = param.replace(/\*/g, '\\');
870
716
  const plugin = await this.matterbridge.plugins.add(param);
871
717
  if (plugin) {
872
718
  this.wssSendSnackbarMessage(`Added plugin ${param}`);
873
719
  if (this.matterbridge.bridgeMode === 'childbridge') {
874
- // 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
875
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
876
720
  this.matterbridge.createDynamicPlugin(plugin, true);
877
721
  }
878
722
  this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
@@ -882,14 +726,13 @@ export class Frontend {
882
726
  res.json({ message: 'Command received' });
883
727
  return;
884
728
  }
885
- // Handle the command removeplugin from Home
886
729
  if (command === 'removeplugin') {
887
730
  if (!this.matterbridge.plugins.has(param)) {
888
731
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
889
732
  }
890
733
  else {
891
734
  const plugin = this.matterbridge.plugins.get(param);
892
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true); // This will also close the server node in childbridge mode
735
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
893
736
  await this.matterbridge.plugins.remove(param);
894
737
  this.wssSendSnackbarMessage(`Removed plugin ${param}`);
895
738
  this.wssSendRefreshRequired('plugins');
@@ -897,7 +740,6 @@ export class Frontend {
897
740
  res.json({ message: 'Command received' });
898
741
  return;
899
742
  }
900
- // Handle the command enableplugin from Home
901
743
  if (command === 'enableplugin') {
902
744
  if (!this.matterbridge.plugins.has(param)) {
903
745
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -916,7 +758,6 @@ export class Frontend {
916
758
  await this.matterbridge.plugins.enable(param);
917
759
  this.wssSendSnackbarMessage(`Enabled plugin ${param}`);
918
760
  if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
919
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
920
761
  this.matterbridge.createDynamicPlugin(plugin, true);
921
762
  }
922
763
  this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
@@ -927,7 +768,6 @@ export class Frontend {
927
768
  res.json({ message: 'Command received' });
928
769
  return;
929
770
  }
930
- // Handle the command disableplugin from Home
931
771
  if (command === 'disableplugin') {
932
772
  if (!this.matterbridge.plugins.has(param)) {
933
773
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -935,7 +775,7 @@ export class Frontend {
935
775
  else {
936
776
  const plugin = this.matterbridge.plugins.get(param);
937
777
  if (plugin && plugin.enabled) {
938
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true); // This will also close the server node in childbridge mode
778
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
939
779
  await this.matterbridge.plugins.disable(param);
940
780
  this.wssSendSnackbarMessage(`Disabled plugin ${param}`);
941
781
  this.wssSendRefreshRequired('plugins');
@@ -954,13 +794,10 @@ export class Frontend {
954
794
  return;
955
795
  }
956
796
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`);
957
- // Define the path where the plugin file will be saved
958
797
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
959
798
  try {
960
- // Move the uploaded file to the specified path
961
799
  await fs.rename(file.path, filePath);
962
800
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
963
- // Install the plugin package
964
801
  if (filename.endsWith('.tgz')) {
965
802
  await this.matterbridge.spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
966
803
  this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
@@ -977,7 +814,6 @@ export class Frontend {
977
814
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
978
815
  }
979
816
  });
980
- // Fallback for routing (must be the last route)
981
817
  this.expressApp.get('*', (req, res) => {
982
818
  this.log.debug('The frontend sent:', req.url);
983
819
  this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -986,31 +822,24 @@ export class Frontend {
986
822
  this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
987
823
  }
988
824
  async stop() {
989
- // Remove all listeners from the cliEmitter
990
- // cliEmitter.removeAllListeners();
991
- // Close the http server
992
825
  if (this.httpServer) {
993
826
  this.httpServer.close();
994
827
  this.httpServer.removeAllListeners();
995
828
  this.httpServer = undefined;
996
829
  this.log.debug('Frontend http server closed successfully');
997
830
  }
998
- // Close the https server
999
831
  if (this.httpsServer) {
1000
832
  this.httpsServer.close();
1001
833
  this.httpsServer.removeAllListeners();
1002
834
  this.httpsServer = undefined;
1003
835
  this.log.debug('Frontend https server closed successfully');
1004
836
  }
1005
- // Remove listeners from the express app
1006
837
  if (this.expressApp) {
1007
838
  this.expressApp.removeAllListeners();
1008
839
  this.expressApp = undefined;
1009
840
  this.log.debug('Frontend app closed successfully');
1010
841
  }
1011
- // Close the WebSocket server
1012
842
  if (this.webSocketServer) {
1013
- // Close all active connections
1014
843
  this.webSocketServer.clients.forEach((client) => {
1015
844
  if (client.readyState === WebSocket.OPEN) {
1016
845
  client.close();
@@ -1027,7 +856,6 @@ export class Frontend {
1027
856
  this.webSocketServer = undefined;
1028
857
  }
1029
858
  }
1030
- // Function to format bytes to KB, MB, or GB
1031
859
  formatMemoryUsage = (bytes) => {
1032
860
  if (bytes >= 1024 ** 3) {
1033
861
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -1039,7 +867,6 @@ export class Frontend {
1039
867
  return `${(bytes / 1024).toFixed(2)} KB`;
1040
868
  }
1041
869
  };
1042
- // Function to format system uptime with only the most significant unit
1043
870
  formatOsUpTime = (seconds) => {
1044
871
  if (seconds >= 86400) {
1045
872
  const days = Math.floor(seconds / 86400);
@@ -1055,13 +882,8 @@ export class Frontend {
1055
882
  }
1056
883
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
1057
884
  };
1058
- /**
1059
- * Retrieves the api settings data.
1060
- * @returns {Promise<object>} A promise that resolve in the api settings object.
1061
- */
1062
885
  async getApiSettings() {
1063
886
  const { lastCpuUsage } = await import('./cli.js');
1064
- // Update the system information
1065
887
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
1066
888
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
1067
889
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -1070,7 +892,6 @@ export class Frontend {
1070
892
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
1071
893
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
1072
894
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
1073
- // Update the matterbridge information
1074
895
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
1075
896
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
1076
897
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
@@ -1089,11 +910,6 @@ export class Frontend {
1089
910
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
1090
911
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
1091
912
  }
1092
- /**
1093
- * Retrieves the reachable attribute.
1094
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
1095
- * @returns {boolean} The reachable attribute.
1096
- */
1097
913
  getReachability(device) {
1098
914
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
1099
915
  return false;
@@ -1103,11 +919,6 @@ export class Frontend {
1103
919
  return true;
1104
920
  return false;
1105
921
  }
1106
- /**
1107
- * Retrieves the cluster text description from a given device.
1108
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
1109
- * @returns {string} The attributes description of the cluster servers in the device.
1110
- */
1111
922
  getClusterTextFromDevice(device) {
1112
923
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
1113
924
  return '';
@@ -1148,7 +959,6 @@ export class Frontend {
1148
959
  };
1149
960
  let attributes = '';
1150
961
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1151
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
1152
962
  if (typeof attributeValue === 'undefined')
1153
963
  return;
1154
964
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -1226,13 +1036,8 @@ export class Frontend {
1226
1036
  if (clusterName === 'userLabel' && attributeName === 'labelList')
1227
1037
  attributes += `${getUserLabel(device)} `;
1228
1038
  });
1229
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
1230
1039
  return attributes.trimStart().trimEnd();
1231
1040
  }
1232
- /**
1233
- * Retrieves the base registered plugins sanitized for res.json().
1234
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
1235
- */
1236
1041
  getBaseRegisteredPlugins() {
1237
1042
  const baseRegisteredPlugins = [];
1238
1043
  for (const plugin of this.matterbridge.plugins) {
@@ -1269,13 +1074,6 @@ export class Frontend {
1269
1074
  }
1270
1075
  return baseRegisteredPlugins;
1271
1076
  }
1272
- /**
1273
- * Handles incoming websocket messages for the Matterbridge frontend.
1274
- *
1275
- * @param {WebSocket} client - The websocket client that sent the message.
1276
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1277
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1278
- */
1279
1077
  async wsMessageHandler(client, message) {
1280
1078
  let data;
1281
1079
  try {
@@ -1431,10 +1229,8 @@ export class Frontend {
1431
1229
  else if (data.method === '/api/devices') {
1432
1230
  const devices = [];
1433
1231
  this.matterbridge.devices.forEach(async (device) => {
1434
- // Filter by pluginName if provided
1435
1232
  if (data.params.pluginName && data.params.pluginName !== device.plugin)
1436
1233
  return;
1437
- // Check if the device has the required properties
1438
1234
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1439
1235
  return;
1440
1236
  const cluster = this.getClusterTextFromDevice(device);
@@ -1518,7 +1314,6 @@ export class Frontend {
1518
1314
  });
1519
1315
  endpointServer.getChildEndpoints().forEach((childEndpoint) => {
1520
1316
  deviceTypes = [];
1521
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1522
1317
  const name = childEndpoint.endpoint?.id;
1523
1318
  const clusterServers = childEndpoint.getAllClusterServers();
1524
1319
  clusterServers.forEach((clusterServer) => {
@@ -1572,7 +1367,6 @@ export class Frontend {
1572
1367
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select' }));
1573
1368
  return;
1574
1369
  }
1575
- // const selectDeviceValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectDevice.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
1576
1370
  const selectDeviceValues = plugin.platform?.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1577
1371
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectDeviceValues }));
1578
1372
  return;
@@ -1587,7 +1381,6 @@ export class Frontend {
1587
1381
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' }));
1588
1382
  return;
1589
1383
  }
1590
- // const selectEntityValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectEntity.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
1591
1384
  const selectEntityValues = plugin.platform?.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1592
1385
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectEntityValues }));
1593
1386
  return;
@@ -1619,7 +1412,6 @@ export class Frontend {
1619
1412
  return;
1620
1413
  }
1621
1414
  const config = plugin.configJson;
1622
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1623
1415
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1624
1416
  this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1625
1417
  if (select === 'serial')
@@ -1627,11 +1419,9 @@ export class Frontend {
1627
1419
  if (select === 'name')
1628
1420
  this.log.info(`Selected device name ${data.params.name}`);
1629
1421
  if (config && select && (select === 'serial' || select === 'name')) {
1630
- // Remove postfix from the serial if it exists
1631
1422
  if (config.postfix) {
1632
1423
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1633
1424
  }
1634
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1635
1425
  if (isValidArray(config.whiteList, 1)) {
1636
1426
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1637
1427
  config.whiteList.push(data.params.serial);
@@ -1640,7 +1430,6 @@ export class Frontend {
1640
1430
  config.whiteList.push(data.params.name);
1641
1431
  }
1642
1432
  }
1643
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1644
1433
  if (isValidArray(config.blackList, 1)) {
1645
1434
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1646
1435
  config.blackList = config.blackList.filter((serial) => serial !== data.params.serial);
@@ -1662,7 +1451,6 @@ export class Frontend {
1662
1451
  return;
1663
1452
  }
1664
1453
  const config = plugin.configJson;
1665
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1666
1454
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1667
1455
  this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1668
1456
  if (select === 'serial')
@@ -1673,7 +1461,6 @@ export class Frontend {
1673
1461
  if (config.postfix) {
1674
1462
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1675
1463
  }
1676
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1677
1464
  if (isValidArray(config.whiteList, 1)) {
1678
1465
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1679
1466
  config.whiteList = config.whiteList.filter((serial) => serial !== data.params.serial);
@@ -1682,7 +1469,6 @@ export class Frontend {
1682
1469
  config.whiteList = config.whiteList.filter((name) => name !== data.params.name);
1683
1470
  }
1684
1471
  }
1685
- // Add the serial to the blackList
1686
1472
  if (isValidArray(config.blackList)) {
1687
1473
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1688
1474
  config.blackList.push(data.params.serial);
@@ -1709,194 +1495,102 @@ export class Frontend {
1709
1495
  return;
1710
1496
  }
1711
1497
  }
1712
- /**
1713
- * Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1714
- *
1715
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1716
- * @param {string} time - The time string of the message
1717
- * @param {string} name - The logger name of the message
1718
- * @param {string} message - The content of the message.
1719
- */
1720
1498
  wssSendMessage(level, time, name, message) {
1721
1499
  if (!level || !time || !name || !message)
1722
1500
  return;
1723
- // Remove ANSI escape codes from the message
1724
- // eslint-disable-next-line no-control-regex
1725
1501
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1726
- // Remove leading asterisks from the message
1727
1502
  message = message.replace(/^\*+/, '');
1728
- // Replace all occurrences of \t and \n
1729
1503
  message = message.replace(/[\t\n]/g, '');
1730
- // Remove non-printable characters
1731
- // eslint-disable-next-line no-control-regex
1732
1504
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1733
- // Replace all occurrences of \" with "
1734
1505
  message = message.replace(/\\"/g, '"');
1735
- // Define the maximum allowed length for continuous characters without a space
1736
1506
  const maxContinuousLength = 100;
1737
1507
  const keepStartLength = 20;
1738
1508
  const keepEndLength = 20;
1739
- // Split the message into words
1740
1509
  message = message
1741
1510
  .split(' ')
1742
1511
  .map((word) => {
1743
- // If the word length exceeds the max continuous length, insert spaces and truncate
1744
1512
  if (word.length > maxContinuousLength) {
1745
1513
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1746
1514
  }
1747
1515
  return word;
1748
1516
  })
1749
1517
  .join(' ');
1750
- // Send the message to all connected clients
1751
1518
  this.webSocketServer?.clients.forEach((client) => {
1752
1519
  if (client.readyState === WebSocket.OPEN) {
1753
1520
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1754
1521
  }
1755
1522
  });
1756
1523
  }
1757
- /**
1758
- * Sends a need to refresh WebSocket message to all connected clients.
1759
- *
1760
- * @param {string} changed - The changed value. If null, the whole page will be refreshed.
1761
- * possible values:
1762
- * - 'matterbridgeLatestVersion'
1763
- * - 'matterbridgeAdvertise'
1764
- * - 'online'
1765
- * - 'offline'
1766
- * - 'reachability'
1767
- * - 'settings'
1768
- * - 'plugins'
1769
- * - 'devices'
1770
- * - 'fabrics'
1771
- * - 'sessions'
1772
- */
1773
1524
  wssSendRefreshRequired(changed = null) {
1774
1525
  this.log.debug('Sending a refresh required message to all connected clients');
1775
- // Send the message to all connected clients
1776
1526
  this.webSocketServer?.clients.forEach((client) => {
1777
1527
  if (client.readyState === WebSocket.OPEN) {
1778
1528
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
1779
1529
  }
1780
1530
  });
1781
1531
  }
1782
- /**
1783
- * Sends a need to restart WebSocket message to all connected clients.
1784
- *
1785
- */
1786
1532
  wssSendRestartRequired(snackbar = true) {
1787
1533
  this.log.debug('Sending a restart required message to all connected clients');
1788
1534
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1789
1535
  if (snackbar === true)
1790
1536
  this.wssSendSnackbarMessage(`Restart required`, 0);
1791
- // Send the message to all connected clients
1792
1537
  this.webSocketServer?.clients.forEach((client) => {
1793
1538
  if (client.readyState === WebSocket.OPEN) {
1794
1539
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
1795
1540
  }
1796
1541
  });
1797
1542
  }
1798
- /**
1799
- * Sends a need to update WebSocket message to all connected clients.
1800
- *
1801
- */
1802
1543
  wssSendUpdateRequired() {
1803
1544
  this.log.debug('Sending an update required message to all connected clients');
1804
1545
  this.matterbridge.matterbridgeInformation.updateRequired = true;
1805
- // Send the message to all connected clients
1806
1546
  this.webSocketServer?.clients.forEach((client) => {
1807
1547
  if (client.readyState === WebSocket.OPEN) {
1808
1548
  client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
1809
1549
  }
1810
1550
  });
1811
1551
  }
1812
- /**
1813
- * Sends a memory update message to all connected clients.
1814
- *
1815
- */
1816
1552
  wssSendCpuUpdate(cpuUsage) {
1817
1553
  this.log.debug('Sending a cpu update message to all connected clients');
1818
- // Send the message to all connected clients
1819
1554
  this.webSocketServer?.clients.forEach((client) => {
1820
1555
  if (client.readyState === WebSocket.OPEN) {
1821
1556
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
1822
1557
  }
1823
1558
  });
1824
1559
  }
1825
- /**
1826
- * Sends a cpu update message to all connected clients.
1827
- *
1828
- */
1829
1560
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
1830
1561
  this.log.debug('Sending a memory update message to all connected clients');
1831
- // Send the message to all connected clients
1832
1562
  this.webSocketServer?.clients.forEach((client) => {
1833
1563
  if (client.readyState === WebSocket.OPEN) {
1834
1564
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
1835
1565
  }
1836
1566
  });
1837
1567
  }
1838
- /**
1839
- * Sends a memory update message to all connected clients.
1840
- *
1841
- */
1842
1568
  wssSendUptimeUpdate(systemUptime, processUptime) {
1843
1569
  this.log.debug('Sending a uptime update message to all connected clients');
1844
- // Send the message to all connected clients
1845
1570
  this.webSocketServer?.clients.forEach((client) => {
1846
1571
  if (client.readyState === WebSocket.OPEN) {
1847
1572
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
1848
1573
  }
1849
1574
  });
1850
1575
  }
1851
- /**
1852
- * Sends a cpu update message to all connected clients.
1853
- * @param {string} message - The message to send.
1854
- * @param {number} timeout - The timeout in seconds for the snackbar message.
1855
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
1856
- *
1857
- */
1858
1576
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
1859
1577
  this.log.debug('Sending a snackbar message to all connected clients');
1860
- // Send the message to all connected clients
1861
1578
  this.webSocketServer?.clients.forEach((client) => {
1862
1579
  if (client.readyState === WebSocket.OPEN) {
1863
1580
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { message, timeout, severity } }));
1864
1581
  }
1865
1582
  });
1866
1583
  }
1867
- /**
1868
- * Sends an attribute update message to all connected WebSocket clients.
1869
- *
1870
- * @param {string | undefined} plugin - The name of the plugin.
1871
- * @param {string | undefined} serialNumber - The serial number of the device.
1872
- * @param {string | undefined} uniqueId - The unique identifier of the device.
1873
- * @param {string} cluster - The cluster name where the attribute belongs.
1874
- * @param {string} attribute - The name of the attribute that changed.
1875
- * @param {number | string | boolean} value - The new value of the attribute.
1876
- *
1877
- * @remarks
1878
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
1879
- * with the updated attribute information.
1880
- */
1881
1584
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
1882
1585
  this.log.debug('Sending an attribute update message to all connected clients');
1883
- // Send the message to all connected clients
1884
1586
  this.webSocketServer?.clients.forEach((client) => {
1885
1587
  if (client.readyState === WebSocket.OPEN) {
1886
1588
  client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
1887
1589
  }
1888
1590
  });
1889
1591
  }
1890
- /**
1891
- * Sends a message to all connected clients.
1892
- * @param {number} id - The message id.
1893
- * @param {string} method - The message method.
1894
- * @param {Record<string, string | number | boolean>} params - The message parameters.
1895
- *
1896
- */
1897
1592
  wssBroadcastMessage(id, method, params) {
1898
1593
  this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
1899
- // Send the message to all connected clients
1900
1594
  this.webSocketServer?.clients.forEach((client) => {
1901
1595
  if (client.readyState === WebSocket.OPEN) {
1902
1596
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -1904,4 +1598,3 @@ export class Frontend {
1904
1598
  });
1905
1599
  }
1906
1600
  }
1907
- //# sourceMappingURL=frontend.js.map