matterbridge 3.4.4 → 3.4.5-dev-20251222-5853b56

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