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