matterbridge 3.2.9 → 3.2.10-dev-20250928-30c21de

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 (287) hide show
  1. package/CHANGELOG.md +18 -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 +27 -417
  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 +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 +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 +0 -304
  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_node_modules.js +19 -19
  74. package/frontend/package-lock.json +115 -100
  75. package/frontend/package.json +9 -9
  76. package/npm-shrinkwrap.json +2 -2
  77. package/package.json +1 -2
  78. package/dist/cli.d.ts +0 -26
  79. package/dist/cli.d.ts.map +0 -1
  80. package/dist/cli.js.map +0 -1
  81. package/dist/cliEmitter.d.ts +0 -34
  82. package/dist/cliEmitter.d.ts.map +0 -1
  83. package/dist/cliEmitter.js.map +0 -1
  84. package/dist/clusters/export.d.ts +0 -2
  85. package/dist/clusters/export.d.ts.map +0 -1
  86. package/dist/clusters/export.js.map +0 -1
  87. package/dist/defaultConfigSchema.d.ts +0 -28
  88. package/dist/defaultConfigSchema.d.ts.map +0 -1
  89. package/dist/defaultConfigSchema.js.map +0 -1
  90. package/dist/deviceManager.d.ts +0 -112
  91. package/dist/deviceManager.d.ts.map +0 -1
  92. package/dist/deviceManager.js.map +0 -1
  93. package/dist/devices/airConditioner.d.ts +0 -98
  94. package/dist/devices/airConditioner.d.ts.map +0 -1
  95. package/dist/devices/airConditioner.js.map +0 -1
  96. package/dist/devices/batteryStorage.d.ts +0 -48
  97. package/dist/devices/batteryStorage.d.ts.map +0 -1
  98. package/dist/devices/batteryStorage.js.map +0 -1
  99. package/dist/devices/cooktop.d.ts +0 -60
  100. package/dist/devices/cooktop.d.ts.map +0 -1
  101. package/dist/devices/cooktop.js.map +0 -1
  102. package/dist/devices/dishwasher.d.ts +0 -71
  103. package/dist/devices/dishwasher.d.ts.map +0 -1
  104. package/dist/devices/dishwasher.js.map +0 -1
  105. package/dist/devices/evse.d.ts +0 -75
  106. package/dist/devices/evse.d.ts.map +0 -1
  107. package/dist/devices/evse.js.map +0 -1
  108. package/dist/devices/export.d.ts +0 -17
  109. package/dist/devices/export.d.ts.map +0 -1
  110. package/dist/devices/export.js.map +0 -1
  111. package/dist/devices/extractorHood.d.ts +0 -46
  112. package/dist/devices/extractorHood.d.ts.map +0 -1
  113. package/dist/devices/extractorHood.js.map +0 -1
  114. package/dist/devices/heatPump.d.ts +0 -47
  115. package/dist/devices/heatPump.d.ts.map +0 -1
  116. package/dist/devices/heatPump.js.map +0 -1
  117. package/dist/devices/laundryDryer.d.ts +0 -67
  118. package/dist/devices/laundryDryer.d.ts.map +0 -1
  119. package/dist/devices/laundryDryer.js.map +0 -1
  120. package/dist/devices/laundryWasher.d.ts +0 -81
  121. package/dist/devices/laundryWasher.d.ts.map +0 -1
  122. package/dist/devices/laundryWasher.js.map +0 -1
  123. package/dist/devices/microwaveOven.d.ts +0 -168
  124. package/dist/devices/microwaveOven.d.ts.map +0 -1
  125. package/dist/devices/microwaveOven.js.map +0 -1
  126. package/dist/devices/oven.d.ts +0 -105
  127. package/dist/devices/oven.d.ts.map +0 -1
  128. package/dist/devices/oven.js.map +0 -1
  129. package/dist/devices/refrigerator.d.ts +0 -118
  130. package/dist/devices/refrigerator.d.ts.map +0 -1
  131. package/dist/devices/refrigerator.js.map +0 -1
  132. package/dist/devices/roboticVacuumCleaner.d.ts +0 -112
  133. package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
  134. package/dist/devices/roboticVacuumCleaner.js.map +0 -1
  135. package/dist/devices/solarPower.d.ts +0 -40
  136. package/dist/devices/solarPower.d.ts.map +0 -1
  137. package/dist/devices/solarPower.js.map +0 -1
  138. package/dist/devices/speaker.d.ts +0 -87
  139. package/dist/devices/speaker.d.ts.map +0 -1
  140. package/dist/devices/speaker.js.map +0 -1
  141. package/dist/devices/temperatureControl.d.ts +0 -166
  142. package/dist/devices/temperatureControl.d.ts.map +0 -1
  143. package/dist/devices/temperatureControl.js.map +0 -1
  144. package/dist/devices/waterHeater.d.ts +0 -111
  145. package/dist/devices/waterHeater.d.ts.map +0 -1
  146. package/dist/devices/waterHeater.js.map +0 -1
  147. package/dist/dgram/coap.d.ts +0 -205
  148. package/dist/dgram/coap.d.ts.map +0 -1
  149. package/dist/dgram/coap.js.map +0 -1
  150. package/dist/dgram/dgram.d.ts +0 -141
  151. package/dist/dgram/dgram.d.ts.map +0 -1
  152. package/dist/dgram/dgram.js.map +0 -1
  153. package/dist/dgram/mb_coap.d.ts +0 -24
  154. package/dist/dgram/mb_coap.d.ts.map +0 -1
  155. package/dist/dgram/mb_coap.js.map +0 -1
  156. package/dist/dgram/mb_mdns.d.ts +0 -24
  157. package/dist/dgram/mb_mdns.d.ts.map +0 -1
  158. package/dist/dgram/mb_mdns.js.map +0 -1
  159. package/dist/dgram/mdns.d.ts +0 -290
  160. package/dist/dgram/mdns.d.ts.map +0 -1
  161. package/dist/dgram/mdns.js.map +0 -1
  162. package/dist/dgram/multicast.d.ts +0 -67
  163. package/dist/dgram/multicast.d.ts.map +0 -1
  164. package/dist/dgram/multicast.js.map +0 -1
  165. package/dist/dgram/unicast.d.ts +0 -56
  166. package/dist/dgram/unicast.d.ts.map +0 -1
  167. package/dist/dgram/unicast.js.map +0 -1
  168. package/dist/frontend.d.ts +0 -232
  169. package/dist/frontend.d.ts.map +0 -1
  170. package/dist/frontend.js.map +0 -1
  171. package/dist/frontendTypes.d.ts +0 -514
  172. package/dist/frontendTypes.d.ts.map +0 -1
  173. package/dist/frontendTypes.js.map +0 -1
  174. package/dist/globalMatterbridge.d.ts +0 -59
  175. package/dist/globalMatterbridge.d.ts.map +0 -1
  176. package/dist/globalMatterbridge.js.map +0 -1
  177. package/dist/helpers.d.ts +0 -48
  178. package/dist/helpers.d.ts.map +0 -1
  179. package/dist/helpers.js.map +0 -1
  180. package/dist/index.d.ts +0 -33
  181. package/dist/index.d.ts.map +0 -1
  182. package/dist/index.js.map +0 -1
  183. package/dist/logger/export.d.ts +0 -2
  184. package/dist/logger/export.d.ts.map +0 -1
  185. package/dist/logger/export.js.map +0 -1
  186. package/dist/matter/behaviors.d.ts +0 -2
  187. package/dist/matter/behaviors.d.ts.map +0 -1
  188. package/dist/matter/behaviors.js.map +0 -1
  189. package/dist/matter/clusters.d.ts +0 -2
  190. package/dist/matter/clusters.d.ts.map +0 -1
  191. package/dist/matter/clusters.js.map +0 -1
  192. package/dist/matter/devices.d.ts +0 -2
  193. package/dist/matter/devices.d.ts.map +0 -1
  194. package/dist/matter/devices.js.map +0 -1
  195. package/dist/matter/endpoints.d.ts +0 -2
  196. package/dist/matter/endpoints.d.ts.map +0 -1
  197. package/dist/matter/endpoints.js.map +0 -1
  198. package/dist/matter/export.d.ts +0 -5
  199. package/dist/matter/export.d.ts.map +0 -1
  200. package/dist/matter/export.js.map +0 -1
  201. package/dist/matter/types.d.ts +0 -3
  202. package/dist/matter/types.d.ts.map +0 -1
  203. package/dist/matter/types.js.map +0 -1
  204. package/dist/matterbridge.d.ts +0 -444
  205. package/dist/matterbridge.d.ts.map +0 -1
  206. package/dist/matterbridge.js.map +0 -1
  207. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
  208. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  209. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  210. package/dist/matterbridgeBehaviors.d.ts +0 -1747
  211. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  212. package/dist/matterbridgeBehaviors.js.map +0 -1
  213. package/dist/matterbridgeDeviceTypes.d.ts +0 -761
  214. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  215. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  216. package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
  217. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  218. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  219. package/dist/matterbridgeEndpoint.d.ts +0 -1534
  220. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  221. package/dist/matterbridgeEndpoint.js.map +0 -1
  222. package/dist/matterbridgeEndpointHelpers.d.ts +0 -407
  223. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  224. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  225. package/dist/matterbridgePlatform.d.ts +0 -365
  226. package/dist/matterbridgePlatform.d.ts.map +0 -1
  227. package/dist/matterbridgePlatform.js.map +0 -1
  228. package/dist/matterbridgeTypes.d.ts +0 -197
  229. package/dist/matterbridgeTypes.d.ts.map +0 -1
  230. package/dist/matterbridgeTypes.js.map +0 -1
  231. package/dist/pluginManager.d.ts +0 -270
  232. package/dist/pluginManager.d.ts.map +0 -1
  233. package/dist/pluginManager.js.map +0 -1
  234. package/dist/shelly.d.ts +0 -174
  235. package/dist/shelly.d.ts.map +0 -1
  236. package/dist/shelly.js.map +0 -1
  237. package/dist/storage/export.d.ts +0 -2
  238. package/dist/storage/export.d.ts.map +0 -1
  239. package/dist/storage/export.js.map +0 -1
  240. package/dist/update.d.ts +0 -75
  241. package/dist/update.d.ts.map +0 -1
  242. package/dist/update.js.map +0 -1
  243. package/dist/utils/colorUtils.d.ts +0 -99
  244. package/dist/utils/colorUtils.d.ts.map +0 -1
  245. package/dist/utils/colorUtils.js.map +0 -1
  246. package/dist/utils/commandLine.d.ts +0 -59
  247. package/dist/utils/commandLine.d.ts.map +0 -1
  248. package/dist/utils/commandLine.js.map +0 -1
  249. package/dist/utils/copyDirectory.d.ts +0 -33
  250. package/dist/utils/copyDirectory.d.ts.map +0 -1
  251. package/dist/utils/copyDirectory.js.map +0 -1
  252. package/dist/utils/createDirectory.d.ts +0 -34
  253. package/dist/utils/createDirectory.d.ts.map +0 -1
  254. package/dist/utils/createDirectory.js.map +0 -1
  255. package/dist/utils/createZip.d.ts +0 -39
  256. package/dist/utils/createZip.d.ts.map +0 -1
  257. package/dist/utils/createZip.js.map +0 -1
  258. package/dist/utils/deepCopy.d.ts +0 -32
  259. package/dist/utils/deepCopy.d.ts.map +0 -1
  260. package/dist/utils/deepCopy.js.map +0 -1
  261. package/dist/utils/deepEqual.d.ts +0 -54
  262. package/dist/utils/deepEqual.d.ts.map +0 -1
  263. package/dist/utils/deepEqual.js.map +0 -1
  264. package/dist/utils/error.d.ts +0 -44
  265. package/dist/utils/error.d.ts.map +0 -1
  266. package/dist/utils/error.js.map +0 -1
  267. package/dist/utils/export.d.ts +0 -13
  268. package/dist/utils/export.d.ts.map +0 -1
  269. package/dist/utils/export.js.map +0 -1
  270. package/dist/utils/hex.d.ts +0 -89
  271. package/dist/utils/hex.d.ts.map +0 -1
  272. package/dist/utils/hex.js.map +0 -1
  273. package/dist/utils/isvalid.d.ts +0 -103
  274. package/dist/utils/isvalid.d.ts.map +0 -1
  275. package/dist/utils/isvalid.js.map +0 -1
  276. package/dist/utils/jestHelpers.d.ts +0 -137
  277. package/dist/utils/jestHelpers.d.ts.map +0 -1
  278. package/dist/utils/jestHelpers.js.map +0 -1
  279. package/dist/utils/network.d.ts +0 -84
  280. package/dist/utils/network.d.ts.map +0 -1
  281. package/dist/utils/network.js.map +0 -1
  282. package/dist/utils/spawn.d.ts +0 -34
  283. package/dist/utils/spawn.d.ts.map +0 -1
  284. package/dist/utils/spawn.js.map +0 -1
  285. package/dist/utils/wait.d.ts +0 -54
  286. package/dist/utils/wait.d.ts.map +0 -1
  287. 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,7 +639,6 @@ 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;
@@ -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,35 @@ 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');
1235
1000
  return;
1236
1001
  })
1237
- // eslint-disable-next-line promise/no-nesting
1238
1002
  .catch((_error) => {
1239
- //
1240
1003
  });
1241
1004
  }
1242
1005
  else {
1243
- // The plugin is already registered
1244
1006
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
1245
1007
  this.wssSendRefreshRequired('plugins');
1246
1008
  this.wssSendRestartRequired(true, true);
1247
1009
  }
1248
1010
  return;
1249
1011
  })
1250
- // eslint-disable-next-line promise/no-nesting
1251
1012
  .catch((_error) => {
1252
- //
1253
1013
  });
1254
1014
  }
1255
1015
  else {
1256
- // The package is matterbridge
1257
- // istanbul ignore next
1258
1016
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = true;
1259
- // istanbul ignore next if
1260
1017
  if (this.matterbridge.restartMode !== '') {
1261
1018
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1262
1019
  this.matterbridge.shutdownProcess();
@@ -1279,9 +1036,7 @@ export class Frontend extends EventEmitter {
1279
1036
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' });
1280
1037
  return;
1281
1038
  }
1282
- // The package is a plugin
1283
1039
  const plugin = this.matterbridge.plugins.get(data.params.packageName);
1284
- // istanbul ignore next if
1285
1040
  if (plugin) {
1286
1041
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
1287
1042
  await this.matterbridge.plugins.remove(data.params.packageName);
@@ -1289,7 +1044,6 @@ export class Frontend extends EventEmitter {
1289
1044
  this.wssSendRefreshRequired('plugins');
1290
1045
  this.wssSendRefreshRequired('devices');
1291
1046
  }
1292
- // Uninstall the package
1293
1047
  this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
1294
1048
  const { spawnCommand } = await import('./utils/spawn.js');
1295
1049
  spawnCommand(this.matterbridge, 'npm', ['uninstall', '-g', data.params.packageName, '--verbose'], data.params.packageName)
@@ -1332,7 +1086,6 @@ export class Frontend extends EventEmitter {
1332
1086
  return;
1333
1087
  })
1334
1088
  .catch((_error) => {
1335
- //
1336
1089
  });
1337
1090
  }
1338
1091
  else {
@@ -1380,7 +1133,6 @@ export class Frontend extends EventEmitter {
1380
1133
  return;
1381
1134
  })
1382
1135
  .catch((_error) => {
1383
- //
1384
1136
  });
1385
1137
  }
1386
1138
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -1406,7 +1158,6 @@ export class Frontend extends EventEmitter {
1406
1158
  const plugin = this.matterbridge.plugins.get(data.params.pluginName);
1407
1159
  await this.matterbridge.plugins.shutdown(plugin, 'The plugin is restarting.', false, true);
1408
1160
  if (plugin.serverNode) {
1409
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1410
1161
  await this.matterbridge.stopServerNode(plugin.serverNode);
1411
1162
  plugin.serverNode = undefined;
1412
1163
  }
@@ -1416,20 +1167,18 @@ export class Frontend extends EventEmitter {
1416
1167
  this.matterbridge.devices.remove(device);
1417
1168
  }
1418
1169
  }
1419
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1420
1170
  if (plugin.type === 'DynamicPlatform' && !plugin.locked)
1421
1171
  await this.matterbridge.createDynamicPlugin(plugin);
1422
1172
  await this.matterbridge.plugins.load(plugin, true, 'The plugin has been restarted', true);
1423
- plugin.restartRequired = false; // Reset plugin restartRequired
1173
+ plugin.restartRequired = false;
1424
1174
  let needRestart = 0;
1425
1175
  for (const plugin of this.matterbridge.plugins) {
1426
1176
  if (plugin.restartRequired)
1427
1177
  needRestart++;
1428
1178
  }
1429
1179
  if (needRestart === 0) {
1430
- this.wssSendRestartNotRequired(true); // Reset global restart required message
1180
+ this.wssSendRestartNotRequired(true);
1431
1181
  }
1432
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1433
1182
  if (plugin.serverNode)
1434
1183
  await this.matterbridge.startServerNode(plugin.serverNode);
1435
1184
  this.wssSendSnackbarMessage(`Restarted plugin ${data.params.pluginName}`, 5, 'success');
@@ -1685,22 +1434,22 @@ export class Frontend extends EventEmitter {
1685
1434
  if (isValidString(data.params.value, 4)) {
1686
1435
  this.log.debug('Matterbridge logger level:', data.params.value);
1687
1436
  if (data.params.value === 'Debug') {
1688
- await this.matterbridge.setLogLevel("debug" /* LogLevel.DEBUG */);
1437
+ await this.matterbridge.setLogLevel("debug");
1689
1438
  }
1690
1439
  else if (data.params.value === 'Info') {
1691
- await this.matterbridge.setLogLevel("info" /* LogLevel.INFO */);
1440
+ await this.matterbridge.setLogLevel("info");
1692
1441
  }
1693
1442
  else if (data.params.value === 'Notice') {
1694
- await this.matterbridge.setLogLevel("notice" /* LogLevel.NOTICE */);
1443
+ await this.matterbridge.setLogLevel("notice");
1695
1444
  }
1696
1445
  else if (data.params.value === 'Warn') {
1697
- await this.matterbridge.setLogLevel("warn" /* LogLevel.WARN */);
1446
+ await this.matterbridge.setLogLevel("warn");
1698
1447
  }
1699
1448
  else if (data.params.value === 'Error') {
1700
- await this.matterbridge.setLogLevel("error" /* LogLevel.ERROR */);
1449
+ await this.matterbridge.setLogLevel("error");
1701
1450
  }
1702
1451
  else if (data.params.value === 'Fatal') {
1703
- await this.matterbridge.setLogLevel("fatal" /* LogLevel.FATAL */);
1452
+ await this.matterbridge.setLogLevel("fatal");
1704
1453
  }
1705
1454
  await this.matterbridge.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
1706
1455
  sendResponse({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, success: true });
@@ -1711,7 +1460,6 @@ export class Frontend extends EventEmitter {
1711
1460
  this.log.debug('Matterbridge file log:', data.params.value);
1712
1461
  this.matterbridge.matterbridgeInformation.fileLogger = data.params.value;
1713
1462
  await this.matterbridge.nodeContext?.set('matterbridgeFileLog', data.params.value);
1714
- // Create the file logger for matterbridge
1715
1463
  if (data.params.value)
1716
1464
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, this.matterbridge.matterbridgeLoggerFile), this.matterbridge.matterbridgeInformation.loggerLevel, true);
1717
1465
  else
@@ -1790,7 +1538,6 @@ export class Frontend extends EventEmitter {
1790
1538
  }
1791
1539
  break;
1792
1540
  case 'setmatterport':
1793
- // eslint-disable-next-line no-case-declarations
1794
1541
  const port = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1795
1542
  if (isValidNumber(port, 5540, 5600)) {
1796
1543
  this.log.debug(`Set matter commissioning port to ${CYAN}${port}${db}`);
@@ -1808,7 +1555,6 @@ export class Frontend extends EventEmitter {
1808
1555
  }
1809
1556
  break;
1810
1557
  case 'setmatterdiscriminator':
1811
- // eslint-disable-next-line no-case-declarations
1812
1558
  const discriminator = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1813
1559
  if (isValidNumber(discriminator, 0, 4095)) {
1814
1560
  this.log.debug(`Set matter commissioning discriminator to ${CYAN}${discriminator}${db}`);
@@ -1826,7 +1572,6 @@ export class Frontend extends EventEmitter {
1826
1572
  }
1827
1573
  break;
1828
1574
  case 'setmatterpasscode':
1829
- // eslint-disable-next-line no-case-declarations
1830
1575
  const passcode = isValidString(data.params.value) ? parseInt(data.params.value) : 0;
1831
1576
  if (isValidNumber(passcode, 1, 99999998) && CommissioningOptions.FORBIDDEN_PASSCODES.includes(passcode) === false) {
1832
1577
  this.matterbridge.matterbridgeInformation.matterPasscode = passcode;
@@ -1870,19 +1615,15 @@ export class Frontend extends EventEmitter {
1870
1615
  return;
1871
1616
  }
1872
1617
  const config = plugin.configJson;
1873
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1874
1618
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1875
- // this.log.debug(`SelectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1876
1619
  if (select === 'serial')
1877
1620
  this.log.info(`Selected device serial ${data.params.serial}`);
1878
1621
  if (select === 'name')
1879
1622
  this.log.info(`Selected device name ${data.params.name}`);
1880
1623
  if (config && select && (select === 'serial' || select === 'name')) {
1881
- // Remove postfix from the serial if it exists
1882
1624
  if (config.postfix) {
1883
1625
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1884
1626
  }
1885
- // Add the serial to the whiteList if the whiteList exists and the serial or name is not already in it
1886
1627
  if (isValidArray(config.whiteList, 1)) {
1887
1628
  if (select === 'serial' && !config.whiteList.includes(data.params.serial)) {
1888
1629
  config.whiteList.push(data.params.serial);
@@ -1891,7 +1632,6 @@ export class Frontend extends EventEmitter {
1891
1632
  config.whiteList.push(data.params.name);
1892
1633
  }
1893
1634
  }
1894
- // Remove the serial from the blackList if the blackList exists and the serial or name is in it
1895
1635
  if (isValidArray(config.blackList, 1)) {
1896
1636
  if (select === 'serial' && config.blackList.includes(data.params.serial)) {
1897
1637
  config.blackList = config.blackList.filter((item) => item !== localData.params.serial);
@@ -1919,9 +1659,7 @@ export class Frontend extends EventEmitter {
1919
1659
  return;
1920
1660
  }
1921
1661
  const config = plugin.configJson;
1922
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1923
1662
  const select = plugin.schemaJson?.properties?.blackList?.selectFrom;
1924
- // this.log.debug(`UnselectDevice(selectMode ${select}) data ${debugStringify(data)}`);
1925
1663
  if (select === 'serial')
1926
1664
  this.log.info(`Unselected device serial ${data.params.serial}`);
1927
1665
  if (select === 'name')
@@ -1930,7 +1668,6 @@ export class Frontend extends EventEmitter {
1930
1668
  if (config.postfix) {
1931
1669
  data.params.serial = data.params.serial.replace('-' + config.postfix, '');
1932
1670
  }
1933
- // Remove the serial from the whiteList if the whiteList exists and the serial is in it
1934
1671
  if (isValidArray(config.whiteList, 1)) {
1935
1672
  if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
1936
1673
  config.whiteList = config.whiteList.filter((item) => item !== localData.params.serial);
@@ -1939,7 +1676,6 @@ export class Frontend extends EventEmitter {
1939
1676
  config.whiteList = config.whiteList.filter((item) => item !== localData.params.name);
1940
1677
  }
1941
1678
  }
1942
- // Add the serial to the blackList
1943
1679
  if (isValidArray(config.blackList)) {
1944
1680
  if (select === 'serial' && !config.blackList.includes(data.params.serial)) {
1945
1681
  config.blackList.push(data.params.serial);
@@ -1962,7 +1698,6 @@ export class Frontend extends EventEmitter {
1962
1698
  }
1963
1699
  }
1964
1700
  else {
1965
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1966
1701
  const localData = data;
1967
1702
  this.log.error(`Invalid method from websocket client: ${debugStringify(localData)}`);
1968
1703
  sendResponse({ id: localData.id, method: localData.method, src: 'Matterbridge', dst: localData.src, error: 'Invalid method' });
@@ -1972,44 +1707,21 @@ export class Frontend extends EventEmitter {
1972
1707
  inspectError(this.log, `Error processing message "${message}" from websocket client`, error);
1973
1708
  }
1974
1709
  }
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
1710
  wssSendLogMessage(level, time, name, message) {
1989
1711
  if (!level || !time || !name || !message)
1990
1712
  return;
1991
- // Remove ANSI escape codes from the message
1992
- // eslint-disable-next-line no-control-regex
1993
1713
  message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
1994
- // Remove leading asterisks from the message
1995
1714
  message = message.replace(/^\*+/, '');
1996
- // Replace all occurrences of \t and \n
1997
1715
  message = message.replace(/[\t\n]/g, '');
1998
- // Remove non-printable characters
1999
- // eslint-disable-next-line no-control-regex
2000
1716
  message = message.replace(/[\x00-\x1F\x7F]/g, '');
2001
- // Replace all occurrences of \" with "
2002
1717
  message = message.replace(/\\"/g, '"');
2003
- // Define the maximum allowed length for continuous characters without a space
2004
1718
  const maxContinuousLength = 100;
2005
1719
  const keepStartLength = 20;
2006
1720
  const keepEndLength = 20;
2007
- // Split the message into words
2008
1721
  if (level !== 'spawn') {
2009
1722
  message = message
2010
1723
  .split(' ')
2011
1724
  .map((word) => {
2012
- // If the word length exceeds the max continuous length, insert spaces and truncate
2013
1725
  if (word.length > maxContinuousLength) {
2014
1726
  return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
2015
1727
  }
@@ -2017,161 +1729,60 @@ export class Frontend extends EventEmitter {
2017
1729
  })
2018
1730
  .join(' ');
2019
1731
  }
2020
- // Send the message to all connected clients
2021
1732
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'log', success: true, response: { level, time, name, message } });
2022
1733
  }
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
1734
  wssSendRefreshRequired(changed, params) {
2036
1735
  this.log.debug('Sending a refresh required message to all connected clients');
2037
- // Send the message to all connected clients
2038
1736
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'refresh_required', success: true, response: { changed, ...params } });
2039
1737
  }
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
1738
  wssSendRestartRequired(snackbar = true, fixed = false) {
2047
1739
  this.log.debug('Sending a restart required message to all connected clients');
2048
1740
  this.matterbridge.matterbridgeInformation.restartRequired = true;
2049
1741
  this.matterbridge.matterbridgeInformation.fixedRestartRequired = fixed;
2050
1742
  if (snackbar === true)
2051
1743
  this.wssSendSnackbarMessage(`Restart required`, 0);
2052
- // Send the message to all connected clients
2053
1744
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', success: true, response: { fixed } });
2054
1745
  }
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
1746
  wssSendRestartNotRequired(snackbar = true) {
2061
1747
  this.log.debug('Sending a restart not required message to all connected clients');
2062
1748
  this.matterbridge.matterbridgeInformation.restartRequired = false;
2063
1749
  if (snackbar === true)
2064
1750
  this.wssSendCloseSnackbarMessage(`Restart required`);
2065
- // Send the message to all connected clients
2066
1751
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
2067
1752
  }
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
1753
  wssSendUpdateRequired(devVersion = false) {
2074
1754
  this.log.debug('Sending an update required message to all connected clients');
2075
1755
  this.matterbridge.matterbridgeInformation.updateRequired = true;
2076
- // Send the message to all connected clients
2077
1756
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
2078
1757
  }
2079
- /**
2080
- * Sends a cpu update message to all connected clients.
2081
- *
2082
- * @param {number} cpuUsage - The CPU usage percentage to send.
2083
- */
2084
1758
  wssSendCpuUpdate(cpuUsage) {
2085
1759
  if (hasParameter('debug'))
2086
1760
  this.log.debug('Sending a cpu update message to all connected clients');
2087
- // Send the message to all connected clients
2088
1761
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'cpu_update', success: true, response: { cpuUsage: Math.round(cpuUsage * 100) / 100 } });
2089
1762
  }
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
1763
  wssSendMemoryUpdate(totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers) {
2102
1764
  if (hasParameter('debug'))
2103
1765
  this.log.debug('Sending a memory update message to all connected clients');
2104
- // Send the message to all connected clients
2105
1766
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'memory_update', success: true, response: { totalMemory, freeMemory, rss, heapTotal, heapUsed, external, arrayBuffers } });
2106
1767
  }
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
1768
  wssSendUptimeUpdate(systemUptime, processUptime) {
2114
1769
  if (hasParameter('debug'))
2115
1770
  this.log.debug('Sending a uptime update message to all connected clients');
2116
- // Send the message to all connected clients
2117
1771
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'uptime_update', success: true, response: { systemUptime, processUptime } });
2118
1772
  }
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
1773
  wssSendSnackbarMessage(message, timeout = 5, severity = 'info') {
2131
1774
  this.log.debug('Sending a snackbar message to all connected clients');
2132
- // Send the message to all connected clients
2133
1775
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'snackbar', success: true, response: { message, timeout, severity } });
2134
1776
  }
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
1777
  wssSendCloseSnackbarMessage(message) {
2142
1778
  this.log.debug('Sending a close snackbar message to all connected clients');
2143
- // Send the message to all connected clients
2144
1779
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'close_snackbar', success: true, response: { message } });
2145
1780
  }
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
1781
  wssSendAttributeChangedMessage(plugin, serialNumber, uniqueId, number, id, cluster, attribute, value) {
2163
1782
  this.log.debug('Sending an attribute update message to all connected clients');
2164
- // Send the message to all connected clients
2165
1783
  this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'state_update', success: true, response: { plugin, serialNumber, uniqueId, number, id, cluster, attribute, value } });
2166
1784
  }
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
1785
  wssBroadcastMessage(msg) {
2174
- // Send the message to all connected clients
2175
1786
  const stringifiedMsg = JSON.stringify(msg);
2176
1787
  if (msg.method !== 'log')
2177
1788
  this.log.debug(`Sending a broadcast message: ${debugStringify(msg)}`);
@@ -2182,4 +1793,3 @@ export class Frontend extends EventEmitter {
2182
1793
  });
2183
1794
  }
2184
1795
  }
2185
- //# sourceMappingURL=frontend.js.map