matterbridge 2.2.9 → 3.0.0-edge.10

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