matterbridge 3.2.5 → 3.2.6-dev-20250903-6ab5022

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 (279) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +1 -43
  3. package/dist/cli.js +2 -91
  4. package/dist/cliEmitter.js +0 -30
  5. package/dist/clusters/export.js +0 -2
  6. package/dist/defaultConfigSchema.js +0 -24
  7. package/dist/deviceManager.js +1 -94
  8. package/dist/devices/batteryStorage.js +1 -48
  9. package/dist/devices/cooktop.js +0 -55
  10. package/dist/devices/dishwasher.js +0 -57
  11. package/dist/devices/evse.js +10 -74
  12. package/dist/devices/export.js +0 -4
  13. package/dist/devices/extractorHood.js +0 -42
  14. package/dist/devices/heatPump.js +2 -50
  15. package/dist/devices/laundryDryer.js +3 -62
  16. package/dist/devices/laundryWasher.js +4 -70
  17. package/dist/devices/microwaveOven.js +5 -88
  18. package/dist/devices/oven.js +0 -85
  19. package/dist/devices/refrigerator.js +0 -102
  20. package/dist/devices/roboticVacuumCleaner.js +10 -96
  21. package/dist/devices/solarPower.js +0 -38
  22. package/dist/devices/temperatureControl.js +3 -25
  23. package/dist/devices/waterHeater.js +2 -82
  24. package/dist/dgram/coap.js +13 -126
  25. package/dist/dgram/dgram.js +2 -113
  26. package/dist/dgram/mb_coap.js +3 -41
  27. package/dist/dgram/mb_mdns.js +13 -51
  28. package/dist/dgram/mdns.js +137 -298
  29. package/dist/dgram/multicast.js +1 -60
  30. package/dist/dgram/unicast.js +0 -54
  31. package/dist/frontend.js +26 -451
  32. package/dist/globalMatterbridge.js +0 -47
  33. package/dist/helpers.js +0 -53
  34. package/dist/index.js +1 -30
  35. package/dist/logger/export.js +0 -1
  36. package/dist/matter/behaviors.js +0 -2
  37. package/dist/matter/clusters.js +0 -2
  38. package/dist/matter/devices.js +0 -2
  39. package/dist/matter/endpoints.js +0 -2
  40. package/dist/matter/export.js +0 -3
  41. package/dist/matter/types.js +0 -3
  42. package/dist/matterbridge.js +53 -789
  43. package/dist/matterbridgeAccessoryPlatform.js +0 -36
  44. package/dist/matterbridgeBehaviors.js +5 -65
  45. package/dist/matterbridgeDeviceTypes.js +17 -581
  46. package/dist/matterbridgeDynamicPlatform.js +0 -36
  47. package/dist/matterbridgeEndpoint.js +54 -1223
  48. package/dist/matterbridgeEndpointHelpers.js +12 -345
  49. package/dist/matterbridgePlatform.js +0 -256
  50. package/dist/matterbridgeTypes.js +0 -25
  51. package/dist/pluginManager.js +3 -249
  52. package/dist/shelly.js +7 -168
  53. package/dist/storage/export.js +0 -1
  54. package/dist/update.js +0 -69
  55. package/dist/utils/colorUtils.js +2 -97
  56. package/dist/utils/commandLine.js +0 -54
  57. package/dist/utils/copyDirectory.js +1 -38
  58. package/dist/utils/createDirectory.js +0 -33
  59. package/dist/utils/createZip.js +2 -47
  60. package/dist/utils/deepCopy.js +0 -39
  61. package/dist/utils/deepEqual.js +1 -72
  62. package/dist/utils/error.js +0 -41
  63. package/dist/utils/export.js +0 -1
  64. package/dist/utils/hex.js +0 -124
  65. package/dist/utils/isvalid.js +0 -101
  66. package/dist/utils/network.js +5 -91
  67. package/dist/utils/spawn.js +0 -40
  68. package/dist/utils/wait.js +8 -60
  69. package/frontend/build/asset-manifest.json +6 -6
  70. package/frontend/build/index.html +1 -1
  71. package/frontend/build/static/css/{main.944b63c3.css → main.a2f4846a.css} +2 -2
  72. package/frontend/build/static/css/main.a2f4846a.css.map +1 -0
  73. package/frontend/build/static/js/main.710b8f9f.js +3 -0
  74. package/frontend/build/static/js/{main.ae006df6.js.LICENSE.txt → main.710b8f9f.js.LICENSE.txt} +11 -0
  75. package/frontend/build/static/js/{main.ae006df6.js.map → main.710b8f9f.js.map} +1 -1
  76. package/frontend/package-lock.json +5 -5
  77. package/frontend/package.json +1 -1
  78. package/npm-shrinkwrap.json +8 -7
  79. package/package.json +1 -2
  80. package/dist/cli.d.ts +0 -26
  81. package/dist/cli.d.ts.map +0 -1
  82. package/dist/cli.js.map +0 -1
  83. package/dist/cliEmitter.d.ts +0 -34
  84. package/dist/cliEmitter.d.ts.map +0 -1
  85. package/dist/cliEmitter.js.map +0 -1
  86. package/dist/clusters/export.d.ts +0 -2
  87. package/dist/clusters/export.d.ts.map +0 -1
  88. package/dist/clusters/export.js.map +0 -1
  89. package/dist/defaultConfigSchema.d.ts +0 -28
  90. package/dist/defaultConfigSchema.d.ts.map +0 -1
  91. package/dist/defaultConfigSchema.js.map +0 -1
  92. package/dist/deviceManager.d.ts +0 -112
  93. package/dist/deviceManager.d.ts.map +0 -1
  94. package/dist/deviceManager.js.map +0 -1
  95. package/dist/devices/batteryStorage.d.ts +0 -48
  96. package/dist/devices/batteryStorage.d.ts.map +0 -1
  97. package/dist/devices/batteryStorage.js.map +0 -1
  98. package/dist/devices/cooktop.d.ts +0 -60
  99. package/dist/devices/cooktop.d.ts.map +0 -1
  100. package/dist/devices/cooktop.js.map +0 -1
  101. package/dist/devices/dishwasher.d.ts +0 -71
  102. package/dist/devices/dishwasher.d.ts.map +0 -1
  103. package/dist/devices/dishwasher.js.map +0 -1
  104. package/dist/devices/evse.d.ts +0 -75
  105. package/dist/devices/evse.d.ts.map +0 -1
  106. package/dist/devices/evse.js.map +0 -1
  107. package/dist/devices/export.d.ts +0 -15
  108. package/dist/devices/export.d.ts.map +0 -1
  109. package/dist/devices/export.js.map +0 -1
  110. package/dist/devices/extractorHood.d.ts +0 -46
  111. package/dist/devices/extractorHood.d.ts.map +0 -1
  112. package/dist/devices/extractorHood.js.map +0 -1
  113. package/dist/devices/heatPump.d.ts +0 -47
  114. package/dist/devices/heatPump.d.ts.map +0 -1
  115. package/dist/devices/heatPump.js.map +0 -1
  116. package/dist/devices/laundryDryer.d.ts +0 -67
  117. package/dist/devices/laundryDryer.d.ts.map +0 -1
  118. package/dist/devices/laundryDryer.js.map +0 -1
  119. package/dist/devices/laundryWasher.d.ts +0 -81
  120. package/dist/devices/laundryWasher.d.ts.map +0 -1
  121. package/dist/devices/laundryWasher.js.map +0 -1
  122. package/dist/devices/microwaveOven.d.ts +0 -168
  123. package/dist/devices/microwaveOven.d.ts.map +0 -1
  124. package/dist/devices/microwaveOven.js.map +0 -1
  125. package/dist/devices/oven.d.ts +0 -105
  126. package/dist/devices/oven.d.ts.map +0 -1
  127. package/dist/devices/oven.js.map +0 -1
  128. package/dist/devices/refrigerator.d.ts +0 -118
  129. package/dist/devices/refrigerator.d.ts.map +0 -1
  130. package/dist/devices/refrigerator.js.map +0 -1
  131. package/dist/devices/roboticVacuumCleaner.d.ts +0 -112
  132. package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
  133. package/dist/devices/roboticVacuumCleaner.js.map +0 -1
  134. package/dist/devices/solarPower.d.ts +0 -40
  135. package/dist/devices/solarPower.d.ts.map +0 -1
  136. package/dist/devices/solarPower.js.map +0 -1
  137. package/dist/devices/temperatureControl.d.ts +0 -166
  138. package/dist/devices/temperatureControl.d.ts.map +0 -1
  139. package/dist/devices/temperatureControl.js.map +0 -1
  140. package/dist/devices/waterHeater.d.ts +0 -111
  141. package/dist/devices/waterHeater.d.ts.map +0 -1
  142. package/dist/devices/waterHeater.js.map +0 -1
  143. package/dist/dgram/coap.d.ts +0 -205
  144. package/dist/dgram/coap.d.ts.map +0 -1
  145. package/dist/dgram/coap.js.map +0 -1
  146. package/dist/dgram/dgram.d.ts +0 -140
  147. package/dist/dgram/dgram.d.ts.map +0 -1
  148. package/dist/dgram/dgram.js.map +0 -1
  149. package/dist/dgram/mb_coap.d.ts +0 -24
  150. package/dist/dgram/mb_coap.d.ts.map +0 -1
  151. package/dist/dgram/mb_coap.js.map +0 -1
  152. package/dist/dgram/mb_mdns.d.ts +0 -24
  153. package/dist/dgram/mb_mdns.d.ts.map +0 -1
  154. package/dist/dgram/mb_mdns.js.map +0 -1
  155. package/dist/dgram/mdns.d.ts +0 -288
  156. package/dist/dgram/mdns.d.ts.map +0 -1
  157. package/dist/dgram/mdns.js.map +0 -1
  158. package/dist/dgram/multicast.d.ts +0 -65
  159. package/dist/dgram/multicast.d.ts.map +0 -1
  160. package/dist/dgram/multicast.js.map +0 -1
  161. package/dist/dgram/unicast.d.ts +0 -56
  162. package/dist/dgram/unicast.d.ts.map +0 -1
  163. package/dist/dgram/unicast.js.map +0 -1
  164. package/dist/frontend.d.ts +0 -313
  165. package/dist/frontend.d.ts.map +0 -1
  166. package/dist/frontend.js.map +0 -1
  167. package/dist/globalMatterbridge.d.ts +0 -59
  168. package/dist/globalMatterbridge.d.ts.map +0 -1
  169. package/dist/globalMatterbridge.js.map +0 -1
  170. package/dist/helpers.d.ts +0 -48
  171. package/dist/helpers.d.ts.map +0 -1
  172. package/dist/helpers.js.map +0 -1
  173. package/dist/index.d.ts +0 -33
  174. package/dist/index.d.ts.map +0 -1
  175. package/dist/index.js.map +0 -1
  176. package/dist/logger/export.d.ts +0 -2
  177. package/dist/logger/export.d.ts.map +0 -1
  178. package/dist/logger/export.js.map +0 -1
  179. package/dist/matter/behaviors.d.ts +0 -2
  180. package/dist/matter/behaviors.d.ts.map +0 -1
  181. package/dist/matter/behaviors.js.map +0 -1
  182. package/dist/matter/clusters.d.ts +0 -2
  183. package/dist/matter/clusters.d.ts.map +0 -1
  184. package/dist/matter/clusters.js.map +0 -1
  185. package/dist/matter/devices.d.ts +0 -2
  186. package/dist/matter/devices.d.ts.map +0 -1
  187. package/dist/matter/devices.js.map +0 -1
  188. package/dist/matter/endpoints.d.ts +0 -2
  189. package/dist/matter/endpoints.d.ts.map +0 -1
  190. package/dist/matter/endpoints.js.map +0 -1
  191. package/dist/matter/export.d.ts +0 -5
  192. package/dist/matter/export.d.ts.map +0 -1
  193. package/dist/matter/export.js.map +0 -1
  194. package/dist/matter/types.d.ts +0 -3
  195. package/dist/matter/types.d.ts.map +0 -1
  196. package/dist/matter/types.js.map +0 -1
  197. package/dist/matterbridge.d.ts +0 -462
  198. package/dist/matterbridge.d.ts.map +0 -1
  199. package/dist/matterbridge.js.map +0 -1
  200. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
  201. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  202. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  203. package/dist/matterbridgeBehaviors.d.ts +0 -1351
  204. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  205. package/dist/matterbridgeBehaviors.js.map +0 -1
  206. package/dist/matterbridgeDeviceTypes.d.ts +0 -710
  207. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  208. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  209. package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
  210. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  211. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  212. package/dist/matterbridgeEndpoint.d.ts +0 -1359
  213. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  214. package/dist/matterbridgeEndpoint.js.map +0 -1
  215. package/dist/matterbridgeEndpointHelpers.d.ts +0 -407
  216. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  217. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  218. package/dist/matterbridgePlatform.d.ts +0 -331
  219. package/dist/matterbridgePlatform.d.ts.map +0 -1
  220. package/dist/matterbridgePlatform.js.map +0 -1
  221. package/dist/matterbridgeTypes.d.ts +0 -198
  222. package/dist/matterbridgeTypes.d.ts.map +0 -1
  223. package/dist/matterbridgeTypes.js.map +0 -1
  224. package/dist/pluginManager.d.ts +0 -270
  225. package/dist/pluginManager.d.ts.map +0 -1
  226. package/dist/pluginManager.js.map +0 -1
  227. package/dist/shelly.d.ts +0 -174
  228. package/dist/shelly.d.ts.map +0 -1
  229. package/dist/shelly.js.map +0 -1
  230. package/dist/storage/export.d.ts +0 -2
  231. package/dist/storage/export.d.ts.map +0 -1
  232. package/dist/storage/export.js.map +0 -1
  233. package/dist/update.d.ts +0 -75
  234. package/dist/update.d.ts.map +0 -1
  235. package/dist/update.js.map +0 -1
  236. package/dist/utils/colorUtils.d.ts +0 -99
  237. package/dist/utils/colorUtils.d.ts.map +0 -1
  238. package/dist/utils/colorUtils.js.map +0 -1
  239. package/dist/utils/commandLine.d.ts +0 -59
  240. package/dist/utils/commandLine.d.ts.map +0 -1
  241. package/dist/utils/commandLine.js.map +0 -1
  242. package/dist/utils/copyDirectory.d.ts +0 -33
  243. package/dist/utils/copyDirectory.d.ts.map +0 -1
  244. package/dist/utils/copyDirectory.js.map +0 -1
  245. package/dist/utils/createDirectory.d.ts +0 -34
  246. package/dist/utils/createDirectory.d.ts.map +0 -1
  247. package/dist/utils/createDirectory.js.map +0 -1
  248. package/dist/utils/createZip.d.ts +0 -39
  249. package/dist/utils/createZip.d.ts.map +0 -1
  250. package/dist/utils/createZip.js.map +0 -1
  251. package/dist/utils/deepCopy.d.ts +0 -32
  252. package/dist/utils/deepCopy.d.ts.map +0 -1
  253. package/dist/utils/deepCopy.js.map +0 -1
  254. package/dist/utils/deepEqual.d.ts +0 -54
  255. package/dist/utils/deepEqual.d.ts.map +0 -1
  256. package/dist/utils/deepEqual.js.map +0 -1
  257. package/dist/utils/error.d.ts +0 -44
  258. package/dist/utils/error.d.ts.map +0 -1
  259. package/dist/utils/error.js.map +0 -1
  260. package/dist/utils/export.d.ts +0 -12
  261. package/dist/utils/export.d.ts.map +0 -1
  262. package/dist/utils/export.js.map +0 -1
  263. package/dist/utils/hex.d.ts +0 -89
  264. package/dist/utils/hex.d.ts.map +0 -1
  265. package/dist/utils/hex.js.map +0 -1
  266. package/dist/utils/isvalid.d.ts +0 -103
  267. package/dist/utils/isvalid.d.ts.map +0 -1
  268. package/dist/utils/isvalid.js.map +0 -1
  269. package/dist/utils/network.d.ts +0 -84
  270. package/dist/utils/network.d.ts.map +0 -1
  271. package/dist/utils/network.js.map +0 -1
  272. package/dist/utils/spawn.d.ts +0 -33
  273. package/dist/utils/spawn.d.ts.map +0 -1
  274. package/dist/utils/spawn.js.map +0 -1
  275. package/dist/utils/wait.d.ts +0 -54
  276. package/dist/utils/wait.d.ts.map +0 -1
  277. package/dist/utils/wait.js.map +0 -1
  278. package/frontend/build/static/css/main.944b63c3.css.map +0 -1
  279. package/frontend/build/static/js/main.ae006df6.js +0 -115
package/dist/frontend.js CHANGED
@@ -1,126 +1,30 @@
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.2.0
8
- * @license Apache-2.0
9
- *
10
- * Copyright 2025, 2026, 2027 Luca Liguori.
11
- *
12
- * Licensed under the Apache License, Version 2.0 (the "License");
13
- * you may not use this file except in compliance with the License.
14
- * You may obtain a copy of the License at
15
- *
16
- * http://www.apache.org/licenses/LICENSE-2.0
17
- *
18
- * Unless required by applicable law or agreed to in writing, software
19
- * distributed under the License is distributed on an "AS IS" BASIS,
20
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
- * See the License for the specific language governing permissions and
22
- * limitations under the License.
23
- */
24
- // Node modules
25
1
  import { createServer } from 'node:http';
26
2
  import https from 'node:https';
27
3
  import os from 'node:os';
28
4
  import path from 'node:path';
29
5
  import { existsSync, promises as fs } from 'node:fs';
30
6
  import EventEmitter from 'node:events';
31
- // Third-party modules
32
7
  import express from 'express';
33
8
  import WebSocket, { WebSocketServer } from 'ws';
34
9
  import multer from 'multer';
35
- // AnsiLogger module
36
10
  import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt } from 'node-ansi-logger';
37
- // @matter
38
11
  import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle } from '@matter/main';
39
12
  import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
40
- // Matterbridge
41
13
  import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout, hasParameter } from './utils/export.js';
42
14
  import { plg } from './matterbridgeTypes.js';
43
15
  import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
44
16
  import { cliEmitter, lastCpuUsage } from './cliEmitter.js';
45
- /**
46
- * Websocket message ID for logging.
47
- *
48
- * @constant {number}
49
- */
50
17
  export const WS_ID_LOG = 0;
51
- /**
52
- * Websocket message ID indicating a refresh is needed.
53
- *
54
- * @constant {number}
55
- */
56
18
  export const WS_ID_REFRESH_NEEDED = 1;
57
- /**
58
- * Websocket message ID indicating a restart is needed.
59
- *
60
- * @constant {number}
61
- */
62
19
  export const WS_ID_RESTART_NEEDED = 2;
63
- /**
64
- * Websocket message ID indicating a cpu update.
65
- *
66
- * @constant {number}
67
- */
68
20
  export const WS_ID_CPU_UPDATE = 3;
69
- /**
70
- * Websocket message ID indicating a memory update.
71
- *
72
- * @constant {number}
73
- */
74
21
  export const WS_ID_MEMORY_UPDATE = 4;
75
- /**
76
- * Websocket message ID indicating an uptime update.
77
- *
78
- * @constant {number}
79
- */
80
22
  export const WS_ID_UPTIME_UPDATE = 5;
81
- /**
82
- * Websocket message ID indicating a snackbar message.
83
- *
84
- * @constant {number}
85
- */
86
23
  export const WS_ID_SNACKBAR = 6;
87
- /**
88
- * Websocket message ID indicating matterbridge has un update available.
89
- *
90
- * @constant {number}
91
- */
92
24
  export const WS_ID_UPDATE_NEEDED = 7;
93
- /**
94
- * Websocket message ID indicating a state update.
95
- *
96
- * @constant {number}
97
- */
98
25
  export const WS_ID_STATEUPDATE = 8;
99
- /**
100
- * Websocket message ID indicating to close a permanent snackbar message.
101
- *
102
- * @constant {number}
103
- */
104
26
  export const WS_ID_CLOSE_SNACKBAR = 9;
105
- /**
106
- * Websocket message ID indicating a shelly system update.
107
- * check:
108
- * curl -k http://127.0.0.1:8101/api/updates/sys/check
109
- * perform:
110
- * curl -k http://127.0.0.1:8101/api/updates/sys/perform
111
- *
112
- * @constant {number}
113
- */
114
27
  export const WS_ID_SHELLY_SYS_UPDATE = 100;
115
- /**
116
- * Websocket message ID indicating a shelly main update.
117
- * check:
118
- * curl -k http://127.0.0.1:8101/api/updates/main/check
119
- * perform:
120
- * curl -k http://127.0.0.1:8101/api/updates/main/perform
121
- *
122
- * @constant {number}
123
- */
124
28
  export const WS_ID_SHELLY_MAIN_UPDATE = 101;
125
29
  export class Frontend extends EventEmitter {
126
30
  matterbridge;
@@ -133,7 +37,7 @@ export class Frontend extends EventEmitter {
133
37
  constructor(matterbridge) {
134
38
  super();
135
39
  this.matterbridge = matterbridge;
136
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
40
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
137
41
  }
138
42
  set logLevel(logLevel) {
139
43
  this.log.logLevel = logLevel;
@@ -141,39 +45,10 @@ export class Frontend extends EventEmitter {
141
45
  async start(port = 8283) {
142
46
  this.port = port;
143
47
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
144
- // Initialize multer with the upload directory
145
- const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads'); // Is created by matterbridge initialize
48
+ const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
146
49
  const upload = multer({ dest: uploadDir });
147
- // Create the express app that serves the frontend
148
50
  this.expressApp = express();
149
- // Inject logging/debug wrapper for route/middleware registration
150
- /*
151
- const methods = ['get', 'post', 'put', 'delete', 'use'];
152
- for (const method of methods) {
153
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
- const original = (this.expressApp as any)[method].bind(this.expressApp);
155
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
- (this.expressApp as any)[method] = (path: any, ...rest: any) => {
157
- try {
158
- console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
159
- return original(path, ...rest);
160
- } catch (err) {
161
- console.error(`[ERROR] Failed to register route: ${path}`);
162
- throw err;
163
- }
164
- };
165
- }
166
- */
167
- // Log all requests to the server for debugging
168
- /*
169
- this.expressApp.use((req, res, next) => {
170
- this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
171
- next();
172
- });
173
- */
174
- // Serve static files from '/static' endpoint
175
51
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
176
- // Read the package.json file to get the frontend version
177
52
  try {
178
53
  this.log.debug(`Reading frontend package.json...`);
179
54
  const frontendJson = await fs.readFile(path.join(this.matterbridge.rootDirectory, 'frontend/package.json'), 'utf8');
@@ -181,11 +56,9 @@ export class Frontend extends EventEmitter {
181
56
  this.log.debug(`Frontend version ${CYAN}${this.matterbridge.matterbridgeInformation.frontendVersion}${db}`);
182
57
  }
183
58
  catch (error) {
184
- // istanbul ignore next
185
59
  this.log.error(`Failed to read frontend package.json: ${error instanceof Error ? error.message : String(error)}`);
186
60
  }
187
61
  if (!hasParameter('ssl')) {
188
- // Create an HTTP server and attach the express app
189
62
  try {
190
63
  this.log.debug(`Creating HTTP server...`);
191
64
  this.httpServer = createServer(this.expressApp);
@@ -195,7 +68,6 @@ export class Frontend extends EventEmitter {
195
68
  this.emit('server_error', error);
196
69
  return;
197
70
  }
198
- // Listen on the specified port
199
71
  if (hasParameter('ingress')) {
200
72
  this.httpServer.listen(this.port, '0.0.0.0', () => {
201
73
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -234,7 +106,6 @@ export class Frontend extends EventEmitter {
234
106
  let passphrase;
235
107
  let httpsServerOptions = {};
236
108
  if (existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
237
- // Load the p12 certificate and the passphrase
238
109
  try {
239
110
  pfx = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
240
111
  this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
@@ -246,7 +117,7 @@ export class Frontend extends EventEmitter {
246
117
  }
247
118
  try {
248
119
  passphrase = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
249
- passphrase = passphrase.trim(); // Ensure no extra characters
120
+ passphrase = passphrase.trim();
250
121
  this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
251
122
  }
252
123
  catch (error) {
@@ -257,7 +128,6 @@ export class Frontend extends EventEmitter {
257
128
  httpsServerOptions = { pfx, passphrase };
258
129
  }
259
130
  else {
260
- // 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.
261
131
  try {
262
132
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
263
133
  this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
@@ -287,10 +157,9 @@ export class Frontend extends EventEmitter {
287
157
  httpsServerOptions = { cert: fullChain ?? cert, key, ca };
288
158
  }
289
159
  if (hasParameter('mtls')) {
290
- httpsServerOptions.requestCert = true; // Request client certificate
291
- httpsServerOptions.rejectUnauthorized = true; // Require client certificate validation
160
+ httpsServerOptions.requestCert = true;
161
+ httpsServerOptions.rejectUnauthorized = true;
292
162
  }
293
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
294
163
  try {
295
164
  this.log.debug(`Creating HTTPS server...`);
296
165
  this.httpsServer = https.createServer(httpsServerOptions, this.expressApp);
@@ -300,7 +169,6 @@ export class Frontend extends EventEmitter {
300
169
  this.emit('server_error', error);
301
170
  return;
302
171
  }
303
- // Listen on the specified port
304
172
  if (hasParameter('ingress')) {
305
173
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
306
174
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -330,19 +198,17 @@ export class Frontend extends EventEmitter {
330
198
  return;
331
199
  });
332
200
  }
333
- // Create a WebSocket server and attach it to the http or https server
334
201
  const wssPort = this.port;
335
202
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
336
203
  this.log.debug(`Creating WebSocketServer on host ${CYAN}${wssHost}${db}...`);
337
204
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
338
205
  this.webSocketServer.on('connection', (ws, request) => {
339
206
  const clientIp = request.socket.remoteAddress;
340
- // Set the global logger callback for the WebSocketServer
341
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
342
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
343
- callbackLogLevel = "info" /* LogLevel.INFO */;
344
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
345
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
207
+ let callbackLogLevel = "notice";
208
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
209
+ callbackLogLevel = "info";
210
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
211
+ callbackLogLevel = "debug";
346
212
  AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), callbackLogLevel);
347
213
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
348
214
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -364,7 +230,6 @@ export class Frontend extends EventEmitter {
364
230
  }
365
231
  });
366
232
  ws.on('error', (error) => {
367
- // istanbul ignore next
368
233
  this.log.error(`WebSocket client error: ${error}`);
369
234
  });
370
235
  });
@@ -378,7 +243,6 @@ export class Frontend extends EventEmitter {
378
243
  this.webSocketServer.on('error', (ws, error) => {
379
244
  this.log.error(`WebSocketServer error: ${error}`);
380
245
  });
381
- // Subscribe to cli events
382
246
  cliEmitter.removeAllListeners();
383
247
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
384
248
  this.wssSendUptimeUpdate(systemUptime, processUptime);
@@ -389,8 +253,6 @@ export class Frontend extends EventEmitter {
389
253
  cliEmitter.on('cpu', (cpuUsage) => {
390
254
  this.wssSendCpuUpdate(cpuUsage);
391
255
  });
392
- // Endpoint to validate login code
393
- // curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
394
256
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
395
257
  const { password } = req.body;
396
258
  this.log.debug('The frontend sent /api/login', password);
@@ -409,27 +271,23 @@ export class Frontend extends EventEmitter {
409
271
  this.log.warn('/api/login error wrong password');
410
272
  res.json({ valid: false });
411
273
  }
412
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
413
274
  }
414
275
  catch (error) {
415
276
  this.log.error('/api/login error getting password');
416
277
  res.json({ valid: false });
417
278
  }
418
279
  });
419
- // Endpoint to provide health check for docker
420
280
  this.expressApp.get('/health', (req, res) => {
421
281
  this.log.debug('Express received /health');
422
282
  const healthStatus = {
423
- status: 'ok', // Indicate service is healthy
424
- uptime: process.uptime(), // Server uptime in seconds
425
- timestamp: new Date().toISOString(), // Current timestamp
283
+ status: 'ok',
284
+ uptime: process.uptime(),
285
+ timestamp: new Date().toISOString(),
426
286
  };
427
287
  res.status(200).json(healthStatus);
428
288
  });
429
- // Endpoint to provide memory usage details
430
289
  this.expressApp.get('/memory', async (req, res) => {
431
290
  this.log.debug('Express received /memory');
432
- // Memory usage from process
433
291
  const memoryUsageRaw = process.memoryUsage();
434
292
  const memoryUsage = {
435
293
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -438,13 +296,10 @@ export class Frontend extends EventEmitter {
438
296
  external: this.formatMemoryUsage(memoryUsageRaw.external),
439
297
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
440
298
  };
441
- // V8 heap statistics
442
299
  const { default: v8 } = await import('node:v8');
443
300
  const heapStatsRaw = v8.getHeapStatistics();
444
301
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
445
- // Format heapStats
446
302
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
447
- // Format heapSpaces
448
303
  const heapSpaces = heapSpacesRaw.map((space) => ({
449
304
  ...space,
450
305
  space_size: this.formatMemoryUsage(space.space_size),
@@ -462,23 +317,19 @@ export class Frontend extends EventEmitter {
462
317
  };
463
318
  res.status(200).json(memoryReport);
464
319
  });
465
- // Endpoint to provide settings
466
320
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
467
321
  this.log.debug('The frontend sent /api/settings');
468
322
  res.json(await this.getApiSettings());
469
323
  });
470
- // Endpoint to provide plugins
471
324
  this.expressApp.get('/api/plugins', async (req, res) => {
472
325
  this.log.debug('The frontend sent /api/plugins');
473
326
  res.json(this.getPlugins());
474
327
  });
475
- // Endpoint to provide devices
476
328
  this.expressApp.get('/api/devices', async (req, res) => {
477
329
  this.log.debug('The frontend sent /api/devices');
478
330
  const devices = await this.getDevices();
479
331
  res.json(devices);
480
332
  });
481
- // Endpoint to view the matterbridge log
482
333
  this.expressApp.get('/api/view-mblog', async (req, res) => {
483
334
  this.log.debug('The frontend sent /api/view-mblog');
484
335
  try {
@@ -491,7 +342,6 @@ export class Frontend extends EventEmitter {
491
342
  res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
492
343
  }
493
344
  });
494
- // Endpoint to view the matter.js log
495
345
  this.expressApp.get('/api/view-mjlog', async (req, res) => {
496
346
  this.log.debug('The frontend sent /api/view-mjlog');
497
347
  try {
@@ -504,7 +354,6 @@ export class Frontend extends EventEmitter {
504
354
  res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
505
355
  }
506
356
  });
507
- // Endpoint to view the shelly log
508
357
  this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
509
358
  this.log.debug('The frontend sent /api/shellyviewsystemlog');
510
359
  try {
@@ -517,7 +366,6 @@ export class Frontend extends EventEmitter {
517
366
  res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
518
367
  }
519
368
  });
520
- // Endpoint to download the matterbridge log
521
369
  this.expressApp.get('/api/download-mblog', async (req, res) => {
522
370
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
523
371
  try {
@@ -531,14 +379,12 @@ export class Frontend extends EventEmitter {
531
379
  }
532
380
  res.type('text/plain');
533
381
  res.download(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'matterbridge.log', (error) => {
534
- /* istanbul ignore if */
535
382
  if (error) {
536
383
  this.log.error(`Error downloading log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
537
384
  res.status(500).send('Error downloading the matterbridge log file');
538
385
  }
539
386
  });
540
387
  });
541
- // Endpoint to download the matter log
542
388
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
543
389
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
544
390
  try {
@@ -552,14 +398,12 @@ export class Frontend extends EventEmitter {
552
398
  }
553
399
  res.type('text/plain');
554
400
  res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
555
- /* istanbul ignore if */
556
401
  if (error) {
557
402
  this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
558
403
  res.status(500).send('Error downloading the matter log file');
559
404
  }
560
405
  });
561
406
  });
562
- // Endpoint to download the shelly log
563
407
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
564
408
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
565
409
  try {
@@ -573,90 +417,74 @@ export class Frontend extends EventEmitter {
573
417
  }
574
418
  res.type('text/plain');
575
419
  res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
576
- /* istanbul ignore if */
577
420
  if (error) {
578
421
  this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
579
422
  res.status(500).send('Error downloading Shelly system log file');
580
423
  }
581
424
  });
582
425
  });
583
- // Endpoint to download the matterbridge storage directory
584
426
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
585
427
  this.log.debug('The frontend sent /api/download-mbstorage');
586
428
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
587
429
  res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), `matterbridge.${this.matterbridge.nodeStorageName}.zip`, (error) => {
588
- /* istanbul ignore if */
589
430
  if (error) {
590
431
  this.log.error(`Error downloading file ${`matterbridge.${this.matterbridge.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
591
432
  res.status(500).send('Error downloading the matterbridge storage file');
592
433
  }
593
434
  });
594
435
  });
595
- // Endpoint to download the matter storage file
596
436
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
597
437
  this.log.debug('The frontend sent /api/download-mjstorage');
598
438
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
599
439
  res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), `matterbridge.${this.matterbridge.matterStorageName}.zip`, (error) => {
600
- /* istanbul ignore if */
601
440
  if (error) {
602
441
  this.log.error(`Error downloading the matter storage matterbridge.${this.matterbridge.matterStorageName}.zip: ${error instanceof Error ? error.message : error}`);
603
442
  res.status(500).send('Error downloading the matter storage zip file');
604
443
  }
605
444
  });
606
445
  });
607
- // Endpoint to download the matterbridge plugin directory
608
446
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
609
447
  this.log.debug('The frontend sent /api/download-pluginstorage');
610
448
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
611
449
  res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
612
- /* istanbul ignore if */
613
450
  if (error) {
614
451
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
615
452
  res.status(500).send('Error downloading the matterbridge plugin storage file');
616
453
  }
617
454
  });
618
455
  });
619
- // Endpoint to download the matterbridge plugin config files
620
456
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
621
457
  this.log.debug('The frontend sent /api/download-pluginconfig');
622
458
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
623
459
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
624
- /* istanbul ignore if */
625
460
  if (error) {
626
461
  this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
627
462
  res.status(500).send('Error downloading the matterbridge plugin config file');
628
463
  }
629
464
  });
630
465
  });
631
- // Endpoint to download the matterbridge backup (created with the backup command)
632
466
  this.expressApp.get('/api/download-backup', async (req, res) => {
633
467
  this.log.debug('The frontend sent /api/download-backup');
634
468
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
635
- /* istanbul ignore if */
636
469
  if (error) {
637
470
  this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
638
471
  res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
639
472
  }
640
473
  });
641
474
  });
642
- // Endpoint to upload a package
643
475
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
644
476
  const { filename } = req.body;
645
477
  const file = req.file;
646
- /* istanbul ignore if */
647
478
  if (!file || !filename) {
648
479
  this.log.error(`uploadpackage: invalid request: file and filename are required`);
649
480
  res.status(400).send('Invalid request: file and filename are required');
650
481
  return;
651
482
  }
652
483
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
653
- // Define the path where the plugin file will be saved
654
484
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
655
485
  try {
656
- // Move the uploaded file to the specified path
657
486
  await fs.rename(file.path, filePath);
658
487
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
659
- // Install the plugin package
660
488
  if (filename.endsWith('.tgz')) {
661
489
  const { spawnCommand } = await import('./utils/spawn.js');
662
490
  await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
@@ -676,7 +504,6 @@ export class Frontend extends EventEmitter {
676
504
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
677
505
  }
678
506
  });
679
- // Fallback for routing (must be the last route)
680
507
  this.expressApp.use((req, res) => {
681
508
  this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
682
509
  res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -685,16 +512,13 @@ export class Frontend extends EventEmitter {
685
512
  }
686
513
  async stop() {
687
514
  this.log.debug('Stopping the frontend...');
688
- // Remove listeners from the express app
689
515
  if (this.expressApp) {
690
516
  this.expressApp.removeAllListeners();
691
517
  this.expressApp = undefined;
692
518
  this.log.debug('Frontend app closed successfully');
693
519
  }
694
- // Close the WebSocket server
695
520
  if (this.webSocketServer) {
696
521
  this.log.debug('Closing WebSocket server...');
697
- // Close all active connections
698
522
  this.webSocketServer.clients.forEach((client) => {
699
523
  if (client.readyState === WebSocket.OPEN) {
700
524
  client.close();
@@ -703,7 +527,6 @@ export class Frontend extends EventEmitter {
703
527
  await withTimeout(new Promise((resolve) => {
704
528
  this.webSocketServer?.close((error) => {
705
529
  if (error) {
706
- // istanbul ignore next
707
530
  this.log.error(`Error closing WebSocket server: ${error}`);
708
531
  }
709
532
  else {
@@ -716,13 +539,11 @@ export class Frontend extends EventEmitter {
716
539
  this.webSocketServer.removeAllListeners();
717
540
  this.webSocketServer = undefined;
718
541
  }
719
- // Close the http server
720
542
  if (this.httpServer) {
721
543
  this.log.debug('Closing http server...');
722
544
  await withTimeout(new Promise((resolve) => {
723
545
  this.httpServer?.close((error) => {
724
546
  if (error) {
725
- // istanbul ignore next
726
547
  this.log.error(`Error closing http server: ${error}`);
727
548
  }
728
549
  else {
@@ -736,13 +557,11 @@ export class Frontend extends EventEmitter {
736
557
  this.httpServer = undefined;
737
558
  this.log.debug('Frontend http server closed successfully');
738
559
  }
739
- // Close the https server
740
560
  if (this.httpsServer) {
741
561
  this.log.debug('Closing https server...');
742
562
  await withTimeout(new Promise((resolve) => {
743
563
  this.httpsServer?.close((error) => {
744
564
  if (error) {
745
- // istanbul ignore next
746
565
  this.log.error(`Error closing https server: ${error}`);
747
566
  }
748
567
  else {
@@ -758,7 +577,6 @@ export class Frontend extends EventEmitter {
758
577
  }
759
578
  this.log.debug('Frontend stopped successfully');
760
579
  }
761
- // Function to format bytes to KB, MB, or GB
762
580
  formatMemoryUsage = (bytes) => {
763
581
  if (bytes >= 1024 ** 3) {
764
582
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -770,7 +588,6 @@ export class Frontend extends EventEmitter {
770
588
  return `${(bytes / 1024).toFixed(2)} KB`;
771
589
  }
772
590
  };
773
- // Function to format system uptime with only the most significant unit
774
591
  formatOsUpTime = (seconds) => {
775
592
  if (seconds >= 86400) {
776
593
  const days = Math.floor(seconds / 86400);
@@ -786,13 +603,7 @@ export class Frontend extends EventEmitter {
786
603
  }
787
604
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
788
605
  };
789
- /**
790
- * Retrieves the api settings data.
791
- *
792
- * @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
793
- */
794
606
  async getApiSettings() {
795
- // Update the system information
796
607
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
797
608
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
798
609
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -801,7 +612,6 @@ export class Frontend extends EventEmitter {
801
612
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
802
613
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
803
614
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
804
- // Update the matterbridge information
805
615
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
806
616
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
807
617
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
@@ -813,7 +623,6 @@ export class Frontend extends EventEmitter {
813
623
  this.matterbridge.matterbridgeInformation.matterPort = (await this.matterbridge.nodeContext?.get('matterport', 5540)) ?? 5540;
814
624
  this.matterbridge.matterbridgeInformation.matterDiscriminator = await this.matterbridge.nodeContext?.get('matterdiscriminator');
815
625
  this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
816
- // Update the matterbridge information in bridge mode
817
626
  if (this.matterbridge.bridgeMode === 'bridge' && this.matterbridge.serverNode && !this.matterbridge.hasCleanupStarted) {
818
627
  this.matterbridge.matterbridgeInformation.matterbridgePaired = this.matterbridge.serverNode.state.commissioning.commissioned;
819
628
  this.matterbridge.matterbridgeInformation.matterbridgeQrPairingCode = this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : this.matterbridge.serverNode.state.commissioning.pairingCodes.qrPairingCode;
@@ -823,12 +632,6 @@ export class Frontend extends EventEmitter {
823
632
  }
824
633
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
825
634
  }
826
- /**
827
- * Retrieves the reachable attribute.
828
- *
829
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
830
- * @returns {boolean} The reachable attribute.
831
- */
832
635
  getReachability(device) {
833
636
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
834
637
  return false;
@@ -840,12 +643,6 @@ export class Frontend extends EventEmitter {
840
643
  return true;
841
644
  return false;
842
645
  }
843
- /**
844
- * Retrieves the power source attribute.
845
- *
846
- * @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
847
- * @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
848
- */
849
646
  getPowerSource(endpoint) {
850
647
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
851
648
  return undefined;
@@ -861,21 +658,13 @@ export class Frontend extends EventEmitter {
861
658
  }
862
659
  return;
863
660
  };
864
- // Root endpoint
865
661
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
866
662
  return powerSource(endpoint);
867
- // Child endpoints
868
663
  for (const child of endpoint.getChildEndpoints()) {
869
664
  if (child.hasClusterServer(PowerSource.Cluster.id))
870
665
  return powerSource(child);
871
666
  }
872
667
  }
873
- /**
874
- * Retrieves the commissioned status, matter pairing codes, fabrics and sessions from a given device in server mode.
875
- *
876
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the data from.
877
- * @returns {ApiDevicesMatter | undefined} An ApiDevicesMatter object or undefined if not found.
878
- */
879
668
  getMatterDataFromDevice(device) {
880
669
  if (device.mode === 'server' && device.serverNode) {
881
670
  return {
@@ -887,13 +676,6 @@ export class Frontend extends EventEmitter {
887
676
  };
888
677
  }
889
678
  }
890
- /**
891
- * Retrieves the cluster text description from a given device.
892
- * The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
893
- *
894
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
895
- * @returns {string} The attributes description of the cluster servers in the device.
896
- */
897
679
  getClusterTextFromDevice(device) {
898
680
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
899
681
  return '';
@@ -904,7 +686,6 @@ export class Frontend extends EventEmitter {
904
686
  if (composed)
905
687
  return 'Composed: ' + composed.value;
906
688
  }
907
- // istanbul ignore next cause is not reachable
908
689
  return '';
909
690
  };
910
691
  const getFixedLabel = (device) => {
@@ -914,13 +695,11 @@ export class Frontend extends EventEmitter {
914
695
  if (composed)
915
696
  return 'Composed: ' + composed.value;
916
697
  }
917
- // istanbul ignore next cause is not reacheable
918
698
  return '';
919
699
  };
920
700
  let attributes = '';
921
701
  let supportedModes = [];
922
702
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
923
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
924
703
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
925
704
  return;
926
705
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -1010,17 +789,11 @@ export class Frontend extends EventEmitter {
1010
789
  if (clusterName === 'userLabel' && attributeName === 'labelList')
1011
790
  attributes += `${getUserLabel(device)} `;
1012
791
  });
1013
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
1014
792
  return attributes.trimStart().trimEnd();
1015
793
  }
1016
- /**
1017
- * Retrieves the registered plugins sanitized for res.json().
1018
- *
1019
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
1020
- */
1021
794
  getPlugins() {
1022
795
  if (this.matterbridge.hasCleanupStarted)
1023
- return []; // Skip if cleanup has started
796
+ return [];
1024
797
  const baseRegisteredPlugins = [];
1025
798
  for (const plugin of this.matterbridge.plugins) {
1026
799
  baseRegisteredPlugins.push({
@@ -1050,7 +823,6 @@ export class Frontend extends EventEmitter {
1050
823
  schemaJson: plugin.schemaJson,
1051
824
  hasWhiteList: plugin.configJson?.whiteList !== undefined,
1052
825
  hasBlackList: plugin.configJson?.blackList !== undefined,
1053
- // Childbridge mode specific data
1054
826
  paired: plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.commissioned : undefined,
1055
827
  qrPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.pairingCodes.qrPairingCode : undefined,
1056
828
  manualPairingCode: this.matterbridge.matterbridgeInformation.matterbridgeEndAdvertise ? undefined : plugin.serverNode && plugin.serverNode.lifecycle.isOnline ? plugin.serverNode.state.commissioning.pairingCodes.manualPairingCode : undefined,
@@ -1060,21 +832,13 @@ export class Frontend extends EventEmitter {
1060
832
  }
1061
833
  return baseRegisteredPlugins;
1062
834
  }
1063
- /**
1064
- * Retrieves the devices from Matterbridge.
1065
- *
1066
- * @param {string} [pluginName] - The name of the plugin to filter devices by.
1067
- * @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
1068
- */
1069
835
  async getDevices(pluginName) {
1070
836
  if (this.matterbridge.hasCleanupStarted)
1071
- return []; // Skip if cleanup has started
837
+ return [];
1072
838
  const devices = [];
1073
839
  for (const device of this.matterbridge.devices.array()) {
1074
- // Filter by pluginName if provided
1075
840
  if (pluginName && pluginName !== device.plugin)
1076
841
  continue;
1077
- // Check if the device has the required properties
1078
842
  if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1079
843
  continue;
1080
844
  devices.push({
@@ -1094,37 +858,22 @@ export class Frontend extends EventEmitter {
1094
858
  }
1095
859
  return devices;
1096
860
  }
1097
- /**
1098
- * Retrieves the clusters from a given plugin and endpoint number.
1099
- *
1100
- * Response for /api/clusters
1101
- *
1102
- * @param {string} pluginName - The name of the plugin.
1103
- * @param {number} endpointNumber - The endpoint number.
1104
- * @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
1105
- */
1106
861
  getClusters(pluginName, endpointNumber) {
1107
862
  const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
1108
863
  if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
1109
864
  this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1110
865
  return;
1111
866
  }
1112
- // this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
1113
- // Get the device types from the main endpoint
1114
867
  const deviceTypes = [];
1115
868
  const clusters = [];
1116
869
  endpoint.state.descriptor.deviceTypeList.forEach((d) => {
1117
870
  deviceTypes.push(d.deviceType);
1118
871
  });
1119
- // Get the clusters from the main endpoint
1120
872
  endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1121
873
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1122
874
  return;
1123
875
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1124
876
  return;
1125
- // console.log(
1126
- // `${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}`,
1127
- // );
1128
877
  clusters.push({
1129
878
  endpoint: endpoint.number.toString(),
1130
879
  id: 'main',
@@ -1137,19 +886,12 @@ export class Frontend extends EventEmitter {
1137
886
  attributeLocalValue: attributeValue,
1138
887
  });
1139
888
  });
1140
- // Get the child endpoints
1141
889
  const childEndpoints = endpoint.getChildEndpoints();
1142
- // if (childEndpoints.length === 0) {
1143
- // this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
1144
- // }
1145
890
  childEndpoints.forEach((childEndpoint) => {
1146
- // istanbul ignore if cause is not reachable: should never happen but ...
1147
891
  if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
1148
892
  this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1149
893
  return;
1150
894
  }
1151
- // this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
1152
- // Get the device types of the child endpoint
1153
895
  const deviceTypes = [];
1154
896
  childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
1155
897
  deviceTypes.push(d.deviceType);
@@ -1159,12 +901,9 @@ export class Frontend extends EventEmitter {
1159
901
  return;
1160
902
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1161
903
  return;
1162
- // console.log(
1163
- // `${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}`,
1164
- // );
1165
904
  clusters.push({
1166
905
  endpoint: childEndpoint.number.toString(),
1167
- id: childEndpoint.maybeId ?? 'null', // Never happens
906
+ id: childEndpoint.maybeId ?? 'null',
1168
907
  deviceTypes,
1169
908
  clusterName: capitalizeFirstLetter(clusterName),
1170
909
  clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
@@ -1177,13 +916,6 @@ export class Frontend extends EventEmitter {
1177
916
  });
1178
917
  return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
1179
918
  }
1180
- /**
1181
- * Handles incoming websocket messages for the Matterbridge frontend.
1182
- *
1183
- * @param {WebSocket} client - The websocket client that sent the message.
1184
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1185
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1186
- */
1187
919
  async wsMessageHandler(client, message) {
1188
920
  let data;
1189
921
  try {
@@ -1230,45 +962,35 @@ export class Frontend extends EventEmitter {
1230
962
  this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
1231
963
  const packageName = data.params.packageName.replace(/@.*$/, '');
1232
964
  if (data.params.restart === false && packageName !== 'matterbridge') {
1233
- // The install comes from InstallPlugins
1234
965
  this.matterbridge.plugins
1235
966
  .add(packageName)
1236
967
  .then((plugin) => {
1237
- // istanbul ignore next if
1238
968
  if (plugin) {
1239
- // The plugin is not registered
1240
969
  this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
970
+ if (this.matterbridge.bridgeMode === 'childbridge')
971
+ this.wssSendRestartRequired(true, true);
1241
972
  this.matterbridge.plugins
1242
973
  .load(plugin, true, 'The plugin has been added', true)
1243
- // eslint-disable-next-line promise/no-nesting
1244
974
  .then(() => {
1245
975
  this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
1246
976
  this.wssSendRefreshRequired('plugins');
1247
977
  return;
1248
978
  })
1249
- // eslint-disable-next-line promise/no-nesting
1250
979
  .catch((_error) => {
1251
- //
1252
980
  });
1253
981
  }
1254
982
  else {
1255
- // The plugin is already registered
1256
983
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
1257
984
  this.wssSendRefreshRequired('plugins');
1258
985
  this.wssSendRestartRequired(true, true);
1259
986
  }
1260
987
  return;
1261
988
  })
1262
- // eslint-disable-next-line promise/no-nesting
1263
989
  .catch((_error) => {
1264
- //
1265
990
  });
1266
991
  }
1267
992
  else {
1268
- // The package is matterbridge
1269
- // istanbul ignore next
1270
993
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
1271
- // istanbul ignore next if
1272
994
  if (this.matterbridge.restartMode !== '') {
1273
995
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1274
996
  this.matterbridge.shutdownProcess();
@@ -1290,9 +1012,7 @@ export class Frontend extends EventEmitter {
1290
1012
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
1291
1013
  return;
1292
1014
  }
1293
- // The package is a plugin
1294
1015
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
1295
- // istanbul ignore next if
1296
1016
  if (plugin) {
1297
1017
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
1298
1018
  await this.matterbridge.plugins.remove(data.params.packageName);
@@ -1300,7 +1020,6 @@ export class Frontend extends EventEmitter {
1300
1020
  this.wssSendRefreshRequired('plugins');
1301
1021
  this.wssSendRefreshRequired('devices');
1302
1022
  }
1303
- // Uninstall the package
1304
1023
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1305
1024
  const { spawnCommand } = await import('./utils/spawn.js');
1306
1025
  spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
@@ -1341,7 +1060,6 @@ export class Frontend extends EventEmitter {
1341
1060
  return;
1342
1061
  })
1343
1062
  .catch((_error) => {
1344
- //
1345
1063
  });
1346
1064
  }
1347
1065
  else {
@@ -1388,7 +1106,6 @@ export class Frontend extends EventEmitter {
1388
1106
  return;
1389
1107
  })
1390
1108
  .catch((_error) => {
1391
- //
1392
1109
  });
1393
1110
  }
1394
1111
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1414,7 +1131,6 @@ export class Frontend extends EventEmitter {
1414
1131
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1415
1132
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
1416
1133
  if (plugin.serverNode) {
1417
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1418
1134
  await this.matterbridge.stopServerNode(plugin.serverNode);
1419
1135
  plugin.serverNode = undefined;
1420
1136
  }
@@ -1425,16 +1141,15 @@ export class Frontend extends EventEmitter {
1425
1141
  }
1426
1142
  }
1427
1143
  await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
1428
- plugin.restartRequired = false; // Reset plugin restartRequired
1144
+ plugin.restartRequired = false;
1429
1145
  let needRestart = 0;
1430
1146
  for (const plugin of this.matterbridge.plugins) {
1431
1147
  if (plugin.restartRequired)
1432
1148
  needRestart++;
1433
1149
  }
1434
1150
  if (needRestart === 0) {
1435
- this.wssSendRestartNotRequired(true); // Reset global restart required message
1151
+ this.wssSendRestartNotRequired(true);
1436
1152
  }
1437
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1438
1153
  if (plugin.serverNode)
1439
1154
  await this.matterbridge.startServerNode(plugin.serverNode);
1440
1155
  this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
@@ -1540,8 +1255,6 @@ export class Frontend extends EventEmitter {
1540
1255
  else if (data.method === '/api/advertise') {
1541
1256
  const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
1542
1257
  this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
1543
- // this.matterbridge.matterbridgeQrPairingCode = pairingCodes?.qrPairingCode;
1544
- // this.matterbridge.matterbridgeManualPairingCode = pairingCodes?.manualPairingCode;
1545
1258
  this.wssSendRefreshRequired('matterbridgeAdvertise');
1546
1259
  this.wssSendSnackbarMessage(`Started fabrics share`, 0);
1547
1260
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes, success: true }));
@@ -1664,22 +1377,22 @@ export class Frontend extends EventEmitter {
1664
1377
  if (isValidString(data.params.value, 4)) {
1665
1378
  this.log.debug('Matterbridge logger level:', data.params.value);
1666
1379
  if (data.params.value === 'Debug') {
1667
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1380
+ await this.matterbridge.setLogLevel("debug");
1668
1381
  }
1669
1382
  else if (data.params.value === 'Info') {
1670
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1383
+ await this.matterbridge.setLogLevel("info");
1671
1384
  }
1672
1385
  else if (data.params.value === 'Notice') {
1673
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1386
+ await this.matterbridge.setLogLevel("notice");
1674
1387
  }
1675
1388
  else if (data.params.value === 'Warn') {
1676
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1389
+ await this.matterbridge.setLogLevel("warn");
1677
1390
  }
1678
1391
  else if (data.params.value === 'Error') {
1679
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1392
+ await this.matterbridge.setLogLevel("error");
1680
1393
  }
1681
1394
  else if (data.params.value === 'Fatal') {
1682
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1395
+ await this.matterbridge.setLogLevel("fatal");
1683
1396
  }
1684
1397
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1685
1398
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true }));
@@ -1690,7 +1403,6 @@ export class Frontend extends EventEmitter {
1690
1403
  this.log.debug('Matterbridge file log:', data.params.value);
1691
1404
  this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
1692
1405
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1693
- // Create the file logger for matterbridge
1694
1406
  if (data.params.value)
1695
1407
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1696
1408
  else
@@ -1737,7 +1449,6 @@ export class Frontend extends EventEmitter {
1737
1449
  });
1738
1450
  }
1739
1451
  catch (error) {
1740
- /* istanbul ignore next */
1741
1452
  this.log.debug(`Error adding the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
1742
1453
  }
1743
1454
  }
@@ -1746,7 +1457,6 @@ export class Frontend extends EventEmitter {
1746
1457
  Logger.removeLogger('matterfilelogger');
1747
1458
  }
1748
1459
  catch (error) {
1749
- /* istanbul ignore next */
1750
1460
  this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
1751
1461
  }
1752
1462
  }
@@ -1859,19 +1569,15 @@ export class Frontend extends EventEmitter {
1859
1569
  return;
1860
1570
  }
1861
1571
  const config = plugin.configJson;
1862
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1863
1572
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1864
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1865
1573
  if (select === 'serial')
1866
1574
  this.log.info(`Selected device serial ${data.params.serial}`);
1867
1575
  if (select === 'name')
1868
1576
  this.log.info(`Selected device name ${data.params.name}`);
1869
1577
  if (config && select && (select === 'serial' || select === 'name')) {
1870
- // Remove postfix from the serial if it exists
1871
1578
  if (config.postfix) {
1872
1579
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1873
1580
  }
1874
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1875
1581
  if (isValidArray(config.whiteList, 1)) {
1876
1582
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1877
1583
  config.whiteList.push(data.params.serial);
@@ -1880,7 +1586,6 @@ export class Frontend extends EventEmitter {
1880
1586
  config.whiteList.push(data.params.name);
1881
1587
  }
1882
1588
  }
1883
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1884
1589
  if (isValidArray(config.blackList, 1)) {
1885
1590
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1886
1591
  config.blackList = config.blackList.filter((item) => item !== data.params.serial);
@@ -1911,9 +1616,7 @@ export class Frontend extends EventEmitter {
1911
1616
  return;
1912
1617
  }
1913
1618
  const config = plugin.configJson;
1914
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1915
1619
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1916
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1917
1620
  if (select === 'serial')
1918
1621
  this.log.info(`Unselected device serial ${data.params.serial}`);
1919
1622
  if (select === 'name')
@@ -1922,7 +1625,6 @@ export class Frontend extends EventEmitter {
1922
1625
  if (config.postfix) {
1923
1626
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1924
1627
  }
1925
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1926
1628
  if (isValidArray(config.whiteList, 1)) {
1927
1629
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1928
1630
  config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
@@ -1931,7 +1633,6 @@ export class Frontend extends EventEmitter {
1931
1633
  config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
1932
1634
  }
1933
1635
  }
1934
- // Add the serial to the blackList
1935
1636
  if (isValidArray(config.blackList)) {
1936
1637
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1937
1638
  config.blackList.push(data.params.serial);
@@ -1965,251 +1666,126 @@ export class Frontend extends EventEmitter {
1965
1666
  this.log.error(`Error parsing message "${message}" from websocket client:`, error instanceof Error ? error.message : error);
1966
1667
  }
1967
1668
  }
1968
- /**
1969
- * Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1970
- *
1971
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1972
- * @param {string} time - The time string of the message
1973
- * @param {string} name - The logger name of the message
1974
- * @param {string} message - The content of the message.
1975
- *
1976
- * @remarks
1977
- * The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
1978
- * It also replaces all occurrences of \" with " and angle-brackets with &lt; and &gt;.
1979
- * The function sends the message to all connected clients.
1980
- */
1981
1669
  wssSendMessage(level, time, name, message) {
1982
1670
  if (!level || !time || !name || !message)
1983
1671
  return;
1984
- // Remove ANSI escape codes from the message
1985
- // eslint-disable-next-line no-control-regex
1986
1672
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1987
- // Remove leading asterisks from the message
1988
1673
  message = message.replace(/^\*+/, '');
1989
- // Replace all occurrences of \t and \n
1990
1674
  message = message.replace(/[\t\n]/g, '');
1991
- // Remove non-printable characters
1992
- // eslint-disable-next-line no-control-regex
1993
1675
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1994
- // Replace all occurrences of \" with "
1995
1676
  message = message.replace(/\\"/g, '"');
1996
- // Replace all occurrences of angle-brackets with &lt; and &gt;"
1997
1677
  message = message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1998
- // Define the maximum allowed length for continuous characters without a space
1999
1678
  const maxContinuousLength = 100;
2000
1679
  const keepStartLength = 20;
2001
1680
  const keepEndLength = 20;
2002
- // Split the message into words
2003
1681
  message = message
2004
1682
  .split(' ')
2005
1683
  .map((word) => {
2006
- // If the word length exceeds the max continuous length, insert spaces and truncate
2007
1684
  if (word.length > maxContinuousLength) {
2008
1685
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
2009
1686
  }
2010
1687
  return word;
2011
1688
  })
2012
1689
  .join(' ');
2013
- // Send the message to all connected clients
2014
1690
  this.webSocketServer?.clients.forEach((client) => {
2015
1691
  if (client.readyState === WebSocket.OPEN) {
2016
1692
  client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
2017
1693
  }
2018
1694
  });
2019
1695
  }
2020
- /**
2021
- * Sends a need to refresh WebSocket message to all connected clients.
2022
- *
2023
- * @param {string} changed - The changed value. If null, the whole page will be refreshed.
2024
- * possible values:
2025
- * - 'matterbridgeLatestVersion'
2026
- * - 'matterbridgeDevVersion'
2027
- * - 'matterbridgeAdvertise'
2028
- * - 'online'
2029
- * - 'offline'
2030
- * - 'reachability'
2031
- * - 'settings'
2032
- * - 'plugins'
2033
- * - 'pluginsRestart'
2034
- * - 'devices'
2035
- * - 'fabrics'
2036
- * - 'sessions'
2037
- */
2038
1696
  wssSendRefreshRequired(changed = null) {
2039
1697
  this.log.debug('Sending a refresh required message to all connected clients');
2040
- // Send the message to all connected clients
2041
1698
  this.webSocketServer?.clients.forEach((client) => {
2042
1699
  if (client.readyState === WebSocket.OPEN) {
2043
1700
  client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed: changed } }));
2044
1701
  }
2045
1702
  });
2046
1703
  }
2047
- /**
2048
- * Sends a need to restart WebSocket message to all connected clients.
2049
- *
2050
- * @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
2051
- * @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
2052
- */
2053
1704
  wssSendRestartRequired(snackbar = true, fixed = false) {
2054
1705
  this.log.debug('Sending a restart required message to all connected clients');
2055
1706
  this.matterbridge.matterbridgeInformation.restartRequired = true;
2056
1707
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
2057
1708
  if (snackbar === true)
2058
1709
  this.wssSendSnackbarMessage(`Restart required`, 0);
2059
- // Send the message to all connected clients
2060
1710
  this.webSocketServer?.clients.forEach((client) => {
2061
1711
  if (client.readyState === WebSocket.OPEN) {
2062
1712
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } }));
2063
1713
  }
2064
1714
  });
2065
1715
  }
2066
- /**
2067
- * Sends a no need to restart WebSocket message to all connected clients.
2068
- *
2069
- * @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients.
2070
- */
2071
1716
  wssSendRestartNotRequired(snackbar = true) {
2072
1717
  this.log.debug('Sending a restart not required message to all connected clients');
2073
1718
  this.matterbridge.matterbridgeInformation.restartRequired = false;
2074
1719
  if (snackbar === true)
2075
1720
  this.wssSendCloseSnackbarMessage(`Restart required`);
2076
- // Send the message to all connected clients
2077
1721
  this.webSocketServer?.clients.forEach((client) => {
2078
1722
  if (client.readyState === WebSocket.OPEN) {
2079
1723
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', params: {} }));
2080
1724
  }
2081
1725
  });
2082
1726
  }
2083
- /**
2084
- * Sends a need to update WebSocket message to all connected clients.
2085
- *
2086
- * @param {boolean} devVersion - If true, the update is for a development version.
2087
- */
2088
1727
  wssSendUpdateRequired(devVersion = false) {
2089
1728
  this.log.debug('Sending an update required message to all connected clients');
2090
1729
  this.matterbridge.matterbridgeInformation.updateRequired = true;
2091
- // Send the message to all connected clients
2092
1730
  this.webSocketServer?.clients.forEach((client) => {
2093
1731
  if (client.readyState === WebSocket.OPEN) {
2094
1732
  client.send(JSON.stringify({ id: WS_ID_UPDATE_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required', params: {} }));
2095
1733
  }
2096
1734
  });
2097
1735
  }
2098
- /**
2099
- * Sends a cpu update message to all connected clients.
2100
- *
2101
- * @param {number} cpuUsage - The CPU usage percentage to send.
2102
- */
2103
1736
  wssSendCpuUpdate(cpuUsage) {
2104
1737
  if (hasParameter('debug'))
2105
1738
  this.log.debug('Sending a cpu update message to all connected clients');
2106
- // Send the message to all connected clients
2107
1739
  this.webSocketServer?.clients.forEach((client) => {
2108
1740
  if (client.readyState === WebSocket.OPEN) {
2109
1741
  client.send(JSON.stringify({ id: WS_ID_CPU_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage } }));
2110
1742
  }
2111
1743
  });
2112
1744
  }
2113
- /**
2114
- * Sends a memory update message to all connected clients.
2115
- *
2116
- * @param {string} totalMemory - The total memory in bytes.
2117
- * @param {string} freeMemory - The free memory in bytes.
2118
- * @param {string} rss - The resident set size in bytes.
2119
- * @param {string} heapTotal - The total heap memory in bytes.
2120
- * @param {string} heapUsed - The used heap memory in bytes.
2121
- * @param {string} external - The external memory in bytes.
2122
- * @param {string} arrayBuffers - The array buffers memory in bytes.
2123
- */
2124
1745
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
2125
1746
  if (hasParameter('debug'))
2126
1747
  this.log.debug('Sending a memory update message to all connected clients');
2127
- // Send the message to all connected clients
2128
1748
  this.webSocketServer?.clients.forEach((client) => {
2129
1749
  if (client.readyState === WebSocket.OPEN) {
2130
1750
  client.send(JSON.stringify({ id: WS_ID_MEMORY_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } }));
2131
1751
  }
2132
1752
  });
2133
1753
  }
2134
- /**
2135
- * Sends an uptime update message to all connected clients.
2136
- *
2137
- * @param {string} systemUptime - The system uptime in a human-readable format.
2138
- * @param {string} processUptime - The process uptime in a human-readable format.
2139
- */
2140
1754
  wssSendUptimeUpdate(systemUptime, processUptime) {
2141
1755
  if (hasParameter('debug'))
2142
1756
  this.log.debug('Sending a uptime update message to all connected clients');
2143
- // Send the message to all connected clients
2144
1757
  this.webSocketServer?.clients.forEach((client) => {
2145
1758
  if (client.readyState === WebSocket.OPEN) {
2146
1759
  client.send(JSON.stringify({ id: WS_ID_UPTIME_UPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } }));
2147
1760
  }
2148
1761
  });
2149
1762
  }
2150
- /**
2151
- * Sends an open snackbar message to all connected clients.
2152
- *
2153
- * @param {string} message - The message to send.
2154
- * @param {number} timeout - The timeout in seconds for the snackbar message.
2155
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the snackbar message (default info).
2156
- */
2157
1763
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
2158
1764
  this.log.debug('Sending a snackbar message to all connected clients');
2159
- // Send the message to all connected clients
2160
1765
  this.webSocketServer?.clients.forEach((client) => {
2161
1766
  if (client.readyState === WebSocket.OPEN) {
2162
1767
  client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
2163
1768
  }
2164
1769
  });
2165
1770
  }
2166
- /**
2167
- * Sends a close snackbar message to all connected clients.
2168
- *
2169
- * @param {string} message - The message to send.
2170
- */
2171
1771
  wssSendCloseSnackbarMessage(message) {
2172
1772
  this.log.debug('Sending a close snackbar message to all connected clients');
2173
- // Send the message to all connected clients
2174
1773
  this.webSocketServer?.clients.forEach((client) => {
2175
1774
  if (client.readyState === WebSocket.OPEN) {
2176
1775
  client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
2177
1776
  }
2178
1777
  });
2179
1778
  }
2180
- /**
2181
- * Sends an attribute update message to all connected WebSocket clients.
2182
- *
2183
- * @param {string | undefined} plugin - The name of the plugin.
2184
- * @param {string | undefined} serialNumber - The serial number of the device.
2185
- * @param {string | undefined} uniqueId - The unique identifier of the device.
2186
- * @param {string} cluster - The cluster name where the attribute belongs.
2187
- * @param {string} attribute - The name of the attribute that changed.
2188
- * @param {number | string | boolean} value - The new value of the attribute.
2189
- *
2190
- * @remarks
2191
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
2192
- * with the updated attribute information.
2193
- */
2194
1779
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
2195
1780
  this.log.debug('Sending an attribute update message to all connected clients');
2196
- // Send the message to all connected clients
2197
1781
  this.webSocketServer?.clients.forEach((client) => {
2198
1782
  if (client.readyState === WebSocket.OPEN) {
2199
1783
  client.send(JSON.stringify({ id: WS_ID_STATEUPDATE, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } }));
2200
1784
  }
2201
1785
  });
2202
1786
  }
2203
- /**
2204
- * Sends a message to all connected clients.
2205
- *
2206
- * @param {number} id - The message id.
2207
- * @param {string} method - The message method.
2208
- * @param {Record<string, string | number | boolean>} params - The message parameters.
2209
- */
2210
1787
  wssBroadcastMessage(id, method, params) {
2211
1788
  this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
2212
- // Send the message to all connected clients
2213
1789
  this.webSocketServer?.clients.forEach((client) => {
2214
1790
  if (client.readyState === WebSocket.OPEN) {
2215
1791
  client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
@@ -2217,4 +1793,3 @@ export class Frontend extends EventEmitter {
2217
1793
  });
2218
1794
  }
2219
1795
  }
2220
- //# sourceMappingURL=frontend.js.map