matterbridge 3.4.0 → 3.4.1-dev-20251127-826b2bf

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 (332) hide show
  1. package/CHANGELOG.md +128 -112
  2. package/README-DEV.md +2 -2
  3. package/README-DOCKER.md +1 -1
  4. package/README-MACOS-PLIST.md +1 -1
  5. package/README-NGINX.md +1 -1
  6. package/README-PODMAN.md +1 -1
  7. package/README-SERVICE-LOCAL.md +1 -1
  8. package/README-SERVICE-OPT.md +6 -1
  9. package/README-SERVICE.md +1 -1
  10. package/README.md +57 -49
  11. package/dist/broadcastServer.js +1 -93
  12. package/dist/broadcastServerTypes.js +0 -24
  13. package/dist/cli.js +1 -97
  14. package/dist/cliEmitter.js +0 -37
  15. package/dist/cliHistory.js +0 -38
  16. package/dist/clusters/export.js +0 -2
  17. package/dist/defaultConfigSchema.js +0 -24
  18. package/dist/deviceManager.js +1 -113
  19. package/dist/devices/airConditioner.js +0 -57
  20. package/dist/devices/batteryStorage.js +1 -48
  21. package/dist/devices/cooktop.js +0 -56
  22. package/dist/devices/dishwasher.js +0 -57
  23. package/dist/devices/evse.js +10 -74
  24. package/dist/devices/export.js +0 -5
  25. package/dist/devices/extractorHood.js +0 -43
  26. package/dist/devices/heatPump.js +2 -50
  27. package/dist/devices/laundryDryer.js +3 -62
  28. package/dist/devices/laundryWasher.js +4 -70
  29. package/dist/devices/microwaveOven.js +5 -88
  30. package/dist/devices/oven.js +0 -85
  31. package/dist/devices/refrigerator.js +0 -102
  32. package/dist/devices/roboticVacuumCleaner.js +9 -100
  33. package/dist/devices/solarPower.js +0 -38
  34. package/dist/devices/speaker.js +0 -84
  35. package/dist/devices/temperatureControl.js +3 -24
  36. package/dist/devices/waterHeater.js +2 -82
  37. package/dist/dgram/coap.js +13 -126
  38. package/dist/dgram/dgram.js +2 -114
  39. package/dist/dgram/mb_coap.js +3 -41
  40. package/dist/dgram/mb_mdns.js +15 -80
  41. package/dist/dgram/mdns.js +137 -299
  42. package/dist/dgram/multicast.js +1 -62
  43. package/dist/dgram/unicast.js +0 -54
  44. package/dist/frontend.js +35 -455
  45. package/dist/frontendTypes.js +0 -45
  46. package/dist/helpers.js +0 -53
  47. package/dist/index.js +0 -25
  48. package/dist/jestutils/export.js +0 -1
  49. package/dist/jestutils/jestHelpers.js +13 -352
  50. package/dist/logger/export.js +0 -1
  51. package/dist/matter/behaviors.js +0 -2
  52. package/dist/matter/clusters.js +0 -2
  53. package/dist/matter/devices.js +0 -2
  54. package/dist/matter/endpoints.js +0 -2
  55. package/dist/matter/export.js +0 -3
  56. package/dist/matter/types.js +0 -3
  57. package/dist/matterNode.js +8 -369
  58. package/dist/matterbridge.js +74 -788
  59. package/dist/matterbridgeAccessoryPlatform.js +0 -38
  60. package/dist/matterbridgeBehaviors.js +5 -68
  61. package/dist/matterbridgeDeviceTypes.js +14 -635
  62. package/dist/matterbridgeDynamicPlatform.js +0 -38
  63. package/dist/matterbridgeEndpoint.js +53 -1444
  64. package/dist/matterbridgeEndpointHelpers.js +20 -483
  65. package/dist/matterbridgeEndpointTypes.js +0 -25
  66. package/dist/matterbridgePlatform.js +2 -460
  67. package/dist/matterbridgeTypes.js +0 -26
  68. package/dist/pluginManager.js +5 -340
  69. package/dist/shelly.js +7 -168
  70. package/dist/storage/export.js +0 -1
  71. package/dist/update.js +0 -69
  72. package/dist/utils/colorUtils.js +2 -97
  73. package/dist/utils/commandLine.js +0 -60
  74. package/dist/utils/copyDirectory.js +0 -37
  75. package/dist/utils/createDirectory.js +0 -33
  76. package/dist/utils/createZip.js +2 -47
  77. package/dist/utils/deepCopy.js +0 -39
  78. package/dist/utils/deepEqual.js +1 -72
  79. package/dist/utils/error.js +0 -41
  80. package/dist/utils/export.js +0 -1
  81. package/dist/utils/format.js +0 -49
  82. package/dist/utils/hex.js +0 -124
  83. package/dist/utils/inspector.js +1 -69
  84. package/dist/utils/isvalid.js +0 -101
  85. package/dist/utils/network.js +5 -96
  86. package/dist/utils/spawn.js +1 -71
  87. package/dist/utils/tracker.js +1 -64
  88. package/dist/utils/wait.js +8 -60
  89. package/frontend/build/assets/index.js +4 -4
  90. package/frontend/build/assets/vendor_mui.js +1 -1
  91. package/frontend/build/assets/vendor_node_modules.js +19 -83
  92. package/frontend/build/assets/vendor_qrcode.js +1 -9
  93. package/frontend/build/assets/vendor_rjsf.js +1 -9
  94. package/frontend/package-lock.json +229 -439
  95. package/frontend/package.json +15 -15
  96. package/marked.ps1 +15 -0
  97. package/npm-shrinkwrap.json +165 -231
  98. package/package.json +2 -3
  99. package/dist/broadcastServer.d.ts +0 -115
  100. package/dist/broadcastServer.d.ts.map +0 -1
  101. package/dist/broadcastServer.js.map +0 -1
  102. package/dist/broadcastServerTypes.d.ts +0 -838
  103. package/dist/broadcastServerTypes.d.ts.map +0 -1
  104. package/dist/broadcastServerTypes.js.map +0 -1
  105. package/dist/cli.d.ts +0 -30
  106. package/dist/cli.d.ts.map +0 -1
  107. package/dist/cli.js.map +0 -1
  108. package/dist/cliEmitter.d.ts +0 -50
  109. package/dist/cliEmitter.d.ts.map +0 -1
  110. package/dist/cliEmitter.js.map +0 -1
  111. package/dist/cliHistory.d.ts +0 -48
  112. package/dist/cliHistory.d.ts.map +0 -1
  113. package/dist/cliHistory.js.map +0 -1
  114. package/dist/clusters/export.d.ts +0 -2
  115. package/dist/clusters/export.d.ts.map +0 -1
  116. package/dist/clusters/export.js.map +0 -1
  117. package/dist/defaultConfigSchema.d.ts +0 -28
  118. package/dist/defaultConfigSchema.d.ts.map +0 -1
  119. package/dist/defaultConfigSchema.js.map +0 -1
  120. package/dist/deviceManager.d.ts +0 -135
  121. package/dist/deviceManager.d.ts.map +0 -1
  122. package/dist/deviceManager.js.map +0 -1
  123. package/dist/devices/airConditioner.d.ts +0 -98
  124. package/dist/devices/airConditioner.d.ts.map +0 -1
  125. package/dist/devices/airConditioner.js.map +0 -1
  126. package/dist/devices/batteryStorage.d.ts +0 -48
  127. package/dist/devices/batteryStorage.d.ts.map +0 -1
  128. package/dist/devices/batteryStorage.js.map +0 -1
  129. package/dist/devices/cooktop.d.ts +0 -61
  130. package/dist/devices/cooktop.d.ts.map +0 -1
  131. package/dist/devices/cooktop.js.map +0 -1
  132. package/dist/devices/dishwasher.d.ts +0 -71
  133. package/dist/devices/dishwasher.d.ts.map +0 -1
  134. package/dist/devices/dishwasher.js.map +0 -1
  135. package/dist/devices/evse.d.ts +0 -76
  136. package/dist/devices/evse.d.ts.map +0 -1
  137. package/dist/devices/evse.js.map +0 -1
  138. package/dist/devices/export.d.ts +0 -17
  139. package/dist/devices/export.d.ts.map +0 -1
  140. package/dist/devices/export.js.map +0 -1
  141. package/dist/devices/extractorHood.d.ts +0 -46
  142. package/dist/devices/extractorHood.d.ts.map +0 -1
  143. package/dist/devices/extractorHood.js.map +0 -1
  144. package/dist/devices/heatPump.d.ts +0 -47
  145. package/dist/devices/heatPump.d.ts.map +0 -1
  146. package/dist/devices/heatPump.js.map +0 -1
  147. package/dist/devices/laundryDryer.d.ts +0 -67
  148. package/dist/devices/laundryDryer.d.ts.map +0 -1
  149. package/dist/devices/laundryDryer.js.map +0 -1
  150. package/dist/devices/laundryWasher.d.ts +0 -81
  151. package/dist/devices/laundryWasher.d.ts.map +0 -1
  152. package/dist/devices/laundryWasher.js.map +0 -1
  153. package/dist/devices/microwaveOven.d.ts +0 -168
  154. package/dist/devices/microwaveOven.d.ts.map +0 -1
  155. package/dist/devices/microwaveOven.js.map +0 -1
  156. package/dist/devices/oven.d.ts +0 -105
  157. package/dist/devices/oven.d.ts.map +0 -1
  158. package/dist/devices/oven.js.map +0 -1
  159. package/dist/devices/refrigerator.d.ts +0 -118
  160. package/dist/devices/refrigerator.d.ts.map +0 -1
  161. package/dist/devices/refrigerator.js.map +0 -1
  162. package/dist/devices/roboticVacuumCleaner.d.ts +0 -112
  163. package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
  164. package/dist/devices/roboticVacuumCleaner.js.map +0 -1
  165. package/dist/devices/solarPower.d.ts +0 -40
  166. package/dist/devices/solarPower.d.ts.map +0 -1
  167. package/dist/devices/solarPower.js.map +0 -1
  168. package/dist/devices/speaker.d.ts +0 -87
  169. package/dist/devices/speaker.d.ts.map +0 -1
  170. package/dist/devices/speaker.js.map +0 -1
  171. package/dist/devices/temperatureControl.d.ts +0 -166
  172. package/dist/devices/temperatureControl.d.ts.map +0 -1
  173. package/dist/devices/temperatureControl.js.map +0 -1
  174. package/dist/devices/waterHeater.d.ts +0 -111
  175. package/dist/devices/waterHeater.d.ts.map +0 -1
  176. package/dist/devices/waterHeater.js.map +0 -1
  177. package/dist/dgram/coap.d.ts +0 -205
  178. package/dist/dgram/coap.d.ts.map +0 -1
  179. package/dist/dgram/coap.js.map +0 -1
  180. package/dist/dgram/dgram.d.ts +0 -141
  181. package/dist/dgram/dgram.d.ts.map +0 -1
  182. package/dist/dgram/dgram.js.map +0 -1
  183. package/dist/dgram/mb_coap.d.ts +0 -24
  184. package/dist/dgram/mb_coap.d.ts.map +0 -1
  185. package/dist/dgram/mb_coap.js.map +0 -1
  186. package/dist/dgram/mb_mdns.d.ts +0 -24
  187. package/dist/dgram/mb_mdns.d.ts.map +0 -1
  188. package/dist/dgram/mb_mdns.js.map +0 -1
  189. package/dist/dgram/mdns.d.ts +0 -290
  190. package/dist/dgram/mdns.d.ts.map +0 -1
  191. package/dist/dgram/mdns.js.map +0 -1
  192. package/dist/dgram/multicast.d.ts +0 -67
  193. package/dist/dgram/multicast.d.ts.map +0 -1
  194. package/dist/dgram/multicast.js.map +0 -1
  195. package/dist/dgram/unicast.d.ts +0 -56
  196. package/dist/dgram/unicast.d.ts.map +0 -1
  197. package/dist/dgram/unicast.js.map +0 -1
  198. package/dist/frontend.d.ts +0 -238
  199. package/dist/frontend.d.ts.map +0 -1
  200. package/dist/frontend.js.map +0 -1
  201. package/dist/frontendTypes.d.ts +0 -529
  202. package/dist/frontendTypes.d.ts.map +0 -1
  203. package/dist/frontendTypes.js.map +0 -1
  204. package/dist/helpers.d.ts +0 -48
  205. package/dist/helpers.d.ts.map +0 -1
  206. package/dist/helpers.js.map +0 -1
  207. package/dist/index.d.ts +0 -34
  208. package/dist/index.d.ts.map +0 -1
  209. package/dist/index.js.map +0 -1
  210. package/dist/jestutils/export.d.ts +0 -2
  211. package/dist/jestutils/export.d.ts.map +0 -1
  212. package/dist/jestutils/export.js.map +0 -1
  213. package/dist/jestutils/jestHelpers.d.ts +0 -303
  214. package/dist/jestutils/jestHelpers.d.ts.map +0 -1
  215. package/dist/jestutils/jestHelpers.js.map +0 -1
  216. package/dist/logger/export.d.ts +0 -2
  217. package/dist/logger/export.d.ts.map +0 -1
  218. package/dist/logger/export.js.map +0 -1
  219. package/dist/matter/behaviors.d.ts +0 -2
  220. package/dist/matter/behaviors.d.ts.map +0 -1
  221. package/dist/matter/behaviors.js.map +0 -1
  222. package/dist/matter/clusters.d.ts +0 -2
  223. package/dist/matter/clusters.d.ts.map +0 -1
  224. package/dist/matter/clusters.js.map +0 -1
  225. package/dist/matter/devices.d.ts +0 -2
  226. package/dist/matter/devices.d.ts.map +0 -1
  227. package/dist/matter/devices.js.map +0 -1
  228. package/dist/matter/endpoints.d.ts +0 -2
  229. package/dist/matter/endpoints.d.ts.map +0 -1
  230. package/dist/matter/endpoints.js.map +0 -1
  231. package/dist/matter/export.d.ts +0 -5
  232. package/dist/matter/export.d.ts.map +0 -1
  233. package/dist/matter/export.js.map +0 -1
  234. package/dist/matter/types.d.ts +0 -3
  235. package/dist/matter/types.d.ts.map +0 -1
  236. package/dist/matter/types.js.map +0 -1
  237. package/dist/matterNode.d.ts +0 -342
  238. package/dist/matterNode.d.ts.map +0 -1
  239. package/dist/matterNode.js.map +0 -1
  240. package/dist/matterbridge.d.ts +0 -473
  241. package/dist/matterbridge.d.ts.map +0 -1
  242. package/dist/matterbridge.js.map +0 -1
  243. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -41
  244. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  245. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  246. package/dist/matterbridgeBehaviors.d.ts +0 -2404
  247. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  248. package/dist/matterbridgeBehaviors.js.map +0 -1
  249. package/dist/matterbridgeDeviceTypes.d.ts +0 -698
  250. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  251. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  252. package/dist/matterbridgeDynamicPlatform.d.ts +0 -41
  253. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  254. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  255. package/dist/matterbridgeEndpoint.d.ts +0 -1507
  256. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  257. package/dist/matterbridgeEndpoint.js.map +0 -1
  258. package/dist/matterbridgeEndpointHelpers.d.ts +0 -787
  259. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  260. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  261. package/dist/matterbridgeEndpointTypes.d.ts +0 -166
  262. package/dist/matterbridgeEndpointTypes.d.ts.map +0 -1
  263. package/dist/matterbridgeEndpointTypes.js.map +0 -1
  264. package/dist/matterbridgePlatform.d.ts +0 -524
  265. package/dist/matterbridgePlatform.d.ts.map +0 -1
  266. package/dist/matterbridgePlatform.js.map +0 -1
  267. package/dist/matterbridgeTypes.d.ts +0 -251
  268. package/dist/matterbridgeTypes.d.ts.map +0 -1
  269. package/dist/matterbridgeTypes.js.map +0 -1
  270. package/dist/pluginManager.d.ts +0 -371
  271. package/dist/pluginManager.d.ts.map +0 -1
  272. package/dist/pluginManager.js.map +0 -1
  273. package/dist/shelly.d.ts +0 -174
  274. package/dist/shelly.d.ts.map +0 -1
  275. package/dist/shelly.js.map +0 -1
  276. package/dist/storage/export.d.ts +0 -2
  277. package/dist/storage/export.d.ts.map +0 -1
  278. package/dist/storage/export.js.map +0 -1
  279. package/dist/update.d.ts +0 -75
  280. package/dist/update.d.ts.map +0 -1
  281. package/dist/update.js.map +0 -1
  282. package/dist/utils/colorUtils.d.ts +0 -101
  283. package/dist/utils/colorUtils.d.ts.map +0 -1
  284. package/dist/utils/colorUtils.js.map +0 -1
  285. package/dist/utils/commandLine.d.ts +0 -66
  286. package/dist/utils/commandLine.d.ts.map +0 -1
  287. package/dist/utils/commandLine.js.map +0 -1
  288. package/dist/utils/copyDirectory.d.ts +0 -35
  289. package/dist/utils/copyDirectory.d.ts.map +0 -1
  290. package/dist/utils/copyDirectory.js.map +0 -1
  291. package/dist/utils/createDirectory.d.ts +0 -34
  292. package/dist/utils/createDirectory.d.ts.map +0 -1
  293. package/dist/utils/createDirectory.js.map +0 -1
  294. package/dist/utils/createZip.d.ts +0 -39
  295. package/dist/utils/createZip.d.ts.map +0 -1
  296. package/dist/utils/createZip.js.map +0 -1
  297. package/dist/utils/deepCopy.d.ts +0 -32
  298. package/dist/utils/deepCopy.d.ts.map +0 -1
  299. package/dist/utils/deepCopy.js.map +0 -1
  300. package/dist/utils/deepEqual.d.ts +0 -54
  301. package/dist/utils/deepEqual.d.ts.map +0 -1
  302. package/dist/utils/deepEqual.js.map +0 -1
  303. package/dist/utils/error.d.ts +0 -44
  304. package/dist/utils/error.d.ts.map +0 -1
  305. package/dist/utils/error.js.map +0 -1
  306. package/dist/utils/export.d.ts +0 -13
  307. package/dist/utils/export.d.ts.map +0 -1
  308. package/dist/utils/export.js.map +0 -1
  309. package/dist/utils/format.d.ts +0 -53
  310. package/dist/utils/format.d.ts.map +0 -1
  311. package/dist/utils/format.js.map +0 -1
  312. package/dist/utils/hex.d.ts +0 -89
  313. package/dist/utils/hex.d.ts.map +0 -1
  314. package/dist/utils/hex.js.map +0 -1
  315. package/dist/utils/inspector.d.ts +0 -87
  316. package/dist/utils/inspector.d.ts.map +0 -1
  317. package/dist/utils/inspector.js.map +0 -1
  318. package/dist/utils/isvalid.d.ts +0 -103
  319. package/dist/utils/isvalid.d.ts.map +0 -1
  320. package/dist/utils/isvalid.js.map +0 -1
  321. package/dist/utils/network.d.ts +0 -111
  322. package/dist/utils/network.d.ts.map +0 -1
  323. package/dist/utils/network.js.map +0 -1
  324. package/dist/utils/spawn.d.ts +0 -33
  325. package/dist/utils/spawn.d.ts.map +0 -1
  326. package/dist/utils/spawn.js.map +0 -1
  327. package/dist/utils/tracker.d.ts +0 -108
  328. package/dist/utils/tracker.d.ts.map +0 -1
  329. package/dist/utils/tracker.js.map +0 -1
  330. package/dist/utils/wait.d.ts +0 -54
  331. package/dist/utils/wait.d.ts.map +0 -1
  332. package/dist/utils/wait.js.map +0 -1
@@ -1,48 +1,19 @@
1
- /**
2
- * This file contains the class Matterbridge.
3
- *
4
- * @file matterbridge.ts
5
- * @author Luca Liguori
6
- * @created 2023-12-29
7
- * @version 1.6.0
8
- * @license Apache-2.0
9
- *
10
- * Copyright 2023, 2024, 2025 Luca Liguori.
11
- *
12
- * Licensed under the Apache License, Version 2.0 (the "License");
13
- * you may not use this file except in compliance with the License.
14
- * You may obtain a copy of the License at
15
- *
16
- * http://www.apache.org/licenses/LICENSE-2.0
17
- *
18
- * Unless required by applicable law or agreed to in writing, software
19
- * distributed under the License is distributed on an "AS IS" BASIS,
20
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
- * See the License for the specific language governing permissions and
22
- * limitations under the License.
23
- */
24
- // eslint-disable-next-line no-console
25
1
  if (process.argv.includes('--loader') || process.argv.includes('-loader'))
26
2
  console.log('\u001B[32mMatterbridge loaded.\u001B[40;0m');
27
- // Node.js modules
28
3
  import os from 'node:os';
29
4
  import path from 'node:path';
30
5
  import fs from 'node:fs';
31
6
  import EventEmitter from 'node:events';
32
7
  import { inspect } from 'node:util';
33
- // AnsiLogger module
34
8
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt, BLUE, or } from 'node-ansi-logger';
35
- // NodeStorage module
36
9
  import { NodeStorageManager } from 'node-persist-manager';
37
- // @matter
38
- import '@matter/nodejs'; // Set up Node.js environment for matter.js
10
+ import '@matter/nodejs';
39
11
  import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, UINT32_MAX, UINT16_MAX, Crypto, Environment, StorageService } from '@matter/general';
40
12
  import { FabricAction, PaseClient } from '@matter/protocol';
41
13
  import { Endpoint, ServerNode } from '@matter/node';
42
14
  import { DeviceTypeId, VendorId } from '@matter/types/datatype';
43
15
  import { AggregatorEndpoint } from '@matter/node/endpoints';
44
16
  import { BasicInformationServer } from '@matter/node/behaviors/basic-information';
45
- // Matterbridge
46
17
  import { getParameter, getIntParameter, hasParameter } from './utils/commandLine.js';
47
18
  import { copyDirectory } from './utils/copyDirectory.js';
48
19
  import { createDirectory } from './utils/createDirectory.js';
@@ -55,29 +26,21 @@ import { DeviceManager } from './deviceManager.js';
55
26
  import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
56
27
  import { bridge } from './matterbridgeDeviceTypes.js';
57
28
  import { Frontend } from './frontend.js';
58
- import { addVirtualDevices } from './helpers.js';
29
+ import { addVirtualDevice, addVirtualDevices } from './helpers.js';
59
30
  import { BroadcastServer } from './broadcastServer.js';
60
- /**
61
- * Represents the Matterbridge application.
62
- */
63
31
  export class Matterbridge extends EventEmitter {
64
- /** Matterbridge system information */
65
32
  systemInformation = {
66
- // Network properties
67
33
  interfaceName: '',
68
34
  macAddress: '',
69
35
  ipv4Address: '',
70
36
  ipv6Address: '',
71
- // Node.js properties
72
37
  nodeVersion: '',
73
- // Fixed system properties
74
38
  hostname: '',
75
39
  user: '',
76
40
  osType: '',
77
41
  osRelease: '',
78
42
  osPlatform: '',
79
43
  osArch: '',
80
- // Cpu and memory properties
81
44
  totalMemory: '',
82
45
  freeMemory: '',
83
46
  systemUptime: '',
@@ -88,7 +51,6 @@ export class Matterbridge extends EventEmitter {
88
51
  heapTotal: '',
89
52
  heapUsed: '',
90
53
  };
91
- // Matterbridge settings
92
54
  homeDirectory = '';
93
55
  rootDirectory = '';
94
56
  matterbridgeDirectory = '';
@@ -103,19 +65,12 @@ export class Matterbridge extends EventEmitter {
103
65
  restartMode = '';
104
66
  virtualMode = 'outlet';
105
67
  profile = getParameter('profile');
106
- /** Matterbridge logger */
107
- log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
108
- /** Matterbridge logger level */
68
+ log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
109
69
  logLevel = this.log.logLevel;
110
- /** Whether to log to a file */
111
70
  fileLogger = false;
112
- /** Matter logger */
113
- matterLog = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
114
- /** Matter logger level */
71
+ matterLog = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
115
72
  matterLogLevel = this.matterLog.logLevel;
116
- /** Whether to log Matter to a file */
117
73
  matterFileLogger = false;
118
- // Frontend settings
119
74
  readOnly = hasParameter('readonly') || hasParameter('shelly');
120
75
  shellyBoard = hasParameter('shelly');
121
76
  shellySysUpdate = false;
@@ -123,18 +78,12 @@ export class Matterbridge extends EventEmitter {
123
78
  restartRequired = false;
124
79
  fixedRestartRequired = false;
125
80
  updateRequired = false;
126
- // Managers
127
81
  plugins = new PluginManager(this);
128
82
  devices = new DeviceManager();
129
- // Frontend
130
83
  frontend = new Frontend(this);
131
- /** Matterbridge node storage manager created in the directory 'storage' in matterbridgeDirectory */
132
84
  nodeStorage;
133
- /** Matterbridge node context created with name 'matterbridge' */
134
85
  nodeContext;
135
- /** The main instance of the Matterbridge class (singleton) */
136
86
  static instance;
137
- // Instance properties
138
87
  shutdown = false;
139
88
  failCountLimit = hasParameter('shelly') ? 600 : 120;
140
89
  hasCleanupStarted = false;
@@ -149,32 +98,19 @@ export class Matterbridge extends EventEmitter {
149
98
  sigtermHandler;
150
99
  exceptionHandler;
151
100
  rejectionHandler;
152
- /** Matter environment default */
153
101
  environment = Environment.default;
154
- /** Matter storage service from environment default */
155
102
  matterStorageService;
156
- /** Matter storage manager created with name 'Matterbridge' */
157
103
  matterStorageManager;
158
- /** Matter matterbridge storage context created in the storage manager with name 'persist' */
159
104
  matterbridgeContext;
160
105
  controllerContext;
161
- /** Matter mdns interface e.g. 'eth0' or 'wlan0' or 'Wi-Fi' */
162
106
  mdnsInterface;
163
- /** Matter listeningAddressIpv4 address */
164
107
  ipv4Address;
165
- /** Matter listeningAddressIpv6 address */
166
108
  ipv6Address;
167
- /** Matter commissioning port */
168
- port; // first server node port
169
- /** Matter commissioning passcode */
170
- passcode; // first server node passcode
171
- /** Matter commissioning discriminator */
172
- discriminator; // first server node discriminator
173
- /** Matter device certification */
174
- certification; // device certification
175
- /** Matter server node in bridge mode */
109
+ port;
110
+ passcode;
111
+ discriminator;
112
+ certification;
176
113
  serverNode;
177
- /** Matter aggregator node in bridge mode */
178
114
  aggregatorNode;
179
115
  aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
180
116
  aggregatorVendorName = getParameter('vendorName') ?? 'Matterbridge';
@@ -183,13 +119,10 @@ export class Matterbridge extends EventEmitter {
183
119
  aggregatorDeviceType = DeviceTypeId(getIntParameter('deviceType') ?? bridge.code);
184
120
  aggregatorSerialNumber = getParameter('serialNumber');
185
121
  aggregatorUniqueId = getParameter('uniqueId');
186
- /** Advertising nodes map: time advertising started keyed by storeId */
187
122
  advertisingNodes = new Map();
188
- /** Broadcast server */
189
123
  server;
190
124
  debug = hasParameter('debug') || hasParameter('verbose');
191
125
  verbose = hasParameter('verbose');
192
- /** We load asyncronously so is private */
193
126
  constructor() {
194
127
  super();
195
128
  this.log.logNameColor = '\x1b[38;5;115m';
@@ -229,19 +162,8 @@ export class Matterbridge extends EventEmitter {
229
162
  }
230
163
  }
231
164
  }
232
- //* ************************************************************************************************************************************ */
233
- // loadInstance() and cleanup() methods */
234
- //* ************************************************************************************************************************************ */
235
- /**
236
- * Loads an instance of the Matterbridge class.
237
- * If an instance already exists, return that instance.
238
- *
239
- * @param {boolean} initialize - Whether to initialize the Matterbridge instance after loading. Defaults to false.
240
- * @returns {Matterbridge} A promise that resolves to the Matterbridge instance.
241
- */
242
165
  static async loadInstance(initialize = false) {
243
166
  if (!Matterbridge.instance) {
244
- // eslint-disable-next-line no-console
245
167
  if (hasParameter('debug'))
246
168
  console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
247
169
  Matterbridge.instance = new Matterbridge();
@@ -250,84 +172,56 @@ export class Matterbridge extends EventEmitter {
250
172
  }
251
173
  return Matterbridge.instance;
252
174
  }
253
- /**
254
- * Initializes the Matterbridge application.
255
- *
256
- * @remarks
257
- * This method performs the necessary setup and initialization steps for the Matterbridge application.
258
- * It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
259
- * node version, registers signal handlers, initializes storage, and parses the command line.
260
- *
261
- * @returns {Promise<void>} A Promise that resolves when the initialization is complete.
262
- */
263
175
  async initialize() {
264
- // for (let i = 1; i <= 255; i++) console.log(`\x1b[38;5;${i}mColor: ${i}`);
265
- // Emit the initialize_started event
266
176
  this.emit('initialize_started');
267
- // Set the restart mode
268
177
  if (hasParameter('service'))
269
178
  this.restartMode = 'service';
270
179
  if (hasParameter('docker'))
271
180
  this.restartMode = 'docker';
272
- // Set the matterbridge home directory
273
181
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
274
182
  await createDirectory(this.homeDirectory, 'Matterbridge Home Directory', this.log);
275
- // Set the matterbridge directory
276
183
  this.matterbridgeDirectory = this.profile ? path.join(this.homeDirectory, '.matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, '.matterbridge');
277
184
  await createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory', this.log);
278
185
  await createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory', this.log);
279
186
  await createDirectory(path.join(this.matterbridgeDirectory, 'uploads'), 'Matterbridge Frontend Uploads Directory', this.log);
280
- // Set the matterbridge plugin directory
281
187
  this.matterbridgePluginDirectory = this.profile ? path.join(this.homeDirectory, 'Matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, 'Matterbridge');
282
188
  await createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory', this.log);
283
- // Set the matterbridge cert directory
284
189
  this.matterbridgeCertDirectory = this.profile ? path.join(this.homeDirectory, '.mattercert', 'profiles', this.profile) : path.join(this.homeDirectory, '.mattercert');
285
190
  await createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory', this.log);
286
- // Set the matterbridge root directory
287
191
  const { fileURLToPath } = await import('node:url');
288
192
  const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
289
- this.rootDirectory = path.resolve(currentFileDirectory, '../'); // Adjust the path for dist directory
290
- // Setup the matter environment with default values
193
+ this.rootDirectory = path.resolve(currentFileDirectory, '../');
291
194
  this.environment.vars.set('log.level', MatterLogLevel.INFO);
292
195
  this.environment.vars.set('log.format', MatterLogFormat.ANSI);
293
196
  this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME));
294
197
  this.environment.vars.set('runtime.signals', false);
295
198
  this.environment.vars.set('runtime.exitcode', false);
296
- // Register process handlers
297
199
  this.registerProcessHandlers();
298
- // Initialize nodeStorage and nodeContext
299
200
  try {
300
201
  this.log.debug(`Creating node storage manager: ${CYAN}${NODE_STORAGE_DIR}${db}`);
301
202
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), writeQueue: false, expiredInterval: undefined, logging: false });
302
203
  this.log.debug('Creating node storage context for matterbridge');
303
204
  this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
304
- // TODO: Remove this code when node-persist-manager is updated
305
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
306
205
  const keys = (await this.nodeStorage?.storage.keys());
307
206
  for (const key of keys) {
308
207
  this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
309
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
310
208
  await this.nodeStorage?.storage.get(key);
311
209
  }
312
210
  const storages = await this.nodeStorage.getStorageNames();
313
211
  for (const storage of storages) {
314
212
  this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
315
213
  const nodeContext = await this.nodeStorage?.createStorage(storage);
316
- // TODO: Remove this code when node-persist-manager is updated
317
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
318
214
  const keys = (await nodeContext?.storage.keys());
319
215
  keys.forEach(async (key) => {
320
216
  this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
321
217
  await nodeContext?.get(key);
322
218
  });
323
219
  }
324
- // Creating a backup of the node storage since it is not corrupted
325
220
  this.log.debug('Creating node storage backup...');
326
221
  await copyDirectory(path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR + '.backup'));
327
222
  this.log.debug('Created node storage backup');
328
223
  }
329
224
  catch (error) {
330
- // Restoring the backup of the node storage since it is corrupted
331
225
  this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
332
226
  if (hasParameter('norestore')) {
333
227
  this.log.fatal(`The matterbridge storage is corrupted. Found -norestore parameter: exiting...`);
@@ -341,19 +235,14 @@ export class Matterbridge extends EventEmitter {
341
235
  if (!this.nodeStorage || !this.nodeContext) {
342
236
  throw new Error('Fatal error creating node storage manager and context for matterbridge');
343
237
  }
344
- // Set the first port to use for the commissioning server (will be incremented in childbridge mode)
345
238
  this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
346
- // Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
347
239
  this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode(this.environment.get(Crypto));
348
- // Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
349
240
  this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
350
- // Certificate management
351
241
  const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
352
242
  try {
353
243
  await fs.promises.access(pairingFilePath, fs.constants.R_OK);
354
244
  const pairingFileContent = await fs.promises.readFile(pairingFilePath, 'utf8');
355
245
  const pairingFileJson = JSON.parse(pairingFileContent);
356
- // Set the vendorId, vendorName, productId, productName, deviceType, serialNumber, uniqueId if they are present in the pairing file
357
246
  if (isValidNumber(pairingFileJson.vendorId)) {
358
247
  this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
359
248
  this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using vendorId ${CYAN}${this.aggregatorVendorId}${nf} from pairing file.`);
@@ -382,13 +271,11 @@ export class Matterbridge extends EventEmitter {
382
271
  this.aggregatorUniqueId = pairingFileJson.uniqueId;
383
272
  this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using uniqueId ${CYAN}${this.aggregatorUniqueId}${nf} from pairing file.`);
384
273
  }
385
- // Override the passcode and discriminator if they are present in the pairing file
386
274
  if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
387
275
  this.passcode = pairingFileJson.passcode;
388
276
  this.discriminator = pairingFileJson.discriminator;
389
277
  this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
390
278
  }
391
- // Set the certification for matter.js if it is present in the pairing file
392
279
  if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
393
280
  const { hexToBuffer } = await import('./utils/hex.js');
394
281
  this.certification = {
@@ -403,44 +290,41 @@ export class Matterbridge extends EventEmitter {
403
290
  catch (error) {
404
291
  this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
405
292
  }
406
- // Store the passcode, discriminator and port in the node context
407
293
  await this.nodeContext.set('matterport', this.port);
408
294
  await this.nodeContext.set('matterpasscode', this.passcode);
409
295
  await this.nodeContext.set('matterdiscriminator', this.discriminator);
410
296
  this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
411
- // Set matterbridge logger level (context: matterbridgeLogLevel)
412
297
  if (hasParameter('logger')) {
413
298
  const level = getParameter('logger');
414
299
  if (level === 'debug') {
415
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
300
+ this.log.logLevel = "debug";
416
301
  }
417
302
  else if (level === 'info') {
418
- this.log.logLevel = "info" /* LogLevel.INFO */;
303
+ this.log.logLevel = "info";
419
304
  }
420
305
  else if (level === 'notice') {
421
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
306
+ this.log.logLevel = "notice";
422
307
  }
423
308
  else if (level === 'warn') {
424
- this.log.logLevel = "warn" /* LogLevel.WARN */;
309
+ this.log.logLevel = "warn";
425
310
  }
426
311
  else if (level === 'error') {
427
- this.log.logLevel = "error" /* LogLevel.ERROR */;
312
+ this.log.logLevel = "error";
428
313
  }
429
314
  else if (level === 'fatal') {
430
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
315
+ this.log.logLevel = "fatal";
431
316
  }
432
317
  else {
433
318
  this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
434
- this.log.logLevel = "info" /* LogLevel.INFO */;
319
+ this.log.logLevel = "info";
435
320
  }
436
321
  }
437
322
  else {
438
- this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice" /* LogLevel.NOTICE */ : "info" /* LogLevel.INFO */);
323
+ this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice" : "info");
439
324
  }
440
325
  this.logLevel = this.log.logLevel;
441
326
  this.frontend.logLevel = this.log.logLevel;
442
327
  MatterbridgeEndpoint.logLevel = this.log.logLevel;
443
- // Create the file logger for matterbridge (context: matterbridgeFileLog)
444
328
  if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
445
329
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), this.log.logLevel, true);
446
330
  this.fileLogger = true;
@@ -449,7 +333,6 @@ export class Matterbridge extends EventEmitter {
449
333
  this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.fileLogger}.`);
450
334
  if (this.profile !== undefined)
451
335
  this.log.debug(`Matterbridge profile: ${this.profile}.`);
452
- // Set matter.js logger level, format and logger (context: matterLogLevel)
453
336
  if (hasParameter('matterlogger')) {
454
337
  const level = getParameter('matterlogger');
455
338
  if (level === 'debug') {
@@ -480,13 +363,11 @@ export class Matterbridge extends EventEmitter {
480
363
  }
481
364
  Logger.format = MatterLogFormat.ANSI;
482
365
  this.matterLogLevel = MatterLogLevel.names[Logger.level];
483
- // Create the logger for matter.js with file logging (context: matterFileLog)
484
366
  if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
485
367
  this.matterFileLogger = true;
486
368
  }
487
369
  Logger.destinations.default.write = this.createDestinationMatterLogger(this.matterFileLogger);
488
370
  this.log.debug(`Matter logLevel: ${this.matterLogLevel} fileLoger: ${this.matterFileLogger}.`);
489
- // Log network interfaces
490
371
  const networkInterfaces = os.networkInterfaces();
491
372
  const availableAddresses = Object.entries(networkInterfaces);
492
373
  const availableInterfaceNames = Object.keys(networkInterfaces);
@@ -499,7 +380,6 @@ export class Matterbridge extends EventEmitter {
499
380
  });
500
381
  }
501
382
  }
502
- // Set the interface to use for matter server node mdnsInterface
503
383
  if (hasParameter('mdnsinterface')) {
504
384
  this.mdnsInterface = getParameter('mdnsinterface');
505
385
  }
@@ -508,7 +388,6 @@ export class Matterbridge extends EventEmitter {
508
388
  if (this.mdnsInterface === '')
509
389
  this.mdnsInterface = undefined;
510
390
  }
511
- // Validate mdnsInterface
512
391
  if (this.mdnsInterface) {
513
392
  if (!availableInterfaceNames.includes(this.mdnsInterface)) {
514
393
  this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaceNames.join(', ')}. Using all available interfaces.`);
@@ -521,7 +400,6 @@ export class Matterbridge extends EventEmitter {
521
400
  }
522
401
  if (this.mdnsInterface)
523
402
  this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
524
- // Set the listeningAddressIpv4 for the matter commissioning server
525
403
  if (hasParameter('ipv4address')) {
526
404
  this.ipv4Address = getParameter('ipv4address');
527
405
  }
@@ -530,7 +408,6 @@ export class Matterbridge extends EventEmitter {
530
408
  if (this.ipv4Address === '')
531
409
  this.ipv4Address = undefined;
532
410
  }
533
- // Validate ipv4address
534
411
  if (this.ipv4Address) {
535
412
  let isValid = false;
536
413
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -546,7 +423,6 @@ export class Matterbridge extends EventEmitter {
546
423
  await this.nodeContext.remove('matteripv4address');
547
424
  }
548
425
  }
549
- // Set the listeningAddressIpv6 for the matter commissioning server
550
426
  if (hasParameter('ipv6address')) {
551
427
  this.ipv6Address = getParameter('ipv6address');
552
428
  }
@@ -555,7 +431,6 @@ export class Matterbridge extends EventEmitter {
555
431
  if (this.ipv6Address === '')
556
432
  this.ipv6Address = undefined;
557
433
  }
558
- // Validate ipv6address
559
434
  if (this.ipv6Address) {
560
435
  let isValid = false;
561
436
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -564,7 +439,6 @@ export class Matterbridge extends EventEmitter {
564
439
  isValid = true;
565
440
  break;
566
441
  }
567
- /* istanbul ignore next */
568
442
  if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6Address)) {
569
443
  this.log.info(`Using ipv6address ${CYAN}${this.ipv6Address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
570
444
  isValid = true;
@@ -577,7 +451,6 @@ export class Matterbridge extends EventEmitter {
577
451
  await this.nodeContext.remove('matteripv6address');
578
452
  }
579
453
  }
580
- // Initialize the virtual mode
581
454
  if (hasParameter('novirtual')) {
582
455
  this.virtualMode = 'disabled';
583
456
  await this.nodeContext.set('virtualmode', 'disabled');
@@ -586,15 +459,10 @@ export class Matterbridge extends EventEmitter {
586
459
  this.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
587
460
  }
588
461
  this.log.debug(`Virtual mode ${this.virtualMode}.`);
589
- // Initialize PluginManager
590
462
  this.plugins.logLevel = this.log.logLevel;
591
463
  await this.plugins.loadFromStorage();
592
- // Initialize DeviceManager
593
464
  this.devices.logLevel = this.log.logLevel;
594
- // Get the plugins from node storage and create the plugins node storage contexts
595
465
  for (const plugin of this.plugins) {
596
- // Try to reinstall the plugin from npm (for Docker pull and external plugins)
597
- // We don't do this when the add and other shutdown parameters are set because we shut down the process after adding the plugin
598
466
  if (!fs.existsSync(plugin.path) && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
599
467
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm...`);
600
468
  const { spawnCommand } = await import('./utils/spawn.js');
@@ -624,7 +492,6 @@ export class Matterbridge extends EventEmitter {
624
492
  await plugin.nodeContext.set('description', plugin.description);
625
493
  await plugin.nodeContext.set('author', plugin.author);
626
494
  }
627
- // Log system info and create .matterbridge directory
628
495
  await this.logNodeAndSystemInfo();
629
496
  this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
630
497
  `${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
@@ -632,7 +499,6 @@ export class Matterbridge extends EventEmitter {
632
499
  `${hasParameter('controller') ? 'mode controller ' : ''}` +
633
500
  `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
634
501
  `running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
635
- // Check node version and throw error
636
502
  const minNodeVersion = 20;
637
503
  const nodeVersion = process.versions.node;
638
504
  const versionMajor = parseInt(nodeVersion.split('.')[0]);
@@ -640,18 +506,10 @@ export class Matterbridge extends EventEmitter {
640
506
  this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
641
507
  throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
642
508
  }
643
- // Parse command line
644
509
  await this.parseCommandLine();
645
- // Emit the initialize_completed event
646
510
  this.emit('initialize_completed');
647
511
  this.initialized = true;
648
512
  }
649
- /**
650
- * Parses the command line arguments and performs the corresponding actions.
651
- *
652
- * @private
653
- * @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
654
- */
655
513
  async parseCommandLine() {
656
514
  if (hasParameter('list')) {
657
515
  this.log.info(`│ Registered plugins (${this.plugins.length})`);
@@ -667,19 +525,6 @@ export class Matterbridge extends EventEmitter {
667
525
  }
668
526
  index++;
669
527
  }
670
- /*
671
- const serializedRegisteredDevices = await this.nodeContext?.get<SerializedMatterbridgeEndpoint[]>('devices', []);
672
- this.log.info(`│ Registered devices (${serializedRegisteredDevices?.length})`);
673
- serializedRegisteredDevices?.forEach((device, index) => {
674
- if (index !== serializedRegisteredDevices.length - 1) {
675
- this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
676
- this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
677
- } else {
678
- this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
679
- this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
680
- }
681
- });
682
- */
683
528
  this.shutdown = true;
684
529
  return;
685
530
  }
@@ -729,10 +574,8 @@ export class Matterbridge extends EventEmitter {
729
574
  this.shutdown = true;
730
575
  return;
731
576
  }
732
- // Initialize frontend
733
577
  if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
734
578
  await this.frontend.start(getIntParameter('frontend'));
735
- // Start the matter storage and create the matterbridge context
736
579
  try {
737
580
  await this.startMatterStorage();
738
581
  if (this.aggregatorSerialNumber && this.aggregatorUniqueId && this.matterStorageService) {
@@ -746,21 +589,18 @@ export class Matterbridge extends EventEmitter {
746
589
  this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
747
590
  throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
748
591
  }
749
- // Clear the matterbridge context if the reset parameter is set (bridge mode)
750
592
  if (hasParameter('reset') && getParameter('reset') === undefined) {
751
593
  this.initialized = true;
752
594
  await this.shutdownProcessAndReset();
753
595
  this.shutdown = true;
754
596
  return;
755
597
  }
756
- // Clear matterbridge plugin context if the reset parameter is set (childbridge mode)
757
598
  if (hasParameter('reset') && getParameter('reset') !== undefined) {
758
599
  this.log.debug(`Reset plugin ${getParameter('reset')}`);
759
600
  const plugin = this.plugins.get(getParameter('reset'));
760
601
  if (plugin) {
761
602
  const matterStorageManager = await this.matterStorageService?.open(plugin.name);
762
603
  if (!matterStorageManager) {
763
- /* istanbul ignore next */
764
604
  this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
765
605
  }
766
606
  else {
@@ -779,42 +619,35 @@ export class Matterbridge extends EventEmitter {
779
619
  this.shutdown = true;
780
620
  return;
781
621
  }
782
- // Check in 30 seconds the latest and dev versions of matterbridge and the plugins
783
622
  clearTimeout(this.checkUpdateTimeout);
784
623
  this.checkUpdateTimeout = setTimeout(async () => {
785
624
  const { checkUpdates } = await import('./update.js');
786
625
  checkUpdates(this);
787
626
  }, 30 * 1000).unref();
788
- // Check each 12 hours the latest and dev versions of matterbridge and the plugins
789
627
  clearInterval(this.checkUpdateInterval);
790
628
  this.checkUpdateInterval = setInterval(async () => {
791
629
  const { checkUpdates } = await import('./update.js');
792
630
  checkUpdates(this);
793
631
  }, 12 * 60 * 60 * 1000).unref();
794
- // Start the matterbridge in mode test
795
632
  if (hasParameter('test')) {
796
633
  this.bridgeMode = 'bridge';
797
634
  return;
798
635
  }
799
- // Start the matterbridge in mode controller
800
636
  if (hasParameter('controller')) {
801
637
  this.bridgeMode = 'controller';
802
638
  await this.startController();
803
639
  return;
804
640
  }
805
- // Check if the bridge mode is set and start matterbridge in bridge mode if not set
806
641
  if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
807
642
  this.log.info('Setting default matterbridge start mode to bridge');
808
643
  await this.nodeContext?.set('bridgeMode', 'bridge');
809
644
  }
810
- // Start matterbridge in bridge mode
811
645
  if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
812
646
  this.bridgeMode = 'bridge';
813
647
  this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
814
648
  await this.startBridge();
815
649
  return;
816
650
  }
817
- // Start matterbridge in childbridge mode
818
651
  if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
819
652
  this.bridgeMode = 'childbridge';
820
653
  this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
@@ -822,22 +655,10 @@ export class Matterbridge extends EventEmitter {
822
655
  return;
823
656
  }
824
657
  }
825
- /**
826
- * Asynchronously loads and starts the registered plugins.
827
- *
828
- * This method is responsible for initializing and starting all enabled plugins.
829
- * It ensures that each plugin is properly loaded and started before the bridge starts.
830
- *
831
- * @param {boolean} [wait] - If true, the method will wait for all plugins to be fully loaded and started before resolving. Defaults to false.
832
- * @param {boolean} [start] - If true, the method will start the plugins after loading them. Defaults to true.
833
- * @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
834
- */
835
658
  async startPlugins(wait = false, start = true) {
836
- // Check, load and start the plugins
837
659
  for (const plugin of this.plugins) {
838
660
  plugin.configJson = await this.plugins.loadConfig(plugin);
839
661
  plugin.schemaJson = await this.plugins.loadSchema(plugin);
840
- // Check if the plugin is available
841
662
  if (!(await this.plugins.resolve(plugin.path))) {
842
663
  this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
843
664
  plugin.enabled = false;
@@ -857,16 +678,10 @@ export class Matterbridge extends EventEmitter {
857
678
  if (wait)
858
679
  await this.plugins.load(plugin, start, 'Matterbridge is starting');
859
680
  else
860
- this.plugins.load(plugin, start, 'Matterbridge is starting'); // No await do it asyncronously
681
+ this.plugins.load(plugin, start, 'Matterbridge is starting');
861
682
  }
862
683
  this.frontend.wssSendRefreshRequired('plugins');
863
684
  }
864
- /**
865
- * Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
866
- * - When an uncaught exception occurs, the exceptionHandler logs the error message and stack trace.
867
- * - When an unhandled promise rejection occurs, the rejectionHandler logs the reason and stack trace.
868
- * - When either of SIGINT and SIGTERM signals are received, the cleanup method is called with an appropriate message.
869
- */
870
685
  registerProcessHandlers() {
871
686
  this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
872
687
  process.removeAllListeners('uncaughtException');
@@ -893,9 +708,6 @@ export class Matterbridge extends EventEmitter {
893
708
  };
894
709
  process.on('SIGTERM', this.sigtermHandler);
895
710
  }
896
- /**
897
- * Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
898
- */
899
711
  deregisterProcessHandlers() {
900
712
  this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
901
713
  if (this.exceptionHandler)
@@ -912,18 +724,13 @@ export class Matterbridge extends EventEmitter {
912
724
  process.off('SIGTERM', this.sigtermHandler);
913
725
  this.sigtermHandler = undefined;
914
726
  }
915
- /**
916
- * Logs the node and system information.
917
- */
918
727
  async logNodeAndSystemInfo() {
919
- // IP address information
920
728
  const networkInterfaces = os.networkInterfaces();
921
729
  this.systemInformation.interfaceName = '';
922
730
  this.systemInformation.ipv4Address = '';
923
731
  this.systemInformation.ipv6Address = '';
924
732
  this.systemInformation.macAddress = '';
925
733
  for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
926
- // this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
927
734
  if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
928
735
  continue;
929
736
  if (!interfaceDetails) {
@@ -949,18 +756,16 @@ export class Matterbridge extends EventEmitter {
949
756
  break;
950
757
  }
951
758
  }
952
- // Node information
953
759
  this.systemInformation.nodeVersion = process.versions.node;
954
760
  const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
955
761
  const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
956
762
  const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
957
- // Host system information
958
763
  this.systemInformation.hostname = os.hostname();
959
764
  this.systemInformation.user = os.userInfo().username;
960
- this.systemInformation.osType = os.type(); // "Windows_NT", "Darwin", etc.
961
- this.systemInformation.osRelease = os.release(); // Kernel version
962
- this.systemInformation.osPlatform = os.platform(); // "win32", "linux", "darwin", etc.
963
- this.systemInformation.osArch = os.arch(); // "x64", "arm", etc.
765
+ this.systemInformation.osType = os.type();
766
+ this.systemInformation.osRelease = os.release();
767
+ this.systemInformation.osPlatform = os.platform();
768
+ this.systemInformation.osArch = os.arch();
964
769
  this.systemInformation.totalMemory = formatBytes(os.totalmem());
965
770
  this.systemInformation.freeMemory = formatBytes(os.freemem());
966
771
  this.systemInformation.systemUptime = formatUptime(os.uptime());
@@ -970,7 +775,6 @@ export class Matterbridge extends EventEmitter {
970
775
  this.systemInformation.rss = formatBytes(process.memoryUsage().rss);
971
776
  this.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
972
777
  this.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
973
- // Log the system information
974
778
  this.log.debug('Host System Information:');
975
779
  this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
976
780
  this.log.debug(`- User: ${this.systemInformation.user}`);
@@ -990,17 +794,14 @@ export class Matterbridge extends EventEmitter {
990
794
  this.log.debug(`- RSS: ${this.systemInformation.rss}`);
991
795
  this.log.debug(`- Heap Total: ${this.systemInformation.heapTotal}`);
992
796
  this.log.debug(`- Heap Used: ${this.systemInformation.heapUsed}`);
993
- // Log directories
994
797
  this.log.debug(`Root Directory: ${this.rootDirectory}`);
995
798
  this.log.debug(`Home Directory: ${this.homeDirectory}`);
996
799
  this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
997
800
  this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
998
801
  this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
999
- // Global node_modules directory
1000
802
  if (this.nodeContext)
1001
803
  this.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
1002
804
  if (this.globalModulesDirectory === '') {
1003
- // First run of Matterbridge so the node storage is empty
1004
805
  this.log.debug(`Getting global node_modules directory...`);
1005
806
  try {
1006
807
  const { getGlobalNodeModules } = await import('./utils/network.js');
@@ -1013,7 +814,6 @@ export class Matterbridge extends EventEmitter {
1013
814
  }
1014
815
  }
1015
816
  else {
1016
- // The global node_modules directory is already set in the node storage and we check if it is still valid
1017
817
  this.log.debug(`Checking global node_modules directory: ${this.globalModulesDirectory}`);
1018
818
  try {
1019
819
  const { getGlobalNodeModules } = await import('./utils/network.js');
@@ -1025,37 +825,25 @@ export class Matterbridge extends EventEmitter {
1025
825
  this.log.error(`Error checking global node_modules directory: ${error}`);
1026
826
  }
1027
827
  }
1028
- // Matterbridge version
1029
828
  this.log.debug(`Reading matterbridge package.json...`);
1030
829
  const packageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
1031
830
  this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
1032
831
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
1033
- // Matterbridge latest version (will be set in the checkUpdate function)
1034
832
  if (this.nodeContext)
1035
833
  this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
1036
834
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
1037
- // Matterbridge dev version (will be set in the checkUpdate function)
1038
835
  if (this.nodeContext)
1039
836
  this.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
1040
837
  this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
1041
- // Frontend version
1042
838
  this.log.debug(`Reading frontend package.json...`);
1043
839
  const frontendPackageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'frontend/package.json'), 'utf8'));
1044
840
  this.frontendVersion = frontendPackageJson.version;
1045
841
  this.log.debug(`Frontend version ${CYAN}${this.frontendVersion}${db}`);
1046
- // Current working directory
1047
842
  const currentDir = process.cwd();
1048
843
  this.log.debug(`Current Working Directory: ${currentDir}`);
1049
- // Command line arguments (excluding 'node' and the script name)
1050
844
  const cmdArgs = process.argv.slice(2).join(' ');
1051
845
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
1052
846
  }
1053
- /**
1054
- * Set the logger logLevel for the Matterbridge classes and call onChangeLoggerLevel() for each plugin.
1055
- *
1056
- * @param {LogLevel} logLevel The logger logLevel to set.
1057
- * @returns {Promise<LogLevel>} A promise that resolves when the logLevel has been set.
1058
- */
1059
847
  async setLogLevel(logLevel) {
1060
848
  this.logLevel = logLevel;
1061
849
  this.log.logLevel = logLevel;
@@ -1066,87 +854,58 @@ export class Matterbridge extends EventEmitter {
1066
854
  for (const plugin of this.plugins) {
1067
855
  if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
1068
856
  continue;
1069
- plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : logLevel;
1070
- await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : logLevel);
1071
- }
1072
- // Set the global logger callback for the WebSocketServer to the common minimum logLevel
1073
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
1074
- if (logLevel === "info" /* LogLevel.INFO */ || Logger.level === MatterLogLevel.INFO)
1075
- callbackLogLevel = "info" /* LogLevel.INFO */;
1076
- if (logLevel === "debug" /* LogLevel.DEBUG */ || Logger.level === MatterLogLevel.DEBUG)
1077
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
857
+ plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : logLevel;
858
+ await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : logLevel);
859
+ }
860
+ let callbackLogLevel = "notice";
861
+ if (logLevel === "info" || Logger.level === MatterLogLevel.INFO)
862
+ callbackLogLevel = "info";
863
+ if (logLevel === "debug" || Logger.level === MatterLogLevel.DEBUG)
864
+ callbackLogLevel = "debug";
1078
865
  AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
1079
866
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
1080
867
  return logLevel;
1081
868
  }
1082
- /**
1083
- * Get the current logger logLevel.
1084
- *
1085
- * @returns {LogLevel} The current logger logLevel.
1086
- */
1087
869
  getLogLevel() {
1088
870
  return this.log.logLevel;
1089
871
  }
1090
- /**
1091
- * Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
1092
- * It also logs to file (matter.log) if fileLogger is true.
1093
- *
1094
- * @param {boolean} fileLogger - Whether to log to file or not.
1095
- * @returns {Function} The MatterLogger function. \x1b[35m for violet \x1b[34m is blue
1096
- */
1097
872
  createDestinationMatterLogger(fileLogger) {
1098
- this.matterLog.logNameColor = '\x1b[34m'; // Blue matter.js Logger
873
+ this.matterLog.logNameColor = '\x1b[34m';
1099
874
  if (fileLogger) {
1100
875
  this.matterLog.logFilePath = path.join(this.matterbridgeDirectory, MATTER_LOGGER_FILE);
1101
876
  }
1102
877
  return (text, message) => {
1103
- // 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
1104
878
  const logger = text.slice(44, 44 + 20).trim();
1105
879
  const msg = text.slice(65);
1106
880
  this.matterLog.logName = logger;
1107
881
  switch (message.level) {
1108
882
  case MatterLogLevel.DEBUG:
1109
- this.matterLog.log("debug" /* LogLevel.DEBUG */, msg);
883
+ this.matterLog.log("debug", msg);
1110
884
  break;
1111
885
  case MatterLogLevel.INFO:
1112
- this.matterLog.log("info" /* LogLevel.INFO */, msg);
886
+ this.matterLog.log("info", msg);
1113
887
  break;
1114
888
  case MatterLogLevel.NOTICE:
1115
- this.matterLog.log("notice" /* LogLevel.NOTICE */, msg);
889
+ this.matterLog.log("notice", msg);
1116
890
  break;
1117
891
  case MatterLogLevel.WARN:
1118
- this.matterLog.log("warn" /* LogLevel.WARN */, msg);
892
+ this.matterLog.log("warn", msg);
1119
893
  break;
1120
894
  case MatterLogLevel.ERROR:
1121
- this.matterLog.log("error" /* LogLevel.ERROR */, msg);
895
+ this.matterLog.log("error", msg);
1122
896
  break;
1123
897
  case MatterLogLevel.FATAL:
1124
- this.matterLog.log("fatal" /* LogLevel.FATAL */, msg);
898
+ this.matterLog.log("fatal", msg);
1125
899
  break;
1126
900
  }
1127
901
  };
1128
902
  }
1129
- /**
1130
- * Restarts the process by exiting the current instance and loading a new instance (/api/restart).
1131
- *
1132
- * @returns {Promise<void>} A promise that resolves when the restart is completed.
1133
- */
1134
903
  async restartProcess() {
1135
904
  await this.cleanup('restarting...', true);
1136
905
  }
1137
- /**
1138
- * Shut down the process (/api/shutdown).
1139
- *
1140
- * @returns {Promise<void>} A promise that resolves when the shutdown is completed.
1141
- */
1142
906
  async shutdownProcess() {
1143
907
  await this.cleanup('shutting down...', false);
1144
908
  }
1145
- /**
1146
- * Update matterbridge and shut down the process (virtual device 'Update Matterbridge').
1147
- *
1148
- * @returns {Promise<void>} A promise that resolves when the update is completed.
1149
- */
1150
909
  async updateProcess() {
1151
910
  this.log.info('Updating matterbridge...');
1152
911
  const { spawnCommand } = await import('./utils/spawn.js');
@@ -1159,13 +918,6 @@ export class Matterbridge extends EventEmitter {
1159
918
  this.frontend.wssSendRestartRequired();
1160
919
  await this.cleanup('updating...', false);
1161
920
  }
1162
- /**
1163
- * Unregister all devices and shut down the process (/api/unregister).
1164
- *
1165
- * @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
1166
- *
1167
- * @returns {Promise<void>} A promise that resolves when the cleanup is completed.
1168
- */
1169
921
  async unregisterAndShutdownProcess(timeout = 1000) {
1170
922
  this.log.info('Unregistering all devices and shutting down...');
1171
923
  for (const plugin of this.plugins.array()) {
@@ -1177,71 +929,46 @@ export class Matterbridge extends EventEmitter {
1177
929
  await this.removeAllBridgedEndpoints(plugin.name, 100);
1178
930
  }
1179
931
  this.log.debug('Waiting for the MessageExchange to finish...');
1180
- await wait(timeout); // Wait for MessageExchange to finish
932
+ await wait(timeout);
1181
933
  this.log.debug('Cleaning up and shutting down...');
1182
934
  await this.cleanup('unregistered all devices and shutting down...', false, timeout);
1183
935
  }
1184
- /**
1185
- * Reset commissioning and shut down the process (/api/reset).
1186
- *
1187
- * @returns {Promise<void>} A promise that resolves when the cleanup is completed.
1188
- */
1189
936
  async shutdownProcessAndReset() {
1190
937
  await this.cleanup('shutting down with reset...', false);
1191
938
  }
1192
- /**
1193
- * Factory reset and shut down the process (/api/factory-reset).
1194
- *
1195
- * @returns {Promise<void>} A promise that resolves when the cleanup is completed.
1196
- */
1197
939
  async shutdownProcessAndFactoryReset() {
1198
940
  await this.cleanup('shutting down with factory reset...', false);
1199
941
  }
1200
- /**
1201
- * Cleans up the Matterbridge instance.
1202
- *
1203
- * @param {string} message - The cleanup message.
1204
- * @param {boolean} [restart] - Indicates whether to restart the instance after cleanup. Default is `false`.
1205
- * @param {number} [pause] - The pause in ms to wait for the message exchange to complete in milliseconds. Default is 1000.
1206
- *
1207
- * @returns {Promise<void>} A promise that resolves when the cleanup is completed.
1208
- */
1209
942
  async cleanup(message, restart = false, pause = 1000) {
1210
943
  if (this.initialized && !this.hasCleanupStarted) {
1211
944
  this.emit('cleanup_started');
1212
945
  this.hasCleanupStarted = true;
1213
946
  this.log.info(message);
1214
- // Clear the start matter interval
1215
947
  if (this.startMatterInterval) {
1216
948
  clearInterval(this.startMatterInterval);
1217
949
  this.startMatterInterval = undefined;
1218
950
  this.log.debug('Start matter interval cleared');
1219
951
  }
1220
- // Clear the check update timeout
1221
952
  if (this.checkUpdateTimeout) {
1222
953
  clearTimeout(this.checkUpdateTimeout);
1223
954
  this.checkUpdateTimeout = undefined;
1224
955
  this.log.debug('Check update timeout cleared');
1225
956
  }
1226
- // Clear the check update interval
1227
957
  if (this.checkUpdateInterval) {
1228
958
  clearInterval(this.checkUpdateInterval);
1229
959
  this.checkUpdateInterval = undefined;
1230
960
  this.log.debug('Check update interval cleared');
1231
961
  }
1232
- // Clear the configure timeout
1233
962
  if (this.configureTimeout) {
1234
963
  clearTimeout(this.configureTimeout);
1235
964
  this.configureTimeout = undefined;
1236
965
  this.log.debug('Matterbridge configure timeout cleared');
1237
966
  }
1238
- // Clear the reachability timeout
1239
967
  if (this.reachabilityTimeout) {
1240
968
  clearTimeout(this.reachabilityTimeout);
1241
969
  this.reachabilityTimeout = undefined;
1242
970
  this.log.debug('Matterbridge reachability timeout cleared');
1243
971
  }
1244
- // Call the shutdown method of each plugin and clear the plugins reachability timeout
1245
972
  for (const plugin of this.plugins) {
1246
973
  if (!plugin.enabled || plugin.error)
1247
974
  continue;
@@ -1252,7 +979,6 @@ export class Matterbridge extends EventEmitter {
1252
979
  this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
1253
980
  }
1254
981
  }
1255
- // Stop matter server nodes
1256
982
  this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
1257
983
  if (pause > 0) {
1258
984
  this.log.debug(`Waiting ${pause}ms for the MessageExchange to finish...`);
@@ -1279,7 +1005,6 @@ export class Matterbridge extends EventEmitter {
1279
1005
  }
1280
1006
  }
1281
1007
  this.log.notice('Stopped matter server nodes');
1282
- // Matter commisioning reset
1283
1008
  if (message === 'shutting down with reset...') {
1284
1009
  this.log.info('Resetting Matterbridge commissioning information...');
1285
1010
  await this.matterStorageManager?.createContext('events')?.clearAll();
@@ -1289,7 +1014,6 @@ export class Matterbridge extends EventEmitter {
1289
1014
  await this.matterbridgeContext?.clearAll();
1290
1015
  this.log.info('Matter storage reset done! Remove the bridge from the controller.');
1291
1016
  }
1292
- // Unregister all devices
1293
1017
  if (message === 'unregistered all devices and shutting down...') {
1294
1018
  if (this.bridgeMode === 'bridge') {
1295
1019
  await this.matterStorageManager?.createContext('root')?.createContext('parts')?.createContext('Matterbridge')?.createContext('parts')?.clearAll();
@@ -1307,35 +1031,16 @@ export class Matterbridge extends EventEmitter {
1307
1031
  }
1308
1032
  this.log.info('Matter storage reset done!');
1309
1033
  }
1310
- // Stop matter storage
1311
1034
  await this.stopMatterStorage();
1312
- // Stop the frontend
1313
1035
  await this.frontend.stop();
1314
1036
  this.frontend.destroy();
1315
- // Close PluginManager and DeviceManager
1316
1037
  this.plugins.destroy();
1317
1038
  this.devices.destroy();
1318
- // Stop thread messaging server
1319
1039
  this.server.close();
1320
- // Close the matterbridge node storage and context
1321
1040
  if (this.nodeStorage && this.nodeContext) {
1322
- /*
1323
- TODO: Implement serialization of registered devices
1324
- this.log.info('Saving registered devices...');
1325
- const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
1326
- this.devices.forEach(async (device) => {
1327
- const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
1328
- this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
1329
- if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
1330
- });
1331
- await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
1332
- this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
1333
- */
1334
- // Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
1335
1041
  this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
1336
1042
  await this.nodeContext.close();
1337
1043
  this.nodeContext = undefined;
1338
- // Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
1339
1044
  for (const plugin of this.plugins) {
1340
1045
  if (plugin.nodeContext) {
1341
1046
  this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
@@ -1352,10 +1057,8 @@ export class Matterbridge extends EventEmitter {
1352
1057
  }
1353
1058
  this.plugins.clear();
1354
1059
  this.devices.clear();
1355
- // Factory reset
1356
1060
  if (message === 'shutting down with factory reset...') {
1357
1061
  try {
1358
- // Delete matter storage directory with its subdirectories and backup
1359
1062
  const dir = path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME);
1360
1063
  this.log.info(`Removing matter storage directory: ${dir}`);
1361
1064
  await fs.promises.rm(dir, { recursive: true });
@@ -1364,13 +1067,11 @@ export class Matterbridge extends EventEmitter {
1364
1067
  await fs.promises.rm(backup, { recursive: true });
1365
1068
  }
1366
1069
  catch (error) {
1367
- // istanbul ignore next if
1368
1070
  if (error instanceof Error && error.code !== 'ENOENT') {
1369
1071
  this.log.error(`Error removing matter storage directory: ${error}`);
1370
1072
  }
1371
1073
  }
1372
1074
  try {
1373
- // Delete matterbridge storage directory with its subdirectories and backup
1374
1075
  const dir = path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR);
1375
1076
  this.log.info(`Removing matterbridge storage directory: ${dir}`);
1376
1077
  await fs.promises.rm(dir, { recursive: true });
@@ -1379,20 +1080,18 @@ export class Matterbridge extends EventEmitter {
1379
1080
  await fs.promises.rm(backup, { recursive: true });
1380
1081
  }
1381
1082
  catch (error) {
1382
- // istanbul ignore next if
1383
1083
  if (error instanceof Error && error.code !== 'ENOENT') {
1384
1084
  this.log.error(`Error removing matterbridge storage directory: ${error}`);
1385
1085
  }
1386
1086
  }
1387
1087
  this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
1388
1088
  }
1389
- // Deregisters the process handlers
1390
1089
  this.deregisterProcessHandlers();
1391
1090
  if (restart) {
1392
1091
  if (message === 'updating...') {
1393
1092
  this.log.info('Cleanup completed. Updating...');
1394
1093
  Matterbridge.instance = undefined;
1395
- this.emit('update'); // Restart the process but the update has been done before. TODO move all updates to the cli
1094
+ this.emit('update');
1396
1095
  }
1397
1096
  else if (message === 'restarting...') {
1398
1097
  this.log.info('Cleanup completed. Restarting...');
@@ -1421,14 +1120,7 @@ export class Matterbridge extends EventEmitter {
1421
1120
  this.log.debug('Cleanup already started...');
1422
1121
  }
1423
1122
  }
1424
- /**
1425
- * Starts the Matterbridge in bridge mode.
1426
- *
1427
- * @private
1428
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1429
- */
1430
1123
  async startBridge() {
1431
- // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1432
1124
  if (!this.matterStorageManager)
1433
1125
  throw new Error('No storage manager initialized');
1434
1126
  if (!this.matterbridgeContext)
@@ -1467,16 +1159,13 @@ export class Matterbridge extends EventEmitter {
1467
1159
  clearInterval(this.startMatterInterval);
1468
1160
  this.startMatterInterval = undefined;
1469
1161
  this.log.debug('Cleared startMatterInterval interval in bridge mode');
1470
- // Start the Matter server node
1471
- this.startServerNode(this.serverNode); // We don't await this, because the server node is started in the background
1472
- // Start the Matter server node of single devices in mode 'server'
1162
+ this.startServerNode(this.serverNode);
1473
1163
  for (const device of this.devices.array()) {
1474
1164
  if (device.mode === 'server' && device.serverNode) {
1475
1165
  this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
1476
- this.startServerNode(device.serverNode); // We don't await this, because the server node is started in the background
1166
+ this.startServerNode(device.serverNode);
1477
1167
  }
1478
1168
  }
1479
- // Configure the plugins
1480
1169
  this.configureTimeout = setTimeout(async () => {
1481
1170
  for (const plugin of this.plugins.array()) {
1482
1171
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1494,40 +1183,28 @@ export class Matterbridge extends EventEmitter {
1494
1183
  }
1495
1184
  this.frontend.wssSendRefreshRequired('plugins');
1496
1185
  }, 30 * 1000).unref();
1497
- // Setting reachability to true
1498
1186
  this.reachabilityTimeout = setTimeout(() => {
1499
1187
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1500
1188
  if (this.aggregatorNode)
1501
1189
  this.setAggregatorReachability(this.aggregatorNode, true);
1502
1190
  }, 60 * 1000).unref();
1503
- // Logger.get('LogServerNode').info(this.serverNode);
1504
1191
  this.emit('bridge_started');
1505
1192
  this.log.notice('Matterbridge bridge started successfully');
1506
1193
  this.frontend.wssSendRefreshRequired('settings');
1507
1194
  this.frontend.wssSendRefreshRequired('plugins');
1508
1195
  }, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
1509
1196
  }
1510
- /**
1511
- * Starts the Matterbridge in childbridge mode.
1512
- *
1513
- * @param {number} [delay] - The delay before starting the childbridge. Default is 1000 milliseconds.
1514
- *
1515
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1516
- */
1517
1197
  async startChildbridge(delay = 1000) {
1518
1198
  if (!this.matterStorageManager)
1519
1199
  throw new Error('No storage manager initialized');
1520
- // Load with await all plugins but don't start them. We get the platform.type to pre-create server nodes for DynamicPlatform plugins
1521
1200
  this.log.debug('Loading all plugins in childbridge mode...');
1522
1201
  await this.startPlugins(true, false);
1523
- // Create server nodes for DynamicPlatform plugins and start all plugins in the background
1524
1202
  this.log.debug('Creating server nodes for DynamicPlatform plugins and starting all plugins in childbridge mode...');
1525
1203
  for (const plugin of this.plugins.array().filter((p) => p.enabled && !p.error)) {
1526
1204
  if (plugin.type === 'DynamicPlatform')
1527
1205
  await this.createDynamicPlugin(plugin);
1528
- this.plugins.start(plugin, 'Matterbridge is starting'); // Start the plugin in the background
1206
+ this.plugins.start(plugin, 'Matterbridge is starting');
1529
1207
  }
1530
- // Start the Matterbridge in childbridge mode when all plugins are loaded and started
1531
1208
  this.log.debug('Starting start matter interval in childbridge mode...');
1532
1209
  let failCount = 0;
1533
1210
  this.startMatterInterval = setInterval(async () => {
@@ -1561,9 +1238,8 @@ export class Matterbridge extends EventEmitter {
1561
1238
  clearInterval(this.startMatterInterval);
1562
1239
  this.startMatterInterval = undefined;
1563
1240
  if (delay > 0)
1564
- await wait(Number(process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS']) || delay); // Wait for the specified delay to ensure all plugins server nodes are ready
1241
+ await wait(Number(process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS']) || delay);
1565
1242
  this.log.debug('Cleared startMatterInterval interval in childbridge mode');
1566
- // Configure the plugins
1567
1243
  this.configureTimeout = setTimeout(async () => {
1568
1244
  for (const plugin of this.plugins.array()) {
1569
1245
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1588,7 +1264,6 @@ export class Matterbridge extends EventEmitter {
1588
1264
  this.log.error(`Plugin ${plg}${plugin.name}${er} didn't register any devices to Matterbridge. Verify the plugin configuration.`);
1589
1265
  continue;
1590
1266
  }
1591
- // istanbul ignore next if cause is just a safety check
1592
1267
  if (!plugin.serverNode) {
1593
1268
  this.log.error(`Server node not found for plugin ${plg}${plugin.name}${er}`);
1594
1269
  continue;
@@ -1601,252 +1276,28 @@ export class Matterbridge extends EventEmitter {
1601
1276
  this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
1602
1277
  continue;
1603
1278
  }
1604
- // Start the Matter server node
1605
- this.startServerNode(plugin.serverNode); // We don't await this, because the server node is started in the background
1606
- // Setting reachability to true
1279
+ this.startServerNode(plugin.serverNode);
1607
1280
  plugin.reachabilityTimeout = setTimeout(() => {
1608
1281
  this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf}`);
1609
1282
  if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
1610
1283
  this.setAggregatorReachability(plugin.aggregatorNode, true);
1611
1284
  }, 60 * 1000).unref();
1612
1285
  }
1613
- // Start the Matter server node of single devices in mode 'server'
1614
1286
  for (const device of this.devices.array()) {
1615
1287
  if (device.mode === 'server' && device.serverNode) {
1616
1288
  this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
1617
- this.startServerNode(device.serverNode); // We don't await this, because the server node is started in the background
1289
+ this.startServerNode(device.serverNode);
1618
1290
  }
1619
1291
  }
1620
- // Logger.get('LogServerNode').info(this.serverNode);
1621
1292
  this.emit('childbridge_started');
1622
1293
  this.log.notice('Matterbridge childbridge started successfully');
1623
1294
  this.frontend.wssSendRefreshRequired('settings');
1624
1295
  this.frontend.wssSendRefreshRequired('plugins');
1625
1296
  }, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
1626
1297
  }
1627
- /**
1628
- * Starts the Matterbridge controller.
1629
- *
1630
- * @private
1631
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1632
- */
1633
1298
  async startController() {
1634
- /*
1635
- if (!this.matterStorageManager) {
1636
- this.log.error('No storage manager initialized');
1637
- await this.cleanup('No storage manager initialized');
1638
- return;
1639
- }
1640
- this.log.info('Creating context: mattercontrollerContext');
1641
- this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
1642
- if (!this.controllerContext) {
1643
- this.log.error('No storage context mattercontrollerContext initialized');
1644
- await this.cleanup('No storage context mattercontrollerContext initialized');
1645
- return;
1646
- }
1647
-
1648
- this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1649
- this.matterServer = await this.createMatterServer(this.storageManager);
1650
- this.log.info('Creating matter commissioning controller');
1651
- this.commissioningController = new CommissioningController({
1652
- autoConnect: false,
1653
- });
1654
- this.log.info('Adding matter commissioning controller to matter server');
1655
- await this.matterServer.addCommissioningController(this.commissioningController);
1656
-
1657
- this.log.info('Starting matter server');
1658
- await this.matterServer.start();
1659
- this.log.info('Matter server started');
1660
- const commissioningOptions: ControllerCommissioningFlowOptions = {
1661
- regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
1662
- regulatoryCountryCode: 'XX',
1663
- };
1664
- const commissioningController = new CommissioningController({
1665
- environment: {
1666
- environment,
1667
- id: uniqueId,
1668
- },
1669
- autoConnect: false, // Do not auto connect to the commissioned nodes
1670
- adminFabricLabel,
1671
- });
1672
-
1673
- if (hasParameter('pairingcode')) {
1674
- this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
1675
- const pairingCode = getParameter('pairingcode');
1676
- const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
1677
- const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
1678
-
1679
- let longDiscriminator, setupPin, shortDiscriminator;
1680
- if (pairingCode !== undefined) {
1681
- const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
1682
- shortDiscriminator = pairingCodeCodec.shortDiscriminator;
1683
- longDiscriminator = undefined;
1684
- setupPin = pairingCodeCodec.passcode;
1685
- this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
1686
- } else {
1687
- longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
1688
- if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
1689
- setupPin = this.controllerContext.get('pin', 20202021);
1690
- }
1691
- if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
1692
- throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
1693
- }
1694
-
1695
- const options = {
1696
- commissioning: commissioningOptions,
1697
- discovery: {
1698
- knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
1699
- identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
1700
- },
1701
- passcode: setupPin,
1702
- } as NodeCommissioningOptions;
1703
- this.log.info('Commissioning with options:', options);
1704
- const nodeId = await this.commissioningController.commissionNode(options);
1705
- this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
1706
- this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
1707
- } // (hasParameter('pairingcode'))
1708
-
1709
- if (hasParameter('unpairall')) {
1710
- this.log.info('***Commissioning controller unpairing all nodes...');
1711
- const nodeIds = this.commissioningController.getCommissionedNodes();
1712
- for (const nodeId of nodeIds) {
1713
- this.log.info('***Commissioning controller unpairing node:', nodeId);
1714
- await this.commissioningController.removeNode(nodeId);
1715
- }
1716
- return;
1717
- }
1718
-
1719
- if (hasParameter('discover')) {
1720
- // const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1721
- // console.log(discover);
1722
- }
1723
-
1724
- if (!this.commissioningController.isCommissioned()) {
1725
- this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
1726
- return;
1727
- }
1728
-
1729
- const nodeIds = this.commissioningController.getCommissionedNodes();
1730
- this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
1731
- for (const nodeId of nodeIds) {
1732
- this.log.info(`***Connecting to commissioned node: ${nodeId}`);
1733
-
1734
- const node = await this.commissioningController.connectNode(nodeId, {
1735
- autoSubscribe: false,
1736
- attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
1737
- this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
1738
- eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
1739
- this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
1740
- stateInformationCallback: (peerNodeId, info) => {
1741
- switch (info) {
1742
- case NodeStateInformation.Connected:
1743
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
1744
- break;
1745
- case NodeStateInformation.Disconnected:
1746
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
1747
- break;
1748
- case NodeStateInformation.Reconnecting:
1749
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
1750
- break;
1751
- case NodeStateInformation.WaitingForDeviceDiscovery:
1752
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
1753
- break;
1754
- case NodeStateInformation.StructureChanged:
1755
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
1756
- break;
1757
- case NodeStateInformation.Decommissioned:
1758
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
1759
- break;
1760
- default:
1761
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
1762
- break;
1763
- }
1764
- },
1765
- });
1766
-
1767
- node.logStructure();
1768
-
1769
- // Get the interaction client
1770
- this.log.info('Getting the interaction client');
1771
- const interactionClient = await node.getInteractionClient();
1772
- let cluster;
1773
- let attributes;
1774
-
1775
- // Log BasicInformationCluster
1776
- cluster = BasicInformationCluster;
1777
- attributes = await interactionClient.getMultipleAttributes({
1778
- attributes: [{ clusterId: cluster.id }],
1779
- });
1780
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1781
- attributes.forEach((attribute) => {
1782
- this.log.info(
1783
- `- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
1784
- );
1785
- });
1786
-
1787
- // Log PowerSourceCluster
1788
- cluster = PowerSourceCluster;
1789
- attributes = await interactionClient.getMultipleAttributes({
1790
- attributes: [{ clusterId: cluster.id }],
1791
- });
1792
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1793
- attributes.forEach((attribute) => {
1794
- this.log.info(
1795
- `- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
1796
- );
1797
- });
1798
-
1799
- // Log ThreadNetworkDiagnostics
1800
- cluster = ThreadNetworkDiagnosticsCluster;
1801
- attributes = await interactionClient.getMultipleAttributes({
1802
- attributes: [{ clusterId: cluster.id }],
1803
- });
1804
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1805
- attributes.forEach((attribute) => {
1806
- this.log.info(
1807
- `- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
1808
- );
1809
- });
1810
-
1811
- // Log SwitchCluster
1812
- cluster = SwitchCluster;
1813
- attributes = await interactionClient.getMultipleAttributes({
1814
- attributes: [{ clusterId: cluster.id }],
1815
- });
1816
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1817
- attributes.forEach((attribute) => {
1818
- this.log.info(
1819
- `- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
1820
- );
1821
- });
1822
-
1823
- this.log.info('Subscribing to all attributes and events');
1824
- await node.subscribeAllAttributesAndEvents({
1825
- ignoreInitialTriggers: false,
1826
- attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
1827
- this.log.info(
1828
- `***${db}Commissioning controller attributeChangedCallback version ${version}: attribute ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${attributeName}${db} changed to ${typeof value === 'object' ? debugStringify(value ?? { none: true }) : value}`,
1829
- ),
1830
- eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
1831
- this.log.info(
1832
- `***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
1833
- );
1834
- },
1835
- });
1836
- this.log.info('Subscribed to all attributes and events');
1837
- }
1838
- */
1839
1299
  }
1840
- /** */
1841
- /** Matter.js methods */
1842
- /** */
1843
- /**
1844
- * Starts the matter storage with name Matterbridge, create the matterbridge context and performs a backup.
1845
- *
1846
- * @returns {Promise<void>} - A promise that resolves when the storage is started.
1847
- */
1848
1300
  async startMatterStorage() {
1849
- // Setup Matter storage
1850
1301
  this.log.info(`Starting matter node storage...`);
1851
1302
  this.matterStorageService = this.environment.get(StorageService);
1852
1303
  this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
@@ -1854,17 +1305,8 @@ export class Matterbridge extends EventEmitter {
1854
1305
  this.log.info('Matter node storage manager "Matterbridge" created');
1855
1306
  this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName, this.aggregatorSerialNumber, this.aggregatorUniqueId);
1856
1307
  this.log.info('Matter node storage started');
1857
- // Backup matter storage since it is created/opened correctly
1858
1308
  await this.backupMatterStorage(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME), path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME + '.backup'));
1859
1309
  }
1860
- /**
1861
- * Makes a backup copy of the specified matter storage directory.
1862
- *
1863
- * @param {string} storageName - The name of the storage directory to be backed up.
1864
- * @param {string} backupName - The name of the backup directory to be created.
1865
- * @private
1866
- * @returns {Promise<void>} A promise that resolves when the has been done.
1867
- */
1868
1310
  async backupMatterStorage(storageName, backupName) {
1869
1311
  this.log.info('Creating matter node storage backup...');
1870
1312
  try {
@@ -1875,11 +1317,6 @@ export class Matterbridge extends EventEmitter {
1875
1317
  this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
1876
1318
  }
1877
1319
  }
1878
- /**
1879
- * Stops the matter storage.
1880
- *
1881
- * @returns {Promise<void>} A promise that resolves when the storage is stopped.
1882
- */
1883
1320
  async stopMatterStorage() {
1884
1321
  this.log.info('Closing matter node storage...');
1885
1322
  await this.matterStorageManager?.close();
@@ -1888,20 +1325,6 @@ export class Matterbridge extends EventEmitter {
1888
1325
  this.matterbridgeContext = undefined;
1889
1326
  this.log.info('Matter node storage closed');
1890
1327
  }
1891
- /**
1892
- * Creates a server node storage context.
1893
- *
1894
- * @param {string} storeId - The storeId.
1895
- * @param {string} deviceName - The name of the device.
1896
- * @param {DeviceTypeId} deviceType - The device type of the device.
1897
- * @param {number} vendorId - The vendor ID.
1898
- * @param {string} vendorName - The vendor name.
1899
- * @param {number} productId - The product ID.
1900
- * @param {string} productName - The product name.
1901
- * @param {string} [serialNumber] - The serial number of the device (optional).
1902
- * @param {string} [uniqueId] - The unique ID of the device (optional).
1903
- * @returns {Promise<StorageContext>} The storage context for the commissioning server.
1904
- */
1905
1328
  async createServerNodeContext(storeId, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId) {
1906
1329
  const { randomBytes } = await import('node:crypto');
1907
1330
  if (!this.matterStorageService)
@@ -1941,15 +1364,6 @@ export class Matterbridge extends EventEmitter {
1941
1364
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1942
1365
  return storageContext;
1943
1366
  }
1944
- /**
1945
- * Creates a server node.
1946
- *
1947
- * @param {StorageContext} storageContext - The storage context for the server node.
1948
- * @param {number} [port] - The port number for the server node. Defaults to 5540.
1949
- * @param {number} [passcode] - The passcode for the server node. Defaults to 20242025.
1950
- * @param {number} [discriminator] - The discriminator for the server node. Defaults to 3850.
1951
- * @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
1952
- */
1953
1367
  async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
1954
1368
  const storeId = await storageContext.get('storeId');
1955
1369
  this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
@@ -1959,35 +1373,25 @@ export class Matterbridge extends EventEmitter {
1959
1373
  this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
1960
1374
  this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1961
1375
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1962
- /**
1963
- * Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
1964
- */
1965
1376
  const serverNode = await ServerNode.create({
1966
- // Required: Give the Node a unique ID which is used to store the state of this node
1967
1377
  id: storeId,
1968
- // Environment to run the server node in
1969
1378
  environment: this.environment,
1970
- // Provide Network relevant configuration like the port
1971
1379
  network: {
1972
1380
  listeningAddressIpv4: this.ipv4Address,
1973
1381
  listeningAddressIpv6: this.ipv6Address,
1974
1382
  port,
1975
1383
  },
1976
- // Provide the certificate for the device
1977
1384
  operationalCredentials: {
1978
1385
  certification: this.certification,
1979
1386
  },
1980
- // Provide Commissioning relevant settings
1981
1387
  commissioning: {
1982
1388
  passcode,
1983
1389
  discriminator,
1984
1390
  },
1985
- // Provide Node announcement settings
1986
1391
  productDescription: {
1987
1392
  name: await storageContext.get('deviceName'),
1988
1393
  deviceType: DeviceTypeId(await storageContext.get('deviceType')),
1989
1394
  },
1990
- // Provide defaults for the BasicInformation cluster on the Root endpoint
1991
1395
  basicInformation: {
1992
1396
  vendorId: VendorId(await storageContext.get('vendorId')),
1993
1397
  vendorName: await storageContext.get('vendorName'),
@@ -2004,23 +1408,17 @@ export class Matterbridge extends EventEmitter {
2004
1408
  reachable: true,
2005
1409
  },
2006
1410
  });
2007
- /**
2008
- * This event is triggered when the device is initially commissioned successfully.
2009
- * This means: It is added to the first fabric.
2010
- */
2011
1411
  serverNode.lifecycle.commissioned.on(() => {
2012
1412
  this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
2013
1413
  this.advertisingNodes.delete(storeId);
2014
1414
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
2015
1415
  });
2016
- /** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
2017
1416
  serverNode.lifecycle.decommissioned.on(() => {
2018
1417
  this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`);
2019
1418
  this.advertisingNodes.delete(storeId);
2020
1419
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
2021
1420
  this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
2022
1421
  });
2023
- /** This event is triggered when the device went online. This means that it is discoverable in the network. */
2024
1422
  serverNode.lifecycle.online.on(async () => {
2025
1423
  this.log.notice(`Server node for ${storeId} is online`);
2026
1424
  if (!serverNode.lifecycle.isCommissioned) {
@@ -2031,16 +1429,13 @@ export class Matterbridge extends EventEmitter {
2031
1429
  this.log.notice(`Manual pairing code: ${manualPairingCode}`);
2032
1430
  }
2033
1431
  else {
2034
- // istanbul ignore next
2035
1432
  this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
2036
- // istanbul ignore next
2037
1433
  this.advertisingNodes.delete(storeId);
2038
1434
  }
2039
1435
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
2040
1436
  this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
2041
1437
  this.emit('online', storeId);
2042
1438
  });
2043
- /** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
2044
1439
  serverNode.lifecycle.offline.on(() => {
2045
1440
  this.log.notice(`Server node for ${storeId} is offline`);
2046
1441
  this.advertisingNodes.delete(storeId);
@@ -2048,15 +1443,11 @@ export class Matterbridge extends EventEmitter {
2048
1443
  this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
2049
1444
  this.emit('offline', storeId);
2050
1445
  });
2051
- /**
2052
- * This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
2053
- * information is needed.
2054
- */
2055
1446
  serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
2056
1447
  let action = '';
2057
1448
  switch (fabricAction) {
2058
1449
  case FabricAction.Added:
2059
- this.advertisingNodes.delete(storeId); // The advertising stops when a fabric is added
1450
+ this.advertisingNodes.delete(storeId);
2060
1451
  action = 'added';
2061
1452
  break;
2062
1453
  case FabricAction.Removed:
@@ -2069,22 +1460,14 @@ export class Matterbridge extends EventEmitter {
2069
1460
  this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
2070
1461
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
2071
1462
  });
2072
- /**
2073
- * This event is triggered when an operative new session was opened by a Controller.
2074
- * It is not triggered for the initial commissioning process, just afterwards for real connections.
2075
- */
2076
1463
  serverNode.events.sessions.opened.on((session) => {
2077
1464
  this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
2078
1465
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
2079
1466
  });
2080
- /**
2081
- * This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
2082
- */
2083
1467
  serverNode.events.sessions.closed.on((session) => {
2084
1468
  this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
2085
1469
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
2086
1470
  });
2087
- /** This event is triggered when a subscription gets added or removed on an operative session. */
2088
1471
  serverNode.events.sessions.subscriptionsChanged.on((session) => {
2089
1472
  this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
2090
1473
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
@@ -2092,12 +1475,6 @@ export class Matterbridge extends EventEmitter {
2092
1475
  this.log.info(`Created server node for ${storeId}`);
2093
1476
  return serverNode;
2094
1477
  }
2095
- /**
2096
- * Gets the matter sanitized data of the specified server node.
2097
- *
2098
- * @param {ServerNode} [serverNode] - The server node to start.
2099
- * @returns {ApiMatter} The sanitized data of the server node.
2100
- */
2101
1478
  getServerNodeData(serverNode) {
2102
1479
  const advertiseTime = this.advertisingNodes.get(serverNode.id) || 0;
2103
1480
  return {
@@ -2114,25 +1491,12 @@ export class Matterbridge extends EventEmitter {
2114
1491
  serialNumber: serverNode.state.basicInformation.serialNumber,
2115
1492
  };
2116
1493
  }
2117
- /**
2118
- * Starts the specified server node.
2119
- *
2120
- * @param {ServerNode} [matterServerNode] - The server node to start.
2121
- * @returns {Promise<void>} A promise that resolves when the server node has started.
2122
- */
2123
1494
  async startServerNode(matterServerNode) {
2124
1495
  if (!matterServerNode)
2125
1496
  return;
2126
1497
  this.log.notice(`Starting ${matterServerNode.id} server node`);
2127
1498
  await matterServerNode.start();
2128
1499
  }
2129
- /**
2130
- * Stops the specified server node.
2131
- *
2132
- * @param {ServerNode} matterServerNode - The server node to stop.
2133
- * @param {number} [timeout] - The timeout in milliseconds for stopping the server node. Defaults to 30 seconds.
2134
- * @returns {Promise<void>} A promise that resolves when the server node has stopped.
2135
- */
2136
1500
  async stopServerNode(matterServerNode, timeout = 30000) {
2137
1501
  if (!matterServerNode)
2138
1502
  return;
@@ -2145,25 +1509,12 @@ export class Matterbridge extends EventEmitter {
2145
1509
  this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
2146
1510
  }
2147
1511
  }
2148
- /**
2149
- * Creates an aggregator node with the specified storage context.
2150
- *
2151
- * @param {StorageContext} storageContext - The storage context for the aggregator node.
2152
- * @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
2153
- */
2154
1512
  async createAggregatorNode(storageContext) {
2155
1513
  this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
2156
1514
  const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
2157
1515
  this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
2158
1516
  return aggregatorNode;
2159
1517
  }
2160
- /**
2161
- * Creates and configures the server node for an accessory plugin for a given device.
2162
- *
2163
- * @param {Plugin} plugin - The plugin to configure.
2164
- * @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
2165
- * @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
2166
- */
2167
1518
  async createAccessoryPlugin(plugin, device) {
2168
1519
  if (!plugin.locked && device.deviceType && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
2169
1520
  plugin.locked = true;
@@ -2175,12 +1526,6 @@ export class Matterbridge extends EventEmitter {
2175
1526
  await plugin.serverNode.add(device);
2176
1527
  }
2177
1528
  }
2178
- /**
2179
- * Creates and configures the server node and the aggregator node for a dynamic plugin.
2180
- *
2181
- * @param {Plugin} plugin - The plugin to configure.
2182
- * @returns {Promise<void>} A promise that resolves when the server node and the aggregator node for the dynamic plugin is created and configured.
2183
- */
2184
1529
  async createDynamicPlugin(plugin) {
2185
1530
  if (!plugin.locked) {
2186
1531
  plugin.locked = true;
@@ -2193,13 +1538,6 @@ export class Matterbridge extends EventEmitter {
2193
1538
  await plugin.serverNode.add(plugin.aggregatorNode);
2194
1539
  }
2195
1540
  }
2196
- /**
2197
- * Creates and configures the server node for a single not bridged device.
2198
- *
2199
- * @param {Plugin} plugin - The plugin to configure.
2200
- * @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
2201
- * @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
2202
- */
2203
1541
  async createDeviceServerNode(plugin, device) {
2204
1542
  if (device.mode === 'server' && !device.serverNode && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
2205
1543
  this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
@@ -2210,15 +1548,7 @@ export class Matterbridge extends EventEmitter {
2210
1548
  this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
2211
1549
  }
2212
1550
  }
2213
- /**
2214
- * Adds a MatterbridgeEndpoint to the specified plugin.
2215
- *
2216
- * @param {string} pluginName - The name of the plugin.
2217
- * @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
2218
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
2219
- */
2220
1551
  async addBridgedEndpoint(pluginName, device) {
2221
- // Check if the plugin is registered
2222
1552
  const plugin = this.plugins.get(pluginName);
2223
1553
  if (!plugin) {
2224
1554
  this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
@@ -2238,7 +1568,6 @@ export class Matterbridge extends EventEmitter {
2238
1568
  }
2239
1569
  else if (this.bridgeMode === 'bridge') {
2240
1570
  if (device.mode === 'matter') {
2241
- // Register and add the device to the matterbridge server node
2242
1571
  this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge server node...`);
2243
1572
  if (!this.serverNode) {
2244
1573
  this.log.error('Server node not found for Matterbridge');
@@ -2255,7 +1584,6 @@ export class Matterbridge extends EventEmitter {
2255
1584
  }
2256
1585
  }
2257
1586
  else {
2258
- // Register and add the device to the matterbridge aggregator node
2259
1587
  this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
2260
1588
  if (!this.aggregatorNode) {
2261
1589
  this.log.error('Aggregator node not found for Matterbridge');
@@ -2273,7 +1601,6 @@ export class Matterbridge extends EventEmitter {
2273
1601
  }
2274
1602
  }
2275
1603
  else if (this.bridgeMode === 'childbridge') {
2276
- // Register and add the device to the plugin server node
2277
1604
  if (plugin.type === 'AccessoryPlatform') {
2278
1605
  try {
2279
1606
  this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
@@ -2297,12 +1624,10 @@ export class Matterbridge extends EventEmitter {
2297
1624
  return;
2298
1625
  }
2299
1626
  }
2300
- // Register and add the device to the plugin aggregator node
2301
1627
  if (plugin.type === 'DynamicPlatform') {
2302
1628
  try {
2303
1629
  this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
2304
1630
  await this.createDynamicPlugin(plugin);
2305
- // Fast plugins can add another device before the server node is ready, so we wait for the server node to be ready
2306
1631
  await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
2307
1632
  if (!plugin.aggregatorNode) {
2308
1633
  this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
@@ -2323,28 +1648,17 @@ export class Matterbridge extends EventEmitter {
2323
1648
  }
2324
1649
  if (plugin.registeredDevices !== undefined)
2325
1650
  plugin.registeredDevices++;
2326
- // Add the device to the DeviceManager
2327
1651
  this.devices.set(device);
2328
- // Subscribe to the attributes changed event
2329
1652
  await this.subscribeAttributeChanged(plugin, device);
2330
1653
  this.log.info(`Added and registered bridged endpoint (${plugin.registeredDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
2331
1654
  }
2332
- /**
2333
- * Removes a MatterbridgeEndpoint from the specified plugin.
2334
- *
2335
- * @param {string} pluginName - The name of the plugin.
2336
- * @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
2337
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
2338
- */
2339
1655
  async removeBridgedEndpoint(pluginName, device) {
2340
1656
  this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
2341
- // Check if the plugin is registered
2342
1657
  const plugin = this.plugins.get(pluginName);
2343
1658
  if (!plugin) {
2344
1659
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
2345
1660
  return;
2346
1661
  }
2347
- // Unregister and remove the device from the matterbridge aggregator node
2348
1662
  if (this.bridgeMode === 'bridge') {
2349
1663
  if (!this.aggregatorNode) {
2350
1664
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
@@ -2357,10 +1671,8 @@ export class Matterbridge extends EventEmitter {
2357
1671
  }
2358
1672
  else if (this.bridgeMode === 'childbridge') {
2359
1673
  if (plugin.type === 'AccessoryPlatform') {
2360
- // Nothing to do here since the server node has no aggregator node but only the device itself
2361
1674
  }
2362
1675
  else if (plugin.type === 'DynamicPlatform') {
2363
- // Unregister and remove the device from the plugin aggregator node
2364
1676
  if (!plugin.aggregatorNode) {
2365
1677
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
2366
1678
  return;
@@ -2371,21 +1683,8 @@ export class Matterbridge extends EventEmitter {
2371
1683
  if (plugin.registeredDevices !== undefined)
2372
1684
  plugin.registeredDevices--;
2373
1685
  }
2374
- // Remove the device from the DeviceManager
2375
1686
  this.devices.remove(device);
2376
1687
  }
2377
- /**
2378
- * Removes all bridged endpoints from the specified plugin.
2379
- *
2380
- * @param {string} pluginName - The name of the plugin.
2381
- * @param {number} [delay] - The delay in milliseconds between removing each bridged endpoint (default: 0).
2382
- * @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
2383
- *
2384
- * @remarks
2385
- * This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
2386
- * It also applies a delay between each removal if specified.
2387
- * The delay is useful to allow the controllers to receive a single subscription for each device removed.
2388
- */
2389
1688
  async removeAllBridgedEndpoints(pluginName, delay = 0) {
2390
1689
  this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
2391
1690
  for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
@@ -2396,24 +1695,40 @@ export class Matterbridge extends EventEmitter {
2396
1695
  if (delay > 0)
2397
1696
  await wait(2000);
2398
1697
  }
2399
- /**
2400
- * Subscribes to the attribute change event for the given device and plugin.
2401
- * Specifically, it listens for changes in the 'reachable' attribute of the
2402
- * BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
2403
- *
2404
- * @param {Plugin} plugin - The plugin associated with the device.
2405
- * @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
2406
- * @returns {Promise<void>} A promise that resolves when the subscription is set up.
2407
- */
1698
+ async addVirtualEndpoint(pluginName, name, type, callback) {
1699
+ const plugin = this.plugins.get(pluginName);
1700
+ if (!plugin) {
1701
+ this.log.error(`Error adding virtual endpoint ${dev}${name}${er} for plugin ${plg}${pluginName}${er}: plugin not found`);
1702
+ return false;
1703
+ }
1704
+ let aggregator;
1705
+ if (this.bridgeMode === 'bridge') {
1706
+ aggregator = this.aggregatorNode;
1707
+ }
1708
+ else if (this.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
1709
+ aggregator = plugin.aggregatorNode;
1710
+ }
1711
+ if (aggregator) {
1712
+ if (aggregator.parts.has(name.replaceAll(' ', '') + ':' + type)) {
1713
+ this.log.error(`Virtual endpoint ${dev}${name}${er} already registered for plugin ${plg}${pluginName}${er}. Please use a different name.`);
1714
+ return false;
1715
+ }
1716
+ else {
1717
+ await addVirtualDevice(aggregator, name.slice(0, 32), type, callback);
1718
+ this.log.info(`Created virtual endpoint ${dev}${name}${nf} for plugin ${plg}${pluginName}${nf}`);
1719
+ return true;
1720
+ }
1721
+ }
1722
+ this.log.error(`Virtual endpoint ${dev}${name}${er} for plugin ${plg}${pluginName}${er} not created. Virtual endpoints are only supported in bridge mode and childbridge mode with a DynamicPlatform.`);
1723
+ return false;
1724
+ }
2408
1725
  async subscribeAttributeChanged(plugin, device) {
2409
1726
  if (!plugin || !device || !device.plugin || !device.serialNumber || !device.uniqueId || !device.maybeNumber)
2410
1727
  return;
2411
1728
  this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
2412
- // Subscribe to the reachable$Changed event of the BasicInformationServer cluster server of the server node in childbridge mode
2413
1729
  if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
2414
1730
  plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
2415
1731
  this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
2416
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2417
1732
  this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, 'BasicInformation', 'reachable', reachable);
2418
1733
  });
2419
1734
  }
@@ -2463,7 +1778,6 @@ export class Matterbridge extends EventEmitter {
2463
1778
  this.log.debug(`Subscribing to endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
2464
1779
  await device.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
2465
1780
  this.log.debug(`Bridged endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${isValidObject(value) ? debugStringify(value) : value}${db}`);
2466
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2467
1781
  this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, sub.cluster, sub.attribute, value);
2468
1782
  });
2469
1783
  }
@@ -2472,19 +1786,12 @@ export class Matterbridge extends EventEmitter {
2472
1786
  this.log.debug(`Subscribing to child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
2473
1787
  await child.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
2474
1788
  this.log.debug(`Bridged child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${isValidObject(value) ? debugStringify(value) : value}${db}`);
2475
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2476
1789
  this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, child.number, child.id, sub.cluster, sub.attribute, value);
2477
1790
  });
2478
1791
  }
2479
1792
  }
2480
1793
  }
2481
1794
  }
2482
- /**
2483
- * Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
2484
- *
2485
- * @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
2486
- * @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
2487
- */
2488
1795
  sanitizeFabricInformations(fabricInfo) {
2489
1796
  return fabricInfo.map((info) => {
2490
1797
  return {
@@ -2498,12 +1805,6 @@ export class Matterbridge extends EventEmitter {
2498
1805
  };
2499
1806
  });
2500
1807
  }
2501
- /**
2502
- * Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
2503
- *
2504
- * @param {SessionsBehavior.Session[]} sessions - The array of session information objects.
2505
- * @returns {SanitizedSession[]} An array of sanitized session information objects.
2506
- */
2507
1808
  sanitizeSessionInformation(sessions) {
2508
1809
  return sessions
2509
1810
  .filter((session) => session.isPeerActive)
@@ -2530,21 +1831,7 @@ export class Matterbridge extends EventEmitter {
2530
1831
  };
2531
1832
  });
2532
1833
  }
2533
- /**
2534
- * Sets the reachability of the specified aggregator node bridged devices and trigger.
2535
- *
2536
- * @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
2537
- * @param {boolean} reachable - A boolean indicating the reachability status to set.
2538
- */
2539
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2540
1834
  async setAggregatorReachability(aggregatorNode, reachable) {
2541
- /*
2542
- for (const child of aggregatorNode.parts) {
2543
- this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
2544
- await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
2545
- child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
2546
- }
2547
- */
2548
1835
  }
2549
1836
  getVendorIdName = (vendorId) => {
2550
1837
  if (!vendorId)
@@ -2584,11 +1871,10 @@ export class Matterbridge extends EventEmitter {
2584
1871
  case 0x1488:
2585
1872
  vendorName = '(ShortcutLabsFlic)';
2586
1873
  break;
2587
- case 65521: // 0xFFF1
1874
+ case 65521:
2588
1875
  vendorName = '(MatterTest)';
2589
1876
  break;
2590
1877
  }
2591
1878
  return vendorName;
2592
1879
  };
2593
1880
  }
2594
- //# sourceMappingURL=matterbridge.js.map