matterbridge 3.3.0 → 3.3.1-dev-20251008-e61b8db

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