matterbridge 3.0.0 → 3.0.1-dev-20250501-4f463f9

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 (153) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +22 -4
  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 -352
  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 +46 -747
  17. package/dist/matterbridgeAccessoryPlatform.js +0 -34
  18. package/dist/matterbridgeBehaviors.js +1 -33
  19. package/dist/matterbridgeDeviceTypes.js +12 -431
  20. package/dist/matterbridgeDynamicPlatform.js +0 -34
  21. package/dist/matterbridgeEndpoint.js +11 -794
  22. package/dist/matterbridgeEndpointHelpers.js +9 -142
  23. package/dist/matterbridgePlatform.js +7 -225
  24. package/dist/matterbridgeTypes.js +0 -24
  25. package/dist/pluginManager.js +3 -262
  26. package/dist/shelly.js +6 -146
  27. package/dist/storage/export.js +0 -1
  28. package/dist/update.js +0 -52
  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 -53
  38. package/dist/utils/wait.js +5 -48
  39. package/frontend/build/asset-manifest.json +3 -3
  40. package/frontend/build/index.html +1 -1
  41. package/frontend/build/static/js/{main.1d983660.js → main.356788d7.js} +3 -3
  42. package/frontend/build/static/js/{main.1d983660.js.map → main.356788d7.js.map} +1 -1
  43. package/npm-shrinkwrap.json +2 -2
  44. package/package.json +1 -2
  45. package/dist/cli.d.ts +0 -29
  46. package/dist/cli.d.ts.map +0 -1
  47. package/dist/cli.js.map +0 -1
  48. package/dist/cluster/export.d.ts +0 -2
  49. package/dist/cluster/export.d.ts.map +0 -1
  50. package/dist/cluster/export.js.map +0 -1
  51. package/dist/defaultConfigSchema.d.ts +0 -27
  52. package/dist/defaultConfigSchema.d.ts.map +0 -1
  53. package/dist/defaultConfigSchema.js.map +0 -1
  54. package/dist/deviceManager.d.ts +0 -114
  55. package/dist/deviceManager.d.ts.map +0 -1
  56. package/dist/deviceManager.js.map +0 -1
  57. package/dist/frontend.d.ts +0 -222
  58. package/dist/frontend.d.ts.map +0 -1
  59. package/dist/frontend.js.map +0 -1
  60. package/dist/index.d.ts +0 -35
  61. package/dist/index.d.ts.map +0 -1
  62. package/dist/index.js.map +0 -1
  63. package/dist/logger/export.d.ts +0 -2
  64. package/dist/logger/export.d.ts.map +0 -1
  65. package/dist/logger/export.js.map +0 -1
  66. package/dist/matter/behaviors.d.ts +0 -2
  67. package/dist/matter/behaviors.d.ts.map +0 -1
  68. package/dist/matter/behaviors.js.map +0 -1
  69. package/dist/matter/clusters.d.ts +0 -2
  70. package/dist/matter/clusters.d.ts.map +0 -1
  71. package/dist/matter/clusters.js.map +0 -1
  72. package/dist/matter/devices.d.ts +0 -2
  73. package/dist/matter/devices.d.ts.map +0 -1
  74. package/dist/matter/devices.js.map +0 -1
  75. package/dist/matter/endpoints.d.ts +0 -2
  76. package/dist/matter/endpoints.d.ts.map +0 -1
  77. package/dist/matter/endpoints.js.map +0 -1
  78. package/dist/matter/export.d.ts +0 -5
  79. package/dist/matter/export.d.ts.map +0 -1
  80. package/dist/matter/export.js.map +0 -1
  81. package/dist/matter/types.d.ts +0 -3
  82. package/dist/matter/types.d.ts.map +0 -1
  83. package/dist/matter/types.js.map +0 -1
  84. package/dist/matterbridge.d.ts +0 -431
  85. package/dist/matterbridge.d.ts.map +0 -1
  86. package/dist/matterbridge.js.map +0 -1
  87. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -40
  88. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  89. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  90. package/dist/matterbridgeBehaviors.d.ts +0 -1514
  91. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  92. package/dist/matterbridgeBehaviors.js.map +0 -1
  93. package/dist/matterbridgeDeviceTypes.d.ts +0 -494
  94. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  95. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  96. package/dist/matterbridgeDynamicPlatform.d.ts +0 -40
  97. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  98. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  99. package/dist/matterbridgeEndpoint.d.ts +0 -943
  100. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  101. package/dist/matterbridgeEndpoint.js.map +0 -1
  102. package/dist/matterbridgeEndpointHelpers.d.ts +0 -2706
  103. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  104. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  105. package/dist/matterbridgePlatform.d.ts +0 -294
  106. package/dist/matterbridgePlatform.d.ts.map +0 -1
  107. package/dist/matterbridgePlatform.js.map +0 -1
  108. package/dist/matterbridgeTypes.d.ts +0 -187
  109. package/dist/matterbridgeTypes.d.ts.map +0 -1
  110. package/dist/matterbridgeTypes.js.map +0 -1
  111. package/dist/pluginManager.d.ts +0 -271
  112. package/dist/pluginManager.d.ts.map +0 -1
  113. package/dist/pluginManager.js.map +0 -1
  114. package/dist/shelly.d.ts +0 -92
  115. package/dist/shelly.d.ts.map +0 -1
  116. package/dist/shelly.js.map +0 -1
  117. package/dist/storage/export.d.ts +0 -2
  118. package/dist/storage/export.d.ts.map +0 -1
  119. package/dist/storage/export.js.map +0 -1
  120. package/dist/update.d.ts +0 -32
  121. package/dist/update.d.ts.map +0 -1
  122. package/dist/update.js.map +0 -1
  123. package/dist/utils/colorUtils.d.ts +0 -61
  124. package/dist/utils/colorUtils.d.ts.map +0 -1
  125. package/dist/utils/colorUtils.js.map +0 -1
  126. package/dist/utils/copyDirectory.d.ts +0 -32
  127. package/dist/utils/copyDirectory.d.ts.map +0 -1
  128. package/dist/utils/copyDirectory.js.map +0 -1
  129. package/dist/utils/createZip.d.ts +0 -38
  130. package/dist/utils/createZip.d.ts.map +0 -1
  131. package/dist/utils/createZip.js.map +0 -1
  132. package/dist/utils/deepCopy.d.ts +0 -31
  133. package/dist/utils/deepCopy.d.ts.map +0 -1
  134. package/dist/utils/deepCopy.js.map +0 -1
  135. package/dist/utils/deepEqual.d.ts +0 -53
  136. package/dist/utils/deepEqual.d.ts.map +0 -1
  137. package/dist/utils/deepEqual.js.map +0 -1
  138. package/dist/utils/export.d.ts +0 -10
  139. package/dist/utils/export.d.ts.map +0 -1
  140. package/dist/utils/export.js.map +0 -1
  141. package/dist/utils/isvalid.d.ts +0 -87
  142. package/dist/utils/isvalid.d.ts.map +0 -1
  143. package/dist/utils/isvalid.js.map +0 -1
  144. package/dist/utils/network.d.ts +0 -69
  145. package/dist/utils/network.d.ts.map +0 -1
  146. package/dist/utils/network.js.map +0 -1
  147. package/dist/utils/parameter.d.ts +0 -58
  148. package/dist/utils/parameter.d.ts.map +0 -1
  149. package/dist/utils/parameter.js.map +0 -1
  150. package/dist/utils/wait.d.ts +0 -43
  151. package/dist/utils/wait.d.ts.map +0 -1
  152. package/dist/utils/wait.js.map +0 -1
  153. /package/frontend/build/static/js/{main.1d983660.js.LICENSE.txt → main.356788d7.js.LICENSE.txt} +0 -0
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, PowerSource } 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,43 +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
- // Inject logging/debug wrapper for route/middleware registration
136
- /*
137
- const methods = ['get', 'post', 'put', 'delete', 'use'];
138
- for (const method of methods) {
139
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
140
- const original = (this.expressApp as any)[method].bind(this.expressApp);
141
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
142
- (this.expressApp as any)[method] = (path: any, ...rest: any) => {
143
- try {
144
- console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
145
- return original(path, ...rest);
146
- } catch (err) {
147
- console.error(`[ERROR] Failed to register route: ${path}`);
148
- throw err;
149
- }
150
- };
151
- }
152
- */
153
- // Log all requests to the server for debugging
154
- /*
155
- this.expressApp.use((req, res, next) => {
156
- this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
157
- next();
158
- });
159
- */
160
- // Serve static files from '/static' endpoint
161
54
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
162
55
  if (!hasParameter('ssl')) {
163
- // Create an HTTP server and attach the express app
164
56
  this.httpServer = createServer(this.expressApp);
165
- // Listen on the specified port
166
57
  if (hasParameter('ingress')) {
167
58
  this.httpServer.listen(this.port, '0.0.0.0', () => {
168
59
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -176,7 +67,6 @@ export class Frontend {
176
67
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
177
68
  });
178
69
  }
179
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
180
70
  this.httpServer.on('error', (error) => {
181
71
  this.log.error(`Frontend http server error listening on ${this.port}`);
182
72
  switch (error.code) {
@@ -192,7 +82,6 @@ export class Frontend {
192
82
  });
193
83
  }
194
84
  else {
195
- // Load the SSL certificate, the private key and optionally the CA certificate
196
85
  let cert;
197
86
  try {
198
87
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
@@ -220,9 +109,7 @@ export class Frontend {
220
109
  this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
221
110
  }
222
111
  const serverOptions = { cert, key, ca };
223
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
224
112
  this.httpsServer = https.createServer(serverOptions, this.expressApp);
225
- // Listen on the specified port
226
113
  if (hasParameter('ingress')) {
227
114
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
228
115
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -236,7 +123,6 @@ export class Frontend {
236
123
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
237
124
  });
238
125
  }
239
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
240
126
  this.httpsServer.on('error', (error) => {
241
127
  this.log.error(`Frontend https server error listening on ${this.port}`);
242
128
  switch (error.code) {
@@ -253,18 +139,16 @@ export class Frontend {
253
139
  }
254
140
  if (this.initializeError)
255
141
  return;
256
- // Create a WebSocket server and attach it to the http or https server
257
142
  const wssPort = this.port;
258
143
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
259
144
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
260
145
  this.webSocketServer.on('connection', (ws, request) => {
261
146
  const clientIp = request.socket.remoteAddress;
262
- // Set the global logger callback for the WebSocketServer
263
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
264
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
265
- callbackLogLevel = "info" /* LogLevel.INFO */;
266
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
267
- 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";
268
152
  AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
269
153
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
270
154
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -298,7 +182,6 @@ export class Frontend {
298
182
  this.webSocketServer.on('error', (ws, error) => {
299
183
  this.log.error(`WebSocketServer error: ${error}`);
300
184
  });
301
- // Subscribe to cli events
302
185
  const { cliEmitter } = await import('./cli.js');
303
186
  cliEmitter.removeAllListeners();
304
187
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
@@ -310,7 +193,6 @@ export class Frontend {
310
193
  cliEmitter.on('cpu', (cpuUsage) => {
311
194
  this.wssSendCpuUpdate(cpuUsage);
312
195
  });
313
- // Endpoint to validate login code
314
196
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
315
197
  const { password } = req.body;
316
198
  this.log.debug('The frontend sent /api/login', password);
@@ -329,27 +211,23 @@ export class Frontend {
329
211
  this.log.warn('/api/login error wrong password');
330
212
  res.json({ valid: false });
331
213
  }
332
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
333
214
  }
334
215
  catch (error) {
335
216
  this.log.error('/api/login error getting password');
336
217
  res.json({ valid: false });
337
218
  }
338
219
  });
339
- // Endpoint to provide health check
340
220
  this.expressApp.get('/health', (req, res) => {
341
221
  this.log.debug('Express received /health');
342
222
  const healthStatus = {
343
- status: 'ok', // Indicate service is healthy
344
- uptime: process.uptime(), // Server uptime in seconds
345
- timestamp: new Date().toISOString(), // Current timestamp
223
+ status: 'ok',
224
+ uptime: process.uptime(),
225
+ timestamp: new Date().toISOString(),
346
226
  };
347
227
  res.status(200).json(healthStatus);
348
228
  });
349
- // Endpoint to provide memory usage details
350
229
  this.expressApp.get('/memory', async (req, res) => {
351
230
  this.log.debug('Express received /memory');
352
- // Memory usage from process
353
231
  const memoryUsageRaw = process.memoryUsage();
354
232
  const memoryUsage = {
355
233
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -358,13 +236,10 @@ export class Frontend {
358
236
  external: this.formatMemoryUsage(memoryUsageRaw.external),
359
237
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
360
238
  };
361
- // V8 heap statistics
362
239
  const { default: v8 } = await import('node:v8');
363
240
  const heapStatsRaw = v8.getHeapStatistics();
364
241
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
365
- // Format heapStats
366
242
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
367
- // Format heapSpaces
368
243
  const heapSpaces = heapSpacesRaw.map((space) => ({
369
244
  ...space,
370
245
  space_size: this.formatMemoryUsage(space.space_size),
@@ -382,7 +257,6 @@ export class Frontend {
382
257
  };
383
258
  res.status(200).json(memoryReport);
384
259
  });
385
- // Endpoint to start advertising the server node
386
260
  this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
387
261
  const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
388
262
  if (pairingCodes) {
@@ -393,22 +267,18 @@ export class Frontend {
393
267
  res.status(500).json({ error: 'Failed to generate pairing codes' });
394
268
  }
395
269
  });
396
- // Endpoint to provide settings
397
270
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
398
271
  this.log.debug('The frontend sent /api/settings');
399
272
  res.json(await this.getApiSettings());
400
273
  });
401
- // Endpoint to provide plugins
402
274
  this.expressApp.get('/api/plugins', async (req, res) => {
403
275
  this.log.debug('The frontend sent /api/plugins');
404
276
  res.json(this.getBaseRegisteredPlugins());
405
277
  });
406
- // Endpoint to provide devices
407
278
  this.expressApp.get('/api/devices', (req, res) => {
408
279
  this.log.debug('The frontend sent /api/devices');
409
280
  const devices = [];
410
281
  this.matterbridge.devices.forEach(async (device) => {
411
- // Check if the device has the required properties
412
282
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
413
283
  return;
414
284
  const cluster = this.getClusterTextFromDevice(device);
@@ -427,7 +297,6 @@ export class Frontend {
427
297
  });
428
298
  res.json(devices);
429
299
  });
430
- // Endpoint to provide the cluster servers of the devices
431
300
  this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
432
301
  const selectedPluginName = req.params.selectedPluginName;
433
302
  const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
@@ -500,7 +369,6 @@ export class Frontend {
500
369
  });
501
370
  res.json(data);
502
371
  });
503
- // Endpoint to view the matterbridge log
504
372
  this.expressApp.get('/api/view-mblog', async (req, res) => {
505
373
  this.log.debug('The frontend sent /api/view-mblog');
506
374
  try {
@@ -513,7 +381,6 @@ export class Frontend {
513
381
  res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
514
382
  }
515
383
  });
516
- // Endpoint to view the matter.js log
517
384
  this.expressApp.get('/api/view-mjlog', async (req, res) => {
518
385
  this.log.debug('The frontend sent /api/view-mjlog');
519
386
  try {
@@ -526,7 +393,6 @@ export class Frontend {
526
393
  res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
527
394
  }
528
395
  });
529
- // Endpoint to view the shelly log
530
396
  this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
531
397
  this.log.debug('The frontend sent /api/shellyviewsystemlog');
532
398
  try {
@@ -539,7 +405,6 @@ export class Frontend {
539
405
  res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
540
406
  }
541
407
  });
542
- // Endpoint to download the matterbridge log
543
408
  this.expressApp.get('/api/download-mblog', async (req, res) => {
544
409
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
545
410
  try {
@@ -552,7 +417,6 @@ export class Frontend {
552
417
  this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
553
418
  }
554
419
  res.type('text/plain');
555
- // res.download(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
556
420
  res.download(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
557
421
  if (error) {
558
422
  this.log.error(`Error downloading log file ${this.matterbridge.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
@@ -560,7 +424,6 @@ export class Frontend {
560
424
  }
561
425
  });
562
426
  });
563
- // Endpoint to download the matter log
564
427
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
565
428
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
566
429
  try {
@@ -580,7 +443,6 @@ export class Frontend {
580
443
  }
581
444
  });
582
445
  });
583
- // Endpoint to download the shelly log
584
446
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
585
447
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
586
448
  try {
@@ -600,7 +462,6 @@ export class Frontend {
600
462
  }
601
463
  });
602
464
  });
603
- // Endpoint to download the matter storage file
604
465
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
605
466
  this.log.debug('The frontend sent /api/download-mjstorage');
606
467
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
@@ -611,7 +472,6 @@ export class Frontend {
611
472
  }
612
473
  });
613
474
  });
614
- // Endpoint to download the matterbridge storage directory
615
475
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
616
476
  this.log.debug('The frontend sent /api/download-mbstorage');
617
477
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
@@ -622,7 +482,6 @@ export class Frontend {
622
482
  }
623
483
  });
624
484
  });
625
- // Endpoint to download the matterbridge plugin directory
626
485
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
627
486
  this.log.debug('The frontend sent /api/download-pluginstorage');
628
487
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
@@ -633,11 +492,9 @@ export class Frontend {
633
492
  }
634
493
  });
635
494
  });
636
- // Endpoint to download the matterbridge plugin config files
637
495
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
638
496
  this.log.debug('The frontend sent /api/download-pluginconfig');
639
497
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
640
- // 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')));
641
498
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
642
499
  if (error) {
643
500
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
@@ -645,7 +502,6 @@ export class Frontend {
645
502
  }
646
503
  });
647
504
  });
648
- // Endpoint to download the matterbridge plugin config files
649
505
  this.expressApp.get('/api/download-backup', async (req, res) => {
650
506
  this.log.debug('The frontend sent /api/download-backup');
651
507
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -655,7 +511,6 @@ export class Frontend {
655
511
  }
656
512
  });
657
513
  });
658
- // Endpoint to receive commands
659
514
  this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
660
515
  const command = req.params.command;
661
516
  let param = req.params.param;
@@ -665,15 +520,13 @@ export class Frontend {
665
520
  return;
666
521
  }
667
522
  this.log.debug(`Received frontend command: ${command}:${param}`);
668
- // Handle the command setpassword from Settings
669
523
  if (command === 'setpassword') {
670
- const password = param.slice(1, -1); // Remove the first and last characters
524
+ const password = param.slice(1, -1);
671
525
  this.log.debug('setpassword', param, password);
672
526
  await this.matterbridge.nodeContext?.set('password', password);
673
527
  res.json({ message: 'Command received' });
674
528
  return;
675
529
  }
676
- // Handle the command setbridgemode from Settings
677
530
  if (command === 'setbridgemode') {
678
531
  this.log.debug(`setbridgemode: ${param}`);
679
532
  this.wssSendRestartRequired();
@@ -681,7 +534,6 @@ export class Frontend {
681
534
  res.json({ message: 'Command received' });
682
535
  return;
683
536
  }
684
- // Handle the command backup from Settings
685
537
  if (command === 'backup') {
686
538
  this.log.notice(`Prepairing the backup...`);
687
539
  await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
@@ -690,33 +542,31 @@ export class Frontend {
690
542
  res.json({ message: 'Command received' });
691
543
  return;
692
544
  }
693
- // Handle the command setmbloglevel from Settings
694
545
  if (command === 'setmbloglevel') {
695
546
  this.log.debug('Matterbridge log level:', param);
696
547
  if (param === 'Debug') {
697
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
548
+ this.log.logLevel = "debug";
698
549
  }
699
550
  else if (param === 'Info') {
700
- this.log.logLevel = "info" /* LogLevel.INFO */;
551
+ this.log.logLevel = "info";
701
552
  }
702
553
  else if (param === 'Notice') {
703
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
554
+ this.log.logLevel = "notice";
704
555
  }
705
556
  else if (param === 'Warn') {
706
- this.log.logLevel = "warn" /* LogLevel.WARN */;
557
+ this.log.logLevel = "warn";
707
558
  }
708
559
  else if (param === 'Error') {
709
- this.log.logLevel = "error" /* LogLevel.ERROR */;
560
+ this.log.logLevel = "error";
710
561
  }
711
562
  else if (param === 'Fatal') {
712
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
563
+ this.log.logLevel = "fatal";
713
564
  }
714
565
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
715
566
  await this.matterbridge.setLogLevel(this.log.logLevel);
716
567
  res.json({ message: 'Command received' });
717
568
  return;
718
569
  }
719
- // Handle the command setmbloglevel from Settings
720
570
  if (command === 'setmjloglevel') {
721
571
  this.log.debug('Matter.js log level:', param);
722
572
  if (param === 'Debug') {
@@ -741,7 +591,6 @@ export class Frontend {
741
591
  res.json({ message: 'Command received' });
742
592
  return;
743
593
  }
744
- // Handle the command setmdnsinterface from Settings
745
594
  if (command === 'setmdnsinterface') {
746
595
  if (param === 'json' && isValidString(req.body.value)) {
747
596
  this.matterbridge.matterbridgeInformation.mattermdnsinterface = req.body.value;
@@ -751,7 +600,6 @@ export class Frontend {
751
600
  res.json({ message: 'Command received' });
752
601
  return;
753
602
  }
754
- // Handle the command setipv4address from Settings
755
603
  if (command === 'setipv4address') {
756
604
  if (param === 'json' && isValidString(req.body.value)) {
757
605
  this.log.debug(`Matter.js ipv4 address: ${req.body.value === '' ? 'all ipv4 addresses' : req.body.value}`);
@@ -761,7 +609,6 @@ export class Frontend {
761
609
  res.json({ message: 'Command received' });
762
610
  return;
763
611
  }
764
- // Handle the command setipv6address from Settings
765
612
  if (command === 'setipv6address') {
766
613
  if (param === 'json' && isValidString(req.body.value)) {
767
614
  this.log.debug(`Matter.js ipv6 address: ${req.body.value === '' ? 'all ipv6 addresses' : req.body.value}`);
@@ -771,7 +618,6 @@ export class Frontend {
771
618
  res.json({ message: 'Command received' });
772
619
  return;
773
620
  }
774
- // Handle the command setmatterport from Settings
775
621
  if (command === 'setmatterport') {
776
622
  const port = Math.min(Math.max(parseInt(req.body.value), 5540), 5560);
777
623
  this.matterbridge.matterbridgeInformation.matterPort = port;
@@ -780,7 +626,6 @@ export class Frontend {
780
626
  res.json({ message: 'Command received' });
781
627
  return;
782
628
  }
783
- // Handle the command setmatterdiscriminator from Settings
784
629
  if (command === 'setmatterdiscriminator') {
785
630
  const discriminator = Math.min(Math.max(parseInt(req.body.value), 1000), 4095);
786
631
  this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
@@ -789,7 +634,6 @@ export class Frontend {
789
634
  res.json({ message: 'Command received' });
790
635
  return;
791
636
  }
792
- // Handle the command setmatterpasscode from Settings
793
637
  if (command === 'setmatterpasscode') {
794
638
  const passcode = Math.min(Math.max(parseInt(req.body.value), 10000000), 90000000);
795
639
  this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
@@ -798,20 +642,17 @@ export class Frontend {
798
642
  res.json({ message: 'Command received' });
799
643
  return;
800
644
  }
801
- // Handle the command setmbloglevel from Settings
802
645
  if (command === 'setmblogfile') {
803
646
  this.log.debug('Matterbridge file log:', param);
804
647
  this.matterbridge.matterbridgeInformation.fileLogger = param === 'true';
805
648
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', param === 'true');
806
- // Create the file logger for matterbridge
807
649
  if (param === 'true')
808
- AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug" /* LogLevel.DEBUG */, true);
650
+ AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), "debug", true);
809
651
  else
810
652
  AnsiLogger.setGlobalLogfile(undefined);
811
653
  res.json({ message: 'Command received' });
812
654
  return;
813
655
  }
814
- // Handle the command setmbloglevel from Settings
815
656
  if (command === 'setmjlogfile') {
816
657
  this.log.debug('Matter file log:', param);
817
658
  this.matterbridge.matterbridgeInformation.matterFileLogger = param === 'true';
@@ -838,48 +679,40 @@ export class Frontend {
838
679
  res.json({ message: 'Command received' });
839
680
  return;
840
681
  }
841
- // Handle the command unregister from Settings
842
682
  if (command === 'unregister') {
843
683
  await this.matterbridge.unregisterAndShutdownProcess();
844
684
  res.json({ message: 'Command received' });
845
685
  return;
846
686
  }
847
- // Handle the command reset from Settings
848
687
  if (command === 'reset') {
849
688
  await this.matterbridge.shutdownProcessAndReset();
850
689
  res.json({ message: 'Command received' });
851
690
  return;
852
691
  }
853
- // Handle the command factoryreset from Settings
854
692
  if (command === 'factoryreset') {
855
693
  await this.matterbridge.shutdownProcessAndFactoryReset();
856
694
  res.json({ message: 'Command received' });
857
695
  return;
858
696
  }
859
- // Handle the command shutdown from Header
860
697
  if (command === 'shutdown') {
861
698
  await this.matterbridge.shutdownProcess();
862
699
  res.json({ message: 'Command received' });
863
700
  return;
864
701
  }
865
- // Handle the command restart from Header
866
702
  if (command === 'restart') {
867
703
  await this.matterbridge.restartProcess();
868
704
  res.json({ message: 'Command received' });
869
705
  return;
870
706
  }
871
- // Handle the command update from Header
872
707
  if (command === 'update') {
873
708
  await this.matterbridge.updateProcess();
874
709
  this.wssSendRestartRequired();
875
710
  res.json({ message: 'Command received' });
876
711
  return;
877
712
  }
878
- // Handle the command saveconfig from Home
879
713
  if (command === 'saveconfig') {
880
714
  param = param.replace(/\*/g, '\\');
881
715
  this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
882
- // console.log('Req.body:', JSON.stringify(req.body, null, 2));
883
716
  if (!this.matterbridge.plugins.has(param)) {
884
717
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
885
718
  }
@@ -894,7 +727,6 @@ export class Frontend {
894
727
  res.json({ message: 'Command received' });
895
728
  return;
896
729
  }
897
- // Handle the command installplugin from Home
898
730
  if (command === 'installplugin') {
899
731
  param = param.replace(/\*/g, '\\');
900
732
  this.log.info(`Installing plugin ${plg}${param}${nf}...`);
@@ -903,7 +735,6 @@ export class Frontend {
903
735
  await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
904
736
  this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
905
737
  this.wssSendSnackbarMessage(`Installed package ${param}`, 10, 'success');
906
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
907
738
  }
908
739
  catch (error) {
909
740
  this.log.error(`Error installing plugin ${plg}${param}${er}`);
@@ -911,22 +742,17 @@ export class Frontend {
911
742
  }
912
743
  this.wssSendRestartRequired();
913
744
  param = param.split('@')[0];
914
- // Also add the plugin to matterbridge so no return!
915
745
  if (param === 'matterbridge') {
916
- // 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
917
746
  res.json({ message: 'Command received' });
918
747
  return;
919
748
  }
920
749
  }
921
- // Handle the command addplugin from Home
922
750
  if (command === 'addplugin' || command === 'installplugin') {
923
751
  param = param.replace(/\*/g, '\\');
924
752
  const plugin = await this.matterbridge.plugins.add(param);
925
753
  if (plugin) {
926
754
  this.wssSendSnackbarMessage(`Added plugin ${param}`);
927
755
  if (this.matterbridge.bridgeMode === 'childbridge') {
928
- // 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
929
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
930
756
  this.matterbridge.createDynamicPlugin(plugin, true);
931
757
  }
932
758
  this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
@@ -936,14 +762,13 @@ export class Frontend {
936
762
  res.json({ message: 'Command received' });
937
763
  return;
938
764
  }
939
- // Handle the command removeplugin from Home
940
765
  if (command === 'removeplugin') {
941
766
  if (!this.matterbridge.plugins.has(param)) {
942
767
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
943
768
  }
944
769
  else {
945
770
  const plugin = this.matterbridge.plugins.get(param);
946
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true); // This will also close the server node in childbridge mode
771
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
947
772
  await this.matterbridge.plugins.remove(param);
948
773
  this.wssSendSnackbarMessage(`Removed plugin ${param}`);
949
774
  this.wssSendRefreshRequired('plugins');
@@ -951,7 +776,6 @@ export class Frontend {
951
776
  res.json({ message: 'Command received' });
952
777
  return;
953
778
  }
954
- // Handle the command enableplugin from Home
955
779
  if (command === 'enableplugin') {
956
780
  if (!this.matterbridge.plugins.has(param)) {
957
781
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -970,7 +794,6 @@ export class Frontend {
970
794
  await this.matterbridge.plugins.enable(param);
971
795
  this.wssSendSnackbarMessage(`Enabled plugin ${param}`);
972
796
  if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
973
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
974
797
  this.matterbridge.createDynamicPlugin(plugin, true);
975
798
  }
976
799
  this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
@@ -981,7 +804,6 @@ export class Frontend {
981
804
  res.json({ message: 'Command received' });
982
805
  return;
983
806
  }
984
- // Handle the command disableplugin from Home
985
807
  if (command === 'disableplugin') {
986
808
  if (!this.matterbridge.plugins.has(param)) {
987
809
  this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
@@ -989,7 +811,7 @@ export class Frontend {
989
811
  else {
990
812
  const plugin = this.matterbridge.plugins.get(param);
991
813
  if (plugin && plugin.enabled) {
992
- await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true); // This will also close the server node in childbridge mode
814
+ await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
993
815
  await this.matterbridge.plugins.disable(param);
994
816
  this.wssSendSnackbarMessage(`Disabled plugin ${param}`);
995
817
  this.wssSendRefreshRequired('plugins');
@@ -1008,13 +830,10 @@ export class Frontend {
1008
830
  return;
1009
831
  }
1010
832
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`);
1011
- // Define the path where the plugin file will be saved
1012
833
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
1013
834
  try {
1014
- // Move the uploaded file to the specified path
1015
835
  await fs.rename(file.path, filePath);
1016
836
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
1017
- // Install the plugin package
1018
837
  if (filename.endsWith('.tgz')) {
1019
838
  await this.matterbridge.spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
1020
839
  this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
@@ -1031,46 +850,31 @@ export class Frontend {
1031
850
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
1032
851
  }
1033
852
  });
1034
- // Fallback for routing (must be the last route)
1035
853
  this.expressApp.use((req, res) => {
1036
854
  this.log.debug('The frontend sent:', req.url);
1037
855
  res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
1038
856
  });
1039
- /* Not working in express v5!
1040
- this.expressApp.get('*', (req, res) => {
1041
- this.log.debug('The frontend sent:', req.url);
1042
- this.log.debug('Response send file:', path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
1043
- res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
1044
- });
1045
- */
1046
857
  this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
1047
858
  }
1048
859
  async stop() {
1049
- // Remove all listeners from the cliEmitter
1050
- // cliEmitter.removeAllListeners();
1051
- // Close the http server
1052
860
  if (this.httpServer) {
1053
861
  this.httpServer.close();
1054
862
  this.httpServer.removeAllListeners();
1055
863
  this.httpServer = undefined;
1056
864
  this.log.debug('Frontend http server closed successfully');
1057
865
  }
1058
- // Close the https server
1059
866
  if (this.httpsServer) {
1060
867
  this.httpsServer.close();
1061
868
  this.httpsServer.removeAllListeners();
1062
869
  this.httpsServer = undefined;
1063
870
  this.log.debug('Frontend https server closed successfully');
1064
871
  }
1065
- // Remove listeners from the express app
1066
872
  if (this.expressApp) {
1067
873
  this.expressApp.removeAllListeners();
1068
874
  this.expressApp = undefined;
1069
875
  this.log.debug('Frontend app closed successfully');
1070
876
  }
1071
- // Close the WebSocket server
1072
877
  if (this.webSocketServer) {
1073
- // Close all active connections
1074
878
  this.webSocketServer.clients.forEach((client) => {
1075
879
  if (client.readyState === WebSocket.OPEN) {
1076
880
  client.close();
@@ -1087,7 +891,6 @@ export class Frontend {
1087
891
  this.webSocketServer = undefined;
1088
892
  }
1089
893
  }
1090
- // Function to format bytes to KB, MB, or GB
1091
894
  formatMemoryUsage = (bytes) => {
1092
895
  if (bytes >= 1024 ** 3) {
1093
896
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -1099,7 +902,6 @@ export class Frontend {
1099
902
  return `${(bytes / 1024).toFixed(2)} KB`;
1100
903
  }
1101
904
  };
1102
- // Function to format system uptime with only the most significant unit
1103
905
  formatOsUpTime = (seconds) => {
1104
906
  if (seconds >= 86400) {
1105
907
  const days = Math.floor(seconds / 86400);
@@ -1115,13 +917,8 @@ export class Frontend {
1115
917
  }
1116
918
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
1117
919
  };
1118
- /**
1119
- * Retrieves the api settings data.
1120
- * @returns {Promise<object>} A promise that resolve in the api settings object.
1121
- */
1122
920
  async getApiSettings() {
1123
921
  const { lastCpuUsage } = await import('./cli.js');
1124
- // Update the system information
1125
922
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
1126
923
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
1127
924
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -1130,7 +927,6 @@ export class Frontend {
1130
927
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
1131
928
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
1132
929
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
1133
- // Update the matterbridge information
1134
930
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
1135
931
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
1136
932
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
@@ -1149,11 +945,6 @@ export class Frontend {
1149
945
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
1150
946
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
1151
947
  }
1152
- /**
1153
- * Retrieves the reachable attribute.
1154
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
1155
- * @returns {boolean} The reachable attribute.
1156
- */
1157
948
  getReachability(device) {
1158
949
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
1159
950
  return false;
@@ -1178,20 +969,13 @@ export class Frontend {
1178
969
  }
1179
970
  return;
1180
971
  };
1181
- // Root endpoint
1182
972
  if (device.hasClusterServer(PowerSource.Cluster.id))
1183
973
  return powerSource(device);
1184
- // Child endpoints
1185
974
  for (const child of device.getChildEndpoints()) {
1186
975
  if (child.hasClusterServer(PowerSource.Cluster.id))
1187
976
  return powerSource(child);
1188
977
  }
1189
978
  }
1190
- /**
1191
- * Retrieves the cluster text description from a given device.
1192
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
1193
- * @returns {string} The attributes description of the cluster servers in the device.
1194
- */
1195
979
  getClusterTextFromDevice(device) {
1196
980
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
1197
981
  return '';
@@ -1233,7 +1017,6 @@ export class Frontend {
1233
1017
  let attributes = '';
1234
1018
  let supportedModes = [];
1235
1019
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1236
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
1237
1020
  if (typeof attributeValue === 'undefined')
1238
1021
  return;
1239
1022
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -1325,13 +1108,8 @@ export class Frontend {
1325
1108
  if (clusterName === 'userLabel' && attributeName === 'labelList')
1326
1109
  attributes += `${getUserLabel(device)} `;
1327
1110
  });
1328
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
1329
1111
  return attributes.trimStart().trimEnd();
1330
1112
  }
1331
- /**
1332
- * Retrieves the base registered plugins sanitized for res.json().
1333
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
1334
- */
1335
1113
  getBaseRegisteredPlugins() {
1336
1114
  const baseRegisteredPlugins = [];
1337
1115
  for (const plugin of this.matterbridge.plugins) {
@@ -1369,13 +1147,6 @@ export class Frontend {
1369
1147
  }
1370
1148
  return baseRegisteredPlugins;
1371
1149
  }
1372
- /**
1373
- * Handles incoming websocket messages for the Matterbridge frontend.
1374
- *
1375
- * @param {WebSocket} client - The websocket client that sent the message.
1376
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1377
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1378
- */
1379
1150
  async wsMessageHandler(client, message) {
1380
1151
  let data;
1381
1152
  try {
@@ -1531,10 +1302,8 @@ export class Frontend {
1531
1302
  else if (data.method === '/api/devices') {
1532
1303
  const devices = [];
1533
1304
  this.matterbridge.devices.forEach(async (device) => {
1534
- // Filter by pluginName if provided
1535
1305
  if (data.params.pluginName && data.params.pluginName !== device.plugin)
1536
1306
  return;
1537
- // Check if the device has the required properties
1538
1307
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1539
1308
  return;
1540
1309
  const cluster = this.getClusterTextFromDevice(device);
@@ -1619,7 +1388,6 @@ export class Frontend {
1619
1388
  });
1620
1389
  endpointServer.getChildEndpoints().forEach((childEndpoint) => {
1621
1390
  deviceTypes = [];
1622
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1623
1391
  const name = childEndpoint.endpoint?.id;
1624
1392
  const clusterServers = childEndpoint.getAllClusterServers();
1625
1393
  clusterServers.forEach((clusterServer) => {
@@ -1718,7 +1486,6 @@ export class Frontend {
1718
1486
  return;
1719
1487
  }
1720
1488
  const config = plugin.configJson;
1721
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1722
1489
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1723
1490
  this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1724
1491
  if (select === 'serial')
@@ -1726,11 +1493,9 @@ export class Frontend {
1726
1493
  if (select === 'name')
1727
1494
  this.log.info(`Selected device name ${data.params.name}`);
1728
1495
  if (config && select && (select === 'serial' || select === 'name')) {
1729
- // Remove postfix from the serial if it exists
1730
1496
  if (config.postfix) {
1731
1497
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1732
1498
  }
1733
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1734
1499
  if (isValidArray(config.whiteList, 1)) {
1735
1500
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1736
1501
  config.whiteList.push(data.params.serial);
@@ -1739,7 +1504,6 @@ export class Frontend {
1739
1504
  config.whiteList.push(data.params.name);
1740
1505
  }
1741
1506
  }
1742
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1743
1507
  if (isValidArray(config.blackList, 1)) {
1744
1508
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1745
1509
  config.blackList = config.blackList.filter((serial) => serial !== data.params.serial);
@@ -1761,7 +1525,6 @@ export class Frontend {
1761
1525
  return;
1762
1526
  }
1763
1527
  const config = plugin.configJson;
1764
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1765
1528
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1766
1529
  this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1767
1530
  if (select === 'serial')
@@ -1772,7 +1535,6 @@ export class Frontend {
1772
1535
  if (config.postfix) {
1773
1536
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1774
1537
  }
1775
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1776
1538
  if (isValidArray(config.whiteList, 1)) {
1777
1539
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1778
1540
  config.whiteList = config.whiteList.filter((serial) => serial !== data.params.serial);
@@ -1781,7 +1543,6 @@ export class Frontend {
1781
1543
  config.whiteList = config.whiteList.filter((name) => name !== data.params.name);
1782
1544
  }
1783
1545
  }
1784
- // Add the serial to the blackList
1785
1546
  if (isValidArray(config.blackList)) {
1786
1547
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1787
1548
  config.blackList.push(data.params.serial);
@@ -1808,196 +1569,103 @@ export class Frontend {
1808
1569
  return;
1809
1570
  }
1810
1571
  }
1811
- /**
1812
- * Sends a WebSocket message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1813
- *
1814
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1815
- * @param {string} time - The time string of the message
1816
- * @param {string} name - The logger name of the message
1817
- * @param {string} message - The content of the message.
1818
- */
1819
1572
  wssSendMessage(level, time, name, message) {
1820
1573
  if (!level || !time || !name || !message)
1821
1574
  return;
1822
- // Remove ANSI escape codes from the message
1823
- // eslint-disable-next-line no-control-regex
1824
1575
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1825
- // Remove leading asterisks from the message
1826
1576
  message = message.replace(/^\*+/, '');
1827
- // Replace all occurrences of \t and \n
1828
1577
  message = message.replace(/[\t\n]/g, '');
1829
- // Remove non-printable characters
1830
- // eslint-disable-next-line no-control-regex
1831
1578
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1832
- // Replace all occurrences of \" with "
1833
1579
  message = message.replace(/\\"/g, '"');
1834
- // Replace all occurrences of angle-brackets with &lt; and &gt;"
1835
1580
  message = message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1836
- // Define the maximum allowed length for continuous characters without a space
1837
1581
  const maxContinuousLength = 100;
1838
1582
  const keepStartLength = 20;
1839
1583
  const keepEndLength = 20;
1840
- // Split the message into words
1841
1584
  message = message
1842
1585
  .split(' ')
1843
1586
  .map((word) => {
1844
- // If the word length exceeds the max continuous length, insert spaces and truncate
1845
1587
  if (word.length > maxContinuousLength) {
1846
1588
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1847
1589
  }
1848
1590
  return word;
1849
1591
  })
1850
1592
  .join(' ');
1851
- // Send the message to all connected clients
1852
1593
  this.webSocketServer?.clients.forEach((client) => {
1853
1594
  if (client.readyState === WebSocket.OPEN) {
1854
1595
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1855
1596
  }
1856
1597
  });
1857
1598
  }
1858
- /**
1859
- * Sends a need to refresh WebSocket message to all connected clients.
1860
- *
1861
- * @param {string} changed - The changed value. If null, the whole page will be refreshed.
1862
- * possible values:
1863
- * - 'matterbridgeLatestVersion'
1864
- * - 'matterbridgeAdvertise'
1865
- * - 'online'
1866
- * - 'offline'
1867
- * - 'reachability'
1868
- * - 'settings'
1869
- * - 'plugins'
1870
- * - 'devices'
1871
- * - 'fabrics'
1872
- * - 'sessions'
1873
- */
1874
1599
  wssSendRefreshRequired(changed = null) {
1875
1600
  this.log.debug('Sending a refresh required message to all connected clients');
1876
- // Send the message to all connected clients
1877
1601
  this.webSocketServer?.clients.forEach((client) => {
1878
1602
  if (client.readyState === WebSocket.OPEN) {
1879
1603
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
1880
1604
  }
1881
1605
  });
1882
1606
  }
1883
- /**
1884
- * Sends a need to restart WebSocket message to all connected clients.
1885
- *
1886
- */
1887
1607
  wssSendRestartRequired(snackbar = true) {
1888
1608
  this.log.debug('Sending a restart required message to all connected clients');
1889
1609
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1890
1610
  if (snackbar === true)
1891
1611
  this.wssSendSnackbarMessage(`Restart required`, 0);
1892
- // Send the message to all connected clients
1893
1612
  this.webSocketServer?.clients.forEach((client) => {
1894
1613
  if (client.readyState === WebSocket.OPEN) {
1895
1614
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
1896
1615
  }
1897
1616
  });
1898
1617
  }
1899
- /**
1900
- * Sends a need to update WebSocket message to all connected clients.
1901
- *
1902
- */
1903
1618
  wssSendUpdateRequired() {
1904
1619
  this.log.debug('Sending an update required message to all connected clients');
1905
1620
  this.matterbridge.matterbridgeInformation.updateRequired = true;
1906
- // Send the message to all connected clients
1907
1621
  this.webSocketServer?.clients.forEach((client) => {
1908
1622
  if (client.readyState === WebSocket.OPEN) {
1909
1623
  client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
1910
1624
  }
1911
1625
  });
1912
1626
  }
1913
- /**
1914
- * Sends a memory update message to all connected clients.
1915
- *
1916
- */
1917
1627
  wssSendCpuUpdate(cpuUsage) {
1918
1628
  this.log.debug('Sending a cpu update message to all connected clients');
1919
- // Send the message to all connected clients
1920
1629
  this.webSocketServer?.clients.forEach((client) => {
1921
1630
  if (client.readyState === WebSocket.OPEN) {
1922
1631
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
1923
1632
  }
1924
1633
  });
1925
1634
  }
1926
- /**
1927
- * Sends a cpu update message to all connected clients.
1928
- *
1929
- */
1930
1635
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
1931
1636
  this.log.debug('Sending a memory update message to all connected clients');
1932
- // Send the message to all connected clients
1933
1637
  this.webSocketServer?.clients.forEach((client) => {
1934
1638
  if (client.readyState === WebSocket.OPEN) {
1935
1639
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
1936
1640
  }
1937
1641
  });
1938
1642
  }
1939
- /**
1940
- * Sends a memory update message to all connected clients.
1941
- *
1942
- */
1943
1643
  wssSendUptimeUpdate(systemUptime, processUptime) {
1944
1644
  this.log.debug('Sending a uptime update message to all connected clients');
1945
- // Send the message to all connected clients
1946
1645
  this.webSocketServer?.clients.forEach((client) => {
1947
1646
  if (client.readyState === WebSocket.OPEN) {
1948
1647
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
1949
1648
  }
1950
1649
  });
1951
1650
  }
1952
- /**
1953
- * Sends a cpu update message to all connected clients.
1954
- * @param {string} message - The message to send.
1955
- * @param {number} timeout - The timeout in seconds for the snackbar message.
1956
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
1957
- *
1958
- */
1959
1651
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
1960
1652
  this.log.debug('Sending a snackbar message to all connected clients');
1961
- // Send the message to all connected clients
1962
1653
  this.webSocketServer?.clients.forEach((client) => {
1963
1654
  if (client.readyState === WebSocket.OPEN) {
1964
1655
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { message, timeout, severity } }));
1965
1656
  }
1966
1657
  });
1967
1658
  }
1968
- /**
1969
- * Sends an attribute update message to all connected WebSocket clients.
1970
- *
1971
- * @param {string | undefined} plugin - The name of the plugin.
1972
- * @param {string | undefined} serialNumber - The serial number of the device.
1973
- * @param {string | undefined} uniqueId - The unique identifier of the device.
1974
- * @param {string} cluster - The cluster name where the attribute belongs.
1975
- * @param {string} attribute - The name of the attribute that changed.
1976
- * @param {number | string | boolean} value - The new value of the attribute.
1977
- *
1978
- * @remarks
1979
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
1980
- * with the updated attribute information.
1981
- */
1982
1659
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
1983
1660
  this.log.debug('Sending an attribute update message to all connected clients');
1984
- // Send the message to all connected clients
1985
1661
  this.webSocketServer?.clients.forEach((client) => {
1986
1662
  if (client.readyState === WebSocket.OPEN) {
1987
1663
  client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
1988
1664
  }
1989
1665
  });
1990
1666
  }
1991
- /**
1992
- * Sends a message to all connected clients.
1993
- * @param {number} id - The message id.
1994
- * @param {string} method - The message method.
1995
- * @param {Record<string, string | number | boolean>} params - The message parameters.
1996
- *
1997
- */
1998
1667
  wssBroadcastMessage(id, method, params) {
1999
1668
  this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
2000
- // Send the message to all connected clients
2001
1669
  this.webSocketServer?.clients.forEach((client) => {
2002
1670
  if (client.readyState === WebSocket.OPEN) {
2003
1671
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -2005,4 +1673,3 @@ export class Frontend {
2005
1673
  });
2006
1674
  }
2007
1675
  }
2008
- //# sourceMappingURL=frontend.js.map