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