matterbridge 2.2.1 → 2.2.2-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 (154) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cli.js +2 -37
  3. package/dist/cluster/export.js +0 -2
  4. package/dist/defaultConfigSchema.js +0 -23
  5. package/dist/deviceManager.js +1 -94
  6. package/dist/frontend.js +50 -291
  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 +62 -731
  16. package/dist/matterbridgeAccessoryPlatform.js +0 -33
  17. package/dist/matterbridgeBehaviors.js +1 -32
  18. package/dist/matterbridgeDeviceTypes.js +11 -112
  19. package/dist/matterbridgeDynamicPlatform.js +0 -33
  20. package/dist/matterbridgeEndpoint.js +6 -690
  21. package/dist/matterbridgeEndpointHelpers.js +9 -118
  22. package/dist/matterbridgePlatform.js +7 -185
  23. package/dist/matterbridgeTypes.js +0 -24
  24. package/dist/pluginManager.js +3 -229
  25. package/dist/shelly.js +6 -121
  26. package/dist/storage/export.js +0 -1
  27. package/dist/update.js +3 -47
  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 +6 -6
  39. package/frontend/build/index.html +1 -1
  40. package/frontend/build/static/css/{main.cf25d33e.css → main.b9449869.css} +2 -2
  41. package/frontend/build/static/css/{main.cf25d33e.css.map → main.b9449869.css.map} +1 -1
  42. package/frontend/build/static/js/{main.025c65d2.js → main.92802eb1.js} +8 -8
  43. package/frontend/build/static/js/{main.025c65d2.js.map → main.92802eb1.js.map} +1 -1
  44. package/npm-shrinkwrap.json +7 -7
  45. package/package.json +1 -2
  46. package/dist/cli.d.ts +0 -29
  47. package/dist/cli.d.ts.map +0 -1
  48. package/dist/cli.js.map +0 -1
  49. package/dist/cluster/export.d.ts +0 -2
  50. package/dist/cluster/export.d.ts.map +0 -1
  51. package/dist/cluster/export.js.map +0 -1
  52. package/dist/defaultConfigSchema.d.ts +0 -27
  53. package/dist/defaultConfigSchema.d.ts.map +0 -1
  54. package/dist/defaultConfigSchema.js.map +0 -1
  55. package/dist/deviceManager.d.ts +0 -114
  56. package/dist/deviceManager.d.ts.map +0 -1
  57. package/dist/deviceManager.js.map +0 -1
  58. package/dist/frontend.d.ts +0 -178
  59. package/dist/frontend.d.ts.map +0 -1
  60. package/dist/frontend.js.map +0 -1
  61. package/dist/index.d.ts +0 -35
  62. package/dist/index.d.ts.map +0 -1
  63. package/dist/index.js.map +0 -1
  64. package/dist/logger/export.d.ts +0 -2
  65. package/dist/logger/export.d.ts.map +0 -1
  66. package/dist/logger/export.js.map +0 -1
  67. package/dist/matter/behaviors.d.ts +0 -2
  68. package/dist/matter/behaviors.d.ts.map +0 -1
  69. package/dist/matter/behaviors.js.map +0 -1
  70. package/dist/matter/clusters.d.ts +0 -2
  71. package/dist/matter/clusters.d.ts.map +0 -1
  72. package/dist/matter/clusters.js.map +0 -1
  73. package/dist/matter/devices.d.ts +0 -2
  74. package/dist/matter/devices.d.ts.map +0 -1
  75. package/dist/matter/devices.js.map +0 -1
  76. package/dist/matter/endpoints.d.ts +0 -2
  77. package/dist/matter/endpoints.d.ts.map +0 -1
  78. package/dist/matter/endpoints.js.map +0 -1
  79. package/dist/matter/export.d.ts +0 -5
  80. package/dist/matter/export.d.ts.map +0 -1
  81. package/dist/matter/export.js.map +0 -1
  82. package/dist/matter/types.d.ts +0 -3
  83. package/dist/matter/types.d.ts.map +0 -1
  84. package/dist/matter/types.js.map +0 -1
  85. package/dist/matterbridge.d.ts +0 -411
  86. package/dist/matterbridge.d.ts.map +0 -1
  87. package/dist/matterbridge.js.map +0 -1
  88. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
  89. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  90. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  91. package/dist/matterbridgeBehaviors.d.ts +0 -1056
  92. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  93. package/dist/matterbridgeBehaviors.js.map +0 -1
  94. package/dist/matterbridgeDeviceTypes.d.ts +0 -177
  95. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  96. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  97. package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
  98. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  99. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  100. package/dist/matterbridgeEndpoint.d.ts +0 -835
  101. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  102. package/dist/matterbridgeEndpoint.js.map +0 -1
  103. package/dist/matterbridgeEndpointHelpers.d.ts +0 -2275
  104. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  105. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  106. package/dist/matterbridgePlatform.d.ts +0 -251
  107. package/dist/matterbridgePlatform.d.ts.map +0 -1
  108. package/dist/matterbridgePlatform.js.map +0 -1
  109. package/dist/matterbridgeTypes.d.ts +0 -177
  110. package/dist/matterbridgeTypes.d.ts.map +0 -1
  111. package/dist/matterbridgeTypes.js.map +0 -1
  112. package/dist/pluginManager.d.ts +0 -236
  113. package/dist/pluginManager.d.ts.map +0 -1
  114. package/dist/pluginManager.js.map +0 -1
  115. package/dist/shelly.d.ts +0 -77
  116. package/dist/shelly.d.ts.map +0 -1
  117. package/dist/shelly.js.map +0 -1
  118. package/dist/storage/export.d.ts +0 -2
  119. package/dist/storage/export.d.ts.map +0 -1
  120. package/dist/storage/export.js.map +0 -1
  121. package/dist/update.d.ts +0 -32
  122. package/dist/update.d.ts.map +0 -1
  123. package/dist/update.js.map +0 -1
  124. package/dist/utils/colorUtils.d.ts +0 -61
  125. package/dist/utils/colorUtils.d.ts.map +0 -1
  126. package/dist/utils/colorUtils.js.map +0 -1
  127. package/dist/utils/copyDirectory.d.ts +0 -32
  128. package/dist/utils/copyDirectory.d.ts.map +0 -1
  129. package/dist/utils/copyDirectory.js.map +0 -1
  130. package/dist/utils/createZip.d.ts +0 -38
  131. package/dist/utils/createZip.d.ts.map +0 -1
  132. package/dist/utils/createZip.js.map +0 -1
  133. package/dist/utils/deepCopy.d.ts +0 -31
  134. package/dist/utils/deepCopy.d.ts.map +0 -1
  135. package/dist/utils/deepCopy.js.map +0 -1
  136. package/dist/utils/deepEqual.d.ts +0 -53
  137. package/dist/utils/deepEqual.d.ts.map +0 -1
  138. package/dist/utils/deepEqual.js.map +0 -1
  139. package/dist/utils/export.d.ts +0 -10
  140. package/dist/utils/export.d.ts.map +0 -1
  141. package/dist/utils/export.js.map +0 -1
  142. package/dist/utils/isvalid.d.ts +0 -87
  143. package/dist/utils/isvalid.d.ts.map +0 -1
  144. package/dist/utils/isvalid.js.map +0 -1
  145. package/dist/utils/network.d.ts +0 -70
  146. package/dist/utils/network.d.ts.map +0 -1
  147. package/dist/utils/network.js.map +0 -1
  148. package/dist/utils/parameter.d.ts +0 -44
  149. package/dist/utils/parameter.d.ts.map +0 -1
  150. package/dist/utils/parameter.js.map +0 -1
  151. package/dist/utils/wait.d.ts +0 -43
  152. package/dist/utils/wait.d.ts.map +0 -1
  153. package/dist/utils/wait.js.map +0 -1
  154. /package/frontend/build/static/js/{main.025c65d2.js.LICENSE.txt → main.92802eb1.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 https from 'https';
28
4
  import express from 'express';
@@ -30,71 +6,21 @@ import WebSocket, { WebSocketServer } from 'ws';
30
6
  import os from 'node:os';
31
7
  import path from 'node:path';
32
8
  import { promises as fs } from 'node:fs';
33
- // AnsiLogger module
34
9
  import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW } from './logger/export.js';
35
- // Matterbridge
36
10
  import { createZip, deepCopy, isValidArray, isValidNumber, isValidObject, isValidString } from './utils/export.js';
37
11
  import { plg } from './matterbridgeTypes.js';
38
12
  import { hasParameter } from './utils/export.js';
39
13
  import { BridgedDeviceBasicInformation } from '@matter/main/clusters';
40
- /**
41
- * Websocket message ID for logging.
42
- * @constant {number}
43
- */
44
14
  export const WS_ID_LOG = 0;
45
- /**
46
- * Websocket message ID indicating a refresh is needed.
47
- * @constant {number}
48
- */
49
15
  export const WS_ID_REFRESH_NEEDED = 1;
50
- /**
51
- * Websocket message ID indicating a restart is needed.
52
- * @constant {number}
53
- */
54
16
  export const WS_ID_RESTART_NEEDED = 2;
55
- /**
56
- * Websocket message ID indicating a cpu update.
57
- * @constant {number}
58
- */
59
17
  export const WS_ID_CPU_UPDATE = 3;
60
- /**
61
- * Websocket message ID indicating a memory update.
62
- * @constant {number}
63
- */
64
18
  export const WS_ID_MEMORY_UPDATE = 4;
65
- /**
66
- * Websocket message ID indicating an uptime update.
67
- * @constant {number}
68
- */
69
19
  export const WS_ID_UPTIME_UPDATE = 5;
70
- /**
71
- * Websocket message ID indicating a memory update.
72
- * @constant {number}
73
- */
74
20
  export const WS_ID_SNACKBAR = 6;
75
- /**
76
- * Websocket message ID indicating a shelly system update.
77
- * check:
78
- * curl -k http://127.0.0.1:8101/api/updates/sys/check
79
- * perform:
80
- * curl -k http://127.0.0.1:8101/api/updates/sys/perform
81
- * @constant {number}
82
- */
21
+ export const WS_ID_UPDATE_NEEDED = 7;
83
22
  export const WS_ID_SHELLY_SYS_UPDATE = 100;
84
- /**
85
- * Websocket message ID indicating a shelly main update.
86
- * check:
87
- * curl -k http://127.0.0.1:8101/api/updates/main/check
88
- * perform:
89
- * curl -k http://127.0.0.1:8101/api/updates/main/perform
90
- * @constant {number}
91
- */
92
23
  export const WS_ID_SHELLY_MAIN_UPDATE = 101;
93
- /**
94
- * Initializes the frontend of Matterbridge.
95
- *
96
- * @param port The port number to run the frontend server on. Default is 8283.
97
- */
98
24
  export class Frontend {
99
25
  matterbridge;
100
26
  log;
@@ -111,7 +37,7 @@ export class Frontend {
111
37
  memoryTimeout;
112
38
  constructor(matterbridge) {
113
39
  this.matterbridge = matterbridge;
114
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
40
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
115
41
  }
116
42
  set logLevel(logLevel) {
117
43
  this.log.logLevel = logLevel;
@@ -119,21 +45,10 @@ export class Frontend {
119
45
  async start(port = 8283) {
120
46
  this.port = port;
121
47
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
122
- // Create the express app that serves the frontend
123
48
  this.expressApp = express();
124
- // Log all requests to the server for debugging
125
- /*
126
- this.expressApp.use((req, res, next) => {
127
- this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
128
- next();
129
- });
130
- */
131
- // Serve static files from '/static' endpoint
132
49
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
133
50
  if (!hasParameter('ssl')) {
134
- // Create an HTTP server and attach the express app
135
51
  this.httpServer = createServer(this.expressApp);
136
- // Listen on the specified port
137
52
  if (hasParameter('ingress')) {
138
53
  this.httpServer.listen(this.port, '0.0.0.0', () => {
139
54
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -147,7 +62,6 @@ export class Frontend {
147
62
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
148
63
  });
149
64
  }
150
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
151
65
  this.httpServer.on('error', (error) => {
152
66
  this.log.error(`Frontend http server error listening on ${this.port}`);
153
67
  switch (error.code) {
@@ -163,7 +77,6 @@ export class Frontend {
163
77
  });
164
78
  }
165
79
  else {
166
- // Load the SSL certificate, the private key and optionally the CA certificate
167
80
  let cert;
168
81
  try {
169
82
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
@@ -191,9 +104,7 @@ export class Frontend {
191
104
  this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
192
105
  }
193
106
  const serverOptions = { cert, key, ca };
194
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
195
107
  this.httpsServer = https.createServer(serverOptions, this.expressApp);
196
- // Listen on the specified port
197
108
  if (hasParameter('ingress')) {
198
109
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
199
110
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -207,7 +118,6 @@ export class Frontend {
207
118
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
208
119
  });
209
120
  }
210
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
211
121
  this.httpsServer.on('error', (error) => {
212
122
  this.log.error(`Frontend https server error listening on ${this.port}`);
213
123
  switch (error.code) {
@@ -224,18 +134,16 @@ export class Frontend {
224
134
  }
225
135
  if (this.initializeError)
226
136
  return;
227
- // Create a WebSocket server and attach it to the http or https server
228
137
  const wssPort = this.port;
229
138
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
230
139
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
231
140
  this.webSocketServer.on('connection', (ws, request) => {
232
141
  const clientIp = request.socket.remoteAddress;
233
- // Set the global logger callback for the WebSocketServer
234
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
235
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
236
- callbackLogLevel = "info" /* LogLevel.INFO */;
237
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
238
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
142
+ let callbackLogLevel = "notice";
143
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
144
+ callbackLogLevel = "info";
145
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
146
+ callbackLogLevel = "debug";
239
147
  AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
240
148
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
241
149
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -269,7 +177,6 @@ export class Frontend {
269
177
  this.webSocketServer.on('error', (ws, error) => {
270
178
  this.log.error(`WebSocketServer error: ${error}`);
271
179
  });
272
- // Subscribe to cli events
273
180
  const { cliEmitter } = await import('./cli.js');
274
181
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
275
182
  this.wssSendUptimeUpdate(systemUptime, processUptime);
@@ -280,7 +187,6 @@ export class Frontend {
280
187
  cliEmitter.on('cpu', (cpuUsage) => {
281
188
  this.wssSendCpuUpdate(cpuUsage);
282
189
  });
283
- // Endpoint to validate login code
284
190
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
285
191
  const { password } = req.body;
286
192
  this.log.debug('The frontend sent /api/login', password);
@@ -299,27 +205,23 @@ export class Frontend {
299
205
  this.log.warn('/api/login error wrong password');
300
206
  res.json({ valid: false });
301
207
  }
302
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
303
208
  }
304
209
  catch (error) {
305
210
  this.log.error('/api/login error getting password');
306
211
  res.json({ valid: false });
307
212
  }
308
213
  });
309
- // Endpoint to provide health check
310
214
  this.expressApp.get('/health', (req, res) => {
311
215
  this.log.debug('Express received /health');
312
216
  const healthStatus = {
313
- status: 'ok', // Indicate service is healthy
314
- uptime: process.uptime(), // Server uptime in seconds
315
- timestamp: new Date().toISOString(), // Current timestamp
217
+ status: 'ok',
218
+ uptime: process.uptime(),
219
+ timestamp: new Date().toISOString(),
316
220
  };
317
221
  res.status(200).json(healthStatus);
318
222
  });
319
- // Endpoint to provide memory usage details
320
223
  this.expressApp.get('/memory', async (req, res) => {
321
224
  this.log.debug('Express received /memory');
322
- // Memory usage from process
323
225
  const memoryUsageRaw = process.memoryUsage();
324
226
  const memoryUsage = {
325
227
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -328,13 +230,10 @@ export class Frontend {
328
230
  external: this.formatMemoryUsage(memoryUsageRaw.external),
329
231
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
330
232
  };
331
- // V8 heap statistics
332
233
  const { default: v8 } = await import('node:v8');
333
234
  const heapStatsRaw = v8.getHeapStatistics();
334
235
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
335
- // Format heapStats
336
236
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
337
- // Format heapSpaces
338
237
  const heapSpaces = heapSpacesRaw.map((space) => ({
339
238
  ...space,
340
239
  space_size: this.formatMemoryUsage(space.space_size),
@@ -352,7 +251,6 @@ export class Frontend {
352
251
  };
353
252
  res.status(200).json(memoryReport);
354
253
  });
355
- // Endpoint to start advertising the server node
356
254
  this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
357
255
  const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
358
256
  if (pairingCodes) {
@@ -363,22 +261,18 @@ export class Frontend {
363
261
  res.status(500).json({ error: 'Failed to generate pairing codes' });
364
262
  }
365
263
  });
366
- // Endpoint to provide settings
367
264
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
368
265
  this.log.debug('The frontend sent /api/settings');
369
266
  res.json(await this.getApiSettings());
370
267
  });
371
- // Endpoint to provide plugins
372
268
  this.expressApp.get('/api/plugins', async (req, res) => {
373
269
  this.log.debug('The frontend sent /api/plugins');
374
270
  res.json(this.getBaseRegisteredPlugins());
375
271
  });
376
- // Endpoint to provide devices
377
272
  this.expressApp.get('/api/devices', (req, res) => {
378
273
  this.log.debug('The frontend sent /api/devices');
379
274
  const devices = [];
380
275
  this.matterbridge.devices.forEach(async (device) => {
381
- // Check if the device has the required properties
382
276
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
383
277
  return;
384
278
  const cluster = this.getClusterTextFromDevice(device);
@@ -397,7 +291,6 @@ export class Frontend {
397
291
  });
398
292
  res.json(devices);
399
293
  });
400
- // Endpoint to provide the cluster servers of the devices
401
294
  this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
402
295
  const selectedPluginName = req.params.selectedPluginName;
403
296
  const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
@@ -470,7 +363,6 @@ export class Frontend {
470
363
  });
471
364
  res.json(data);
472
365
  });
473
- // Endpoint to view the log
474
366
  this.expressApp.get('/api/view-log', async (req, res) => {
475
367
  this.log.debug('The frontend sent /api/log');
476
368
  try {
@@ -483,12 +375,10 @@ export class Frontend {
483
375
  res.status(500).send('Error reading log file');
484
376
  }
485
377
  });
486
- // Endpoint to download the matterbridge log
487
378
  this.expressApp.get('/api/download-mblog', async (req, res) => {
488
379
  this.log.debug('The frontend sent /api/download-mblog');
489
380
  try {
490
381
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
491
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
492
382
  }
493
383
  catch (error) {
494
384
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -500,12 +390,10 @@ export class Frontend {
500
390
  }
501
391
  });
502
392
  });
503
- // Endpoint to download the matter log
504
393
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
505
394
  this.log.debug('The frontend sent /api/download-mjlog');
506
395
  try {
507
396
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
508
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
509
397
  }
510
398
  catch (error) {
511
399
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
@@ -517,12 +405,10 @@ export class Frontend {
517
405
  }
518
406
  });
519
407
  });
520
- // Endpoint to download the matter log
521
408
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
522
409
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
523
410
  try {
524
411
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
525
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
526
412
  }
527
413
  catch (error) {
528
414
  fs.appendFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'Create the Shelly system log before downloading it.');
@@ -534,7 +420,6 @@ export class Frontend {
534
420
  }
535
421
  });
536
422
  });
537
- // Endpoint to download the matter storage file
538
423
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
539
424
  this.log.debug('The frontend sent /api/download-mjstorage');
540
425
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
@@ -545,7 +430,6 @@ export class Frontend {
545
430
  }
546
431
  });
547
432
  });
548
- // Endpoint to download the matterbridge storage directory
549
433
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
550
434
  this.log.debug('The frontend sent /api/download-mbstorage');
551
435
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
@@ -556,7 +440,6 @@ export class Frontend {
556
440
  }
557
441
  });
558
442
  });
559
- // Endpoint to download the matterbridge plugin directory
560
443
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
561
444
  this.log.debug('The frontend sent /api/download-pluginstorage');
562
445
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
@@ -567,11 +450,9 @@ export class Frontend {
567
450
  }
568
451
  });
569
452
  });
570
- // Endpoint to download the matterbridge plugin config files
571
453
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
572
454
  this.log.debug('The frontend sent /api/download-pluginconfig');
573
455
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
574
- // 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')));
575
456
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
576
457
  if (error) {
577
458
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
@@ -579,7 +460,6 @@ export class Frontend {
579
460
  }
580
461
  });
581
462
  });
582
- // Endpoint to download the matterbridge plugin config files
583
463
  this.expressApp.get('/api/download-backup', async (req, res) => {
584
464
  this.log.debug('The frontend sent /api/download-backup');
585
465
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -589,7 +469,6 @@ export class Frontend {
589
469
  }
590
470
  });
591
471
  });
592
- // Endpoint to receive commands
593
472
  this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
594
473
  const command = req.params.command;
595
474
  let param = req.params.param;
@@ -599,15 +478,13 @@ export class Frontend {
599
478
  return;
600
479
  }
601
480
  this.log.debug(`Received frontend command: ${command}:${param}`);
602
- // Handle the command setpassword from Settings
603
481
  if (command === 'setpassword') {
604
- const password = param.slice(1, -1); // Remove the first and last characters
482
+ const password = param.slice(1, -1);
605
483
  this.log.debug('setpassword', param, password);
606
484
  await this.matterbridge.nodeContext?.set('password', password);
607
485
  res.json({ message: 'Command received' });
608
486
  return;
609
487
  }
610
- // Handle the command setbridgemode from Settings
611
488
  if (command === 'setbridgemode') {
612
489
  this.log.debug(`setbridgemode: ${param}`);
613
490
  this.wssSendRestartRequired();
@@ -615,7 +492,6 @@ export class Frontend {
615
492
  res.json({ message: 'Command received' });
616
493
  return;
617
494
  }
618
- // Handle the command backup from Settings
619
495
  if (command === 'backup') {
620
496
  this.log.notice(`Prepairing the backup...`);
621
497
  await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
@@ -624,33 +500,31 @@ export class Frontend {
624
500
  res.json({ message: 'Command received' });
625
501
  return;
626
502
  }
627
- // Handle the command setmbloglevel from Settings
628
503
  if (command === 'setmbloglevel') {
629
504
  this.log.debug('Matterbridge log level:', param);
630
505
  if (param === 'Debug') {
631
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
506
+ this.log.logLevel = "debug";
632
507
  }
633
508
  else if (param === 'Info') {
634
- this.log.logLevel = "info" /* LogLevel.INFO */;
509
+ this.log.logLevel = "info";
635
510
  }
636
511
  else if (param === 'Notice') {
637
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
512
+ this.log.logLevel = "notice";
638
513
  }
639
514
  else if (param === 'Warn') {
640
- this.log.logLevel = "warn" /* LogLevel.WARN */;
515
+ this.log.logLevel = "warn";
641
516
  }
642
517
  else if (param === 'Error') {
643
- this.log.logLevel = "error" /* LogLevel.ERROR */;
518
+ this.log.logLevel = "error";
644
519
  }
645
520
  else if (param === 'Fatal') {
646
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
521
+ this.log.logLevel = "fatal";
647
522
  }
648
523
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
649
524
  await this.matterbridge.setLogLevel(this.log.logLevel);
650
525
  res.json({ message: 'Command received' });
651
526
  return;
652
527
  }
653
- // Handle the command setmbloglevel from Settings
654
528
  if (command === 'setmjloglevel') {
655
529
  this.log.debug('Matter.js log level:', param);
656
530
  if (param === 'Debug') {
@@ -675,34 +549,30 @@ export class Frontend {
675
549
  res.json({ message: 'Command received' });
676
550
  return;
677
551
  }
678
- // Handle the command setmdnsinterface from Settings
679
552
  if (command === 'setmdnsinterface') {
680
- param = param.slice(1, -1); // Remove the first and last characters *mdns*
553
+ param = param.slice(1, -1);
681
554
  this.matterbridge.matterbridgeInformation.mattermdnsinterface = param;
682
555
  this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
683
556
  await this.matterbridge.nodeContext?.set('mattermdnsinterface', param);
684
557
  res.json({ message: 'Command received' });
685
558
  return;
686
559
  }
687
- // Handle the command setipv4address from Settings
688
560
  if (command === 'setipv4address') {
689
- param = param.slice(1, -1); // Remove the first and last characters *ip*
561
+ param = param.slice(1, -1);
690
562
  this.matterbridge.matterbridgeInformation.matteripv4address = param;
691
563
  this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
692
564
  await this.matterbridge.nodeContext?.set('matteripv4address', param);
693
565
  res.json({ message: 'Command received' });
694
566
  return;
695
567
  }
696
- // Handle the command setipv6address from Settings
697
568
  if (command === 'setipv6address') {
698
- param = param.slice(1, -1); // Remove the first and last characters *ip*
569
+ param = param.slice(1, -1);
699
570
  this.matterbridge.matterbridgeInformation.matteripv6address = param;
700
571
  this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
701
572
  await this.matterbridge.nodeContext?.set('matteripv6address', param);
702
573
  res.json({ message: 'Command received' });
703
574
  return;
704
575
  }
705
- // Handle the command setmatterport from Settings
706
576
  if (command === 'setmatterport') {
707
577
  const port = Math.min(Math.max(parseInt(param), 5540), 5560);
708
578
  this.matterbridge.matterbridgeInformation.matterPort = port;
@@ -711,7 +581,6 @@ export class Frontend {
711
581
  res.json({ message: 'Command received' });
712
582
  return;
713
583
  }
714
- // Handle the command setmatterdiscriminator from Settings
715
584
  if (command === 'setmatterdiscriminator') {
716
585
  const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
717
586
  this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
@@ -720,7 +589,6 @@ export class Frontend {
720
589
  res.json({ message: 'Command received' });
721
590
  return;
722
591
  }
723
- // Handle the command setmatterpasscode from Settings
724
592
  if (command === 'setmatterpasscode') {
725
593
  const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
726
594
  this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
@@ -729,20 +597,17 @@ export class Frontend {
729
597
  res.json({ message: 'Command received' });
730
598
  return;
731
599
  }
732
- // Handle the command setmbloglevel from Settings
733
600
  if (command === 'setmblogfile') {
734
601
  this.log.debug('Matterbridge file log:', param);
735
602
  this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
736
603
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
737
- // Create the file logger for matterbridge
738
604
  if (param === 'true')
739
- AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
605
+ AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
740
606
  else
741
607
  AnsiLogger.setGlobalLogfile(undefined);
742
608
  res.json({ message: 'Command received' });
743
609
  return;
744
610
  }
745
- // Handle the command setmbloglevel from Settings
746
611
  if (command === 'setmjlogfile') {
747
612
  this.log.debug('Matter file log:', param);
748
613
  this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
@@ -769,48 +634,40 @@ export class Frontend {
769
634
  res.json({ message: 'Command received' });
770
635
  return;
771
636
  }
772
- // Handle the command unregister from Settings
773
637
  if (command === 'unregister') {
774
638
  await this.matterbridge.unregisterAndShutdownProcess();
775
639
  res.json({ message: 'Command received' });
776
640
  return;
777
641
  }
778
- // Handle the command reset from Settings
779
642
  if (command === 'reset') {
780
643
  await this.matterbridge.shutdownProcessAndReset();
781
644
  res.json({ message: 'Command received' });
782
645
  return;
783
646
  }
784
- // Handle the command factoryreset from Settings
785
647
  if (command === 'factoryreset') {
786
648
  await this.matterbridge.shutdownProcessAndFactoryReset();
787
649
  res.json({ message: 'Command received' });
788
650
  return;
789
651
  }
790
- // Handle the command shutdown from Header
791
652
  if (command === 'shutdown') {
792
653
  await this.matterbridge.shutdownProcess();
793
654
  res.json({ message: 'Command received' });
794
655
  return;
795
656
  }
796
- // Handle the command restart from Header
797
657
  if (command === 'restart') {
798
658
  await this.matterbridge.restartProcess();
799
659
  res.json({ message: 'Command received' });
800
660
  return;
801
661
  }
802
- // Handle the command update from Header
803
662
  if (command === 'update') {
804
663
  await this.matterbridge.updateProcess();
805
664
  this.wssSendRestartRequired();
806
665
  res.json({ message: 'Command received' });
807
666
  return;
808
667
  }
809
- // Handle the command saveconfig from Home
810
668
  if (command === 'saveconfig') {
811
669
  param = param.replace(/\*/g, '\\');
812
670
  this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
813
- // console.log('Req.body:', JSON.stringify(req.body, null, 2));
814
671
  if (!this.matterbridge.plugins.has(param)) {
815
672
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
816
673
  }
@@ -825,7 +682,6 @@ export class Frontend {
825
682
  res.json({ message: 'Command received' });
826
683
  return;
827
684
  }
828
- // Handle the command installplugin from Home
829
685
  if (command === 'installplugin') {
830
686
  param = param.replace(/\*/g, '\\');
831
687
  this.log.info(`Installing plugin ${plg}${param}${nf}...`);
@@ -834,7 +690,6 @@ export class Frontend {
834
690
  await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
835
691
  this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
836
692
  this.wssSendSnackbarMessage(`Installed package ${param}`);
837
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
838
693
  }
839
694
  catch (error) {
840
695
  this.log.error(`Error installing plugin ${plg}${param}${er}`);
@@ -842,47 +697,40 @@ export class Frontend {
842
697
  }
843
698
  this.wssSendRestartRequired();
844
699
  param = param.split('@')[0];
845
- // Also add the plugin to matterbridge so no return!
846
700
  if (param === 'matterbridge') {
847
- // 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
848
701
  res.json({ message: 'Command received' });
849
702
  return;
850
703
  }
851
704
  }
852
- // Handle the command addplugin from Home
853
705
  if (command === 'addplugin' || command === 'installplugin') {
854
706
  param = param.replace(/\*/g, '\\');
855
707
  const plugin = await this.matterbridge.plugins.add(param);
856
708
  if (plugin) {
857
709
  this.wssSendSnackbarMessage(`Added plugin ${param}`);
858
710
  if (this.matterbridge.bridgeMode === 'childbridge') {
859
- // 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
860
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
861
711
  this.matterbridge.createDynamicPlugin(plugin, true);
862
712
  }
863
713
  this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
864
- this.wssSendRefreshRequired();
714
+ this.wssSendRefreshRequired('plugins');
865
715
  });
866
716
  }
867
717
  res.json({ message: 'Command received' });
868
718
  return;
869
719
  }
870
- // Handle the command removeplugin from Home
871
720
  if (command === 'removeplugin') {
872
721
  if (!this.matterbridge.plugins.has(param)) {
873
722
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
874
723
  }
875
724
  else {
876
725
  const plugin = this.matterbridge.plugins.get(param);
877
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true); // This will also close the server node in childbridge mode
726
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
878
727
  await this.matterbridge.plugins.remove(param);
879
728
  this.wssSendSnackbarMessage(`Removed plugin ${param}`);
729
+ this.wssSendRefreshRequired('plugins');
880
730
  }
881
731
  res.json({ message: 'Command received' });
882
- this.wssSendRefreshRequired();
883
732
  return;
884
733
  }
885
- // Handle the command enableplugin from Home
886
734
  if (command === 'enableplugin') {
887
735
  if (!this.matterbridge.plugins.has(param)) {
888
736
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -901,19 +749,16 @@ export class Frontend {
901
749
  await this.matterbridge.plugins.enable(param);
902
750
  this.wssSendSnackbarMessage(`Enabled plugin ${param}`);
903
751
  if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
904
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
905
752
  this.matterbridge.createDynamicPlugin(plugin, true);
906
753
  }
907
754
  this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
908
- this.wssSendRefreshRequired();
755
+ this.wssSendRefreshRequired('plugins');
909
756
  });
910
757
  }
911
758
  }
912
759
  res.json({ message: 'Command received' });
913
- this.wssSendRefreshRequired();
914
760
  return;
915
761
  }
916
- // Handle the command disableplugin from Home
917
762
  if (command === 'disableplugin') {
918
763
  if (!this.matterbridge.plugins.has(param)) {
919
764
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -921,17 +766,16 @@ export class Frontend {
921
766
  else {
922
767
  const plugin = this.matterbridge.plugins.get(param);
923
768
  if (plugin && plugin.enabled) {
924
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true); // This will also close the server node in childbridge mode
769
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
925
770
  await this.matterbridge.plugins.disable(param);
926
771
  this.wssSendSnackbarMessage(`Disabled plugin ${param}`);
772
+ this.wssSendRefreshRequired('plugins');
927
773
  }
928
774
  }
929
775
  res.json({ message: 'Command received' });
930
- this.wssSendRefreshRequired();
931
776
  return;
932
777
  }
933
778
  });
934
- // Fallback for routing (must be the last route)
935
779
  this.expressApp.get('*', (req, res) => {
936
780
  this.log.debug('The frontend sent:', req.url);
937
781
  this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -940,29 +784,24 @@ export class Frontend {
940
784
  this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
941
785
  }
942
786
  async stop() {
943
- // Close the http server
944
787
  if (this.httpServer) {
945
788
  this.httpServer.close();
946
789
  this.httpServer.removeAllListeners();
947
790
  this.httpServer = undefined;
948
791
  this.log.debug('Frontend http server closed successfully');
949
792
  }
950
- // Close the https server
951
793
  if (this.httpsServer) {
952
794
  this.httpsServer.close();
953
795
  this.httpsServer.removeAllListeners();
954
796
  this.httpsServer = undefined;
955
797
  this.log.debug('Frontend https server closed successfully');
956
798
  }
957
- // Remove listeners from the express app
958
799
  if (this.expressApp) {
959
800
  this.expressApp.removeAllListeners();
960
801
  this.expressApp = undefined;
961
802
  this.log.debug('Frontend app closed successfully');
962
803
  }
963
- // Close the WebSocket server
964
804
  if (this.webSocketServer) {
965
- // Close all active connections
966
805
  this.webSocketServer.clients.forEach((client) => {
967
806
  if (client.readyState === WebSocket.OPEN) {
968
807
  client.close();
@@ -979,7 +818,6 @@ export class Frontend {
979
818
  this.webSocketServer = undefined;
980
819
  }
981
820
  }
982
- // Function to format bytes to KB, MB, or GB
983
821
  formatMemoryUsage = (bytes) => {
984
822
  if (bytes >= 1024 ** 3) {
985
823
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -991,7 +829,6 @@ export class Frontend {
991
829
  return `${(bytes / 1024).toFixed(2)} KB`;
992
830
  }
993
831
  };
994
- // Function to format system uptime with only the most significant unit
995
832
  formatOsUpTime = (seconds) => {
996
833
  if (seconds >= 86400) {
997
834
  const days = Math.floor(seconds / 86400);
@@ -1007,13 +844,8 @@ export class Frontend {
1007
844
  }
1008
845
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
1009
846
  };
1010
- /**
1011
- * Retrieves the api settings data.
1012
- * @returns {Promise<object>} A promise that resolve in the api settings object.
1013
- */
1014
847
  async getApiSettings() {
1015
848
  const { lastCpuUsage } = await import('./cli.js');
1016
- // Update the system information
1017
849
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
1018
850
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
1019
851
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -1022,7 +854,6 @@ export class Frontend {
1022
854
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
1023
855
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
1024
856
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
1025
- // Update the matterbridge information
1026
857
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
1027
858
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
1028
859
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
@@ -1041,11 +872,6 @@ export class Frontend {
1041
872
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
1042
873
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
1043
874
  }
1044
- /**
1045
- * Retrieves the reachable attribute.
1046
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
1047
- * @returns {boolean} The reachable attribute.
1048
- */
1049
875
  getReachability(device) {
1050
876
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
1051
877
  return false;
@@ -1055,11 +881,6 @@ export class Frontend {
1055
881
  return true;
1056
882
  return false;
1057
883
  }
1058
- /**
1059
- * Retrieves the cluster text description from a given device.
1060
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
1061
- * @returns {string} The attributes description of the cluster servers in the device.
1062
- */
1063
884
  getClusterTextFromDevice(device) {
1064
885
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
1065
886
  return '';
@@ -1100,7 +921,6 @@ export class Frontend {
1100
921
  };
1101
922
  let attributes = '';
1102
923
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1103
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
1104
924
  if (typeof attributeValue === 'undefined')
1105
925
  return;
1106
926
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -1178,13 +998,8 @@ export class Frontend {
1178
998
  if (clusterName === 'userLabel' && attributeName === 'labelList')
1179
999
  attributes += `${getUserLabel(device)} `;
1180
1000
  });
1181
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
1182
1001
  return attributes.trimStart().trimEnd();
1183
1002
  }
1184
- /**
1185
- * Retrieves the base registered plugins sanitized for res.json().
1186
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
1187
- */
1188
1003
  getBaseRegisteredPlugins() {
1189
1004
  const baseRegisteredPlugins = [];
1190
1005
  for (const plugin of this.matterbridge.plugins) {
@@ -1217,13 +1032,6 @@ export class Frontend {
1217
1032
  }
1218
1033
  return baseRegisteredPlugins;
1219
1034
  }
1220
- /**
1221
- * Handles incoming websocket messages for the Matterbridge frontend.
1222
- *
1223
- * @param {WebSocket} client - The websocket client that sent the message.
1224
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1225
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1226
- */
1227
1035
  async wsMessageHandler(client, message) {
1228
1036
  let data;
1229
1037
  try {
@@ -1344,7 +1152,7 @@ export class Frontend {
1344
1152
  this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
1345
1153
  this.matterbridge.matterbridgeQrPairingCode = pairingCodes?.qrPairingCode;
1346
1154
  this.matterbridge.matterbridgeManualPairingCode = pairingCodes?.manualPairingCode;
1347
- this.wssSendRefreshRequired();
1155
+ this.wssSendRefreshRequired('matterbridgeAdvertise');
1348
1156
  this.wssSendSnackbarMessage(`Started fabrics share`, 0);
1349
1157
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes }));
1350
1158
  return;
@@ -1352,7 +1160,7 @@ export class Frontend {
1352
1160
  else if (data.method === '/api/stopadvertise') {
1353
1161
  await this.matterbridge.stopAdvertiseServerNode(this.matterbridge.serverNode);
1354
1162
  this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = false;
1355
- this.wssSendRefreshRequired();
1163
+ this.wssSendRefreshRequired('matterbridgeAdvertise');
1356
1164
  this.wssSendSnackbarMessage(`Stopped fabrics share`, 0);
1357
1165
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src }));
1358
1166
  return;
@@ -1369,10 +1177,8 @@ export class Frontend {
1369
1177
  else if (data.method === '/api/devices') {
1370
1178
  const devices = [];
1371
1179
  this.matterbridge.devices.forEach(async (device) => {
1372
- // Filter by pluginName if provided
1373
1180
  if (data.params.pluginName && data.params.pluginName !== device.plugin)
1374
1181
  return;
1375
- // Check if the device has the required properties
1376
1182
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1377
1183
  return;
1378
1184
  const cluster = this.getClusterTextFromDevice(device);
@@ -1456,7 +1262,6 @@ export class Frontend {
1456
1262
  });
1457
1263
  endpointServer.getChildEndpoints().forEach((childEndpoint) => {
1458
1264
  deviceTypes = [];
1459
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1460
1265
  const name = childEndpoint.endpoint?.id;
1461
1266
  const clusterServers = childEndpoint.getAllClusterServers();
1462
1267
  clusterServers.forEach((clusterServer) => {
@@ -1510,7 +1315,6 @@ export class Frontend {
1510
1315
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select' }));
1511
1316
  return;
1512
1317
  }
1513
- // const selectDeviceValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectDevice.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
1514
1318
  const selectDeviceValues = plugin.platform?.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1515
1319
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectDeviceValues }));
1516
1320
  return;
@@ -1525,7 +1329,6 @@ export class Frontend {
1525
1329
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' }));
1526
1330
  return;
1527
1331
  }
1528
- // const selectEntityValues = plugin.platform?.selectDevice ? Array.from(plugin.platform.selectEntity.values()).sort((keyA, keyB) => keyA.name.localeCompare(keyB.name)) : [];
1529
1332
  const selectEntityValues = plugin.platform?.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1530
1333
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, response: selectEntityValues }));
1531
1334
  return;
@@ -1546,13 +1349,11 @@ export class Frontend {
1546
1349
  if (config.postfix) {
1547
1350
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1548
1351
  }
1549
- // Add the serial to the whiteList if the whiteList exists and the serial is not already in it
1550
1352
  if (isValidArray(config.whiteList, 1)) {
1551
1353
  if (!config.whiteList.includes(data.params.serial)) {
1552
1354
  config.whiteList.push(data.params.serial);
1553
1355
  }
1554
1356
  }
1555
- // Remove the serial from the blackList if the blackList exists and the serial is in it
1556
1357
  if (isValidArray(config.blackList, 1)) {
1557
1358
  if (config.blackList.includes(data.params.serial)) {
1558
1359
  config.blackList = config.blackList.filter((serial) => serial !== data.params.serial);
@@ -1561,7 +1362,7 @@ export class Frontend {
1561
1362
  if (plugin.platform)
1562
1363
  plugin.platform.config = config;
1563
1364
  await this.matterbridge.plugins.saveConfigFromPlugin(plugin);
1564
- this.wssSendRestartRequired();
1365
+ this.wssSendRestartRequired(false);
1565
1366
  }
1566
1367
  }
1567
1368
  else if (data.params.command === 'unselectdevice' && isValidString(data.params.plugin, 10) && isValidString(data.params.serial, 1)) {
@@ -1575,7 +1376,11 @@ export class Frontend {
1575
1376
  if (config.postfix) {
1576
1377
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1577
1378
  }
1578
- // Add the serial to the blackList
1379
+ if (isValidArray(config.whiteList, 1)) {
1380
+ if (config.whiteList.includes(data.params.serial)) {
1381
+ config.whiteList = config.whiteList.filter((serial) => serial !== data.params.serial);
1382
+ }
1383
+ }
1579
1384
  if (isValidArray(config.blackList)) {
1580
1385
  if (!config.blackList.includes(data.params.serial)) {
1581
1386
  config.blackList.push(data.params.serial);
@@ -1584,7 +1389,7 @@ export class Frontend {
1584
1389
  if (plugin.platform)
1585
1390
  plugin.platform.config = config;
1586
1391
  await this.matterbridge.plugins.saveConfigFromPlugin(plugin);
1587
- this.wssSendRestartRequired();
1392
+ this.wssSendRestartRequired(false);
1588
1393
  }
1589
1394
  }
1590
1395
  }
@@ -1599,139 +1404,94 @@ export class Frontend {
1599
1404
  return;
1600
1405
  }
1601
1406
  }
1602
- /**
1603
- * Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1604
- *
1605
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1606
- * @param {string} time - The time string of the message
1607
- * @param {string} name - The logger name of the message
1608
- * @param {string} message - The content of the message.
1609
- */
1610
1407
  wssSendMessage(level, time, name, message) {
1611
1408
  if (!level || !time || !name || !message)
1612
1409
  return;
1613
- // Remove ANSI escape codes from the message
1614
- // eslint-disable-next-line no-control-regex
1615
1410
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1616
- // Remove leading asterisks from the message
1617
1411
  message = message.replace(/^\*+/, '');
1618
- // Replace all occurrences of \t and \n
1619
1412
  message = message.replace(/[\t\n]/g, '');
1620
- // Remove non-printable characters
1621
- // eslint-disable-next-line no-control-regex
1622
1413
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1623
- // Replace all occurrences of \" with "
1624
1414
  message = message.replace(/\\"/g, '"');
1625
- // Define the maximum allowed length for continuous characters without a space
1626
1415
  const maxContinuousLength = 100;
1627
1416
  const keepStartLength = 20;
1628
1417
  const keepEndLength = 20;
1629
- // Split the message into words
1630
1418
  message = message
1631
1419
  .split(' ')
1632
1420
  .map((word) => {
1633
- // If the word length exceeds the max continuous length, insert spaces and truncate
1634
1421
  if (word.length > maxContinuousLength) {
1635
1422
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1636
1423
  }
1637
1424
  return word;
1638
1425
  })
1639
1426
  .join(' ');
1640
- // Send the message to all connected clients
1641
1427
  this.webSocketServer?.clients.forEach((client) => {
1642
1428
  if (client.readyState === WebSocket.OPEN) {
1643
1429
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1644
1430
  }
1645
1431
  });
1646
1432
  }
1647
- /**
1648
- * Sends a need to refresh WebSocket message to all connected clients.
1649
- *
1650
- */
1651
- wssSendRefreshRequired() {
1433
+ wssSendRefreshRequired(changed = null) {
1652
1434
  this.log.debug('Sending a refresh required message to all connected clients');
1653
- this.matterbridge.matterbridgeInformation.refreshRequired = true;
1654
- // Send the message to all connected clients
1655
1435
  this.webSocketServer?.clients.forEach((client) => {
1656
1436
  if (client.readyState === WebSocket.OPEN) {
1657
- client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: {} }));
1437
+ client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
1658
1438
  }
1659
1439
  });
1660
1440
  }
1661
- /**
1662
- * Sends a need to restart WebSocket message to all connected clients.
1663
- *
1664
- */
1665
- wssSendRestartRequired() {
1441
+ wssSendRestartRequired(snackbar = true) {
1666
1442
  this.log.debug('Sending a restart required message to all connected clients');
1667
1443
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1668
- this.wssSendSnackbarMessage(`Restart required`, 0);
1669
- // Send the message to all connected clients
1444
+ if (snackbar === true)
1445
+ this.wssSendSnackbarMessage(`Restart required`, 0);
1670
1446
  this.webSocketServer?.clients.forEach((client) => {
1671
1447
  if (client.readyState === WebSocket.OPEN) {
1672
1448
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
1673
1449
  }
1674
1450
  });
1675
1451
  }
1676
- /**
1677
- * Sends a memory update message to all connected clients.
1678
- *
1679
- */
1452
+ wssSendUpdateRequired() {
1453
+ this.log.debug('Sending an update required message to all connected clients');
1454
+ this.matterbridge.matterbridgeInformation.updateRequired = true;
1455
+ this.webSocketServer?.clients.forEach((client) => {
1456
+ if (client.readyState === WebSocket.OPEN) {
1457
+ client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
1458
+ }
1459
+ });
1460
+ }
1680
1461
  wssSendCpuUpdate(cpuUsage) {
1681
1462
  this.log.debug('Sending a cpu update message to all connected clients');
1682
- // Send the message to all connected clients
1683
1463
  this.webSocketServer?.clients.forEach((client) => {
1684
1464
  if (client.readyState === WebSocket.OPEN) {
1685
1465
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
1686
1466
  }
1687
1467
  });
1688
1468
  }
1689
- /**
1690
- * Sends a cpu update message to all connected clients.
1691
- *
1692
- */
1693
1469
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
1694
1470
  this.log.debug('Sending a memory update message to all connected clients');
1695
- // Send the message to all connected clients
1696
1471
  this.webSocketServer?.clients.forEach((client) => {
1697
1472
  if (client.readyState === WebSocket.OPEN) {
1698
1473
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
1699
1474
  }
1700
1475
  });
1701
1476
  }
1702
- /**
1703
- * Sends a memory update message to all connected clients.
1704
- *
1705
- */
1706
1477
  wssSendUptimeUpdate(systemUptime, processUptime) {
1707
1478
  this.log.debug('Sending a uptime update message to all connected clients');
1708
- // Send the message to all connected clients
1709
1479
  this.webSocketServer?.clients.forEach((client) => {
1710
1480
  if (client.readyState === WebSocket.OPEN) {
1711
1481
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
1712
1482
  }
1713
1483
  });
1714
1484
  }
1715
- /**
1716
- * Sends a cpu update message to all connected clients.
1717
- *
1718
- */
1719
1485
  wssSendSnackbarMessage(message, timeout = 5) {
1720
1486
  this.log.debug('Sending a snackbar message to all connected clients');
1721
- // Send the message to all connected clients
1722
1487
  this.webSocketServer?.clients.forEach((client) => {
1723
1488
  if (client.readyState === WebSocket.OPEN) {
1724
1489
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { message, timeout } }));
1725
1490
  }
1726
1491
  });
1727
1492
  }
1728
- /**
1729
- * Sends a message to all connected clients.
1730
- *
1731
- */
1732
1493
  wssBroadcastMessage(id, method, params) {
1733
1494
  this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
1734
- // Send the message to all connected clients
1735
1495
  this.webSocketServer?.clients.forEach((client) => {
1736
1496
  if (client.readyState === WebSocket.OPEN) {
1737
1497
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -1739,4 +1499,3 @@ export class Frontend {
1739
1499
  });
1740
1500
  }
1741
1501
  }
1742
- //# sourceMappingURL=frontend.js.map