matterbridge 3.3.3 → 3.3.4-dev-20251020-df40d12

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