matterbridge 3.0.4 → 3.0.5-dev-20250526-422f029

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 (164) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cli.js +16 -43
  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 +16 -363
  7. package/dist/helpers.js +0 -51
  8. package/dist/index.js +1 -27
  9. package/dist/laundryWasher.js +122 -0
  10. package/dist/logger/export.js +0 -1
  11. package/dist/matter/behaviors.js +0 -2
  12. package/dist/matter/clusters.js +0 -2
  13. package/dist/matter/devices.js +0 -2
  14. package/dist/matter/endpoints.js +0 -2
  15. package/dist/matter/export.js +0 -2
  16. package/dist/matter/types.js +0 -2
  17. package/dist/matterbridge.js +47 -747
  18. package/dist/matterbridgeAccessoryPlatform.js +0 -34
  19. package/dist/matterbridgeBehaviors.js +4 -63
  20. package/dist/matterbridgeDeviceTypes.js +15 -563
  21. package/dist/matterbridgeDynamicPlatform.js +0 -34
  22. package/dist/matterbridgeEndpoint.js +23 -903
  23. package/dist/matterbridgeEndpointHelpers.js +11 -173
  24. package/dist/matterbridgePlatform.js +7 -225
  25. package/dist/matterbridgeTypes.js +0 -24
  26. package/dist/pluginManager.js +3 -264
  27. package/dist/roboticVacuumCleaner.js +3 -78
  28. package/dist/shelly.js +7 -155
  29. package/dist/storage/export.js +0 -1
  30. package/dist/update.js +0 -53
  31. package/dist/utils/colorUtils.js +2 -205
  32. package/dist/utils/commandLine.js +0 -53
  33. package/dist/utils/copyDirectory.js +1 -37
  34. package/dist/utils/createZip.js +2 -42
  35. package/dist/utils/deepCopy.js +0 -38
  36. package/dist/utils/deepEqual.js +1 -71
  37. package/dist/utils/export.js +0 -1
  38. package/dist/utils/hex.js +0 -57
  39. package/dist/utils/isvalid.js +0 -100
  40. package/dist/utils/network.js +5 -76
  41. package/dist/utils/wait.js +9 -58
  42. package/dist/waterHeater.js +0 -52
  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 -256
  58. package/dist/frontend.d.ts.map +0 -1
  59. package/dist/frontend.js.map +0 -1
  60. package/dist/helpers.d.ts +0 -47
  61. package/dist/helpers.d.ts.map +0 -1
  62. package/dist/helpers.js.map +0 -1
  63. package/dist/index.d.ts +0 -35
  64. package/dist/index.d.ts.map +0 -1
  65. package/dist/index.js.map +0 -1
  66. package/dist/logger/export.d.ts +0 -2
  67. package/dist/logger/export.d.ts.map +0 -1
  68. package/dist/logger/export.js.map +0 -1
  69. package/dist/matter/behaviors.d.ts +0 -2
  70. package/dist/matter/behaviors.d.ts.map +0 -1
  71. package/dist/matter/behaviors.js.map +0 -1
  72. package/dist/matter/clusters.d.ts +0 -2
  73. package/dist/matter/clusters.d.ts.map +0 -1
  74. package/dist/matter/clusters.js.map +0 -1
  75. package/dist/matter/devices.d.ts +0 -2
  76. package/dist/matter/devices.d.ts.map +0 -1
  77. package/dist/matter/devices.js.map +0 -1
  78. package/dist/matter/endpoints.d.ts +0 -2
  79. package/dist/matter/endpoints.d.ts.map +0 -1
  80. package/dist/matter/endpoints.js.map +0 -1
  81. package/dist/matter/export.d.ts +0 -5
  82. package/dist/matter/export.d.ts.map +0 -1
  83. package/dist/matter/export.js.map +0 -1
  84. package/dist/matter/types.d.ts +0 -3
  85. package/dist/matter/types.d.ts.map +0 -1
  86. package/dist/matter/types.js.map +0 -1
  87. package/dist/matterbridge.d.ts +0 -445
  88. package/dist/matterbridge.d.ts.map +0 -1
  89. package/dist/matterbridge.js.map +0 -1
  90. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -40
  91. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  92. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  93. package/dist/matterbridgeBehaviors.d.ts +0 -1398
  94. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  95. package/dist/matterbridgeBehaviors.js.map +0 -1
  96. package/dist/matterbridgeDeviceTypes.d.ts +0 -629
  97. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  98. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  99. package/dist/matterbridgeDynamicPlatform.d.ts +0 -40
  100. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  101. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  102. package/dist/matterbridgeEndpoint.d.ts +0 -1053
  103. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  104. package/dist/matterbridgeEndpoint.js.map +0 -1
  105. package/dist/matterbridgeEndpointHelpers.d.ts +0 -2749
  106. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  107. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  108. package/dist/matterbridgePlatform.d.ts +0 -294
  109. package/dist/matterbridgePlatform.d.ts.map +0 -1
  110. package/dist/matterbridgePlatform.js.map +0 -1
  111. package/dist/matterbridgeTypes.d.ts +0 -196
  112. package/dist/matterbridgeTypes.d.ts.map +0 -1
  113. package/dist/matterbridgeTypes.js.map +0 -1
  114. package/dist/pluginManager.d.ts +0 -273
  115. package/dist/pluginManager.d.ts.map +0 -1
  116. package/dist/pluginManager.js.map +0 -1
  117. package/dist/roboticVacuumCleaner.d.ts +0 -82
  118. package/dist/roboticVacuumCleaner.d.ts.map +0 -1
  119. package/dist/roboticVacuumCleaner.js.map +0 -1
  120. package/dist/shelly.d.ts +0 -153
  121. package/dist/shelly.d.ts.map +0 -1
  122. package/dist/shelly.js.map +0 -1
  123. package/dist/storage/export.d.ts +0 -2
  124. package/dist/storage/export.d.ts.map +0 -1
  125. package/dist/storage/export.js.map +0 -1
  126. package/dist/update.d.ts +0 -58
  127. package/dist/update.d.ts.map +0 -1
  128. package/dist/update.js.map +0 -1
  129. package/dist/utils/colorUtils.d.ts +0 -61
  130. package/dist/utils/colorUtils.d.ts.map +0 -1
  131. package/dist/utils/colorUtils.js.map +0 -1
  132. package/dist/utils/commandLine.d.ts +0 -58
  133. package/dist/utils/commandLine.d.ts.map +0 -1
  134. package/dist/utils/commandLine.js.map +0 -1
  135. package/dist/utils/copyDirectory.d.ts +0 -32
  136. package/dist/utils/copyDirectory.d.ts.map +0 -1
  137. package/dist/utils/copyDirectory.js.map +0 -1
  138. package/dist/utils/createZip.d.ts +0 -38
  139. package/dist/utils/createZip.d.ts.map +0 -1
  140. package/dist/utils/createZip.js.map +0 -1
  141. package/dist/utils/deepCopy.d.ts +0 -31
  142. package/dist/utils/deepCopy.d.ts.map +0 -1
  143. package/dist/utils/deepCopy.js.map +0 -1
  144. package/dist/utils/deepEqual.d.ts +0 -53
  145. package/dist/utils/deepEqual.d.ts.map +0 -1
  146. package/dist/utils/deepEqual.js.map +0 -1
  147. package/dist/utils/export.d.ts +0 -11
  148. package/dist/utils/export.d.ts.map +0 -1
  149. package/dist/utils/export.js.map +0 -1
  150. package/dist/utils/hex.d.ts +0 -48
  151. package/dist/utils/hex.d.ts.map +0 -1
  152. package/dist/utils/hex.js.map +0 -1
  153. package/dist/utils/isvalid.d.ts +0 -102
  154. package/dist/utils/isvalid.d.ts.map +0 -1
  155. package/dist/utils/isvalid.js.map +0 -1
  156. package/dist/utils/network.d.ts +0 -69
  157. package/dist/utils/network.d.ts.map +0 -1
  158. package/dist/utils/network.js.map +0 -1
  159. package/dist/utils/wait.d.ts +0 -52
  160. package/dist/utils/wait.d.ts.map +0 -1
  161. package/dist/utils/wait.js.map +0 -1
  162. package/dist/waterHeater.d.ts +0 -75
  163. package/dist/waterHeater.d.ts.map +0 -1
  164. package/dist/waterHeater.js.map +0 -1
package/dist/frontend.js CHANGED
@@ -1,112 +1,29 @@
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 { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
25
2
  import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
26
- // Node modules
27
3
  import { createServer } from 'node:http';
28
4
  import https from 'node:https';
29
5
  import os from 'node:os';
30
6
  import path from 'node:path';
31
7
  import { promises as fs } from 'node:fs';
32
- // Third-party modules
33
8
  import express from 'express';
34
9
  import WebSocket, { WebSocketServer } from 'ws';
35
10
  import multer from 'multer';
36
- // AnsiLogger module
37
11
  import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from './logger/export.js';
38
- // Matterbridge
39
12
  import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout } from './utils/export.js';
40
13
  import { plg } from './matterbridgeTypes.js';
41
14
  import { hasParameter } from './utils/export.js';
42
15
  import { capitalizeFirstLetter } from './matterbridgeEndpointHelpers.js';
43
- /**
44
- * Websocket message ID for logging.
45
- * @constant {number}
46
- */
47
16
  export const WS_ID_LOG = 0;
48
- /**
49
- * Websocket message ID indicating a refresh is needed.
50
- * @constant {number}
51
- */
52
17
  export const WS_ID_REFRESH_NEEDED = 1;
53
- /**
54
- * Websocket message ID indicating a restart is needed.
55
- * @constant {number}
56
- */
57
18
  export const WS_ID_RESTART_NEEDED = 2;
58
- /**
59
- * Websocket message ID indicating a cpu update.
60
- * @constant {number}
61
- */
62
19
  export const WS_ID_CPU_UPDATE = 3;
63
- /**
64
- * Websocket message ID indicating a memory update.
65
- * @constant {number}
66
- */
67
20
  export const WS_ID_MEMORY_UPDATE = 4;
68
- /**
69
- * Websocket message ID indicating an uptime update.
70
- * @constant {number}
71
- */
72
21
  export const WS_ID_UPTIME_UPDATE = 5;
73
- /**
74
- * Websocket message ID indicating a snackbar message.
75
- * @constant {number}
76
- */
77
22
  export const WS_ID_SNACKBAR = 6;
78
- /**
79
- * Websocket message ID indicating matterbridge has un update available.
80
- * @constant {number}
81
- */
82
23
  export const WS_ID_UPDATE_NEEDED = 7;
83
- /**
84
- * Websocket message ID indicating a state update.
85
- * @constant {number}
86
- */
87
24
  export const WS_ID_STATEUPDATE = 8;
88
- /**
89
- * Websocket message ID indicating to close a permanent snackbar message.
90
- * @constant {number}
91
- */
92
25
  export const WS_ID_CLOSE_SNACKBAR = 9;
93
- /**
94
- * Websocket message ID indicating a shelly system update.
95
- * check:
96
- * curl -k http://127.0.0.1:8101/api/updates/sys/check
97
- * perform:
98
- * curl -k http://127.0.0.1:8101/api/updates/sys/perform
99
- * @constant {number}
100
- */
101
26
  export const WS_ID_SHELLY_SYS_UPDATE = 100;
102
- /**
103
- * Websocket message ID indicating a shelly main update.
104
- * check:
105
- * curl -k http://127.0.0.1:8101/api/updates/main/check
106
- * perform:
107
- * curl -k http://127.0.0.1:8101/api/updates/main/perform
108
- * @constant {number}
109
- */
110
27
  export const WS_ID_SHELLY_MAIN_UPDATE = 101;
111
28
  export class Frontend {
112
29
  matterbridge;
@@ -119,7 +36,7 @@ export class Frontend {
119
36
  webSocketServer;
120
37
  constructor(matterbridge) {
121
38
  this.matterbridge = matterbridge;
122
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
39
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
123
40
  }
124
41
  set logLevel(logLevel) {
125
42
  this.log.logLevel = logLevel;
@@ -127,43 +44,13 @@ export class Frontend {
127
44
  async start(port = 8283) {
128
45
  this.port = port;
129
46
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
130
- // Initialize multer with the upload directory
131
47
  const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
132
48
  await fs.mkdir(uploadDir, { recursive: true });
133
49
  const upload = multer({ dest: uploadDir });
134
- // Create the express app that serves the frontend
135
50
  this.expressApp = express();
136
- // Inject logging/debug wrapper for route/middleware registration
137
- /*
138
- const methods = ['get', 'post', 'put', 'delete', 'use'];
139
- for (const method of methods) {
140
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
- const original = (this.expressApp as any)[method].bind(this.expressApp);
142
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
143
- (this.expressApp as any)[method] = (path: any, ...rest: any) => {
144
- try {
145
- console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
146
- return original(path, ...rest);
147
- } catch (err) {
148
- console.error(`[ERROR] Failed to register route: ${path}`);
149
- throw err;
150
- }
151
- };
152
- }
153
- */
154
- // Log all requests to the server for debugging
155
- /*
156
- this.expressApp.use((req, res, next) => {
157
- this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
158
- next();
159
- });
160
- */
161
- // Serve static files from '/static' endpoint
162
51
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
163
52
  if (!hasParameter('ssl')) {
164
- // Create an HTTP server and attach the express app
165
53
  this.httpServer = createServer(this.expressApp);
166
- // Listen on the specified port
167
54
  if (hasParameter('ingress')) {
168
55
  this.httpServer.listen(this.port, '0.0.0.0', () => {
169
56
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -177,7 +64,6 @@ export class Frontend {
177
64
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
178
65
  });
179
66
  }
180
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
181
67
  this.httpServer.on('error', (error) => {
182
68
  this.log.error(`Frontend http server error listening on ${this.port}`);
183
69
  switch (error.code) {
@@ -193,7 +79,6 @@ export class Frontend {
193
79
  });
194
80
  }
195
81
  else {
196
- // Load the SSL certificate, the private key and optionally the CA certificate
197
82
  let cert;
198
83
  try {
199
84
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
@@ -221,9 +106,7 @@ export class Frontend {
221
106
  this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
222
107
  }
223
108
  const serverOptions = { cert, key, ca };
224
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
225
109
  this.httpsServer = https.createServer(serverOptions, this.expressApp);
226
- // Listen on the specified port
227
110
  if (hasParameter('ingress')) {
228
111
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
229
112
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -237,7 +120,6 @@ export class Frontend {
237
120
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
238
121
  });
239
122
  }
240
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
241
123
  this.httpsServer.on('error', (error) => {
242
124
  this.log.error(`Frontend https server error listening on ${this.port}`);
243
125
  switch (error.code) {
@@ -254,18 +136,16 @@ export class Frontend {
254
136
  }
255
137
  if (this.initializeError)
256
138
  return;
257
- // Create a WebSocket server and attach it to the http or https server
258
139
  const wssPort = this.port;
259
140
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
260
141
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
261
142
  this.webSocketServer.on('connection', (ws, request) => {
262
143
  const clientIp = request.socket.remoteAddress;
263
- // Set the global logger callback for the WebSocketServer
264
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
265
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
266
- callbackLogLevel = "info" /* LogLevel.INFO */;
267
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
268
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
144
+ let callbackLogLevel = "notice";
145
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
146
+ callbackLogLevel = "info";
147
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
148
+ callbackLogLevel = "debug";
269
149
  AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
270
150
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
271
151
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -299,7 +179,6 @@ export class Frontend {
299
179
  this.webSocketServer.on('error', (ws, error) => {
300
180
  this.log.error(`WebSocketServer error: ${error}`);
301
181
  });
302
- // Subscribe to cli events
303
182
  const { cliEmitter } = await import('./cli.js');
304
183
  cliEmitter.removeAllListeners();
305
184
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
@@ -311,8 +190,6 @@ export class Frontend {
311
190
  cliEmitter.on('cpu', (cpuUsage) => {
312
191
  this.wssSendCpuUpdate(cpuUsage);
313
192
  });
314
- // Endpoint to validate login code
315
- // curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
316
193
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
317
194
  const { password } = req.body;
318
195
  this.log.debug('The frontend sent /api/login', password);
@@ -331,27 +208,23 @@ export class Frontend {
331
208
  this.log.warn('/api/login error wrong password');
332
209
  res.json({ valid: false });
333
210
  }
334
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
335
211
  }
336
212
  catch (error) {
337
213
  this.log.error('/api/login error getting password');
338
214
  res.json({ valid: false });
339
215
  }
340
216
  });
341
- // Endpoint to provide health check for docker
342
217
  this.expressApp.get('/health', (req, res) => {
343
218
  this.log.debug('Express received /health');
344
219
  const healthStatus = {
345
- status: 'ok', // Indicate service is healthy
346
- uptime: process.uptime(), // Server uptime in seconds
347
- timestamp: new Date().toISOString(), // Current timestamp
220
+ status: 'ok',
221
+ uptime: process.uptime(),
222
+ timestamp: new Date().toISOString(),
348
223
  };
349
224
  res.status(200).json(healthStatus);
350
225
  });
351
- // Endpoint to provide memory usage details
352
226
  this.expressApp.get('/memory', async (req, res) => {
353
227
  this.log.debug('Express received /memory');
354
- // Memory usage from process
355
228
  const memoryUsageRaw = process.memoryUsage();
356
229
  const memoryUsage = {
357
230
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -360,13 +233,10 @@ export class Frontend {
360
233
  external: this.formatMemoryUsage(memoryUsageRaw.external),
361
234
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
362
235
  };
363
- // V8 heap statistics
364
236
  const { default: v8 } = await import('node:v8');
365
237
  const heapStatsRaw = v8.getHeapStatistics();
366
238
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
367
- // Format heapStats
368
239
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
369
- // Format heapSpaces
370
240
  const heapSpaces = heapSpacesRaw.map((space) => ({
371
241
  ...space,
372
242
  space_size: this.formatMemoryUsage(space.space_size),
@@ -384,23 +254,19 @@ export class Frontend {
384
254
  };
385
255
  res.status(200).json(memoryReport);
386
256
  });
387
- // Endpoint to provide settings
388
257
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
389
258
  this.log.debug('The frontend sent /api/settings');
390
259
  res.json(await this.getApiSettings());
391
260
  });
392
- // Endpoint to provide plugins
393
261
  this.expressApp.get('/api/plugins', async (req, res) => {
394
262
  this.log.debug('The frontend sent /api/plugins');
395
263
  res.json(this.getBaseRegisteredPlugins());
396
264
  });
397
- // Endpoint to provide devices
398
265
  this.expressApp.get('/api/devices', async (req, res) => {
399
266
  this.log.debug('The frontend sent /api/devices');
400
267
  const devices = await this.getDevices();
401
268
  res.json(devices);
402
269
  });
403
- // Endpoint to view the matterbridge log
404
270
  this.expressApp.get('/api/view-mblog', async (req, res) => {
405
271
  this.log.debug('The frontend sent /api/view-mblog');
406
272
  try {
@@ -413,7 +279,6 @@ export class Frontend {
413
279
  res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
414
280
  }
415
281
  });
416
- // Endpoint to view the matter.js log
417
282
  this.expressApp.get('/api/view-mjlog', async (req, res) => {
418
283
  this.log.debug('The frontend sent /api/view-mjlog');
419
284
  try {
@@ -426,7 +291,6 @@ export class Frontend {
426
291
  res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
427
292
  }
428
293
  });
429
- // Endpoint to view the shelly log
430
294
  this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
431
295
  this.log.debug('The frontend sent /api/shellyviewsystemlog');
432
296
  try {
@@ -439,7 +303,6 @@ export class Frontend {
439
303
  res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
440
304
  }
441
305
  });
442
- // Endpoint to download the matterbridge log
443
306
  this.expressApp.get('/api/download-mblog', async (req, res) => {
444
307
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
445
308
  try {
@@ -452,7 +315,6 @@ export class Frontend {
452
315
  this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
453
316
  }
454
317
  res.type('text/plain');
455
- // res.download(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
456
318
  res.download(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
457
319
  if (error) {
458
320
  this.log.error(`Error downloading log file ${this.matterbridge.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
@@ -460,7 +322,6 @@ export class Frontend {
460
322
  }
461
323
  });
462
324
  });
463
- // Endpoint to download the matter log
464
325
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
465
326
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
466
327
  try {
@@ -480,7 +341,6 @@ export class Frontend {
480
341
  }
481
342
  });
482
343
  });
483
- // Endpoint to download the shelly log
484
344
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
485
345
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
486
346
  try {
@@ -500,7 +360,6 @@ export class Frontend {
500
360
  }
501
361
  });
502
362
  });
503
- // Endpoint to download the matterbridge storage directory
504
363
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
505
364
  this.log.debug('The frontend sent /api/download-mbstorage');
506
365
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
@@ -511,7 +370,6 @@ export class Frontend {
511
370
  }
512
371
  });
513
372
  });
514
- // Endpoint to download the matter storage file
515
373
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
516
374
  this.log.debug('The frontend sent /api/download-mjstorage');
517
375
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
@@ -522,7 +380,6 @@ export class Frontend {
522
380
  }
523
381
  });
524
382
  });
525
- // Endpoint to download the matterbridge plugin directory
526
383
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
527
384
  this.log.debug('The frontend sent /api/download-pluginstorage');
528
385
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
@@ -533,7 +390,6 @@ export class Frontend {
533
390
  }
534
391
  });
535
392
  });
536
- // Endpoint to download the matterbridge plugin config files
537
393
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
538
394
  this.log.debug('The frontend sent /api/download-pluginconfig');
539
395
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
@@ -544,7 +400,6 @@ export class Frontend {
544
400
  }
545
401
  });
546
402
  });
547
- // Endpoint to download the matterbridge backup (created with the backup command)
548
403
  this.expressApp.get('/api/download-backup', async (req, res) => {
549
404
  this.log.debug('The frontend sent /api/download-backup');
550
405
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -554,7 +409,6 @@ export class Frontend {
554
409
  }
555
410
  });
556
411
  });
557
- // Endpoint to upload a package
558
412
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
559
413
  const { filename } = req.body;
560
414
  const file = req.file;
@@ -564,13 +418,10 @@ export class Frontend {
564
418
  return;
565
419
  }
566
420
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
567
- // Define the path where the plugin file will be saved
568
421
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
569
422
  try {
570
- // Move the uploaded file to the specified path
571
423
  await fs.rename(file.path, filePath);
572
424
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
573
- // Install the plugin package
574
425
  if (filename.endsWith('.tgz')) {
575
426
  await this.matterbridge.spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
576
427
  this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
@@ -589,7 +440,6 @@ export class Frontend {
589
440
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
590
441
  }
591
442
  });
592
- // Fallback for routing (must be the last route)
593
443
  this.expressApp.use((req, res) => {
594
444
  this.log.debug('The frontend sent:', req.url);
595
445
  res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -598,15 +448,12 @@ export class Frontend {
598
448
  }
599
449
  async stop() {
600
450
  this.log.debug('Stopping the frontend...');
601
- // Remove listeners from the express app
602
451
  if (this.expressApp) {
603
452
  this.expressApp.removeAllListeners();
604
453
  this.expressApp = undefined;
605
454
  this.log.debug('Frontend app closed successfully');
606
455
  }
607
- // Close the WebSocket server
608
456
  if (this.webSocketServer) {
609
- // Close all active connections
610
457
  this.webSocketServer.clients.forEach((client) => {
611
458
  if (client.readyState === WebSocket.OPEN) {
612
459
  client.close();
@@ -626,7 +473,6 @@ export class Frontend {
626
473
  this.webSocketServer.removeAllListeners();
627
474
  this.webSocketServer = undefined;
628
475
  }
629
- // Close the http server
630
476
  if (this.httpServer) {
631
477
  await withTimeout(new Promise((resolve) => {
632
478
  this.httpServer?.close((error) => {
@@ -643,7 +489,6 @@ export class Frontend {
643
489
  this.httpServer = undefined;
644
490
  this.log.debug('Frontend http server closed successfully');
645
491
  }
646
- // Close the https server
647
492
  if (this.httpsServer) {
648
493
  await withTimeout(new Promise((resolve) => {
649
494
  this.httpsServer?.close((error) => {
@@ -662,7 +507,6 @@ export class Frontend {
662
507
  }
663
508
  this.log.debug('Frontend stopped successfully');
664
509
  }
665
- // Function to format bytes to KB, MB, or GB
666
510
  formatMemoryUsage = (bytes) => {
667
511
  if (bytes >= 1024 ** 3) {
668
512
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -674,7 +518,6 @@ export class Frontend {
674
518
  return `${(bytes / 1024).toFixed(2)} KB`;
675
519
  }
676
520
  };
677
- // Function to format system uptime with only the most significant unit
678
521
  formatOsUpTime = (seconds) => {
679
522
  if (seconds >= 86400) {
680
523
  const days = Math.floor(seconds / 86400);
@@ -690,14 +533,8 @@ export class Frontend {
690
533
  }
691
534
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
692
535
  };
693
- /**
694
- * Retrieves the api settings data.
695
- *
696
- * @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
697
- */
698
536
  async getApiSettings() {
699
537
  const { lastCpuUsage } = await import('./cli.js');
700
- // Update the system information
701
538
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
702
539
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
703
540
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -706,7 +543,6 @@ export class Frontend {
706
543
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
707
544
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
708
545
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
709
- // Update the matterbridge information
710
546
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
711
547
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
712
548
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
@@ -725,11 +561,6 @@ export class Frontend {
725
561
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
726
562
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
727
563
  }
728
- /**
729
- * Retrieves the reachable attribute.
730
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
731
- * @returns {boolean} The reachable attribute.
732
- */
733
564
  getReachability(device) {
734
565
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
735
566
  return false;
@@ -739,11 +570,6 @@ export class Frontend {
739
570
  return true;
740
571
  return false;
741
572
  }
742
- /**
743
- * Retrieves the power source attribute.
744
- * @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice object.
745
- * @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
746
- */
747
573
  getPowerSource(endpoint) {
748
574
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
749
575
  return undefined;
@@ -759,20 +585,13 @@ export class Frontend {
759
585
  }
760
586
  return;
761
587
  };
762
- // Root endpoint
763
588
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
764
589
  return powerSource(endpoint);
765
- // Child endpoints
766
590
  for (const child of endpoint.getChildEndpoints()) {
767
591
  if (child.hasClusterServer(PowerSource.Cluster.id))
768
592
  return powerSource(child);
769
593
  }
770
594
  }
771
- /**
772
- * Retrieves the cluster text description from a given device.
773
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
774
- * @returns {string} The attributes description of the cluster servers in the device.
775
- */
776
595
  getClusterTextFromDevice(device) {
777
596
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
778
597
  return '';
@@ -814,7 +633,6 @@ export class Frontend {
814
633
  let attributes = '';
815
634
  let supportedModes = [];
816
635
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
817
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
818
636
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
819
637
  return;
820
638
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -906,13 +724,8 @@ export class Frontend {
906
724
  if (clusterName === 'userLabel' && attributeName === 'labelList')
907
725
  attributes += `${getUserLabel(device)} `;
908
726
  });
909
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
910
727
  return attributes.trimStart().trimEnd();
911
728
  }
912
- /**
913
- * Retrieves the base registered plugins sanitized for res.json().
914
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
915
- */
916
729
  getBaseRegisteredPlugins() {
917
730
  const baseRegisteredPlugins = [];
918
731
  for (const plugin of this.matterbridge.plugins) {
@@ -951,18 +764,11 @@ export class Frontend {
951
764
  }
952
765
  return baseRegisteredPlugins;
953
766
  }
954
- /**
955
- * Retrieves the devices from Matterbridge.
956
- * @param {string} [pluginName] - The name of the plugin to filter devices by.
957
- * @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices.
958
- */
959
767
  async getDevices(pluginName) {
960
768
  const devices = [];
961
769
  this.matterbridge.devices.forEach(async (device) => {
962
- // Filter by pluginName if provided
963
770
  if (pluginName && pluginName !== device.plugin)
964
771
  return;
965
- // Check if the device has the required properties
966
772
  if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
967
773
  return;
968
774
  const cluster = this.getClusterTextFromDevice(device);
@@ -982,37 +788,22 @@ export class Frontend {
982
788
  });
983
789
  return devices;
984
790
  }
985
- /**
986
- * Retrieves the clusters from a given plugin and endpoint number.
987
- *
988
- * Response for /api/clusters
989
- *
990
- * @param {string} pluginName - The name of the plugin.
991
- * @param {number} endpointNumber - The endpoint number.
992
- * @returns {Promise<ApiClustersResponse | undefined>} A promise that resolves to the clusters or undefined if not found.
993
- */
994
791
  getClusters(pluginName, endpointNumber) {
995
792
  const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
996
793
  if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
997
794
  this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
998
795
  return;
999
796
  }
1000
- // this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
1001
- // Get the device types from the main endpoint
1002
797
  const deviceTypes = [];
1003
798
  const clusters = [];
1004
799
  endpoint.state.descriptor.deviceTypeList.forEach((d) => {
1005
800
  deviceTypes.push(d.deviceType);
1006
801
  });
1007
- // Get the clusters from the main endpoint
1008
802
  endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1009
803
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1010
804
  return;
1011
805
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1012
806
  return;
1013
- // console.log(
1014
- // `${idn}${endpoint.deviceName}${rs}${nf} => Cluster: ${CYAN}${clusterName} (0x${clusterId.toString(16).padStart(2, '0')})${nf} Attribute: ${CYAN}${attributeName} (0x${attributeId.toString(16).padStart(2, '0')})${nf} Value: ${YELLOW}${typeof attributeValue === 'object' ? stringify(attributeValue as object) : attributeValue}${nf}`,
1015
- // );
1016
807
  clusters.push({
1017
808
  endpoint: endpoint.number.toString(),
1018
809
  id: 'main',
@@ -1025,18 +816,12 @@ export class Frontend {
1025
816
  attributeLocalValue: attributeValue,
1026
817
  });
1027
818
  });
1028
- // Get the child endpoints
1029
819
  const childEndpoints = endpoint.getChildEndpoints();
1030
- // if (childEndpoints.length === 0) {
1031
- // this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
1032
- // }
1033
820
  childEndpoints.forEach((childEndpoint) => {
1034
821
  if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
1035
822
  this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1036
823
  return;
1037
824
  }
1038
- // this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
1039
- // Get the device types of the child endpoint
1040
825
  const deviceTypes = [];
1041
826
  childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
1042
827
  deviceTypes.push(d.deviceType);
@@ -1046,12 +831,9 @@ export class Frontend {
1046
831
  return;
1047
832
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1048
833
  return;
1049
- // console.log(
1050
- // `${idn}${childEndpoint.deviceName}${rs}${nf} => Cluster: ${CYAN}${clusterName} (0x${clusterId.toString(16).padStart(2, '0')})${nf} Attribute: ${CYAN}${attributeName} (0x${attributeId.toString(16).padStart(2, '0')})${nf} Value: ${YELLOW}${typeof attributeValue === 'object' ? stringify(attributeValue as object) : attributeValue}${nf}`,
1051
- // );
1052
834
  clusters.push({
1053
835
  endpoint: childEndpoint.number.toString(),
1054
- id: childEndpoint.maybeId ?? 'null', // Never happens
836
+ id: childEndpoint.maybeId ?? 'null',
1055
837
  deviceTypes,
1056
838
  clusterName: capitalizeFirstLetter(clusterName),
1057
839
  clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
@@ -1064,13 +846,6 @@ export class Frontend {
1064
846
  });
1065
847
  return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
1066
848
  }
1067
- /**
1068
- * Handles incoming websocket messages for the Matterbridge frontend.
1069
- *
1070
- * @param {WebSocket} client - The websocket client that sent the message.
1071
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1072
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1073
- */
1074
849
  async wsMessageHandler(client, message) {
1075
850
  let data;
1076
851
  try {
@@ -1117,10 +892,8 @@ export class Frontend {
1117
892
  this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
1118
893
  const packageName = data.params.packageName.replace(/@.*$/, '');
1119
894
  if (data.params.restart === false && packageName !== 'matterbridge') {
1120
- // The install comes from InstallPlugins
1121
895
  this.matterbridge.plugins.add(packageName).then((plugin) => {
1122
896
  if (plugin) {
1123
- // The plugin is not registered
1124
897
  this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
1125
898
  this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
1126
899
  this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
@@ -1128,7 +901,6 @@ export class Frontend {
1128
901
  });
1129
902
  }
1130
903
  else {
1131
- // The plugin is already registered
1132
904
  this.wssSendSnackbarMessage(`Restart required`, 0);
1133
905
  this.wssSendRefreshRequired('plugins');
1134
906
  this.wssSendRestartRequired();
@@ -1136,7 +908,6 @@ export class Frontend {
1136
908
  });
1137
909
  }
1138
910
  else {
1139
- // The package is matterbridge
1140
911
  if (this.matterbridge.restartMode !== '') {
1141
912
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1142
913
  this.matterbridge.shutdownProcess();
@@ -1158,7 +929,6 @@ export class Frontend {
1158
929
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
1159
930
  return;
1160
931
  }
1161
- // The package is a plugin
1162
932
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
1163
933
  if (plugin) {
1164
934
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
@@ -1167,7 +937,6 @@ export class Frontend {
1167
937
  this.wssSendRefreshRequired('plugins');
1168
938
  this.wssSendRefreshRequired('devices');
1169
939
  }
1170
- // Uninstall the package
1171
940
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1172
941
  this.matterbridge
1173
942
  .spawnCommand('npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
@@ -1479,22 +1248,22 @@ export class Frontend {
1479
1248
  if (isValidString(data.params.value, 4)) {
1480
1249
  this.log.debug('Matterbridge logger level:', data.params.value);
1481
1250
  if (data.params.value === 'Debug') {
1482
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1251
+ await this.matterbridge.setLogLevel("debug");
1483
1252
  }
1484
1253
  else if (data.params.value === 'Info') {
1485
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1254
+ await this.matterbridge.setLogLevel("info");
1486
1255
  }
1487
1256
  else if (data.params.value === 'Notice') {
1488
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1257
+ await this.matterbridge.setLogLevel("notice");
1489
1258
  }
1490
1259
  else if (data.params.value === 'Warn') {
1491
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1260
+ await this.matterbridge.setLogLevel("warn");
1492
1261
  }
1493
1262
  else if (data.params.value === 'Error') {
1494
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1263
+ await this.matterbridge.setLogLevel("error");
1495
1264
  }
1496
1265
  else if (data.params.value === 'Fatal') {
1497
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1266
+ await this.matterbridge.setLogLevel("fatal");
1498
1267
  }
1499
1268
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1500
1269
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1505,7 +1274,6 @@ export class Frontend {
1505
1274
  this.log.debug('Matterbridge file log:', data.params.value);
1506
1275
  this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
1507
1276
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1508
- // Create the file logger for matterbridge
1509
1277
  if (data.params.value)
1510
1278
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1511
1279
  else
@@ -1670,19 +1438,15 @@ export class Frontend {
1670
1438
  return;
1671
1439
  }
1672
1440
  const config = plugin.configJson;
1673
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1674
1441
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1675
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1676
1442
  if (select === 'serial')
1677
1443
  this.log.info(`Selected device serial ${data.params.serial}`);
1678
1444
  if (select === 'name')
1679
1445
  this.log.info(`Selected device name ${data.params.name}`);
1680
1446
  if (config && select && (select === 'serial' || select === 'name')) {
1681
- // Remove postfix from the serial if it exists
1682
1447
  if (config.postfix) {
1683
1448
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1684
1449
  }
1685
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1686
1450
  if (isValidArray(config.whiteList, 1)) {
1687
1451
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1688
1452
  config.whiteList.push(data.params.serial);
@@ -1691,7 +1455,6 @@ export class Frontend {
1691
1455
  config.whiteList.push(data.params.name);
1692
1456
  }
1693
1457
  }
1694
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1695
1458
  if (isValidArray(config.blackList, 1)) {
1696
1459
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1697
1460
  config.blackList = config.blackList.filter((item) => item !== data.params.serial);
@@ -1721,9 +1484,7 @@ export class Frontend {
1721
1484
  return;
1722
1485
  }
1723
1486
  const config = plugin.configJson;
1724
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1725
1487
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1726
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1727
1488
  if (select === 'serial')
1728
1489
  this.log.info(`Unselected device serial ${data.params.serial}`);
1729
1490
  if (select === 'name')
@@ -1732,7 +1493,6 @@ export class Frontend {
1732
1493
  if (config.postfix) {
1733
1494
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1734
1495
  }
1735
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1736
1496
  if (isValidArray(config.whiteList, 1)) {
1737
1497
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1738
1498
  config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
@@ -1741,7 +1501,6 @@ export class Frontend {
1741
1501
  config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
1742
1502
  }
1743
1503
  }
1744
- // Add the serial to the blackList
1745
1504
  if (isValidArray(config.blackList)) {
1746
1505
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1747
1506
  config.blackList.push(data.params.serial);
@@ -1774,219 +1533,114 @@ export class Frontend {
1774
1533
  this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
1775
1534
  }
1776
1535
  }
1777
- /**
1778
- * Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1779
- *
1780
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1781
- * @param {string} time - The time string of the message
1782
- * @param {string} name - The logger name of the message
1783
- * @param {string} message - The content of the message.
1784
- *
1785
- * @remark
1786
- * The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
1787
- * It also replaces all occurrences of \" with " and angle-brackets with &lt; and &gt;.
1788
- * The function sends the message to all connected clients.
1789
- */
1790
1536
  wssSendMessage(level, time, name, message) {
1791
1537
  if (!level || !time || !name || !message)
1792
1538
  return;
1793
- // Remove ANSI escape codes from the message
1794
- // eslint-disable-next-line no-control-regex
1795
1539
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1796
- // Remove leading asterisks from the message
1797
1540
  message = message.replace(/^\*+/, '');
1798
- // Replace all occurrences of \t and \n
1799
1541
  message = message.replace(/[\t\n]/g, '');
1800
- // Remove non-printable characters
1801
- // eslint-disable-next-line no-control-regex
1802
1542
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1803
- // Replace all occurrences of \" with "
1804
1543
  message = message.replace(/\\"/g, '"');
1805
- // Replace all occurrences of angle-brackets with &lt; and &gt;"
1806
1544
  message = message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1807
- // Define the maximum allowed length for continuous characters without a space
1808
1545
  const maxContinuousLength = 100;
1809
1546
  const keepStartLength = 20;
1810
1547
  const keepEndLength = 20;
1811
- // Split the message into words
1812
1548
  message = message
1813
1549
  .split(' ')
1814
1550
  .map((word) => {
1815
- // If the word length exceeds the max continuous length, insert spaces and truncate
1816
1551
  if (word.length > maxContinuousLength) {
1817
1552
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1818
1553
  }
1819
1554
  return word;
1820
1555
  })
1821
1556
  .join(' ');
1822
- // Send the message to all connected clients
1823
1557
  this.webSocketServer?.clients.forEach((client) => {
1824
1558
  if (client.readyState === WebSocket.OPEN) {
1825
1559
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1826
1560
  }
1827
1561
  });
1828
1562
  }
1829
- /**
1830
- * Sends a need to refresh WebSocket message to all connected clients.
1831
- *
1832
- * @param {string} changed - The changed value. If null, the whole page will be refreshed.
1833
- * possible values:
1834
- * - 'matterbridgeLatestVersion'
1835
- * - 'matterbridgeAdvertise'
1836
- * - 'online'
1837
- * - 'offline'
1838
- * - 'reachability'
1839
- * - 'settings'
1840
- * - 'plugins'
1841
- * - 'pluginsRestart'
1842
- * - 'devices'
1843
- * - 'fabrics'
1844
- * - 'sessions'
1845
- */
1846
1563
  wssSendRefreshRequired(changed = null) {
1847
1564
  this.log.debug('Sending a refresh required message to all connected clients');
1848
- // Send the message to all connected clients
1849
1565
  this.webSocketServer?.clients.forEach((client) => {
1850
1566
  if (client.readyState === WebSocket.OPEN) {
1851
1567
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
1852
1568
  }
1853
1569
  });
1854
1570
  }
1855
- /**
1856
- * Sends a need to restart WebSocket message to all connected clients.
1857
- *
1858
- */
1859
1571
  wssSendRestartRequired(snackbar = true) {
1860
1572
  this.log.debug('Sending a restart required message to all connected clients');
1861
1573
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1862
1574
  if (snackbar === true)
1863
1575
  this.wssSendSnackbarMessage(`Restart required`, 0);
1864
- // Send the message to all connected clients
1865
1576
  this.webSocketServer?.clients.forEach((client) => {
1866
1577
  if (client.readyState === WebSocket.OPEN) {
1867
1578
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
1868
1579
  }
1869
1580
  });
1870
1581
  }
1871
- /**
1872
- * Sends a need to update WebSocket message to all connected clients.
1873
- *
1874
- */
1875
1582
  wssSendUpdateRequired() {
1876
1583
  this.log.debug('Sending an update required message to all connected clients');
1877
1584
  this.matterbridge.matterbridgeInformation.updateRequired = true;
1878
- // Send the message to all connected clients
1879
1585
  this.webSocketServer?.clients.forEach((client) => {
1880
1586
  if (client.readyState === WebSocket.OPEN) {
1881
1587
  client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
1882
1588
  }
1883
1589
  });
1884
1590
  }
1885
- /**
1886
- * Sends a cpu update message to all connected clients.
1887
- *
1888
- */
1889
1591
  wssSendCpuUpdate(cpuUsage) {
1890
1592
  if (hasParameter('debug'))
1891
1593
  this.log.debug('Sending a cpu update message to all connected clients');
1892
- // Send the message to all connected clients
1893
1594
  this.webSocketServer?.clients.forEach((client) => {
1894
1595
  if (client.readyState === WebSocket.OPEN) {
1895
1596
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
1896
1597
  }
1897
1598
  });
1898
1599
  }
1899
- /**
1900
- * Sends a memory update message to all connected clients.
1901
- *
1902
- */
1903
1600
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
1904
1601
  if (hasParameter('debug'))
1905
1602
  this.log.debug('Sending a memory update message to all connected clients');
1906
- // Send the message to all connected clients
1907
1603
  this.webSocketServer?.clients.forEach((client) => {
1908
1604
  if (client.readyState === WebSocket.OPEN) {
1909
1605
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
1910
1606
  }
1911
1607
  });
1912
1608
  }
1913
- /**
1914
- * Sends an uptime update message to all connected clients.
1915
- *
1916
- */
1917
1609
  wssSendUptimeUpdate(systemUptime, processUptime) {
1918
1610
  if (hasParameter('debug'))
1919
1611
  this.log.debug('Sending a uptime update message to all connected clients');
1920
- // Send the message to all connected clients
1921
1612
  this.webSocketServer?.clients.forEach((client) => {
1922
1613
  if (client.readyState === WebSocket.OPEN) {
1923
1614
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
1924
1615
  }
1925
1616
  });
1926
1617
  }
1927
- /**
1928
- * Sends an open snackbar message to all connected clients.
1929
- * @param {string} message - The message to send.
1930
- * @param {number} timeout - The timeout in seconds for the snackbar message.
1931
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
1932
- *
1933
- */
1934
1618
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
1935
1619
  this.log.debug('Sending a snackbar message to all connected clients');
1936
- // Send the message to all connected clients
1937
1620
  this.webSocketServer?.clients.forEach((client) => {
1938
1621
  if (client.readyState === WebSocket.OPEN) {
1939
1622
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
1940
1623
  }
1941
1624
  });
1942
1625
  }
1943
- /**
1944
- * Sends a close snackbar message to all connected clients.
1945
- * @param {string} message - The message to send.
1946
- *
1947
- */
1948
1626
  wssSendCloseSnackbarMessage(message) {
1949
1627
  this.log.debug('Sending a close snackbar message to all connected clients');
1950
- // Send the message to all connected clients
1951
1628
  this.webSocketServer?.clients.forEach((client) => {
1952
1629
  if (client.readyState === WebSocket.OPEN) {
1953
1630
  client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
1954
1631
  }
1955
1632
  });
1956
1633
  }
1957
- /**
1958
- * Sends an attribute update message to all connected WebSocket clients.
1959
- *
1960
- * @param {string | undefined} plugin - The name of the plugin.
1961
- * @param {string | undefined} serialNumber - The serial number of the device.
1962
- * @param {string | undefined} uniqueId - The unique identifier of the device.
1963
- * @param {string} cluster - The cluster name where the attribute belongs.
1964
- * @param {string} attribute - The name of the attribute that changed.
1965
- * @param {number | string | boolean} value - The new value of the attribute.
1966
- *
1967
- * @remarks
1968
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
1969
- * with the updated attribute information.
1970
- */
1971
1634
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
1972
1635
  this.log.debug('Sending an attribute update message to all connected clients');
1973
- // Send the message to all connected clients
1974
1636
  this.webSocketServer?.clients.forEach((client) => {
1975
1637
  if (client.readyState === WebSocket.OPEN) {
1976
1638
  client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
1977
1639
  }
1978
1640
  });
1979
1641
  }
1980
- /**
1981
- * Sends a message to all connected clients.
1982
- * @param {number} id - The message id.
1983
- * @param {string} method - The message method.
1984
- * @param {Record<string, string | number | boolean>} params - The message parameters.
1985
- *
1986
- */
1987
1642
  wssBroadcastMessage(id, method, params) {
1988
1643
  this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
1989
- // Send the message to all connected clients
1990
1644
  this.webSocketServer?.clients.forEach((client) => {
1991
1645
  if (client.readyState === WebSocket.OPEN) {
1992
1646
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -1994,4 +1648,3 @@ export class Frontend {
1994
1648
  });
1995
1649
  }
1996
1650
  }
1997
- //# sourceMappingURL=frontend.js.map