matterbridge 2.2.5 → 2.2.6-dev.2

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