matterbridge 3.2.9 → 3.3.0-dev-20250928-3d2b558

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 (291) hide show
  1. package/CHANGELOG.md +83 -0
  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 +29 -418
  33. package/dist/frontendTypes.js +0 -45
  34. package/dist/globalMatterbridge.js +0 -47
  35. package/dist/helpers.js +0 -53
  36. package/dist/index.js +2 -31
  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 +52 -785
  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 +58 -1398
  50. package/dist/matterbridgeEndpointHelpers.js +12 -345
  51. package/dist/matterbridgePlatform.js +32 -326
  52. package/dist/matterbridgeTypes.js +0 -25
  53. package/dist/pluginManager.js +3 -249
  54. package/dist/shelly.js +7 -168
  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 -69
  71. package/dist/utils/wait.js +8 -60
  72. package/frontend/build/assets/index.js +7 -7
  73. package/frontend/build/assets/vendor_mui.js +23 -31
  74. package/frontend/build/assets/vendor_node_modules.js +33 -25
  75. package/frontend/build/assets/vendor_notistack.js +2 -2
  76. package/frontend/build/assets/vendor_qrcode.js +1 -1
  77. package/frontend/build/assets/vendor_rjsf.js +7 -7
  78. package/frontend/package-lock.json +401 -310
  79. package/frontend/package.json +9 -9
  80. package/npm-shrinkwrap.json +2 -2
  81. package/package.json +1 -2
  82. package/dist/cli.d.ts +0 -26
  83. package/dist/cli.d.ts.map +0 -1
  84. package/dist/cli.js.map +0 -1
  85. package/dist/cliEmitter.d.ts +0 -34
  86. package/dist/cliEmitter.d.ts.map +0 -1
  87. package/dist/cliEmitter.js.map +0 -1
  88. package/dist/clusters/export.d.ts +0 -2
  89. package/dist/clusters/export.d.ts.map +0 -1
  90. package/dist/clusters/export.js.map +0 -1
  91. package/dist/defaultConfigSchema.d.ts +0 -28
  92. package/dist/defaultConfigSchema.d.ts.map +0 -1
  93. package/dist/defaultConfigSchema.js.map +0 -1
  94. package/dist/deviceManager.d.ts +0 -112
  95. package/dist/deviceManager.d.ts.map +0 -1
  96. package/dist/deviceManager.js.map +0 -1
  97. package/dist/devices/airConditioner.d.ts +0 -98
  98. package/dist/devices/airConditioner.d.ts.map +0 -1
  99. package/dist/devices/airConditioner.js.map +0 -1
  100. package/dist/devices/batteryStorage.d.ts +0 -48
  101. package/dist/devices/batteryStorage.d.ts.map +0 -1
  102. package/dist/devices/batteryStorage.js.map +0 -1
  103. package/dist/devices/cooktop.d.ts +0 -60
  104. package/dist/devices/cooktop.d.ts.map +0 -1
  105. package/dist/devices/cooktop.js.map +0 -1
  106. package/dist/devices/dishwasher.d.ts +0 -71
  107. package/dist/devices/dishwasher.d.ts.map +0 -1
  108. package/dist/devices/dishwasher.js.map +0 -1
  109. package/dist/devices/evse.d.ts +0 -75
  110. package/dist/devices/evse.d.ts.map +0 -1
  111. package/dist/devices/evse.js.map +0 -1
  112. package/dist/devices/export.d.ts +0 -17
  113. package/dist/devices/export.d.ts.map +0 -1
  114. package/dist/devices/export.js.map +0 -1
  115. package/dist/devices/extractorHood.d.ts +0 -46
  116. package/dist/devices/extractorHood.d.ts.map +0 -1
  117. package/dist/devices/extractorHood.js.map +0 -1
  118. package/dist/devices/heatPump.d.ts +0 -47
  119. package/dist/devices/heatPump.d.ts.map +0 -1
  120. package/dist/devices/heatPump.js.map +0 -1
  121. package/dist/devices/laundryDryer.d.ts +0 -67
  122. package/dist/devices/laundryDryer.d.ts.map +0 -1
  123. package/dist/devices/laundryDryer.js.map +0 -1
  124. package/dist/devices/laundryWasher.d.ts +0 -81
  125. package/dist/devices/laundryWasher.d.ts.map +0 -1
  126. package/dist/devices/laundryWasher.js.map +0 -1
  127. package/dist/devices/microwaveOven.d.ts +0 -168
  128. package/dist/devices/microwaveOven.d.ts.map +0 -1
  129. package/dist/devices/microwaveOven.js.map +0 -1
  130. package/dist/devices/oven.d.ts +0 -105
  131. package/dist/devices/oven.d.ts.map +0 -1
  132. package/dist/devices/oven.js.map +0 -1
  133. package/dist/devices/refrigerator.d.ts +0 -118
  134. package/dist/devices/refrigerator.d.ts.map +0 -1
  135. package/dist/devices/refrigerator.js.map +0 -1
  136. package/dist/devices/roboticVacuumCleaner.d.ts +0 -112
  137. package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
  138. package/dist/devices/roboticVacuumCleaner.js.map +0 -1
  139. package/dist/devices/solarPower.d.ts +0 -40
  140. package/dist/devices/solarPower.d.ts.map +0 -1
  141. package/dist/devices/solarPower.js.map +0 -1
  142. package/dist/devices/speaker.d.ts +0 -87
  143. package/dist/devices/speaker.d.ts.map +0 -1
  144. package/dist/devices/speaker.js.map +0 -1
  145. package/dist/devices/temperatureControl.d.ts +0 -166
  146. package/dist/devices/temperatureControl.d.ts.map +0 -1
  147. package/dist/devices/temperatureControl.js.map +0 -1
  148. package/dist/devices/waterHeater.d.ts +0 -111
  149. package/dist/devices/waterHeater.d.ts.map +0 -1
  150. package/dist/devices/waterHeater.js.map +0 -1
  151. package/dist/dgram/coap.d.ts +0 -205
  152. package/dist/dgram/coap.d.ts.map +0 -1
  153. package/dist/dgram/coap.js.map +0 -1
  154. package/dist/dgram/dgram.d.ts +0 -141
  155. package/dist/dgram/dgram.d.ts.map +0 -1
  156. package/dist/dgram/dgram.js.map +0 -1
  157. package/dist/dgram/mb_coap.d.ts +0 -24
  158. package/dist/dgram/mb_coap.d.ts.map +0 -1
  159. package/dist/dgram/mb_coap.js.map +0 -1
  160. package/dist/dgram/mb_mdns.d.ts +0 -24
  161. package/dist/dgram/mb_mdns.d.ts.map +0 -1
  162. package/dist/dgram/mb_mdns.js.map +0 -1
  163. package/dist/dgram/mdns.d.ts +0 -290
  164. package/dist/dgram/mdns.d.ts.map +0 -1
  165. package/dist/dgram/mdns.js.map +0 -1
  166. package/dist/dgram/multicast.d.ts +0 -67
  167. package/dist/dgram/multicast.d.ts.map +0 -1
  168. package/dist/dgram/multicast.js.map +0 -1
  169. package/dist/dgram/unicast.d.ts +0 -56
  170. package/dist/dgram/unicast.d.ts.map +0 -1
  171. package/dist/dgram/unicast.js.map +0 -1
  172. package/dist/frontend.d.ts +0 -232
  173. package/dist/frontend.d.ts.map +0 -1
  174. package/dist/frontend.js.map +0 -1
  175. package/dist/frontendTypes.d.ts +0 -514
  176. package/dist/frontendTypes.d.ts.map +0 -1
  177. package/dist/frontendTypes.js.map +0 -1
  178. package/dist/globalMatterbridge.d.ts +0 -59
  179. package/dist/globalMatterbridge.d.ts.map +0 -1
  180. package/dist/globalMatterbridge.js.map +0 -1
  181. package/dist/helpers.d.ts +0 -48
  182. package/dist/helpers.d.ts.map +0 -1
  183. package/dist/helpers.js.map +0 -1
  184. package/dist/index.d.ts +0 -33
  185. package/dist/index.d.ts.map +0 -1
  186. package/dist/index.js.map +0 -1
  187. package/dist/logger/export.d.ts +0 -2
  188. package/dist/logger/export.d.ts.map +0 -1
  189. package/dist/logger/export.js.map +0 -1
  190. package/dist/matter/behaviors.d.ts +0 -2
  191. package/dist/matter/behaviors.d.ts.map +0 -1
  192. package/dist/matter/behaviors.js.map +0 -1
  193. package/dist/matter/clusters.d.ts +0 -2
  194. package/dist/matter/clusters.d.ts.map +0 -1
  195. package/dist/matter/clusters.js.map +0 -1
  196. package/dist/matter/devices.d.ts +0 -2
  197. package/dist/matter/devices.d.ts.map +0 -1
  198. package/dist/matter/devices.js.map +0 -1
  199. package/dist/matter/endpoints.d.ts +0 -2
  200. package/dist/matter/endpoints.d.ts.map +0 -1
  201. package/dist/matter/endpoints.js.map +0 -1
  202. package/dist/matter/export.d.ts +0 -5
  203. package/dist/matter/export.d.ts.map +0 -1
  204. package/dist/matter/export.js.map +0 -1
  205. package/dist/matter/types.d.ts +0 -3
  206. package/dist/matter/types.d.ts.map +0 -1
  207. package/dist/matter/types.js.map +0 -1
  208. package/dist/matterbridge.d.ts +0 -444
  209. package/dist/matterbridge.d.ts.map +0 -1
  210. package/dist/matterbridge.js.map +0 -1
  211. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
  212. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  213. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  214. package/dist/matterbridgeBehaviors.d.ts +0 -1747
  215. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  216. package/dist/matterbridgeBehaviors.js.map +0 -1
  217. package/dist/matterbridgeDeviceTypes.d.ts +0 -761
  218. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  219. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  220. package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
  221. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  222. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  223. package/dist/matterbridgeEndpoint.d.ts +0 -1534
  224. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  225. package/dist/matterbridgeEndpoint.js.map +0 -1
  226. package/dist/matterbridgeEndpointHelpers.d.ts +0 -407
  227. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  228. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  229. package/dist/matterbridgePlatform.d.ts +0 -365
  230. package/dist/matterbridgePlatform.d.ts.map +0 -1
  231. package/dist/matterbridgePlatform.js.map +0 -1
  232. package/dist/matterbridgeTypes.d.ts +0 -197
  233. package/dist/matterbridgeTypes.d.ts.map +0 -1
  234. package/dist/matterbridgeTypes.js.map +0 -1
  235. package/dist/pluginManager.d.ts +0 -270
  236. package/dist/pluginManager.d.ts.map +0 -1
  237. package/dist/pluginManager.js.map +0 -1
  238. package/dist/shelly.d.ts +0 -174
  239. package/dist/shelly.d.ts.map +0 -1
  240. package/dist/shelly.js.map +0 -1
  241. package/dist/storage/export.d.ts +0 -2
  242. package/dist/storage/export.d.ts.map +0 -1
  243. package/dist/storage/export.js.map +0 -1
  244. package/dist/update.d.ts +0 -75
  245. package/dist/update.d.ts.map +0 -1
  246. package/dist/update.js.map +0 -1
  247. package/dist/utils/colorUtils.d.ts +0 -99
  248. package/dist/utils/colorUtils.d.ts.map +0 -1
  249. package/dist/utils/colorUtils.js.map +0 -1
  250. package/dist/utils/commandLine.d.ts +0 -59
  251. package/dist/utils/commandLine.d.ts.map +0 -1
  252. package/dist/utils/commandLine.js.map +0 -1
  253. package/dist/utils/copyDirectory.d.ts +0 -33
  254. package/dist/utils/copyDirectory.d.ts.map +0 -1
  255. package/dist/utils/copyDirectory.js.map +0 -1
  256. package/dist/utils/createDirectory.d.ts +0 -34
  257. package/dist/utils/createDirectory.d.ts.map +0 -1
  258. package/dist/utils/createDirectory.js.map +0 -1
  259. package/dist/utils/createZip.d.ts +0 -39
  260. package/dist/utils/createZip.d.ts.map +0 -1
  261. package/dist/utils/createZip.js.map +0 -1
  262. package/dist/utils/deepCopy.d.ts +0 -32
  263. package/dist/utils/deepCopy.d.ts.map +0 -1
  264. package/dist/utils/deepCopy.js.map +0 -1
  265. package/dist/utils/deepEqual.d.ts +0 -54
  266. package/dist/utils/deepEqual.d.ts.map +0 -1
  267. package/dist/utils/deepEqual.js.map +0 -1
  268. package/dist/utils/error.d.ts +0 -44
  269. package/dist/utils/error.d.ts.map +0 -1
  270. package/dist/utils/error.js.map +0 -1
  271. package/dist/utils/export.d.ts +0 -13
  272. package/dist/utils/export.d.ts.map +0 -1
  273. package/dist/utils/export.js.map +0 -1
  274. package/dist/utils/hex.d.ts +0 -89
  275. package/dist/utils/hex.d.ts.map +0 -1
  276. package/dist/utils/hex.js.map +0 -1
  277. package/dist/utils/isvalid.d.ts +0 -103
  278. package/dist/utils/isvalid.d.ts.map +0 -1
  279. package/dist/utils/isvalid.js.map +0 -1
  280. package/dist/utils/jestHelpers.d.ts +0 -137
  281. package/dist/utils/jestHelpers.d.ts.map +0 -1
  282. package/dist/utils/jestHelpers.js.map +0 -1
  283. package/dist/utils/network.d.ts +0 -84
  284. package/dist/utils/network.d.ts.map +0 -1
  285. package/dist/utils/network.js.map +0 -1
  286. package/dist/utils/spawn.d.ts +0 -34
  287. package/dist/utils/spawn.d.ts.map +0 -1
  288. package/dist/utils/spawn.js.map +0 -1
  289. package/dist/utils/wait.d.ts +0 -54
  290. package/dist/utils/wait.d.ts.map +0 -1
  291. package/dist/utils/wait.js.map +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,18 +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
15
  import { CommissioningOptions } from '@matter/main/types';
43
- // Matterbridge
44
16
  import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean, withTimeout, hasParameter, wait, inspectError } from './utils/export.js';
45
17
  import { plg } from './matterbridgeTypes.js';
46
18
  import { capitalizeFirstLetter, getAttribute } from './matterbridgeEndpointHelpers.js';
@@ -57,7 +29,7 @@ export class Frontend extends EventEmitter {
57
29
  constructor(matterbridge) {
58
30
  super();
59
31
  this.matterbridge = matterbridge;
60
- this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
32
+ this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
61
33
  this.log.logNameColor = '\x1b[38;5;97m';
62
34
  }
63
35
  set logLevel(logLevel) {
@@ -66,39 +38,10 @@ export class Frontend extends EventEmitter {
66
38
  async start(port = 8283) {
67
39
  this.port = port;
68
40
  this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${this.port}${db}`);
69
- // Initialize multer with the upload directory
70
- const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads'); // Is created by matterbridge initialize
41
+ const uploadDir = path.join(this.matterbridge.matterbridgeDirectory, 'uploads');
71
42
  const upload = multer({ dest: uploadDir });
72
- // Create the express app that serves the frontend
73
43
  this.expressApp = express();
74
- // Inject logging/debug wrapper for route/middleware registration
75
- /*
76
- const methods = ['get', 'post', 'put', 'delete', 'use'];
77
- for (const method of methods) {
78
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
- const original = (this.expressApp as any)[method].bind(this.expressApp);
80
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
- (this.expressApp as any)[method] = (path: any, ...rest: any) => {
82
- try {
83
- console.log(`[DEBUG] Registering ${method.toUpperCase()} route:`, path);
84
- return original(path, ...rest);
85
- } catch (err) {
86
- console.error(`[ERROR] Failed to register route: ${path}`);
87
- throw err;
88
- }
89
- };
90
- }
91
- */
92
- // Log all requests to the server for debugging
93
- /*
94
- this.expressApp.use((req, res, next) => {
95
- this.log.debug(`***Received request on expressApp: ${req.method} ${req.url}`);
96
- next();
97
- });
98
- */
99
- // Serve static files from '/static' endpoint
100
44
  this.expressApp.use(express.static(path.join(this.matterbridge.rootDirectory, 'frontend/build')));
101
- // Read the package.json file to get the frontend version
102
45
  try {
103
46
  this.log.debug(`Reading frontend package.json...`);
104
47
  const frontendJson = await fs.readFile(path.join(this.matterbridge.rootDirectory, 'frontend/package.json'), 'utf8');
@@ -106,11 +49,9 @@ export class Frontend extends EventEmitter {
106
49
  this.log.debug(`Frontend version ${CYAN}${this.matterbridge.matterbridgeInformation.frontendVersion}${db}`);
107
50
  }
108
51
  catch (error) {
109
- // istanbul ignore next
110
52
  this.log.error(`Failed to read frontend package.json: ${error instanceof Error ? error.message : String(error)}`);
111
53
  }
112
54
  if (!hasParameter('ssl')) {
113
- // Create an HTTP server and attach the express app
114
55
  try {
115
56
  this.log.debug(`Creating HTTP server...`);
116
57
  this.httpServer = createServer(this.expressApp);
@@ -120,7 +61,6 @@ export class Frontend extends EventEmitter {
120
61
  this.emit('server_error', error);
121
62
  return;
122
63
  }
123
- // Listen on the specified port
124
64
  if (hasParameter('ingress')) {
125
65
  this.httpServer.listen(this.port, '0.0.0.0', () => {
126
66
  this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -161,7 +101,6 @@ export class Frontend extends EventEmitter {
161
101
  let passphrase;
162
102
  let httpsServerOptions = {};
163
103
  if (existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'))) {
164
- // Load the p12 certificate and the passphrase
165
104
  try {
166
105
  pfx = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12'));
167
106
  this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.p12')}`);
@@ -173,7 +112,7 @@ export class Frontend extends EventEmitter {
173
112
  }
174
113
  try {
175
114
  passphrase = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass'), 'utf8');
176
- passphrase = passphrase.trim(); // Ensure no extra characters
115
+ passphrase = passphrase.trim();
177
116
  this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pass')}`);
178
117
  }
179
118
  catch (error) {
@@ -184,7 +123,6 @@ export class Frontend extends EventEmitter {
184
123
  httpsServerOptions = { pfx, passphrase };
185
124
  }
186
125
  else {
187
- // 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.
188
126
  try {
189
127
  cert = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
190
128
  this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs/cert.pem')}`);
@@ -214,10 +152,9 @@ export class Frontend extends EventEmitter {
214
152
  httpsServerOptions = { cert: fullChain ?? cert, key, ca };
215
153
  }
216
154
  if (hasParameter('mtls')) {
217
- httpsServerOptions.requestCert = true; // Request client certificate
218
- httpsServerOptions.rejectUnauthorized = true; // Require client certificate validation
155
+ httpsServerOptions.requestCert = true;
156
+ httpsServerOptions.rejectUnauthorized = true;
219
157
  }
220
- // Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
221
158
  try {
222
159
  this.log.debug(`Creating HTTPS server...`);
223
160
  this.httpsServer = https.createServer(httpsServerOptions, this.expressApp);
@@ -227,7 +164,6 @@ export class Frontend extends EventEmitter {
227
164
  this.emit('server_error', error);
228
165
  return;
229
166
  }
230
- // Listen on the specified port
231
167
  if (hasParameter('ingress')) {
232
168
  this.httpsServer.listen(this.port, '0.0.0.0', () => {
233
169
  this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${this.port}${UNDERLINEOFF}${rs}`);
@@ -259,19 +195,17 @@ export class Frontend extends EventEmitter {
259
195
  return;
260
196
  });
261
197
  }
262
- // Create a WebSocket server and attach it to the http or https server
263
198
  const wssPort = this.port;
264
199
  const wssHost = hasParameter('ssl') ? `wss://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.matterbridge.systemInformation.ipv4Address}:${wssPort}`;
265
200
  this.log.debug(`Creating WebSocketServer on host ${CYAN}${wssHost}${db}...`);
266
201
  this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
267
202
  this.webSocketServer.on('connection', (ws, request) => {
268
203
  const clientIp = request.socket.remoteAddress;
269
- // Set the global logger callback for the WebSocketServer
270
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
271
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
272
- callbackLogLevel = "info" /* LogLevel.INFO */;
273
- if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
274
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
204
+ let callbackLogLevel = "notice";
205
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "info" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
206
+ callbackLogLevel = "info";
207
+ if (this.matterbridge.matterbridgeInformation.loggerLevel === "debug" || this.matterbridge.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
208
+ callbackLogLevel = "debug";
275
209
  AnsiLogger.setGlobalCallback(this.wssSendLogMessage.bind(this), callbackLogLevel);
276
210
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
277
211
  this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
@@ -293,7 +227,6 @@ export class Frontend extends EventEmitter {
293
227
  }
294
228
  });
295
229
  ws.on('error', (error) => {
296
- // istanbul ignore next
297
230
  this.log.error(`WebSocket client error: ${error}`);
298
231
  });
299
232
  });
@@ -307,7 +240,6 @@ export class Frontend extends EventEmitter {
307
240
  this.webSocketServer.on('error', (ws, error) => {
308
241
  this.log.error(`WebSocketServer error: ${error}`);
309
242
  });
310
- // Subscribe to cli events
311
243
  cliEmitter.removeAllListeners();
312
244
  cliEmitter.on('uptime', (systemUptime, processUptime) => {
313
245
  this.wssSendUptimeUpdate(systemUptime, processUptime);
@@ -318,8 +250,6 @@ export class Frontend extends EventEmitter {
318
250
  cliEmitter.on('cpu', (cpuUsage) => {
319
251
  this.wssSendCpuUpdate(cpuUsage);
320
252
  });
321
- // Endpoint to validate login code
322
- // curl -X POST "http://localhost:8283/api/login" -H "Content-Type: application/json" -d "{\"password\":\"Here\"}"
323
253
  this.expressApp.post('/api/login', express.json(), async (req, res) => {
324
254
  const { password } = req.body;
325
255
  this.log.debug('The frontend sent /api/login', password);
@@ -338,27 +268,23 @@ export class Frontend extends EventEmitter {
338
268
  this.log.warn('/api/login error wrong password');
339
269
  res.json({ valid: false });
340
270
  }
341
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
342
271
  }
343
272
  catch (error) {
344
273
  this.log.error('/api/login error getting password');
345
274
  res.json({ valid: false });
346
275
  }
347
276
  });
348
- // Endpoint to provide health check for docker
349
277
  this.expressApp.get('/health', (req, res) => {
350
278
  this.log.debug('Express received /health');
351
279
  const healthStatus = {
352
- status: 'ok', // Indicate service is healthy
353
- uptime: process.uptime(), // Server uptime in seconds
354
- timestamp: new Date().toISOString(), // Current timestamp
280
+ status: 'ok',
281
+ uptime: process.uptime(),
282
+ timestamp: new Date().toISOString(),
355
283
  };
356
284
  res.status(200).json(healthStatus);
357
285
  });
358
- // Endpoint to provide memory usage details
359
286
  this.expressApp.get('/memory', async (req, res) => {
360
287
  this.log.debug('Express received /memory');
361
- // Memory usage from process
362
288
  const memoryUsageRaw = process.memoryUsage();
363
289
  const memoryUsage = {
364
290
  rss: this.formatMemoryUsage(memoryUsageRaw.rss),
@@ -367,13 +293,10 @@ export class Frontend extends EventEmitter {
367
293
  external: this.formatMemoryUsage(memoryUsageRaw.external),
368
294
  arrayBuffers: this.formatMemoryUsage(memoryUsageRaw.arrayBuffers),
369
295
  };
370
- // V8 heap statistics
371
296
  const { default: v8 } = await import('node:v8');
372
297
  const heapStatsRaw = v8.getHeapStatistics();
373
298
  const heapSpacesRaw = v8.getHeapSpaceStatistics();
374
- // Format heapStats
375
299
  const heapStats = Object.fromEntries(Object.entries(heapStatsRaw).map(([key, value]) => [key, this.formatMemoryUsage(value)]));
376
- // Format heapSpaces
377
300
  const heapSpaces = heapSpacesRaw.map((space) => ({
378
301
  ...space,
379
302
  space_size: this.formatMemoryUsage(space.space_size),
@@ -391,23 +314,19 @@ export class Frontend extends EventEmitter {
391
314
  };
392
315
  res.status(200).json(memoryReport);
393
316
  });
394
- // Endpoint to provide settings
395
317
  this.expressApp.get('/api/settings', express.json(), async (req, res) => {
396
318
  this.log.debug('The frontend sent /api/settings');
397
319
  res.json(await this.getApiSettings());
398
320
  });
399
- // Endpoint to provide plugins
400
321
  this.expressApp.get('/api/plugins', async (req, res) => {
401
322
  this.log.debug('The frontend sent /api/plugins');
402
323
  res.json(this.getPlugins());
403
324
  });
404
- // Endpoint to provide devices
405
325
  this.expressApp.get('/api/devices', async (req, res) => {
406
326
  this.log.debug('The frontend sent /api/devices');
407
327
  const devices = await this.getDevices();
408
328
  res.json(devices);
409
329
  });
410
- // Endpoint to view the matterbridge log
411
330
  this.expressApp.get('/api/view-mblog', async (req, res) => {
412
331
  this.log.debug('The frontend sent /api/view-mblog');
413
332
  try {
@@ -420,7 +339,6 @@ export class Frontend extends EventEmitter {
420
339
  res.status(500).send('Error reading matterbridge log file. Please enable the matterbridge log on file in the settings.');
421
340
  }
422
341
  });
423
- // Endpoint to view the matter.js log
424
342
  this.expressApp.get('/api/view-mjlog', async (req, res) => {
425
343
  this.log.debug('The frontend sent /api/view-mjlog');
426
344
  try {
@@ -433,11 +351,9 @@ export class Frontend extends EventEmitter {
433
351
  res.status(500).send('Error reading matter log file. Please enable the matter log on file in the settings.');
434
352
  }
435
353
  });
436
- // Endpoint to view the diagnostic.log
437
354
  this.expressApp.get('/api/view-diagnostic', async (req, res) => {
438
355
  this.log.debug('The frontend sent /api/view-diagnostic');
439
356
  const serverNodes = [];
440
- // istanbul ignore else
441
357
  if (this.matterbridge.bridgeMode === 'bridge') {
442
358
  if (this.matterbridge.serverNode)
443
359
  serverNodes.push(this.matterbridge.serverNode);
@@ -448,7 +364,6 @@ export class Frontend extends EventEmitter {
448
364
  serverNodes.push(plugin.serverNode);
449
365
  }
450
366
  }
451
- // istanbul ignore next
452
367
  for (const device of this.matterbridge.getDevices()) {
453
368
  if (device.serverNode)
454
369
  serverNodes.push(device.serverNode);
@@ -471,20 +386,17 @@ export class Frontend extends EventEmitter {
471
386
  values: [...serverNodes],
472
387
  })));
473
388
  delete Logger.destinations.diagnostic;
474
- await wait(500); // Wait for the log to be written
389
+ await wait(500);
475
390
  try {
476
391
  const data = await fs.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'diagnostic.log'), 'utf8');
477
392
  res.type('text/plain');
478
393
  res.send(data.slice(29));
479
394
  }
480
395
  catch (error) {
481
- // istanbul ignore next
482
396
  this.log.error(`Error reading diagnostic log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
483
- // istanbul ignore next
484
397
  res.status(500).send('Error reading diagnostic log file.');
485
398
  }
486
399
  });
487
- // Endpoint to view the shelly log
488
400
  this.expressApp.get('/api/shellyviewsystemlog', async (req, res) => {
489
401
  this.log.debug('The frontend sent /api/shellyviewsystemlog');
490
402
  try {
@@ -497,7 +409,6 @@ export class Frontend extends EventEmitter {
497
409
  res.status(500).send('Error reading shelly log file. Please create the shelly system log before loading it.');
498
410
  }
499
411
  });
500
- // Endpoint to download the matterbridge log
501
412
  this.expressApp.get('/api/download-mblog', async (req, res) => {
502
413
  this.log.debug(`The frontend sent /api/download-mblog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
503
414
  try {
@@ -511,14 +422,12 @@ export class Frontend extends EventEmitter {
511
422
  }
512
423
  res.type('text/plain');
513
424
  res.download(path.join(os.tmpdir(), this.matterbridge.matterbridgeLoggerFile), 'matterbridge.log', (error) => {
514
- /* istanbul ignore if */
515
425
  if (error) {
516
426
  this.log.error(`Error downloading log file ${this.matterbridge.matterbridgeLoggerFile}: ${error instanceof Error ? error.message : error}`);
517
427
  res.status(500).send('Error downloading the matterbridge log file');
518
428
  }
519
429
  });
520
430
  });
521
- // Endpoint to download the matter log
522
431
  this.expressApp.get('/api/download-mjlog', async (req, res) => {
523
432
  this.log.debug(`The frontend sent /api/download-mjlog ${path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile)}`);
524
433
  try {
@@ -532,14 +441,12 @@ export class Frontend extends EventEmitter {
532
441
  }
533
442
  res.type('text/plain');
534
443
  res.download(path.join(os.tmpdir(), this.matterbridge.matterLoggerFile), 'matter.log', (error) => {
535
- /* istanbul ignore if */
536
444
  if (error) {
537
445
  this.log.error(`Error downloading log file ${this.matterbridge.matterLoggerFile}: ${error instanceof Error ? error.message : error}`);
538
446
  res.status(500).send('Error downloading the matter log file');
539
447
  }
540
448
  });
541
449
  });
542
- // Endpoint to download the shelly log
543
450
  this.expressApp.get('/api/shellydownloadsystemlog', async (req, res) => {
544
451
  this.log.debug('The frontend sent /api/shellydownloadsystemlog');
545
452
  try {
@@ -553,90 +460,74 @@ export class Frontend extends EventEmitter {
553
460
  }
554
461
  res.type('text/plain');
555
462
  res.download(path.join(os.tmpdir(), 'shelly.log'), 'shelly.log', (error) => {
556
- /* istanbul ignore if */
557
463
  if (error) {
558
464
  this.log.error(`Error downloading Shelly system log file: ${error instanceof Error ? error.message : error}`);
559
465
  res.status(500).send('Error downloading Shelly system log file');
560
466
  }
561
467
  });
562
468
  });
563
- // Endpoint to download the matterbridge storage directory
564
469
  this.expressApp.get('/api/download-mbstorage', async (req, res) => {
565
470
  this.log.debug('The frontend sent /api/download-mbstorage');
566
471
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.nodeStorageName));
567
472
  res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.nodeStorageName}.zip`), `matterbridge.${this.matterbridge.nodeStorageName}.zip`, (error) => {
568
- /* istanbul ignore if */
569
473
  if (error) {
570
474
  this.log.error(`Error downloading file ${`matterbridge.${this.matterbridge.nodeStorageName}.zip`}: ${error instanceof Error ? error.message : error}`);
571
475
  res.status(500).send('Error downloading the matterbridge storage file');
572
476
  }
573
477
  });
574
478
  });
575
- // Endpoint to download the matter storage file
576
479
  this.expressApp.get('/api/download-mjstorage', async (req, res) => {
577
480
  this.log.debug('The frontend sent /api/download-mjstorage');
578
481
  await createZip(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterStorageName));
579
482
  res.download(path.join(os.tmpdir(), `matterbridge.${this.matterbridge.matterStorageName}.zip`), `matterbridge.${this.matterbridge.matterStorageName}.zip`, (error) => {
580
- /* istanbul ignore if */
581
483
  if (error) {
582
484
  this.log.error(`Error downloading the matter storage matterbridge.${this.matterbridge.matterStorageName}.zip: ${error instanceof Error ? error.message : error}`);
583
485
  res.status(500).send('Error downloading the matter storage zip file');
584
486
  }
585
487
  });
586
488
  });
587
- // Endpoint to download the matterbridge plugin directory
588
489
  this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
589
490
  this.log.debug('The frontend sent /api/download-pluginstorage');
590
491
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridge.matterbridgePluginDirectory);
591
492
  res.download(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), `matterbridge.pluginstorage.zip`, (error) => {
592
- /* istanbul ignore if */
593
493
  if (error) {
594
494
  this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
595
495
  res.status(500).send('Error downloading the matterbridge plugin storage file');
596
496
  }
597
497
  });
598
498
  });
599
- // Endpoint to download the matterbridge plugin config files
600
499
  this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
601
500
  this.log.debug('The frontend sent /api/download-pluginconfig');
602
501
  await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridge.matterbridgeDirectory, '*.config.json')));
603
502
  res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
604
- /* istanbul ignore if */
605
503
  if (error) {
606
504
  this.log.error(`Error downloading file matterbridge.pluginconfig.zip: ${error instanceof Error ? error.message : error}`);
607
505
  res.status(500).send('Error downloading the matterbridge plugin config file');
608
506
  }
609
507
  });
610
508
  });
611
- // Endpoint to download the matterbridge backup (created with the backup command)
612
509
  this.expressApp.get('/api/download-backup', async (req, res) => {
613
510
  this.log.debug('The frontend sent /api/download-backup');
614
511
  res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
615
- /* istanbul ignore if */
616
512
  if (error) {
617
513
  this.log.error(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
618
514
  res.status(500).send(`Error downloading file matterbridge.backup.zip: ${error instanceof Error ? error.message : error}`);
619
515
  }
620
516
  });
621
517
  });
622
- // Endpoint to upload a package
623
518
  this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
624
519
  const { filename } = req.body;
625
520
  const file = req.file;
626
- /* istanbul ignore if */
627
521
  if (!file || !filename) {
628
522
  this.log.error(`uploadpackage: invalid request: file and filename are required`);
629
523
  res.status(400).send('Invalid request: file and filename are required');
630
524
  return;
631
525
  }
632
526
  this.wssSendSnackbarMessage(`Installing package ${filename}. Please wait...`, 0);
633
- // Define the path where the plugin file will be saved
634
527
  const filePath = path.join(this.matterbridge.matterbridgeDirectory, 'uploads', filename);
635
528
  try {
636
- // Move the uploaded file to the specified path
637
529
  await fs.rename(file.path, filePath);
638
530
  this.log.info(`File ${plg}${filename}${nf} uploaded successfully`);
639
- // Install the plugin package
640
531
  if (filename.endsWith('.tgz')) {
641
532
  const { spawnCommand } = await import('./utils/spawn.js');
642
533
  await spawnCommand(this.matterbridge, 'npm', ['install', '-g', filePath, '--omit=dev', '--verbose'], filename);
@@ -656,7 +547,6 @@ export class Frontend extends EventEmitter {
656
547
  res.status(500).send(`Error uploading or installing plugin package ${filename}`);
657
548
  }
658
549
  });
659
- // Fallback for routing (must be the last route)
660
550
  this.expressApp.use((req, res) => {
661
551
  this.log.debug(`The frontend sent ${req.url} method ${req.method}: sending index.html as fallback`);
662
552
  res.sendFile(path.join(this.matterbridge.rootDirectory, 'frontend/build/index.html'));
@@ -665,16 +555,13 @@ export class Frontend extends EventEmitter {
665
555
  }
666
556
  async stop() {
667
557
  this.log.debug('Stopping the frontend...');
668
- // Remove listeners from the express app
669
558
  if (this.expressApp) {
670
559
  this.expressApp.removeAllListeners();
671
560
  this.expressApp = undefined;
672
561
  this.log.debug('Frontend app closed successfully');
673
562
  }
674
- // Close the WebSocket server
675
563
  if (this.webSocketServer) {
676
564
  this.log.debug('Closing WebSocket server...');
677
- // Close all active connections
678
565
  this.webSocketServer.clients.forEach((client) => {
679
566
  if (client.readyState === WebSocket.OPEN) {
680
567
  client.close();
@@ -683,7 +570,6 @@ export class Frontend extends EventEmitter {
683
570
  await withTimeout(new Promise((resolve) => {
684
571
  this.webSocketServer?.close((error) => {
685
572
  if (error) {
686
- // istanbul ignore next
687
573
  this.log.error(`Error closing WebSocket server: ${error}`);
688
574
  }
689
575
  else {
@@ -696,27 +582,8 @@ export class Frontend extends EventEmitter {
696
582
  this.webSocketServer.removeAllListeners();
697
583
  this.webSocketServer = undefined;
698
584
  }
699
- // Close the http server
700
585
  if (this.httpServer) {
701
586
  this.log.debug('Closing http server...');
702
- /*
703
- await withTimeout(
704
- new Promise<void>((resolve) => {
705
- this.httpServer?.close((error) => {
706
- if (error) {
707
- // istanbul ignore next
708
- this.log.error(`Error closing http server: ${error}`);
709
- } else {
710
- this.log.debug('Http server closed successfully');
711
- this.emit('server_stopped');
712
- }
713
- resolve();
714
- });
715
- }),
716
- 5000,
717
- false,
718
- );
719
- */
720
587
  this.httpServer.close();
721
588
  this.log.debug('Http server closed successfully');
722
589
  this.listening = false;
@@ -725,27 +592,8 @@ export class Frontend extends EventEmitter {
725
592
  this.httpServer = undefined;
726
593
  this.log.debug('Frontend http server closed successfully');
727
594
  }
728
- // Close the https server
729
595
  if (this.httpsServer) {
730
596
  this.log.debug('Closing https server...');
731
- /*
732
- await withTimeout(
733
- new Promise<void>((resolve) => {
734
- this.httpsServer?.close((error) => {
735
- if (error) {
736
- // istanbul ignore next
737
- this.log.error(`Error closing https server: ${error}`);
738
- } else {
739
- this.log.debug('Https server closed successfully');
740
- this.emit('server_stopped');
741
- }
742
- resolve();
743
- });
744
- }),
745
- 5000,
746
- false,
747
- );
748
- */
749
597
  this.httpsServer.close();
750
598
  this.log.debug('Https server closed successfully');
751
599
  this.listening = false;
@@ -756,7 +604,6 @@ export class Frontend extends EventEmitter {
756
604
  }
757
605
  this.log.debug('Frontend stopped successfully');
758
606
  }
759
- // Function to format bytes to KB, MB, or GB
760
607
  formatMemoryUsage = (bytes) => {
761
608
  if (bytes >= 1024 ** 3) {
762
609
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
@@ -768,7 +615,6 @@ export class Frontend extends EventEmitter {
768
615
  return `${(bytes / 1024).toFixed(2)} KB`;
769
616
  }
770
617
  };
771
- // Function to format system uptime with only the most significant unit
772
618
  formatOsUpTime = (seconds) => {
773
619
  if (seconds >= 86400) {
774
620
  const days = Math.floor(seconds / 86400);
@@ -784,13 +630,7 @@ export class Frontend extends EventEmitter {
784
630
  }
785
631
  return `${seconds} second${seconds !== 1 ? 's' : ''}`;
786
632
  };
787
- /**
788
- * Retrieves the api settings data.
789
- *
790
- * @returns {Promise<{ matterbridgeInformation: MatterbridgeInformation, systemInformation: SystemInformation }>} A promise that resolve in the api settings object.
791
- */
792
633
  async getApiSettings() {
793
- // Update the system information
794
634
  this.matterbridge.systemInformation.totalMemory = this.formatMemoryUsage(os.totalmem());
795
635
  this.matterbridge.systemInformation.freeMemory = this.formatMemoryUsage(os.freemem());
796
636
  this.matterbridge.systemInformation.systemUptime = this.formatOsUpTime(os.uptime());
@@ -799,12 +639,11 @@ export class Frontend extends EventEmitter {
799
639
  this.matterbridge.systemInformation.rss = this.formatMemoryUsage(process.memoryUsage().rss);
800
640
  this.matterbridge.systemInformation.heapTotal = this.formatMemoryUsage(process.memoryUsage().heapTotal);
801
641
  this.matterbridge.systemInformation.heapUsed = this.formatMemoryUsage(process.memoryUsage().heapUsed);
802
- // Update the matterbridge information
803
642
  this.matterbridge.matterbridgeInformation.bridgeMode = this.matterbridge.bridgeMode;
804
643
  this.matterbridge.matterbridgeInformation.restartMode = this.matterbridge.restartMode;
805
644
  this.matterbridge.matterbridgeInformation.profile = this.matterbridge.profile;
806
645
  this.matterbridge.matterbridgeInformation.loggerLevel = this.matterbridge.log.logLevel;
807
- this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
646
+ this.matterbridge.matterbridgeInformation.matterLoggerLevel = Logger.level;
808
647
  this.matterbridge.matterbridgeInformation.matterMdnsInterface = this.matterbridge.mdnsInterface;
809
648
  this.matterbridge.matterbridgeInformation.matterIpv4Address = this.matterbridge.ipv4address;
810
649
  this.matterbridge.matterbridgeInformation.matterIpv6Address = this.matterbridge.ipv6address;
@@ -813,12 +652,6 @@ export class Frontend extends EventEmitter {
813
652
  this.matterbridge.matterbridgeInformation.matterPasscode = await this.matterbridge.nodeContext?.get('matterpasscode');
814
653
  return { systemInformation: this.matterbridge.systemInformation, matterbridgeInformation: this.matterbridge.matterbridgeInformation };
815
654
  }
816
- /**
817
- * Retrieves the reachable attribute.
818
- *
819
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint object.
820
- * @returns {boolean} The reachable attribute.
821
- */
822
655
  getReachability(device) {
823
656
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
824
657
  return false;
@@ -830,12 +663,6 @@ export class Frontend extends EventEmitter {
830
663
  return true;
831
664
  return false;
832
665
  }
833
- /**
834
- * Retrieves the power source attribute.
835
- *
836
- * @param {MatterbridgeEndpoint} endpoint - The MatterbridgeDevice to retrieve the power source from.
837
- * @returns {'ac' | 'dc' | 'ok' | 'warning' | 'critical' | undefined} The power source attribute.
838
- */
839
666
  getPowerSource(endpoint) {
840
667
  if (!endpoint.lifecycle.isReady || endpoint.construction.status !== Lifecycle.Status.Active)
841
668
  return undefined;
@@ -851,22 +678,13 @@ export class Frontend extends EventEmitter {
851
678
  }
852
679
  return;
853
680
  };
854
- // Root endpoint
855
681
  if (endpoint.hasClusterServer(PowerSource.Cluster.id))
856
682
  return powerSource(endpoint);
857
- // Child endpoints
858
683
  for (const child of endpoint.getChildEndpoints()) {
859
684
  if (child.hasClusterServer(PowerSource.Cluster.id))
860
685
  return powerSource(child);
861
686
  }
862
687
  }
863
- /**
864
- * Retrieves the cluster text description from a given device.
865
- * The output is a string with the attributes description of the cluster servers in the device to show in the frontend.
866
- *
867
- * @param {MatterbridgeEndpoint} device - The MatterbridgeEndpoint to retrieve the cluster text from.
868
- * @returns {string} The attributes description of the cluster servers in the device.
869
- */
870
688
  getClusterTextFromDevice(device) {
871
689
  if (!device.lifecycle.isReady || device.construction.status !== Lifecycle.Status.Active)
872
690
  return '';
@@ -877,7 +695,6 @@ export class Frontend extends EventEmitter {
877
695
  if (composed)
878
696
  return 'Composed: ' + composed.value;
879
697
  }
880
- // istanbul ignore next cause is not reachable
881
698
  return '';
882
699
  };
883
700
  const getFixedLabel = (device) => {
@@ -887,13 +704,11 @@ export class Frontend extends EventEmitter {
887
704
  if (composed)
888
705
  return 'Composed: ' + composed.value;
889
706
  }
890
- // istanbul ignore next cause is not reacheable
891
707
  return '';
892
708
  };
893
709
  let attributes = '';
894
710
  let supportedModes = [];
895
711
  device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
896
- // console.log(`${device.deviceName} => Cluster: ${clusterName}-${clusterId} Attribute: ${attributeName}-${attributeId} Value(${typeof attributeValue}): ${attributeValue}`);
897
712
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
898
713
  return;
899
714
  if (clusterName === 'onOff' && attributeName === 'onOff')
@@ -983,17 +798,11 @@ export class Frontend extends EventEmitter {
983
798
  if (clusterName === 'userLabel' && attributeName === 'labelList')
984
799
  attributes += `${getUserLabel(device)} `;
985
800
  });
986
- // console.log(`${device.deviceName}.forEachAttribute: ${attributes}`);
987
801
  return attributes.trimStart().trimEnd();
988
802
  }
989
- /**
990
- * Retrieves the registered plugins sanitized for res.json().
991
- *
992
- * @returns {BaseRegisteredPlugin[]} An array of BaseRegisteredPlugin.
993
- */
994
803
  getPlugins() {
995
804
  if (this.matterbridge.hasCleanupStarted)
996
- return []; // Skip if cleanup has started
805
+ return [];
997
806
  const baseRegisteredPlugins = [];
998
807
  for (const plugin of this.matterbridge.plugins) {
999
808
  baseRegisteredPlugins.push({
@@ -1021,27 +830,18 @@ export class Frontend extends EventEmitter {
1021
830
  schemaJson: plugin.schemaJson,
1022
831
  hasWhiteList: plugin.configJson?.whiteList !== undefined,
1023
832
  hasBlackList: plugin.configJson?.blackList !== undefined,
1024
- // Childbridge mode specific data
1025
833
  matter: plugin.serverNode ? this.matterbridge.getServerNodeData(plugin.serverNode) : undefined,
1026
834
  });
1027
835
  }
1028
836
  return baseRegisteredPlugins;
1029
837
  }
1030
- /**
1031
- * Retrieves the devices from Matterbridge.
1032
- *
1033
- * @param {string} [pluginName] - The name of the plugin to filter devices by.
1034
- * @returns {Promise<ApiDevices[]>} A promise that resolves to an array of ApiDevices for the frontend.
1035
- */
1036
838
  async getDevices(pluginName) {
1037
839
  if (this.matterbridge.hasCleanupStarted)
1038
- return []; // Skip if cleanup has started
840
+ return [];
1039
841
  const devices = [];
1040
842
  for (const device of this.matterbridge.devices.array()) {
1041
- // Filter by pluginName if provided
1042
843
  if (pluginName && pluginName !== device.plugin)
1043
844
  continue;
1044
- // Check if the device has the required properties
1045
845
  if (!device.plugin || !device.deviceType || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1046
846
  continue;
1047
847
  devices.push({
@@ -1061,37 +861,24 @@ export class Frontend extends EventEmitter {
1061
861
  }
1062
862
  return devices;
1063
863
  }
1064
- /**
1065
- * Retrieves the clusters from a given plugin and endpoint number.
1066
- *
1067
- * Response for /api/clusters
1068
- *
1069
- * @param {string} pluginName - The name of the plugin.
1070
- * @param {number} endpointNumber - The endpoint number.
1071
- * @returns {ApiClustersResponse | undefined} A promise that resolves to the clusters or undefined if not found.
1072
- */
1073
864
  getClusters(pluginName, endpointNumber) {
865
+ if (this.matterbridge.hasCleanupStarted)
866
+ return;
1074
867
  const endpoint = this.matterbridge.devices.array().find((d) => d.plugin === pluginName && d.maybeNumber === endpointNumber);
1075
868
  if (!endpoint || !endpoint.plugin || !endpoint.maybeNumber || !endpoint.maybeId || !endpoint.deviceName || !endpoint.serialNumber) {
1076
869
  this.log.error(`getClusters: no device found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1077
870
  return;
1078
871
  }
1079
- // this.log.debug(`***getClusters: getting clusters for device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${endpointNumber}`);
1080
- // Get the device types from the main endpoint
1081
872
  const deviceTypes = [];
1082
873
  const clusters = [];
1083
874
  endpoint.state.descriptor.deviceTypeList.forEach((d) => {
1084
875
  deviceTypes.push(d.deviceType);
1085
876
  });
1086
- // Get the clusters from the main endpoint
1087
877
  endpoint.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
1088
878
  if (typeof attributeValue === 'undefined' || attributeValue === undefined)
1089
879
  return;
1090
880
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1091
881
  return;
1092
- // console.log(
1093
- // `${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}`,
1094
- // );
1095
882
  clusters.push({
1096
883
  endpoint: endpoint.number.toString(),
1097
884
  number: endpoint.number,
@@ -1105,19 +892,12 @@ export class Frontend extends EventEmitter {
1105
892
  attributeLocalValue: attributeValue,
1106
893
  });
1107
894
  });
1108
- // Get the child endpoints
1109
895
  const childEndpoints = endpoint.getChildEndpoints();
1110
- // if (childEndpoints.length === 0) {
1111
- // this.log.debug(`***getClusters: found ${childEndpoints.length} child endpoints for device ${endpoint.deviceName} plugin ${pluginName} and endpoint number ${endpointNumber}`);
1112
- // }
1113
896
  childEndpoints.forEach((childEndpoint) => {
1114
- // istanbul ignore if cause is not reachable: should never happen but ...
1115
897
  if (!childEndpoint.maybeId || !childEndpoint.maybeNumber) {
1116
898
  this.log.error(`getClusters: no child endpoint found for plugin ${pluginName} and endpoint number ${endpointNumber}`);
1117
899
  return;
1118
900
  }
1119
- // this.log.debug(`***getClusters: getting clusters for child endpoint ${childEndpoint.id} of device ${endpoint.deviceName} plugin ${pluginName} endpoint number ${childEndpoint.number}`);
1120
- // Get the device types of the child endpoint
1121
901
  const deviceTypes = [];
1122
902
  childEndpoint.state.descriptor.deviceTypeList.forEach((d) => {
1123
903
  deviceTypes.push(d.deviceType);
@@ -1127,9 +907,6 @@ export class Frontend extends EventEmitter {
1127
907
  return;
1128
908
  if (clusterName === 'EveHistory' && ['configDataGet', 'configDataSet', 'historyStatus', 'historyEntries', 'historyRequest', 'historySetTime', 'rLoc'].includes(attributeName))
1129
909
  return;
1130
- // console.log(
1131
- // `${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}`,
1132
- // );
1133
910
  clusters.push({
1134
911
  endpoint: childEndpoint.number.toString(),
1135
912
  number: childEndpoint.number,
@@ -1146,13 +923,6 @@ export class Frontend extends EventEmitter {
1146
923
  });
1147
924
  return { plugin: endpoint.plugin, deviceName: endpoint.deviceName, serialNumber: endpoint.serialNumber, number: endpoint.number, id: endpoint.id, deviceTypes, clusters };
1148
925
  }
1149
- /**
1150
- * Handles incoming websocket api request messages from the Matterbridge frontend.
1151
- *
1152
- * @param {WebSocket} client - The websocket client that sent the message.
1153
- * @param {WebSocket.RawData} message - The raw data of the message received from the client.
1154
- * @returns {Promise<void>} A promise that resolves when the message has been handled.
1155
- */
1156
926
  async wsMessageHandler(client, message) {
1157
927
  let data;
1158
928
  const sendResponse = (data) => {
@@ -1172,7 +942,7 @@ export class Frontend extends EventEmitter {
1172
942
  };
1173
943
  try {
1174
944
  data = JSON.parse(message.toString());
1175
- if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) /* || !isValidObject(data.params)*/ || data.dst !== 'Matterbridge') {
945
+ if (!isValidNumber(data.id) || !isValidString(data.dst) || !isValidString(data.src) || !isValidString(data.method) || data.dst !== 'Matterbridge') {
1176
946
  this.log.error(`Invalid message from websocket client: ${debugStringify(data)}`);
1177
947
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Invalid message' });
1178
948
  return;
@@ -1215,48 +985,36 @@ export class Frontend extends EventEmitter {
1215
985
  this.wssSendSnackbarMessage(`Installed package ${localData.params.packageName}`, 5, 'success');
1216
986
  const packageName = localData.params.packageName.replace(/@.*$/, '');
1217
987
  if (localData.params.restart === false && packageName !== 'matterbridge') {
1218
- // The install comes from InstallPlugins
1219
988
  this.matterbridge.plugins
1220
989
  .add(packageName)
1221
990
  .then((plugin) => {
1222
- // istanbul ignore next if
1223
991
  if (plugin) {
1224
- // The plugin is not registered
1225
992
  this.wssSendSnackbarMessage(`Added plugin ${packageName}`, 5, 'success');
1226
- // In childbridge mode the plugins server node is not started when added
1227
993
  if (this.matterbridge.bridgeMode === 'childbridge')
1228
994
  this.wssSendRestartRequired(true, true);
1229
995
  this.matterbridge.plugins
1230
996
  .load(plugin, true, 'The plugin has been added', true)
1231
- // eslint-disable-next-line promise/no-nesting
1232
997
  .then(() => {
1233
998
  this.wssSendSnackbarMessage(`Started plugin ${packageName}`, 5, 'success');
1234
999
  this.wssSendRefreshRequired('plugins');
1000
+ this.wssSendRefreshRequired('devices');
1235
1001
  return;
1236
1002
  })
1237
- // eslint-disable-next-line promise/no-nesting
1238
1003
  .catch((_error) => {
1239
- //
1240
1004
  });
1241
1005
  }
1242
1006
  else {
1243
- // The plugin is already registered
1244
1007
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
1245
1008
  this.wssSendRefreshRequired('plugins');
1246
1009
  this.wssSendRestartRequired(true, true);
1247
1010
  }
1248
1011
  return;
1249
1012
  })
1250
- // eslint-disable-next-line promise/no-nesting
1251
1013
  .catch((_error) => {
1252
- //
1253
1014
  });
1254
1015
  }
1255
1016
  else {
1256
- // The package is matterbridge
1257
- // istanbul ignore next
1258
1017
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
1259
- // istanbul ignore next if
1260
1018
  if (this.matterbridge.restartMode !== '') {
1261
1019
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1262
1020
  this.matterbridge.shutdownProcess();
@@ -1279,9 +1037,7 @@ export class Frontend extends EventEmitter {
1279
1037
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' });
1280
1038
  return;
1281
1039
  }
1282
- // The package is a plugin
1283
1040
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
1284
- // istanbul ignore next if
1285
1041
  if (plugin) {
1286
1042
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
1287
1043
  await this.matterbridge.plugins.remove(data.params.packageName);
@@ -1289,7 +1045,6 @@ export class Frontend extends EventEmitter {
1289
1045
  this.wssSendRefreshRequired('plugins');
1290
1046
  this.wssSendRefreshRequired('devices');
1291
1047
  }
1292
- // Uninstall the package
1293
1048
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1294
1049
  const { spawnCommand } = await import('./utils/spawn.js');
1295
1050
  spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'], data.params.packageName)
@@ -1332,7 +1087,6 @@ export class Frontend extends EventEmitter {
1332
1087
  return;
1333
1088
  })
1334
1089
  .catch((_error) => {
1335
- //
1336
1090
  });
1337
1091
  }
1338
1092
  else {
@@ -1380,7 +1134,6 @@ export class Frontend extends EventEmitter {
1380
1134
  return;
1381
1135
  })
1382
1136
  .catch((_error) => {
1383
- //
1384
1137
  });
1385
1138
  }
1386
1139
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -1406,7 +1159,6 @@ export class Frontend extends EventEmitter {
1406
1159
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1407
1160
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
1408
1161
  if (plugin.serverNode) {
1409
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1410
1162
  await this.matterbridge.stopServerNode(plugin.serverNode);
1411
1163
  plugin.serverNode = undefined;
1412
1164
  }
@@ -1416,20 +1168,18 @@ export class Frontend extends EventEmitter {
1416
1168
  this.matterbridge.devices.remove(device);
1417
1169
  }
1418
1170
  }
1419
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1420
1171
  if (plugin.type === 'DynamicPlatform' && !plugin.locked)
1421
1172
  await this.matterbridge.createDynamicPlugin(plugin);
1422
1173
  await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
1423
- plugin.restartRequired = false; // Reset plugin restartRequired
1174
+ plugin.restartRequired = false;
1424
1175
  let needRestart = 0;
1425
1176
  for (const plugin of this.matterbridge.plugins) {
1426
1177
  if (plugin.restartRequired)
1427
1178
  needRestart++;
1428
1179
  }
1429
1180
  if (needRestart === 0) {
1430
- this.wssSendRestartNotRequired(true); // Reset global restart required message
1181
+ this.wssSendRestartNotRequired(true);
1431
1182
  }
1432
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1433
1183
  if (plugin.serverNode)
1434
1184
  await this.matterbridge.startServerNode(plugin.serverNode);
1435
1185
  this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
@@ -1685,22 +1435,22 @@ export class Frontend extends EventEmitter {
1685
1435
  if (isValidString(data.params.value, 4)) {
1686
1436
  this.log.debug('Matterbridge logger level:', data.params.value);
1687
1437
  if (data.params.value === 'Debug') {
1688
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1438
+ await this.matterbridge.setLogLevel("debug");
1689
1439
  }
1690
1440
  else if (data.params.value === 'Info') {
1691
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1441
+ await this.matterbridge.setLogLevel("info");
1692
1442
  }
1693
1443
  else if (data.params.value === 'Notice') {
1694
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1444
+ await this.matterbridge.setLogLevel("notice");
1695
1445
  }
1696
1446
  else if (data.params.value === 'Warn') {
1697
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1447
+ await this.matterbridge.setLogLevel("warn");
1698
1448
  }
1699
1449
  else if (data.params.value === 'Error') {
1700
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1450
+ await this.matterbridge.setLogLevel("error");
1701
1451
  }
1702
1452
  else if (data.params.value === 'Fatal') {
1703
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1453
+ await this.matterbridge.setLogLevel("fatal");
1704
1454
  }
1705
1455
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1706
1456
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -1711,7 +1461,6 @@ export class Frontend extends EventEmitter {
1711
1461
  this.log.debug('Matterbridge file log:', data.params.value);
1712
1462
  this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
1713
1463
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1714
- // Create the file logger for matterbridge
1715
1464
  if (data.params.value)
1716
1465
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1717
1466
  else
@@ -1790,7 +1539,6 @@ export class Frontend extends EventEmitter {
1790
1539
  }
1791
1540
  break;
1792
1541
  case 'setmatterport':
1793
- // eslint-disable-next-line no-case-declarations
1794
1542
  const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1795
1543
  if (isValidNumber(port, 5540, 5600)) {
1796
1544
  this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
@@ -1808,7 +1556,6 @@ export class Frontend extends EventEmitter {
1808
1556
  }
1809
1557
  break;
1810
1558
  case 'setmatterdiscriminator':
1811
- // eslint-disable-next-line no-case-declarations
1812
1559
  const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1813
1560
  if (isValidNumber(discriminator, 0, 4095)) {
1814
1561
  this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
@@ -1826,7 +1573,6 @@ export class Frontend extends EventEmitter {
1826
1573
  }
1827
1574
  break;
1828
1575
  case 'setmatterpasscode':
1829
- // eslint-disable-next-line no-case-declarations
1830
1576
  const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1831
1577
  if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
1832
1578
  this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
@@ -1870,19 +1616,15 @@ export class Frontend extends EventEmitter {
1870
1616
  return;
1871
1617
  }
1872
1618
  const config = plugin.configJson;
1873
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1874
1619
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1875
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1876
1620
  if (select === 'serial')
1877
1621
  this.log.info(`Selected device serial ${data.params.serial}`);
1878
1622
  if (select === 'name')
1879
1623
  this.log.info(`Selected device name ${data.params.name}`);
1880
1624
  if (config && select && (select === 'serial' || select === 'name')) {
1881
- // Remove postfix from the serial if it exists
1882
1625
  if (config.postfix) {
1883
1626
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1884
1627
  }
1885
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1886
1628
  if (isValidArray(config.whiteList, 1)) {
1887
1629
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1888
1630
  config.whiteList.push(data.params.serial);
@@ -1891,7 +1633,6 @@ export class Frontend extends EventEmitter {
1891
1633
  config.whiteList.push(data.params.name);
1892
1634
  }
1893
1635
  }
1894
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1895
1636
  if (isValidArray(config.blackList, 1)) {
1896
1637
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1897
1638
  config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
@@ -1919,9 +1660,7 @@ export class Frontend extends EventEmitter {
1919
1660
  return;
1920
1661
  }
1921
1662
  const config = plugin.configJson;
1922
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1923
1663
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1924
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1925
1664
  if (select === 'serial')
1926
1665
  this.log.info(`Unselected device serial ${data.params.serial}`);
1927
1666
  if (select === 'name')
@@ -1930,7 +1669,6 @@ export class Frontend extends EventEmitter {
1930
1669
  if (config.postfix) {
1931
1670
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1932
1671
  }
1933
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1934
1672
  if (isValidArray(config.whiteList, 1)) {
1935
1673
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1936
1674
  config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
@@ -1939,7 +1677,6 @@ export class Frontend extends EventEmitter {
1939
1677
  config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
1940
1678
  }
1941
1679
  }
1942
- // Add the serial to the blackList
1943
1680
  if (isValidArray(config.blackList)) {
1944
1681
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1945
1682
  config.blackList.push(data.params.serial);
@@ -1962,7 +1699,6 @@ export class Frontend extends EventEmitter {
1962
1699
  }
1963
1700
  }
1964
1701
  else {
1965
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1966
1702
  const localData = data;
1967
1703
  this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
1968
1704
  sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
@@ -1972,44 +1708,21 @@ export class Frontend extends EventEmitter {
1972
1708
  inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
1973
1709
  }
1974
1710
  }
1975
- /**
1976
- * Sends a WebSocket log message to all connected clients. The function is called by AnsiLogger.setGlobalCallback.
1977
- *
1978
- * @param {string} level - The logger level of the message: debug info notice warn error fatal...
1979
- * @param {string} time - The time string of the message
1980
- * @param {string} name - The logger name of the message
1981
- * @param {string} message - The content of the message.
1982
- *
1983
- * @remarks
1984
- * The function removes ANSI escape codes, leading asterisks, non-printable characters, and replaces all occurrences of \t and \n.
1985
- * It also replaces all occurrences of \" with " and angle-brackets with &lt; and &gt;.
1986
- * The function sends the message to all connected clients.
1987
- */
1988
1711
  wssSendLogMessage(level, time, name, message) {
1989
1712
  if (!level || !time || !name || !message)
1990
1713
  return;
1991
- // Remove ANSI escape codes from the message
1992
- // eslint-disable-next-line no-control-regex
1993
1714
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1994
- // Remove leading asterisks from the message
1995
1715
  message = message.replace(/^\*+/, '');
1996
- // Replace all occurrences of \t and \n
1997
1716
  message = message.replace(/[\t\n]/g, '');
1998
- // Remove non-printable characters
1999
- // eslint-disable-next-line no-control-regex
2000
1717
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
2001
- // Replace all occurrences of \" with "
2002
1718
  message = message.replace(/\\"/g, '"');
2003
- // Define the maximum allowed length for continuous characters without a space
2004
1719
  const maxContinuousLength = 100;
2005
1720
  const keepStartLength = 20;
2006
1721
  const keepEndLength = 20;
2007
- // Split the message into words
2008
1722
  if (level !== 'spawn') {
2009
1723
  message = message
2010
1724
  .split(' ')
2011
1725
  .map((word) => {
2012
- // If the word length exceeds the max continuous length, insert spaces and truncate
2013
1726
  if (word.length > maxContinuousLength) {
2014
1727
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
2015
1728
  }
@@ -2017,161 +1730,60 @@ export class Frontend extends EventEmitter {
2017
1730
  })
2018
1731
  .join(' ');
2019
1732
  }
2020
- // Send the message to all connected clients
2021
1733
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
2022
1734
  }
2023
- /**
2024
- * Sends a need to refresh WebSocket message to all connected clients.
2025
- *
2026
- * @param {string} changed - The changed value.
2027
- * @param {Record<string, unknown>} params - Additional parameters to send with the message.
2028
- * possible values for changed:
2029
- * - 'settings'
2030
- * - 'plugins'
2031
- * - 'devices'
2032
- * - 'matter' with param 'matter' (QRDiv component)
2033
- * @param {ApiMatterResponse} params.matter - The matter device that has changed. Required if changed is 'matter'.
2034
- */
2035
1735
  wssSendRefreshRequired(changed, params) {
2036
1736
  this.log.debug('Sending a refresh required message to all connected clients');
2037
- // Send the message to all connected clients
2038
1737
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
2039
1738
  }
2040
- /**
2041
- * Sends a need to restart WebSocket message to all connected clients.
2042
- *
2043
- * @param {boolean} snackbar - If true, a snackbar message will be sent to all connected clients. Default is true.
2044
- * @param {boolean} fixed - If true, the restart is fixed and will not be reset by plugin restarts. Default is false.
2045
- */
2046
1739
  wssSendRestartRequired(snackbar = true, fixed = false) {
2047
1740
  this.log.debug('Sending a restart required message to all connected clients');
2048
1741
  this.matterbridge.matterbridgeInformation.restartRequired = true;
2049
1742
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
2050
1743
  if (snackbar === true)
2051
1744
  this.wssSendSnackbarMessage(`Restart required`, 0);
2052
- // Send the message to all connected clients
2053
1745
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
2054
1746
  }
2055
- /**
2056
- * Sends a no need to restart WebSocket message to all connected clients.
2057
- *
2058
- * @param {boolean} snackbar - If true, the snackbar message will be cleared from all connected clients. Default is true.
2059
- */
2060
1747
  wssSendRestartNotRequired(snackbar = true) {
2061
1748
  this.log.debug('Sending a restart not required message to all connected clients');
2062
1749
  this.matterbridge.matterbridgeInformation.restartRequired = false;
2063
1750
  if (snackbar === true)
2064
1751
  this.wssSendCloseSnackbarMessage(`Restart required`);
2065
- // Send the message to all connected clients
2066
1752
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
2067
1753
  }
2068
- /**
2069
- * Sends a need to update WebSocket message to all connected clients.
2070
- *
2071
- * @param {boolean} devVersion - If true, the update is for a development version. Default is false.
2072
- */
2073
1754
  wssSendUpdateRequired(devVersion = false) {
2074
1755
  this.log.debug('Sending an update required message to all connected clients');
2075
1756
  this.matterbridge.matterbridgeInformation.updateRequired = true;
2076
- // Send the message to all connected clients
2077
1757
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
2078
1758
  }
2079
- /**
2080
- * Sends a cpu update message to all connected clients.
2081
- *
2082
- * @param {number} cpuUsage - The CPU usage percentage to send.
2083
- */
2084
1759
  wssSendCpuUpdate(cpuUsage) {
2085
1760
  if (hasParameter('debug'))
2086
1761
  this.log.debug('Sending a cpu update message to all connected clients');
2087
- // Send the message to all connected clients
2088
1762
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', success: true, response: { cpuUsage: Math.round(cpuUsage * 100) / 100 } });
2089
1763
  }
2090
- /**
2091
- * Sends a memory update message to all connected clients.
2092
- *
2093
- * @param {string} totalMemory - The total memory in bytes.
2094
- * @param {string} freeMemory - The free memory in bytes.
2095
- * @param {string} rss - The resident set size in bytes.
2096
- * @param {string} heapTotal - The total heap memory in bytes.
2097
- * @param {string} heapUsed - The used heap memory in bytes.
2098
- * @param {string} external - The external memory in bytes.
2099
- * @param {string} arrayBuffers - The array buffers memory in bytes.
2100
- */
2101
1764
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
2102
1765
  if (hasParameter('debug'))
2103
1766
  this.log.debug('Sending a memory update message to all connected clients');
2104
- // Send the message to all connected clients
2105
1767
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', success: true, response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
2106
1768
  }
2107
- /**
2108
- * Sends an uptime update message to all connected clients.
2109
- *
2110
- * @param {string} systemUptime - The system uptime in a human-readable format.
2111
- * @param {string} processUptime - The process uptime in a human-readable format.
2112
- */
2113
1769
  wssSendUptimeUpdate(systemUptime, processUptime) {
2114
1770
  if (hasParameter('debug'))
2115
1771
  this.log.debug('Sending a uptime update message to all connected clients');
2116
- // Send the message to all connected clients
2117
1772
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
2118
1773
  }
2119
- /**
2120
- * Sends an open snackbar message to all connected clients.
2121
- *
2122
- * @param {string} message - The message to send.
2123
- * @param {number} timeout - The timeout in seconds for the snackbar message. Default is 5 seconds.
2124
- * @param {'info' | 'warning' | 'error' | 'success'} severity - The severity of the message.
2125
- * possible values are: 'info', 'warning', 'error', 'success'. Default is 'info'.
2126
- *
2127
- * @remarks
2128
- * If timeout is 0, the snackbar message will be displayed until closed by the user.
2129
- */
2130
1774
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
2131
1775
  this.log.debug('Sending a snackbar message to all connected clients');
2132
- // Send the message to all connected clients
2133
1776
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
2134
1777
  }
2135
- /**
2136
- * Sends a close snackbar message to all connected clients.
2137
- * It will close the snackbar message with the same message and timeout = 0.
2138
- *
2139
- * @param {string} message - The message to send.
2140
- */
2141
1778
  wssSendCloseSnackbarMessage(message) {
2142
1779
  this.log.debug('Sending a close snackbar message to all connected clients');
2143
- // Send the message to all connected clients
2144
1780
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
2145
1781
  }
2146
- /**
2147
- * Sends an attribute update message to all connected WebSocket clients.
2148
- *
2149
- * @param {string | undefined} plugin - The name of the plugin.
2150
- * @param {string | undefined} serialNumber - The serial number of the device.
2151
- * @param {string | undefined} uniqueId - The unique identifier of the device.
2152
- * @param {EndpointNumber} number - The endpoint number where the attribute belongs.
2153
- * @param {string} id - The endpoint id where the attribute belongs.
2154
- * @param {string} cluster - The cluster name where the attribute belongs.
2155
- * @param {string} attribute - The name of the attribute that changed.
2156
- * @param {number | string | boolean} value - The new value of the attribute.
2157
- *
2158
- * @remarks
2159
- * This method logs a debug message and sends a JSON-formatted message to all connected WebSocket clients
2160
- * with the updated attribute information.
2161
- */
2162
1782
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
2163
1783
  this.log.debug('Sending an attribute update message to all connected clients');
2164
- // Send the message to all connected clients
2165
1784
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', success: true, response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value } });
2166
1785
  }
2167
- /**
2168
- * Sends a message to all connected clients.
2169
- * This is an helper function to send a broadcast message to all connected clients.
2170
- *
2171
- * @param {WsMessageBroadcast} msg - The message to send.
2172
- */
2173
1786
  wssBroadcastMessage(msg) {
2174
- // Send the message to all connected clients
2175
1787
  const stringifiedMsg = JSON.stringify(msg);
2176
1788
  if (msg.method !== 'log')
2177
1789
  this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
@@ -2182,4 +1794,3 @@ export class Frontend extends EventEmitter {
2182
1794
  });
2183
1795
  }
2184
1796
  }
2185
- //# sourceMappingURL=frontend.js.map