matterbridge 3.3.8 → 3.3.9-dev-20251118-930cfdb

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