matterbridge 3.3.3 → 3.3.4-dev-20251020-4d2dd49

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 (296) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/broadcastServer.js +1 -86
  3. package/dist/broadcastServerTypes.js +0 -24
  4. package/dist/cli.js +110 -444
  5. package/dist/cliEmitter.js +0 -37
  6. package/dist/cliHistory.js +15 -95
  7. package/dist/clusters/export.js +0 -2
  8. package/dist/defaultConfigSchema.js +0 -24
  9. package/dist/deviceManager.js +1 -124
  10. package/dist/devices/airConditioner.js +0 -57
  11. package/dist/devices/batteryStorage.js +1 -48
  12. package/dist/devices/cooktop.js +0 -55
  13. package/dist/devices/dishwasher.js +0 -57
  14. package/dist/devices/evse.js +10 -74
  15. package/dist/devices/export.js +0 -5
  16. package/dist/devices/extractorHood.js +0 -42
  17. package/dist/devices/heatPump.js +2 -50
  18. package/dist/devices/laundryDryer.js +3 -62
  19. package/dist/devices/laundryWasher.js +4 -70
  20. package/dist/devices/microwaveOven.js +5 -88
  21. package/dist/devices/oven.js +0 -85
  22. package/dist/devices/refrigerator.js +0 -102
  23. package/dist/devices/roboticVacuumCleaner.js +9 -100
  24. package/dist/devices/solarPower.js +0 -38
  25. package/dist/devices/speaker.js +0 -84
  26. package/dist/devices/temperatureControl.js +3 -25
  27. package/dist/devices/waterHeater.js +2 -82
  28. package/dist/dgram/coap.js +13 -126
  29. package/dist/dgram/dgram.js +2 -114
  30. package/dist/dgram/mb_coap.js +3 -41
  31. package/dist/dgram/mb_mdns.js +15 -80
  32. package/dist/dgram/mdns.js +137 -299
  33. package/dist/dgram/multicast.js +1 -62
  34. package/dist/dgram/unicast.js +0 -54
  35. package/dist/frontend.js +34 -413
  36. package/dist/frontendTypes.js +0 -45
  37. package/dist/helpers.js +0 -53
  38. package/dist/index.js +0 -25
  39. package/dist/logger/export.js +0 -1
  40. package/dist/matter/behaviors.js +0 -2
  41. package/dist/matter/clusters.js +0 -2
  42. package/dist/matter/devices.js +0 -2
  43. package/dist/matter/endpoints.js +0 -2
  44. package/dist/matter/export.js +0 -3
  45. package/dist/matter/types.js +0 -3
  46. package/dist/matterbridge.js +51 -873
  47. package/dist/matterbridgeAccessoryPlatform.js +0 -36
  48. package/dist/matterbridgeBehaviors.js +5 -71
  49. package/dist/matterbridgeDeviceTypes.js +17 -630
  50. package/dist/matterbridgeDynamicPlatform.js +0 -36
  51. package/dist/matterbridgeEndpoint.js +58 -1412
  52. package/dist/matterbridgeEndpointHelpers.js +10 -368
  53. package/dist/matterbridgePlatform.js +1 -341
  54. package/dist/matterbridgeTypes.js +0 -26
  55. package/dist/pluginManager.js +3 -325
  56. package/dist/shelly.js +7 -168
  57. package/dist/storage/export.js +0 -1
  58. package/dist/update.js +0 -69
  59. package/dist/utils/colorUtils.js +2 -97
  60. package/dist/utils/commandLine.js +6 -55
  61. package/dist/utils/copyDirectory.js +1 -38
  62. package/dist/utils/createDirectory.js +0 -33
  63. package/dist/utils/createZip.js +2 -47
  64. package/dist/utils/deepCopy.js +0 -39
  65. package/dist/utils/deepEqual.js +1 -72
  66. package/dist/utils/error.js +0 -41
  67. package/dist/utils/export.js +0 -1
  68. package/dist/utils/hex.js +0 -124
  69. package/dist/utils/inspector.js +200 -0
  70. package/dist/utils/isvalid.js +0 -101
  71. package/dist/utils/jestHelpers.js +3 -153
  72. package/dist/utils/network.js +5 -108
  73. package/dist/utils/spawn.js +0 -71
  74. package/dist/utils/tracker.js +229 -0
  75. package/dist/utils/wait.js +8 -60
  76. package/frontend/build/assets/index.js +4 -4
  77. package/frontend/build/assets/vendor_mdi.js +1 -1
  78. package/frontend/package.json +1 -1
  79. package/npm-shrinkwrap.json +44 -44
  80. package/package.json +2 -3
  81. package/dist/broadcastServer.d.ts +0 -105
  82. package/dist/broadcastServer.d.ts.map +0 -1
  83. package/dist/broadcastServer.js.map +0 -1
  84. package/dist/broadcastServerTypes.d.ts +0 -719
  85. package/dist/broadcastServerTypes.d.ts.map +0 -1
  86. package/dist/broadcastServerTypes.js.map +0 -1
  87. package/dist/cli.d.ts +0 -26
  88. package/dist/cli.d.ts.map +0 -1
  89. package/dist/cli.js.map +0 -1
  90. package/dist/cliEmitter.d.ts +0 -50
  91. package/dist/cliEmitter.d.ts.map +0 -1
  92. package/dist/cliEmitter.js.map +0 -1
  93. package/dist/cliHistory.d.ts +0 -74
  94. package/dist/cliHistory.d.ts.map +0 -1
  95. package/dist/cliHistory.js.map +0 -1
  96. package/dist/clusters/export.d.ts +0 -2
  97. package/dist/clusters/export.d.ts.map +0 -1
  98. package/dist/clusters/export.js.map +0 -1
  99. package/dist/defaultConfigSchema.d.ts +0 -28
  100. package/dist/defaultConfigSchema.d.ts.map +0 -1
  101. package/dist/defaultConfigSchema.js.map +0 -1
  102. package/dist/deviceManager.d.ts +0 -117
  103. package/dist/deviceManager.d.ts.map +0 -1
  104. package/dist/deviceManager.js.map +0 -1
  105. package/dist/devices/airConditioner.d.ts +0 -98
  106. package/dist/devices/airConditioner.d.ts.map +0 -1
  107. package/dist/devices/airConditioner.js.map +0 -1
  108. package/dist/devices/batteryStorage.d.ts +0 -48
  109. package/dist/devices/batteryStorage.d.ts.map +0 -1
  110. package/dist/devices/batteryStorage.js.map +0 -1
  111. package/dist/devices/cooktop.d.ts +0 -60
  112. package/dist/devices/cooktop.d.ts.map +0 -1
  113. package/dist/devices/cooktop.js.map +0 -1
  114. package/dist/devices/dishwasher.d.ts +0 -71
  115. package/dist/devices/dishwasher.d.ts.map +0 -1
  116. package/dist/devices/dishwasher.js.map +0 -1
  117. package/dist/devices/evse.d.ts +0 -75
  118. package/dist/devices/evse.d.ts.map +0 -1
  119. package/dist/devices/evse.js.map +0 -1
  120. package/dist/devices/export.d.ts +0 -17
  121. package/dist/devices/export.d.ts.map +0 -1
  122. package/dist/devices/export.js.map +0 -1
  123. package/dist/devices/extractorHood.d.ts +0 -46
  124. package/dist/devices/extractorHood.d.ts.map +0 -1
  125. package/dist/devices/extractorHood.js.map +0 -1
  126. package/dist/devices/heatPump.d.ts +0 -47
  127. package/dist/devices/heatPump.d.ts.map +0 -1
  128. package/dist/devices/heatPump.js.map +0 -1
  129. package/dist/devices/laundryDryer.d.ts +0 -67
  130. package/dist/devices/laundryDryer.d.ts.map +0 -1
  131. package/dist/devices/laundryDryer.js.map +0 -1
  132. package/dist/devices/laundryWasher.d.ts +0 -81
  133. package/dist/devices/laundryWasher.d.ts.map +0 -1
  134. package/dist/devices/laundryWasher.js.map +0 -1
  135. package/dist/devices/microwaveOven.d.ts +0 -168
  136. package/dist/devices/microwaveOven.d.ts.map +0 -1
  137. package/dist/devices/microwaveOven.js.map +0 -1
  138. package/dist/devices/oven.d.ts +0 -105
  139. package/dist/devices/oven.d.ts.map +0 -1
  140. package/dist/devices/oven.js.map +0 -1
  141. package/dist/devices/refrigerator.d.ts +0 -118
  142. package/dist/devices/refrigerator.d.ts.map +0 -1
  143. package/dist/devices/refrigerator.js.map +0 -1
  144. package/dist/devices/roboticVacuumCleaner.d.ts +0 -112
  145. package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
  146. package/dist/devices/roboticVacuumCleaner.js.map +0 -1
  147. package/dist/devices/solarPower.d.ts +0 -40
  148. package/dist/devices/solarPower.d.ts.map +0 -1
  149. package/dist/devices/solarPower.js.map +0 -1
  150. package/dist/devices/speaker.d.ts +0 -87
  151. package/dist/devices/speaker.d.ts.map +0 -1
  152. package/dist/devices/speaker.js.map +0 -1
  153. package/dist/devices/temperatureControl.d.ts +0 -166
  154. package/dist/devices/temperatureControl.d.ts.map +0 -1
  155. package/dist/devices/temperatureControl.js.map +0 -1
  156. package/dist/devices/waterHeater.d.ts +0 -111
  157. package/dist/devices/waterHeater.d.ts.map +0 -1
  158. package/dist/devices/waterHeater.js.map +0 -1
  159. package/dist/dgram/coap.d.ts +0 -205
  160. package/dist/dgram/coap.d.ts.map +0 -1
  161. package/dist/dgram/coap.js.map +0 -1
  162. package/dist/dgram/dgram.d.ts +0 -141
  163. package/dist/dgram/dgram.d.ts.map +0 -1
  164. package/dist/dgram/dgram.js.map +0 -1
  165. package/dist/dgram/mb_coap.d.ts +0 -24
  166. package/dist/dgram/mb_coap.d.ts.map +0 -1
  167. package/dist/dgram/mb_coap.js.map +0 -1
  168. package/dist/dgram/mb_mdns.d.ts +0 -24
  169. package/dist/dgram/mb_mdns.d.ts.map +0 -1
  170. package/dist/dgram/mb_mdns.js.map +0 -1
  171. package/dist/dgram/mdns.d.ts +0 -290
  172. package/dist/dgram/mdns.d.ts.map +0 -1
  173. package/dist/dgram/mdns.js.map +0 -1
  174. package/dist/dgram/multicast.d.ts +0 -67
  175. package/dist/dgram/multicast.d.ts.map +0 -1
  176. package/dist/dgram/multicast.js.map +0 -1
  177. package/dist/dgram/unicast.d.ts +0 -56
  178. package/dist/dgram/unicast.d.ts.map +0 -1
  179. package/dist/dgram/unicast.js.map +0 -1
  180. package/dist/frontend.d.ts +0 -235
  181. package/dist/frontend.d.ts.map +0 -1
  182. package/dist/frontend.js.map +0 -1
  183. package/dist/frontendTypes.d.ts +0 -529
  184. package/dist/frontendTypes.d.ts.map +0 -1
  185. package/dist/frontendTypes.js.map +0 -1
  186. package/dist/helpers.d.ts +0 -48
  187. package/dist/helpers.d.ts.map +0 -1
  188. package/dist/helpers.js.map +0 -1
  189. package/dist/index.d.ts +0 -33
  190. package/dist/index.d.ts.map +0 -1
  191. package/dist/index.js.map +0 -1
  192. package/dist/logger/export.d.ts +0 -2
  193. package/dist/logger/export.d.ts.map +0 -1
  194. package/dist/logger/export.js.map +0 -1
  195. package/dist/matter/behaviors.d.ts +0 -2
  196. package/dist/matter/behaviors.d.ts.map +0 -1
  197. package/dist/matter/behaviors.js.map +0 -1
  198. package/dist/matter/clusters.d.ts +0 -2
  199. package/dist/matter/clusters.d.ts.map +0 -1
  200. package/dist/matter/clusters.js.map +0 -1
  201. package/dist/matter/devices.d.ts +0 -2
  202. package/dist/matter/devices.d.ts.map +0 -1
  203. package/dist/matter/devices.js.map +0 -1
  204. package/dist/matter/endpoints.d.ts +0 -2
  205. package/dist/matter/endpoints.d.ts.map +0 -1
  206. package/dist/matter/endpoints.js.map +0 -1
  207. package/dist/matter/export.d.ts +0 -5
  208. package/dist/matter/export.d.ts.map +0 -1
  209. package/dist/matter/export.js.map +0 -1
  210. package/dist/matter/types.d.ts +0 -3
  211. package/dist/matter/types.d.ts.map +0 -1
  212. package/dist/matter/types.js.map +0 -1
  213. package/dist/matterbridge.d.ts +0 -469
  214. package/dist/matterbridge.d.ts.map +0 -1
  215. package/dist/matterbridge.js.map +0 -1
  216. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
  217. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  218. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  219. package/dist/matterbridgeBehaviors.d.ts +0 -2399
  220. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  221. package/dist/matterbridgeBehaviors.js.map +0 -1
  222. package/dist/matterbridgeDeviceTypes.d.ts +0 -761
  223. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  224. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  225. package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
  226. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  227. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  228. package/dist/matterbridgeEndpoint.d.ts +0 -1545
  229. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  230. package/dist/matterbridgeEndpoint.js.map +0 -1
  231. package/dist/matterbridgeEndpointHelpers.d.ts +0 -560
  232. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  233. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  234. package/dist/matterbridgePlatform.d.ts +0 -402
  235. package/dist/matterbridgePlatform.d.ts.map +0 -1
  236. package/dist/matterbridgePlatform.js.map +0 -1
  237. package/dist/matterbridgeTypes.d.ts +0 -209
  238. package/dist/matterbridgeTypes.d.ts.map +0 -1
  239. package/dist/matterbridgeTypes.js.map +0 -1
  240. package/dist/pluginManager.d.ts +0 -353
  241. package/dist/pluginManager.d.ts.map +0 -1
  242. package/dist/pluginManager.js.map +0 -1
  243. package/dist/shelly.d.ts +0 -174
  244. package/dist/shelly.d.ts.map +0 -1
  245. package/dist/shelly.js.map +0 -1
  246. package/dist/storage/export.d.ts +0 -2
  247. package/dist/storage/export.d.ts.map +0 -1
  248. package/dist/storage/export.js.map +0 -1
  249. package/dist/update.d.ts +0 -75
  250. package/dist/update.d.ts.map +0 -1
  251. package/dist/update.js.map +0 -1
  252. package/dist/utils/colorUtils.d.ts +0 -99
  253. package/dist/utils/colorUtils.d.ts.map +0 -1
  254. package/dist/utils/colorUtils.js.map +0 -1
  255. package/dist/utils/commandLine.d.ts +0 -59
  256. package/dist/utils/commandLine.d.ts.map +0 -1
  257. package/dist/utils/commandLine.js.map +0 -1
  258. package/dist/utils/copyDirectory.d.ts +0 -33
  259. package/dist/utils/copyDirectory.d.ts.map +0 -1
  260. package/dist/utils/copyDirectory.js.map +0 -1
  261. package/dist/utils/createDirectory.d.ts +0 -34
  262. package/dist/utils/createDirectory.d.ts.map +0 -1
  263. package/dist/utils/createDirectory.js.map +0 -1
  264. package/dist/utils/createZip.d.ts +0 -39
  265. package/dist/utils/createZip.d.ts.map +0 -1
  266. package/dist/utils/createZip.js.map +0 -1
  267. package/dist/utils/deepCopy.d.ts +0 -32
  268. package/dist/utils/deepCopy.d.ts.map +0 -1
  269. package/dist/utils/deepCopy.js.map +0 -1
  270. package/dist/utils/deepEqual.d.ts +0 -54
  271. package/dist/utils/deepEqual.d.ts.map +0 -1
  272. package/dist/utils/deepEqual.js.map +0 -1
  273. package/dist/utils/error.d.ts +0 -44
  274. package/dist/utils/error.d.ts.map +0 -1
  275. package/dist/utils/error.js.map +0 -1
  276. package/dist/utils/export.d.ts +0 -13
  277. package/dist/utils/export.d.ts.map +0 -1
  278. package/dist/utils/export.js.map +0 -1
  279. package/dist/utils/hex.d.ts +0 -89
  280. package/dist/utils/hex.d.ts.map +0 -1
  281. package/dist/utils/hex.js.map +0 -1
  282. package/dist/utils/isvalid.d.ts +0 -103
  283. package/dist/utils/isvalid.d.ts.map +0 -1
  284. package/dist/utils/isvalid.js.map +0 -1
  285. package/dist/utils/jestHelpers.d.ts +0 -137
  286. package/dist/utils/jestHelpers.d.ts.map +0 -1
  287. package/dist/utils/jestHelpers.js.map +0 -1
  288. package/dist/utils/network.d.ts +0 -115
  289. package/dist/utils/network.d.ts.map +0 -1
  290. package/dist/utils/network.js.map +0 -1
  291. package/dist/utils/spawn.d.ts +0 -35
  292. package/dist/utils/spawn.d.ts.map +0 -1
  293. package/dist/utils/spawn.js.map +0 -1
  294. package/dist/utils/wait.d.ts +0 -54
  295. package/dist/utils/wait.d.ts.map +0 -1
  296. package/dist/utils/wait.js.map +0 -1
package/dist/frontend.js CHANGED
@@ -1,35 +1,9 @@
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.3.0
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
- // eslint-disable-next-line no-console
25
1
  if (process.argv.includes('--loader') || process.argv.includes('-loader'))
26
2
  console.log('\u001B[32mFrontend loaded.\u001B[40;0m');
27
3
  import os from 'node:os';
28
4
  import path from 'node:path';
29
5
  import EventEmitter from 'node:events';
30
- // AnsiLogger module
31
6
  import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt, wr } from 'node-ansi-logger';
32
- // @matter
33
7
  import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle, LogDestination, Diagnostic, Time, FabricIndex } from '@matter/main';
34
8
  import { BridgedDeviceBasicInformation } from '@matter/main/clusters/bridged-device-basic-information';
35
9
  import { PowerSource } from '@matter/main/clusters/power-source';
@@ -59,7 +33,7 @@ export class Frontend extends EventEmitter {
59
33
  constructor(matterbridge) {
60
34
  super();
61
35
  this.matterbridge = matterbridge;
62
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
36
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
63
37
  this.log.logNameColor = '\x1b[38;5;97m';
64
38
  this.server = new BroadcastServer('frontend', this.log);
65
39
  this.server.on('broadcast_message', this.msgHandler.bind(this));
@@ -119,42 +93,13 @@ export class Frontend extends EventEmitter {
119
93
  async start(port = 8283) {
120
94
  this.port = port;
121
95
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
122
- // Initialize multer with the upload directory
123
96
  const multer = await import('multer');
124
- const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads'); // Is created by matterbridge initialize
97
+ const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
125
98
  const upload = multer.default({ dest: uploadDir });
126
- // Create the express app that serves the frontend
127
99
  const express = await import('express');
128
100
  this.expressApp = express.default();
129
- // Inject logging/debug wrapper for route/middleware registration
130
- /*
131
- const methods = ['get', 'post', 'put', 'delete', 'use'];
132
- for (const method of methods) {
133
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
134
- const original = (this.expressApp as any)[method].bind(this.expressApp);
135
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
- (this.expressApp as any)[method] = (path: any, ...rest: any) => {
137
- try {
138
- console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
139
- return original(path, ...rest);
140
- } catch (err) {
141
- console.error(`[ERROR] Failed to register route: ${path}`);
142
- throw err;
143
- }
144
- };
145
- }
146
- */
147
- // Log all requests to the server for debugging
148
- /*
149
- this.expressApp.use((req, res, next) => {
150
- this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
151
- next();
152
- });
153
- */
154
- // Serve static files from 'frontend/build' directory
155
101
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
156
102
  if (!hasParameter('ssl')) {
157
- // Create an HTTP server and attach the express app
158
103
  const http = await import('node:http');
159
104
  try {
160
105
  this.log.debug(`Creating HTTP server...`);
@@ -165,7 +110,6 @@ export class Frontend extends EventEmitter {
165
110
  this.emit('server_error', error);
166
111
  return;
167
112
  }
168
- // Listen on the specified port
169
113
  if (hasParameter('ingress')) {
170
114
  this.httpServer.listen(this.port, '0.0.0.0', () => {
171
115
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -198,7 +142,6 @@ export class Frontend extends EventEmitter {
198
142
  });
199
143
  }
200
144
  else {
201
- // SSL is enabled, load the certificate and the private key
202
145
  let cert;
203
146
  let key;
204
147
  let ca;
@@ -208,7 +151,6 @@ export class Frontend extends EventEmitter {
208
151
  let httpsServerOptions = {};
209
152
  const fs = await import('node:fs');
210
153
  if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
211
- // Load the p12 certificate and the passphrase
212
154
  try {
213
155
  pfx = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
214
156
  this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
@@ -220,7 +162,7 @@ export class Frontend extends EventEmitter {
220
162
  }
221
163
  try {
222
164
  passphrase = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
223
- passphrase = passphrase.trim(); // Ensure no extra characters
165
+ passphrase = passphrase.trim();
224
166
  this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
225
167
  }
226
168
  catch (error) {
@@ -231,7 +173,6 @@ export class Frontend extends EventEmitter {
231
173
  httpsServerOptions = { pfx, passphrase };
232
174
  }
233
175
  else {
234
- // Load the SSL certificate, the private key and optionally the CA certificate. If the CA certificate is present, it will be used to create a full chain certificate.
235
176
  try {
236
177
  cert = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
237
178
  this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
@@ -261,10 +202,9 @@ export class Frontend extends EventEmitter {
261
202
  httpsServerOptions = { cert: fullChain ?? cert, key, ca };
262
203
  }
263
204
  if (hasParameter('mtls')) {
264
- httpsServerOptions.requestCert = true; // Request client certificate
265
- httpsServerOptions.rejectUnauthorized = true; // Require client certificate validation
205
+ httpsServerOptions.requestCert = true;
206
+ httpsServerOptions.rejectUnauthorized = true;
266
207
  }
267
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
268
208
  const https = await import('node:https');
269
209
  try {
270
210
  this.log.debug(`Creating HTTPS server...`);
@@ -275,7 +215,6 @@ export class Frontend extends EventEmitter {
275
215
  this.emit('server_error', error);
276
216
  return;
277
217
  }
278
- // Listen on the specified port
279
218
  if (hasParameter('ingress')) {
280
219
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
281
220
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -307,18 +246,16 @@ export class Frontend extends EventEmitter {
307
246
  return;
308
247
  });
309
248
  }
310
- // Create a WebSocket server and attach it to the http or https server
311
249
  const ws = await import('ws');
312
250
  this.log.debug(`Creating WebSocketServer...`);
313
251
  this.webSocketServer = new ws.WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
314
252
  this.webSocketServer.on('connection', (ws, request) => {
315
253
  const clientIp = request.socket.remoteAddress;
316
- // Set the global logger callback for the WebSocketServer
317
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
318
- if (this.matterbridge.getLogLevel() === "info" /* LogLevel.INFO */ || Logger.level === MatterLogLevel.INFO)
319
- callbackLogLevel = "info" /* LogLevel.INFO */;
320
- if (this.matterbridge.getLogLevel() === "debug" /* LogLevel.DEBUG */ || Logger.level === MatterLogLevel.DEBUG)
321
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
254
+ let callbackLogLevel = "notice";
255
+ if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
256
+ callbackLogLevel = "info";
257
+ if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
258
+ callbackLogLevel = "debug";
322
259
  AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
323
260
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
324
261
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -340,7 +277,6 @@ export class Frontend extends EventEmitter {
340
277
  }
341
278
  });
342
279
  ws.on('error', (error) => {
343
- // istanbul ignore next
344
280
  this.log.error(`WebSocket client error: ${error}`);
345
281
  });
346
282
  });
@@ -354,7 +290,6 @@ export class Frontend extends EventEmitter {
354
290
  this.webSocketServer.on('error', (ws, error) => {
355
291
  this.log.error(`WebSocketServer error: ${error}`);
356
292
  });
357
- // Subscribe to cli events
358
293
  cliEmitter.removeAllListeners();
359
294
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
360
295
  this.wssSendUptimeUpdate(systemUptime, processUptime);
@@ -365,8 +300,6 @@ export class Frontend extends EventEmitter {
365
300
  cliEmitter.on('cpu', (cpuUsage, processCpuUsage) => {
366
301
  this.wssSendCpuUpdate(cpuUsage, processCpuUsage);
367
302
  });
368
- // Endpoint to validate login code
369
- // curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
370
303
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
371
304
  const { password } = req.body;
372
305
  this.log.debug('The frontend sent /api/login', password);
@@ -385,27 +318,23 @@ export class Frontend extends EventEmitter {
385
318
  this.log.warn('/api/login error wrong password');
386
319
  res.json({ valid: false });
387
320
  }
388
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
389
321
  }
390
322
  catch (error) {
391
323
  this.log.error('/api/login error getting password');
392
324
  res.json({ valid: false });
393
325
  }
394
326
  });
395
- // Endpoint to provide health check for docker
396
327
  this.expressApp.get('/health', (req, res) => {
397
328
  this.log.debug('Express received /health');
398
329
  const healthStatus = {
399
- status: 'ok', // Indicate service is healthy
400
- uptime: process.uptime(), // Server uptime in seconds
401
- timestamp: new Date().toISOString(), // Current timestamp
330
+ status: 'ok',
331
+ uptime: process.uptime(),
332
+ timestamp: new Date().toISOString(),
402
333
  };
403
334
  res.status(200).json(healthStatus);
404
335
  });
405
- // Endpoint to provide memory usage details
406
336
  this.expressApp.get('/memory', async (req, res) => {
407
337
  this.log.debug('Express received /memory');
408
- // Memory usage from process
409
338
  const memoryUsageRaw = process.memoryUsage();
410
339
  const memoryUsage = {
411
340
  rss: formatMemoryUsage(memoryUsageRaw.rss),
@@ -414,13 +343,10 @@ export class Frontend extends EventEmitter {
414
343
  external: formatMemoryUsage(memoryUsageRaw.external),
415
344
  arrayBuffers: formatMemoryUsage(memoryUsageRaw.arrayBuffers),
416
345
  };
417
- // V8 heap statistics
418
346
  const { default: v8 } = await import('node:v8');
419
347
  const heapStatsRaw = v8.getHeapStatistics();
420
348
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
421
- // Format heapStats
422
349
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, formatMemoryUsage(value)]));
423
- // Format heapSpaces
424
350
  const heapSpaces = heapSpacesRaw.map((space) => ({
425
351
  ...space,
426
352
  space_size: formatMemoryUsage(space.space_size),
@@ -439,22 +365,18 @@ export class Frontend extends EventEmitter {
439
365
  };
440
366
  res.status(200).json(memoryReport);
441
367
  });
442
- // Endpoint to provide settings
443
368
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
444
369
  this.log.debug('The frontend sent /api/settings');
445
370
  res.json(await this.getApiSettings());
446
371
  });
447
- // Endpoint to provide plugins
448
372
  this.expressApp.get('/api/plugins', async (req, res) => {
449
373
  this.log.debug('The frontend sent /api/plugins');
450
374
  res.json(this.matterbridge.hasCleanupStarted ? [] : this.getPlugins());
451
375
  });
452
- // Endpoint to provide devices
453
376
  this.expressApp.get('/api/devices', async (req, res) => {
454
377
  this.log.debug('The frontend sent /api/devices');
455
378
  res.json(this.matterbridge.hasCleanupStarted ? [] : this.getDevices());
456
379
  });
457
- // Endpoint to view the matterbridge log
458
380
  this.expressApp.get('/api/view-mblog', async (req, res) => {
459
381
  this.log.debug('The frontend sent /api/view-mblog');
460
382
  try {
@@ -468,7 +390,6 @@ export class Frontend extends EventEmitter {
468
390
  res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
469
391
  }
470
392
  });
471
- // Endpoint to view the matter.js log
472
393
  this.expressApp.get('/api/view-mjlog', async (req, res) => {
473
394
  this.log.debug('The frontend sent /api/view-mjlog');
474
395
  try {
@@ -482,7 +403,6 @@ export class Frontend extends EventEmitter {
482
403
  res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
483
404
  }
484
405
  });
485
- // Endpoint to view the diagnostic.log
486
406
  this.expressApp.get('/api/view-diagnostic', async (req, res) => {
487
407
  this.log.debug('The frontend sent /api/view-diagnostic');
488
408
  await this.generateDiagnostic();
@@ -493,13 +413,10 @@ export class Frontend extends EventEmitter {
493
413
  res.send(data.slice(29));
494
414
  }
495
415
  catch (error) {
496
- // istanbul ignore next
497
416
  this.log.error(`Error reading diagnostic log file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
498
- // istanbul ignore next
499
417
  res.status(500).send('Error reading diagnostic log file.');
500
418
  }
501
419
  });
502
- // Endpoint to download the diagnostic.log
503
420
  this.expressApp.get('/api/download-diagnostic', async (req, res) => {
504
421
  this.log.debug(`The frontend sent /api/download-diagnostic`);
505
422
  await this.generateDiagnostic();
@@ -510,19 +427,16 @@ export class Frontend extends EventEmitter {
510
427
  await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), data, 'utf-8');
511
428
  }
512
429
  catch (error) {
513
- // istanbul ignore next
514
430
  this.log.debug(`Error in /api/download-diagnostic: ${error instanceof Error ? error.message : error}`);
515
431
  }
516
432
  res.type('text/plain');
517
433
  res.download(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), MATTERBRIDGE_DIAGNOSTIC_FILE, (error) => {
518
- /* istanbul ignore if */
519
434
  if (error) {
520
435
  this.log.error(`Error downloading file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
521
436
  res.status(500).send('Error downloading the diagnostic log file');
522
437
  }
523
438
  });
524
439
  });
525
- // Endpoint to view the history.html
526
440
  this.expressApp.get('/api/viewhistory', async (req, res) => {
527
441
  this.log.debug('The frontend sent /api/viewhistory');
528
442
  try {
@@ -536,7 +450,6 @@ export class Frontend extends EventEmitter {
536
450
  res.status(500).send('Error reading history file.');
537
451
  }
538
452
  });
539
- // Endpoint to download the history.html
540
453
  this.expressApp.get('/api/downloadhistory', async (req, res) => {
541
454
  this.log.debug(`The frontend sent /api/downloadhistory`);
542
455
  try {
@@ -546,7 +459,6 @@ export class Frontend extends EventEmitter {
546
459
  await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), data, 'utf-8');
547
460
  res.type('text/plain');
548
461
  res.download(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), MATTERBRIDGE_HISTORY_FILE, (error) => {
549
- /* istanbul ignore if */
550
462
  if (error) {
551
463
  this.log.error(`Error in /api/downloadhistory downloading history file ${MATTERBRIDGE_HISTORY_FILE}: ${error instanceof Error ? error.message : error}`);
552
464
  res.status(500).send('Error downloading history file');
@@ -558,7 +470,6 @@ export class Frontend extends EventEmitter {
558
470
  res.status(500).send('Error reading history file.');
559
471
  }
560
472
  });
561
- // Endpoint to view the shelly log
562
473
  this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
563
474
  this.log.debug('The frontend sent /api/shellyviewsystemlog');
564
475
  try {
@@ -572,7 +483,6 @@ export class Frontend extends EventEmitter {
572
483
  res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
573
484
  }
574
485
  });
575
- // Endpoint to download the matterbridge log
576
486
  this.expressApp.get('/api/download-mblog', async (req, res) => {
577
487
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
578
488
  const fs = await import('node:fs');
@@ -587,14 +497,12 @@ export class Frontend extends EventEmitter {
587
497
  }
588
498
  res.type('text/plain');
589
499
  res.download(path.join(os.tmpdir(), MATTERBRIDGE_LOGGER_FILE), 'matterbridge.log', (error) => {
590
- /* istanbul ignore if */
591
500
  if (error) {
592
501
  this.log.error(`Error downloading log file ${MATTERBRIDGE_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
593
502
  res.status(500).send('Error downloading the matterbridge log file');
594
503
  }
595
504
  });
596
505
  });
597
- // Endpoint to download the matter log
598
506
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
599
507
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
600
508
  const fs = await import('node:fs');
@@ -609,14 +517,12 @@ export class Frontend extends EventEmitter {
609
517
  }
610
518
  res.type('text/plain');
611
519
  res.download(path.join(os.tmpdir(), MATTER_LOGGER_FILE), 'matter.log', (error) => {
612
- /* istanbul ignore if */
613
520
  if (error) {
614
521
  this.log.error(`Error downloading log file ${MATTER_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
615
522
  res.status(500).send('Error downloading the matter log file');
616
523
  }
617
524
  });
618
525
  });
619
- // Endpoint to download the shelly log
620
526
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
621
527
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
622
528
  const fs = await import('node:fs');
@@ -631,91 +537,75 @@ export class Frontend extends EventEmitter {
631
537
  }
632
538
  res.type('text/plain');
633
539
  res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
634
- /* istanbul ignore if */
635
540
  if (error) {
636
541
  this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
637
542
  res.status(500).send('Error downloading Shelly system log file');
638
543
  }
639
544
  });
640
545
  });
641
- // Endpoint to download the matterbridge storage directory
642
546
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
643
547
  this.log.debug('The frontend sent /api/download-mbstorage');
644
548
  await createZip(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), path.join(this.matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR));
645
549
  res.download(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), `matterbridge.${NODE_STORAGE_DIR}.zip`, (error) => {
646
- /* istanbul ignore if */
647
550
  if (error) {
648
551
  this.log.error(`Error downloading file ${`matterbridge.${NODE_STORAGE_DIR}.zip`}: ${error instanceof Error ? error.message : error}`);
649
552
  res.status(500).send('Error downloading the matterbridge storage file');
650
553
  }
651
554
  });
652
555
  });
653
- // Endpoint to download the matter storage file
654
556
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
655
557
  this.log.debug('The frontend sent /api/download-mjstorage');
656
558
  await createZip(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME));
657
559
  res.download(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), `matterbridge.${MATTER_STORAGE_NAME}.zip`, (error) => {
658
- /* istanbul ignore if */
659
560
  if (error) {
660
561
  this.log.error(`Error downloading the matter storage matterbridge.${MATTER_STORAGE_NAME}.zip: ${error instanceof Error ? error.message : error}`);
661
562
  res.status(500).send('Error downloading the matter storage zip file');
662
563
  }
663
564
  });
664
565
  });
665
- // Endpoint to download the matterbridge plugin directory
666
566
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
667
567
  this.log.debug('The frontend sent /api/download-pluginstorage');
668
568
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
669
569
  res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
670
- /* istanbul ignore if */
671
570
  if (error) {
672
571
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
673
572
  res.status(500).send('Error downloading the matterbridge plugin storage file');
674
573
  }
675
574
  });
676
575
  });
677
- // Endpoint to download the matterbridge plugin config files
678
576
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
679
577
  this.log.debug('The frontend sent /api/download-pluginconfig');
680
578
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
681
579
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
682
- /* istanbul ignore if */
683
580
  if (error) {
684
581
  this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
685
582
  res.status(500).send('Error downloading the matterbridge plugin config file');
686
583
  }
687
584
  });
688
585
  });
689
- // Endpoint to download the matterbridge backup (created with the backup command)
690
586
  this.expressApp.get('/api/download-backup', async (req, res) => {
691
587
  this.log.debug('The frontend sent /api/download-backup');
692
588
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
693
- /* istanbul ignore if */
694
589
  if (error) {
695
590
  this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
696
591
  res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
697
592
  }
698
593
  });
699
594
  });
700
- // Endpoint to upload a package
701
595
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
702
596
  const { filename } = req.body;
703
597
  const file = req.file;
704
- /* istanbul ignore if */
705
598
  if (!file || !filename) {
706
599
  this.log.error(`uploadpackage: invalid request: file and filename are required`);
707
600
  res.status(400).send('Invalid request: file and filename are required');
708
601
  return;
709
602
  }
710
603
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
711
- // Define the path where the plugin file will be saved
712
604
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
713
605
  try {
714
- // Move the uploaded file to the specified path
715
606
  const fs = await import('node:fs');
716
607
  await fs.promises.rename(file.path, filePath);
717
608
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
718
- // Install the plugin package
719
609
  if (filename.endsWith('.tgz')) {
720
610
  const { spawnCommand } = await import('./utils/spawn.js');
721
611
  await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], 'install', filename);
@@ -735,7 +625,6 @@ export class Frontend extends EventEmitter {
735
625
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
736
626
  }
737
627
  });
738
- // Fallback for routing (must be the last route)
739
628
  this.expressApp.use((req, res) => {
740
629
  this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
741
630
  res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -745,16 +634,13 @@ export class Frontend extends EventEmitter {
745
634
  async stop() {
746
635
  this.log.debug('Stopping the frontend...');
747
636
  const ws = await import('ws');
748
- // Remove listeners from the express app
749
637
  if (this.expressApp) {
750
638
  this.expressApp.removeAllListeners();
751
639
  this.expressApp = undefined;
752
640
  this.log.debug('Frontend app closed successfully');
753
641
  }
754
- // Close the WebSocket server
755
642
  if (this.webSocketServer) {
756
643
  this.log.debug('Closing WebSocket server...');
757
- // Close all active connections
758
644
  this.webSocketServer.clients.forEach((client) => {
759
645
  if (client.readyState === ws.WebSocket.OPEN) {
760
646
  client.close();
@@ -763,7 +649,6 @@ export class Frontend extends EventEmitter {
763
649
  await withTimeout(new Promise((resolve) => {
764
650
  this.webSocketServer?.close((error) => {
765
651
  if (error) {
766
- // istanbul ignore next
767
652
  this.log.error(`Error closing WebSocket server: ${error}`);
768
653
  }
769
654
  else {
@@ -776,27 +661,8 @@ export class Frontend extends EventEmitter {
776
661
  this.webSocketServer.removeAllListeners();
777
662
  this.webSocketServer = undefined;
778
663
  }
779
- // Close the http server
780
664
  if (this.httpServer) {
781
665
  this.log.debug('Closing http server...');
782
- /*
783
- await withTimeout(
784
- new Promise<void>((resolve) => {
785
- this.httpServer?.close((error) => {
786
- if (error) {
787
- // istanbul ignore next
788
- this.log.error(`Error closing http server: ${error}`);
789
- } else {
790
- this.log.debug('Http server closed successfully');
791
- this.emit('server_stopped');
792
- }
793
- resolve();
794
- });
795
- }),
796
- 5000,
797
- false,
798
- );
799
- */
800
666
  this.httpServer.close();
801
667
  this.log.debug('Http server closed successfully');
802
668
  this.listening = false;
@@ -805,27 +671,8 @@ export class Frontend extends EventEmitter {
805
671
  this.httpServer = undefined;
806
672
  this.log.debug('Frontend http server closed successfully');
807
673
  }
808
- // Close the https server
809
674
  if (this.httpsServer) {
810
675
  this.log.debug('Closing https server...');
811
- /*
812
- await withTimeout(
813
- new Promise<void>((resolve) => {
814
- this.httpsServer?.close((error) => {
815
- if (error) {
816
- // istanbul ignore next
817
- this.log.error(`Error closing https server: ${error}`);
818
- } else {
819
- this.log.debug('Https server closed successfully');
820
- this.emit('server_stopped');
821
- }
822
- resolve();
823
- });
824
- }),
825
- 5000,
826
- false,
827
- );
828
- */
829
676
  this.httpsServer.close();
830
677
  this.log.debug('Https server closed successfully');
831
678
  this.listening = false;
@@ -836,13 +683,7 @@ export class Frontend extends EventEmitter {
836
683
  }
837
684
  this.log.debug('Frontend stopped successfully');
838
685
  }
839
- /**
840
- * Retrieves the api settings data.
841
- *
842
- * @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
843
- */
844
686
  async getApiSettings() {
845
- // Update the variable system information properties
846
687
  this.matterbridge.systemInformation.totalMemory = formatMemoryUsage(os.totalmem());
847
688
  this.matterbridge.systemInformation.freeMemory = formatMemoryUsage(os.freemem());
848
689
  this.matterbridge.systemInformation.systemUptime = formatOsUpTime(os.uptime());
@@ -852,7 +693,6 @@ export class Frontend extends EventEmitter {
852
693
  this.matterbridge.systemInformation.rss = formatMemoryUsage(process.memoryUsage().rss);
853
694
  this.matterbridge.systemInformation.heapTotal = formatMemoryUsage(process.memoryUsage().heapTotal);
854
695
  this.matterbridge.systemInformation.heapUsed = formatMemoryUsage(process.memoryUsage().heapUsed);
855
- // Create the matterbridge information
856
696
  const info = {
857
697
  homeDirectory: this.matterbridge.homeDirectory,
858
698
  rootDirectory: this.matterbridge.rootDirectory,
@@ -888,15 +728,9 @@ export class Frontend extends EventEmitter {
888
728
  };
889
729
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: info };
890
730
  }
891
- /**
892
- * Retrieves the reachable attribute.
893
- *
894
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
895
- * @returns {boolean} The reachable attribute.
896
- */
897
731
  getReachability(device) {
898
732
  if (this.matterbridge.hasCleanupStarted)
899
- return false; // Skip if cleanup has started
733
+ return false;
900
734
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
901
735
  return false;
902
736
  if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
@@ -907,15 +741,9 @@ export class Frontend extends EventEmitter {
907
741
  return true;
908
742
  return false;
909
743
  }
910
- /**
911
- * Retrieves the power source attribute.
912
- *
913
- * @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
914
- * @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
915
- */
916
744
  getPowerSource(endpoint) {
917
745
  if (this.matterbridge.hasCleanupStarted)
918
- return; // Skip if cleanup has started
746
+ return;
919
747
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
920
748
  return undefined;
921
749
  const powerSource = (device) => {
@@ -930,25 +758,16 @@ export class Frontend extends EventEmitter {
930
758
  }
931
759
  return;
932
760
  };
933
- // Root endpoint
934
761
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
935
762
  return powerSource(endpoint);
936
- // Child endpoints
937
763
  for (const child of endpoint.getChildEndpoints()) {
938
764
  if (child.hasClusterServer(PowerSource.Cluster.id))
939
765
  return powerSource(child);
940
766
  }
941
767
  }
942
- /**
943
- * Retrieves the cluster text description from a given device.
944
- * The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
945
- *
946
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
947
- * @returns {string} The attributes description of the cluster servers in the device.
948
- */
949
768
  getClusterTextFromDevice(device) {
950
769
  if (this.matterbridge.hasCleanupStarted)
951
- return ''; // Skip if cleanup has started
770
+ return '';
952
771
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
953
772
  return '';
954
773
  const getUserLabel = (device) => {
@@ -958,7 +777,6 @@ export class Frontend extends EventEmitter {
958
777
  if (composed)
959
778
  return 'Composed: ' + composed.value;
960
779
  }
961
- // istanbul ignore next cause is not reachable
962
780
  return '';
963
781
  };
964
782
  const getFixedLabel = (device) => {
@@ -968,13 +786,11 @@ export class Frontend extends EventEmitter {
968
786
  if (composed)
969
787
  return 'Composed: ' + composed.value;
970
788
  }
971
- // istanbul ignore next cause is not reacheable
972
789
  return '';
973
790
  };
974
791
  let attributes = '';
975
792
  let supportedModes = [];
976
793
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
977
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
978
794
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
979
795
  return;
980
796
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -1064,17 +880,11 @@ export class Frontend extends EventEmitter {
1064
880
  if (clusterName === 'userLabel' && attributeName === 'labelList')
1065
881
  attributes += `${getUserLabel(device)} `;
1066
882
  });
1067
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
1068
883
  return attributes.trimStart().trimEnd();
1069
884
  }
1070
- /**
1071
- * Retrieves the registered plugins sanitized for res.json().
1072
- *
1073
- * @returns {ApiPlugin[]} An array of BaseRegisteredPlugin.
1074
- */
1075
885
  getPlugins() {
1076
886
  if (this.matterbridge.hasCleanupStarted)
1077
- return []; // Skip if cleanup has started
887
+ return [];
1078
888
  const plugins = [];
1079
889
  for (const plugin of this.matterbridge.plugins.array()) {
1080
890
  plugins.push({
@@ -1102,27 +912,18 @@ export class Frontend extends EventEmitter {
1102
912
  schemaJson: plugin.schemaJson,
1103
913
  hasWhiteList: plugin.configJson?.whiteList !== undefined,
1104
914
  hasBlackList: plugin.configJson?.blackList !== undefined,
1105
- // Childbridge mode specific data
1106
915
  matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
1107
916
  });
1108
917
  }
1109
918
  return plugins;
1110
919
  }
1111
- /**
1112
- * Retrieves the devices from Matterbridge.
1113
- *
1114
- * @param {string} [pluginName] - The name of the plugin to filter devices by.
1115
- * @returns {ApiDevice[]} An array of ApiDevices for the frontend.
1116
- */
1117
920
  getDevices(pluginName) {
1118
921
  if (this.matterbridge.hasCleanupStarted)
1119
- return []; // Skip if cleanup has started
922
+ return [];
1120
923
  const devices = [];
1121
924
  for (const device of this.matterbridge.devices.array()) {
1122
- // Filter by pluginName if provided
1123
925
  if (pluginName && pluginName !== device.plugin)
1124
926
  continue;
1125
- // Check if the device has the required properties
1126
927
  if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1127
928
  continue;
1128
929
  devices.push({
@@ -1142,39 +943,24 @@ export class Frontend extends EventEmitter {
1142
943
  }
1143
944
  return devices;
1144
945
  }
1145
- /**
1146
- * Retrieves the clusters from a given plugin and endpoint number.
1147
- *
1148
- * Response for /api/clusters
1149
- *
1150
- * @param {string} pluginName - The name of the plugin.
1151
- * @param {number} endpointNumber - The endpoint number.
1152
- * @returns {ApiClusters | undefined} A promise that resolves to the clusters or undefined if not found.
1153
- */
1154
946
  getClusters(pluginName, endpointNumber) {
1155
947
  if (this.matterbridge.hasCleanupStarted)
1156
- return; // Skip if cleanup has started
948
+ return;
1157
949
  const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
1158
950
  if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.maybeId || !endpoint.deviceName || !endpoint.serialNumber) {
1159
951
  this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1160
952
  return;
1161
953
  }
1162
- // this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
1163
- // Get the device types from the main endpoint
1164
954
  const deviceTypes = [];
1165
955
  const clusters = [];
1166
956
  endpoint.state.descriptor.deviceTypeList.forEach((d) => {
1167
957
  deviceTypes.push(d.deviceType);
1168
958
  });
1169
- // Get the clusters from the main endpoint
1170
959
  endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1171
960
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1172
961
  return;
1173
962
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1174
963
  return;
1175
- // console.log(
1176
- // `${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}`,
1177
- // );
1178
964
  clusters.push({
1179
965
  endpoint: endpoint.number.toString(),
1180
966
  number: endpoint.number,
@@ -1188,19 +974,12 @@ export class Frontend extends EventEmitter {
1188
974
  attributeLocalValue: attributeValue,
1189
975
  });
1190
976
  });
1191
- // Get the child endpoints
1192
977
  const childEndpoints = endpoint.getChildEndpoints();
1193
- // if (childEndpoints.length === 0) {
1194
- // this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
1195
- // }
1196
978
  childEndpoints.forEach((childEndpoint) => {
1197
- // istanbul ignore if cause is not reachable: should never happen but ...
1198
979
  if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
1199
980
  this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1200
981
  return;
1201
982
  }
1202
- // this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
1203
- // Get the device types of the child endpoint
1204
983
  const deviceTypes = [];
1205
984
  childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
1206
985
  deviceTypes.push(d.deviceType);
@@ -1210,9 +989,6 @@ export class Frontend extends EventEmitter {
1210
989
  return;
1211
990
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1212
991
  return;
1213
- // console.log(
1214
- // `${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}`,
1215
- // );
1216
992
  clusters.push({
1217
993
  endpoint: childEndpoint.number.toString(),
1218
994
  number: childEndpoint.number,
@@ -1232,7 +1008,6 @@ export class Frontend extends EventEmitter {
1232
1008
  async generateDiagnostic() {
1233
1009
  this.log.debug('Generating diagnostic...');
1234
1010
  const serverNodes = [];
1235
- // istanbul ignore else
1236
1011
  if (this.matterbridge.bridgeMode === 'bridge') {
1237
1012
  if (this.matterbridge.serverNode)
1238
1013
  serverNodes.push(this.matterbridge.serverNode);
@@ -1243,7 +1018,6 @@ export class Frontend extends EventEmitter {
1243
1018
  serverNodes.push(plugin.serverNode);
1244
1019
  }
1245
1020
  }
1246
- // istanbul ignore next
1247
1021
  for (const device of this.matterbridge.devices.array()) {
1248
1022
  if (device.serverNode)
1249
1023
  serverNodes.push(device.serverNode);
@@ -1267,15 +1041,8 @@ export class Frontend extends EventEmitter {
1267
1041
  values: [...serverNodes],
1268
1042
  })));
1269
1043
  delete Logger.destinations.diagnostic;
1270
- await wait(500); // Wait for the log to be written
1044
+ await wait(500);
1271
1045
  }
1272
- /**
1273
- * Handles incoming websocket api request messages from the Matterbridge frontend.
1274
- *
1275
- * @param {WebSocket} client - The websocket client that sent the message.
1276
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1277
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1278
- */
1279
1046
  async wsMessageHandler(client, message) {
1280
1047
  let data;
1281
1048
  const sendResponse = (data) => {
@@ -1295,7 +1062,7 @@ export class Frontend extends EventEmitter {
1295
1062
  };
1296
1063
  try {
1297
1064
  data = JSON.parse(message.toString());
1298
- if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) /* || !isValidObject(data.params)*/ || data.dst !== 'Matterbridge') {
1065
+ if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || data.dst !== 'Matterbridge') {
1299
1066
  this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
1300
1067
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
1301
1068
  return;
@@ -1369,7 +1136,6 @@ export class Frontend extends EventEmitter {
1369
1136
  return;
1370
1137
  })
1371
1138
  .catch((_error) => {
1372
- //
1373
1139
  });
1374
1140
  }
1375
1141
  else {
@@ -1417,7 +1183,6 @@ export class Frontend extends EventEmitter {
1417
1183
  return;
1418
1184
  })
1419
1185
  .catch((_error) => {
1420
- //
1421
1186
  });
1422
1187
  }
1423
1188
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -1443,7 +1208,6 @@ export class Frontend extends EventEmitter {
1443
1208
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1444
1209
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
1445
1210
  if (plugin.serverNode) {
1446
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1447
1211
  await this.matterbridge.stopServerNode(plugin.serverNode);
1448
1212
  plugin.serverNode = undefined;
1449
1213
  }
@@ -1453,20 +1217,18 @@ export class Frontend extends EventEmitter {
1453
1217
  this.matterbridge.devices.remove(device);
1454
1218
  }
1455
1219
  }
1456
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1457
1220
  if (plugin.type === 'DynamicPlatform' && !plugin.locked)
1458
1221
  await this.matterbridge.createDynamicPlugin(plugin);
1459
1222
  await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
1460
- plugin.restartRequired = false; // Reset plugin restartRequired
1223
+ plugin.restartRequired = false;
1461
1224
  let needRestart = 0;
1462
1225
  for (const plugin of this.matterbridge.plugins) {
1463
1226
  if (plugin.restartRequired)
1464
1227
  needRestart++;
1465
1228
  }
1466
1229
  if (needRestart === 0) {
1467
- this.wssSendRestartNotRequired(true); // Reset global restart required message
1230
+ this.wssSendRestartNotRequired(true);
1468
1231
  }
1469
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1470
1232
  if (plugin.serverNode)
1471
1233
  await this.matterbridge.startServerNode(plugin.serverNode);
1472
1234
  this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
@@ -1731,22 +1493,22 @@ export class Frontend extends EventEmitter {
1731
1493
  if (isValidString(data.params.value, 4)) {
1732
1494
  this.log.debug('Matterbridge logger level:', data.params.value);
1733
1495
  if (data.params.value === 'Debug') {
1734
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1496
+ await this.matterbridge.setLogLevel("debug");
1735
1497
  }
1736
1498
  else if (data.params.value === 'Info') {
1737
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1499
+ await this.matterbridge.setLogLevel("info");
1738
1500
  }
1739
1501
  else if (data.params.value === 'Notice') {
1740
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1502
+ await this.matterbridge.setLogLevel("notice");
1741
1503
  }
1742
1504
  else if (data.params.value === 'Warn') {
1743
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1505
+ await this.matterbridge.setLogLevel("warn");
1744
1506
  }
1745
1507
  else if (data.params.value === 'Error') {
1746
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1508
+ await this.matterbridge.setLogLevel("error");
1747
1509
  }
1748
1510
  else if (data.params.value === 'Fatal') {
1749
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1511
+ await this.matterbridge.setLogLevel("fatal");
1750
1512
  }
1751
1513
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1752
1514
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -1757,7 +1519,6 @@ export class Frontend extends EventEmitter {
1757
1519
  this.log.debug('Matterbridge file log:', data.params.value);
1758
1520
  this.matterbridge.fileLogger = data.params.value;
1759
1521
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1760
- // Create the file logger for matterbridge
1761
1522
  if (data.params.value)
1762
1523
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), await this.matterbridge.getLogLevel(), true);
1763
1524
  else
@@ -1786,12 +1547,11 @@ export class Frontend extends EventEmitter {
1786
1547
  else if (data.params.value === 'Fatal') {
1787
1548
  Logger.level = MatterLogLevel.FATAL;
1788
1549
  }
1789
- // Set the global logger callback for the WebSocketServer to the common minimum logLevel
1790
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
1791
- if (this.matterbridge.getLogLevel() === "info" /* LogLevel.INFO */ || Logger.level === MatterLogLevel.INFO)
1792
- callbackLogLevel = "info" /* LogLevel.INFO */;
1793
- if (this.matterbridge.getLogLevel() === "debug" /* LogLevel.DEBUG */ || Logger.level === MatterLogLevel.DEBUG)
1794
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
1550
+ let callbackLogLevel = "notice";
1551
+ if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
1552
+ callbackLogLevel = "info";
1553
+ if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
1554
+ callbackLogLevel = "debug";
1795
1555
  AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
1796
1556
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
1797
1557
  await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.level);
@@ -1843,7 +1603,6 @@ export class Frontend extends EventEmitter {
1843
1603
  }
1844
1604
  break;
1845
1605
  case 'setmatterport':
1846
- // eslint-disable-next-line no-case-declarations
1847
1606
  const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1848
1607
  if (isValidNumber(port, 5540, 5600)) {
1849
1608
  this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
@@ -1863,7 +1622,6 @@ export class Frontend extends EventEmitter {
1863
1622
  }
1864
1623
  break;
1865
1624
  case 'setmatterdiscriminator':
1866
- // eslint-disable-next-line no-case-declarations
1867
1625
  const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1868
1626
  if (isValidNumber(discriminator, 0, 4095)) {
1869
1627
  this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
@@ -1883,7 +1641,6 @@ export class Frontend extends EventEmitter {
1883
1641
  }
1884
1642
  break;
1885
1643
  case 'setmatterpasscode':
1886
- // eslint-disable-next-line no-case-declarations
1887
1644
  const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1888
1645
  if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
1889
1646
  this.matterbridge.passcode = passcode;
@@ -1929,19 +1686,15 @@ export class Frontend extends EventEmitter {
1929
1686
  return;
1930
1687
  }
1931
1688
  const config = plugin.configJson;
1932
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1933
1689
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1934
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1935
1690
  if (select === 'serial')
1936
1691
  this.log.info(`Selected device serial ${data.params.serial}`);
1937
1692
  if (select === 'name')
1938
1693
  this.log.info(`Selected device name ${data.params.name}`);
1939
1694
  if (config && select && (select === 'serial' || select === 'name')) {
1940
- // Remove postfix from the serial if it exists
1941
1695
  if (config.postfix) {
1942
1696
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1943
1697
  }
1944
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1945
1698
  if (isValidArray(config.whiteList, 1)) {
1946
1699
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1947
1700
  config.whiteList.push(data.params.serial);
@@ -1950,7 +1703,6 @@ export class Frontend extends EventEmitter {
1950
1703
  config.whiteList.push(data.params.name);
1951
1704
  }
1952
1705
  }
1953
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1954
1706
  if (isValidArray(config.blackList, 1)) {
1955
1707
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1956
1708
  config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
@@ -1978,9 +1730,7 @@ export class Frontend extends EventEmitter {
1978
1730
  return;
1979
1731
  }
1980
1732
  const config = plugin.configJson;
1981
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1982
1733
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1983
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1984
1734
  if (select === 'serial')
1985
1735
  this.log.info(`Unselected device serial ${data.params.serial}`);
1986
1736
  if (select === 'name')
@@ -1989,7 +1739,6 @@ export class Frontend extends EventEmitter {
1989
1739
  if (config.postfix) {
1990
1740
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1991
1741
  }
1992
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1993
1742
  if (isValidArray(config.whiteList, 1)) {
1994
1743
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1995
1744
  config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
@@ -1998,7 +1747,6 @@ export class Frontend extends EventEmitter {
1998
1747
  config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
1999
1748
  }
2000
1749
  }
2001
- // Add the serial to the blackList
2002
1750
  if (isValidArray(config.blackList)) {
2003
1751
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
2004
1752
  config.blackList.push(data.params.serial);
@@ -2021,7 +1769,6 @@ export class Frontend extends EventEmitter {
2021
1769
  }
2022
1770
  }
2023
1771
  else {
2024
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2025
1772
  const localData = data;
2026
1773
  this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
2027
1774
  sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
@@ -2031,46 +1778,23 @@ export class Frontend extends EventEmitter {
2031
1778
  inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
2032
1779
  }
2033
1780
  }
2034
- /**
2035
- * Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
2036
- *
2037
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
2038
- * @param {string} time - The time string of the message
2039
- * @param {string} name - The logger name of the message
2040
- * @param {string} message - The content of the message.
2041
- *
2042
- * @remarks
2043
- * The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
2044
- * It also replaces all occurrences of \" with " and angle-brackets with &lt; and &gt;.
2045
- * The function sends the message to all connected clients.
2046
- */
2047
1781
  wssSendLogMessage(level, time, name, message) {
2048
1782
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2049
1783
  return;
2050
1784
  if (!level || !time || !name || !message)
2051
1785
  return;
2052
- // Remove ANSI escape codes from the message
2053
- // eslint-disable-next-line no-control-regex
2054
1786
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
2055
- // Remove leading asterisks from the message
2056
1787
  message = message.replace(/^\*+/, '');
2057
- // Replace all occurrences of \t and \n
2058
1788
  message = message.replace(/[\t\n]/g, '');
2059
- // Remove non-printable characters
2060
- // eslint-disable-next-line no-control-regex
2061
1789
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
2062
- // Replace all occurrences of \" with "
2063
1790
  message = message.replace(/\\"/g, '"');
2064
- // Define the maximum allowed length for continuous characters without a space
2065
1791
  const maxContinuousLength = 100;
2066
1792
  const keepStartLength = 20;
2067
1793
  const keepEndLength = 20;
2068
- // Split the message into words
2069
1794
  if (level !== 'spawn') {
2070
1795
  message = message
2071
1796
  .split(' ')
2072
1797
  .map((word) => {
2073
- // If the word length exceeds the max continuous length, insert spaces and truncate
2074
1798
  if (word.length > maxContinuousLength) {
2075
1799
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
2076
1800
  }
@@ -2078,34 +1802,14 @@ export class Frontend extends EventEmitter {
2078
1802
  })
2079
1803
  .join(' ');
2080
1804
  }
2081
- // Send the message to all connected clients
2082
1805
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
2083
1806
  }
2084
- /**
2085
- * Sends a need to refresh WebSocket message to all connected clients.
2086
- *
2087
- * @param {string} changed - The changed value.
2088
- * @param {Record<string, unknown>} params - Additional parameters to send with the message.
2089
- * possible values for changed:
2090
- * - 'settings' (when the bridge has started in bridge mode or childbridge mode and when update finds a new version)
2091
- * - 'plugins'
2092
- * - 'devices'
2093
- * - 'matter' with param 'matter' (QRDiv component)
2094
- * @param {ApiMatter} params.matter - The matter device that has changed. Required if changed is 'matter'.
2095
- */
2096
1807
  wssSendRefreshRequired(changed, params) {
2097
1808
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2098
1809
  return;
2099
1810
  this.log.debug('Sending a refresh required message to all connected clients');
2100
- // Send the message to all connected clients
2101
1811
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
2102
1812
  }
2103
- /**
2104
- * Sends a need to restart WebSocket message to all connected clients.
2105
- *
2106
- * @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
2107
- * @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
2108
- */
2109
1813
  wssSendRestartRequired(snackbar = true, fixed = false) {
2110
1814
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2111
1815
  return;
@@ -2114,14 +1818,8 @@ export class Frontend extends EventEmitter {
2114
1818
  this.matterbridge.fixedRestartRequired = fixed;
2115
1819
  if (snackbar === true)
2116
1820
  this.wssSendSnackbarMessage(`Restart required`, 0);
2117
- // Send the message to all connected clients
2118
1821
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
2119
1822
  }
2120
- /**
2121
- * Sends a no need to restart WebSocket message to all connected clients.
2122
- *
2123
- * @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients. Default is true.
2124
- */
2125
1823
  wssSendRestartNotRequired(snackbar = true) {
2126
1824
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2127
1825
  return;
@@ -2129,133 +1827,57 @@ export class Frontend extends EventEmitter {
2129
1827
  this.matterbridge.restartRequired = false;
2130
1828
  if (snackbar === true)
2131
1829
  this.wssSendCloseSnackbarMessage(`Restart required`);
2132
- // Send the message to all connected clients
2133
1830
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
2134
1831
  }
2135
- /**
2136
- * Sends a need to update WebSocket message to all connected clients.
2137
- *
2138
- * @param {boolean} devVersion - If true, the update is for a development version. Default is false.
2139
- */
2140
1832
  wssSendUpdateRequired(devVersion = false) {
2141
1833
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2142
1834
  return;
2143
1835
  this.log.debug('Sending an update required message to all connected clients');
2144
1836
  this.matterbridge.updateRequired = true;
2145
- // Send the message to all connected clients
2146
1837
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
2147
1838
  }
2148
- /**
2149
- * Sends a cpu update message to all connected clients.
2150
- *
2151
- * @param {number} cpuUsage - The CPU usage percentage to send.
2152
- * @param {number} processCpuUsage - The CPU usage percentage of the process to send.
2153
- */
2154
1839
  wssSendCpuUpdate(cpuUsage, processCpuUsage) {
2155
1840
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2156
1841
  return;
2157
1842
  if (hasParameter('debug'))
2158
1843
  this.log.debug('Sending a cpu update message to all connected clients');
2159
- // Send the message to all connected clients
2160
1844
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', success: true, response: { cpuUsage: Math.round(cpuUsage * 100) / 100, processCpuUsage: Math.round(processCpuUsage * 100) / 100 } });
2161
1845
  }
2162
- /**
2163
- * Sends a memory update message to all connected clients.
2164
- *
2165
- * @param {string} totalMemory - The total memory in bytes.
2166
- * @param {string} freeMemory - The free memory in bytes.
2167
- * @param {string} rss - The resident set size in bytes.
2168
- * @param {string} heapTotal - The total heap memory in bytes.
2169
- * @param {string} heapUsed - The used heap memory in bytes.
2170
- * @param {string} external - The external memory in bytes.
2171
- * @param {string} arrayBuffers - The array buffers memory in bytes.
2172
- */
2173
1846
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
2174
1847
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2175
1848
  return;
2176
1849
  if (hasParameter('debug'))
2177
1850
  this.log.debug('Sending a memory update message to all connected clients');
2178
- // Send the message to all connected clients
2179
1851
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', success: true, response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
2180
1852
  }
2181
- /**
2182
- * Sends an uptime update message to all connected clients.
2183
- *
2184
- * @param {string} systemUptime - The system uptime in a human-readable format.
2185
- * @param {string} processUptime - The process uptime in a human-readable format.
2186
- */
2187
1853
  wssSendUptimeUpdate(systemUptime, processUptime) {
2188
1854
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2189
1855
  return;
2190
1856
  if (hasParameter('debug'))
2191
1857
  this.log.debug('Sending a uptime update message to all connected clients');
2192
- // Send the message to all connected clients
2193
1858
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
2194
1859
  }
2195
- /**
2196
- * Sends an open snackbar message to all connected clients.
2197
- *
2198
- * @param {string} message - The message to send.
2199
- * @param {number} timeout - The timeout in seconds for the snackbar message. Default is 5 seconds.
2200
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the message.
2201
- * possible values are: 'info', 'warning', 'error', 'success'. Default is 'info'.
2202
- *
2203
- * @remarks
2204
- * If timeout is 0, the snackbar message will be displayed until closed by the user.
2205
- */
2206
1860
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
2207
1861
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2208
1862
  return;
2209
1863
  this.log.debug('Sending a snackbar message to all connected clients');
2210
- // Send the message to all connected clients
2211
1864
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
2212
1865
  }
2213
- /**
2214
- * Sends a close snackbar message to all connected clients.
2215
- * It will close the snackbar message with the same message and timeout = 0.
2216
- *
2217
- * @param {string} message - The message to send.
2218
- */
2219
1866
  wssSendCloseSnackbarMessage(message) {
2220
1867
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2221
1868
  return;
2222
1869
  this.log.debug('Sending a close snackbar message to all connected clients');
2223
- // Send the message to all connected clients
2224
1870
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
2225
1871
  }
2226
- /**
2227
- * Sends an attribute update message to all connected WebSocket clients.
2228
- *
2229
- * @param {string | undefined} plugin - The name of the plugin.
2230
- * @param {string | undefined} serialNumber - The serial number of the device.
2231
- * @param {string | undefined} uniqueId - The unique identifier of the device.
2232
- * @param {EndpointNumber} number - The endpoint number where the attribute belongs.
2233
- * @param {string} id - The endpoint id where the attribute belongs.
2234
- * @param {string} cluster - The cluster name where the attribute belongs.
2235
- * @param {string} attribute - The name of the attribute that changed.
2236
- * @param {number | string | boolean} value - The new value of the attribute.
2237
- *
2238
- * @remarks
2239
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
2240
- * with the updated attribute information.
2241
- */
2242
1872
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
2243
1873
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2244
1874
  return;
2245
1875
  this.log.debug('Sending an attribute update message to all connected clients');
2246
- // Send the message to all connected clients
2247
1876
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', success: true, response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value } });
2248
1877
  }
2249
- /**
2250
- * Sends a message to all connected clients.
2251
- * This is an helper function to send a broadcast message to all connected clients.
2252
- *
2253
- * @param {WsMessageBroadcast} msg - The message to send.
2254
- */
2255
1878
  wssBroadcastMessage(msg) {
2256
1879
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2257
1880
  return;
2258
- // Send the message to all connected clients
2259
1881
  const stringifiedMsg = JSON.stringify(msg);
2260
1882
  if (msg.method !== 'log')
2261
1883
  this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
@@ -2266,4 +1888,3 @@ export class Frontend extends EventEmitter {
2266
1888
  });
2267
1889
  }
2268
1890
  }
2269
- //# sourceMappingURL=frontend.js.map