matterbridge 3.3.6 → 3.3.7-dev-20251104-7c779b9

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