matterbridge 3.2.0 → 3.2.1-dev-20250802-f978b33

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