@matterbridge/core 3.5.3 → 3.5.4-dev-20260211-c4f9359

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 (294) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.d.ts +0 -24
  3. package/dist/cli.js +1 -97
  4. package/dist/cliEmitter.d.ts +0 -36
  5. package/dist/cliEmitter.js +0 -37
  6. package/dist/cliHistory.d.ts +0 -42
  7. package/dist/cliHistory.js +0 -38
  8. package/dist/clusters/export.d.ts +0 -1
  9. package/dist/clusters/export.js +0 -2
  10. package/dist/deviceManager.d.ts +0 -108
  11. package/dist/deviceManager.js +1 -114
  12. package/dist/devices/airConditioner.d.ts +0 -75
  13. package/dist/devices/airConditioner.js +0 -57
  14. package/dist/devices/basicVideoPlayer.d.ts +0 -58
  15. package/dist/devices/basicVideoPlayer.js +1 -56
  16. package/dist/devices/batteryStorage.d.ts +0 -43
  17. package/dist/devices/batteryStorage.js +1 -48
  18. package/dist/devices/castingVideoPlayer.d.ts +0 -63
  19. package/dist/devices/castingVideoPlayer.js +2 -65
  20. package/dist/devices/cooktop.d.ts +0 -55
  21. package/dist/devices/cooktop.js +0 -56
  22. package/dist/devices/dishwasher.d.ts +0 -55
  23. package/dist/devices/dishwasher.js +0 -57
  24. package/dist/devices/evse.d.ts +0 -57
  25. package/dist/devices/evse.js +10 -74
  26. package/dist/devices/export.d.ts +0 -1
  27. package/dist/devices/export.js +0 -5
  28. package/dist/devices/extractorHood.d.ts +0 -41
  29. package/dist/devices/extractorHood.js +0 -43
  30. package/dist/devices/heatPump.d.ts +0 -43
  31. package/dist/devices/heatPump.js +2 -50
  32. package/dist/devices/laundryDryer.d.ts +0 -58
  33. package/dist/devices/laundryDryer.js +3 -62
  34. package/dist/devices/laundryWasher.d.ts +0 -64
  35. package/dist/devices/laundryWasher.js +4 -70
  36. package/dist/devices/microwaveOven.d.ts +1 -77
  37. package/dist/devices/microwaveOven.js +5 -88
  38. package/dist/devices/oven.d.ts +0 -82
  39. package/dist/devices/oven.js +0 -85
  40. package/dist/devices/refrigerator.d.ts +0 -100
  41. package/dist/devices/refrigerator.js +0 -102
  42. package/dist/devices/roboticVacuumCleaner.d.ts +0 -83
  43. package/dist/devices/roboticVacuumCleaner.js +9 -100
  44. package/dist/devices/solarPower.d.ts +0 -36
  45. package/dist/devices/solarPower.js +0 -38
  46. package/dist/devices/speaker.d.ts +0 -79
  47. package/dist/devices/speaker.js +0 -84
  48. package/dist/devices/temperatureControl.d.ts +0 -21
  49. package/dist/devices/temperatureControl.js +3 -24
  50. package/dist/devices/waterHeater.d.ts +0 -74
  51. package/dist/devices/waterHeater.js +2 -82
  52. package/dist/dgram/export.d.ts +0 -1
  53. package/dist/dgram/export.js +0 -1
  54. package/dist/export.d.ts +0 -23
  55. package/dist/export.js +0 -28
  56. package/dist/frontend.d.ts +0 -187
  57. package/dist/frontend.js +38 -539
  58. package/dist/helpers.d.ts +0 -43
  59. package/dist/helpers.js +0 -86
  60. package/dist/jestutils/export.d.ts +0 -1
  61. package/dist/jestutils/export.js +0 -1
  62. package/dist/jestutils/jestHelpers.d.ts +0 -259
  63. package/dist/jestutils/jestHelpers.js +14 -395
  64. package/dist/matter/behaviors.d.ts +0 -1
  65. package/dist/matter/behaviors.js +0 -2
  66. package/dist/matter/clusters.d.ts +0 -1
  67. package/dist/matter/clusters.js +0 -2
  68. package/dist/matter/devices.d.ts +0 -1
  69. package/dist/matter/devices.js +0 -2
  70. package/dist/matter/endpoints.d.ts +0 -1
  71. package/dist/matter/endpoints.js +0 -2
  72. package/dist/matter/export.d.ts +0 -1
  73. package/dist/matter/export.js +0 -2
  74. package/dist/matter/types.d.ts +0 -1
  75. package/dist/matter/types.js +0 -2
  76. package/dist/matterNode.d.ts +0 -258
  77. package/dist/matterNode.js +8 -356
  78. package/dist/matterbridge.d.ts +0 -389
  79. package/dist/matterbridge.js +48 -878
  80. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
  81. package/dist/matterbridgeAccessoryPlatform.js +0 -50
  82. package/dist/matterbridgeBehaviors.d.ts +0 -24
  83. package/dist/matterbridgeBehaviors.js +5 -65
  84. package/dist/matterbridgeDeviceTypes.d.ts +0 -649
  85. package/dist/matterbridgeDeviceTypes.js +6 -673
  86. package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
  87. package/dist/matterbridgeDynamicPlatform.js +0 -50
  88. package/dist/matterbridgeEndpoint.d.ts +0 -1369
  89. package/dist/matterbridgeEndpoint.js +56 -1514
  90. package/dist/matterbridgeEndpointHelpers.d.ts +0 -425
  91. package/dist/matterbridgeEndpointHelpers.js +20 -483
  92. package/dist/matterbridgeEndpointTypes.d.ts +0 -70
  93. package/dist/matterbridgeEndpointTypes.js +0 -25
  94. package/dist/matterbridgePlatform.d.ts +0 -434
  95. package/dist/matterbridgePlatform.js +1 -473
  96. package/dist/mb_coap.d.ts +0 -23
  97. package/dist/mb_coap.js +3 -41
  98. package/dist/mb_health.d.ts +0 -67
  99. package/dist/mb_health.js +0 -70
  100. package/dist/mb_mdns.d.ts +0 -23
  101. package/dist/mb_mdns.js +36 -94
  102. package/dist/pluginManager.d.ts +0 -307
  103. package/dist/pluginManager.js +6 -346
  104. package/dist/spawn.d.ts +0 -32
  105. package/dist/spawn.js +1 -71
  106. package/dist/utils/export.d.ts +0 -1
  107. package/dist/utils/export.js +0 -1
  108. package/package.json +27 -6
  109. package/dist/cli.d.ts.map +0 -1
  110. package/dist/cli.js.map +0 -1
  111. package/dist/cliEmitter.d.ts.map +0 -1
  112. package/dist/cliEmitter.js.map +0 -1
  113. package/dist/cliHistory.d.ts.map +0 -1
  114. package/dist/cliHistory.js.map +0 -1
  115. package/dist/clusters/export.d.ts.map +0 -1
  116. package/dist/clusters/export.js.map +0 -1
  117. package/dist/crypto/attestationDecoder.d.ts +0 -180
  118. package/dist/crypto/attestationDecoder.d.ts.map +0 -1
  119. package/dist/crypto/attestationDecoder.js +0 -176
  120. package/dist/crypto/attestationDecoder.js.map +0 -1
  121. package/dist/crypto/declarationDecoder.d.ts +0 -72
  122. package/dist/crypto/declarationDecoder.d.ts.map +0 -1
  123. package/dist/crypto/declarationDecoder.js +0 -241
  124. package/dist/crypto/declarationDecoder.js.map +0 -1
  125. package/dist/crypto/extract/342/200/220cert/342/200/220extensions.d.ts +0 -9
  126. package/dist/crypto/extract/342/200/220cert/342/200/220extensions.d.ts.map +0 -1
  127. package/dist/crypto/extract/342/200/220cert/342/200/220extensions.js +0 -120
  128. package/dist/crypto/extract/342/200/220cert/342/200/220extensions.js.map +0 -1
  129. package/dist/crypto/read-extensions.d.ts +0 -2
  130. package/dist/crypto/read-extensions.d.ts.map +0 -1
  131. package/dist/crypto/read-extensions.js +0 -81
  132. package/dist/crypto/read-extensions.js.map +0 -1
  133. package/dist/crypto/testData.d.ts +0 -31
  134. package/dist/crypto/testData.d.ts.map +0 -1
  135. package/dist/crypto/testData.js +0 -131
  136. package/dist/crypto/testData.js.map +0 -1
  137. package/dist/crypto/walk-der.d.ts +0 -2
  138. package/dist/crypto/walk-der.d.ts.map +0 -1
  139. package/dist/crypto/walk-der.js +0 -165
  140. package/dist/crypto/walk-der.js.map +0 -1
  141. package/dist/deviceManager.d.ts.map +0 -1
  142. package/dist/deviceManager.js.map +0 -1
  143. package/dist/devices/airConditioner.d.ts.map +0 -1
  144. package/dist/devices/airConditioner.js.map +0 -1
  145. package/dist/devices/basicVideoPlayer.d.ts.map +0 -1
  146. package/dist/devices/basicVideoPlayer.js.map +0 -1
  147. package/dist/devices/batteryStorage.d.ts.map +0 -1
  148. package/dist/devices/batteryStorage.js.map +0 -1
  149. package/dist/devices/castingVideoPlayer.d.ts.map +0 -1
  150. package/dist/devices/castingVideoPlayer.js.map +0 -1
  151. package/dist/devices/cooktop.d.ts.map +0 -1
  152. package/dist/devices/cooktop.js.map +0 -1
  153. package/dist/devices/dishwasher.d.ts.map +0 -1
  154. package/dist/devices/dishwasher.js.map +0 -1
  155. package/dist/devices/evse.d.ts.map +0 -1
  156. package/dist/devices/evse.js.map +0 -1
  157. package/dist/devices/export.d.ts.map +0 -1
  158. package/dist/devices/export.js.map +0 -1
  159. package/dist/devices/extractorHood.d.ts.map +0 -1
  160. package/dist/devices/extractorHood.js.map +0 -1
  161. package/dist/devices/heatPump.d.ts.map +0 -1
  162. package/dist/devices/heatPump.js.map +0 -1
  163. package/dist/devices/laundryDryer.d.ts.map +0 -1
  164. package/dist/devices/laundryDryer.js.map +0 -1
  165. package/dist/devices/laundryWasher.d.ts.map +0 -1
  166. package/dist/devices/laundryWasher.js.map +0 -1
  167. package/dist/devices/microwaveOven.d.ts.map +0 -1
  168. package/dist/devices/microwaveOven.js.map +0 -1
  169. package/dist/devices/oven.d.ts.map +0 -1
  170. package/dist/devices/oven.js.map +0 -1
  171. package/dist/devices/refrigerator.d.ts.map +0 -1
  172. package/dist/devices/refrigerator.js.map +0 -1
  173. package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
  174. package/dist/devices/roboticVacuumCleaner.js.map +0 -1
  175. package/dist/devices/solarPower.d.ts.map +0 -1
  176. package/dist/devices/solarPower.js.map +0 -1
  177. package/dist/devices/speaker.d.ts.map +0 -1
  178. package/dist/devices/speaker.js.map +0 -1
  179. package/dist/devices/temperatureControl.d.ts.map +0 -1
  180. package/dist/devices/temperatureControl.js.map +0 -1
  181. package/dist/devices/waterHeater.d.ts.map +0 -1
  182. package/dist/devices/waterHeater.js.map +0 -1
  183. package/dist/dgram/export.d.ts.map +0 -1
  184. package/dist/dgram/export.js.map +0 -1
  185. package/dist/export.d.ts.map +0 -1
  186. package/dist/export.js.map +0 -1
  187. package/dist/frontend.d.ts.map +0 -1
  188. package/dist/frontend.js.map +0 -1
  189. package/dist/helpers.d.ts.map +0 -1
  190. package/dist/helpers.js.map +0 -1
  191. package/dist/jestutils/export.d.ts.map +0 -1
  192. package/dist/jestutils/export.js.map +0 -1
  193. package/dist/jestutils/jestHelpers.d.ts.map +0 -1
  194. package/dist/jestutils/jestHelpers.js.map +0 -1
  195. package/dist/matter/behaviors.d.ts.map +0 -1
  196. package/dist/matter/behaviors.js.map +0 -1
  197. package/dist/matter/clusters.d.ts.map +0 -1
  198. package/dist/matter/clusters.js.map +0 -1
  199. package/dist/matter/devices.d.ts.map +0 -1
  200. package/dist/matter/devices.js.map +0 -1
  201. package/dist/matter/endpoints.d.ts.map +0 -1
  202. package/dist/matter/endpoints.js.map +0 -1
  203. package/dist/matter/export.d.ts.map +0 -1
  204. package/dist/matter/export.js.map +0 -1
  205. package/dist/matter/types.d.ts.map +0 -1
  206. package/dist/matter/types.js.map +0 -1
  207. package/dist/matterNode.d.ts.map +0 -1
  208. package/dist/matterNode.js.map +0 -1
  209. package/dist/matterbridge.d.ts.map +0 -1
  210. package/dist/matterbridge.js.map +0 -1
  211. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  212. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  213. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  214. package/dist/matterbridgeBehaviors.js.map +0 -1
  215. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  216. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  217. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  218. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  219. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  220. package/dist/matterbridgeEndpoint.js.map +0 -1
  221. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  222. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  223. package/dist/matterbridgeEndpointTypes.d.ts.map +0 -1
  224. package/dist/matterbridgeEndpointTypes.js.map +0 -1
  225. package/dist/matterbridgePlatform.d.ts.map +0 -1
  226. package/dist/matterbridgePlatform.js.map +0 -1
  227. package/dist/mb_coap.d.ts.map +0 -1
  228. package/dist/mb_coap.js.map +0 -1
  229. package/dist/mb_health.d.ts.map +0 -1
  230. package/dist/mb_health.js.map +0 -1
  231. package/dist/mb_mdns.d.ts.map +0 -1
  232. package/dist/mb_mdns.js.map +0 -1
  233. package/dist/pluginManager.d.ts.map +0 -1
  234. package/dist/pluginManager.js.map +0 -1
  235. package/dist/spawn.d.ts.map +0 -1
  236. package/dist/spawn.js.map +0 -1
  237. package/dist/utils/export.d.ts.map +0 -1
  238. package/dist/utils/export.js.map +0 -1
  239. package/dist/workers/brand.d.ts +0 -25
  240. package/dist/workers/brand.d.ts.map +0 -1
  241. package/dist/workers/brand.extend.d.ts +0 -10
  242. package/dist/workers/brand.extend.d.ts.map +0 -1
  243. package/dist/workers/brand.extend.js +0 -15
  244. package/dist/workers/brand.extend.js.map +0 -1
  245. package/dist/workers/brand.invalid.d.ts +0 -9
  246. package/dist/workers/brand.invalid.d.ts.map +0 -1
  247. package/dist/workers/brand.invalid.js +0 -19
  248. package/dist/workers/brand.invalid.js.map +0 -1
  249. package/dist/workers/brand.js +0 -67
  250. package/dist/workers/brand.js.map +0 -1
  251. package/dist/workers/clusterTypes.d.ts +0 -47
  252. package/dist/workers/clusterTypes.d.ts.map +0 -1
  253. package/dist/workers/clusterTypes.js +0 -57
  254. package/dist/workers/clusterTypes.js.map +0 -1
  255. package/dist/workers/frontendWorker.d.ts +0 -2
  256. package/dist/workers/frontendWorker.d.ts.map +0 -1
  257. package/dist/workers/frontendWorker.js +0 -90
  258. package/dist/workers/frontendWorker.js.map +0 -1
  259. package/dist/workers/helloWorld.d.ts +0 -2
  260. package/dist/workers/helloWorld.d.ts.map +0 -1
  261. package/dist/workers/helloWorld.js +0 -135
  262. package/dist/workers/helloWorld.js.map +0 -1
  263. package/dist/workers/matterWorker.d.ts +0 -2
  264. package/dist/workers/matterWorker.d.ts.map +0 -1
  265. package/dist/workers/matterWorker.js +0 -104
  266. package/dist/workers/matterWorker.js.map +0 -1
  267. package/dist/workers/matterbridgeWorker.d.ts +0 -2
  268. package/dist/workers/matterbridgeWorker.d.ts.map +0 -1
  269. package/dist/workers/matterbridgeWorker.js +0 -75
  270. package/dist/workers/matterbridgeWorker.js.map +0 -1
  271. package/dist/workers/messageLab.d.ts +0 -134
  272. package/dist/workers/messageLab.d.ts.map +0 -1
  273. package/dist/workers/messageLab.js +0 -129
  274. package/dist/workers/messageLab.js.map +0 -1
  275. package/dist/workers/testWorker.d.ts +0 -2
  276. package/dist/workers/testWorker.d.ts.map +0 -1
  277. package/dist/workers/testWorker.js +0 -45
  278. package/dist/workers/testWorker.js.map +0 -1
  279. package/dist/workers/usage.d.ts +0 -19
  280. package/dist/workers/usage.d.ts.map +0 -1
  281. package/dist/workers/usage.js +0 -140
  282. package/dist/workers/usage.js.map +0 -1
  283. package/dist/workers/workerManager.d.ts +0 -115
  284. package/dist/workers/workerManager.d.ts.map +0 -1
  285. package/dist/workers/workerManager.js +0 -464
  286. package/dist/workers/workerManager.js.map +0 -1
  287. package/dist/workers/workerServer.d.ts +0 -126
  288. package/dist/workers/workerServer.d.ts.map +0 -1
  289. package/dist/workers/workerServer.js +0 -340
  290. package/dist/workers/workerServer.js.map +0 -1
  291. package/dist/workers/workerTypes.d.ts +0 -23
  292. package/dist/workers/workerTypes.d.ts.map +0 -1
  293. package/dist/workers/workerTypes.js +0 -3
  294. package/dist/workers/workerTypes.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.3
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 */ /* istanbul ignore next */
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';
@@ -36,7 +10,6 @@ import { FabricIndex } from '@matter/types/datatype';
36
10
  import { CommissioningOptions } from '@matter/types/commissioning';
37
11
  import { BridgedDeviceBasicInformation } from '@matter/types/clusters/bridged-device-basic-information';
38
12
  import { PowerSource } from '@matter/types/clusters/power-source';
39
- // @matterbridge
40
13
  import { createZip, formatBytes, formatPercent, formatUptime, getParameter, hasParameter, inspectError, isValidArray, isValidBoolean, isValidNumber, isValidObject, isValidString, wait, withTimeout, } from '@matterbridge/utils';
41
14
  import { MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_DIAGNOSTIC_FILE, MATTERBRIDGE_HISTORY_FILE, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg, } from '@matterbridge/types';
42
15
  import { BroadcastServer } from '@matterbridge/thread';
@@ -63,8 +36,8 @@ export class Frontend extends EventEmitter {
63
36
  this.log = new AnsiLogger({
64
37
  logName: 'Frontend',
65
38
  logNameColor: '\x1b[38;5;97m',
66
- logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */,
67
- logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */,
39
+ logTimestampFormat: 4,
40
+ logLevel: hasParameter('debug') ? "debug" : "info",
68
41
  });
69
42
  this.server = new BroadcastServer('frontend', this.log);
70
43
  this.server.on('broadcast_message', this.msgHandler.bind(this));
@@ -75,7 +48,6 @@ export class Frontend extends EventEmitter {
75
48
  }
76
49
  async msgHandler(msg) {
77
50
  if (this.server.isWorkerRequest(msg)) {
78
- // istanbul ignore else
79
51
  if (this.verbose)
80
52
  this.log.debug(`Received broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
81
53
  switch (msg.type) {
@@ -127,13 +99,11 @@ export class Frontend extends EventEmitter {
127
99
  this.server.respond({ ...msg, result: { success: true } });
128
100
  break;
129
101
  default:
130
- // istanbul ignore next
131
102
  if (this.verbose)
132
103
  this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
133
104
  }
134
105
  }
135
106
  if (this.server.isWorkerResponse(msg) && msg.result) {
136
- // istanbul ignore next
137
107
  if (this.verbose)
138
108
  this.log.debug(`Received broadcast response ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}: ${debugStringify(msg)}${db}`);
139
109
  switch (msg.type) {
@@ -177,55 +147,23 @@ export class Frontend extends EventEmitter {
177
147
  this.port = port;
178
148
  this.storedPassword = await this.matterbridge.nodeContext?.get('password', '');
179
149
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
180
- // Initialize multer with the upload directory
181
150
  const multer = await import('multer');
182
- const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads'); // Is created by matterbridge initialize
151
+ const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
183
152
  const upload = multer.default({ dest: uploadDir });
184
- // Create the express app that serves the frontend
185
153
  const express = await import('express');
186
154
  this.expressApp = express.default();
187
- // Inject logging/debug wrapper for route/middleware registration
188
- /*
189
- const methods = ['get', 'post', 'put', 'delete', 'use'];
190
- for (const method of methods) {
191
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
192
- const original = (this.expressApp as any)[method].bind(this.expressApp);
193
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
194
- (this.expressApp as any)[method] = (path: any, ...rest: any) => {
195
- try {
196
- console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
197
- return original(path, ...rest);
198
- } catch (err) {
199
- console.error(`[ERROR] Failed to register route: ${path}`);
200
- throw err;
201
- }
202
- };
203
- }
204
- */
205
- // Log all requests to the server for debugging
206
- /*
207
- this.expressApp.use((req, res, next) => {
208
- this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
209
- next();
210
- });
211
- */
212
- // Serve static files from 'frontend/build' directory
213
155
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'apps', 'frontend', 'build')));
214
- // Create a WebSocket server and attach it to the http or https server
215
156
  this.log.debug(`Creating WebSocketServer...`);
216
157
  const ws = await import('ws');
217
158
  this.webSocketServer = new ws.WebSocketServer({ noServer: true });
218
159
  this.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
219
160
  this.webSocketServer.on('connection', (ws, request) => {
220
161
  const clientIp = request.socket.remoteAddress;
221
- // Set the global logger callback for the WebSocketServer
222
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
223
- // istanbul ignore else
224
- if (this.matterbridge.getLogLevel() === "info" /* LogLevel.INFO */ || Logger.level === MatterLogLevel.INFO)
225
- callbackLogLevel = "info" /* LogLevel.INFO */;
226
- // istanbul ignore else
227
- if (this.matterbridge.getLogLevel() === "debug" /* LogLevel.DEBUG */ || Logger.level === MatterLogLevel.DEBUG)
228
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
162
+ let callbackLogLevel = "notice";
163
+ if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
164
+ callbackLogLevel = "info";
165
+ if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
166
+ callbackLogLevel = "debug";
229
167
  AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
230
168
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
231
169
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -241,34 +179,23 @@ export class Frontend extends EventEmitter {
241
179
  });
242
180
  ws.on('close', () => {
243
181
  this.log.info('WebSocket client disconnected');
244
- // istanbul ignore else
245
182
  if (this.webSocketServer?.clients.size === 0) {
246
183
  AnsiLogger.setGlobalCallback(undefined);
247
184
  this.log.debug('All WebSocket clients disconnected. WebSocketServer logger global callback removed');
248
185
  this.authClients = [];
249
186
  }
250
187
  });
251
- // istanbul ignore next
252
188
  ws.on('error', (error) => {
253
- // istanbul ignore next
254
189
  this.log.error(`WebSocket client error: ${error}`);
255
190
  });
256
191
  });
257
192
  this.webSocketServer.on('close', () => {
258
193
  this.log.debug(`WebSocketServer closed`);
259
194
  });
260
- /* With { noServer: true } it never fires
261
- this.webSocketServer.on('listening', () => {
262
- this.log.info(`The WebSocketServer is listening`);
263
- this.emit('websocket_server_listening', hasParameter('ssl') ? 'wss' : 'ws');
264
- });
265
- */
266
- // istanbul ignore next
267
195
  this.webSocketServer.on('error', (ws, error) => {
268
196
  this.log.error(`WebSocketServer error: ${error}`);
269
197
  });
270
198
  if (!hasParameter('ssl')) {
271
- // Create an HTTP server and attach the express app
272
199
  const http = await import('node:http');
273
200
  try {
274
201
  this.log.debug(`Creating HTTP server...`);
@@ -279,9 +206,7 @@ export class Frontend extends EventEmitter {
279
206
  this.emit('server_error', error);
280
207
  return;
281
208
  }
282
- // Listen on the specified port
283
209
  if (hasParameter('ingress')) {
284
- // We limit to all ipv4 addresses when running in ingress mode (Home Assistant add-on)
285
210
  this.httpServer.listen(this.port, '0.0.0.0', () => {
286
211
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
287
212
  this.listening = true;
@@ -289,17 +214,13 @@ export class Frontend extends EventEmitter {
289
214
  });
290
215
  }
291
216
  else {
292
- // We listen to all available addresses
293
217
  this.httpServer.listen(this.port, getParameter('bind'), () => {
294
218
  const addr = this.httpServer?.address();
295
- // istanbul ignore else
296
219
  if (addr && typeof addr !== 'string') {
297
220
  this.log.info(`The frontend http server is bound to ${addr.family} ${addr.address}:${addr.port}`);
298
221
  }
299
- // istanbul ignore else
300
222
  if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
301
223
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
302
- // istanbul ignore else
303
224
  if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
304
225
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
305
226
  this.listening = true;
@@ -308,25 +229,19 @@ export class Frontend extends EventEmitter {
308
229
  }
309
230
  this.httpServer.on('upgrade', async (req, socket, head) => {
310
231
  try {
311
- // Only proceed for real WebSocket upgrades
312
- // istanbul ignore next cause is only a safety check
313
232
  if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
314
233
  this.log.error(`WebSocket upgrade error: Invalid upgrade header ${req.headers.upgrade}`);
315
234
  socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
316
235
  return socket.destroy();
317
236
  }
318
- // Build a URL so we can read ?password=...
319
237
  const url = new URL(req.url ?? '/', `http://${req.headers.host || 'localhost'}`);
320
- // Validate WebSocket password
321
238
  const password = url.searchParams.get('password') ?? '';
322
239
  if (password !== this.storedPassword) {
323
240
  this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
324
241
  socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
325
242
  return socket.destroy();
326
243
  }
327
- // Complete the WebSocket handshake
328
244
  this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
329
- // istanbul ignore else
330
245
  if (req.socket.remoteAddress)
331
246
  this.authClients.push(req.socket.remoteAddress);
332
247
  this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
@@ -334,7 +249,6 @@ export class Frontend extends EventEmitter {
334
249
  });
335
250
  }
336
251
  catch (err) {
337
- /* istanbul ignore next: only triggered on unexpected internal error */
338
252
  {
339
253
  inspectError(this.log, 'WebSocket upgrade error:', err);
340
254
  socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
@@ -357,7 +271,6 @@ export class Frontend extends EventEmitter {
357
271
  });
358
272
  }
359
273
  else {
360
- // SSL is enabled, load the certificate and the private key
361
274
  let cert;
362
275
  let key;
363
276
  let ca;
@@ -367,7 +280,6 @@ export class Frontend extends EventEmitter {
367
280
  let httpsServerOptions = {};
368
281
  const fs = await import('node:fs');
369
282
  if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'))) {
370
- // Load the p12 certificate and the passphrase
371
283
  try {
372
284
  pfx = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'));
373
285
  this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12')}`);
@@ -379,7 +291,7 @@ export class Frontend extends EventEmitter {
379
291
  }
380
292
  try {
381
293
  passphrase = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass'), 'utf8');
382
- passphrase = passphrase.trim(); // Ensure no extra characters
294
+ passphrase = passphrase.trim();
383
295
  this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass')}`);
384
296
  }
385
297
  catch (error) {
@@ -390,7 +302,6 @@ export class Frontend extends EventEmitter {
390
302
  httpsServerOptions = { pfx, passphrase };
391
303
  }
392
304
  else {
393
- // 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.
394
305
  try {
395
306
  cert = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem'), 'utf8');
396
307
  this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem')}`);
@@ -420,10 +331,9 @@ export class Frontend extends EventEmitter {
420
331
  httpsServerOptions = { cert: fullChain ?? cert, key, ca };
421
332
  }
422
333
  if (hasParameter('mtls')) {
423
- httpsServerOptions.requestCert = true; // Request client certificate
424
- httpsServerOptions.rejectUnauthorized = true; // Require client certificate validation
334
+ httpsServerOptions.requestCert = true;
335
+ httpsServerOptions.rejectUnauthorized = true;
425
336
  }
426
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
427
337
  const https = await import('node:https');
428
338
  try {
429
339
  this.log.debug(`Creating HTTPS server...`);
@@ -434,9 +344,7 @@ export class Frontend extends EventEmitter {
434
344
  this.emit('server_error', error);
435
345
  return;
436
346
  }
437
- // Listen on the specified port
438
347
  if (hasParameter('ingress')) {
439
- // We limit to all ipv4 addresses when running in ingress mode (Home Assistant add-on)
440
348
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
441
349
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
442
350
  this.listening = true;
@@ -444,17 +352,13 @@ export class Frontend extends EventEmitter {
444
352
  });
445
353
  }
446
354
  else {
447
- // We listen to all available addresses
448
355
  this.httpsServer.listen(this.port, getParameter('bind'), () => {
449
356
  const addr = this.httpsServer?.address();
450
- // istanbul ignore else
451
357
  if (addr && typeof addr !== 'string') {
452
358
  this.log.info(`The frontend https server is bound to ${addr.family} ${addr.address}:${addr.port}`);
453
359
  }
454
- // istanbul ignore else
455
360
  if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
456
361
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
457
- // istanbul ignore else
458
362
  if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
459
363
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
460
364
  this.listening = true;
@@ -463,24 +367,18 @@ export class Frontend extends EventEmitter {
463
367
  }
464
368
  this.httpsServer.on('upgrade', async (req, socket, head) => {
465
369
  try {
466
- // Only proceed for real WebSocket upgrades
467
- // istanbul ignore next cause is only a safety check
468
370
  if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
469
371
  socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
470
372
  return socket.destroy();
471
373
  }
472
- // Build a URL so we can read ?password=...
473
374
  const url = new URL(req.url ?? '/', `https://${req.headers.host || 'localhost'}`);
474
- // Validate WebSocket password
475
375
  const password = url.searchParams.get('password') ?? '';
476
376
  if (password !== this.storedPassword) {
477
377
  this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
478
378
  socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
479
379
  return socket.destroy();
480
380
  }
481
- // Complete the WebSocket handshake
482
381
  this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
483
- // istanbul ignore else
484
382
  if (req.socket.remoteAddress)
485
383
  this.authClients.push(req.socket.remoteAddress);
486
384
  this.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
@@ -488,7 +386,6 @@ export class Frontend extends EventEmitter {
488
386
  });
489
387
  }
490
388
  catch (err) {
491
- /* istanbul ignore next: only triggered on unexpected internal error */
492
389
  {
493
390
  inspectError(this.log, 'WebSocket upgrade error:', err);
494
391
  socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
@@ -510,7 +407,6 @@ export class Frontend extends EventEmitter {
510
407
  return;
511
408
  });
512
409
  }
513
- // Subscribe to cli events
514
410
  cliEmitter.removeAllListeners();
515
411
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
516
412
  this.wssSendUptimeUpdate(systemUptime, processUptime);
@@ -521,8 +417,6 @@ export class Frontend extends EventEmitter {
521
417
  cliEmitter.on('cpu', (cpuUsage, processCpuUsage) => {
522
418
  this.wssSendCpuUpdate(cpuUsage, processCpuUsage);
523
419
  });
524
- // Endpoint to validate login code
525
- // curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
526
420
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
527
421
  const { password } = req.body;
528
422
  this.log.debug(`The frontend sent /api/login with password ${password ? '[redacted]' : '(empty)'}`);
@@ -537,20 +431,17 @@ export class Frontend extends EventEmitter {
537
431
  res.json({ valid: false });
538
432
  }
539
433
  });
540
- // Endpoint to provide health check for docker
541
434
  this.expressApp.get('/health', (req, res) => {
542
435
  this.log.debug('Express received /health');
543
436
  const healthStatus = {
544
- status: 'ok', // Indicate service is healthy
545
- uptime: process.uptime(), // Server uptime in seconds
546
- timestamp: new Date().toISOString(), // Current timestamp
437
+ status: 'ok',
438
+ uptime: process.uptime(),
439
+ timestamp: new Date().toISOString(),
547
440
  };
548
441
  res.status(200).json(healthStatus);
549
442
  });
550
- // Endpoint to provide memory usage details
551
443
  this.expressApp.get('/memory', async (req, res) => {
552
444
  this.log.debug('Express received /memory');
553
- // Memory usage from process
554
445
  const memoryUsageRaw = process.memoryUsage();
555
446
  const memoryUsage = {
556
447
  rss: formatBytes(memoryUsageRaw.rss),
@@ -559,13 +450,10 @@ export class Frontend extends EventEmitter {
559
450
  external: formatBytes(memoryUsageRaw.external),
560
451
  arrayBuffers: formatBytes(memoryUsageRaw.arrayBuffers),
561
452
  };
562
- // V8 heap statistics
563
453
  const { default: v8 } = await import('node:v8');
564
454
  const heapStatsRaw = v8.getHeapStatistics();
565
455
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
566
- // Format heapStats
567
456
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, formatBytes(value)]));
568
- // Format heapSpaces
569
457
  const heapSpaces = heapSpacesRaw.map((space) => ({
570
458
  ...space,
571
459
  space_size: formatBytes(space.space_size),
@@ -584,28 +472,24 @@ export class Frontend extends EventEmitter {
584
472
  };
585
473
  res.status(200).json(memoryReport);
586
474
  });
587
- // Endpoint to provide settings
588
475
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
589
476
  this.log.debug('The frontend sent /api/settings');
590
477
  if (!this.validateReq(req, res))
591
478
  return;
592
479
  res.json(await this.getApiSettings());
593
480
  });
594
- // Endpoint to provide plugins
595
481
  this.expressApp.get('/api/plugins', async (req, res) => {
596
482
  this.log.debug('The frontend sent /api/plugins');
597
483
  if (!this.validateReq(req, res))
598
484
  return;
599
485
  res.json(this.matterbridge.hasCleanupStarted ? [] : this.getPlugins());
600
486
  });
601
- // Endpoint to provide devices
602
487
  this.expressApp.get('/api/devices', async (req, res) => {
603
488
  this.log.debug('The frontend sent /api/devices');
604
489
  if (!this.validateReq(req, res))
605
490
  return;
606
491
  res.json(this.matterbridge.hasCleanupStarted ? [] : this.getDevices());
607
492
  });
608
- // Endpoint to view the matterbridge log
609
493
  this.expressApp.get('/api/view-mblog', async (req, res) => {
610
494
  this.log.debug('The frontend sent /api/view-mblog');
611
495
  if (!this.validateReq(req, res))
@@ -621,7 +505,6 @@ export class Frontend extends EventEmitter {
621
505
  res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
622
506
  }
623
507
  });
624
- // Endpoint to view the matter.js log
625
508
  this.expressApp.get('/api/view-mjlog', async (req, res) => {
626
509
  this.log.debug('The frontend sent /api/view-mjlog');
627
510
  if (!this.validateReq(req, res))
@@ -637,7 +520,6 @@ export class Frontend extends EventEmitter {
637
520
  res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
638
521
  }
639
522
  });
640
- // Endpoint to view the diagnostic.log
641
523
  this.expressApp.get('/api/view-diagnostic', async (req, res) => {
642
524
  this.log.debug('The frontend sent /api/view-diagnostic');
643
525
  if (!this.validateReq(req, res))
@@ -650,13 +532,10 @@ export class Frontend extends EventEmitter {
650
532
  res.send(data.slice(29));
651
533
  }
652
534
  catch (error) {
653
- // istanbul ignore next
654
535
  this.log.error(`Error reading diagnostic log file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
655
- // istanbul ignore next
656
536
  res.status(500).send('Error reading diagnostic log file.');
657
537
  }
658
538
  });
659
- // Endpoint to download the diagnostic.log
660
539
  this.expressApp.get('/api/download-diagnostic', async (req, res) => {
661
540
  this.log.debug(`The frontend sent /api/download-diagnostic`);
662
541
  if (!this.validateReq(req, res))
@@ -669,19 +548,16 @@ export class Frontend extends EventEmitter {
669
548
  await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), data, 'utf-8');
670
549
  }
671
550
  catch (error) {
672
- // istanbul ignore next
673
551
  this.log.debug(`Error in /api/download-diagnostic: ${error instanceof Error ? error.message : error}`);
674
552
  }
675
553
  res.type('text/plain');
676
554
  res.download(path.join(os.tmpdir(), MATTERBRIDGE_DIAGNOSTIC_FILE), MATTERBRIDGE_DIAGNOSTIC_FILE, (error) => {
677
- /* istanbul ignore if */
678
555
  if (error) {
679
556
  this.log.error(`Error downloading file ${MATTERBRIDGE_DIAGNOSTIC_FILE}: ${error instanceof Error ? error.message : error}`);
680
557
  res.status(500).send('Error downloading the diagnostic log file');
681
558
  }
682
559
  });
683
560
  });
684
- // Endpoint to view the history.html
685
561
  this.expressApp.get('/api/viewhistory', async (req, res) => {
686
562
  this.log.debug('The frontend sent /api/viewhistory');
687
563
  if (!this.validateReq(req, res))
@@ -697,7 +573,6 @@ export class Frontend extends EventEmitter {
697
573
  res.status(500).send('Error reading history file.');
698
574
  }
699
575
  });
700
- // Endpoint to download the history.html
701
576
  this.expressApp.get('/api/downloadhistory', async (req, res) => {
702
577
  this.log.debug(`The frontend sent /api/downloadhistory`);
703
578
  if (!this.validateReq(req, res))
@@ -709,7 +584,6 @@ export class Frontend extends EventEmitter {
709
584
  await fs.promises.writeFile(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), data, 'utf-8');
710
585
  res.type('text/plain');
711
586
  res.download(path.join(os.tmpdir(), MATTERBRIDGE_HISTORY_FILE), MATTERBRIDGE_HISTORY_FILE, (error) => {
712
- /* istanbul ignore if */
713
587
  if (error) {
714
588
  this.log.error(`Error in /api/downloadhistory downloading history file ${MATTERBRIDGE_HISTORY_FILE}: ${error instanceof Error ? error.message : error}`);
715
589
  res.status(500).send('Error downloading history file');
@@ -721,7 +595,6 @@ export class Frontend extends EventEmitter {
721
595
  res.status(500).send('Error reading history file.');
722
596
  }
723
597
  });
724
- // Endpoint to view the shelly log
725
598
  this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
726
599
  this.log.debug('The frontend sent /api/shellyviewsystemlog');
727
600
  if (!this.validateReq(req, res))
@@ -737,7 +610,6 @@ export class Frontend extends EventEmitter {
737
610
  res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
738
611
  }
739
612
  });
740
- // Endpoint to download the matterbridge log
741
613
  this.expressApp.get('/api/download-mblog', async (req, res) => {
742
614
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
743
615
  if (!this.validateReq(req, res))
@@ -754,14 +626,12 @@ export class Frontend extends EventEmitter {
754
626
  }
755
627
  res.type('text/plain');
756
628
  res.download(path.join(os.tmpdir(), MATTERBRIDGE_LOGGER_FILE), 'matterbridge.log', (error) => {
757
- /* istanbul ignore if */
758
629
  if (error) {
759
630
  this.log.error(`Error downloading log file ${MATTERBRIDGE_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
760
631
  res.status(500).send('Error downloading the matterbridge log file');
761
632
  }
762
633
  });
763
634
  });
764
- // Endpoint to download the matter log
765
635
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
766
636
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE)}`);
767
637
  if (!this.validateReq(req, res))
@@ -778,14 +648,12 @@ export class Frontend extends EventEmitter {
778
648
  }
779
649
  res.type('text/plain');
780
650
  res.download(path.join(os.tmpdir(), MATTER_LOGGER_FILE), 'matter.log', (error) => {
781
- /* istanbul ignore if */
782
651
  if (error) {
783
652
  this.log.error(`Error downloading log file ${MATTER_LOGGER_FILE}: ${error instanceof Error ? error.message : error}`);
784
653
  res.status(500).send('Error downloading the matter log file');
785
654
  }
786
655
  });
787
656
  });
788
- // Endpoint to download the shelly log
789
657
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
790
658
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
791
659
  if (!this.validateReq(req, res))
@@ -802,103 +670,87 @@ export class Frontend extends EventEmitter {
802
670
  }
803
671
  res.type('text/plain');
804
672
  res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
805
- /* istanbul ignore if */
806
673
  if (error) {
807
674
  this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
808
675
  res.status(500).send('Error downloading Shelly system log file');
809
676
  }
810
677
  });
811
678
  });
812
- // Endpoint to download the matterbridge storage directory
813
679
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
814
680
  this.log.debug('The frontend sent /api/download-mbstorage');
815
681
  if (!this.validateReq(req, res))
816
682
  return;
817
683
  await createZip(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), path.join(this.matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR));
818
684
  res.download(path.join(os.tmpdir(), `matterbridge.${NODE_STORAGE_DIR}.zip`), `matterbridge.${NODE_STORAGE_DIR}.zip`, (error) => {
819
- /* istanbul ignore if */
820
685
  if (error) {
821
686
  this.log.error(`Error downloading file ${`matterbridge.${NODE_STORAGE_DIR}.zip`}: ${error instanceof Error ? error.message : error}`);
822
687
  res.status(500).send('Error downloading the matterbridge storage file');
823
688
  }
824
689
  });
825
690
  });
826
- // Endpoint to download the matter storage file
827
691
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
828
692
  this.log.debug('The frontend sent /api/download-mjstorage');
829
693
  if (!this.validateReq(req, res))
830
694
  return;
831
695
  await createZip(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME));
832
696
  res.download(path.join(os.tmpdir(), `matterbridge.${MATTER_STORAGE_NAME}.zip`), `matterbridge.${MATTER_STORAGE_NAME}.zip`, (error) => {
833
- /* istanbul ignore if */
834
697
  if (error) {
835
698
  this.log.error(`Error downloading the matter storage matterbridge.${MATTER_STORAGE_NAME}.zip: ${error instanceof Error ? error.message : error}`);
836
699
  res.status(500).send('Error downloading the matter storage zip file');
837
700
  }
838
701
  });
839
702
  });
840
- // Endpoint to download the matterbridge plugin directory
841
703
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
842
704
  this.log.debug('The frontend sent /api/download-pluginstorage');
843
705
  if (!this.validateReq(req, res))
844
706
  return;
845
707
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
846
708
  res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
847
- /* istanbul ignore if */
848
709
  if (error) {
849
710
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
850
711
  res.status(500).send('Error downloading the matterbridge plugin storage file');
851
712
  }
852
713
  });
853
714
  });
854
- // Endpoint to download the matterbridge plugin config files
855
715
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
856
716
  this.log.debug('The frontend sent /api/download-pluginconfig');
857
717
  if (!this.validateReq(req, res))
858
718
  return;
859
719
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
860
720
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
861
- /* istanbul ignore if */
862
721
  if (error) {
863
722
  this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
864
723
  res.status(500).send('Error downloading the matterbridge plugin config file');
865
724
  }
866
725
  });
867
726
  });
868
- // Endpoint to download the matterbridge backup (created with the backup command)
869
727
  this.expressApp.get('/api/download-backup', async (req, res) => {
870
728
  this.log.debug('The frontend sent /api/download-backup');
871
729
  if (!this.validateReq(req, res))
872
730
  return;
873
731
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
874
- /* istanbul ignore if */
875
732
  if (error) {
876
733
  this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
877
734
  res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
878
735
  }
879
736
  });
880
737
  });
881
- // Endpoint to upload a package
882
738
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
883
739
  if (!this.validateReq(req, res))
884
740
  return;
885
741
  const { filename } = req.body;
886
742
  const file = req.file;
887
- /* istanbul ignore if */
888
743
  if (!file || !filename) {
889
744
  this.log.error(`uploadpackage: invalid request: file and filename are required`);
890
745
  res.status(400).send('Invalid request: file and filename are required');
891
746
  return;
892
747
  }
893
748
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
894
- // Define the path where the plugin file will be saved
895
749
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
896
750
  try {
897
- // Move the uploaded file to the specified path
898
751
  const fs = await import('node:fs');
899
752
  await fs.promises.rename(file.path, filePath);
900
753
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
901
- // Install the plugin package
902
754
  if (filename.endsWith('.tgz')) {
903
755
  const { spawnCommand } = await import('./spawn.js');
904
756
  if (await spawnCommand('npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], 'install', filename)) {
@@ -926,7 +778,6 @@ export class Frontend extends EventEmitter {
926
778
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
927
779
  }
928
780
  });
929
- // Fallback for routing (must be the last route)
930
781
  this.expressApp.use((req, res) => {
931
782
  const filePath = path.resolve(this.matterbridge.rootDirectory, 'apps', 'frontend', 'build');
932
783
  this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html in ${filePath} as fallback`);
@@ -937,16 +788,13 @@ export class Frontend extends EventEmitter {
937
788
  async stop() {
938
789
  this.log.debug('Stopping the frontend...');
939
790
  const ws = await import('ws');
940
- // Remove listeners from the express app
941
791
  if (this.expressApp) {
942
792
  this.expressApp.removeAllListeners();
943
793
  this.expressApp = undefined;
944
794
  this.log.debug('Frontend app closed successfully');
945
795
  }
946
- // Close the WebSocket server
947
796
  if (this.webSocketServer) {
948
797
  this.log.debug('Closing WebSocket server...');
949
- // Close all active connections
950
798
  this.webSocketServer.clients.forEach((client) => {
951
799
  if (client.readyState === ws.WebSocket.OPEN) {
952
800
  client.close();
@@ -954,9 +802,7 @@ export class Frontend extends EventEmitter {
954
802
  });
955
803
  await withTimeout(new Promise((resolve) => {
956
804
  this.webSocketServer?.close((error) => {
957
- // istanbul ignore if
958
805
  if (error) {
959
- // istanbul ignore next
960
806
  this.log.error(`Error closing WebSocket server: ${error}`);
961
807
  }
962
808
  else {
@@ -969,27 +815,8 @@ export class Frontend extends EventEmitter {
969
815
  this.webSocketServer.removeAllListeners();
970
816
  this.webSocketServer = undefined;
971
817
  }
972
- // Close the http server
973
818
  if (this.httpServer) {
974
819
  this.log.debug('Closing http server...');
975
- /*
976
- await withTimeout(
977
- new Promise<void>((resolve) => {
978
- this.httpServer?.close((error) => {
979
- if (error) {
980
- // istanbul ignore next
981
- this.log.error(`Error closing http server: ${error}`);
982
- } else {
983
- this.log.debug('Http server closed successfully');
984
- this.emit('server_stopped');
985
- }
986
- resolve();
987
- });
988
- }),
989
- 5000,
990
- false,
991
- );
992
- */
993
820
  this.httpServer.close();
994
821
  this.log.debug('Http server closed successfully');
995
822
  this.listening = false;
@@ -998,27 +825,8 @@ export class Frontend extends EventEmitter {
998
825
  this.httpServer = undefined;
999
826
  this.log.debug('Frontend http server closed successfully');
1000
827
  }
1001
- // Close the https server
1002
828
  if (this.httpsServer) {
1003
829
  this.log.debug('Closing https server...');
1004
- /*
1005
- await withTimeout(
1006
- new Promise<void>((resolve) => {
1007
- this.httpsServer?.close((error) => {
1008
- if (error) {
1009
- // istanbul ignore next
1010
- this.log.error(`Error closing https server: ${error}`);
1011
- } else {
1012
- this.log.debug('Https server closed successfully');
1013
- this.emit('server_stopped');
1014
- }
1015
- resolve();
1016
- });
1017
- }),
1018
- 5000,
1019
- false,
1020
- );
1021
- */
1022
830
  this.httpsServer.close();
1023
831
  this.log.debug('Https server closed successfully');
1024
832
  this.listening = false;
@@ -1029,13 +837,7 @@ export class Frontend extends EventEmitter {
1029
837
  }
1030
838
  this.log.debug('Frontend stopped successfully');
1031
839
  }
1032
- /**
1033
- * Retrieves the api settings data.
1034
- *
1035
- * @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
1036
- */
1037
840
  async getApiSettings() {
1038
- // Update the variable system information properties
1039
841
  this.matterbridge.systemInformation.totalMemory = formatBytes(os.totalmem());
1040
842
  this.matterbridge.systemInformation.freeMemory = formatBytes(os.freemem());
1041
843
  this.matterbridge.systemInformation.systemUptime = formatUptime(os.uptime());
@@ -1045,7 +847,6 @@ export class Frontend extends EventEmitter {
1045
847
  this.matterbridge.systemInformation.rss = formatBytes(process.memoryUsage().rss);
1046
848
  this.matterbridge.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
1047
849
  this.matterbridge.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
1048
- // Create the matterbridge information
1049
850
  const info = {
1050
851
  homeDirectory: this.matterbridge.homeDirectory,
1051
852
  rootDirectory: this.matterbridge.rootDirectory,
@@ -1081,15 +882,9 @@ export class Frontend extends EventEmitter {
1081
882
  };
1082
883
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: info };
1083
884
  }
1084
- /**
1085
- * Retrieves the reachable attribute.
1086
- *
1087
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
1088
- * @returns {boolean} The reachable attribute.
1089
- */
1090
885
  getReachability(device) {
1091
886
  if (this.matterbridge.hasCleanupStarted)
1092
- return false; // Skip if cleanup has started
887
+ return false;
1093
888
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
1094
889
  return false;
1095
890
  if (device.hasClusterServer(BridgedDeviceBasicInformation.Cluster.id))
@@ -1100,15 +895,9 @@ export class Frontend extends EventEmitter {
1100
895
  return true;
1101
896
  return false;
1102
897
  }
1103
- /**
1104
- * Retrieves the power source attribute.
1105
- *
1106
- * @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
1107
- * @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
1108
- */
1109
898
  getPowerSource(endpoint) {
1110
899
  if (this.matterbridge.hasCleanupStarted)
1111
- return undefined; // Skip if cleanup has started
900
+ return undefined;
1112
901
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
1113
902
  return undefined;
1114
903
  const powerSource = (device) => {
@@ -1123,25 +912,16 @@ export class Frontend extends EventEmitter {
1123
912
  }
1124
913
  return;
1125
914
  };
1126
- // Root endpoint
1127
915
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
1128
916
  return powerSource(endpoint);
1129
- // Child endpoints
1130
917
  for (const child of endpoint.getChildEndpoints()) {
1131
- // istanbul ignore else
1132
918
  if (child.hasClusterServer(PowerSource.Cluster.id))
1133
919
  return powerSource(child);
1134
920
  }
1135
921
  }
1136
- /**
1137
- * Retrieves the battery level attribute.
1138
- *
1139
- * @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
1140
- * @returns {number | undefined} The battery level attribute.
1141
- */
1142
922
  getBatteryLevel(endpoint) {
1143
923
  if (this.matterbridge.hasCleanupStarted)
1144
- return undefined; // Skip if cleanup has started
924
+ return undefined;
1145
925
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
1146
926
  return undefined;
1147
927
  const batteryLevel = (device) => {
@@ -1152,27 +932,16 @@ export class Frontend extends EventEmitter {
1152
932
  }
1153
933
  return undefined;
1154
934
  };
1155
- // Root endpoint
1156
935
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
1157
936
  return batteryLevel(endpoint);
1158
- // Child endpoints
1159
937
  for (const child of endpoint.getChildEndpoints()) {
1160
- // istanbul ignore else
1161
938
  if (child.hasClusterServer(PowerSource.Cluster.id))
1162
939
  return batteryLevel(child);
1163
940
  }
1164
941
  }
1165
- /**
1166
- * Retrieves the cluster text description from a given device.
1167
- * The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
1168
- *
1169
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
1170
- * @returns {string} The attributes description of the cluster servers in the device.
1171
- */
1172
942
  getClusterTextFromDevice(device) {
1173
943
  if (this.matterbridge.hasCleanupStarted)
1174
- return ''; // Skip if cleanup has started
1175
- // istanbul ignore else
944
+ return '';
1176
945
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
1177
946
  return '';
1178
947
  const getUserLabel = (device) => {
@@ -1182,7 +951,6 @@ export class Frontend extends EventEmitter {
1182
951
  if (composed)
1183
952
  return 'Composed: ' + composed.value;
1184
953
  }
1185
- // istanbul ignore next cause is not reachable
1186
954
  return '';
1187
955
  };
1188
956
  const getFixedLabel = (device) => {
@@ -1192,13 +960,11 @@ export class Frontend extends EventEmitter {
1192
960
  if (composed)
1193
961
  return 'Composed: ' + composed.value;
1194
962
  }
1195
- // istanbul ignore next cause is not reacheable
1196
963
  return '';
1197
964
  };
1198
965
  let attributes = '';
1199
966
  let supportedModes = [];
1200
967
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1201
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
1202
968
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1203
969
  return;
1204
970
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -1288,17 +1054,11 @@ export class Frontend extends EventEmitter {
1288
1054
  if (clusterName === 'userLabel' && attributeName === 'labelList')
1289
1055
  attributes += `${getUserLabel(device)} `;
1290
1056
  });
1291
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
1292
1057
  return attributes.trimStart().trimEnd();
1293
1058
  }
1294
- /**
1295
- * Retrieves the registered plugins sanitized for res.json().
1296
- *
1297
- * @returns {ApiPlugin[]} An array of BaseRegisteredPlugin.
1298
- */
1299
1059
  getPlugins() {
1300
1060
  if (this.matterbridge.hasCleanupStarted)
1301
- return []; // Skip if cleanup has started
1061
+ return [];
1302
1062
  const plugins = [];
1303
1063
  for (const plugin of this.matterbridge.plugins.array()) {
1304
1064
  plugins.push({
@@ -1326,27 +1086,18 @@ export class Frontend extends EventEmitter {
1326
1086
  schemaJson: plugin.schemaJson,
1327
1087
  hasWhiteList: plugin.configJson?.whiteList !== undefined,
1328
1088
  hasBlackList: plugin.configJson?.blackList !== undefined,
1329
- // Childbridge mode specific data
1330
1089
  matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
1331
1090
  });
1332
1091
  }
1333
1092
  return plugins;
1334
1093
  }
1335
- /**
1336
- * Retrieves the devices from Matterbridge.
1337
- *
1338
- * @param {string} [pluginName] - The name of the plugin to filter devices by.
1339
- * @returns {ApiDevice[]} An array of ApiDevices for the frontend.
1340
- */
1341
1094
  getDevices(pluginName) {
1342
1095
  if (this.matterbridge.hasCleanupStarted)
1343
- return []; // Skip if cleanup has started
1096
+ return [];
1344
1097
  const devices = [];
1345
1098
  for (const device of this.matterbridge.devices.array()) {
1346
- // Filter by pluginName if provided
1347
1099
  if (pluginName && pluginName !== device.plugin)
1348
1100
  continue;
1349
- // Check if the device has the required properties
1350
1101
  if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1351
1102
  continue;
1352
1103
  devices.push({
@@ -1367,40 +1118,24 @@ export class Frontend extends EventEmitter {
1367
1118
  }
1368
1119
  return devices;
1369
1120
  }
1370
- /**
1371
- * Retrieves the clusters from a given plugin and endpoint number.
1372
- *
1373
- * Response for /api/clusters
1374
- *
1375
- * @param {string} pluginName - The name of the plugin.
1376
- * @param {number} endpointNumber - The endpoint number.
1377
- * @returns {ApiClusters | undefined} A promise that resolves to the clusters or undefined if not found.
1378
- */
1379
1121
  getClusters(pluginName, endpointNumber) {
1380
1122
  if (this.matterbridge.hasCleanupStarted)
1381
- return; // Skip if cleanup has started
1123
+ return;
1382
1124
  const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
1383
1125
  if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.maybeId || !endpoint.deviceName || !endpoint.serialNumber) {
1384
1126
  this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1385
1127
  return;
1386
1128
  }
1387
- // this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
1388
- // Get the device types from the main endpoint
1389
1129
  const deviceTypes = [];
1390
1130
  const clusters = [];
1391
1131
  endpoint.state.descriptor.deviceTypeList.forEach((d) => {
1392
1132
  deviceTypes.push(d.deviceType);
1393
1133
  });
1394
- // Get the clusters from the main endpoint
1395
1134
  endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1396
1135
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1397
1136
  return;
1398
- // istanbul ignore if cause is not reachable without the EveHistory cluster
1399
1137
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1400
1138
  return;
1401
- // console.log(
1402
- // `${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}`,
1403
- // );
1404
1139
  clusters.push({
1405
1140
  endpoint: endpoint.number.toString(),
1406
1141
  number: endpoint.number,
@@ -1414,19 +1149,12 @@ export class Frontend extends EventEmitter {
1414
1149
  attributeLocalValue: attributeValue,
1415
1150
  });
1416
1151
  });
1417
- // Get the child endpoints
1418
1152
  const childEndpoints = endpoint.getChildEndpoints();
1419
- // if (childEndpoints.length === 0) {
1420
- // this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
1421
- // }
1422
1153
  childEndpoints.forEach((childEndpoint) => {
1423
- // istanbul ignore if cause is not reachable: should never happen but ...
1424
1154
  if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
1425
1155
  this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1426
1156
  return;
1427
1157
  }
1428
- // this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
1429
- // Get the device types of the child endpoint
1430
1158
  const deviceTypes = [];
1431
1159
  childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
1432
1160
  deviceTypes.push(d.deviceType);
@@ -1434,13 +1162,9 @@ export class Frontend extends EventEmitter {
1434
1162
  childEndpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1435
1163
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1436
1164
  return;
1437
- // istanbul ignore if cause is not reachable without the EveHistory cluster
1438
1165
  if (clusterName === 'EveHistory' &&
1439
1166
  ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1440
1167
  return;
1441
- // console.log(
1442
- // `${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}`,
1443
- // );
1444
1168
  clusters.push({
1445
1169
  endpoint: childEndpoint.number.toString(),
1446
1170
  number: childEndpoint.number,
@@ -1460,7 +1184,6 @@ export class Frontend extends EventEmitter {
1460
1184
  async generateDiagnostic() {
1461
1185
  this.log.debug('Generating diagnostic...');
1462
1186
  const serverNodes = [];
1463
- // istanbul ignore else
1464
1187
  if (this.matterbridge.bridgeMode === 'bridge') {
1465
1188
  if (this.matterbridge.serverNode)
1466
1189
  serverNodes.push(this.matterbridge.serverNode);
@@ -1471,7 +1194,6 @@ export class Frontend extends EventEmitter {
1471
1194
  serverNodes.push(plugin.serverNode);
1472
1195
  }
1473
1196
  }
1474
- // istanbul ignore next
1475
1197
  for (const device of this.matterbridge.devices.array()) {
1476
1198
  if (device.serverNode)
1477
1199
  serverNodes.push(device.serverNode);
@@ -1495,15 +1217,8 @@ export class Frontend extends EventEmitter {
1495
1217
  values: [...serverNodes],
1496
1218
  })));
1497
1219
  delete Logger.destinations.diagnostic;
1498
- await wait(500); // Wait for the log to be written
1220
+ await wait(500);
1499
1221
  }
1500
- /**
1501
- * Handles incoming websocket api request messages from the Matterbridge frontend.
1502
- *
1503
- * @param {WebSocket} client - The websocket client that sent the message.
1504
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1505
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1506
- */
1507
1222
  async wsMessageHandler(client, message) {
1508
1223
  let data;
1509
1224
  const sendResponse = (data) => {
@@ -1521,7 +1236,6 @@ export class Frontend extends EventEmitter {
1521
1236
  client.send(JSON.stringify(data));
1522
1237
  }
1523
1238
  else {
1524
- // istanbul ignore next cause is only a safety check
1525
1239
  this.log.error('Cannot send api response, client not connected');
1526
1240
  }
1527
1241
  };
@@ -1530,7 +1244,7 @@ export class Frontend extends EventEmitter {
1530
1244
  if (!isValidNumber(data.id) ||
1531
1245
  !isValidString(data.dst) ||
1532
1246
  !isValidString(data.src) ||
1533
- !isValidString(data.method) /* || !isValidObject(data.params)*/ ||
1247
+ !isValidString(data.method) ||
1534
1248
  data.dst !== 'Matterbridge') {
1535
1249
  this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
1536
1250
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
@@ -1588,22 +1302,7 @@ export class Frontend extends EventEmitter {
1588
1302
  }
1589
1303
  this.wssSendSnackbarMessage(`Adding plugin ${data.params.pluginNameOrPath}...`, 5);
1590
1304
  this.log.debug(`Adding plugin ${data.params.pluginNameOrPath}...`);
1591
- data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, ''); // Remove @version if present
1592
- /*
1593
- const plugin = (await this.server.fetch({ type: 'plugins_add', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginNameOrPath } }, 5000)).response.plugin;
1594
- if (plugin) {
1595
- this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
1596
- await this.server.fetch({ type: 'plugins_load', src: this.server.name, dst: 'plugins', params: { plugin: plugin.name } }, 5000);
1597
- this.wssSendRestartRequired();
1598
- this.wssSendRefreshRequired('plugins');
1599
- this.wssSendRefreshRequired('devices');
1600
- this.wssSendSnackbarMessage(`Loaded plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
1601
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1602
- } else {
1603
- this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
1604
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
1605
- }
1606
- */
1305
+ data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
1607
1306
  const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
1608
1307
  if (plugin) {
1609
1308
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -1616,7 +1315,7 @@ export class Frontend extends EventEmitter {
1616
1315
  this.wssSendSnackbarMessage(`Loaded plugin ${localData.params.pluginNameOrPath}`, 5, 'success');
1617
1316
  return;
1618
1317
  })
1619
- .catch(/* istanbul ignore next */ (_error) => { });
1318
+ .catch((_error) => { });
1620
1319
  }
1621
1320
  else {
1622
1321
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` });
@@ -1630,10 +1329,6 @@ export class Frontend extends EventEmitter {
1630
1329
  }
1631
1330
  this.wssSendSnackbarMessage(`Removing plugin ${data.params.pluginName}...`, 5);
1632
1331
  this.log.debug(`Removing plugin ${data.params.pluginName}...`);
1633
- /*
1634
- 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);
1635
- await this.server.fetch({ type: 'plugins_remove', src: this.server.name, dst: 'plugins', params: { nameOrPath: data.params.pluginName } }, 5000);
1636
- */
1637
1332
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1638
1333
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
1639
1334
  await this.matterbridge.plugins.remove(data.params.pluginName);
@@ -1661,11 +1356,9 @@ export class Frontend extends EventEmitter {
1661
1356
  this.wssSendSnackbarMessage(`Enabled plugin ${data.params.pluginName}`, 5, 'success');
1662
1357
  setImmediate(async () => {
1663
1358
  await this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true);
1664
- // @ts-expect-error Accessing private method
1665
1359
  if (plugin.serverNode)
1666
1360
  await this.matterbridge.startServerNode(plugin.serverNode);
1667
1361
  for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode))
1668
- // @ts-expect-error Accessing private method
1669
1362
  await this.matterbridge.startServerNode(device.serverNode);
1670
1363
  this.wssSendSnackbarMessage(`Started plugin ${localData.params.pluginName}`, 5, 'success');
1671
1364
  this.wssSendRefreshRequired('plugins');
@@ -1679,16 +1372,12 @@ export class Frontend extends EventEmitter {
1679
1372
  return;
1680
1373
  }
1681
1374
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1682
- // Stop server nodes devices first
1683
1375
  for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode)) {
1684
- // @ts-expect-error Accessing private method
1685
1376
  await this.matterbridge.stopServerNode(device.serverNode);
1686
1377
  device.serverNode = undefined;
1687
1378
  }
1688
- // Then shutdown plugin removing devices, disable it and stop plugin server node
1689
1379
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
1690
1380
  await this.matterbridge.plugins.disable(data.params.pluginName);
1691
- // @ts-expect-error Accessing private method
1692
1381
  if (plugin.serverNode)
1693
1382
  await this.matterbridge.stopServerNode(plugin.serverNode);
1694
1383
  plugin.serverNode = undefined;
@@ -1705,37 +1394,30 @@ export class Frontend extends EventEmitter {
1705
1394
  this.wssSendSnackbarMessage(`Restarting plugin ${data.params.pluginName}`, 5, 'info');
1706
1395
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1707
1396
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
1708
- // Stop server nodes
1709
1397
  if (plugin.serverNode) {
1710
- // @ts-expect-error Accessing private method
1711
1398
  await this.matterbridge.stopServerNode(plugin.serverNode);
1712
1399
  plugin.serverNode = undefined;
1713
1400
  }
1714
1401
  for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name)) {
1715
- // @ts-expect-error Accessing private method
1716
1402
  if (device.serverNode)
1717
1403
  await this.matterbridge.stopServerNode(device.serverNode);
1718
1404
  device.serverNode = undefined;
1719
1405
  this.log.debug(`Removing device ${device.deviceName} from plugin ${plugin.name}`);
1720
1406
  this.matterbridge.devices.remove(device);
1721
1407
  }
1722
- // @ts-expect-error Accessing private method
1723
1408
  if (plugin.type === 'DynamicPlatform' && !plugin.locked)
1724
1409
  await this.matterbridge.createDynamicPlugin(plugin);
1725
1410
  await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
1726
- plugin.restartRequired = false; // Reset plugin restartRequired
1411
+ plugin.restartRequired = false;
1727
1412
  let needRestart = 0;
1728
1413
  for (const plugin of this.matterbridge.plugins) {
1729
1414
  if (plugin.restartRequired)
1730
1415
  needRestart++;
1731
1416
  }
1732
1417
  if (needRestart === 0)
1733
- this.wssSendRestartNotRequired(true); // Reset global restart required message
1734
- // Start server nodes
1735
- // @ts-expect-error Accessing private method
1418
+ this.wssSendRestartNotRequired(true);
1736
1419
  if (plugin.serverNode)
1737
1420
  await this.matterbridge.startServerNode(plugin.serverNode);
1738
- // @ts-expect-error Accessing private method
1739
1421
  for (const device of this.matterbridge.devices.array().filter((d) => d.plugin === plugin.name && d.serverNode))
1740
1422
  await this.matterbridge.startServerNode(device.serverNode);
1741
1423
  this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
@@ -1767,54 +1449,18 @@ export class Frontend extends EventEmitter {
1767
1449
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1768
1450
  }
1769
1451
  else if (data.method === '/api/shellysysupdate') {
1770
- /*
1771
- const { triggerShellySysUpdate } = await import('./shelly.js');
1772
- triggerShellySysUpdate(this.matterbridge);
1773
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1774
- */
1775
1452
  }
1776
1453
  else if (data.method === '/api/shellymainupdate') {
1777
- /*
1778
- const { triggerShellyMainUpdate } = await import('./shelly.js');
1779
- triggerShellyMainUpdate(this.matterbridge);
1780
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1781
- */
1782
1454
  }
1783
1455
  else if (data.method === '/api/shellycreatesystemlog') {
1784
- /*
1785
- const { createShellySystemLog } = await import('./shelly.js');
1786
- createShellySystemLog(this.matterbridge);
1787
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1788
- */
1789
1456
  }
1790
1457
  else if (data.method === '/api/shellynetconfig') {
1791
- /*
1792
- this.log.debug('/api/shellynetconfig:', data.params);
1793
- const { triggerShellyChangeIp } = await import('./shelly.js');
1794
- triggerShellyChangeIp(this.matterbridge, data.params as { type: 'static' | 'dhcp'; ip: string; subnet: string; gateway: string; dns: string });
1795
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1796
- */
1797
1458
  }
1798
1459
  else if (data.method === '/api/softreset') {
1799
- /*
1800
- const { triggerShellySoftReset } = await import('./shelly.js');
1801
- triggerShellySoftReset(this.matterbridge);
1802
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1803
- */
1804
1460
  }
1805
1461
  else if (data.method === '/api/hardreset') {
1806
- /*
1807
- const { triggerShellyHardReset } = await import('./shelly.js');
1808
- triggerShellyHardReset(this.matterbridge);
1809
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1810
- */
1811
1462
  }
1812
1463
  else if (data.method === '/api/reboot') {
1813
- /*
1814
- const { triggerShellyReboot } = await import('./shelly.js');
1815
- triggerShellyReboot(this.matterbridge);
1816
- sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1817
- */
1818
1464
  }
1819
1465
  else if (data.method === '/api/restart') {
1820
1466
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
@@ -1954,7 +1600,6 @@ export class Frontend extends EventEmitter {
1954
1600
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/devices' });
1955
1601
  return;
1956
1602
  }
1957
- // istanbul ignore next
1958
1603
  const selectDeviceValues = !plugin.platform ? [] : plugin.platform.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1959
1604
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectDeviceValues });
1960
1605
  }
@@ -1968,7 +1613,6 @@ export class Frontend extends EventEmitter {
1968
1613
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' });
1969
1614
  return;
1970
1615
  }
1971
- // istanbul ignore next
1972
1616
  const selectEntityValues = !plugin.platform ? [] : plugin.platform.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1973
1617
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectEntityValues });
1974
1618
  }
@@ -2026,22 +1670,22 @@ export class Frontend extends EventEmitter {
2026
1670
  if (isValidString(data.params.value, 4)) {
2027
1671
  this.log.debug('Matterbridge logger level:', data.params.value);
2028
1672
  if (data.params.value === 'Debug') {
2029
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1673
+ await this.matterbridge.setLogLevel("debug");
2030
1674
  }
2031
1675
  else if (data.params.value === 'Info') {
2032
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1676
+ await this.matterbridge.setLogLevel("info");
2033
1677
  }
2034
1678
  else if (data.params.value === 'Notice') {
2035
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1679
+ await this.matterbridge.setLogLevel("notice");
2036
1680
  }
2037
1681
  else if (data.params.value === 'Warn') {
2038
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1682
+ await this.matterbridge.setLogLevel("warn");
2039
1683
  }
2040
1684
  else if (data.params.value === 'Error') {
2041
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1685
+ await this.matterbridge.setLogLevel("error");
2042
1686
  }
2043
1687
  else if (data.params.value === 'Fatal') {
2044
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1688
+ await this.matterbridge.setLogLevel("fatal");
2045
1689
  }
2046
1690
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
2047
1691
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -2052,7 +1696,6 @@ export class Frontend extends EventEmitter {
2052
1696
  this.log.debug('Matterbridge file log:', data.params.value);
2053
1697
  this.matterbridge.fileLogger = data.params.value;
2054
1698
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
2055
- // Create the file logger for matterbridge
2056
1699
  if (data.params.value)
2057
1700
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), await this.matterbridge.getLogLevel(), true);
2058
1701
  else
@@ -2082,12 +1725,11 @@ export class Frontend extends EventEmitter {
2082
1725
  Logger.level = MatterLogLevel.FATAL;
2083
1726
  }
2084
1727
  this.matterbridge.matterLogLevel = MatterLogLevel.names[Logger.level];
2085
- // Set the global logger callback for the WebSocketServer to the common minimum logLevel
2086
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
2087
- if (this.matterbridge.getLogLevel() === "info" /* LogLevel.INFO */ || Logger.level === MatterLogLevel.INFO)
2088
- callbackLogLevel = "info" /* LogLevel.INFO */;
2089
- if (this.matterbridge.getLogLevel() === "debug" /* LogLevel.DEBUG */ || Logger.level === MatterLogLevel.DEBUG)
2090
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
1728
+ let callbackLogLevel = "notice";
1729
+ if (this.matterbridge.getLogLevel() === "info" || Logger.level === MatterLogLevel.INFO)
1730
+ callbackLogLevel = "info";
1731
+ if (this.matterbridge.getLogLevel() === "debug" || Logger.level === MatterLogLevel.DEBUG)
1732
+ callbackLogLevel = "debug";
2091
1733
  AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
2092
1734
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
2093
1735
  await this.matterbridge.nodeContext?.set('matterLogLevel', Logger.level);
@@ -2139,7 +1781,6 @@ export class Frontend extends EventEmitter {
2139
1781
  }
2140
1782
  break;
2141
1783
  case 'setmatterport':
2142
- // eslint-disable-next-line no-case-declarations
2143
1784
  const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
2144
1785
  if (isValidNumber(port, 5540, 5600)) {
2145
1786
  this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
@@ -2159,7 +1800,6 @@ export class Frontend extends EventEmitter {
2159
1800
  }
2160
1801
  break;
2161
1802
  case 'setmatterdiscriminator':
2162
- // eslint-disable-next-line no-case-declarations
2163
1803
  const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
2164
1804
  if (isValidNumber(discriminator, 0, 4095)) {
2165
1805
  this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
@@ -2185,7 +1825,6 @@ export class Frontend extends EventEmitter {
2185
1825
  }
2186
1826
  break;
2187
1827
  case 'setmatterpasscode':
2188
- // eslint-disable-next-line no-case-declarations
2189
1828
  const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
2190
1829
  if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
2191
1830
  this.matterbridge.passcode = passcode;
@@ -2237,19 +1876,15 @@ export class Frontend extends EventEmitter {
2237
1876
  return;
2238
1877
  }
2239
1878
  const config = plugin.configJson;
2240
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2241
1879
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
2242
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
2243
1880
  if (select === 'serial')
2244
1881
  this.log.info(`Selected device serial ${data.params.serial}`);
2245
1882
  if (select === 'name')
2246
1883
  this.log.info(`Selected device name ${data.params.name}`);
2247
1884
  if (config && select && (select === 'serial' || select === 'name')) {
2248
- // Remove postfix from the serial if it exists
2249
1885
  if (config.postfix) {
2250
1886
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
2251
1887
  }
2252
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
2253
1888
  if (isValidArray(config.whiteList, 1)) {
2254
1889
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
2255
1890
  config.whiteList.push(data.params.serial);
@@ -2258,7 +1893,6 @@ export class Frontend extends EventEmitter {
2258
1893
  config.whiteList.push(data.params.name);
2259
1894
  }
2260
1895
  }
2261
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
2262
1896
  if (isValidArray(config.blackList, 1)) {
2263
1897
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
2264
1898
  config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
@@ -2289,9 +1923,7 @@ export class Frontend extends EventEmitter {
2289
1923
  return;
2290
1924
  }
2291
1925
  const config = plugin.configJson;
2292
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2293
1926
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
2294
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
2295
1927
  if (select === 'serial')
2296
1928
  this.log.info(`Unselected device serial ${data.params.serial}`);
2297
1929
  if (select === 'name')
@@ -2300,7 +1932,6 @@ export class Frontend extends EventEmitter {
2300
1932
  if (config.postfix) {
2301
1933
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
2302
1934
  }
2303
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
2304
1935
  if (isValidArray(config.whiteList, 1)) {
2305
1936
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
2306
1937
  config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
@@ -2309,7 +1940,6 @@ export class Frontend extends EventEmitter {
2309
1940
  config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
2310
1941
  }
2311
1942
  }
2312
- // Add the serial to the blackList
2313
1943
  if (isValidArray(config.blackList)) {
2314
1944
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
2315
1945
  config.blackList.push(data.params.serial);
@@ -2332,7 +1962,6 @@ export class Frontend extends EventEmitter {
2332
1962
  }
2333
1963
  }
2334
1964
  else {
2335
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
2336
1965
  const localData = data;
2337
1966
  this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
2338
1967
  sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
@@ -2342,46 +1971,23 @@ export class Frontend extends EventEmitter {
2342
1971
  inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
2343
1972
  }
2344
1973
  }
2345
- /**
2346
- * Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
2347
- *
2348
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
2349
- * @param {string} time - The time string of the message
2350
- * @param {string} name - The logger name of the message
2351
- * @param {string} message - The content of the message.
2352
- *
2353
- * @remarks
2354
- * The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
2355
- * It also replaces all occurrences of \" with " and angle-brackets with &lt; and &gt;.
2356
- * The function sends the message to all connected clients.
2357
- */
2358
1974
  wssSendLogMessage(level, time, name, message) {
2359
1975
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2360
1976
  return;
2361
1977
  if (!level || !time || !name || !message)
2362
1978
  return;
2363
- // Remove ANSI escape codes from the message
2364
- // eslint-disable-next-line no-control-regex
2365
1979
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
2366
- // Remove leading asterisks from the message
2367
1980
  message = message.replace(/^\*+/, '');
2368
- // Replace all occurrences of \t and \n
2369
1981
  message = message.replace(/[\t\n]/g, '');
2370
- // Remove non-printable characters
2371
- // eslint-disable-next-line no-control-regex
2372
1982
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
2373
- // Replace all occurrences of \" with "
2374
1983
  message = message.replace(/\\"/g, '"');
2375
- // Define the maximum allowed length for continuous characters without a space
2376
1984
  const maxContinuousLength = 100;
2377
1985
  const keepStartLength = 20;
2378
1986
  const keepEndLength = 20;
2379
- // Split the message into words
2380
1987
  if (level !== 'spawn') {
2381
1988
  message = message
2382
1989
  .split(' ')
2383
1990
  .map((word) => {
2384
- // If the word length exceeds the max continuous length, insert spaces and truncate
2385
1991
  if (word.length > maxContinuousLength) {
2386
1992
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
2387
1993
  }
@@ -2389,34 +1995,14 @@ export class Frontend extends EventEmitter {
2389
1995
  })
2390
1996
  .join(' ');
2391
1997
  }
2392
- // Send the message to all connected clients
2393
1998
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
2394
1999
  }
2395
- /**
2396
- * Sends a need to refresh WebSocket message to all connected clients.
2397
- *
2398
- * @param {string} changed - The changed value.
2399
- * @param {Record<string, unknown>} params - Additional parameters to send with the message.
2400
- * possible values for changed:
2401
- * - 'settings' (when the bridge has started in bridge mode or childbridge mode and when update finds a new version)
2402
- * - 'plugins'
2403
- * - 'devices'
2404
- * - 'matter' with param 'matter' (QRDiv component)
2405
- * @param {ApiMatter} params.matter - The matter device that has changed. Required if changed is 'matter'.
2406
- */
2407
2000
  wssSendRefreshRequired(changed, params) {
2408
2001
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2409
2002
  return;
2410
2003
  this.log.debug('Sending a refresh required message to all connected clients');
2411
- // Send the message to all connected clients
2412
2004
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
2413
2005
  }
2414
- /**
2415
- * Sends a need to restart WebSocket message to all connected clients.
2416
- *
2417
- * @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
2418
- * @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
2419
- */
2420
2006
  wssSendRestartRequired(snackbar = true, fixed = false) {
2421
2007
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2422
2008
  return;
@@ -2425,14 +2011,8 @@ export class Frontend extends EventEmitter {
2425
2011
  this.matterbridge.fixedRestartRequired = fixed;
2426
2012
  if (snackbar === true)
2427
2013
  this.wssSendSnackbarMessage(`Restart required`, 0);
2428
- // Send the message to all connected clients
2429
2014
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
2430
2015
  }
2431
- /**
2432
- * Sends a no need to restart WebSocket message to all connected clients.
2433
- *
2434
- * @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients. Default is true.
2435
- */
2436
2016
  wssSendRestartNotRequired(snackbar = true) {
2437
2017
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2438
2018
  return;
@@ -2440,35 +2020,20 @@ export class Frontend extends EventEmitter {
2440
2020
  this.matterbridge.restartRequired = false;
2441
2021
  if (snackbar === true)
2442
2022
  this.wssSendCloseSnackbarMessage(`Restart required`);
2443
- // Send the message to all connected clients
2444
2023
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
2445
2024
  }
2446
- /**
2447
- * Sends a need to update WebSocket message to all connected clients.
2448
- *
2449
- * @param {boolean} devVersion - If true, the update is for a development version. Default is false.
2450
- */
2451
2025
  wssSendUpdateRequired(devVersion = false) {
2452
2026
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2453
2027
  return;
2454
2028
  this.log.debug('Sending an update required message to all connected clients');
2455
2029
  this.matterbridge.updateRequired = true;
2456
- // Send the message to all connected clients
2457
2030
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
2458
2031
  }
2459
- /**
2460
- * Sends a cpu update message to all connected clients.
2461
- *
2462
- * @param {number} cpuUsage - The CPU usage percentage to send.
2463
- * @param {number} processCpuUsage - The CPU usage percentage of the process to send.
2464
- */
2465
2032
  wssSendCpuUpdate(cpuUsage, processCpuUsage) {
2466
2033
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2467
2034
  return;
2468
- // istanbul ignore else
2469
2035
  if (hasParameter('debug'))
2470
2036
  this.log.debug('Sending a cpu update message to all connected clients');
2471
- // Send the message to all connected clients
2472
2037
  this.wssBroadcastMessage({
2473
2038
  id: 0,
2474
2039
  src: 'Matterbridge',
@@ -2478,24 +2043,11 @@ export class Frontend extends EventEmitter {
2478
2043
  response: { cpuUsage: Math.round(cpuUsage * 100) / 100, processCpuUsage: Math.round(processCpuUsage * 100) / 100 },
2479
2044
  });
2480
2045
  }
2481
- /**
2482
- * Sends a memory update message to all connected clients.
2483
- *
2484
- * @param {string} totalMemory - The total memory in bytes.
2485
- * @param {string} freeMemory - The free memory in bytes.
2486
- * @param {string} rss - The resident set size in bytes.
2487
- * @param {string} heapTotal - The total heap memory in bytes.
2488
- * @param {string} heapUsed - The used heap memory in bytes.
2489
- * @param {string} external - The external memory in bytes.
2490
- * @param {string} arrayBuffers - The array buffers memory in bytes.
2491
- */
2492
2046
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
2493
2047
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2494
2048
  return;
2495
- // istanbul ignore else
2496
2049
  if (hasParameter('debug'))
2497
2050
  this.log.debug('Sending a memory update message to all connected clients');
2498
- // Send the message to all connected clients
2499
2051
  this.wssBroadcastMessage({
2500
2052
  id: 0,
2501
2053
  src: 'Matterbridge',
@@ -2505,73 +2057,29 @@ export class Frontend extends EventEmitter {
2505
2057
  response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers },
2506
2058
  });
2507
2059
  }
2508
- /**
2509
- * Sends an uptime update message to all connected clients.
2510
- *
2511
- * @param {string} systemUptime - The system uptime in a human-readable format.
2512
- * @param {string} processUptime - The process uptime in a human-readable format.
2513
- */
2514
2060
  wssSendUptimeUpdate(systemUptime, processUptime) {
2515
2061
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2516
2062
  return;
2517
- // istanbul ignore else
2518
2063
  if (hasParameter('debug'))
2519
2064
  this.log.debug('Sending a uptime update message to all connected clients');
2520
- // Send the message to all connected clients
2521
2065
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
2522
2066
  }
2523
- /**
2524
- * Sends an open snackbar message to all connected clients.
2525
- *
2526
- * @param {string} message - The message to send.
2527
- * @param {number} timeout - The timeout in seconds for the snackbar message. Default is 5 seconds.
2528
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the message.
2529
- * possible values are: 'info', 'warning', 'error', 'success'. Default is 'info'.
2530
- *
2531
- * @remarks
2532
- * If timeout is 0, the snackbar message will be displayed until closed by the user.
2533
- */
2534
2067
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
2535
2068
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2536
2069
  return;
2537
2070
  this.log.debug('Sending a snackbar message to all connected clients');
2538
- // Send the message to all connected clients
2539
2071
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
2540
2072
  }
2541
- /**
2542
- * Sends a close snackbar message to all connected clients.
2543
- * It will close the snackbar message with the same message and timeout = 0.
2544
- *
2545
- * @param {string} message - The message to send.
2546
- */
2547
2073
  wssSendCloseSnackbarMessage(message) {
2548
2074
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2549
2075
  return;
2550
2076
  this.log.debug('Sending a close snackbar message to all connected clients');
2551
- // Send the message to all connected clients
2552
2077
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
2553
2078
  }
2554
- /**
2555
- * Sends an attribute update message to all connected WebSocket clients.
2556
- *
2557
- * @param {string | undefined} plugin - The name of the plugin.
2558
- * @param {string | undefined} serialNumber - The serial number of the device.
2559
- * @param {string | undefined} uniqueId - The unique identifier of the device.
2560
- * @param {EndpointNumber} number - The endpoint number where the attribute belongs.
2561
- * @param {string} id - The endpoint id where the attribute belongs.
2562
- * @param {string} cluster - The cluster name where the attribute belongs.
2563
- * @param {string} attribute - The name of the attribute that changed.
2564
- * @param {number | string | boolean} value - The new value of the attribute.
2565
- *
2566
- * @remarks
2567
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
2568
- * with the updated attribute information.
2569
- */
2570
2079
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
2571
2080
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2572
2081
  return;
2573
2082
  this.log.debug('Sending an attribute update message to all connected clients');
2574
- // Send the message to all connected clients
2575
2083
  this.wssBroadcastMessage({
2576
2084
  id: 0,
2577
2085
  src: 'Matterbridge',
@@ -2581,25 +2089,16 @@ export class Frontend extends EventEmitter {
2581
2089
  response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value },
2582
2090
  });
2583
2091
  }
2584
- /**
2585
- * Sends a message to all connected clients.
2586
- * This is an helper function to send a broadcast message to all connected clients.
2587
- *
2588
- * @param {WsMessageBroadcast} msg - The message to send.
2589
- */
2590
2092
  wssBroadcastMessage(msg) {
2591
2093
  if (!this.listening || this.webSocketServer?.clients.size === 0)
2592
2094
  return;
2593
- // Send the message to all connected clients
2594
2095
  const stringifiedMsg = JSON.stringify(msg);
2595
2096
  if (msg.method !== 'log')
2596
2097
  this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
2597
2098
  this.webSocketServer?.clients.forEach((client) => {
2598
- // istanbul ignore else
2599
2099
  if (client.readyState === client.OPEN) {
2600
2100
  client.send(stringifiedMsg);
2601
2101
  }
2602
2102
  });
2603
2103
  }
2604
2104
  }
2605
- //# sourceMappingURL=frontend.js.map