matterbridge 3.3.7 → 3.3.8-dev-20251115-920acfc

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