matterbridge 3.4.2 → 3.4.3-dev-20251207-3ce5a0e

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 (332) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README-SERVICE-LOCAL.md +3 -3
  3. package/README-SERVICE-OPT.md +3 -5
  4. package/README.md +8 -14
  5. package/dist/broadcastServer.js +0 -119
  6. package/dist/broadcastServerTypes.js +0 -24
  7. package/dist/cli.js +3 -97
  8. package/dist/cliEmitter.js +0 -37
  9. package/dist/cliHistory.js +0 -38
  10. package/dist/clusters/export.js +0 -2
  11. package/dist/deviceManager.js +1 -113
  12. package/dist/devices/airConditioner.js +0 -57
  13. package/dist/devices/batteryStorage.js +1 -48
  14. package/dist/devices/cooktop.js +0 -56
  15. package/dist/devices/dishwasher.js +0 -57
  16. package/dist/devices/evse.js +10 -74
  17. package/dist/devices/export.js +0 -5
  18. package/dist/devices/extractorHood.js +0 -43
  19. package/dist/devices/heatPump.js +2 -50
  20. package/dist/devices/laundryDryer.js +3 -62
  21. package/dist/devices/laundryWasher.js +4 -70
  22. package/dist/devices/microwaveOven.js +5 -88
  23. package/dist/devices/oven.js +0 -85
  24. package/dist/devices/refrigerator.js +0 -102
  25. package/dist/devices/roboticVacuumCleaner.js +9 -100
  26. package/dist/devices/solarPower.js +0 -38
  27. package/dist/devices/speaker.js +0 -84
  28. package/dist/devices/temperatureControl.js +3 -24
  29. package/dist/devices/waterHeater.js +2 -82
  30. package/dist/dgram/coap.js +13 -126
  31. package/dist/dgram/dgram.js +2 -114
  32. package/dist/dgram/mb_coap.js +3 -41
  33. package/dist/dgram/mb_mdns.js +15 -80
  34. package/dist/dgram/mdns.js +137 -299
  35. package/dist/dgram/multicast.js +1 -62
  36. package/dist/dgram/unicast.js +0 -54
  37. package/dist/frontend.js +35 -455
  38. package/dist/frontendTypes.js +0 -45
  39. package/dist/helpers.js +0 -53
  40. package/dist/index.js +0 -25
  41. package/dist/jestutils/export.js +0 -1
  42. package/dist/jestutils/jestHelpers.js +14 -371
  43. package/dist/logger/export.js +0 -1
  44. package/dist/matter/behaviors.js +0 -2
  45. package/dist/matter/clusters.js +0 -2
  46. package/dist/matter/devices.js +0 -2
  47. package/dist/matter/endpoints.js +0 -2
  48. package/dist/matter/export.js +0 -3
  49. package/dist/matter/types.js +0 -3
  50. package/dist/matterNode.js +8 -369
  51. package/dist/matterbridge.js +54 -817
  52. package/dist/matterbridgeAccessoryPlatform.js +0 -38
  53. package/dist/matterbridgeBehaviors.js +5 -68
  54. package/dist/matterbridgeDeviceTypes.js +14 -635
  55. package/dist/matterbridgeDynamicPlatform.js +0 -38
  56. package/dist/matterbridgeEndpoint.js +53 -1444
  57. package/dist/matterbridgeEndpointHelpers.js +20 -483
  58. package/dist/matterbridgeEndpointTypes.js +0 -25
  59. package/dist/matterbridgePlatform.js +1 -451
  60. package/dist/matterbridgeTypes.js +0 -26
  61. package/dist/pluginManager.js +5 -341
  62. package/dist/shelly.js +7 -178
  63. package/dist/storage/export.js +0 -1
  64. package/dist/update.js +1 -93
  65. package/dist/utils/colorUtils.js +2 -97
  66. package/dist/utils/commandLine.js +0 -60
  67. package/dist/utils/copyDirectory.js +0 -37
  68. package/dist/utils/createDirectory.js +0 -33
  69. package/dist/utils/createZip.js +2 -47
  70. package/dist/utils/deepCopy.js +0 -39
  71. package/dist/utils/deepEqual.js +1 -72
  72. package/dist/utils/error.js +0 -42
  73. package/dist/utils/export.js +0 -1
  74. package/dist/utils/format.js +0 -49
  75. package/dist/utils/hex.js +0 -124
  76. package/dist/utils/inspector.js +1 -69
  77. package/dist/utils/isvalid.js +0 -101
  78. package/dist/utils/network.js +5 -96
  79. package/dist/utils/spawn.js +1 -71
  80. package/dist/utils/tracker.js +1 -64
  81. package/dist/utils/wait.js +8 -60
  82. package/dist/workerGlobalPrefix.js +5 -37
  83. package/dist/workerTypes.js +0 -24
  84. package/dist/workers.js +4 -68
  85. package/frontend/build/assets/index.js +4 -4
  86. package/frontend/build/assets/vendor_mui.js +1 -1
  87. package/frontend/package.json +1 -1
  88. package/npm-shrinkwrap.json +8 -7
  89. package/package.json +3 -4
  90. package/scripts/marked.mjs +132 -0
  91. package/scripts/markedFooter.html +42 -0
  92. package/scripts/markedHeader.html +78 -0
  93. package/dist/broadcastServer.d.ts +0 -144
  94. package/dist/broadcastServer.d.ts.map +0 -1
  95. package/dist/broadcastServer.js.map +0 -1
  96. package/dist/broadcastServerTypes.d.ts +0 -841
  97. package/dist/broadcastServerTypes.d.ts.map +0 -1
  98. package/dist/broadcastServerTypes.js.map +0 -1
  99. package/dist/cli.d.ts +0 -30
  100. package/dist/cli.d.ts.map +0 -1
  101. package/dist/cli.js.map +0 -1
  102. package/dist/cliEmitter.d.ts +0 -50
  103. package/dist/cliEmitter.d.ts.map +0 -1
  104. package/dist/cliEmitter.js.map +0 -1
  105. package/dist/cliHistory.d.ts +0 -48
  106. package/dist/cliHistory.d.ts.map +0 -1
  107. package/dist/cliHistory.js.map +0 -1
  108. package/dist/clusters/export.d.ts +0 -2
  109. package/dist/clusters/export.d.ts.map +0 -1
  110. package/dist/clusters/export.js.map +0 -1
  111. package/dist/deviceManager.d.ts +0 -135
  112. package/dist/deviceManager.d.ts.map +0 -1
  113. package/dist/deviceManager.js.map +0 -1
  114. package/dist/devices/airConditioner.d.ts +0 -98
  115. package/dist/devices/airConditioner.d.ts.map +0 -1
  116. package/dist/devices/airConditioner.js.map +0 -1
  117. package/dist/devices/batteryStorage.d.ts +0 -48
  118. package/dist/devices/batteryStorage.d.ts.map +0 -1
  119. package/dist/devices/batteryStorage.js.map +0 -1
  120. package/dist/devices/cooktop.d.ts +0 -61
  121. package/dist/devices/cooktop.d.ts.map +0 -1
  122. package/dist/devices/cooktop.js.map +0 -1
  123. package/dist/devices/dishwasher.d.ts +0 -71
  124. package/dist/devices/dishwasher.d.ts.map +0 -1
  125. package/dist/devices/dishwasher.js.map +0 -1
  126. package/dist/devices/evse.d.ts +0 -76
  127. package/dist/devices/evse.d.ts.map +0 -1
  128. package/dist/devices/evse.js.map +0 -1
  129. package/dist/devices/export.d.ts +0 -17
  130. package/dist/devices/export.d.ts.map +0 -1
  131. package/dist/devices/export.js.map +0 -1
  132. package/dist/devices/extractorHood.d.ts +0 -46
  133. package/dist/devices/extractorHood.d.ts.map +0 -1
  134. package/dist/devices/extractorHood.js.map +0 -1
  135. package/dist/devices/heatPump.d.ts +0 -47
  136. package/dist/devices/heatPump.d.ts.map +0 -1
  137. package/dist/devices/heatPump.js.map +0 -1
  138. package/dist/devices/laundryDryer.d.ts +0 -67
  139. package/dist/devices/laundryDryer.d.ts.map +0 -1
  140. package/dist/devices/laundryDryer.js.map +0 -1
  141. package/dist/devices/laundryWasher.d.ts +0 -81
  142. package/dist/devices/laundryWasher.d.ts.map +0 -1
  143. package/dist/devices/laundryWasher.js.map +0 -1
  144. package/dist/devices/microwaveOven.d.ts +0 -168
  145. package/dist/devices/microwaveOven.d.ts.map +0 -1
  146. package/dist/devices/microwaveOven.js.map +0 -1
  147. package/dist/devices/oven.d.ts +0 -105
  148. package/dist/devices/oven.d.ts.map +0 -1
  149. package/dist/devices/oven.js.map +0 -1
  150. package/dist/devices/refrigerator.d.ts +0 -118
  151. package/dist/devices/refrigerator.d.ts.map +0 -1
  152. package/dist/devices/refrigerator.js.map +0 -1
  153. package/dist/devices/roboticVacuumCleaner.d.ts +0 -112
  154. package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
  155. package/dist/devices/roboticVacuumCleaner.js.map +0 -1
  156. package/dist/devices/solarPower.d.ts +0 -40
  157. package/dist/devices/solarPower.d.ts.map +0 -1
  158. package/dist/devices/solarPower.js.map +0 -1
  159. package/dist/devices/speaker.d.ts +0 -87
  160. package/dist/devices/speaker.d.ts.map +0 -1
  161. package/dist/devices/speaker.js.map +0 -1
  162. package/dist/devices/temperatureControl.d.ts +0 -166
  163. package/dist/devices/temperatureControl.d.ts.map +0 -1
  164. package/dist/devices/temperatureControl.js.map +0 -1
  165. package/dist/devices/waterHeater.d.ts +0 -111
  166. package/dist/devices/waterHeater.d.ts.map +0 -1
  167. package/dist/devices/waterHeater.js.map +0 -1
  168. package/dist/dgram/coap.d.ts +0 -205
  169. package/dist/dgram/coap.d.ts.map +0 -1
  170. package/dist/dgram/coap.js.map +0 -1
  171. package/dist/dgram/dgram.d.ts +0 -141
  172. package/dist/dgram/dgram.d.ts.map +0 -1
  173. package/dist/dgram/dgram.js.map +0 -1
  174. package/dist/dgram/mb_coap.d.ts +0 -24
  175. package/dist/dgram/mb_coap.d.ts.map +0 -1
  176. package/dist/dgram/mb_coap.js.map +0 -1
  177. package/dist/dgram/mb_mdns.d.ts +0 -24
  178. package/dist/dgram/mb_mdns.d.ts.map +0 -1
  179. package/dist/dgram/mb_mdns.js.map +0 -1
  180. package/dist/dgram/mdns.d.ts +0 -290
  181. package/dist/dgram/mdns.d.ts.map +0 -1
  182. package/dist/dgram/mdns.js.map +0 -1
  183. package/dist/dgram/multicast.d.ts +0 -67
  184. package/dist/dgram/multicast.d.ts.map +0 -1
  185. package/dist/dgram/multicast.js.map +0 -1
  186. package/dist/dgram/unicast.d.ts +0 -56
  187. package/dist/dgram/unicast.d.ts.map +0 -1
  188. package/dist/dgram/unicast.js.map +0 -1
  189. package/dist/frontend.d.ts +0 -238
  190. package/dist/frontend.d.ts.map +0 -1
  191. package/dist/frontend.js.map +0 -1
  192. package/dist/frontendTypes.d.ts +0 -529
  193. package/dist/frontendTypes.d.ts.map +0 -1
  194. package/dist/frontendTypes.js.map +0 -1
  195. package/dist/helpers.d.ts +0 -48
  196. package/dist/helpers.d.ts.map +0 -1
  197. package/dist/helpers.js.map +0 -1
  198. package/dist/index.d.ts +0 -34
  199. package/dist/index.d.ts.map +0 -1
  200. package/dist/index.js.map +0 -1
  201. package/dist/jestutils/export.d.ts +0 -2
  202. package/dist/jestutils/export.d.ts.map +0 -1
  203. package/dist/jestutils/export.js.map +0 -1
  204. package/dist/jestutils/jestHelpers.d.ts +0 -345
  205. package/dist/jestutils/jestHelpers.d.ts.map +0 -1
  206. package/dist/jestutils/jestHelpers.js.map +0 -1
  207. package/dist/logger/export.d.ts +0 -2
  208. package/dist/logger/export.d.ts.map +0 -1
  209. package/dist/logger/export.js.map +0 -1
  210. package/dist/matter/behaviors.d.ts +0 -2
  211. package/dist/matter/behaviors.d.ts.map +0 -1
  212. package/dist/matter/behaviors.js.map +0 -1
  213. package/dist/matter/clusters.d.ts +0 -2
  214. package/dist/matter/clusters.d.ts.map +0 -1
  215. package/dist/matter/clusters.js.map +0 -1
  216. package/dist/matter/devices.d.ts +0 -2
  217. package/dist/matter/devices.d.ts.map +0 -1
  218. package/dist/matter/devices.js.map +0 -1
  219. package/dist/matter/endpoints.d.ts +0 -2
  220. package/dist/matter/endpoints.d.ts.map +0 -1
  221. package/dist/matter/endpoints.js.map +0 -1
  222. package/dist/matter/export.d.ts +0 -5
  223. package/dist/matter/export.d.ts.map +0 -1
  224. package/dist/matter/export.js.map +0 -1
  225. package/dist/matter/types.d.ts +0 -3
  226. package/dist/matter/types.d.ts.map +0 -1
  227. package/dist/matter/types.js.map +0 -1
  228. package/dist/matterNode.d.ts +0 -342
  229. package/dist/matterNode.d.ts.map +0 -1
  230. package/dist/matterNode.js.map +0 -1
  231. package/dist/matterbridge.d.ts +0 -492
  232. package/dist/matterbridge.d.ts.map +0 -1
  233. package/dist/matterbridge.js.map +0 -1
  234. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -41
  235. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  236. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  237. package/dist/matterbridgeBehaviors.d.ts +0 -2404
  238. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  239. package/dist/matterbridgeBehaviors.js.map +0 -1
  240. package/dist/matterbridgeDeviceTypes.d.ts +0 -698
  241. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  242. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  243. package/dist/matterbridgeDynamicPlatform.d.ts +0 -41
  244. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  245. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  246. package/dist/matterbridgeEndpoint.d.ts +0 -1507
  247. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  248. package/dist/matterbridgeEndpoint.js.map +0 -1
  249. package/dist/matterbridgeEndpointHelpers.d.ts +0 -787
  250. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  251. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  252. package/dist/matterbridgeEndpointTypes.d.ts +0 -166
  253. package/dist/matterbridgeEndpointTypes.d.ts.map +0 -1
  254. package/dist/matterbridgeEndpointTypes.js.map +0 -1
  255. package/dist/matterbridgePlatform.d.ts +0 -539
  256. package/dist/matterbridgePlatform.d.ts.map +0 -1
  257. package/dist/matterbridgePlatform.js.map +0 -1
  258. package/dist/matterbridgeTypes.d.ts +0 -251
  259. package/dist/matterbridgeTypes.d.ts.map +0 -1
  260. package/dist/matterbridgeTypes.js.map +0 -1
  261. package/dist/pluginManager.d.ts +0 -372
  262. package/dist/pluginManager.d.ts.map +0 -1
  263. package/dist/pluginManager.js.map +0 -1
  264. package/dist/shelly.d.ts +0 -181
  265. package/dist/shelly.d.ts.map +0 -1
  266. package/dist/shelly.js.map +0 -1
  267. package/dist/storage/export.d.ts +0 -2
  268. package/dist/storage/export.d.ts.map +0 -1
  269. package/dist/storage/export.js.map +0 -1
  270. package/dist/update.d.ts +0 -84
  271. package/dist/update.d.ts.map +0 -1
  272. package/dist/update.js.map +0 -1
  273. package/dist/utils/colorUtils.d.ts +0 -101
  274. package/dist/utils/colorUtils.d.ts.map +0 -1
  275. package/dist/utils/colorUtils.js.map +0 -1
  276. package/dist/utils/commandLine.d.ts +0 -66
  277. package/dist/utils/commandLine.d.ts.map +0 -1
  278. package/dist/utils/commandLine.js.map +0 -1
  279. package/dist/utils/copyDirectory.d.ts +0 -35
  280. package/dist/utils/copyDirectory.d.ts.map +0 -1
  281. package/dist/utils/copyDirectory.js.map +0 -1
  282. package/dist/utils/createDirectory.d.ts +0 -34
  283. package/dist/utils/createDirectory.d.ts.map +0 -1
  284. package/dist/utils/createDirectory.js.map +0 -1
  285. package/dist/utils/createZip.d.ts +0 -39
  286. package/dist/utils/createZip.d.ts.map +0 -1
  287. package/dist/utils/createZip.js.map +0 -1
  288. package/dist/utils/deepCopy.d.ts +0 -32
  289. package/dist/utils/deepCopy.d.ts.map +0 -1
  290. package/dist/utils/deepCopy.js.map +0 -1
  291. package/dist/utils/deepEqual.d.ts +0 -54
  292. package/dist/utils/deepEqual.d.ts.map +0 -1
  293. package/dist/utils/deepEqual.js.map +0 -1
  294. package/dist/utils/error.d.ts +0 -45
  295. package/dist/utils/error.d.ts.map +0 -1
  296. package/dist/utils/error.js.map +0 -1
  297. package/dist/utils/export.d.ts +0 -13
  298. package/dist/utils/export.d.ts.map +0 -1
  299. package/dist/utils/export.js.map +0 -1
  300. package/dist/utils/format.d.ts +0 -53
  301. package/dist/utils/format.d.ts.map +0 -1
  302. package/dist/utils/format.js.map +0 -1
  303. package/dist/utils/hex.d.ts +0 -89
  304. package/dist/utils/hex.d.ts.map +0 -1
  305. package/dist/utils/hex.js.map +0 -1
  306. package/dist/utils/inspector.d.ts +0 -87
  307. package/dist/utils/inspector.d.ts.map +0 -1
  308. package/dist/utils/inspector.js.map +0 -1
  309. package/dist/utils/isvalid.d.ts +0 -103
  310. package/dist/utils/isvalid.d.ts.map +0 -1
  311. package/dist/utils/isvalid.js.map +0 -1
  312. package/dist/utils/network.d.ts +0 -111
  313. package/dist/utils/network.d.ts.map +0 -1
  314. package/dist/utils/network.js.map +0 -1
  315. package/dist/utils/spawn.d.ts +0 -33
  316. package/dist/utils/spawn.d.ts.map +0 -1
  317. package/dist/utils/spawn.js.map +0 -1
  318. package/dist/utils/tracker.d.ts +0 -108
  319. package/dist/utils/tracker.d.ts.map +0 -1
  320. package/dist/utils/tracker.js.map +0 -1
  321. package/dist/utils/wait.d.ts +0 -54
  322. package/dist/utils/wait.d.ts.map +0 -1
  323. package/dist/utils/wait.js.map +0 -1
  324. package/dist/workerGlobalPrefix.d.ts +0 -25
  325. package/dist/workerGlobalPrefix.d.ts.map +0 -1
  326. package/dist/workerGlobalPrefix.js.map +0 -1
  327. package/dist/workerTypes.d.ts +0 -52
  328. package/dist/workerTypes.d.ts.map +0 -1
  329. package/dist/workerTypes.js.map +0 -1
  330. package/dist/workers.d.ts +0 -69
  331. package/dist/workers.d.ts.map +0 -1
  332. package/dist/workers.js.map +0 -1
package/dist/frontend.js CHANGED
@@ -1,34 +1,8 @@
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.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
- // 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
- // Node modules
28
3
  import os from 'node:os';
29
4
  import path from 'node:path';
30
5
  import EventEmitter from 'node:events';
31
- // AnsiLogger module
32
6
  import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt } from 'node-ansi-logger';
33
7
  import { Logger, Diagnostic, LogDestination, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/general';
34
8
  import { DeviceAdvertiser, DeviceCommissioner, FabricManager } from '@matter/protocol';
@@ -63,7 +37,7 @@ export class Frontend extends EventEmitter {
63
37
  constructor(matterbridge) {
64
38
  super();
65
39
  this.matterbridge = matterbridge;
66
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
40
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
67
41
  this.log.logNameColor = '\x1b[38;5;97m';
68
42
  this.server = new BroadcastServer('frontend', this.log);
69
43
  this.server.on('broadcast_message', this.msgHandler.bind(this));
@@ -165,53 +139,23 @@ export class Frontend extends EventEmitter {
165
139
  this.port = port;
166
140
  this.storedPassword = await this.matterbridge.nodeContext?.get('password', '');
167
141
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
168
- // Initialize multer with the upload directory
169
142
  const multer = await import('multer');
170
- const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads'); // Is created by matterbridge initialize
143
+ const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
171
144
  const upload = multer.default({ dest: uploadDir });
172
- // Create the express app that serves the frontend
173
145
  const express = await import('express');
174
146
  this.expressApp = express.default();
175
- // Inject logging/debug wrapper for route/middleware registration
176
- /*
177
- const methods = ['get', 'post', 'put', 'delete', 'use'];
178
- for (const method of methods) {
179
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
180
- const original = (this.expressApp as any)[method].bind(this.expressApp);
181
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
- (this.expressApp as any)[method] = (path: any, ...rest: any) => {
183
- try {
184
- console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
185
- return original(path, ...rest);
186
- } catch (err) {
187
- console.error(`[ERROR] Failed to register route: ${path}`);
188
- throw err;
189
- }
190
- };
191
- }
192
- */
193
- // Log all requests to the server for debugging
194
- /*
195
- this.expressApp.use((req, res, next) => {
196
- this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
197
- next();
198
- });
199
- */
200
- // Serve static files from 'frontend/build' directory
201
147
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend', 'build')));
202
- // Create a WebSocket server and attach it to the http or https server
203
148
  this.log.debug(`Creating WebSocketServer...`);
204
149
  const ws = await import('ws');
205
150
  this.webSocketServer = new ws.WebSocketServer({ noServer: true });
206
151
  this.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
207
152
  this.webSocketServer.on('connection', (ws, request) => {
208
153
  const clientIp = request.socket.remoteAddress;
209
- // Set the global logger callback for the WebSocketServer
210
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
211
- if (this.matterbridge.getLogLevel() === "info" /* LogLevel.INFO */ || Logger.level === MatterLogLevel.INFO)
212
- callbackLogLevel = "info" /* LogLevel.INFO */;
213
- if (this.matterbridge.getLogLevel() === "debug" /* LogLevel.DEBUG */ || Logger.level === MatterLogLevel.DEBUG)
214
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
154
+ let callbackLogLevel = "notice";
155
+ if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
156
+ callbackLogLevel = "info";
157
+ if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
158
+ callbackLogLevel = "debug";
215
159
  AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
216
160
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
217
161
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -233,25 +177,16 @@ export class Frontend extends EventEmitter {
233
177
  }
234
178
  });
235
179
  ws.on('error', (error) => {
236
- // istanbul ignore next
237
180
  this.log.error(`WebSocket client error: ${error}`);
238
181
  });
239
182
  });
240
183
  this.webSocketServer.on('close', () => {
241
184
  this.log.debug(`WebSocketServer closed`);
242
185
  });
243
- /* With { noServer: true } it never fires
244
- this.webSocketServer.on('listening', () => {
245
- this.log.info(`The WebSocketServer is listening`);
246
- this.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
247
- });
248
- */
249
- // istanbul ignore next
250
186
  this.webSocketServer.on('error', (ws, error) => {
251
187
  this.log.error(`WebSocketServer error: ${error}`);
252
188
  });
253
189
  if (!hasParameter('ssl')) {
254
- // Create an HTTP server and attach the express app
255
190
  const http = await import('node:http');
256
191
  try {
257
192
  this.log.debug(`Creating HTTP server...`);
@@ -262,7 +197,6 @@ export class Frontend extends EventEmitter {
262
197
  this.emit('server_error', error);
263
198
  return;
264
199
  }
265
- // Listen on the specified port
266
200
  if (hasParameter('ingress')) {
267
201
  this.httpServer.listen(this.port, '0.0.0.0', () => {
268
202
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -282,30 +216,24 @@ export class Frontend extends EventEmitter {
282
216
  }
283
217
  this.httpServer.on('upgrade', async (req, socket, head) => {
284
218
  try {
285
- // Only proceed for real WebSocket upgrades
286
- // istanbul ignore next cause is only a safety check
287
219
  if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
288
220
  this.log.error(`WebSocket upgrade error: Invalid upgrade header ${req.headers.upgrade}`);
289
221
  socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
290
222
  return socket.destroy();
291
223
  }
292
- // Build a URL so we can read ?password=...
293
224
  const url = new URL(req.url ?? '/', `http://${req.headers.host || 'localhost'}`);
294
- // Validate WebSocket password
295
225
  const password = url.searchParams.get('password') ?? '';
296
226
  if (password !== this.storedPassword) {
297
227
  this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
298
228
  socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
299
229
  return socket.destroy();
300
230
  }
301
- // Complete the WebSocket handshake
302
231
  this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
303
232
  this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
304
233
  this.webSocketServer?.emit('connection', ws, req);
305
234
  });
306
235
  }
307
236
  catch (err) {
308
- /* istanbul ignore next: only triggered on unexpected internal error */
309
237
  {
310
238
  inspectError(this.log, 'WebSocket upgrade error:', err);
311
239
  socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
@@ -328,7 +256,6 @@ export class Frontend extends EventEmitter {
328
256
  });
329
257
  }
330
258
  else {
331
- // SSL is enabled, load the certificate and the private key
332
259
  let cert;
333
260
  let key;
334
261
  let ca;
@@ -338,7 +265,6 @@ export class Frontend extends EventEmitter {
338
265
  let httpsServerOptions = {};
339
266
  const fs = await import('node:fs');
340
267
  if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'))) {
341
- // Load the p12 certificate and the passphrase
342
268
  try {
343
269
  pfx = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'));
344
270
  this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12')}`);
@@ -350,7 +276,7 @@ export class Frontend extends EventEmitter {
350
276
  }
351
277
  try {
352
278
  passphrase = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass'), 'utf8');
353
- passphrase = passphrase.trim(); // Ensure no extra characters
279
+ passphrase = passphrase.trim();
354
280
  this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass')}`);
355
281
  }
356
282
  catch (error) {
@@ -361,7 +287,6 @@ export class Frontend extends EventEmitter {
361
287
  httpsServerOptions = { pfx, passphrase };
362
288
  }
363
289
  else {
364
- // 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.
365
290
  try {
366
291
  cert = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem'), 'utf8');
367
292
  this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem')}`);
@@ -391,10 +316,9 @@ export class Frontend extends EventEmitter {
391
316
  httpsServerOptions = { cert: fullChain ?? cert, key, ca };
392
317
  }
393
318
  if (hasParameter('mtls')) {
394
- httpsServerOptions.requestCert = true; // Request client certificate
395
- httpsServerOptions.rejectUnauthorized = true; // Require client certificate validation
319
+ httpsServerOptions.requestCert = true;
320
+ httpsServerOptions.rejectUnauthorized = true;
396
321
  }
397
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
398
322
  const https = await import('node:https');
399
323
  try {
400
324
  this.log.debug(`Creating HTTPS server...`);
@@ -405,7 +329,6 @@ export class Frontend extends EventEmitter {
405
329
  this.emit('server_error', error);
406
330
  return;
407
331
  }
408
- // Listen on the specified port
409
332
  if (hasParameter('ingress')) {
410
333
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
411
334
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -425,29 +348,23 @@ export class Frontend extends EventEmitter {
425
348
  }
426
349
  this.httpsServer.on('upgrade', async (req, socket, head) => {
427
350
  try {
428
- // Only proceed for real WebSocket upgrades
429
- // istanbul ignore next cause is only a safety check
430
351
  if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
431
352
  socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
432
353
  return socket.destroy();
433
354
  }
434
- // Build a URL so we can read ?password=...
435
355
  const url = new URL(req.url ?? '/', `https://${req.headers.host || 'localhost'}`);
436
- // Validate WebSocket password
437
356
  const password = url.searchParams.get('password') ?? '';
438
357
  if (password !== this.storedPassword) {
439
358
  this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
440
359
  socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
441
360
  return socket.destroy();
442
361
  }
443
- // Complete the WebSocket handshake
444
362
  this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
445
363
  this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
446
364
  this.webSocketServer?.emit('connection', ws, req);
447
365
  });
448
366
  }
449
367
  catch (err) {
450
- /* istanbul ignore next: only triggered on unexpected internal error */
451
368
  {
452
369
  inspectError(this.log, 'WebSocket upgrade error:', err);
453
370
  socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
@@ -469,7 +386,6 @@ export class Frontend extends EventEmitter {
469
386
  return;
470
387
  });
471
388
  }
472
- // Subscribe to cli events
473
389
  cliEmitter.removeAllListeners();
474
390
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
475
391
  this.wssSendUptimeUpdate(systemUptime, processUptime);
@@ -480,8 +396,6 @@ export class Frontend extends EventEmitter {
480
396
  cliEmitter.on('cpu', (cpuUsage, processCpuUsage) => {
481
397
  this.wssSendCpuUpdate(cpuUsage, processCpuUsage);
482
398
  });
483
- // Endpoint to validate login code
484
- // curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
485
399
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
486
400
  const { password } = req.body;
487
401
  this.log.debug(`The frontend sent /api/login with password ${password ? '[redacted]' : '(empty)'}`);
@@ -494,20 +408,17 @@ export class Frontend extends EventEmitter {
494
408
  res.json({ valid: false });
495
409
  }
496
410
  });
497
- // Endpoint to provide health check for docker
498
411
  this.expressApp.get('/health', (req, res) => {
499
412
  this.log.debug('Express received /health');
500
413
  const healthStatus = {
501
- status: 'ok', // Indicate service is healthy
502
- uptime: process.uptime(), // Server uptime in seconds
503
- timestamp: new Date().toISOString(), // Current timestamp
414
+ status: 'ok',
415
+ uptime: process.uptime(),
416
+ timestamp: new Date().toISOString(),
504
417
  };
505
418
  res.status(200).json(healthStatus);
506
419
  });
507
- // Endpoint to provide memory usage details
508
420
  this.expressApp.get('/memory', async (req, res) => {
509
421
  this.log.debug('Express received /memory');
510
- // Memory usage from process
511
422
  const memoryUsageRaw = process.memoryUsage();
512
423
  const memoryUsage = {
513
424
  rss: formatBytes(memoryUsageRaw.rss),
@@ -516,13 +427,10 @@ export class Frontend extends EventEmitter {
516
427
  external: formatBytes(memoryUsageRaw.external),
517
428
  arrayBuffers: formatBytes(memoryUsageRaw.arrayBuffers),
518
429
  };
519
- // V8 heap statistics
520
430
  const { default: v8 } = await import('node:v8');
521
431
  const heapStatsRaw = v8.getHeapStatistics();
522
432
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
523
- // Format heapStats
524
433
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, formatBytes(value)]));
525
- // Format heapSpaces
526
434
  const heapSpaces = heapSpacesRaw.map((space) => ({
527
435
  ...space,
528
436
  space_size: formatBytes(space.space_size),
@@ -541,22 +449,18 @@ export class Frontend extends EventEmitter {
541
449
  };
542
450
  res.status(200).json(memoryReport);
543
451
  });
544
- // Endpoint to provide settings
545
452
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
546
453
  this.log.debug('The frontend sent /api/settings');
547
454
  res.json(await this.getApiSettings());
548
455
  });
549
- // Endpoint to provide plugins
550
456
  this.expressApp.get('/api/plugins', async (req, res) => {
551
457
  this.log.debug('The frontend sent /api/plugins');
552
458
  res.json(this.matterbridge.hasCleanupStarted ? [] : this.getPlugins());
553
459
  });
554
- // Endpoint to provide devices
555
460
  this.expressApp.get('/api/devices', async (req, res) => {
556
461
  this.log.debug('The frontend sent /api/devices');
557
462
  res.json(this.matterbridge.hasCleanupStarted ? [] : this.getDevices());
558
463
  });
559
- // Endpoint to view the matterbridge log
560
464
  this.expressApp.get('/api/view-mblog', async (req, res) => {
561
465
  this.log.debug('The frontend sent /api/view-mblog');
562
466
  try {
@@ -570,7 +474,6 @@ export class Frontend extends EventEmitter {
570
474
  res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
571
475
  }
572
476
  });
573
- // Endpoint to view the matter.js log
574
477
  this.expressApp.get('/api/view-mjlog', async (req, res) => {
575
478
  this.log.debug('The frontend sent /api/view-mjlog');
576
479
  try {
@@ -584,7 +487,6 @@ export class Frontend extends EventEmitter {
584
487
  res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
585
488
  }
586
489
  });
587
- // Endpoint to view the diagnostic.log
588
490
  this.expressApp.get('/api/view-diagnostic', async (req, res) => {
589
491
  this.log.debug('The frontend sent /api/view-diagnostic');
590
492
  await this.generateDiagnostic();
@@ -595,13 +497,10 @@ export class Frontend extends EventEmitter {
595
497
  res.send(data.slice(29));
596
498
  }
597
499
  catch (error) {
598
- // istanbul ignore next
599
500
  this.log.error(`Error reading diagnostic log file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
600
- // istanbul ignore next
601
501
  res.status(500).send('Error reading diagnostic log file.');
602
502
  }
603
503
  });
604
- // Endpoint to download the diagnostic.log
605
504
  this.expressApp.get('/api/download-diagnostic', async (req, res) => {
606
505
  this.log.debug(`The frontend sent /api/download-diagnostic`);
607
506
  await this.generateDiagnostic();
@@ -612,19 +511,16 @@ export class Frontend extends EventEmitter {
612
511
  await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), data, 'utf-8');
613
512
  }
614
513
  catch (error) {
615
- // istanbul ignore next
616
514
  this.log.debug(`Error in /api/download-diagnostic: ${error instanceof Error ? error.message : error}`);
617
515
  }
618
516
  res.type('text/plain');
619
517
  res.download(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), MATTERBRIDGE_DIAGNOSTIC_FILE, (error) => {
620
- /* istanbul ignore if */
621
518
  if (error) {
622
519
  this.log.error(`Error downloading file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
623
520
  res.status(500).send('Error downloading the diagnostic log file');
624
521
  }
625
522
  });
626
523
  });
627
- // Endpoint to view the history.html
628
524
  this.expressApp.get('/api/viewhistory', async (req, res) => {
629
525
  this.log.debug('The frontend sent /api/viewhistory');
630
526
  try {
@@ -638,7 +534,6 @@ export class Frontend extends EventEmitter {
638
534
  res.status(500).send('Error reading history file.');
639
535
  }
640
536
  });
641
- // Endpoint to download the history.html
642
537
  this.expressApp.get('/api/downloadhistory', async (req, res) => {
643
538
  this.log.debug(`The frontend sent /api/downloadhistory`);
644
539
  try {
@@ -648,7 +543,6 @@ export class Frontend extends EventEmitter {
648
543
  await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), data, 'utf-8');
649
544
  res.type('text/plain');
650
545
  res.download(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), MATTERBRIDGE_HISTORY_FILE, (error) => {
651
- /* istanbul ignore if */
652
546
  if (error) {
653
547
  this.log.error(`Error in /api/downloadhistory downloading history file ${MATTERBRIDGE_HISTORY_FILE}: ${error instanceof Error ? error.message : error}`);
654
548
  res.status(500).send('Error downloading history file');
@@ -660,7 +554,6 @@ export class Frontend extends EventEmitter {
660
554
  res.status(500).send('Error reading history file.');
661
555
  }
662
556
  });
663
- // Endpoint to view the shelly log
664
557
  this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
665
558
  this.log.debug('The frontend sent /api/shellyviewsystemlog');
666
559
  try {
@@ -674,7 +567,6 @@ export class Frontend extends EventEmitter {
674
567
  res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
675
568
  }
676
569
  });
677
- // Endpoint to download the matterbridge log
678
570
  this.expressApp.get('/api/download-mblog', async (req, res) => {
679
571
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
680
572
  const fs = await import('node:fs');
@@ -689,14 +581,12 @@ export class Frontend extends EventEmitter {
689
581
  }
690
582
  res.type('text/plain');
691
583
  res.download(path.join(os.tmpdir(), MATTERBRIDGE_LOGGER_FILE), 'matterbridge.log', (error) => {
692
- /* istanbul ignore if */
693
584
  if (error) {
694
585
  this.log.error(`Error downloading log file ${MATTERBRIDGE_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
695
586
  res.status(500).send('Error downloading the matterbridge log file');
696
587
  }
697
588
  });
698
589
  });
699
- // Endpoint to download the matter log
700
590
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
701
591
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
702
592
  const fs = await import('node:fs');
@@ -711,14 +601,12 @@ export class Frontend extends EventEmitter {
711
601
  }
712
602
  res.type('text/plain');
713
603
  res.download(path.join(os.tmpdir(), MATTER_LOGGER_FILE), 'matter.log', (error) => {
714
- /* istanbul ignore if */
715
604
  if (error) {
716
605
  this.log.error(`Error downloading log file ${MATTER_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
717
606
  res.status(500).send('Error downloading the matter log file');
718
607
  }
719
608
  });
720
609
  });
721
- // Endpoint to download the shelly log
722
610
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
723
611
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
724
612
  const fs = await import('node:fs');
@@ -733,91 +621,75 @@ export class Frontend extends EventEmitter {
733
621
  }
734
622
  res.type('text/plain');
735
623
  res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
736
- /* istanbul ignore if */
737
624
  if (error) {
738
625
  this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
739
626
  res.status(500).send('Error downloading Shelly system log file');
740
627
  }
741
628
  });
742
629
  });
743
- // Endpoint to download the matterbridge storage directory
744
630
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
745
631
  this.log.debug('The frontend sent /api/download-mbstorage');
746
632
  await createZip(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), path.join(this.matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR));
747
633
  res.download(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), `matterbridge.${NODE_STORAGE_DIR}.zip`, (error) => {
748
- /* istanbul ignore if */
749
634
  if (error) {
750
635
  this.log.error(`Error downloading file ${`matterbridge.${NODE_STORAGE_DIR}.zip`}: ${error instanceof Error ? error.message : error}`);
751
636
  res.status(500).send('Error downloading the matterbridge storage file');
752
637
  }
753
638
  });
754
639
  });
755
- // Endpoint to download the matter storage file
756
640
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
757
641
  this.log.debug('The frontend sent /api/download-mjstorage');
758
642
  await createZip(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME));
759
643
  res.download(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), `matterbridge.${MATTER_STORAGE_NAME}.zip`, (error) => {
760
- /* istanbul ignore if */
761
644
  if (error) {
762
645
  this.log.error(`Error downloading the matter storage matterbridge.${MATTER_STORAGE_NAME}.zip: ${error instanceof Error ? error.message : error}`);
763
646
  res.status(500).send('Error downloading the matter storage zip file');
764
647
  }
765
648
  });
766
649
  });
767
- // Endpoint to download the matterbridge plugin directory
768
650
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
769
651
  this.log.debug('The frontend sent /api/download-pluginstorage');
770
652
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
771
653
  res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
772
- /* istanbul ignore if */
773
654
  if (error) {
774
655
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
775
656
  res.status(500).send('Error downloading the matterbridge plugin storage file');
776
657
  }
777
658
  });
778
659
  });
779
- // Endpoint to download the matterbridge plugin config files
780
660
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
781
661
  this.log.debug('The frontend sent /api/download-pluginconfig');
782
662
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
783
663
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
784
- /* istanbul ignore if */
785
664
  if (error) {
786
665
  this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
787
666
  res.status(500).send('Error downloading the matterbridge plugin config file');
788
667
  }
789
668
  });
790
669
  });
791
- // Endpoint to download the matterbridge backup (created with the backup command)
792
670
  this.expressApp.get('/api/download-backup', async (req, res) => {
793
671
  this.log.debug('The frontend sent /api/download-backup');
794
672
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
795
- /* istanbul ignore if */
796
673
  if (error) {
797
674
  this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
798
675
  res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
799
676
  }
800
677
  });
801
678
  });
802
- // Endpoint to upload a package
803
679
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
804
680
  const { filename } = req.body;
805
681
  const file = req.file;
806
- /* istanbul ignore if */
807
682
  if (!file || !filename) {
808
683
  this.log.error(`uploadpackage: invalid request: file and filename are required`);
809
684
  res.status(400).send('Invalid request: file and filename are required');
810
685
  return;
811
686
  }
812
687
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
813
- // Define the path where the plugin file will be saved
814
688
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
815
689
  try {
816
- // Move the uploaded file to the specified path
817
690
  const fs = await import('node:fs');
818
691
  await fs.promises.rename(file.path, filePath);
819
692
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
820
- // Install the plugin package
821
693
  if (filename.endsWith('.tgz')) {
822
694
  const { spawnCommand } = await import('./utils/spawn.js');
823
695
  if (await spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], 'install', filename)) {
@@ -845,7 +717,6 @@ export class Frontend extends EventEmitter {
845
717
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
846
718
  }
847
719
  });
848
- // Fallback for routing (must be the last route)
849
720
  this.expressApp.use((req, res) => {
850
721
  const filePath = path.resolve(this.matterbridge.rootDirectory, 'frontend', 'build');
851
722
  this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html in ${filePath} as fallback`);
@@ -856,16 +727,13 @@ export class Frontend extends EventEmitter {
856
727
  async stop() {
857
728
  this.log.debug('Stopping the frontend...');
858
729
  const ws = await import('ws');
859
- // Remove listeners from the express app
860
730
  if (this.expressApp) {
861
731
  this.expressApp.removeAllListeners();
862
732
  this.expressApp = undefined;
863
733
  this.log.debug('Frontend app closed successfully');
864
734
  }
865
- // Close the WebSocket server
866
735
  if (this.webSocketServer) {
867
736
  this.log.debug('Closing WebSocket server...');
868
- // Close all active connections
869
737
  this.webSocketServer.clients.forEach((client) => {
870
738
  if (client.readyState === ws.WebSocket.OPEN) {
871
739
  client.close();
@@ -874,7 +742,6 @@ export class Frontend extends EventEmitter {
874
742
  await withTimeout(new Promise((resolve) => {
875
743
  this.webSocketServer?.close((error) => {
876
744
  if (error) {
877
- // istanbul ignore next
878
745
  this.log.error(`Error closing WebSocket server: ${error}`);
879
746
  }
880
747
  else {
@@ -887,27 +754,8 @@ export class Frontend extends EventEmitter {
887
754
  this.webSocketServer.removeAllListeners();
888
755
  this.webSocketServer = undefined;
889
756
  }
890
- // Close the http server
891
757
  if (this.httpServer) {
892
758
  this.log.debug('Closing http server...');
893
- /*
894
- await withTimeout(
895
- new Promise<void>((resolve) => {
896
- this.httpServer?.close((error) => {
897
- if (error) {
898
- // istanbul ignore next
899
- this.log.error(`Error closing http server: ${error}`);
900
- } else {
901
- this.log.debug('Http server closed successfully');
902
- this.emit('server_stopped');
903
- }
904
- resolve();
905
- });
906
- }),
907
- 5000,
908
- false,
909
- );
910
- */
911
759
  this.httpServer.close();
912
760
  this.log.debug('Http server closed successfully');
913
761
  this.listening = false;
@@ -916,27 +764,8 @@ export class Frontend extends EventEmitter {
916
764
  this.httpServer = undefined;
917
765
  this.log.debug('Frontend http server closed successfully');
918
766
  }
919
- // Close the https server
920
767
  if (this.httpsServer) {
921
768
  this.log.debug('Closing https server...');
922
- /*
923
- await withTimeout(
924
- new Promise<void>((resolve) => {
925
- this.httpsServer?.close((error) => {
926
- if (error) {
927
- // istanbul ignore next
928
- this.log.error(`Error closing https server: ${error}`);
929
- } else {
930
- this.log.debug('Https server closed successfully');
931
- this.emit('server_stopped');
932
- }
933
- resolve();
934
- });
935
- }),
936
- 5000,
937
- false,
938
- );
939
- */
940
769
  this.httpsServer.close();
941
770
  this.log.debug('Https server closed successfully');
942
771
  this.listening = false;
@@ -947,13 +776,7 @@ export class Frontend extends EventEmitter {
947
776
  }
948
777
  this.log.debug('Frontend stopped successfully');
949
778
  }
950
- /**
951
- * Retrieves the api settings data.
952
- *
953
- * @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
954
- */
955
779
  async getApiSettings() {
956
- // Update the variable system information properties
957
780
  this.matterbridge.systemInformation.totalMemory = formatBytes(os.totalmem());
958
781
  this.matterbridge.systemInformation.freeMemory = formatBytes(os.freemem());
959
782
  this.matterbridge.systemInformation.systemUptime = formatUptime(os.uptime());
@@ -963,7 +786,6 @@ export class Frontend extends EventEmitter {
963
786
  this.matterbridge.systemInformation.rss = formatBytes(process.memoryUsage().rss);
964
787
  this.matterbridge.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
965
788
  this.matterbridge.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
966
- // Create the matterbridge information
967
789
  const info = {
968
790
  homeDirectory: this.matterbridge.homeDirectory,
969
791
  rootDirectory: this.matterbridge.rootDirectory,
@@ -999,15 +821,9 @@ export class Frontend extends EventEmitter {
999
821
  };
1000
822
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: info };
1001
823
  }
1002
- /**
1003
- * Retrieves the reachable attribute.
1004
- *
1005
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
1006
- * @returns {boolean} The reachable attribute.
1007
- */
1008
824
  getReachability(device) {
1009
825
  if (this.matterbridge.hasCleanupStarted)
1010
- return false; // Skip if cleanup has started
826
+ return false;
1011
827
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
1012
828
  return false;
1013
829
  if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
@@ -1018,15 +834,9 @@ export class Frontend extends EventEmitter {
1018
834
  return true;
1019
835
  return false;
1020
836
  }
1021
- /**
1022
- * Retrieves the power source attribute.
1023
- *
1024
- * @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
1025
- * @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
1026
- */
1027
837
  getPowerSource(endpoint) {
1028
838
  if (this.matterbridge.hasCleanupStarted)
1029
- return; // Skip if cleanup has started
839
+ return;
1030
840
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
1031
841
  return undefined;
1032
842
  const powerSource = (device) => {
@@ -1041,25 +851,16 @@ export class Frontend extends EventEmitter {
1041
851
  }
1042
852
  return;
1043
853
  };
1044
- // Root endpoint
1045
854
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
1046
855
  return powerSource(endpoint);
1047
- // Child endpoints
1048
856
  for (const child of endpoint.getChildEndpoints()) {
1049
857
  if (child.hasClusterServer(PowerSource.Cluster.id))
1050
858
  return powerSource(child);
1051
859
  }
1052
860
  }
1053
- /**
1054
- * Retrieves the cluster text description from a given device.
1055
- * The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
1056
- *
1057
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
1058
- * @returns {string} The attributes description of the cluster servers in the device.
1059
- */
1060
861
  getClusterTextFromDevice(device) {
1061
862
  if (this.matterbridge.hasCleanupStarted)
1062
- return ''; // Skip if cleanup has started
863
+ return '';
1063
864
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
1064
865
  return '';
1065
866
  const getUserLabel = (device) => {
@@ -1069,7 +870,6 @@ export class Frontend extends EventEmitter {
1069
870
  if (composed)
1070
871
  return 'Composed: ' + composed.value;
1071
872
  }
1072
- // istanbul ignore next cause is not reachable
1073
873
  return '';
1074
874
  };
1075
875
  const getFixedLabel = (device) => {
@@ -1079,13 +879,11 @@ export class Frontend extends EventEmitter {
1079
879
  if (composed)
1080
880
  return 'Composed: ' + composed.value;
1081
881
  }
1082
- // istanbul ignore next cause is not reacheable
1083
882
  return '';
1084
883
  };
1085
884
  let attributes = '';
1086
885
  let supportedModes = [];
1087
886
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1088
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
1089
887
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1090
888
  return;
1091
889
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -1175,17 +973,11 @@ export class Frontend extends EventEmitter {
1175
973
  if (clusterName === 'userLabel' && attributeName === 'labelList')
1176
974
  attributes += `${getUserLabel(device)} `;
1177
975
  });
1178
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
1179
976
  return attributes.trimStart().trimEnd();
1180
977
  }
1181
- /**
1182
- * Retrieves the registered plugins sanitized for res.json().
1183
- *
1184
- * @returns {ApiPlugin[]} An array of BaseRegisteredPlugin.
1185
- */
1186
978
  getPlugins() {
1187
979
  if (this.matterbridge.hasCleanupStarted)
1188
- return []; // Skip if cleanup has started
980
+ return [];
1189
981
  const plugins = [];
1190
982
  for (const plugin of this.matterbridge.plugins.array()) {
1191
983
  plugins.push({
@@ -1213,27 +1005,18 @@ export class Frontend extends EventEmitter {
1213
1005
  schemaJson: plugin.schemaJson,
1214
1006
  hasWhiteList: plugin.configJson?.whiteList !== undefined,
1215
1007
  hasBlackList: plugin.configJson?.blackList !== undefined,
1216
- // Childbridge mode specific data
1217
1008
  matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
1218
1009
  });
1219
1010
  }
1220
1011
  return plugins;
1221
1012
  }
1222
- /**
1223
- * Retrieves the devices from Matterbridge.
1224
- *
1225
- * @param {string} [pluginName] - The name of the plugin to filter devices by.
1226
- * @returns {ApiDevice[]} An array of ApiDevices for the frontend.
1227
- */
1228
1013
  getDevices(pluginName) {
1229
1014
  if (this.matterbridge.hasCleanupStarted)
1230
- return []; // Skip if cleanup has started
1015
+ return [];
1231
1016
  const devices = [];
1232
1017
  for (const device of this.matterbridge.devices.array()) {
1233
- // Filter by pluginName if provided
1234
1018
  if (pluginName && pluginName !== device.plugin)
1235
1019
  continue;
1236
- // Check if the device has the required properties
1237
1020
  if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1238
1021
  continue;
1239
1022
  devices.push({
@@ -1253,39 +1036,24 @@ export class Frontend extends EventEmitter {
1253
1036
  }
1254
1037
  return devices;
1255
1038
  }
1256
- /**
1257
- * Retrieves the clusters from a given plugin and endpoint number.
1258
- *
1259
- * Response for /api/clusters
1260
- *
1261
- * @param {string} pluginName - The name of the plugin.
1262
- * @param {number} endpointNumber - The endpoint number.
1263
- * @returns {ApiClusters | undefined} A promise that resolves to the clusters or undefined if not found.
1264
- */
1265
1039
  getClusters(pluginName, endpointNumber) {
1266
1040
  if (this.matterbridge.hasCleanupStarted)
1267
- return; // Skip if cleanup has started
1041
+ return;
1268
1042
  const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
1269
1043
  if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.maybeId || !endpoint.deviceName || !endpoint.serialNumber) {
1270
1044
  this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1271
1045
  return;
1272
1046
  }
1273
- // this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
1274
- // Get the device types from the main endpoint
1275
1047
  const deviceTypes = [];
1276
1048
  const clusters = [];
1277
1049
  endpoint.state.descriptor.deviceTypeList.forEach((d) => {
1278
1050
  deviceTypes.push(d.deviceType);
1279
1051
  });
1280
- // Get the clusters from the main endpoint
1281
1052
  endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1282
1053
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1283
1054
  return;
1284
1055
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1285
1056
  return;
1286
- // console.log(
1287
- // `${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}`,
1288
- // );
1289
1057
  clusters.push({
1290
1058
  endpoint: endpoint.number.toString(),
1291
1059
  number: endpoint.number,
@@ -1299,19 +1067,12 @@ export class Frontend extends EventEmitter {
1299
1067
  attributeLocalValue: attributeValue,
1300
1068
  });
1301
1069
  });
1302
- // Get the child endpoints
1303
1070
  const childEndpoints = endpoint.getChildEndpoints();
1304
- // if (childEndpoints.length === 0) {
1305
- // this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
1306
- // }
1307
1071
  childEndpoints.forEach((childEndpoint) => {
1308
- // istanbul ignore if cause is not reachable: should never happen but ...
1309
1072
  if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
1310
1073
  this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1311
1074
  return;
1312
1075
  }
1313
- // this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
1314
- // Get the device types of the child endpoint
1315
1076
  const deviceTypes = [];
1316
1077
  childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
1317
1078
  deviceTypes.push(d.deviceType);
@@ -1321,9 +1082,6 @@ export class Frontend extends EventEmitter {
1321
1082
  return;
1322
1083
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1323
1084
  return;
1324
- // console.log(
1325
- // `${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}`,
1326
- // );
1327
1085
  clusters.push({
1328
1086
  endpoint: childEndpoint.number.toString(),
1329
1087
  number: childEndpoint.number,
@@ -1343,7 +1101,6 @@ export class Frontend extends EventEmitter {
1343
1101
  async generateDiagnostic() {
1344
1102
  this.log.debug('Generating diagnostic...');
1345
1103
  const serverNodes = [];
1346
- // istanbul ignore else
1347
1104
  if (this.matterbridge.bridgeMode === 'bridge') {
1348
1105
  if (this.matterbridge.serverNode)
1349
1106
  serverNodes.push(this.matterbridge.serverNode);
@@ -1354,7 +1111,6 @@ export class Frontend extends EventEmitter {
1354
1111
  serverNodes.push(plugin.serverNode);
1355
1112
  }
1356
1113
  }
1357
- // istanbul ignore next
1358
1114
  for (const device of this.matterbridge.devices.array()) {
1359
1115
  if (device.serverNode)
1360
1116
  serverNodes.push(device.serverNode);
@@ -1378,15 +1134,8 @@ export class Frontend extends EventEmitter {
1378
1134
  values: [...serverNodes],
1379
1135
  })));
1380
1136
  delete Logger.destinations.diagnostic;
1381
- await wait(500); // Wait for the log to be written
1137
+ await wait(500);
1382
1138
  }
1383
- /**
1384
- * Handles incoming websocket api request messages from the Matterbridge frontend.
1385
- *
1386
- * @param {WebSocket} client - The websocket client that sent the message.
1387
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1388
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1389
- */
1390
1139
  async wsMessageHandler(client, message) {
1391
1140
  let data;
1392
1141
  const sendResponse = (data) => {
@@ -1404,13 +1153,12 @@ export class Frontend extends EventEmitter {
1404
1153
  client.send(JSON.stringify(data));
1405
1154
  }
1406
1155
  else {
1407
- // istanbul ignore next cause is only a safety check
1408
1156
  this.log.error('Cannot send api response, client not connected');
1409
1157
  }
1410
1158
  };
1411
1159
  try {
1412
1160
  data = JSON.parse(message.toString());
1413
- if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) /* || !isValidObject(data.params)*/ || data.dst !== 'Matterbridge') {
1161
+ if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || data.dst !== 'Matterbridge') {
1414
1162
  this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
1415
1163
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
1416
1164
  return;
@@ -1467,22 +1215,7 @@ export class Frontend extends EventEmitter {
1467
1215
  }
1468
1216
  this.wssSendSnackbarMessage(`Adding plugin ${data.params.pluginNameOrPath}...`, 5);
1469
1217
  this.log.debug(`Adding plugin ${data.params.pluginNameOrPath}...`);
1470
- data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, ''); // Remove @version if present
1471
- /*
1472
- const plugin = (await this.server.fetch({ type: 'plugins_add', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginNameOrPath } }, 5000)).response.plugin;
1473
- if (plugin) {
1474
- this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
1475
- await this.server.fetch({ type: 'plugins_load', src: this.server.name, dst: 'plugins', params: { plugin: plugin.name } }, 5000);
1476
- this.wssSendRestartRequired();
1477
- this.wssSendRefreshRequired('plugins');
1478
- this.wssSendRefreshRequired('devices');
1479
- this.wssSendSnackbarMessage(`Loaded plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
1480
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1481
- } else {
1482
- this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
1483
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
1484
- }
1485
- */
1218
+ data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
1486
1219
  const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
1487
1220
  if (plugin) {
1488
1221
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -1496,7 +1229,6 @@ export class Frontend extends EventEmitter {
1496
1229
  return;
1497
1230
  })
1498
1231
  .catch((_error) => {
1499
- //
1500
1232
  });
1501
1233
  }
1502
1234
  else {
@@ -1511,10 +1243,6 @@ export class Frontend extends EventEmitter {
1511
1243
  }
1512
1244
  this.wssSendSnackbarMessage(`Removing plugin ${data.params.pluginName}...`, 5);
1513
1245
  this.log.debug(`Removing plugin ${data.params.pluginName}...`);
1514
- /*
1515
- await this.server.fetch({ type: 'plugins_shutdown', src: this.server.name, dst: 'plugins', params: { plugin: data.params.pluginName, reason: 'The plugin has been removed.', removeAllDevices: true } }, 5000);
1516
- await this.server.fetch({ type: 'plugins_remove', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginName } }, 5000);
1517
- */
1518
1246
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1519
1247
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
1520
1248
  await this.matterbridge.plugins.remove(data.params.pluginName);
@@ -1550,7 +1278,6 @@ export class Frontend extends EventEmitter {
1550
1278
  return;
1551
1279
  })
1552
1280
  .catch((_error) => {
1553
- //
1554
1281
  });
1555
1282
  }
1556
1283
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -1576,7 +1303,6 @@ export class Frontend extends EventEmitter {
1576
1303
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1577
1304
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
1578
1305
  if (plugin.serverNode) {
1579
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1580
1306
  await this.matterbridge.stopServerNode(plugin.serverNode);
1581
1307
  plugin.serverNode = undefined;
1582
1308
  }
@@ -1586,20 +1312,18 @@ export class Frontend extends EventEmitter {
1586
1312
  this.matterbridge.devices.remove(device);
1587
1313
  }
1588
1314
  }
1589
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1590
1315
  if (plugin.type === 'DynamicPlatform' && !plugin.locked)
1591
1316
  await this.matterbridge.createDynamicPlugin(plugin);
1592
1317
  await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
1593
- plugin.restartRequired = false; // Reset plugin restartRequired
1318
+ plugin.restartRequired = false;
1594
1319
  let needRestart = 0;
1595
1320
  for (const plugin of this.matterbridge.plugins) {
1596
1321
  if (plugin.restartRequired)
1597
1322
  needRestart++;
1598
1323
  }
1599
1324
  if (needRestart === 0) {
1600
- this.wssSendRestartNotRequired(true); // Reset global restart required message
1325
+ this.wssSendRestartNotRequired(true);
1601
1326
  }
1602
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1603
1327
  if (plugin.serverNode)
1604
1328
  await this.matterbridge.startServerNode(plugin.serverNode);
1605
1329
  this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
@@ -1744,9 +1468,6 @@ export class Frontend extends EventEmitter {
1744
1468
  this.wssSendRefreshRequired('matter', { matter: { ...matter, advertiseTime: 0, advertising: false } });
1745
1469
  }
1746
1470
  if (data.params.advertise) {
1747
- // TODO: matter.js 0.16.0
1748
- // await serverNode.env.get(DeviceAdvertiser)?.advertise(true);
1749
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1750
1471
  const advertiser = serverNode.env.get(DeviceAdvertiser);
1751
1472
  if (advertiser && advertiser.advertise && typeof advertiser.advertise === 'function')
1752
1473
  await advertiser.advertise(true);
@@ -1872,22 +1593,22 @@ export class Frontend extends EventEmitter {
1872
1593
  if (isValidString(data.params.value, 4)) {
1873
1594
  this.log.debug('Matterbridge logger level:', data.params.value);
1874
1595
  if (data.params.value === 'Debug') {
1875
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1596
+ await this.matterbridge.setLogLevel("debug");
1876
1597
  }
1877
1598
  else if (data.params.value === 'Info') {
1878
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1599
+ await this.matterbridge.setLogLevel("info");
1879
1600
  }
1880
1601
  else if (data.params.value === 'Notice') {
1881
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1602
+ await this.matterbridge.setLogLevel("notice");
1882
1603
  }
1883
1604
  else if (data.params.value === 'Warn') {
1884
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1605
+ await this.matterbridge.setLogLevel("warn");
1885
1606
  }
1886
1607
  else if (data.params.value === 'Error') {
1887
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1608
+ await this.matterbridge.setLogLevel("error");
1888
1609
  }
1889
1610
  else if (data.params.value === 'Fatal') {
1890
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1611
+ await this.matterbridge.setLogLevel("fatal");
1891
1612
  }
1892
1613
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1893
1614
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -1898,7 +1619,6 @@ export class Frontend extends EventEmitter {
1898
1619
  this.log.debug('Matterbridge file log:', data.params.value);
1899
1620
  this.matterbridge.fileLogger = data.params.value;
1900
1621
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1901
- // Create the file logger for matterbridge
1902
1622
  if (data.params.value)
1903
1623
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), await this.matterbridge.getLogLevel(), true);
1904
1624
  else
@@ -1928,12 +1648,11 @@ export class Frontend extends EventEmitter {
1928
1648
  Logger.level = MatterLogLevel.FATAL;
1929
1649
  }
1930
1650
  this.matterbridge.matterLogLevel = MatterLogLevel.names[Logger.level];
1931
- // Set the global logger callback for the WebSocketServer to the common minimum logLevel
1932
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
1933
- if (this.matterbridge.getLogLevel() === "info" /* LogLevel.INFO */ || Logger.level === MatterLogLevel.INFO)
1934
- callbackLogLevel = "info" /* LogLevel.INFO */;
1935
- if (this.matterbridge.getLogLevel() === "debug" /* LogLevel.DEBUG */ || Logger.level === MatterLogLevel.DEBUG)
1936
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
1651
+ let callbackLogLevel = "notice";
1652
+ if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
1653
+ callbackLogLevel = "info";
1654
+ if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
1655
+ callbackLogLevel = "debug";
1937
1656
  AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
1938
1657
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
1939
1658
  await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.level);
@@ -1985,7 +1704,6 @@ export class Frontend extends EventEmitter {
1985
1704
  }
1986
1705
  break;
1987
1706
  case 'setmatterport':
1988
- // eslint-disable-next-line no-case-declarations
1989
1707
  const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1990
1708
  if (isValidNumber(port, 5540, 5600)) {
1991
1709
  this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
@@ -2005,7 +1723,6 @@ export class Frontend extends EventEmitter {
2005
1723
  }
2006
1724
  break;
2007
1725
  case 'setmatterdiscriminator':
2008
- // eslint-disable-next-line no-case-declarations
2009
1726
  const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
2010
1727
  if (isValidNumber(discriminator, 0, 4095)) {
2011
1728
  this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
@@ -2025,7 +1742,6 @@ export class Frontend extends EventEmitter {
2025
1742
  }
2026
1743
  break;
2027
1744
  case 'setmatterpasscode':
2028
- // eslint-disable-next-line no-case-declarations
2029
1745
  const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
2030
1746
  if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
2031
1747
  this.matterbridge.passcode = passcode;
@@ -2071,19 +1787,15 @@ export class Frontend extends EventEmitter {
2071
1787
  return;
2072
1788
  }
2073
1789
  const config = plugin.configJson;
2074
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2075
1790
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
2076
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
2077
1791
  if (select === 'serial')
2078
1792
  this.log.info(`Selected device serial ${data.params.serial}`);
2079
1793
  if (select === 'name')
2080
1794
  this.log.info(`Selected device name ${data.params.name}`);
2081
1795
  if (config && select && (select === 'serial' || select === 'name')) {
2082
- // Remove postfix from the serial if it exists
2083
1796
  if (config.postfix) {
2084
1797
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
2085
1798
  }
2086
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
2087
1799
  if (isValidArray(config.whiteList, 1)) {
2088
1800
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
2089
1801
  config.whiteList.push(data.params.serial);
@@ -2092,7 +1804,6 @@ export class Frontend extends EventEmitter {
2092
1804
  config.whiteList.push(data.params.name);
2093
1805
  }
2094
1806
  }
2095
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
2096
1807
  if (isValidArray(config.blackList, 1)) {
2097
1808
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
2098
1809
  config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
@@ -2120,9 +1831,7 @@ export class Frontend extends EventEmitter {
2120
1831
  return;
2121
1832
  }
2122
1833
  const config = plugin.configJson;
2123
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2124
1834
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
2125
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
2126
1835
  if (select === 'serial')
2127
1836
  this.log.info(`Unselected device serial ${data.params.serial}`);
2128
1837
  if (select === 'name')
@@ -2131,7 +1840,6 @@ export class Frontend extends EventEmitter {
2131
1840
  if (config.postfix) {
2132
1841
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
2133
1842
  }
2134
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
2135
1843
  if (isValidArray(config.whiteList, 1)) {
2136
1844
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
2137
1845
  config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
@@ -2140,7 +1848,6 @@ export class Frontend extends EventEmitter {
2140
1848
  config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
2141
1849
  }
2142
1850
  }
2143
- // Add the serial to the blackList
2144
1851
  if (isValidArray(config.blackList)) {
2145
1852
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
2146
1853
  config.blackList.push(data.params.serial);
@@ -2163,7 +1870,6 @@ export class Frontend extends EventEmitter {
2163
1870
  }
2164
1871
  }
2165
1872
  else {
2166
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2167
1873
  const localData = data;
2168
1874
  this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
2169
1875
  sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
@@ -2173,46 +1879,23 @@ export class Frontend extends EventEmitter {
2173
1879
  inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
2174
1880
  }
2175
1881
  }
2176
- /**
2177
- * Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
2178
- *
2179
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
2180
- * @param {string} time - The time string of the message
2181
- * @param {string} name - The logger name of the message
2182
- * @param {string} message - The content of the message.
2183
- *
2184
- * @remarks
2185
- * The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
2186
- * It also replaces all occurrences of \" with " and angle-brackets with &lt; and &gt;.
2187
- * The function sends the message to all connected clients.
2188
- */
2189
1882
  wssSendLogMessage(level, time, name, message) {
2190
1883
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2191
1884
  return;
2192
1885
  if (!level || !time || !name || !message)
2193
1886
  return;
2194
- // Remove ANSI escape codes from the message
2195
- // eslint-disable-next-line no-control-regex
2196
1887
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
2197
- // Remove leading asterisks from the message
2198
1888
  message = message.replace(/^\*+/, '');
2199
- // Replace all occurrences of \t and \n
2200
1889
  message = message.replace(/[\t\n]/g, '');
2201
- // Remove non-printable characters
2202
- // eslint-disable-next-line no-control-regex
2203
1890
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
2204
- // Replace all occurrences of \" with "
2205
1891
  message = message.replace(/\\"/g, '"');
2206
- // Define the maximum allowed length for continuous characters without a space
2207
1892
  const maxContinuousLength = 100;
2208
1893
  const keepStartLength = 20;
2209
1894
  const keepEndLength = 20;
2210
- // Split the message into words
2211
1895
  if (level !== 'spawn') {
2212
1896
  message = message
2213
1897
  .split(' ')
2214
1898
  .map((word) => {
2215
- // If the word length exceeds the max continuous length, insert spaces and truncate
2216
1899
  if (word.length > maxContinuousLength) {
2217
1900
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
2218
1901
  }
@@ -2220,34 +1903,14 @@ export class Frontend extends EventEmitter {
2220
1903
  })
2221
1904
  .join(' ');
2222
1905
  }
2223
- // Send the message to all connected clients
2224
1906
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
2225
1907
  }
2226
- /**
2227
- * Sends a need to refresh WebSocket message to all connected clients.
2228
- *
2229
- * @param {string} changed - The changed value.
2230
- * @param {Record<string, unknown>} params - Additional parameters to send with the message.
2231
- * possible values for changed:
2232
- * - 'settings' (when the bridge has started in bridge mode or childbridge mode and when update finds a new version)
2233
- * - 'plugins'
2234
- * - 'devices'
2235
- * - 'matter' with param 'matter' (QRDiv component)
2236
- * @param {ApiMatter} params.matter - The matter device that has changed. Required if changed is 'matter'.
2237
- */
2238
1908
  wssSendRefreshRequired(changed, params) {
2239
1909
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2240
1910
  return;
2241
1911
  this.log.debug('Sending a refresh required message to all connected clients');
2242
- // Send the message to all connected clients
2243
1912
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
2244
1913
  }
2245
- /**
2246
- * Sends a need to restart WebSocket message to all connected clients.
2247
- *
2248
- * @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
2249
- * @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
2250
- */
2251
1914
  wssSendRestartRequired(snackbar = true, fixed = false) {
2252
1915
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2253
1916
  return;
@@ -2256,14 +1919,8 @@ export class Frontend extends EventEmitter {
2256
1919
  this.matterbridge.fixedRestartRequired = fixed;
2257
1920
  if (snackbar === true)
2258
1921
  this.wssSendSnackbarMessage(`Restart required`, 0);
2259
- // Send the message to all connected clients
2260
1922
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
2261
1923
  }
2262
- /**
2263
- * Sends a no need to restart WebSocket message to all connected clients.
2264
- *
2265
- * @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients. Default is true.
2266
- */
2267
1924
  wssSendRestartNotRequired(snackbar = true) {
2268
1925
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2269
1926
  return;
@@ -2271,133 +1928,57 @@ export class Frontend extends EventEmitter {
2271
1928
  this.matterbridge.restartRequired = false;
2272
1929
  if (snackbar === true)
2273
1930
  this.wssSendCloseSnackbarMessage(`Restart required`);
2274
- // Send the message to all connected clients
2275
1931
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
2276
1932
  }
2277
- /**
2278
- * Sends a need to update WebSocket message to all connected clients.
2279
- *
2280
- * @param {boolean} devVersion - If true, the update is for a development version. Default is false.
2281
- */
2282
1933
  wssSendUpdateRequired(devVersion = false) {
2283
1934
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2284
1935
  return;
2285
1936
  this.log.debug('Sending an update required message to all connected clients');
2286
1937
  this.matterbridge.updateRequired = true;
2287
- // Send the message to all connected clients
2288
1938
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
2289
1939
  }
2290
- /**
2291
- * Sends a cpu update message to all connected clients.
2292
- *
2293
- * @param {number} cpuUsage - The CPU usage percentage to send.
2294
- * @param {number} processCpuUsage - The CPU usage percentage of the process to send.
2295
- */
2296
1940
  wssSendCpuUpdate(cpuUsage, processCpuUsage) {
2297
1941
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2298
1942
  return;
2299
1943
  if (hasParameter('debug'))
2300
1944
  this.log.debug('Sending a cpu update message to all connected clients');
2301
- // Send the message to all connected clients
2302
1945
  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 } });
2303
1946
  }
2304
- /**
2305
- * Sends a memory update message to all connected clients.
2306
- *
2307
- * @param {string} totalMemory - The total memory in bytes.
2308
- * @param {string} freeMemory - The free memory in bytes.
2309
- * @param {string} rss - The resident set size in bytes.
2310
- * @param {string} heapTotal - The total heap memory in bytes.
2311
- * @param {string} heapUsed - The used heap memory in bytes.
2312
- * @param {string} external - The external memory in bytes.
2313
- * @param {string} arrayBuffers - The array buffers memory in bytes.
2314
- */
2315
1947
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
2316
1948
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2317
1949
  return;
2318
1950
  if (hasParameter('debug'))
2319
1951
  this.log.debug('Sending a memory update message to all connected clients');
2320
- // Send the message to all connected clients
2321
1952
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', success: true, response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
2322
1953
  }
2323
- /**
2324
- * Sends an uptime update message to all connected clients.
2325
- *
2326
- * @param {string} systemUptime - The system uptime in a human-readable format.
2327
- * @param {string} processUptime - The process uptime in a human-readable format.
2328
- */
2329
1954
  wssSendUptimeUpdate(systemUptime, processUptime) {
2330
1955
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2331
1956
  return;
2332
1957
  if (hasParameter('debug'))
2333
1958
  this.log.debug('Sending a uptime update message to all connected clients');
2334
- // Send the message to all connected clients
2335
1959
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
2336
1960
  }
2337
- /**
2338
- * Sends an open snackbar message to all connected clients.
2339
- *
2340
- * @param {string} message - The message to send.
2341
- * @param {number} timeout - The timeout in seconds for the snackbar message. Default is 5 seconds.
2342
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the message.
2343
- * possible values are: 'info', 'warning', 'error', 'success'. Default is 'info'.
2344
- *
2345
- * @remarks
2346
- * If timeout is 0, the snackbar message will be displayed until closed by the user.
2347
- */
2348
1961
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
2349
1962
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2350
1963
  return;
2351
1964
  this.log.debug('Sending a snackbar message to all connected clients');
2352
- // Send the message to all connected clients
2353
1965
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
2354
1966
  }
2355
- /**
2356
- * Sends a close snackbar message to all connected clients.
2357
- * It will close the snackbar message with the same message and timeout = 0.
2358
- *
2359
- * @param {string} message - The message to send.
2360
- */
2361
1967
  wssSendCloseSnackbarMessage(message) {
2362
1968
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2363
1969
  return;
2364
1970
  this.log.debug('Sending a close snackbar message to all connected clients');
2365
- // Send the message to all connected clients
2366
1971
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
2367
1972
  }
2368
- /**
2369
- * Sends an attribute update message to all connected WebSocket clients.
2370
- *
2371
- * @param {string | undefined} plugin - The name of the plugin.
2372
- * @param {string | undefined} serialNumber - The serial number of the device.
2373
- * @param {string | undefined} uniqueId - The unique identifier of the device.
2374
- * @param {EndpointNumber} number - The endpoint number where the attribute belongs.
2375
- * @param {string} id - The endpoint id where the attribute belongs.
2376
- * @param {string} cluster - The cluster name where the attribute belongs.
2377
- * @param {string} attribute - The name of the attribute that changed.
2378
- * @param {number | string | boolean} value - The new value of the attribute.
2379
- *
2380
- * @remarks
2381
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
2382
- * with the updated attribute information.
2383
- */
2384
1973
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
2385
1974
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2386
1975
  return;
2387
1976
  this.log.debug('Sending an attribute update message to all connected clients');
2388
- // Send the message to all connected clients
2389
1977
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', success: true, response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value } });
2390
1978
  }
2391
- /**
2392
- * Sends a message to all connected clients.
2393
- * This is an helper function to send a broadcast message to all connected clients.
2394
- *
2395
- * @param {WsMessageBroadcast} msg - The message to send.
2396
- */
2397
1979
  wssBroadcastMessage(msg) {
2398
1980
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2399
1981
  return;
2400
- // Send the message to all connected clients
2401
1982
  const stringifiedMsg = JSON.stringify(msg);
2402
1983
  if (msg.method !== 'log')
2403
1984
  this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
@@ -2408,4 +1989,3 @@ export class Frontend extends EventEmitter {
2408
1989
  });
2409
1990
  }
2410
1991
  }
2411
- //# sourceMappingURL=frontend.js.map