matterbridge 3.2.8 → 3.2.9-dev-20250922-6e00637

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 (295) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/dist/cli.js +2 -91
  3. package/dist/cliEmitter.js +0 -30
  4. package/dist/clusters/export.js +0 -2
  5. package/dist/defaultConfigSchema.js +0 -24
  6. package/dist/deviceManager.js +1 -94
  7. package/dist/devices/airConditioner.js +0 -57
  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 -5
  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 +9 -100
  21. package/dist/devices/solarPower.js +0 -38
  22. package/dist/devices/speaker.js +0 -84
  23. package/dist/devices/temperatureControl.js +3 -25
  24. package/dist/devices/waterHeater.js +2 -82
  25. package/dist/dgram/coap.js +13 -126
  26. package/dist/dgram/dgram.js +2 -114
  27. package/dist/dgram/mb_coap.js +3 -41
  28. package/dist/dgram/mb_mdns.js +15 -80
  29. package/dist/dgram/mdns.js +137 -299
  30. package/dist/dgram/multicast.js +1 -62
  31. package/dist/dgram/unicast.js +0 -54
  32. package/dist/frontend.js +45 -435
  33. package/dist/frontendTypes.js +3 -51
  34. package/dist/globalMatterbridge.js +0 -47
  35. package/dist/helpers.js +0 -53
  36. package/dist/index.js +1 -30
  37. package/dist/logger/export.js +0 -1
  38. package/dist/matter/behaviors.js +0 -2
  39. package/dist/matter/clusters.js +0 -2
  40. package/dist/matter/devices.js +0 -2
  41. package/dist/matter/endpoints.js +0 -2
  42. package/dist/matter/export.js +0 -3
  43. package/dist/matter/types.js +0 -3
  44. package/dist/matterbridge.js +57 -780
  45. package/dist/matterbridgeAccessoryPlatform.js +0 -36
  46. package/dist/matterbridgeBehaviors.js +5 -65
  47. package/dist/matterbridgeDeviceTypes.js +17 -630
  48. package/dist/matterbridgeDynamicPlatform.js +0 -36
  49. package/dist/matterbridgeEndpoint.js +55 -1373
  50. package/dist/matterbridgeEndpointHelpers.js +12 -345
  51. package/dist/matterbridgePlatform.js +0 -304
  52. package/dist/matterbridgeTypes.js +0 -25
  53. package/dist/pluginManager.js +3 -249
  54. package/dist/shelly.js +11 -172
  55. package/dist/storage/export.js +0 -1
  56. package/dist/update.js +0 -69
  57. package/dist/utils/colorUtils.js +2 -97
  58. package/dist/utils/commandLine.js +0 -54
  59. package/dist/utils/copyDirectory.js +1 -38
  60. package/dist/utils/createDirectory.js +0 -33
  61. package/dist/utils/createZip.js +2 -47
  62. package/dist/utils/deepCopy.js +0 -39
  63. package/dist/utils/deepEqual.js +1 -72
  64. package/dist/utils/error.js +0 -41
  65. package/dist/utils/export.js +0 -1
  66. package/dist/utils/hex.js +0 -124
  67. package/dist/utils/isvalid.js +0 -101
  68. package/dist/utils/jestHelpers.js +3 -153
  69. package/dist/utils/network.js +5 -91
  70. package/dist/utils/spawn.js +0 -68
  71. package/dist/utils/wait.js +8 -60
  72. package/frontend/build/assets/index.js +2 -2
  73. package/frontend/build/assets/vendor_lodash.js +1 -1
  74. package/frontend/build/assets/vendor_mdi.js +1 -1
  75. package/frontend/build/assets/vendor_mui.js +31 -23
  76. package/frontend/build/assets/vendor_node_modules.js +28 -45
  77. package/frontend/build/assets/vendor_notistack.js +2 -2
  78. package/frontend/build/assets/vendor_qrcode.js +1 -1
  79. package/frontend/build/assets/vendor_rjsf.js +12 -3
  80. package/frontend/build/index.html +0 -1
  81. package/frontend/package-lock.json +208 -283
  82. package/frontend/package.json +4 -13
  83. package/npm-shrinkwrap.json +44 -44
  84. package/package.json +2 -3
  85. package/dist/cli.d.ts +0 -26
  86. package/dist/cli.d.ts.map +0 -1
  87. package/dist/cli.js.map +0 -1
  88. package/dist/cliEmitter.d.ts +0 -34
  89. package/dist/cliEmitter.d.ts.map +0 -1
  90. package/dist/cliEmitter.js.map +0 -1
  91. package/dist/clusters/export.d.ts +0 -2
  92. package/dist/clusters/export.d.ts.map +0 -1
  93. package/dist/clusters/export.js.map +0 -1
  94. package/dist/defaultConfigSchema.d.ts +0 -28
  95. package/dist/defaultConfigSchema.d.ts.map +0 -1
  96. package/dist/defaultConfigSchema.js.map +0 -1
  97. package/dist/deviceManager.d.ts +0 -112
  98. package/dist/deviceManager.d.ts.map +0 -1
  99. package/dist/deviceManager.js.map +0 -1
  100. package/dist/devices/airConditioner.d.ts +0 -98
  101. package/dist/devices/airConditioner.d.ts.map +0 -1
  102. package/dist/devices/airConditioner.js.map +0 -1
  103. package/dist/devices/batteryStorage.d.ts +0 -48
  104. package/dist/devices/batteryStorage.d.ts.map +0 -1
  105. package/dist/devices/batteryStorage.js.map +0 -1
  106. package/dist/devices/cooktop.d.ts +0 -60
  107. package/dist/devices/cooktop.d.ts.map +0 -1
  108. package/dist/devices/cooktop.js.map +0 -1
  109. package/dist/devices/dishwasher.d.ts +0 -71
  110. package/dist/devices/dishwasher.d.ts.map +0 -1
  111. package/dist/devices/dishwasher.js.map +0 -1
  112. package/dist/devices/evse.d.ts +0 -75
  113. package/dist/devices/evse.d.ts.map +0 -1
  114. package/dist/devices/evse.js.map +0 -1
  115. package/dist/devices/export.d.ts +0 -17
  116. package/dist/devices/export.d.ts.map +0 -1
  117. package/dist/devices/export.js.map +0 -1
  118. package/dist/devices/extractorHood.d.ts +0 -46
  119. package/dist/devices/extractorHood.d.ts.map +0 -1
  120. package/dist/devices/extractorHood.js.map +0 -1
  121. package/dist/devices/heatPump.d.ts +0 -47
  122. package/dist/devices/heatPump.d.ts.map +0 -1
  123. package/dist/devices/heatPump.js.map +0 -1
  124. package/dist/devices/laundryDryer.d.ts +0 -67
  125. package/dist/devices/laundryDryer.d.ts.map +0 -1
  126. package/dist/devices/laundryDryer.js.map +0 -1
  127. package/dist/devices/laundryWasher.d.ts +0 -81
  128. package/dist/devices/laundryWasher.d.ts.map +0 -1
  129. package/dist/devices/laundryWasher.js.map +0 -1
  130. package/dist/devices/microwaveOven.d.ts +0 -168
  131. package/dist/devices/microwaveOven.d.ts.map +0 -1
  132. package/dist/devices/microwaveOven.js.map +0 -1
  133. package/dist/devices/oven.d.ts +0 -105
  134. package/dist/devices/oven.d.ts.map +0 -1
  135. package/dist/devices/oven.js.map +0 -1
  136. package/dist/devices/refrigerator.d.ts +0 -118
  137. package/dist/devices/refrigerator.d.ts.map +0 -1
  138. package/dist/devices/refrigerator.js.map +0 -1
  139. package/dist/devices/roboticVacuumCleaner.d.ts +0 -112
  140. package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
  141. package/dist/devices/roboticVacuumCleaner.js.map +0 -1
  142. package/dist/devices/solarPower.d.ts +0 -40
  143. package/dist/devices/solarPower.d.ts.map +0 -1
  144. package/dist/devices/solarPower.js.map +0 -1
  145. package/dist/devices/speaker.d.ts +0 -87
  146. package/dist/devices/speaker.d.ts.map +0 -1
  147. package/dist/devices/speaker.js.map +0 -1
  148. package/dist/devices/temperatureControl.d.ts +0 -166
  149. package/dist/devices/temperatureControl.d.ts.map +0 -1
  150. package/dist/devices/temperatureControl.js.map +0 -1
  151. package/dist/devices/waterHeater.d.ts +0 -111
  152. package/dist/devices/waterHeater.d.ts.map +0 -1
  153. package/dist/devices/waterHeater.js.map +0 -1
  154. package/dist/dgram/coap.d.ts +0 -205
  155. package/dist/dgram/coap.d.ts.map +0 -1
  156. package/dist/dgram/coap.js.map +0 -1
  157. package/dist/dgram/dgram.d.ts +0 -141
  158. package/dist/dgram/dgram.d.ts.map +0 -1
  159. package/dist/dgram/dgram.js.map +0 -1
  160. package/dist/dgram/mb_coap.d.ts +0 -24
  161. package/dist/dgram/mb_coap.d.ts.map +0 -1
  162. package/dist/dgram/mb_coap.js.map +0 -1
  163. package/dist/dgram/mb_mdns.d.ts +0 -24
  164. package/dist/dgram/mb_mdns.d.ts.map +0 -1
  165. package/dist/dgram/mb_mdns.js.map +0 -1
  166. package/dist/dgram/mdns.d.ts +0 -290
  167. package/dist/dgram/mdns.d.ts.map +0 -1
  168. package/dist/dgram/mdns.js.map +0 -1
  169. package/dist/dgram/multicast.d.ts +0 -67
  170. package/dist/dgram/multicast.d.ts.map +0 -1
  171. package/dist/dgram/multicast.js.map +0 -1
  172. package/dist/dgram/unicast.d.ts +0 -56
  173. package/dist/dgram/unicast.d.ts.map +0 -1
  174. package/dist/dgram/unicast.js.map +0 -1
  175. package/dist/frontend.d.ts +0 -228
  176. package/dist/frontend.d.ts.map +0 -1
  177. package/dist/frontend.js.map +0 -1
  178. package/dist/frontendTypes.d.ts +0 -572
  179. package/dist/frontendTypes.d.ts.map +0 -1
  180. package/dist/frontendTypes.js.map +0 -1
  181. package/dist/globalMatterbridge.d.ts +0 -59
  182. package/dist/globalMatterbridge.d.ts.map +0 -1
  183. package/dist/globalMatterbridge.js.map +0 -1
  184. package/dist/helpers.d.ts +0 -48
  185. package/dist/helpers.d.ts.map +0 -1
  186. package/dist/helpers.js.map +0 -1
  187. package/dist/index.d.ts +0 -33
  188. package/dist/index.d.ts.map +0 -1
  189. package/dist/index.js.map +0 -1
  190. package/dist/logger/export.d.ts +0 -2
  191. package/dist/logger/export.d.ts.map +0 -1
  192. package/dist/logger/export.js.map +0 -1
  193. package/dist/matter/behaviors.d.ts +0 -2
  194. package/dist/matter/behaviors.d.ts.map +0 -1
  195. package/dist/matter/behaviors.js.map +0 -1
  196. package/dist/matter/clusters.d.ts +0 -2
  197. package/dist/matter/clusters.d.ts.map +0 -1
  198. package/dist/matter/clusters.js.map +0 -1
  199. package/dist/matter/devices.d.ts +0 -2
  200. package/dist/matter/devices.d.ts.map +0 -1
  201. package/dist/matter/devices.js.map +0 -1
  202. package/dist/matter/endpoints.d.ts +0 -2
  203. package/dist/matter/endpoints.d.ts.map +0 -1
  204. package/dist/matter/endpoints.js.map +0 -1
  205. package/dist/matter/export.d.ts +0 -5
  206. package/dist/matter/export.d.ts.map +0 -1
  207. package/dist/matter/export.js.map +0 -1
  208. package/dist/matter/types.d.ts +0 -3
  209. package/dist/matter/types.d.ts.map +0 -1
  210. package/dist/matter/types.js.map +0 -1
  211. package/dist/matterbridge.d.ts +0 -442
  212. package/dist/matterbridge.d.ts.map +0 -1
  213. package/dist/matterbridge.js.map +0 -1
  214. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
  215. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  216. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  217. package/dist/matterbridgeBehaviors.d.ts +0 -1747
  218. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  219. package/dist/matterbridgeBehaviors.js.map +0 -1
  220. package/dist/matterbridgeDeviceTypes.d.ts +0 -761
  221. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  222. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  223. package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
  224. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  225. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  226. package/dist/matterbridgeEndpoint.d.ts +0 -1515
  227. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  228. package/dist/matterbridgeEndpoint.js.map +0 -1
  229. package/dist/matterbridgeEndpointHelpers.d.ts +0 -407
  230. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  231. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  232. package/dist/matterbridgePlatform.d.ts +0 -380
  233. package/dist/matterbridgePlatform.d.ts.map +0 -1
  234. package/dist/matterbridgePlatform.js.map +0 -1
  235. package/dist/matterbridgeTypes.d.ts +0 -190
  236. package/dist/matterbridgeTypes.d.ts.map +0 -1
  237. package/dist/matterbridgeTypes.js.map +0 -1
  238. package/dist/pluginManager.d.ts +0 -270
  239. package/dist/pluginManager.d.ts.map +0 -1
  240. package/dist/pluginManager.js.map +0 -1
  241. package/dist/shelly.d.ts +0 -174
  242. package/dist/shelly.d.ts.map +0 -1
  243. package/dist/shelly.js.map +0 -1
  244. package/dist/storage/export.d.ts +0 -2
  245. package/dist/storage/export.d.ts.map +0 -1
  246. package/dist/storage/export.js.map +0 -1
  247. package/dist/update.d.ts +0 -75
  248. package/dist/update.d.ts.map +0 -1
  249. package/dist/update.js.map +0 -1
  250. package/dist/utils/colorUtils.d.ts +0 -99
  251. package/dist/utils/colorUtils.d.ts.map +0 -1
  252. package/dist/utils/colorUtils.js.map +0 -1
  253. package/dist/utils/commandLine.d.ts +0 -59
  254. package/dist/utils/commandLine.d.ts.map +0 -1
  255. package/dist/utils/commandLine.js.map +0 -1
  256. package/dist/utils/copyDirectory.d.ts +0 -33
  257. package/dist/utils/copyDirectory.d.ts.map +0 -1
  258. package/dist/utils/copyDirectory.js.map +0 -1
  259. package/dist/utils/createDirectory.d.ts +0 -34
  260. package/dist/utils/createDirectory.d.ts.map +0 -1
  261. package/dist/utils/createDirectory.js.map +0 -1
  262. package/dist/utils/createZip.d.ts +0 -39
  263. package/dist/utils/createZip.d.ts.map +0 -1
  264. package/dist/utils/createZip.js.map +0 -1
  265. package/dist/utils/deepCopy.d.ts +0 -32
  266. package/dist/utils/deepCopy.d.ts.map +0 -1
  267. package/dist/utils/deepCopy.js.map +0 -1
  268. package/dist/utils/deepEqual.d.ts +0 -54
  269. package/dist/utils/deepEqual.d.ts.map +0 -1
  270. package/dist/utils/deepEqual.js.map +0 -1
  271. package/dist/utils/error.d.ts +0 -44
  272. package/dist/utils/error.d.ts.map +0 -1
  273. package/dist/utils/error.js.map +0 -1
  274. package/dist/utils/export.d.ts +0 -13
  275. package/dist/utils/export.d.ts.map +0 -1
  276. package/dist/utils/export.js.map +0 -1
  277. package/dist/utils/hex.d.ts +0 -89
  278. package/dist/utils/hex.d.ts.map +0 -1
  279. package/dist/utils/hex.js.map +0 -1
  280. package/dist/utils/isvalid.d.ts +0 -103
  281. package/dist/utils/isvalid.d.ts.map +0 -1
  282. package/dist/utils/isvalid.js.map +0 -1
  283. package/dist/utils/jestHelpers.d.ts +0 -137
  284. package/dist/utils/jestHelpers.d.ts.map +0 -1
  285. package/dist/utils/jestHelpers.js.map +0 -1
  286. package/dist/utils/network.d.ts +0 -84
  287. package/dist/utils/network.d.ts.map +0 -1
  288. package/dist/utils/network.js.map +0 -1
  289. package/dist/utils/spawn.d.ts +0 -33
  290. package/dist/utils/spawn.d.ts.map +0 -1
  291. package/dist/utils/spawn.js.map +0 -1
  292. package/dist/utils/wait.d.ts +0 -54
  293. package/dist/utils/wait.d.ts.map +0 -1
  294. package/dist/utils/wait.js.map +0 -1
  295. package/frontend/build/assets/vendor_react_table.js +0 -1
package/dist/frontend.js CHANGED
@@ -1,27 +1,3 @@
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
- // Node modules
25
1
  import { createServer } from 'node:http';
26
2
  import https from 'node:https';
27
3
  import os from 'node:os';
@@ -29,17 +5,14 @@ import path from 'node:path';
29
5
  import { existsSync, promises as fs, unlinkSync } from 'node:fs';
30
6
  import EventEmitter from 'node:events';
31
7
  import { appendFile } from 'node:fs/promises';
32
- // Third-party modules
33
8
  import express from 'express';
34
9
  import WebSocket, { WebSocketServer } from 'ws';
35
10
  import multer from 'multer';
36
- // AnsiLogger module
37
11
  import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, YELLOW, nt } from 'node-ansi-logger';
38
- // @matter
39
12
  import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Lifecycle, LogDestination, Diagnostic, Time, FabricIndex } from '@matter/main';
40
13
  import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
41
14
  import { DeviceAdvertiser, DeviceCommissioner, FabricManager } from '@matter/main/protocol';
42
- // Matterbridge
15
+ import { CommissioningOptions } from '@matter/main/types';
43
16
  import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout, hasParameter, wait, inspectError } from './utils/export.js';
44
17
  import { plg } from './matterbridgeTypes.js';
45
18
  import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
@@ -55,7 +28,7 @@ export class Frontend extends EventEmitter {
55
28
  constructor(matterbridge) {
56
29
  super();
57
30
  this.matterbridge = matterbridge;
58
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
31
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
59
32
  this.log.logNameColor = '\x1b[38;5;97m';
60
33
  }
61
34
  set logLevel(logLevel) {
@@ -64,39 +37,10 @@ export class Frontend extends EventEmitter {
64
37
  async start(port = 8283) {
65
38
  this.port = port;
66
39
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
67
- // Initialize multer with the upload directory
68
- const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads'); // Is created by matterbridge initialize
40
+ const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
69
41
  const upload = multer({ dest: uploadDir });
70
- // Create the express app that serves the frontend
71
42
  this.expressApp = express();
72
- // Inject logging/debug wrapper for route/middleware registration
73
- /*
74
- const methods = ['get', 'post', 'put', 'delete', 'use'];
75
- for (const method of methods) {
76
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
- const original = (this.expressApp as any)[method].bind(this.expressApp);
78
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
- (this.expressApp as any)[method] = (path: any, ...rest: any) => {
80
- try {
81
- console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
82
- return original(path, ...rest);
83
- } catch (err) {
84
- console.error(`[ERROR] Failed to register route: ${path}`);
85
- throw err;
86
- }
87
- };
88
- }
89
- */
90
- // Log all requests to the server for debugging
91
- /*
92
- this.expressApp.use((req, res, next) => {
93
- this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
94
- next();
95
- });
96
- */
97
- // Serve static files from '/static' endpoint
98
43
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
99
- // Read the package.json file to get the frontend version
100
44
  try {
101
45
  this.log.debug(`Reading frontend package.json...`);
102
46
  const frontendJson = await fs.readFile(path.join(this.matterbridge.rootDirectory, 'frontend/package.json'), 'utf8');
@@ -104,11 +48,9 @@ export class Frontend extends EventEmitter {
104
48
  this.log.debug(`Frontend version ${CYAN}${this.matterbridge.matterbridgeInformation.frontendVersion}${db}`);
105
49
  }
106
50
  catch (error) {
107
- // istanbul ignore next
108
51
  this.log.error(`Failed to read frontend package.json: ${error instanceof Error ? error.message : String(error)}`);
109
52
  }
110
53
  if (!hasParameter('ssl')) {
111
- // Create an HTTP server and attach the express app
112
54
  try {
113
55
  this.log.debug(`Creating HTTP server...`);
114
56
  this.httpServer = createServer(this.expressApp);
@@ -118,7 +60,6 @@ export class Frontend extends EventEmitter {
118
60
  this.emit('server_error', error);
119
61
  return;
120
62
  }
121
- // Listen on the specified port
122
63
  if (hasParameter('ingress')) {
123
64
  this.httpServer.listen(this.port, '0.0.0.0', () => {
124
65
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -157,7 +98,6 @@ export class Frontend extends EventEmitter {
157
98
  let passphrase;
158
99
  let httpsServerOptions = {};
159
100
  if (existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
160
- // Load the p12 certificate and the passphrase
161
101
  try {
162
102
  pfx = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
163
103
  this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
@@ -169,7 +109,7 @@ export class Frontend extends EventEmitter {
169
109
  }
170
110
  try {
171
111
  passphrase = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
172
- passphrase = passphrase.trim(); // Ensure no extra characters
112
+ passphrase = passphrase.trim();
173
113
  this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
174
114
  }
175
115
  catch (error) {
@@ -180,7 +120,6 @@ export class Frontend extends EventEmitter {
180
120
  httpsServerOptions = { pfx, passphrase };
181
121
  }
182
122
  else {
183
- // 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.
184
123
  try {
185
124
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
186
125
  this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
@@ -210,10 +149,9 @@ export class Frontend extends EventEmitter {
210
149
  httpsServerOptions = { cert: fullChain ?? cert, key, ca };
211
150
  }
212
151
  if (hasParameter('mtls')) {
213
- httpsServerOptions.requestCert = true; // Request client certificate
214
- httpsServerOptions.rejectUnauthorized = true; // Require client certificate validation
152
+ httpsServerOptions.requestCert = true;
153
+ httpsServerOptions.rejectUnauthorized = true;
215
154
  }
216
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
217
155
  try {
218
156
  this.log.debug(`Creating HTTPS server...`);
219
157
  this.httpsServer = https.createServer(httpsServerOptions, this.expressApp);
@@ -223,7 +161,6 @@ export class Frontend extends EventEmitter {
223
161
  this.emit('server_error', error);
224
162
  return;
225
163
  }
226
- // Listen on the specified port
227
164
  if (hasParameter('ingress')) {
228
165
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
229
166
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -253,19 +190,17 @@ export class Frontend extends EventEmitter {
253
190
  return;
254
191
  });
255
192
  }
256
- // Create a WebSocket server and attach it to the http or https server
257
193
  const wssPort = this.port;
258
194
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
259
195
  this.log.debug(`Creating WebSocketServer on host ${CYAN}${wssHost}${db}...`);
260
196
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
261
197
  this.webSocketServer.on('connection', (ws, request) => {
262
198
  const clientIp = request.socket.remoteAddress;
263
- // Set the global logger callback for the WebSocketServer
264
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
265
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
266
- callbackLogLevel = "info" /* LogLevel.INFO */;
267
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
268
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
199
+ let callbackLogLevel = "notice";
200
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
201
+ callbackLogLevel = "info";
202
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
203
+ callbackLogLevel = "debug";
269
204
  AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
270
205
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
271
206
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -287,7 +222,6 @@ export class Frontend extends EventEmitter {
287
222
  }
288
223
  });
289
224
  ws.on('error', (error) => {
290
- // istanbul ignore next
291
225
  this.log.error(`WebSocket client error: ${error}`);
292
226
  });
293
227
  });
@@ -301,7 +235,6 @@ export class Frontend extends EventEmitter {
301
235
  this.webSocketServer.on('error', (ws, error) => {
302
236
  this.log.error(`WebSocketServer error: ${error}`);
303
237
  });
304
- // Subscribe to cli events
305
238
  cliEmitter.removeAllListeners();
306
239
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
307
240
  this.wssSendUptimeUpdate(systemUptime, processUptime);
@@ -312,8 +245,6 @@ export class Frontend extends EventEmitter {
312
245
  cliEmitter.on('cpu', (cpuUsage) => {
313
246
  this.wssSendCpuUpdate(cpuUsage);
314
247
  });
315
- // Endpoint to validate login code
316
- // curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
317
248
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
318
249
  const { password } = req.body;
319
250
  this.log.debug('The frontend sent /api/login', password);
@@ -332,27 +263,23 @@ export class Frontend extends EventEmitter {
332
263
  this.log.warn('/api/login error wrong password');
333
264
  res.json({ valid: false });
334
265
  }
335
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
336
266
  }
337
267
  catch (error) {
338
268
  this.log.error('/api/login error getting password');
339
269
  res.json({ valid: false });
340
270
  }
341
271
  });
342
- // Endpoint to provide health check for docker
343
272
  this.expressApp.get('/health', (req, res) => {
344
273
  this.log.debug('Express received /health');
345
274
  const healthStatus = {
346
- status: 'ok', // Indicate service is healthy
347
- uptime: process.uptime(), // Server uptime in seconds
348
- timestamp: new Date().toISOString(), // Current timestamp
275
+ status: 'ok',
276
+ uptime: process.uptime(),
277
+ timestamp: new Date().toISOString(),
349
278
  };
350
279
  res.status(200).json(healthStatus);
351
280
  });
352
- // Endpoint to provide memory usage details
353
281
  this.expressApp.get('/memory', async (req, res) => {
354
282
  this.log.debug('Express received /memory');
355
- // Memory usage from process
356
283
  const memoryUsageRaw = process.memoryUsage();
357
284
  const memoryUsage = {
358
285
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -361,13 +288,10 @@ export class Frontend extends EventEmitter {
361
288
  external: this.formatMemoryUsage(memoryUsageRaw.external),
362
289
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
363
290
  };
364
- // V8 heap statistics
365
291
  const { default: v8 } = await import('node:v8');
366
292
  const heapStatsRaw = v8.getHeapStatistics();
367
293
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
368
- // Format heapStats
369
294
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
370
- // Format heapSpaces
371
295
  const heapSpaces = heapSpacesRaw.map((space) => ({
372
296
  ...space,
373
297
  space_size: this.formatMemoryUsage(space.space_size),
@@ -385,23 +309,19 @@ export class Frontend extends EventEmitter {
385
309
  };
386
310
  res.status(200).json(memoryReport);
387
311
  });
388
- // Endpoint to provide settings
389
312
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
390
313
  this.log.debug('The frontend sent /api/settings');
391
314
  res.json(await this.getApiSettings());
392
315
  });
393
- // Endpoint to provide plugins
394
316
  this.expressApp.get('/api/plugins', async (req, res) => {
395
317
  this.log.debug('The frontend sent /api/plugins');
396
318
  res.json(this.getPlugins());
397
319
  });
398
- // Endpoint to provide devices
399
320
  this.expressApp.get('/api/devices', async (req, res) => {
400
321
  this.log.debug('The frontend sent /api/devices');
401
322
  const devices = await this.getDevices();
402
323
  res.json(devices);
403
324
  });
404
- // Endpoint to view the matterbridge log
405
325
  this.expressApp.get('/api/view-mblog', async (req, res) => {
406
326
  this.log.debug('The frontend sent /api/view-mblog');
407
327
  try {
@@ -414,7 +334,6 @@ export class Frontend extends EventEmitter {
414
334
  res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
415
335
  }
416
336
  });
417
- // Endpoint to view the matter.js log
418
337
  this.expressApp.get('/api/view-mjlog', async (req, res) => {
419
338
  this.log.debug('The frontend sent /api/view-mjlog');
420
339
  try {
@@ -427,11 +346,9 @@ export class Frontend extends EventEmitter {
427
346
  res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
428
347
  }
429
348
  });
430
- // Endpoint to view the diagnostic.log
431
349
  this.expressApp.get('/api/view-diagnostic', async (req, res) => {
432
350
  this.log.debug('The frontend sent /api/view-diagnostic');
433
351
  const serverNodes = [];
434
- // istanbul ignore else
435
352
  if (this.matterbridge.bridgeMode === 'bridge') {
436
353
  if (this.matterbridge.serverNode)
437
354
  serverNodes.push(this.matterbridge.serverNode);
@@ -442,7 +359,6 @@ export class Frontend extends EventEmitter {
442
359
  serverNodes.push(plugin.serverNode);
443
360
  }
444
361
  }
445
- // istanbul ignore next
446
362
  for (const device of this.matterbridge.getDevices()) {
447
363
  if (device.serverNode)
448
364
  serverNodes.push(device.serverNode);
@@ -465,20 +381,17 @@ export class Frontend extends EventEmitter {
465
381
  values: [...serverNodes],
466
382
  })));
467
383
  delete Logger.destinations.diagnostic;
468
- await wait(500); // Wait for the log to be written
384
+ await wait(500);
469
385
  try {
470
386
  const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'), 'utf8');
471
387
  res.type('text/plain');
472
388
  res.send(data.slice(29));
473
389
  }
474
390
  catch (error) {
475
- // istanbul ignore next
476
391
  this.log.error(`Error reading diagnostic log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
477
- // istanbul ignore next
478
392
  res.status(500).send('Error reading diagnostic log file.');
479
393
  }
480
394
  });
481
- // Endpoint to view the shelly log
482
395
  this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
483
396
  this.log.debug('The frontend sent /api/shellyviewsystemlog');
484
397
  try {
@@ -491,7 +404,6 @@ export class Frontend extends EventEmitter {
491
404
  res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
492
405
  }
493
406
  });
494
- // Endpoint to download the matterbridge log
495
407
  this.expressApp.get('/api/download-mblog', async (req, res) => {
496
408
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
497
409
  try {
@@ -505,14 +417,12 @@ export class Frontend extends EventEmitter {
505
417
  }
506
418
  res.type('text/plain');
507
419
  res.download(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'matterbridge.log', (error) => {
508
- /* istanbul ignore if */
509
420
  if (error) {
510
421
  this.log.error(`Error downloading log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
511
422
  res.status(500).send('Error downloading the matterbridge log file');
512
423
  }
513
424
  });
514
425
  });
515
- // Endpoint to download the matter log
516
426
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
517
427
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
518
428
  try {
@@ -526,14 +436,12 @@ export class Frontend extends EventEmitter {
526
436
  }
527
437
  res.type('text/plain');
528
438
  res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
529
- /* istanbul ignore if */
530
439
  if (error) {
531
440
  this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
532
441
  res.status(500).send('Error downloading the matter log file');
533
442
  }
534
443
  });
535
444
  });
536
- // Endpoint to download the shelly log
537
445
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
538
446
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
539
447
  try {
@@ -547,90 +455,74 @@ export class Frontend extends EventEmitter {
547
455
  }
548
456
  res.type('text/plain');
549
457
  res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
550
- /* istanbul ignore if */
551
458
  if (error) {
552
459
  this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
553
460
  res.status(500).send('Error downloading Shelly system log file');
554
461
  }
555
462
  });
556
463
  });
557
- // Endpoint to download the matterbridge storage directory
558
464
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
559
465
  this.log.debug('The frontend sent /api/download-mbstorage');
560
466
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
561
467
  res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), `matterbridge.${this.matterbridge.nodeStorageName}.zip`, (error) => {
562
- /* istanbul ignore if */
563
468
  if (error) {
564
469
  this.log.error(`Error downloading file ${`matterbridge.${this.matterbridge.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
565
470
  res.status(500).send('Error downloading the matterbridge storage file');
566
471
  }
567
472
  });
568
473
  });
569
- // Endpoint to download the matter storage file
570
474
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
571
475
  this.log.debug('The frontend sent /api/download-mjstorage');
572
476
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
573
477
  res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), `matterbridge.${this.matterbridge.matterStorageName}.zip`, (error) => {
574
- /* istanbul ignore if */
575
478
  if (error) {
576
479
  this.log.error(`Error downloading the matter storage matterbridge.${this.matterbridge.matterStorageName}.zip: ${error instanceof Error ? error.message : error}`);
577
480
  res.status(500).send('Error downloading the matter storage zip file');
578
481
  }
579
482
  });
580
483
  });
581
- // Endpoint to download the matterbridge plugin directory
582
484
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
583
485
  this.log.debug('The frontend sent /api/download-pluginstorage');
584
486
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
585
487
  res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
586
- /* istanbul ignore if */
587
488
  if (error) {
588
489
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
589
490
  res.status(500).send('Error downloading the matterbridge plugin storage file');
590
491
  }
591
492
  });
592
493
  });
593
- // Endpoint to download the matterbridge plugin config files
594
494
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
595
495
  this.log.debug('The frontend sent /api/download-pluginconfig');
596
496
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
597
497
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
598
- /* istanbul ignore if */
599
498
  if (error) {
600
499
  this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
601
500
  res.status(500).send('Error downloading the matterbridge plugin config file');
602
501
  }
603
502
  });
604
503
  });
605
- // Endpoint to download the matterbridge backup (created with the backup command)
606
504
  this.expressApp.get('/api/download-backup', async (req, res) => {
607
505
  this.log.debug('The frontend sent /api/download-backup');
608
506
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
609
- /* istanbul ignore if */
610
507
  if (error) {
611
508
  this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
612
509
  res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
613
510
  }
614
511
  });
615
512
  });
616
- // Endpoint to upload a package
617
513
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
618
514
  const { filename } = req.body;
619
515
  const file = req.file;
620
- /* istanbul ignore if */
621
516
  if (!file || !filename) {
622
517
  this.log.error(`uploadpackage: invalid request: file and filename are required`);
623
518
  res.status(400).send('Invalid request: file and filename are required');
624
519
  return;
625
520
  }
626
521
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
627
- // Define the path where the plugin file will be saved
628
522
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
629
523
  try {
630
- // Move the uploaded file to the specified path
631
524
  await fs.rename(file.path, filePath);
632
525
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
633
- // Install the plugin package
634
526
  if (filename.endsWith('.tgz')) {
635
527
  const { spawnCommand } = await import('./utils/spawn.js');
636
528
  await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose']);
@@ -650,7 +542,6 @@ export class Frontend extends EventEmitter {
650
542
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
651
543
  }
652
544
  });
653
- // Fallback for routing (must be the last route)
654
545
  this.expressApp.use((req, res) => {
655
546
  this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
656
547
  res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -659,16 +550,13 @@ export class Frontend extends EventEmitter {
659
550
  }
660
551
  async stop() {
661
552
  this.log.debug('Stopping the frontend...');
662
- // Remove listeners from the express app
663
553
  if (this.expressApp) {
664
554
  this.expressApp.removeAllListeners();
665
555
  this.expressApp = undefined;
666
556
  this.log.debug('Frontend app closed successfully');
667
557
  }
668
- // Close the WebSocket server
669
558
  if (this.webSocketServer) {
670
559
  this.log.debug('Closing WebSocket server...');
671
- // Close all active connections
672
560
  this.webSocketServer.clients.forEach((client) => {
673
561
  if (client.readyState === WebSocket.OPEN) {
674
562
  client.close();
@@ -677,7 +565,6 @@ export class Frontend extends EventEmitter {
677
565
  await withTimeout(new Promise((resolve) => {
678
566
  this.webSocketServer?.close((error) => {
679
567
  if (error) {
680
- // istanbul ignore next
681
568
  this.log.error(`Error closing WebSocket server: ${error}`);
682
569
  }
683
570
  else {
@@ -690,27 +577,8 @@ export class Frontend extends EventEmitter {
690
577
  this.webSocketServer.removeAllListeners();
691
578
  this.webSocketServer = undefined;
692
579
  }
693
- // Close the http server
694
580
  if (this.httpServer) {
695
581
  this.log.debug('Closing http server...');
696
- /*
697
- await withTimeout(
698
- new Promise<void>((resolve) => {
699
- this.httpServer?.close((error) => {
700
- if (error) {
701
- // istanbul ignore next
702
- this.log.error(`Error closing http server: ${error}`);
703
- } else {
704
- this.log.debug('Http server closed successfully');
705
- this.emit('server_stopped');
706
- }
707
- resolve();
708
- });
709
- }),
710
- 5000,
711
- false,
712
- );
713
- */
714
582
  this.httpServer.close();
715
583
  this.log.debug('Http server closed successfully');
716
584
  this.emit('server_stopped');
@@ -718,27 +586,8 @@ export class Frontend extends EventEmitter {
718
586
  this.httpServer = undefined;
719
587
  this.log.debug('Frontend http server closed successfully');
720
588
  }
721
- // Close the https server
722
589
  if (this.httpsServer) {
723
590
  this.log.debug('Closing https server...');
724
- /*
725
- await withTimeout(
726
- new Promise<void>((resolve) => {
727
- this.httpsServer?.close((error) => {
728
- if (error) {
729
- // istanbul ignore next
730
- this.log.error(`Error closing https server: ${error}`);
731
- } else {
732
- this.log.debug('Https server closed successfully');
733
- this.emit('server_stopped');
734
- }
735
- resolve();
736
- });
737
- }),
738
- 5000,
739
- false,
740
- );
741
- */
742
591
  this.httpsServer.close();
743
592
  this.log.debug('Https server closed successfully');
744
593
  this.emit('server_stopped');
@@ -748,7 +597,6 @@ export class Frontend extends EventEmitter {
748
597
  }
749
598
  this.log.debug('Frontend stopped successfully');
750
599
  }
751
- // Function to format bytes to KB, MB, or GB
752
600
  formatMemoryUsage = (bytes) => {
753
601
  if (bytes >= 1024 ** 3) {
754
602
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -760,7 +608,6 @@ export class Frontend extends EventEmitter {
760
608
  return `${(bytes / 1024).toFixed(2)} KB`;
761
609
  }
762
610
  };
763
- // Function to format system uptime with only the most significant unit
764
611
  formatOsUpTime = (seconds) => {
765
612
  if (seconds >= 86400) {
766
613
  const days = Math.floor(seconds / 86400);
@@ -776,13 +623,7 @@ export class Frontend extends EventEmitter {
776
623
  }
777
624
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
778
625
  };
779
- /**
780
- * Retrieves the api settings data.
781
- *
782
- * @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
783
- */
784
626
  async getApiSettings() {
785
- // Update the system information
786
627
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
787
628
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
788
629
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -791,7 +632,6 @@ export class Frontend extends EventEmitter {
791
632
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
792
633
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
793
634
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
794
- // Update the matterbridge information
795
635
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
796
636
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
797
637
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
@@ -805,12 +645,6 @@ export class Frontend extends EventEmitter {
805
645
  this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
806
646
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
807
647
  }
808
- /**
809
- * Retrieves the reachable attribute.
810
- *
811
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
812
- * @returns {boolean} The reachable attribute.
813
- */
814
648
  getReachability(device) {
815
649
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
816
650
  return false;
@@ -822,12 +656,6 @@ export class Frontend extends EventEmitter {
822
656
  return true;
823
657
  return false;
824
658
  }
825
- /**
826
- * Retrieves the power source attribute.
827
- *
828
- * @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
829
- * @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
830
- */
831
659
  getPowerSource(endpoint) {
832
660
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
833
661
  return undefined;
@@ -843,22 +671,13 @@ export class Frontend extends EventEmitter {
843
671
  }
844
672
  return;
845
673
  };
846
- // Root endpoint
847
674
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
848
675
  return powerSource(endpoint);
849
- // Child endpoints
850
676
  for (const child of endpoint.getChildEndpoints()) {
851
677
  if (child.hasClusterServer(PowerSource.Cluster.id))
852
678
  return powerSource(child);
853
679
  }
854
680
  }
855
- /**
856
- * Retrieves the cluster text description from a given device.
857
- * The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
858
- *
859
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
860
- * @returns {string} The attributes description of the cluster servers in the device.
861
- */
862
681
  getClusterTextFromDevice(device) {
863
682
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
864
683
  return '';
@@ -869,7 +688,6 @@ export class Frontend extends EventEmitter {
869
688
  if (composed)
870
689
  return 'Composed: ' + composed.value;
871
690
  }
872
- // istanbul ignore next cause is not reachable
873
691
  return '';
874
692
  };
875
693
  const getFixedLabel = (device) => {
@@ -879,13 +697,11 @@ export class Frontend extends EventEmitter {
879
697
  if (composed)
880
698
  return 'Composed: ' + composed.value;
881
699
  }
882
- // istanbul ignore next cause is not reacheable
883
700
  return '';
884
701
  };
885
702
  let attributes = '';
886
703
  let supportedModes = [];
887
704
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
888
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
889
705
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
890
706
  return;
891
707
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -975,17 +791,11 @@ export class Frontend extends EventEmitter {
975
791
  if (clusterName === 'userLabel' && attributeName === 'labelList')
976
792
  attributes += `${getUserLabel(device)} `;
977
793
  });
978
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
979
794
  return attributes.trimStart().trimEnd();
980
795
  }
981
- /**
982
- * Retrieves the registered plugins sanitized for res.json().
983
- *
984
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
985
- */
986
796
  getPlugins() {
987
797
  if (this.matterbridge.hasCleanupStarted)
988
- return []; // Skip if cleanup has started
798
+ return [];
989
799
  const baseRegisteredPlugins = [];
990
800
  for (const plugin of this.matterbridge.plugins) {
991
801
  baseRegisteredPlugins.push({
@@ -1013,27 +823,18 @@ export class Frontend extends EventEmitter {
1013
823
  schemaJson: plugin.schemaJson,
1014
824
  hasWhiteList: plugin.configJson?.whiteList !== undefined,
1015
825
  hasBlackList: plugin.configJson?.blackList !== undefined,
1016
- // Childbridge mode specific data
1017
826
  matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
1018
827
  });
1019
828
  }
1020
829
  return baseRegisteredPlugins;
1021
830
  }
1022
- /**
1023
- * Retrieves the devices from Matterbridge.
1024
- *
1025
- * @param {string} [pluginName] - The name of the plugin to filter devices by.
1026
- * @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
1027
- */
1028
831
  async getDevices(pluginName) {
1029
832
  if (this.matterbridge.hasCleanupStarted)
1030
- return []; // Skip if cleanup has started
833
+ return [];
1031
834
  const devices = [];
1032
835
  for (const device of this.matterbridge.devices.array()) {
1033
- // Filter by pluginName if provided
1034
836
  if (pluginName && pluginName !== device.plugin)
1035
837
  continue;
1036
- // Check if the device has the required properties
1037
838
  if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1038
839
  continue;
1039
840
  devices.push({
@@ -1053,37 +854,22 @@ export class Frontend extends EventEmitter {
1053
854
  }
1054
855
  return devices;
1055
856
  }
1056
- /**
1057
- * Retrieves the clusters from a given plugin and endpoint number.
1058
- *
1059
- * Response for /api/clusters
1060
- *
1061
- * @param {string} pluginName - The name of the plugin.
1062
- * @param {number} endpointNumber - The endpoint number.
1063
- * @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
1064
- */
1065
857
  getClusters(pluginName, endpointNumber) {
1066
858
  const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
1067
859
  if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.deviceName || !endpoint.serialNumber) {
1068
860
  this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1069
861
  return;
1070
862
  }
1071
- // this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
1072
- // Get the device types from the main endpoint
1073
863
  const deviceTypes = [];
1074
864
  const clusters = [];
1075
865
  endpoint.state.descriptor.deviceTypeList.forEach((d) => {
1076
866
  deviceTypes.push(d.deviceType);
1077
867
  });
1078
- // Get the clusters from the main endpoint
1079
868
  endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1080
869
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1081
870
  return;
1082
871
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1083
872
  return;
1084
- // console.log(
1085
- // `${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}`,
1086
- // );
1087
873
  clusters.push({
1088
874
  endpoint: endpoint.number.toString(),
1089
875
  id: 'main',
@@ -1096,19 +882,12 @@ export class Frontend extends EventEmitter {
1096
882
  attributeLocalValue: attributeValue,
1097
883
  });
1098
884
  });
1099
- // Get the child endpoints
1100
885
  const childEndpoints = endpoint.getChildEndpoints();
1101
- // if (childEndpoints.length === 0) {
1102
- // this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
1103
- // }
1104
886
  childEndpoints.forEach((childEndpoint) => {
1105
- // istanbul ignore if cause is not reachable: should never happen but ...
1106
887
  if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
1107
888
  this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1108
889
  return;
1109
890
  }
1110
- // this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
1111
- // Get the device types of the child endpoint
1112
891
  const deviceTypes = [];
1113
892
  childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
1114
893
  deviceTypes.push(d.deviceType);
@@ -1118,12 +897,9 @@ export class Frontend extends EventEmitter {
1118
897
  return;
1119
898
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1120
899
  return;
1121
- // console.log(
1122
- // `${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}`,
1123
- // );
1124
900
  clusters.push({
1125
901
  endpoint: childEndpoint.number.toString(),
1126
- id: childEndpoint.maybeId ?? 'null', // Never happens
902
+ id: childEndpoint.maybeId ?? 'null',
1127
903
  deviceTypes,
1128
904
  clusterName: capitalizeFirstLetter(clusterName),
1129
905
  clusterId: '0x' + clusterId.toString(16).padStart(2, '0'),
@@ -1136,13 +912,6 @@ export class Frontend extends EventEmitter {
1136
912
  });
1137
913
  return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, endpoint: endpoint.maybeNumber, deviceTypes, clusters };
1138
914
  }
1139
- /**
1140
- * Handles incoming websocket api request messages from the Matterbridge frontend.
1141
- *
1142
- * @param {WebSocket} client - The websocket client that sent the message.
1143
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1144
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1145
- */
1146
915
  async wsMessageHandler(client, message) {
1147
916
  let data;
1148
917
  const sendResponse = (data) => {
@@ -1162,7 +931,7 @@ export class Frontend extends EventEmitter {
1162
931
  };
1163
932
  try {
1164
933
  data = JSON.parse(message.toString());
1165
- if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) /* || !isValidObject(data.params)*/ || data.dst !== 'Matterbridge') {
934
+ if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || data.dst !== 'Matterbridge') {
1166
935
  this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
1167
936
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
1168
937
  return;
@@ -1205,48 +974,35 @@ export class Frontend extends EventEmitter {
1205
974
  this.wssSendSnackbarMessage(`Installed package ${localData.params.packageName}`, 5, 'success');
1206
975
  const packageName = localData.params.packageName.replace(/@.*$/, '');
1207
976
  if (localData.params.restart === false && packageName !== 'matterbridge') {
1208
- // The install comes from InstallPlugins
1209
977
  this.matterbridge.plugins
1210
978
  .add(packageName)
1211
979
  .then((plugin) => {
1212
- // istanbul ignore next if
1213
980
  if (plugin) {
1214
- // The plugin is not registered
1215
981
  this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
1216
- // In childbridge mode the plugins server node is not started when added
1217
982
  if (this.matterbridge.bridgeMode === 'childbridge')
1218
983
  this.wssSendRestartRequired(true, true);
1219
984
  this.matterbridge.plugins
1220
985
  .load(plugin, true, 'The plugin has been added', true)
1221
- // eslint-disable-next-line promise/no-nesting
1222
986
  .then(() => {
1223
987
  this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
1224
988
  this.wssSendRefreshRequired('plugins');
1225
989
  return;
1226
990
  })
1227
- // eslint-disable-next-line promise/no-nesting
1228
991
  .catch((_error) => {
1229
- //
1230
992
  });
1231
993
  }
1232
994
  else {
1233
- // The plugin is already registered
1234
995
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
1235
996
  this.wssSendRefreshRequired('plugins');
1236
997
  this.wssSendRestartRequired(true, true);
1237
998
  }
1238
999
  return;
1239
1000
  })
1240
- // eslint-disable-next-line promise/no-nesting
1241
1001
  .catch((_error) => {
1242
- //
1243
1002
  });
1244
1003
  }
1245
1004
  else {
1246
- // The package is matterbridge
1247
- // istanbul ignore next
1248
1005
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
1249
- // istanbul ignore next if
1250
1006
  if (this.matterbridge.restartMode !== '') {
1251
1007
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1252
1008
  this.matterbridge.shutdownProcess();
@@ -1269,9 +1025,7 @@ export class Frontend extends EventEmitter {
1269
1025
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' });
1270
1026
  return;
1271
1027
  }
1272
- // The package is a plugin
1273
1028
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
1274
- // istanbul ignore next if
1275
1029
  if (plugin) {
1276
1030
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
1277
1031
  await this.matterbridge.plugins.remove(data.params.packageName);
@@ -1279,7 +1033,6 @@ export class Frontend extends EventEmitter {
1279
1033
  this.wssSendRefreshRequired('plugins');
1280
1034
  this.wssSendRefreshRequired('devices');
1281
1035
  }
1282
- // Uninstall the package
1283
1036
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1284
1037
  const { spawnCommand } = await import('./utils/spawn.js');
1285
1038
  spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
@@ -1322,7 +1075,6 @@ export class Frontend extends EventEmitter {
1322
1075
  return;
1323
1076
  })
1324
1077
  .catch((_error) => {
1325
- //
1326
1078
  });
1327
1079
  }
1328
1080
  else {
@@ -1370,7 +1122,6 @@ export class Frontend extends EventEmitter {
1370
1122
  return;
1371
1123
  })
1372
1124
  .catch((_error) => {
1373
- //
1374
1125
  });
1375
1126
  }
1376
1127
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -1396,7 +1147,6 @@ export class Frontend extends EventEmitter {
1396
1147
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1397
1148
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
1398
1149
  if (plugin.serverNode) {
1399
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1400
1150
  await this.matterbridge.stopServerNode(plugin.serverNode);
1401
1151
  plugin.serverNode = undefined;
1402
1152
  }
@@ -1406,20 +1156,18 @@ export class Frontend extends EventEmitter {
1406
1156
  this.matterbridge.devices.remove(device);
1407
1157
  }
1408
1158
  }
1409
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1410
1159
  if (plugin.type === 'DynamicPlatform' && !plugin.locked)
1411
1160
  await this.matterbridge.createDynamicPlugin(plugin);
1412
1161
  await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
1413
- plugin.restartRequired = false; // Reset plugin restartRequired
1162
+ plugin.restartRequired = false;
1414
1163
  let needRestart = 0;
1415
1164
  for (const plugin of this.matterbridge.plugins) {
1416
1165
  if (plugin.restartRequired)
1417
1166
  needRestart++;
1418
1167
  }
1419
1168
  if (needRestart === 0) {
1420
- this.wssSendRestartNotRequired(true); // Reset global restart required message
1169
+ this.wssSendRestartNotRequired(true);
1421
1170
  }
1422
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1423
1171
  if (plugin.serverNode)
1424
1172
  await this.matterbridge.startServerNode(plugin.serverNode);
1425
1173
  this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
@@ -1467,8 +1215,8 @@ export class Frontend extends EventEmitter {
1467
1215
  }
1468
1216
  else if (data.method === '/api/shellynetconfig') {
1469
1217
  this.log.debug('/api/shellynetconfig:', data.params);
1470
- const { triggerShellyChangeIp: triggerShellyChangeNet } = await import('./shelly.js');
1471
- triggerShellyChangeNet(this.matterbridge, data.params);
1218
+ const { triggerShellyChangeIp } = await import('./shelly.js');
1219
+ triggerShellyChangeIp(this.matterbridge, data.params);
1472
1220
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
1473
1221
  }
1474
1222
  else if (data.method === '/api/softreset') {
@@ -1612,7 +1360,7 @@ export class Frontend extends EventEmitter {
1612
1360
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/devices' });
1613
1361
  return;
1614
1362
  }
1615
- const selectDeviceValues = plugin.platform?.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1363
+ const selectDeviceValues = !plugin.platform ? [] : plugin.platform.getSelectDevices().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1616
1364
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectDeviceValues });
1617
1365
  }
1618
1366
  else if (data.method === '/api/select/entities') {
@@ -1625,7 +1373,7 @@ export class Frontend extends EventEmitter {
1625
1373
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Plugin not found in /api/select/entities' });
1626
1374
  return;
1627
1375
  }
1628
- const selectEntityValues = plugin.platform?.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1376
+ const selectEntityValues = !plugin.platform ? [] : plugin.platform.getSelectEntities().sort((keyA, keyB) => keyA.name.localeCompare(keyB.name));
1629
1377
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true, response: selectEntityValues });
1630
1378
  }
1631
1379
  else if (data.method === '/api/action') {
@@ -1675,22 +1423,22 @@ export class Frontend extends EventEmitter {
1675
1423
  if (isValidString(data.params.value, 4)) {
1676
1424
  this.log.debug('Matterbridge logger level:', data.params.value);
1677
1425
  if (data.params.value === 'Debug') {
1678
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1426
+ await this.matterbridge.setLogLevel("debug");
1679
1427
  }
1680
1428
  else if (data.params.value === 'Info') {
1681
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1429
+ await this.matterbridge.setLogLevel("info");
1682
1430
  }
1683
1431
  else if (data.params.value === 'Notice') {
1684
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1432
+ await this.matterbridge.setLogLevel("notice");
1685
1433
  }
1686
1434
  else if (data.params.value === 'Warn') {
1687
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1435
+ await this.matterbridge.setLogLevel("warn");
1688
1436
  }
1689
1437
  else if (data.params.value === 'Error') {
1690
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1438
+ await this.matterbridge.setLogLevel("error");
1691
1439
  }
1692
1440
  else if (data.params.value === 'Fatal') {
1693
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1441
+ await this.matterbridge.setLogLevel("fatal");
1694
1442
  }
1695
1443
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1696
1444
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -1701,7 +1449,6 @@ export class Frontend extends EventEmitter {
1701
1449
  this.log.debug('Matterbridge file log:', data.params.value);
1702
1450
  this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
1703
1451
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1704
- // Create the file logger for matterbridge
1705
1452
  if (data.params.value)
1706
1453
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1707
1454
  else
@@ -1780,7 +1527,6 @@ export class Frontend extends EventEmitter {
1780
1527
  }
1781
1528
  break;
1782
1529
  case 'setmatterport':
1783
- // eslint-disable-next-line no-case-declarations
1784
1530
  const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1785
1531
  if (isValidNumber(port, 5540, 5600)) {
1786
1532
  this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
@@ -1798,9 +1544,8 @@ export class Frontend extends EventEmitter {
1798
1544
  }
1799
1545
  break;
1800
1546
  case 'setmatterdiscriminator':
1801
- // eslint-disable-next-line no-case-declarations
1802
1547
  const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1803
- if (isValidNumber(discriminator, 1000, 4095)) {
1548
+ if (isValidNumber(discriminator, 0, 4095)) {
1804
1549
  this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
1805
1550
  this.matterbridge.matterbridgeInformation.matterDiscriminator = discriminator;
1806
1551
  await this.matterbridge.nodeContext?.set('matterdiscriminator', discriminator);
@@ -1816,9 +1561,8 @@ export class Frontend extends EventEmitter {
1816
1561
  }
1817
1562
  break;
1818
1563
  case 'setmatterpasscode':
1819
- // eslint-disable-next-line no-case-declarations
1820
1564
  const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1821
- if (isValidNumber(passcode, 10000000, 90000000)) {
1565
+ if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
1822
1566
  this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
1823
1567
  this.log.debug(`Set matter commissioning passcode to ${CYAN}${passcode}${db}`);
1824
1568
  await this.matterbridge.nodeContext?.set('matterpasscode', passcode);
@@ -1860,19 +1604,15 @@ export class Frontend extends EventEmitter {
1860
1604
  return;
1861
1605
  }
1862
1606
  const config = plugin.configJson;
1863
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1864
1607
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1865
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1866
1608
  if (select === 'serial')
1867
1609
  this.log.info(`Selected device serial ${data.params.serial}`);
1868
1610
  if (select === 'name')
1869
1611
  this.log.info(`Selected device name ${data.params.name}`);
1870
1612
  if (config && select && (select === 'serial' || select === 'name')) {
1871
- // Remove postfix from the serial if it exists
1872
1613
  if (config.postfix) {
1873
1614
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1874
1615
  }
1875
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1876
1616
  if (isValidArray(config.whiteList, 1)) {
1877
1617
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1878
1618
  config.whiteList.push(data.params.serial);
@@ -1881,7 +1621,6 @@ export class Frontend extends EventEmitter {
1881
1621
  config.whiteList.push(data.params.name);
1882
1622
  }
1883
1623
  }
1884
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1885
1624
  if (isValidArray(config.blackList, 1)) {
1886
1625
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1887
1626
  config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
@@ -1909,9 +1648,7 @@ export class Frontend extends EventEmitter {
1909
1648
  return;
1910
1649
  }
1911
1650
  const config = plugin.configJson;
1912
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1913
1651
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1914
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1915
1652
  if (select === 'serial')
1916
1653
  this.log.info(`Unselected device serial ${data.params.serial}`);
1917
1654
  if (select === 'name')
@@ -1920,7 +1657,6 @@ export class Frontend extends EventEmitter {
1920
1657
  if (config.postfix) {
1921
1658
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1922
1659
  }
1923
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1924
1660
  if (isValidArray(config.whiteList, 1)) {
1925
1661
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1926
1662
  config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
@@ -1929,7 +1665,6 @@ export class Frontend extends EventEmitter {
1929
1665
  config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
1930
1666
  }
1931
1667
  }
1932
- // Add the serial to the blackList
1933
1668
  if (isValidArray(config.blackList)) {
1934
1669
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1935
1670
  config.blackList.push(data.params.serial);
@@ -1952,7 +1687,6 @@ export class Frontend extends EventEmitter {
1952
1687
  }
1953
1688
  }
1954
1689
  else {
1955
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1956
1690
  const localData = data;
1957
1691
  this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
1958
1692
  sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
@@ -1962,206 +1696,83 @@ export class Frontend extends EventEmitter {
1962
1696
  inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
1963
1697
  }
1964
1698
  }
1965
- /**
1966
- * Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1967
- *
1968
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1969
- * @param {string} time - The time string of the message
1970
- * @param {string} name - The logger name of the message
1971
- * @param {string} message - The content of the message.
1972
- *
1973
- * @remarks
1974
- * The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
1975
- * It also replaces all occurrences of \" with " and angle-brackets with &lt; and &gt;.
1976
- * The function sends the message to all connected clients.
1977
- */
1978
1699
  wssSendLogMessage(level, time, name, message) {
1979
1700
  if (!level || !time || !name || !message)
1980
1701
  return;
1981
- // Remove ANSI escape codes from the message
1982
- // eslint-disable-next-line no-control-regex
1983
1702
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1984
- // Remove leading asterisks from the message
1985
1703
  message = message.replace(/^\*+/, '');
1986
- // Replace all occurrences of \t and \n
1987
1704
  message = message.replace(/[\t\n]/g, '');
1988
- // Remove non-printable characters
1989
- // eslint-disable-next-line no-control-regex
1990
1705
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
1991
- // Replace all occurrences of \" with "
1992
1706
  message = message.replace(/\\"/g, '"');
1993
- // Replace all occurrences of angle-brackets with &lt; and &gt;"
1994
1707
  message = message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1995
- // Define the maximum allowed length for continuous characters without a space
1996
1708
  const maxContinuousLength = 100;
1997
1709
  const keepStartLength = 20;
1998
1710
  const keepEndLength = 20;
1999
- // Split the message into words
2000
1711
  message = message
2001
1712
  .split(' ')
2002
1713
  .map((word) => {
2003
- // If the word length exceeds the max continuous length, insert spaces and truncate
2004
1714
  if (word.length > maxContinuousLength) {
2005
1715
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
2006
1716
  }
2007
1717
  return word;
2008
1718
  })
2009
1719
  .join(' ');
2010
- // Send the message to all connected clients
2011
- this.wssBroadcastMessage({ id: 0 /* WsBroadcastMessageId.Log */, src: 'Matterbridge', dst: 'Frontend', method: 'log', params: { level, time, name, message } });
1720
+ this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', params: { level, time, name, message } });
2012
1721
  }
2013
- /**
2014
- * Sends a need to refresh WebSocket message to all connected clients.
2015
- *
2016
- * @param {string} changed - The changed value.
2017
- * @param {Record<string, unknown>} params - Additional parameters to send with the message.
2018
- * possible values for changed:
2019
- * - 'settings'
2020
- * - 'plugins'
2021
- * - 'devices'
2022
- * - 'matter' with param 'matter' (QRDiv component)
2023
- * @param {ApiMatterResponse} params.matter - The matter device that has changed. Required if changed is 'matter'.
2024
- */
2025
1722
  wssSendRefreshRequired(changed, params) {
2026
1723
  this.log.debug('Sending a refresh required message to all connected clients');
2027
- // Send the message to all connected clients
2028
- this.wssBroadcastMessage({ id: 1 /* WsBroadcastMessageId.RefreshRequired */, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed, ...params } });
1724
+ this.wssBroadcastMessage({ id: 1, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', params: { changed, ...params } });
2029
1725
  }
2030
- /**
2031
- * Sends a need to restart WebSocket message to all connected clients.
2032
- *
2033
- * @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
2034
- * @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
2035
- */
2036
1726
  wssSendRestartRequired(snackbar = true, fixed = false) {
2037
1727
  this.log.debug('Sending a restart required message to all connected clients');
2038
1728
  this.matterbridge.matterbridgeInformation.restartRequired = true;
2039
1729
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
2040
1730
  if (snackbar === true)
2041
1731
  this.wssSendSnackbarMessage(`Restart required`, 0);
2042
- // Send the message to all connected clients
2043
- this.wssBroadcastMessage({ id: 2 /* WsBroadcastMessageId.RestartRequired */, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } });
1732
+ this.wssBroadcastMessage({ id: 2, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: { fixed } });
2044
1733
  }
2045
- /**
2046
- * Sends a no need to restart WebSocket message to all connected clients.
2047
- *
2048
- * @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients. Default is true.
2049
- */
2050
1734
  wssSendRestartNotRequired(snackbar = true) {
2051
1735
  this.log.debug('Sending a restart not required message to all connected clients');
2052
1736
  this.matterbridge.matterbridgeInformation.restartRequired = false;
2053
1737
  if (snackbar === true)
2054
1738
  this.wssSendCloseSnackbarMessage(`Restart required`);
2055
- // Send the message to all connected clients
2056
- this.wssBroadcastMessage({ id: 3 /* WsBroadcastMessageId.RestartNotRequired */, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required' });
1739
+ this.wssBroadcastMessage({ id: 3, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required' });
2057
1740
  }
2058
- /**
2059
- * Sends a need to update WebSocket message to all connected clients.
2060
- *
2061
- * @param {boolean} devVersion - If true, the update is for a development version. Default is false.
2062
- */
2063
1741
  wssSendUpdateRequired(devVersion = false) {
2064
1742
  this.log.debug('Sending an update required message to all connected clients');
2065
1743
  this.matterbridge.matterbridgeInformation.updateRequired = true;
2066
- // Send the message to all connected clients
2067
- this.wssBroadcastMessage({ id: 8 /* WsBroadcastMessageId.UpdateRequired */, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required' });
1744
+ this.wssBroadcastMessage({ id: 8, src: 'Matterbridge', dst: 'Frontend', method: devVersion ? 'update_required_dev' : 'update_required' });
2068
1745
  }
2069
- /**
2070
- * Sends a cpu update message to all connected clients.
2071
- *
2072
- * @param {number} cpuUsage - The CPU usage percentage to send.
2073
- */
2074
1746
  wssSendCpuUpdate(cpuUsage) {
2075
1747
  if (hasParameter('debug'))
2076
1748
  this.log.debug('Sending a cpu update message to all connected clients');
2077
- // Send the message to all connected clients
2078
- this.wssBroadcastMessage({ id: 4 /* WsBroadcastMessageId.CpuUpdate */, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage: Math.round(cpuUsage * 100) / 100 } });
1749
+ this.wssBroadcastMessage({ id: 4, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', params: { cpuUsage: Math.round(cpuUsage * 100) / 100 } });
2079
1750
  }
2080
- /**
2081
- * Sends a memory update message to all connected clients.
2082
- *
2083
- * @param {string} totalMemory - The total memory in bytes.
2084
- * @param {string} freeMemory - The free memory in bytes.
2085
- * @param {string} rss - The resident set size in bytes.
2086
- * @param {string} heapTotal - The total heap memory in bytes.
2087
- * @param {string} heapUsed - The used heap memory in bytes.
2088
- * @param {string} external - The external memory in bytes.
2089
- * @param {string} arrayBuffers - The array buffers memory in bytes.
2090
- */
2091
1751
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
2092
1752
  if (hasParameter('debug'))
2093
1753
  this.log.debug('Sending a memory update message to all connected clients');
2094
- // Send the message to all connected clients
2095
- this.wssBroadcastMessage({ id: 5 /* WsBroadcastMessageId.MemoryUpdate */, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
1754
+ this.wssBroadcastMessage({ id: 5, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', params: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
2096
1755
  }
2097
- /**
2098
- * Sends an uptime update message to all connected clients.
2099
- *
2100
- * @param {string} systemUptime - The system uptime in a human-readable format.
2101
- * @param {string} processUptime - The process uptime in a human-readable format.
2102
- */
2103
1756
  wssSendUptimeUpdate(systemUptime, processUptime) {
2104
1757
  if (hasParameter('debug'))
2105
1758
  this.log.debug('Sending a uptime update message to all connected clients');
2106
- // Send the message to all connected clients
2107
- this.wssBroadcastMessage({ id: 6 /* WsBroadcastMessageId.UptimeUpdate */, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } });
1759
+ this.wssBroadcastMessage({ id: 6, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', params: { systemUptime, processUptime } });
2108
1760
  }
2109
- /**
2110
- * Sends an open snackbar message to all connected clients.
2111
- *
2112
- * @param {string} message - The message to send.
2113
- * @param {number} timeout - The timeout in seconds for the snackbar message. Default is 5 seconds.
2114
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the message.
2115
- * possible values are: 'info', 'warning', 'error', 'success'. Default is 'info'.
2116
- *
2117
- * @remarks
2118
- * If timeout is 0, the snackbar message will be displayed until closed by the user.
2119
- */
2120
1761
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
2121
1762
  this.log.debug('Sending a snackbar message to all connected clients');
2122
- // Send the message to all connected clients
2123
- this.wssBroadcastMessage({ id: 7 /* WsBroadcastMessageId.Snackbar */, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', params: { message, timeout, severity } });
1763
+ this.wssBroadcastMessage({ id: 7, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', params: { message, timeout, severity } });
2124
1764
  }
2125
- /**
2126
- * Sends a close snackbar message to all connected clients.
2127
- * It will close the snackbar message with the same message and timeout = 0.
2128
- *
2129
- * @param {string} message - The message to send.
2130
- */
2131
1765
  wssSendCloseSnackbarMessage(message) {
2132
1766
  this.log.debug('Sending a close snackbar message to all connected clients');
2133
- // Send the message to all connected clients
2134
- this.wssBroadcastMessage({ id: 10 /* WsBroadcastMessageId.CloseSnackbar */, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', params: { message } });
1767
+ this.wssBroadcastMessage({ id: 10, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', params: { message } });
2135
1768
  }
2136
- /**
2137
- * Sends an attribute update message to all connected WebSocket clients.
2138
- *
2139
- * @param {string | undefined} plugin - The name of the plugin.
2140
- * @param {string | undefined} serialNumber - The serial number of the device.
2141
- * @param {string | undefined} uniqueId - The unique identifier of the device.
2142
- * @param {string} cluster - The cluster name where the attribute belongs.
2143
- * @param {string} attribute - The name of the attribute that changed.
2144
- * @param {number | string | boolean} value - The new value of the attribute.
2145
- *
2146
- * @remarks
2147
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
2148
- * with the updated attribute information.
2149
- */
2150
1769
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, cluster, attribute, value) {
2151
1770
  this.log.debug('Sending an attribute update message to all connected clients');
2152
- // Send the message to all connected clients
2153
- this.wssBroadcastMessage({ id: 9 /* WsBroadcastMessageId.StateUpdate */, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } });
1771
+ this.wssBroadcastMessage({ id: 9, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', params: { plugin, serialNumber, uniqueId, cluster, attribute, value } });
2154
1772
  }
2155
- /**
2156
- * Sends a message to all connected clients.
2157
- * This is an helper function to send a broadcast message to all connected clients.
2158
- *
2159
- * @param {WsMessageBroadcast} msg - The message to send.
2160
- */
2161
1773
  wssBroadcastMessage(msg) {
2162
- // Send the message to all connected clients
2163
1774
  const stringifiedMsg = JSON.stringify(msg);
2164
- if (msg.id !== 0 /* WsBroadcastMessageId.Log */)
1775
+ if (msg.id !== 0)
2165
1776
  this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
2166
1777
  this.webSocketServer?.clients.forEach((client) => {
2167
1778
  if (client.readyState === WebSocket.OPEN) {
@@ -2170,4 +1781,3 @@ export class Frontend extends EventEmitter {
2170
1781
  });
2171
1782
  }
2172
1783
  }
2173
- //# sourceMappingURL=frontend.js.map