matterbridge 3.4.2 → 3.4.3-dev-20251207-3ce5a0e

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