matterbridge 3.1.0 → 3.1.1-dev-20250629-cfe9124

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 (194) hide show
  1. package/CHANGELOG.md +25 -4
  2. package/README-DEV.md +17 -3
  3. package/README-DOCKER.md +3 -1
  4. package/README-NGINX.md +30 -22
  5. package/README-PODMAN.md +3 -1
  6. package/README-SERVICE.md +8 -2
  7. package/dist/batteryStorage.js +24 -0
  8. package/dist/cli.js +2 -91
  9. package/dist/clusters/export.js +0 -2
  10. package/dist/defaultConfigSchema.js +0 -24
  11. package/dist/deviceManager.js +1 -94
  12. package/dist/devices/export.js +2 -2
  13. package/dist/evse.js +9 -70
  14. package/dist/frontend.js +16 -413
  15. package/dist/globalMatterbridge.js +0 -47
  16. package/dist/helpers.js +0 -53
  17. package/dist/index.js +3 -32
  18. package/dist/laundryWasher.js +7 -92
  19. package/dist/logger/export.js +0 -1
  20. package/dist/matter/behaviors.js +0 -2
  21. package/dist/matter/clusters.js +0 -2
  22. package/dist/matter/devices.js +0 -2
  23. package/dist/matter/endpoints.js +0 -2
  24. package/dist/matter/export.js +0 -3
  25. package/dist/matter/types.js +0 -3
  26. package/dist/matterbridge.js +50 -797
  27. package/dist/matterbridgeAccessoryPlatform.js +0 -36
  28. package/dist/matterbridgeBehaviors.js +16 -55
  29. package/dist/matterbridgeDeviceTypes.js +15 -579
  30. package/dist/matterbridgeDynamicPlatform.js +0 -36
  31. package/dist/matterbridgeEndpoint.js +66 -1025
  32. package/dist/matterbridgeEndpointHelpers.js +12 -322
  33. package/dist/matterbridgePlatform.js +0 -233
  34. package/dist/matterbridgeTypes.js +0 -25
  35. package/dist/pluginManager.js +3 -269
  36. package/dist/roboticVacuumCleaner.js +6 -83
  37. package/dist/shelly.js +7 -168
  38. package/dist/solarPower.js +20 -0
  39. package/dist/storage/export.js +0 -1
  40. package/dist/update.js +0 -54
  41. package/dist/utils/colorUtils.js +2 -263
  42. package/dist/utils/commandLine.js +0 -54
  43. package/dist/utils/copyDirectory.js +1 -38
  44. package/dist/utils/createDirectory.js +0 -33
  45. package/dist/utils/createZip.js +2 -47
  46. package/dist/utils/deepCopy.js +0 -39
  47. package/dist/utils/deepEqual.js +1 -72
  48. package/dist/utils/export.js +0 -1
  49. package/dist/utils/hex.js +0 -58
  50. package/dist/utils/isvalid.js +0 -101
  51. package/dist/utils/network.js +5 -83
  52. package/dist/utils/spawn.js +0 -18
  53. package/dist/utils/wait.js +9 -62
  54. package/dist/waterHeater.js +2 -77
  55. package/npm-shrinkwrap.json +2 -2
  56. package/package.json +1 -2
  57. package/dist/cli.d.ts +0 -29
  58. package/dist/cli.d.ts.map +0 -1
  59. package/dist/cli.js.map +0 -1
  60. package/dist/clusters/export.d.ts +0 -2
  61. package/dist/clusters/export.d.ts.map +0 -1
  62. package/dist/clusters/export.js.map +0 -1
  63. package/dist/defaultConfigSchema.d.ts +0 -28
  64. package/dist/defaultConfigSchema.d.ts.map +0 -1
  65. package/dist/defaultConfigSchema.js.map +0 -1
  66. package/dist/deviceManager.d.ts +0 -112
  67. package/dist/deviceManager.d.ts.map +0 -1
  68. package/dist/deviceManager.js.map +0 -1
  69. package/dist/devices/export.d.ts +0 -5
  70. package/dist/devices/export.d.ts.map +0 -1
  71. package/dist/devices/export.js.map +0 -1
  72. package/dist/evse.d.ts +0 -72
  73. package/dist/evse.d.ts.map +0 -1
  74. package/dist/evse.js.map +0 -1
  75. package/dist/frontend.d.ts +0 -285
  76. package/dist/frontend.d.ts.map +0 -1
  77. package/dist/frontend.js.map +0 -1
  78. package/dist/globalMatterbridge.d.ts +0 -59
  79. package/dist/globalMatterbridge.d.ts.map +0 -1
  80. package/dist/globalMatterbridge.js.map +0 -1
  81. package/dist/helpers.d.ts +0 -48
  82. package/dist/helpers.d.ts.map +0 -1
  83. package/dist/helpers.js.map +0 -1
  84. package/dist/index.d.ts +0 -38
  85. package/dist/index.d.ts.map +0 -1
  86. package/dist/index.js.map +0 -1
  87. package/dist/laundryWasher.d.ts +0 -243
  88. package/dist/laundryWasher.d.ts.map +0 -1
  89. package/dist/laundryWasher.js.map +0 -1
  90. package/dist/logger/export.d.ts +0 -2
  91. package/dist/logger/export.d.ts.map +0 -1
  92. package/dist/logger/export.js.map +0 -1
  93. package/dist/matter/behaviors.d.ts +0 -2
  94. package/dist/matter/behaviors.d.ts.map +0 -1
  95. package/dist/matter/behaviors.js.map +0 -1
  96. package/dist/matter/clusters.d.ts +0 -2
  97. package/dist/matter/clusters.d.ts.map +0 -1
  98. package/dist/matter/clusters.js.map +0 -1
  99. package/dist/matter/devices.d.ts +0 -2
  100. package/dist/matter/devices.d.ts.map +0 -1
  101. package/dist/matter/devices.js.map +0 -1
  102. package/dist/matter/endpoints.d.ts +0 -2
  103. package/dist/matter/endpoints.d.ts.map +0 -1
  104. package/dist/matter/endpoints.js.map +0 -1
  105. package/dist/matter/export.d.ts +0 -5
  106. package/dist/matter/export.d.ts.map +0 -1
  107. package/dist/matter/export.js.map +0 -1
  108. package/dist/matter/types.d.ts +0 -3
  109. package/dist/matter/types.d.ts.map +0 -1
  110. package/dist/matter/types.js.map +0 -1
  111. package/dist/matterbridge.d.ts +0 -450
  112. package/dist/matterbridge.d.ts.map +0 -1
  113. package/dist/matterbridge.js.map +0 -1
  114. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
  115. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  116. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  117. package/dist/matterbridgeBehaviors.d.ts +0 -1334
  118. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  119. package/dist/matterbridgeBehaviors.js.map +0 -1
  120. package/dist/matterbridgeDeviceTypes.d.ts +0 -709
  121. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  122. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  123. package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
  124. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  125. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  126. package/dist/matterbridgeEndpoint.d.ts +0 -1173
  127. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  128. package/dist/matterbridgeEndpoint.js.map +0 -1
  129. package/dist/matterbridgeEndpointHelpers.d.ts +0 -3198
  130. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  131. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  132. package/dist/matterbridgePlatform.d.ts +0 -310
  133. package/dist/matterbridgePlatform.d.ts.map +0 -1
  134. package/dist/matterbridgePlatform.js.map +0 -1
  135. package/dist/matterbridgeTypes.d.ts +0 -184
  136. package/dist/matterbridgeTypes.d.ts.map +0 -1
  137. package/dist/matterbridgeTypes.js.map +0 -1
  138. package/dist/pluginManager.d.ts +0 -291
  139. package/dist/pluginManager.d.ts.map +0 -1
  140. package/dist/pluginManager.js.map +0 -1
  141. package/dist/roboticVacuumCleaner.d.ts +0 -104
  142. package/dist/roboticVacuumCleaner.d.ts.map +0 -1
  143. package/dist/roboticVacuumCleaner.js.map +0 -1
  144. package/dist/shelly.d.ts +0 -174
  145. package/dist/shelly.d.ts.map +0 -1
  146. package/dist/shelly.js.map +0 -1
  147. package/dist/storage/export.d.ts +0 -2
  148. package/dist/storage/export.d.ts.map +0 -1
  149. package/dist/storage/export.js.map +0 -1
  150. package/dist/update.d.ts +0 -59
  151. package/dist/update.d.ts.map +0 -1
  152. package/dist/update.js.map +0 -1
  153. package/dist/utils/colorUtils.d.ts +0 -117
  154. package/dist/utils/colorUtils.d.ts.map +0 -1
  155. package/dist/utils/colorUtils.js.map +0 -1
  156. package/dist/utils/commandLine.d.ts +0 -59
  157. package/dist/utils/commandLine.d.ts.map +0 -1
  158. package/dist/utils/commandLine.js.map +0 -1
  159. package/dist/utils/copyDirectory.d.ts +0 -33
  160. package/dist/utils/copyDirectory.d.ts.map +0 -1
  161. package/dist/utils/copyDirectory.js.map +0 -1
  162. package/dist/utils/createDirectory.d.ts +0 -34
  163. package/dist/utils/createDirectory.d.ts.map +0 -1
  164. package/dist/utils/createDirectory.js.map +0 -1
  165. package/dist/utils/createZip.d.ts +0 -39
  166. package/dist/utils/createZip.d.ts.map +0 -1
  167. package/dist/utils/createZip.js.map +0 -1
  168. package/dist/utils/deepCopy.d.ts +0 -32
  169. package/dist/utils/deepCopy.d.ts.map +0 -1
  170. package/dist/utils/deepCopy.js.map +0 -1
  171. package/dist/utils/deepEqual.d.ts +0 -54
  172. package/dist/utils/deepEqual.d.ts.map +0 -1
  173. package/dist/utils/deepEqual.js.map +0 -1
  174. package/dist/utils/export.d.ts +0 -12
  175. package/dist/utils/export.d.ts.map +0 -1
  176. package/dist/utils/export.js.map +0 -1
  177. package/dist/utils/hex.d.ts +0 -49
  178. package/dist/utils/hex.d.ts.map +0 -1
  179. package/dist/utils/hex.js.map +0 -1
  180. package/dist/utils/isvalid.d.ts +0 -103
  181. package/dist/utils/isvalid.d.ts.map +0 -1
  182. package/dist/utils/isvalid.js.map +0 -1
  183. package/dist/utils/network.d.ts +0 -76
  184. package/dist/utils/network.d.ts.map +0 -1
  185. package/dist/utils/network.js.map +0 -1
  186. package/dist/utils/spawn.d.ts +0 -14
  187. package/dist/utils/spawn.d.ts.map +0 -1
  188. package/dist/utils/spawn.js.map +0 -1
  189. package/dist/utils/wait.d.ts +0 -56
  190. package/dist/utils/wait.d.ts.map +0 -1
  191. package/dist/utils/wait.js.map +0 -1
  192. package/dist/waterHeater.d.ts +0 -106
  193. package/dist/waterHeater.d.ts.map +0 -1
  194. package/dist/waterHeater.js.map +0 -1
package/dist/frontend.js CHANGED
@@ -1,125 +1,29 @@
1
- /**
2
- * This file contains the class Frontend.
3
- *
4
- * @file frontend.ts
5
- * @author Luca Liguori
6
- * @created 2025-01-13
7
- * @version 1.0.2
8
- * @license Apache-2.0
9
- *
10
- * Copyright 2025, 2026, 2027 Luca Liguori.
11
- *
12
- * Licensed under the Apache License, Version 2.0 (the "License");
13
- * you may not use this file except in compliance with the License.
14
- * You may obtain a copy of the License at
15
- *
16
- * http://www.apache.org/licenses/LICENSE-2.0
17
- *
18
- * Unless required by applicable law or agreed to in writing, software
19
- * distributed under the License is distributed on an "AS IS" BASIS,
20
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
- * See the License for the specific language governing permissions and
22
- * limitations under the License.
23
- */
24
- // Node modules
25
1
  import { createServer } from 'node:http';
26
2
  import https from 'node:https';
27
3
  import os from 'node:os';
28
4
  import path from 'node:path';
29
5
  import { promises as fs } from 'node:fs';
30
- // Third-party modules
31
6
  import express from 'express';
32
7
  import WebSocket, { WebSocketServer } from 'ws';
33
8
  import multer from 'multer';
34
- // AnsiLogger module
35
9
  import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from 'node-ansi-logger';
36
- // @matter
37
10
  import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
38
11
  import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
39
- // Matterbridge
40
12
  import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout, hasParameter } from './utils/export.js';
41
13
  import { plg } from './matterbridgeTypes.js';
42
14
  import { capitalizeFirstLetter } from './matterbridgeEndpointHelpers.js';
43
15
  import spawn from './utils/spawn.js';
44
- /**
45
- * Websocket message ID for logging.
46
- *
47
- * @constant {number}
48
- */
49
16
  export const WS_ID_LOG = 0;
50
- /**
51
- * Websocket message ID indicating a refresh is needed.
52
- *
53
- * @constant {number}
54
- */
55
17
  export const WS_ID_REFRESH_NEEDED = 1;
56
- /**
57
- * Websocket message ID indicating a restart is needed.
58
- *
59
- * @constant {number}
60
- */
61
18
  export const WS_ID_RESTART_NEEDED = 2;
62
- /**
63
- * Websocket message ID indicating a cpu update.
64
- *
65
- * @constant {number}
66
- */
67
19
  export const WS_ID_CPU_UPDATE = 3;
68
- /**
69
- * Websocket message ID indicating a memory update.
70
- *
71
- * @constant {number}
72
- */
73
20
  export const WS_ID_MEMORY_UPDATE = 4;
74
- /**
75
- * Websocket message ID indicating an uptime update.
76
- *
77
- * @constant {number}
78
- */
79
21
  export const WS_ID_UPTIME_UPDATE = 5;
80
- /**
81
- * Websocket message ID indicating a snackbar message.
82
- *
83
- * @constant {number}
84
- */
85
22
  export const WS_ID_SNACKBAR = 6;
86
- /**
87
- * Websocket message ID indicating matterbridge has un update available.
88
- *
89
- * @constant {number}
90
- */
91
23
  export const WS_ID_UPDATE_NEEDED = 7;
92
- /**
93
- * Websocket message ID indicating a state update.
94
- *
95
- * @constant {number}
96
- */
97
24
  export const WS_ID_STATEUPDATE = 8;
98
- /**
99
- * Websocket message ID indicating to close a permanent snackbar message.
100
- *
101
- * @constant {number}
102
- */
103
25
  export const WS_ID_CLOSE_SNACKBAR = 9;
104
- /**
105
- * Websocket message ID indicating a shelly system update.
106
- * check:
107
- * curl -k http://127.0.0.1:8101/api/updates/sys/check
108
- * perform:
109
- * curl -k http://127.0.0.1:8101/api/updates/sys/perform
110
- *
111
- * @constant {number}
112
- */
113
26
  export const WS_ID_SHELLY_SYS_UPDATE = 100;
114
- /**
115
- * Websocket message ID indicating a shelly main update.
116
- * check:
117
- * curl -k http://127.0.0.1:8101/api/updates/main/check
118
- * perform:
119
- * curl -k http://127.0.0.1:8101/api/updates/main/perform
120
- *
121
- * @constant {number}
122
- */
123
27
  export const WS_ID_SHELLY_MAIN_UPDATE = 101;
124
28
  export class Frontend {
125
29
  matterbridge;
@@ -132,7 +36,7 @@ export class Frontend {
132
36
  webSocketServer;
133
37
  constructor(matterbridge) {
134
38
  this.matterbridge = matterbridge;
135
- 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" });
136
40
  }
137
41
  set logLevel(logLevel) {
138
42
  this.log.logLevel = logLevel;
@@ -140,43 +44,13 @@ export class Frontend {
140
44
  async start(port = 8283) {
141
45
  this.port = port;
142
46
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
143
- // Initialize multer with the upload directory
144
47
  const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
145
48
  await fs.mkdir(uploadDir, { recursive: true });
146
49
  const upload = multer({ dest: uploadDir });
147
- // Create the express app that serves the frontend
148
50
  this.expressApp = express();
149
- // Inject logging/debug wrapper for route/middleware registration
150
- /*
151
- const methods = ['get', 'post', 'put', 'delete', 'use'];
152
- for (const method of methods) {
153
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
- const original = (this.expressApp as any)[method].bind(this.expressApp);
155
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
- (this.expressApp as any)[method] = (path: any, ...rest: any) => {
157
- try {
158
- console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
159
- return original(path, ...rest);
160
- } catch (err) {
161
- console.error(`[ERROR] Failed to register route: ${path}`);
162
- throw err;
163
- }
164
- };
165
- }
166
- */
167
- // Log all requests to the server for debugging
168
- /*
169
- this.expressApp.use((req, res, next) => {
170
- this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
171
- next();
172
- });
173
- */
174
- // Serve static files from '/static' endpoint
175
51
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
176
52
  if (!hasParameter('ssl')) {
177
- // Create an HTTP server and attach the express app
178
53
  this.httpServer = createServer(this.expressApp);
179
- // Listen on the specified port
180
54
  if (hasParameter('ingress')) {
181
55
  this.httpServer.listen(this.port, '0.0.0.0', () => {
182
56
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -190,7 +64,6 @@ export class Frontend {
190
64
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
191
65
  });
192
66
  }
193
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
194
67
  this.httpServer.on('error', (error) => {
195
68
  this.log.error(`Frontend http server error listening on ${this.port}`);
196
69
  switch (error.code) {
@@ -206,7 +79,6 @@ export class Frontend {
206
79
  });
207
80
  }
208
81
  else {
209
- // Load the SSL certificate, the private key and optionally the CA certificate
210
82
  let cert;
211
83
  try {
212
84
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
@@ -234,9 +106,7 @@ export class Frontend {
234
106
  this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
235
107
  }
236
108
  const serverOptions = { cert, key, ca };
237
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
238
109
  this.httpsServer = https.createServer(serverOptions, this.expressApp);
239
- // Listen on the specified port
240
110
  if (hasParameter('ingress')) {
241
111
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
242
112
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -250,7 +120,6 @@ export class Frontend {
250
120
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
251
121
  });
252
122
  }
253
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
254
123
  this.httpsServer.on('error', (error) => {
255
124
  this.log.error(`Frontend https server error listening on ${this.port}`);
256
125
  switch (error.code) {
@@ -267,18 +136,16 @@ export class Frontend {
267
136
  }
268
137
  if (this.initializeError)
269
138
  return;
270
- // Create a WebSocket server and attach it to the http or https server
271
139
  const wssPort = this.port;
272
140
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
273
141
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
274
142
  this.webSocketServer.on('connection', (ws, request) => {
275
143
  const clientIp = request.socket.remoteAddress;
276
- // Set the global logger callback for the WebSocketServer
277
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
278
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
279
- callbackLogLevel = "info" /* LogLevel.INFO */;
280
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
281
- 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";
282
149
  AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
283
150
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
284
151
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -312,7 +179,6 @@ export class Frontend {
312
179
  this.webSocketServer.on('error', (ws, error) => {
313
180
  this.log.error(`WebSocketServer error: ${error}`);
314
181
  });
315
- // Subscribe to cli events
316
182
  const { cliEmitter } = await import('./cli.js');
317
183
  cliEmitter.removeAllListeners();
318
184
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
@@ -324,8 +190,6 @@ export class Frontend {
324
190
  cliEmitter.on('cpu', (cpuUsage) => {
325
191
  this.wssSendCpuUpdate(cpuUsage);
326
192
  });
327
- // Endpoint to validate login code
328
- // curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
329
193
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
330
194
  const { password } = req.body;
331
195
  this.log.debug('The frontend sent /api/login', password);
@@ -344,27 +208,23 @@ export class Frontend {
344
208
  this.log.warn('/api/login error wrong password');
345
209
  res.json({ valid: false });
346
210
  }
347
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
348
211
  }
349
212
  catch (error) {
350
213
  this.log.error('/api/login error getting password');
351
214
  res.json({ valid: false });
352
215
  }
353
216
  });
354
- // Endpoint to provide health check for docker
355
217
  this.expressApp.get('/health', (req, res) => {
356
218
  this.log.debug('Express received /health');
357
219
  const healthStatus = {
358
- status: 'ok', // Indicate service is healthy
359
- uptime: process.uptime(), // Server uptime in seconds
360
- timestamp: new Date().toISOString(), // Current timestamp
220
+ status: 'ok',
221
+ uptime: process.uptime(),
222
+ timestamp: new Date().toISOString(),
361
223
  };
362
224
  res.status(200).json(healthStatus);
363
225
  });
364
- // Endpoint to provide memory usage details
365
226
  this.expressApp.get('/memory', async (req, res) => {
366
227
  this.log.debug('Express received /memory');
367
- // Memory usage from process
368
228
  const memoryUsageRaw = process.memoryUsage();
369
229
  const memoryUsage = {
370
230
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -373,13 +233,10 @@ export class Frontend {
373
233
  external: this.formatMemoryUsage(memoryUsageRaw.external),
374
234
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
375
235
  };
376
- // V8 heap statistics
377
236
  const { default: v8 } = await import('node:v8');
378
237
  const heapStatsRaw = v8.getHeapStatistics();
379
238
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
380
- // Format heapStats
381
239
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
382
- // Format heapSpaces
383
240
  const heapSpaces = heapSpacesRaw.map((space) => ({
384
241
  ...space,
385
242
  space_size: this.formatMemoryUsage(space.space_size),
@@ -397,23 +254,19 @@ export class Frontend {
397
254
  };
398
255
  res.status(200).json(memoryReport);
399
256
  });
400
- // Endpoint to provide settings
401
257
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
402
258
  this.log.debug('The frontend sent /api/settings');
403
259
  res.json(await this.getApiSettings());
404
260
  });
405
- // Endpoint to provide plugins
406
261
  this.expressApp.get('/api/plugins', async (req, res) => {
407
262
  this.log.debug('The frontend sent /api/plugins');
408
263
  res.json(this.getBaseRegisteredPlugins());
409
264
  });
410
- // Endpoint to provide devices
411
265
  this.expressApp.get('/api/devices', async (req, res) => {
412
266
  this.log.debug('The frontend sent /api/devices');
413
267
  const devices = await this.getDevices();
414
268
  res.json(devices);
415
269
  });
416
- // Endpoint to view the matterbridge log
417
270
  this.expressApp.get('/api/view-mblog', async (req, res) => {
418
271
  this.log.debug('The frontend sent /api/view-mblog');
419
272
  try {
@@ -426,7 +279,6 @@ export class Frontend {
426
279
  res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
427
280
  }
428
281
  });
429
- // Endpoint to view the matter.js log
430
282
  this.expressApp.get('/api/view-mjlog', async (req, res) => {
431
283
  this.log.debug('The frontend sent /api/view-mjlog');
432
284
  try {
@@ -439,7 +291,6 @@ export class Frontend {
439
291
  res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
440
292
  }
441
293
  });
442
- // Endpoint to view the shelly log
443
294
  this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
444
295
  this.log.debug('The frontend sent /api/shellyviewsystemlog');
445
296
  try {
@@ -452,11 +303,9 @@ export class Frontend {
452
303
  res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
453
304
  }
454
305
  });
455
- // Endpoint to download the matterbridge log
456
306
  this.expressApp.get('/api/download-mblog', async (req, res) => {
457
307
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
458
308
  try {
459
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
460
309
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), fs.constants.F_OK);
461
310
  const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'utf8');
462
311
  await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), data, 'utf-8');
@@ -466,7 +315,6 @@ export class Frontend {
466
315
  this.log.debug(`Error in /api/download-mblog: ${error instanceof Error ? error.message : error}`);
467
316
  }
468
317
  res.type('text/plain');
469
- // res.download(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
470
318
  res.download(path.join(os.tmpdir(), this.matterbridge.matterbrideLoggerFile), 'matterbridge.log', (error) => {
471
319
  if (error) {
472
320
  this.log.error(`Error downloading log file ${this.matterbridge.matterbrideLoggerFile}: ${error instanceof Error ? error.message : error}`);
@@ -474,11 +322,9 @@ export class Frontend {
474
322
  }
475
323
  });
476
324
  });
477
- // Endpoint to download the matter log
478
325
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
479
326
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile)}`);
480
327
  try {
481
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
482
328
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), fs.constants.F_OK);
483
329
  const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile), 'utf8');
484
330
  await fs.writeFile(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), data, 'utf-8');
@@ -495,11 +341,9 @@ export class Frontend {
495
341
  }
496
342
  });
497
343
  });
498
- // Endpoint to download the shelly log
499
344
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
500
345
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
501
346
  try {
502
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
503
347
  await fs.access(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), fs.constants.F_OK);
504
348
  const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'shelly.log'), 'utf8');
505
349
  await fs.writeFile(path.join(os.tmpdir(), 'shelly.log'), data, 'utf-8');
@@ -516,7 +360,6 @@ export class Frontend {
516
360
  }
517
361
  });
518
362
  });
519
- // Endpoint to download the matterbridge storage directory
520
363
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
521
364
  this.log.debug('The frontend sent /api/download-mbstorage');
522
365
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
@@ -527,7 +370,6 @@ export class Frontend {
527
370
  }
528
371
  });
529
372
  });
530
- // Endpoint to download the matter storage file
531
373
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
532
374
  this.log.debug('The frontend sent /api/download-mjstorage');
533
375
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
@@ -538,7 +380,6 @@ export class Frontend {
538
380
  }
539
381
  });
540
382
  });
541
- // Endpoint to download the matterbridge plugin directory
542
383
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
543
384
  this.log.debug('The frontend sent /api/download-pluginstorage');
544
385
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
@@ -549,7 +390,6 @@ export class Frontend {
549
390
  }
550
391
  });
551
392
  });
552
- // Endpoint to download the matterbridge plugin config files
553
393
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
554
394
  this.log.debug('The frontend sent /api/download-pluginconfig');
555
395
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
@@ -560,7 +400,6 @@ export class Frontend {
560
400
  }
561
401
  });
562
402
  });
563
- // Endpoint to download the matterbridge backup (created with the backup command)
564
403
  this.expressApp.get('/api/download-backup', async (req, res) => {
565
404
  this.log.debug('The frontend sent /api/download-backup');
566
405
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
@@ -570,7 +409,6 @@ export class Frontend {
570
409
  }
571
410
  });
572
411
  });
573
- // Endpoint to upload a package
574
412
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
575
413
  const { filename } = req.body;
576
414
  const file = req.file;
@@ -580,13 +418,10 @@ export class Frontend {
580
418
  return;
581
419
  }
582
420
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
583
- // Define the path where the plugin file will be saved
584
421
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
585
422
  try {
586
- // Move the uploaded file to the specified path
587
423
  await fs.rename(file.path, filePath);
588
424
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
589
- // Install the plugin package
590
425
  if (filename.endsWith('.tgz')) {
591
426
  await spawn.spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
592
427
  this.log.info(`Plugin package ${plg}${filename}${nf} installed successfully. Full restart required.`);
@@ -605,7 +440,6 @@ export class Frontend {
605
440
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
606
441
  }
607
442
  });
608
- // Fallback for routing (must be the last route)
609
443
  this.expressApp.use((req, res) => {
610
444
  this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
611
445
  res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -614,15 +448,12 @@ export class Frontend {
614
448
  }
615
449
  async stop() {
616
450
  this.log.debug('Stopping the frontend...');
617
- // Remove listeners from the express app
618
451
  if (this.expressApp) {
619
452
  this.expressApp.removeAllListeners();
620
453
  this.expressApp = undefined;
621
454
  this.log.debug('Frontend app closed successfully');
622
455
  }
623
- // Close the WebSocket server
624
456
  if (this.webSocketServer) {
625
- // Close all active connections
626
457
  this.webSocketServer.clients.forEach((client) => {
627
458
  if (client.readyState === WebSocket.OPEN) {
628
459
  client.close();
@@ -642,7 +473,6 @@ export class Frontend {
642
473
  this.webSocketServer.removeAllListeners();
643
474
  this.webSocketServer = undefined;
644
475
  }
645
- // Close the http server
646
476
  if (this.httpServer) {
647
477
  await withTimeout(new Promise((resolve) => {
648
478
  this.httpServer?.close((error) => {
@@ -659,7 +489,6 @@ export class Frontend {
659
489
  this.httpServer = undefined;
660
490
  this.log.debug('Frontend http server closed successfully');
661
491
  }
662
- // Close the https server
663
492
  if (this.httpsServer) {
664
493
  await withTimeout(new Promise((resolve) => {
665
494
  this.httpsServer?.close((error) => {
@@ -678,7 +507,6 @@ export class Frontend {
678
507
  }
679
508
  this.log.debug('Frontend stopped successfully');
680
509
  }
681
- // Function to format bytes to KB, MB, or GB
682
510
  formatMemoryUsage = (bytes) => {
683
511
  if (bytes >= 1024 ** 3) {
684
512
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -690,7 +518,6 @@ export class Frontend {
690
518
  return `${(bytes / 1024).toFixed(2)} KB`;
691
519
  }
692
520
  };
693
- // Function to format system uptime with only the most significant unit
694
521
  formatOsUpTime = (seconds) => {
695
522
  if (seconds >= 86400) {
696
523
  const days = Math.floor(seconds / 86400);
@@ -706,14 +533,8 @@ export class Frontend {
706
533
  }
707
534
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
708
535
  };
709
- /**
710
- * Retrieves the api settings data.
711
- *
712
- * @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
713
- */
714
536
  async getApiSettings() {
715
537
  const { lastCpuUsage } = await import('./cli.js');
716
- // Update the system information
717
538
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
718
539
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
719
540
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -722,7 +543,6 @@ export class Frontend {
722
543
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
723
544
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
724
545
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
725
- // Update the matterbridge information
726
546
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
727
547
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
728
548
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
@@ -741,12 +561,6 @@ export class Frontend {
741
561
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
742
562
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
743
563
  }
744
- /**
745
- * Retrieves the reachable attribute.
746
- *
747
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
748
- * @returns {boolean} The reachable attribute.
749
- */
750
564
  getReachability(device) {
751
565
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
752
566
  return false;
@@ -756,12 +570,6 @@ export class Frontend {
756
570
  return true;
757
571
  return false;
758
572
  }
759
- /**
760
- * Retrieves the power source attribute.
761
- *
762
- * @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice object.
763
- * @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
764
- */
765
573
  getPowerSource(endpoint) {
766
574
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
767
575
  return undefined;
@@ -777,21 +585,13 @@ export class Frontend {
777
585
  }
778
586
  return;
779
587
  };
780
- // Root endpoint
781
588
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
782
589
  return powerSource(endpoint);
783
- // Child endpoints
784
590
  for (const child of endpoint.getChildEndpoints()) {
785
591
  if (child.hasClusterServer(PowerSource.Cluster.id))
786
592
  return powerSource(child);
787
593
  }
788
594
  }
789
- /**
790
- * Retrieves the cluster text description from a given device.
791
- *
792
- * @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
793
- * @returns {string} The attributes description of the cluster servers in the device.
794
- */
795
595
  getClusterTextFromDevice(device) {
796
596
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
797
597
  return '';
@@ -832,19 +632,7 @@ export class Frontend {
832
632
  };
833
633
  let attributes = '';
834
634
  let supportedModes = [];
835
- /*
836
- Object.keys(device.behaviors.supported).forEach((clusterName) => {
837
- const clusterBehavior = device.behaviors.supported[lowercaseFirstLetter(clusterName)] as ClusterBehavior.Type | undefined;
838
- // console.log(`Device: ${device.deviceName} => Cluster: ${clusterName} Behavior: ${clusterBehavior?.id}`, clusterBehavior);
839
- if (clusterBehavior && clusterBehavior.cluster && clusterBehavior.cluster.attributes) {
840
- Object.entries(clusterBehavior.cluster.attributes).forEach(([attributeName, attribute]) => {
841
- // console.log(`${device.deviceName} => Cluster: ${clusterName} Attribute: ${attributeName}`, attribute);
842
- });
843
- }
844
- });
845
- */
846
635
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
847
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
848
636
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
849
637
  return;
850
638
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -936,14 +724,8 @@ export class Frontend {
936
724
  if (clusterName === 'userLabel' && attributeName === 'labelList')
937
725
  attributes += `${getUserLabel(device)} `;
938
726
  });
939
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
940
727
  return attributes.trimStart().trimEnd();
941
728
  }
942
- /**
943
- * Retrieves the base registered plugins sanitized for res.json().
944
- *
945
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
946
- */
947
729
  getBaseRegisteredPlugins() {
948
730
  const baseRegisteredPlugins = [];
949
731
  for (const plugin of this.matterbridge.plugins) {
@@ -982,19 +764,11 @@ export class Frontend {
982
764
  }
983
765
  return baseRegisteredPlugins;
984
766
  }
985
- /**
986
- * Retrieves the devices from Matterbridge.
987
- *
988
- * @param {string} [pluginName] - The name of the plugin to filter devices by.
989
- * @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices.
990
- */
991
767
  async getDevices(pluginName) {
992
768
  const devices = [];
993
769
  this.matterbridge.devices.forEach(async (device) => {
994
- // Filter by pluginName if provided
995
770
  if (pluginName && pluginName !== device.plugin)
996
771
  return;
997
- // Check if the device has the required properties
998
772
  if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
999
773
  return;
1000
774
  const cluster = this.getClusterTextFromDevice(device);
@@ -1014,37 +788,22 @@ export class Frontend {
1014
788
  });
1015
789
  return devices;
1016
790
  }
1017
- /**
1018
- * Retrieves the clusters from a given plugin and endpoint number.
1019
- *
1020
- * Response for /api/clusters
1021
- *
1022
- * @param {string} pluginName - The name of the plugin.
1023
- * @param {number} endpointNumber - The endpoint number.
1024
- * @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
1025
- */
1026
791
  getClusters(pluginName, endpointNumber) {
1027
792
  const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
1028
793
  if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
1029
794
  this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1030
795
  return;
1031
796
  }
1032
- // this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
1033
- // Get the device types from the main endpoint
1034
797
  const deviceTypes = [];
1035
798
  const clusters = [];
1036
799
  endpoint.state.descriptor.deviceTypeList.forEach((d) => {
1037
800
  deviceTypes.push(d.deviceType);
1038
801
  });
1039
- // Get the clusters from the main endpoint
1040
802
  endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1041
803
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1042
804
  return;
1043
805
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1044
806
  return;
1045
- // console.log(
1046
- // `${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}`,
1047
- // );
1048
807
  clusters.push({
1049
808
  endpoint: endpoint.number.toString(),
1050
809
  id: 'main',
@@ -1057,18 +816,12 @@ export class Frontend {
1057
816
  attributeLocalValue: attributeValue,
1058
817
  });
1059
818
  });
1060
- // Get the child endpoints
1061
819
  const childEndpoints = endpoint.getChildEndpoints();
1062
- // if (childEndpoints.length === 0) {
1063
- // this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
1064
- // }
1065
820
  childEndpoints.forEach((childEndpoint) => {
1066
821
  if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
1067
822
  this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1068
823
  return;
1069
824
  }
1070
- // this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
1071
- // Get the device types of the child endpoint
1072
825
  const deviceTypes = [];
1073
826
  childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
1074
827
  deviceTypes.push(d.deviceType);
@@ -1078,12 +831,9 @@ export class Frontend {
1078
831
  return;
1079
832
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1080
833
  return;
1081
- // console.log(
1082
- // `${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}`,
1083
- // );
1084
834
  clusters.push({
1085
835
  endpoint: childEndpoint.number.toString(),
1086
- id: childEndpoint.maybeId ?? 'null', // Never happens
836
+ id: childEndpoint.maybeId ?? 'null',
1087
837
  deviceTypes,
1088
838
  clusterName: capitalizeFirstLetter(clusterName),
1089
839
  clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
@@ -1096,13 +846,6 @@ export class Frontend {
1096
846
  });
1097
847
  return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
1098
848
  }
1099
- /**
1100
- * Handles incoming websocket messages for the Matterbridge frontend.
1101
- *
1102
- * @param {WebSocket} client - The websocket client that sent the message.
1103
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1104
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1105
- */
1106
849
  async wsMessageHandler(client, message) {
1107
850
  let data;
1108
851
  try {
@@ -1149,41 +892,32 @@ export class Frontend {
1149
892
  this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
1150
893
  const packageName = data.params.packageName.replace(/@.*$/, '');
1151
894
  if (data.params.restart === false && packageName !== 'matterbridge') {
1152
- // The install comes from InstallPlugins
1153
895
  this.matterbridge.plugins
1154
896
  .add(packageName)
1155
897
  .then((plugin) => {
1156
898
  if (plugin) {
1157
- // The plugin is not registered
1158
899
  this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
1159
900
  this.matterbridge.plugins
1160
901
  .load(plugin, true, 'The plugin has been added', true)
1161
- // eslint-disable-next-line promise/no-nesting
1162
902
  .then(() => {
1163
903
  this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
1164
904
  this.wssSendRefreshRequired('plugins');
1165
905
  return;
1166
906
  })
1167
- // eslint-disable-next-line promise/no-nesting
1168
907
  .catch((_error) => {
1169
- //
1170
908
  });
1171
909
  }
1172
910
  else {
1173
- // The plugin is already registered
1174
911
  this.wssSendSnackbarMessage(`Restart required`, 0);
1175
912
  this.wssSendRefreshRequired('plugins');
1176
913
  this.wssSendRestartRequired();
1177
914
  }
1178
915
  return;
1179
916
  })
1180
- // eslint-disable-next-line promise/no-nesting
1181
917
  .catch((_error) => {
1182
- //
1183
918
  });
1184
919
  }
1185
920
  else {
1186
- // The package is matterbridge
1187
921
  if (this.matterbridge.restartMode !== '') {
1188
922
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1189
923
  this.matterbridge.shutdownProcess();
@@ -1206,7 +940,6 @@ export class Frontend {
1206
940
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
1207
941
  return;
1208
942
  }
1209
- // The package is a plugin
1210
943
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
1211
944
  if (plugin) {
1212
945
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
@@ -1215,7 +948,6 @@ export class Frontend {
1215
948
  this.wssSendRefreshRequired('plugins');
1216
949
  this.wssSendRefreshRequired('devices');
1217
950
  }
1218
- // Uninstall the package
1219
951
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1220
952
  spawn
1221
953
  .spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
@@ -1256,7 +988,6 @@ export class Frontend {
1256
988
  return;
1257
989
  })
1258
990
  .catch((_error) => {
1259
- //
1260
991
  });
1261
992
  }
1262
993
  else {
@@ -1303,7 +1034,6 @@ export class Frontend {
1303
1034
  return;
1304
1035
  })
1305
1036
  .catch((_error) => {
1306
- //
1307
1037
  });
1308
1038
  }
1309
1039
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1541,22 +1271,22 @@ export class Frontend {
1541
1271
  if (isValidString(data.params.value, 4)) {
1542
1272
  this.log.debug('Matterbridge logger level:', data.params.value);
1543
1273
  if (data.params.value === 'Debug') {
1544
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1274
+ await this.matterbridge.setLogLevel("debug");
1545
1275
  }
1546
1276
  else if (data.params.value === 'Info') {
1547
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1277
+ await this.matterbridge.setLogLevel("info");
1548
1278
  }
1549
1279
  else if (data.params.value === 'Notice') {
1550
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1280
+ await this.matterbridge.setLogLevel("notice");
1551
1281
  }
1552
1282
  else if (data.params.value === 'Warn') {
1553
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1283
+ await this.matterbridge.setLogLevel("warn");
1554
1284
  }
1555
1285
  else if (data.params.value === 'Error') {
1556
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1286
+ await this.matterbridge.setLogLevel("error");
1557
1287
  }
1558
1288
  else if (data.params.value === 'Fatal') {
1559
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1289
+ await this.matterbridge.setLogLevel("fatal");
1560
1290
  }
1561
1291
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1562
1292
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1567,7 +1297,6 @@ export class Frontend {
1567
1297
  this.log.debug('Matterbridge file log:', data.params.value);
1568
1298
  this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
1569
1299
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1570
- // Create the file logger for matterbridge
1571
1300
  if (data.params.value)
1572
1301
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbrideLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1573
1302
  else
@@ -1732,19 +1461,15 @@ export class Frontend {
1732
1461
  return;
1733
1462
  }
1734
1463
  const config = plugin.configJson;
1735
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1736
1464
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1737
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1738
1465
  if (select === 'serial')
1739
1466
  this.log.info(`Selected device serial ${data.params.serial}`);
1740
1467
  if (select === 'name')
1741
1468
  this.log.info(`Selected device name ${data.params.name}`);
1742
1469
  if (config && select && (select === 'serial' || select === 'name')) {
1743
- // Remove postfix from the serial if it exists
1744
1470
  if (config.postfix) {
1745
1471
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1746
1472
  }
1747
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1748
1473
  if (isValidArray(config.whiteList, 1)) {
1749
1474
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1750
1475
  config.whiteList.push(data.params.serial);
@@ -1753,7 +1478,6 @@ export class Frontend {
1753
1478
  config.whiteList.push(data.params.name);
1754
1479
  }
1755
1480
  }
1756
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1757
1481
  if (isValidArray(config.blackList, 1)) {
1758
1482
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1759
1483
  config.blackList = config.blackList.filter((item) => item !== data.params.serial);
@@ -1783,9 +1507,7 @@ export class Frontend {
1783
1507
  return;
1784
1508
  }
1785
1509
  const config = plugin.configJson;
1786
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1787
1510
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1788
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1789
1511
  if (select === 'serial')
1790
1512
  this.log.info(`Unselected device serial ${data.params.serial}`);
1791
1513
  if (select === 'name')
@@ -1794,7 +1516,6 @@ export class Frontend {
1794
1516
  if (config.postfix) {
1795
1517
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1796
1518
  }
1797
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1798
1519
  if (isValidArray(config.whiteList, 1)) {
1799
1520
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1800
1521
  config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
@@ -1803,7 +1524,6 @@ export class Frontend {
1803
1524
  config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
1804
1525
  }
1805
1526
  }
1806
- // Add the serial to the blackList
1807
1527
  if (isValidArray(config.blackList)) {
1808
1528
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1809
1529
  config.blackList.push(data.params.serial);
@@ -1836,230 +1556,114 @@ export class Frontend {
1836
1556
  this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
1837
1557
  }
1838
1558
  }
1839
- /**
1840
- * Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1841
- *
1842
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1843
- * @param {string} time - The time string of the message
1844
- * @param {string} name - The logger name of the message
1845
- * @param {string} message - The content of the message.
1846
- *
1847
- * @remarks
1848
- * The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
1849
- * It also replaces all occurrences of \" with " and angle-brackets with &lt; and &gt;.
1850
- * The function sends the message to all connected clients.
1851
- */
1852
1559
  wssSendMessage(level, time, name, message) {
1853
1560
  if (!level || !time || !name || !message)
1854
1561
  return;
1855
- // Remove ANSI escape codes from the message
1856
- // eslint-disable-next-line no-control-regex
1857
1562
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1858
- // Remove leading asterisks from the message
1859
1563
  message = message.replace(/^\*+/, '');
1860
- // Replace all occurrences of \t and \n
1861
1564
  message = message.replace(/[\t\n]/g, '');
1862
- // Remove non-printable characters
1863
- // eslint-disable-next-line no-control-regex
1864
1565
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1865
- // Replace all occurrences of \" with "
1866
1566
  message = message.replace(/\\"/g, '"');
1867
- // Replace all occurrences of angle-brackets with &lt; and &gt;"
1868
1567
  message = message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1869
- // Define the maximum allowed length for continuous characters without a space
1870
1568
  const maxContinuousLength = 100;
1871
1569
  const keepStartLength = 20;
1872
1570
  const keepEndLength = 20;
1873
- // Split the message into words
1874
1571
  message = message
1875
1572
  .split(' ')
1876
1573
  .map((word) => {
1877
- // If the word length exceeds the max continuous length, insert spaces and truncate
1878
1574
  if (word.length > maxContinuousLength) {
1879
1575
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
1880
1576
  }
1881
1577
  return word;
1882
1578
  })
1883
1579
  .join(' ');
1884
- // Send the message to all connected clients
1885
1580
  this.webSocketServer?.clients.forEach((client) => {
1886
1581
  if (client.readyState === WebSocket.OPEN) {
1887
1582
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
1888
1583
  }
1889
1584
  });
1890
1585
  }
1891
- /**
1892
- * Sends a need to refresh WebSocket message to all connected clients.
1893
- *
1894
- * @param {string} changed - The changed value. If null, the whole page will be refreshed.
1895
- * possible values:
1896
- * - 'matterbridgeLatestVersion'
1897
- * - 'matterbridgeAdvertise'
1898
- * - 'online'
1899
- * - 'offline'
1900
- * - 'reachability'
1901
- * - 'settings'
1902
- * - 'plugins'
1903
- * - 'pluginsRestart'
1904
- * - 'devices'
1905
- * - 'fabrics'
1906
- * - 'sessions'
1907
- */
1908
1586
  wssSendRefreshRequired(changed = null) {
1909
1587
  this.log.debug('Sending a refresh required message to all connected clients');
1910
- // Send the message to all connected clients
1911
1588
  this.webSocketServer?.clients.forEach((client) => {
1912
1589
  if (client.readyState === WebSocket.OPEN) {
1913
1590
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
1914
1591
  }
1915
1592
  });
1916
1593
  }
1917
- /**
1918
- * Sends a need to restart WebSocket message to all connected clients.
1919
- *
1920
- * @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients.
1921
- */
1922
1594
  wssSendRestartRequired(snackbar = true) {
1923
1595
  this.log.debug('Sending a restart required message to all connected clients');
1924
1596
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1925
1597
  if (snackbar === true)
1926
1598
  this.wssSendSnackbarMessage(`Restart required`, 0);
1927
- // Send the message to all connected clients
1928
1599
  this.webSocketServer?.clients.forEach((client) => {
1929
1600
  if (client.readyState === WebSocket.OPEN) {
1930
1601
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
1931
1602
  }
1932
1603
  });
1933
1604
  }
1934
- /**
1935
- * Sends a need to update WebSocket message to all connected clients.
1936
- *
1937
- */
1938
1605
  wssSendUpdateRequired() {
1939
1606
  this.log.debug('Sending an update required message to all connected clients');
1940
1607
  this.matterbridge.matterbridgeInformation.updateRequired = true;
1941
- // Send the message to all connected clients
1942
1608
  this.webSocketServer?.clients.forEach((client) => {
1943
1609
  if (client.readyState === WebSocket.OPEN) {
1944
1610
  client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', params: {} }));
1945
1611
  }
1946
1612
  });
1947
1613
  }
1948
- /**
1949
- * Sends a cpu update message to all connected clients.
1950
- *
1951
- * @param {number} cpuUsage - The CPU usage percentage to send.
1952
- */
1953
1614
  wssSendCpuUpdate(cpuUsage) {
1954
1615
  if (hasParameter('debug'))
1955
1616
  this.log.debug('Sending a cpu update message to all connected clients');
1956
- // Send the message to all connected clients
1957
1617
  this.webSocketServer?.clients.forEach((client) => {
1958
1618
  if (client.readyState === WebSocket.OPEN) {
1959
1619
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
1960
1620
  }
1961
1621
  });
1962
1622
  }
1963
- /**
1964
- * Sends a memory update message to all connected clients.
1965
- *
1966
- * @param {string} totalMemory - The total memory in bytes.
1967
- * @param {string} freeMemory - The free memory in bytes.
1968
- * @param {string} rss - The resident set size in bytes.
1969
- * @param {string} heapTotal - The total heap memory in bytes.
1970
- * @param {string} heapUsed - The used heap memory in bytes.
1971
- * @param {string} external - The external memory in bytes.
1972
- * @param {string} arrayBuffers - The array buffers memory in bytes.
1973
- */
1974
1623
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
1975
1624
  if (hasParameter('debug'))
1976
1625
  this.log.debug('Sending a memory update message to all connected clients');
1977
- // Send the message to all connected clients
1978
1626
  this.webSocketServer?.clients.forEach((client) => {
1979
1627
  if (client.readyState === WebSocket.OPEN) {
1980
1628
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
1981
1629
  }
1982
1630
  });
1983
1631
  }
1984
- /**
1985
- * Sends an uptime update message to all connected clients.
1986
- *
1987
- * @param {string} systemUptime - The system uptime in a human-readable format.
1988
- * @param {string} processUptime - The process uptime in a human-readable format.
1989
- */
1990
1632
  wssSendUptimeUpdate(systemUptime, processUptime) {
1991
1633
  if (hasParameter('debug'))
1992
1634
  this.log.debug('Sending a uptime update message to all connected clients');
1993
- // Send the message to all connected clients
1994
1635
  this.webSocketServer?.clients.forEach((client) => {
1995
1636
  if (client.readyState === WebSocket.OPEN) {
1996
1637
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
1997
1638
  }
1998
1639
  });
1999
1640
  }
2000
- /**
2001
- * Sends an open snackbar message to all connected clients.
2002
- *
2003
- * @param {string} message - The message to send.
2004
- * @param {number} timeout - The timeout in seconds for the snackbar message.
2005
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
2006
- */
2007
1641
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
2008
1642
  this.log.debug('Sending a snackbar message to all connected clients');
2009
- // Send the message to all connected clients
2010
1643
  this.webSocketServer?.clients.forEach((client) => {
2011
1644
  if (client.readyState === WebSocket.OPEN) {
2012
1645
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
2013
1646
  }
2014
1647
  });
2015
1648
  }
2016
- /**
2017
- * Sends a close snackbar message to all connected clients.
2018
- *
2019
- * @param {string} message - The message to send.
2020
- */
2021
1649
  wssSendCloseSnackbarMessage(message) {
2022
1650
  this.log.debug('Sending a close snackbar message to all connected clients');
2023
- // Send the message to all connected clients
2024
1651
  this.webSocketServer?.clients.forEach((client) => {
2025
1652
  if (client.readyState === WebSocket.OPEN) {
2026
1653
  client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
2027
1654
  }
2028
1655
  });
2029
1656
  }
2030
- /**
2031
- * Sends an attribute update message to all connected WebSocket clients.
2032
- *
2033
- * @param {string | undefined} plugin - The name of the plugin.
2034
- * @param {string | undefined} serialNumber - The serial number of the device.
2035
- * @param {string | undefined} uniqueId - The unique identifier of the device.
2036
- * @param {string} cluster - The cluster name where the attribute belongs.
2037
- * @param {string} attribute - The name of the attribute that changed.
2038
- * @param {number | string | boolean} value - The new value of the attribute.
2039
- *
2040
- * @remarks
2041
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
2042
- * with the updated attribute information.
2043
- */
2044
1657
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
2045
1658
  this.log.debug('Sending an attribute update message to all connected clients');
2046
- // Send the message to all connected clients
2047
1659
  this.webSocketServer?.clients.forEach((client) => {
2048
1660
  if (client.readyState === WebSocket.OPEN) {
2049
1661
  client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
2050
1662
  }
2051
1663
  });
2052
1664
  }
2053
- /**
2054
- * Sends a message to all connected clients.
2055
- *
2056
- * @param {number} id - The message id.
2057
- * @param {string} method - The message method.
2058
- * @param {Record<string, string | number | boolean>} params - The message parameters.
2059
- */
2060
1665
  wssBroadcastMessage(id, method, params) {
2061
1666
  this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
2062
- // Send the message to all connected clients
2063
1667
  this.webSocketServer?.clients.forEach((client) => {
2064
1668
  if (client.readyState === WebSocket.OPEN) {
2065
1669
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -2067,4 +1671,3 @@ export class Frontend {
2067
1671
  });
2068
1672
  }
2069
1673
  }
2070
- //# sourceMappingURL=frontend.js.map