matterbridge 3.3.4 → 3.3.5-dev-20251025-26d5c31

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