matterbridge 3.3.0 → 3.3.1-dev-20251007-4e5eaac

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 +29 -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 +2 -91
  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 +172 -604
  36. package/dist/frontendTypes.js +0 -45
  37. package/dist/helpers.js +4 -57
  38. package/dist/index.js +0 -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 +148 -877
  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 +2 -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,27 @@
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
25
1
  import os from 'node:os';
26
2
  import path from 'node:path';
27
3
  import { promises as fs } from 'node:fs';
28
4
  import EventEmitter from 'node:events';
29
5
  import { inspect } from 'node:util';
30
- // AnsiLogger module
31
6
  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
7
  import { NodeStorageManager } from 'node-persist-manager';
34
- // @matter
35
8
  import { DeviceTypeId, Endpoint, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode, UINT32_MAX, UINT16_MAX, Crypto, } from '@matter/main';
36
9
  import { FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
37
10
  import { AggregatorEndpoint } from '@matter/main/endpoints';
38
11
  import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
39
- // Matterbridge
40
- import { getParameter, getIntParameter, hasParameter, copyDirectory, isValidString, parseVersionString, isValidNumber, createDirectory } from './utils/export.js';
12
+ import { getParameter, getIntParameter, hasParameter } from './utils/commandLine.js';
13
+ import { copyDirectory } from './utils/copyDirectory.js';
14
+ import { createDirectory } from './utils/createDirectory.js';
15
+ import { isValidString, parseVersionString, isValidNumber } from './utils/isvalid.js';
16
+ import { formatMemoryUsage, formatOsUpTime } from './utils/network.js';
41
17
  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';
18
+ import { dev, MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg, typ } from './matterbridgeTypes.js';
43
19
  import { PluginManager } from './pluginManager.js';
44
20
  import { DeviceManager } from './deviceManager.js';
45
21
  import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
46
22
  import { bridge } from './matterbridgeDeviceTypes.js';
47
23
  import { Frontend } from './frontend.js';
48
24
  import { addVirtualDevices } from './helpers.js';
49
- /**
50
- * Represents the Matterbridge application.
51
- */
52
25
  export class Matterbridge extends EventEmitter {
53
26
  systemInformation = {
54
27
  interfaceName: '',
@@ -71,38 +44,6 @@ export class Matterbridge extends EventEmitter {
71
44
  heapTotal: '',
72
45
  heapUsed: '',
73
46
  };
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
47
  homeDirectory = '';
107
48
  rootDirectory = '';
108
49
  matterbridgeDirectory = '';
@@ -112,23 +53,30 @@ export class Matterbridge extends EventEmitter {
112
53
  matterbridgeVersion = '';
113
54
  matterbridgeLatestVersion = '';
114
55
  matterbridgeDevVersion = '';
56
+ frontendVersion = '';
115
57
  bridgeMode = '';
116
58
  restartMode = '';
59
+ virtualMode = 'outlet';
117
60
  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
61
+ log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
62
+ fileLogger = false;
63
+ matterLog = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
64
+ matterFileLogger = false;
65
+ readOnly = hasParameter('readonly') || hasParameter('shelly');
66
+ shellyBoard = hasParameter('shelly');
67
+ shellySysUpdate = false;
68
+ shellyMainUpdate = false;
69
+ restartRequired = false;
70
+ fixedRestartRequired = false;
71
+ updateRequired = false;
125
72
  plugins = new PluginManager(this);
126
- devices = new DeviceManager(this);
73
+ devices = new DeviceManager();
127
74
  frontend = new Frontend(this);
128
- // Matterbridge storage
129
75
  nodeStorage;
130
76
  nodeContext;
131
- // Cleanup
77
+ static instance;
78
+ shutdown = false;
79
+ failCountLimit = hasParameter('shelly') ? 600 : 120;
132
80
  hasCleanupStarted = false;
133
81
  initialized = false;
134
82
  startMatterInterval;
@@ -141,22 +89,18 @@ export class Matterbridge extends EventEmitter {
141
89
  sigtermHandler;
142
90
  exceptionHandler;
143
91
  rejectionHandler;
144
- // Matter environment
145
92
  environment = Environment.default;
146
- // Matter storage
147
93
  matterStorageService;
148
94
  matterStorageManager;
149
95
  matterbridgeContext;
150
96
  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
97
+ mdnsInterface;
98
+ ipv4Address;
99
+ ipv6Address;
100
+ port;
101
+ passcode;
102
+ discriminator;
103
+ certification;
160
104
  serverNode;
161
105
  aggregatorNode;
162
106
  aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
@@ -166,57 +110,13 @@ export class Matterbridge extends EventEmitter {
166
110
  aggregatorDeviceType = DeviceTypeId(getIntParameter('deviceType') ?? bridge.code);
167
111
  aggregatorSerialNumber = getParameter('serialNumber');
168
112
  aggregatorUniqueId = getParameter('uniqueId');
169
- /** Advertising nodes map: time advertising started keyed by storeId */
170
113
  advertisingNodes = new Map();
171
- static instance;
172
- // We load asyncronously so is private
173
114
  constructor() {
174
115
  super();
175
116
  this.log.logNameColor = '\x1b[38;5;115m';
176
117
  }
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
118
  static async loadInstance(initialize = false) {
218
119
  if (!Matterbridge.instance) {
219
- // eslint-disable-next-line no-console
220
120
  if (hasParameter('debug'))
221
121
  console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
222
122
  Matterbridge.instance = new Matterbridge();
@@ -225,17 +125,8 @@ export class Matterbridge extends EventEmitter {
225
125
  }
226
126
  return Matterbridge.instance;
227
127
  }
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
128
  async destroyInstance(timeout = 1000, pause = 250) {
237
129
  this.log.info(`Destroy instance...`);
238
- // Save server nodes to close
239
130
  const servers = [];
240
131
  if (this.bridgeMode === 'bridge') {
241
132
  if (this.serverNode)
@@ -253,106 +144,67 @@ export class Matterbridge extends EventEmitter {
253
144
  servers.push(device.serverNode);
254
145
  }
255
146
  }
256
- // Let any already‐queued microtasks run first
257
147
  await Promise.resolve();
258
- // Wait for the cleanup to finish
259
148
  await wait(pause, 'destroyInstance start', true);
260
- // Cleanup
261
149
  await this.cleanup('destroying instance...', false, timeout);
262
- // Close servers mdns service
263
150
  this.log.info(`Dispose ${servers.length} MdnsService...`);
264
151
  for (const server of servers) {
265
152
  await server.env.get(MdnsService)[Symbol.asyncDispose]();
266
153
  this.log.info(`Closed ${server.id} MdnsService`);
267
154
  }
268
- // Let any already‐queued microtasks run first
269
155
  await Promise.resolve();
270
- // Wait for the cleanup to finish
271
156
  await wait(pause, 'destroyInstance stop', true);
272
157
  }
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
158
  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
159
  this.emit('initialize_started');
287
- // Set the restart mode
288
160
  if (hasParameter('service'))
289
161
  this.restartMode = 'service';
290
162
  if (hasParameter('docker'))
291
163
  this.restartMode = 'docker';
292
- // Set the matterbridge home directory
293
164
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
294
- this.matterbridgeInformation.homeDirectory = this.homeDirectory;
295
165
  await createDirectory(this.homeDirectory, 'Matterbridge Home Directory', this.log);
296
- // Set the matterbridge directory
297
166
  this.matterbridgeDirectory = this.profile ? path.join(this.homeDirectory, '.matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, '.matterbridge');
298
- this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
299
167
  await createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory', this.log);
300
168
  await createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory', this.log);
301
169
  await createDirectory(path.join(this.matterbridgeDirectory, 'uploads'), 'Matterbridge Frontend Uploads Directory', this.log);
302
- // Set the matterbridge plugin directory
303
170
  this.matterbridgePluginDirectory = this.profile ? path.join(this.homeDirectory, 'Matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, 'Matterbridge');
304
- this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
305
171
  await createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory', this.log);
306
- // Set the matterbridge cert directory
307
172
  this.matterbridgeCertDirectory = this.profile ? path.join(this.homeDirectory, '.mattercert', 'profiles', this.profile) : path.join(this.homeDirectory, '.mattercert');
308
- this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
309
173
  await createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory', this.log);
310
- // Set the matterbridge root directory
311
174
  const { fileURLToPath } = await import('node:url');
312
175
  const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
313
176
  this.rootDirectory = path.resolve(currentFileDirectory, '../');
314
- this.matterbridgeInformation.rootDirectory = this.rootDirectory;
315
- // Setup the matter environment
316
177
  this.environment.vars.set('log.level', MatterLogLevel.INFO);
317
178
  this.environment.vars.set('log.format', MatterLogFormat.ANSI);
318
179
  this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME));
319
180
  this.environment.vars.set('runtime.signals', false);
320
181
  this.environment.vars.set('runtime.exitcode', false);
321
- // Register process handlers
322
182
  this.registerProcessHandlers();
323
- // Initialize nodeStorage and nodeContext
324
183
  try {
325
184
  this.log.debug(`Creating node storage manager: ${CYAN}${NODE_STORAGE_DIR}${db}`);
326
185
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), writeQueue: false, expiredInterval: undefined, logging: false });
327
186
  this.log.debug('Creating node storage context for matterbridge');
328
187
  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
188
  const keys = (await this.nodeStorage?.storage.keys());
332
189
  for (const key of keys) {
333
190
  this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
334
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
335
191
  await this.nodeStorage?.storage.get(key);
336
192
  }
337
193
  const storages = await this.nodeStorage.getStorageNames();
338
194
  for (const storage of storages) {
339
195
  this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
340
196
  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
197
  const keys = (await nodeContext?.storage.keys());
344
198
  keys.forEach(async (key) => {
345
199
  this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
346
200
  await nodeContext?.get(key);
347
201
  });
348
202
  }
349
- // Creating a backup of the node storage since it is not corrupted
350
203
  this.log.debug('Creating node storage backup...');
351
204
  await copyDirectory(path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR + '.backup'));
352
205
  this.log.debug('Created node storage backup');
353
206
  }
354
207
  catch (error) {
355
- // Restoring the backup of the node storage since it is corrupted
356
208
  this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
357
209
  if (hasParameter('norestore')) {
358
210
  this.log.fatal(`The matterbridge storage is corrupted. Found -norestore parameter: exiting...`);
@@ -366,19 +218,14 @@ export class Matterbridge extends EventEmitter {
366
218
  if (!this.nodeStorage || !this.nodeContext) {
367
219
  throw new Error('Fatal error creating node storage manager and context for matterbridge');
368
220
  }
369
- // Set the first port to use for the commissioning server (will be incremented in childbridge mode)
370
221
  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
222
  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
223
  this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
375
- // Certificate management
376
224
  const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
377
225
  try {
378
226
  await fs.access(pairingFilePath, fs.constants.R_OK);
379
227
  const pairingFileContent = await fs.readFile(pairingFilePath, 'utf8');
380
228
  const pairingFileJson = JSON.parse(pairingFileContent);
381
- // Set the vendorId, vendorName, productId, productName, deviceType, serialNumber, uniqueId if they are present in the pairing file
382
229
  if (isValidNumber(pairingFileJson.vendorId)) {
383
230
  this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
384
231
  this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using vendorId ${CYAN}${this.aggregatorVendorId}${nf} from pairing file.`);
@@ -407,13 +254,11 @@ export class Matterbridge extends EventEmitter {
407
254
  this.aggregatorUniqueId = pairingFileJson.uniqueId;
408
255
  this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using uniqueId ${CYAN}${this.aggregatorUniqueId}${nf} from pairing file.`);
409
256
  }
410
- // Override the passcode and discriminator if they are present in the pairing file
411
257
  if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
412
258
  this.passcode = pairingFileJson.passcode;
413
259
  this.discriminator = pairingFileJson.discriminator;
414
260
  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
261
  }
416
- // Set the certification for matter.js if it is present in the pairing file
417
262
  if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
418
263
  const { hexToBuffer } = await import('./utils/hex.js');
419
264
  this.certification = {
@@ -428,53 +273,48 @@ export class Matterbridge extends EventEmitter {
428
273
  catch (error) {
429
274
  this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
430
275
  }
431
- // Store the passcode, discriminator and port in the node context
432
276
  await this.nodeContext.set('matterport', this.port);
433
277
  await this.nodeContext.set('matterpasscode', this.passcode);
434
278
  await this.nodeContext.set('matterdiscriminator', this.discriminator);
435
279
  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
280
  if (hasParameter('logger')) {
438
281
  const level = getParameter('logger');
439
282
  if (level === 'debug') {
440
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
283
+ this.log.logLevel = "debug";
441
284
  }
442
285
  else if (level === 'info') {
443
- this.log.logLevel = "info" /* LogLevel.INFO */;
286
+ this.log.logLevel = "info";
444
287
  }
445
288
  else if (level === 'notice') {
446
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
289
+ this.log.logLevel = "notice";
447
290
  }
448
291
  else if (level === 'warn') {
449
- this.log.logLevel = "warn" /* LogLevel.WARN */;
292
+ this.log.logLevel = "warn";
450
293
  }
451
294
  else if (level === 'error') {
452
- this.log.logLevel = "error" /* LogLevel.ERROR */;
295
+ this.log.logLevel = "error";
453
296
  }
454
297
  else if (level === 'fatal') {
455
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
298
+ this.log.logLevel = "fatal";
456
299
  }
457
300
  else {
458
301
  this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
459
- this.log.logLevel = "info" /* LogLevel.INFO */;
302
+ this.log.logLevel = "info";
460
303
  }
461
304
  }
462
305
  else {
463
- this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" /* LogLevel.NOTICE */ : "info" /* LogLevel.INFO */);
306
+ this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice" : "info");
464
307
  }
465
308
  this.frontend.logLevel = this.log.logLevel;
466
309
  MatterbridgeEndpoint.logLevel = this.log.logLevel;
467
- this.matterbridgeInformation.loggerLevel = this.log.logLevel;
468
- // Create the file logger for matterbridge (context: matterbridgeFileLog)
469
310
  if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
470
311
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), this.log.logLevel, true);
471
- this.matterbridgeInformation.fileLogger = true;
312
+ this.fileLogger = true;
472
313
  }
473
314
  this.log.notice('Matterbridge is starting...');
474
- this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
315
+ this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.fileLogger}.`);
475
316
  if (this.profile !== undefined)
476
317
  this.log.debug(`Matterbridge profile: ${this.profile}.`);
477
- // Set matter.js logger level, format and logger (context: matterLogLevel)
478
318
  if (hasParameter('matterlogger')) {
479
319
  const level = getParameter('matterlogger');
480
320
  if (level === 'debug') {
@@ -501,20 +341,17 @@ export class Matterbridge extends EventEmitter {
501
341
  }
502
342
  }
503
343
  else {
504
- Logger.level = (await this.nodeContext.get('matterLogLevel', this.matterbridgeInformation.shellyBoard ? MatterLogLevel.NOTICE : MatterLogLevel.INFO));
344
+ Logger.level = (await this.nodeContext.get('matterLogLevel', this.shellyBoard ? MatterLogLevel.NOTICE : MatterLogLevel.INFO));
505
345
  }
506
346
  Logger.format = MatterLogFormat.ANSI;
507
- // Create the logger for matter.js with file logging (context: matterFileLog)
508
347
  if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
509
- this.matterbridgeInformation.matterFileLogger = true;
348
+ this.matterFileLogger = true;
510
349
  }
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
350
+ Logger.destinations.default.write = this.createDestinationMatterLogger(this.matterFileLogger);
351
+ this.log.debug(`Matter logLevel: ${Logger.level} fileLoger: ${this.matterFileLogger}.`);
515
352
  const networkInterfaces = os.networkInterfaces();
516
353
  const availableAddresses = Object.entries(networkInterfaces);
517
- const availableInterfaces = Object.keys(networkInterfaces);
354
+ const availableInterfaceNames = Object.keys(networkInterfaces);
518
355
  for (const [ifaceName, ifaces] of availableAddresses) {
519
356
  if (ifaces && ifaces.length > 0) {
520
357
  this.log.debug(`Network interface ${BLUE}${ifaceName}${db}:`);
@@ -524,7 +361,6 @@ export class Matterbridge extends EventEmitter {
524
361
  });
525
362
  }
526
363
  }
527
- // Set the interface to use for matter server node mdnsInterface
528
364
  if (hasParameter('mdnsinterface')) {
529
365
  this.mdnsInterface = getParameter('mdnsinterface');
530
366
  }
@@ -533,10 +369,9 @@ export class Matterbridge extends EventEmitter {
533
369
  if (this.mdnsInterface === '')
534
370
  this.mdnsInterface = undefined;
535
371
  }
536
- // Validate mdnsInterface
537
372
  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.`);
373
+ if (!availableInterfaceNames.includes(this.mdnsInterface)) {
374
+ this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaceNames.join(', ')}. Using all available interfaces.`);
540
375
  this.mdnsInterface = undefined;
541
376
  await this.nodeContext.remove('mattermdnsinterface');
542
377
  }
@@ -546,86 +381,75 @@ export class Matterbridge extends EventEmitter {
546
381
  }
547
382
  if (this.mdnsInterface)
548
383
  this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
549
- // Set the listeningAddressIpv4 for the matter commissioning server
550
384
  if (hasParameter('ipv4address')) {
551
- this.ipv4address = getParameter('ipv4address');
385
+ this.ipv4Address = getParameter('ipv4address');
552
386
  }
553
387
  else {
554
- this.ipv4address = await this.nodeContext.get('matteripv4address', undefined);
555
- if (this.ipv4address === '')
556
- this.ipv4address = undefined;
388
+ this.ipv4Address = await this.nodeContext.get('matteripv4address', undefined);
389
+ if (this.ipv4Address === '')
390
+ this.ipv4Address = undefined;
557
391
  }
558
- // Validate ipv4address
559
- if (this.ipv4address) {
392
+ if (this.ipv4Address) {
560
393
  let isValid = false;
561
394
  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.`);
395
+ if (ifaces && ifaces.find((iface) => iface.address === this.ipv4Address)) {
396
+ this.log.info(`Using ipv4address ${CYAN}${this.ipv4Address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
564
397
  isValid = true;
565
398
  break;
566
399
  }
567
400
  }
568
401
  if (!isValid) {
569
- this.log.error(`Invalid ipv4address: ${this.ipv4address}. Using all available addresses.`);
570
- this.ipv4address = undefined;
402
+ this.log.error(`Invalid ipv4address: ${this.ipv4Address}. Using all available addresses.`);
403
+ this.ipv4Address = undefined;
571
404
  await this.nodeContext.remove('matteripv4address');
572
405
  }
573
406
  }
574
- // Set the listeningAddressIpv6 for the matter commissioning server
575
407
  if (hasParameter('ipv6address')) {
576
- this.ipv6address = getParameter('ipv6address');
408
+ this.ipv6Address = getParameter('ipv6address');
577
409
  }
578
410
  else {
579
- this.ipv6address = await this.nodeContext?.get('matteripv6address', undefined);
580
- if (this.ipv6address === '')
581
- this.ipv6address = undefined;
411
+ this.ipv6Address = await this.nodeContext?.get('matteripv6address', undefined);
412
+ if (this.ipv6Address === '')
413
+ this.ipv6Address = undefined;
582
414
  }
583
- // Validate ipv6address
584
- if (this.ipv6address) {
415
+ if (this.ipv6Address) {
585
416
  let isValid = false;
586
417
  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.`);
418
+ if (ifaces && ifaces.find((iface) => (iface.scopeid === undefined || iface.scopeid === 0) && iface.address === this.ipv6Address)) {
419
+ this.log.info(`Using ipv6address ${CYAN}${this.ipv6Address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
589
420
  isValid = true;
590
421
  break;
591
422
  }
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.`);
423
+ if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6Address)) {
424
+ this.log.info(`Using ipv6address ${CYAN}${this.ipv6Address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
595
425
  isValid = true;
596
426
  break;
597
427
  }
598
428
  }
599
429
  if (!isValid) {
600
- this.log.error(`Invalid ipv6address: ${this.ipv6address}. Using all available addresses.`);
601
- this.ipv6address = undefined;
430
+ this.log.error(`Invalid ipv6address: ${this.ipv6Address}. Using all available addresses.`);
431
+ this.ipv6Address = undefined;
602
432
  await this.nodeContext.remove('matteripv6address');
603
433
  }
604
434
  }
605
- // Initialize the virtual mode
606
435
  if (hasParameter('novirtual')) {
607
- this.matterbridgeInformation.virtualMode = 'disabled';
436
+ this.virtualMode = 'disabled';
608
437
  await this.nodeContext.set('virtualmode', 'disabled');
609
438
  }
610
439
  else {
611
- this.matterbridgeInformation.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
440
+ this.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
612
441
  }
613
- this.log.debug(`Virtual mode ${this.matterbridgeInformation.virtualMode}.`);
614
- // Initialize PluginManager
442
+ this.log.debug(`Virtual mode ${this.virtualMode}.`);
615
443
  this.plugins.logLevel = this.log.logLevel;
616
444
  await this.plugins.loadFromStorage();
617
- // Initialize DeviceManager
618
445
  this.devices.logLevel = this.log.logLevel;
619
- // Get the plugins from node storage and create the plugins node storage contexts
620
446
  for (const plugin of this.plugins) {
621
447
  const packageJson = await this.plugins.parse(plugin);
622
448
  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
449
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
626
450
  try {
627
451
  const { spawnCommand } = await import('./utils/spawn.js');
628
- await spawnCommand(this, 'npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose'], plugin.name);
452
+ await spawnCommand(this, 'npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose'], 'install', plugin.name);
629
453
  this.log.info(`Plugin ${plg}${plugin.name}${nf} reinstalled.`);
630
454
  plugin.error = false;
631
455
  }
@@ -644,7 +468,6 @@ export class Matterbridge extends EventEmitter {
644
468
  await plugin.nodeContext.set('description', plugin.description);
645
469
  await plugin.nodeContext.set('author', plugin.author);
646
470
  }
647
- // Log system info and create .matterbridge directory
648
471
  await this.logNodeAndSystemInfo();
649
472
  this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
650
473
  `${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
@@ -652,7 +475,6 @@ export class Matterbridge extends EventEmitter {
652
475
  `${hasParameter('controller') ? 'mode controller ' : ''}` +
653
476
  `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
654
477
  `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
478
  const minNodeVersion = 18;
657
479
  const nodeVersion = process.versions.node;
658
480
  const versionMajor = parseInt(nodeVersion.split('.')[0]);
@@ -660,18 +482,10 @@ export class Matterbridge extends EventEmitter {
660
482
  this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
661
483
  throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
662
484
  }
663
- // Parse command line
664
485
  await this.parseCommandLine();
665
- // Emit the initialize_completed event
666
486
  this.emit('initialize_completed');
667
487
  this.initialized = true;
668
488
  }
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
489
  async parseCommandLine() {
676
490
  if (hasParameter('help')) {
677
491
  this.log.info(`\nUsage: matterbridge [options]\n
@@ -733,19 +547,6 @@ export class Matterbridge extends EventEmitter {
733
547
  }
734
548
  index++;
735
549
  }
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
550
  this.shutdown = true;
750
551
  return;
751
552
  }
@@ -795,10 +596,8 @@ export class Matterbridge extends EventEmitter {
795
596
  this.shutdown = true;
796
597
  return;
797
598
  }
798
- // Initialize frontend
799
599
  if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
800
600
  await this.frontend.start(getIntParameter('frontend'));
801
- // Start the matter storage and create the matterbridge context
802
601
  try {
803
602
  await this.startMatterStorage();
804
603
  if (this.aggregatorSerialNumber && this.aggregatorUniqueId && this.matterStorageService) {
@@ -814,21 +613,18 @@ export class Matterbridge extends EventEmitter {
814
613
  this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
815
614
  throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
816
615
  }
817
- // Clear the matterbridge context if the reset parameter is set
818
616
  if (hasParameter('reset') && getParameter('reset') === undefined) {
819
617
  this.initialized = true;
820
618
  await this.shutdownProcessAndReset();
821
619
  this.shutdown = true;
822
620
  return;
823
621
  }
824
- // Clear matterbridge plugin context if the reset parameter is set
825
622
  if (hasParameter('reset') && getParameter('reset') !== undefined) {
826
623
  this.log.debug(`Reset plugin ${getParameter('reset')}`);
827
624
  const plugin = this.plugins.get(getParameter('reset'));
828
625
  if (plugin) {
829
626
  const matterStorageManager = await this.matterStorageService?.open(plugin.name);
830
627
  if (!matterStorageManager) {
831
- /* istanbul ignore next */
832
628
  this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
833
629
  }
834
630
  else {
@@ -847,42 +643,35 @@ export class Matterbridge extends EventEmitter {
847
643
  this.shutdown = true;
848
644
  return;
849
645
  }
850
- // Check in 30 seconds the latest and dev versions of matterbridge and the plugins
851
646
  clearTimeout(this.checkUpdateTimeout);
852
647
  this.checkUpdateTimeout = setTimeout(async () => {
853
648
  const { checkUpdates } = await import('./update.js');
854
649
  checkUpdates(this);
855
650
  }, 30 * 1000).unref();
856
- // Check each 12 hours the latest and dev versions of matterbridge and the plugins
857
651
  clearInterval(this.checkUpdateInterval);
858
652
  this.checkUpdateInterval = setInterval(async () => {
859
653
  const { checkUpdates } = await import('./update.js');
860
654
  checkUpdates(this);
861
655
  }, 12 * 60 * 60 * 1000).unref();
862
- // Start the matterbridge in mode test
863
656
  if (hasParameter('test')) {
864
657
  this.bridgeMode = 'bridge';
865
658
  return;
866
659
  }
867
- // Start the matterbridge in mode controller
868
660
  if (hasParameter('controller')) {
869
661
  this.bridgeMode = 'controller';
870
662
  await this.startController();
871
663
  return;
872
664
  }
873
- // Check if the bridge mode is set and start matterbridge in bridge mode if not set
874
665
  if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
875
666
  this.log.info('Setting default matterbridge start mode to bridge');
876
667
  await this.nodeContext?.set('bridgeMode', 'bridge');
877
668
  }
878
- // Start matterbridge in bridge mode
879
669
  if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
880
670
  this.bridgeMode = 'bridge';
881
671
  this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
882
672
  await this.startBridge();
883
673
  return;
884
674
  }
885
- // Start matterbridge in childbridge mode
886
675
  if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
887
676
  this.bridgeMode = 'childbridge';
888
677
  this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
@@ -890,22 +679,10 @@ export class Matterbridge extends EventEmitter {
890
679
  return;
891
680
  }
892
681
  }
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
682
  async startPlugins(wait = false, start = true) {
904
- // Check, load and start the plugins
905
683
  for (const plugin of this.plugins) {
906
684
  plugin.configJson = await this.plugins.loadConfig(plugin);
907
685
  plugin.schemaJson = await this.plugins.loadSchema(plugin);
908
- // Check if the plugin is available
909
686
  if (!(await this.plugins.resolve(plugin.path))) {
910
687
  this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
911
688
  plugin.enabled = false;
@@ -925,16 +702,10 @@ export class Matterbridge extends EventEmitter {
925
702
  if (wait)
926
703
  await this.plugins.load(plugin, start, 'Matterbridge is starting');
927
704
  else
928
- this.plugins.load(plugin, start, 'Matterbridge is starting'); // No await do it asyncronously
705
+ this.plugins.load(plugin, start, 'Matterbridge is starting');
929
706
  }
930
707
  this.frontend.wssSendRefreshRequired('plugins');
931
708
  }
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
709
  registerProcessHandlers() {
939
710
  this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
940
711
  process.removeAllListeners('uncaughtException');
@@ -961,9 +732,6 @@ export class Matterbridge extends EventEmitter {
961
732
  };
962
733
  process.on('SIGTERM', this.sigtermHandler);
963
734
  }
964
- /**
965
- * Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
966
- */
967
735
  deregisterProcessHandlers() {
968
736
  this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
969
737
  if (this.exceptionHandler)
@@ -980,17 +748,13 @@ export class Matterbridge extends EventEmitter {
980
748
  process.off('SIGTERM', this.sigtermHandler);
981
749
  this.sigtermHandler = undefined;
982
750
  }
983
- /**
984
- * Logs the node and system information.
985
- */
986
751
  async logNodeAndSystemInfo() {
987
- // IP address information
988
752
  const networkInterfaces = os.networkInterfaces();
989
753
  this.systemInformation.interfaceName = '';
990
754
  this.systemInformation.ipv4Address = '';
991
755
  this.systemInformation.ipv6Address = '';
756
+ this.systemInformation.macAddress = '';
992
757
  for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
993
- // this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
994
758
  if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
995
759
  continue;
996
760
  if (!interfaceDetails) {
@@ -1016,22 +780,24 @@ export class Matterbridge extends EventEmitter {
1016
780
  break;
1017
781
  }
1018
782
  }
1019
- // Node information
1020
783
  this.systemInformation.nodeVersion = process.versions.node;
1021
784
  const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
1022
785
  const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
1023
786
  const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
1024
- // Host system information
1025
787
  this.systemInformation.hostname = os.hostname();
1026
788
  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
789
+ this.systemInformation.osType = os.type();
790
+ this.systemInformation.osRelease = os.release();
791
+ this.systemInformation.osPlatform = os.platform();
792
+ this.systemInformation.osArch = os.arch();
793
+ this.systemInformation.totalMemory = formatMemoryUsage(os.totalmem());
794
+ this.systemInformation.freeMemory = formatMemoryUsage(os.freemem());
795
+ this.systemInformation.systemUptime = formatOsUpTime(os.uptime());
796
+ this.systemInformation.processUptime = formatOsUpTime(Math.floor(process.uptime()));
797
+ this.systemInformation.cpuUsage = '0.00 %';
798
+ this.systemInformation.rss = formatMemoryUsage(process.memoryUsage().rss);
799
+ this.systemInformation.heapTotal = formatMemoryUsage(process.memoryUsage().heapTotal);
800
+ this.systemInformation.heapUsed = formatMemoryUsage(process.memoryUsage().heapUsed);
1035
801
  this.log.debug('Host System Information:');
1036
802
  this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
1037
803
  this.log.debug(`- User: ${this.systemInformation.user}`);
@@ -1047,21 +813,22 @@ export class Matterbridge extends EventEmitter {
1047
813
  this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
1048
814
  this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
1049
815
  this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
1050
- // Log directories
816
+ this.log.debug(`- Process Uptime: ${this.systemInformation.processUptime}`);
817
+ this.log.debug(`- RSS: ${this.systemInformation.rss}`);
818
+ this.log.debug(`- Heap Total: ${this.systemInformation.heapTotal}`);
819
+ this.log.debug(`- Heap Used: ${this.systemInformation.heapUsed}`);
1051
820
  this.log.debug(`Root Directory: ${this.rootDirectory}`);
1052
821
  this.log.debug(`Home Directory: ${this.homeDirectory}`);
1053
822
  this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
1054
823
  this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
1055
824
  this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
1056
- // Global node_modules directory
1057
825
  if (this.nodeContext)
1058
- this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
826
+ this.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
1059
827
  if (this.globalModulesDirectory === '') {
1060
- // First run of Matterbridge so the node storage is empty
1061
828
  this.log.debug(`Getting global node_modules directory...`);
1062
829
  try {
1063
830
  const { getGlobalNodeModules } = await import('./utils/network.js');
1064
- this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory = await getGlobalNodeModules();
831
+ this.globalModulesDirectory = await getGlobalNodeModules();
1065
832
  this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
1066
833
  await this.nodeContext?.set('globalModulesDirectory', this.globalModulesDirectory);
1067
834
  }
@@ -1070,11 +837,10 @@ export class Matterbridge extends EventEmitter {
1070
837
  }
1071
838
  }
1072
839
  else {
1073
- // The global node_modules directory is already set in the node storage and we check if it is still valid
1074
840
  this.log.debug(`Checking global node_modules directory: ${this.globalModulesDirectory}`);
1075
841
  try {
1076
842
  const { getGlobalNodeModules } = await import('./utils/network.js');
1077
- this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory = await getGlobalNodeModules();
843
+ this.globalModulesDirectory = await getGlobalNodeModules();
1078
844
  this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
1079
845
  await this.nodeContext?.set('globalModulesDirectory', this.globalModulesDirectory);
1080
846
  }
@@ -1082,91 +848,93 @@ export class Matterbridge extends EventEmitter {
1082
848
  this.log.error(`Error checking global node_modules directory: ${error}`);
1083
849
  }
1084
850
  }
1085
- // Matterbridge version
851
+ this.log.debug(`Reading matterbridge package.json...`);
1086
852
  const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
1087
853
  this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
1088
- this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeDevVersion = packageJson.version;
1089
854
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
1090
- // Matterbridge latest version (will be set in the checkUpdate function)
1091
855
  if (this.nodeContext)
1092
- this.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
856
+ this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
1093
857
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
1094
- // Matterbridge dev version (will be set in the checkUpdate function)
1095
858
  if (this.nodeContext)
1096
- this.matterbridgeDevVersion = this.matterbridgeInformation.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
859
+ this.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
1097
860
  this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
1098
- // Current working directory
861
+ this.log.debug(`Reading frontend package.json...`);
862
+ const frontendPackageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'frontend/package.json'), 'utf8'));
863
+ this.frontendVersion = frontendPackageJson.version;
864
+ this.log.debug(`Frontend version ${CYAN}${this.frontendVersion}${db}`);
1099
865
  const currentDir = process.cwd();
1100
866
  this.log.debug(`Current Working Directory: ${currentDir}`);
1101
- // Command line arguments (excluding 'node' and the script name)
1102
867
  const cmdArgs = process.argv.slice(2).join(' ');
1103
868
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
1104
869
  }
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
- */
870
+ async setLogLevel(logLevel) {
871
+ if (this.log)
872
+ this.log.logLevel = logLevel;
873
+ this.frontend.logLevel = logLevel;
874
+ MatterbridgeEndpoint.logLevel = logLevel;
875
+ if (this.devices)
876
+ this.devices.logLevel = logLevel;
877
+ if (this.plugins)
878
+ this.plugins.logLevel = logLevel;
879
+ for (const plugin of this.plugins) {
880
+ if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
881
+ continue;
882
+ plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
883
+ await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
884
+ }
885
+ let callbackLogLevel = "notice";
886
+ if (this.log.logLevel === "info" || Logger.level === MatterLogLevel.INFO)
887
+ callbackLogLevel = "info";
888
+ if (this.log.logLevel === "debug" || Logger.level === MatterLogLevel.DEBUG)
889
+ callbackLogLevel = "debug";
890
+ AnsiLogger.setGlobalCallback(this.frontend.wssSendLogMessage.bind(this.frontend), callbackLogLevel);
891
+ this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
892
+ }
893
+ getLogLevel() {
894
+ return this.log.logLevel;
895
+ }
1112
896
  createDestinationMatterLogger(fileLogger) {
1113
- this.matterLog.logNameColor = '\x1b[34m'; // Blue matter.js Logger
897
+ this.matterLog.logNameColor = '\x1b[34m';
1114
898
  if (fileLogger) {
1115
899
  this.matterLog.logFilePath = path.join(this.matterbridgeDirectory, MATTER_LOGGER_FILE);
1116
900
  }
1117
901
  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
902
  const logger = text.slice(44, 44 + 20).trim();
1120
903
  const msg = text.slice(65);
1121
904
  this.matterLog.logName = logger;
1122
905
  switch (message.level) {
1123
906
  case MatterLogLevel.DEBUG:
1124
- this.matterLog.log("debug" /* LogLevel.DEBUG */, msg);
907
+ this.matterLog.log("debug", msg);
1125
908
  break;
1126
909
  case MatterLogLevel.INFO:
1127
- this.matterLog.log("info" /* LogLevel.INFO */, msg);
910
+ this.matterLog.log("info", msg);
1128
911
  break;
1129
912
  case MatterLogLevel.NOTICE:
1130
- this.matterLog.log("notice" /* LogLevel.NOTICE */, msg);
913
+ this.matterLog.log("notice", msg);
1131
914
  break;
1132
915
  case MatterLogLevel.WARN:
1133
- this.matterLog.log("warn" /* LogLevel.WARN */, msg);
916
+ this.matterLog.log("warn", msg);
1134
917
  break;
1135
918
  case MatterLogLevel.ERROR:
1136
- this.matterLog.log("error" /* LogLevel.ERROR */, msg);
919
+ this.matterLog.log("error", msg);
1137
920
  break;
1138
921
  case MatterLogLevel.FATAL:
1139
- this.matterLog.log("fatal" /* LogLevel.FATAL */, msg);
922
+ this.matterLog.log("fatal", msg);
1140
923
  break;
1141
924
  }
1142
925
  };
1143
926
  }
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
927
  async restartProcess() {
1150
928
  await this.cleanup('restarting...', true);
1151
929
  }
1152
- /**
1153
- * Shut down the process (/api/shutdown).
1154
- *
1155
- * @returns {Promise<void>} A promise that resolves when the shutdown is completed.
1156
- */
1157
930
  async shutdownProcess() {
1158
931
  await this.cleanup('shutting down...', false);
1159
932
  }
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
933
  async updateProcess() {
1166
934
  this.log.info('Updating matterbridge...');
1167
935
  try {
1168
936
  const { spawnCommand } = await import('./utils/spawn.js');
1169
- await spawnCommand(this, 'npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose'], 'matterbridge');
937
+ await spawnCommand(this, 'npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose'], 'install', 'matterbridge');
1170
938
  this.log.info('Matterbridge has been updated. Full restart required.');
1171
939
  }
1172
940
  catch (error) {
@@ -1175,13 +943,6 @@ export class Matterbridge extends EventEmitter {
1175
943
  this.frontend.wssSendRestartRequired();
1176
944
  await this.cleanup('updating...', false);
1177
945
  }
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
946
  async unregisterAndShutdownProcess(timeout = 1000) {
1186
947
  this.log.info('Unregistering all devices and shutting down...');
1187
948
  for (const plugin of this.plugins.array()) {
@@ -1193,71 +954,46 @@ export class Matterbridge extends EventEmitter {
1193
954
  await this.removeAllBridgedEndpoints(plugin.name, 100);
1194
955
  }
1195
956
  this.log.debug('Waiting for the MessageExchange to finish...');
1196
- await wait(timeout); // Wait for MessageExchange to finish
957
+ await wait(timeout);
1197
958
  this.log.debug('Cleaning up and shutting down...');
1198
959
  await this.cleanup('unregistered all devices and shutting down...', false, timeout);
1199
960
  }
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
961
  async shutdownProcessAndReset() {
1206
962
  await this.cleanup('shutting down with reset...', false);
1207
963
  }
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
964
  async shutdownProcessAndFactoryReset() {
1214
965
  await this.cleanup('shutting down with factory reset...', false);
1215
966
  }
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
967
  async cleanup(message, restart = false, timeout = 1000) {
1226
968
  if (this.initialized && !this.hasCleanupStarted) {
1227
969
  this.emit('cleanup_started');
1228
970
  this.hasCleanupStarted = true;
1229
971
  this.log.info(message);
1230
- // Clear the start matter interval
1231
972
  if (this.startMatterInterval) {
1232
973
  clearInterval(this.startMatterInterval);
1233
974
  this.startMatterInterval = undefined;
1234
975
  this.log.debug('Start matter interval cleared');
1235
976
  }
1236
- // Clear the check update timeout
1237
977
  if (this.checkUpdateTimeout) {
1238
978
  clearTimeout(this.checkUpdateTimeout);
1239
979
  this.checkUpdateTimeout = undefined;
1240
980
  this.log.debug('Check update timeout cleared');
1241
981
  }
1242
- // Clear the check update interval
1243
982
  if (this.checkUpdateInterval) {
1244
983
  clearInterval(this.checkUpdateInterval);
1245
984
  this.checkUpdateInterval = undefined;
1246
985
  this.log.debug('Check update interval cleared');
1247
986
  }
1248
- // Clear the configure timeout
1249
987
  if (this.configureTimeout) {
1250
988
  clearTimeout(this.configureTimeout);
1251
989
  this.configureTimeout = undefined;
1252
990
  this.log.debug('Matterbridge configure timeout cleared');
1253
991
  }
1254
- // Clear the reachability timeout
1255
992
  if (this.reachabilityTimeout) {
1256
993
  clearTimeout(this.reachabilityTimeout);
1257
994
  this.reachabilityTimeout = undefined;
1258
995
  this.log.debug('Matterbridge reachability timeout cleared');
1259
996
  }
1260
- // Call the shutdown method of each plugin and clear the plugins reachability timeout
1261
997
  for (const plugin of this.plugins) {
1262
998
  if (!plugin.enabled || plugin.error)
1263
999
  continue;
@@ -1268,7 +1004,6 @@ export class Matterbridge extends EventEmitter {
1268
1004
  this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
1269
1005
  }
1270
1006
  }
1271
- // Stop matter server nodes
1272
1007
  this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
1273
1008
  if (timeout > 0) {
1274
1009
  this.log.debug(`Waiting ${timeout}ms for the MessageExchange to finish...`);
@@ -1295,7 +1030,6 @@ export class Matterbridge extends EventEmitter {
1295
1030
  }
1296
1031
  }
1297
1032
  this.log.notice('Stopped matter server nodes');
1298
- // Matter commisioning reset
1299
1033
  if (message === 'shutting down with reset...') {
1300
1034
  this.log.info('Resetting Matterbridge commissioning information...');
1301
1035
  await this.matterStorageManager?.createContext('events')?.clearAll();
@@ -1305,7 +1039,6 @@ export class Matterbridge extends EventEmitter {
1305
1039
  await this.matterbridgeContext?.clearAll();
1306
1040
  this.log.info('Matter storage reset done! Remove the bridge from the controller.');
1307
1041
  }
1308
- // Unregister all devices
1309
1042
  if (message === 'unregistered all devices and shutting down...') {
1310
1043
  if (this.bridgeMode === 'bridge') {
1311
1044
  await this.matterStorageManager?.createContext('root')?.createContext('parts')?.createContext('Matterbridge')?.createContext('parts')?.clearAll();
@@ -1323,29 +1056,15 @@ export class Matterbridge extends EventEmitter {
1323
1056
  }
1324
1057
  this.log.info('Matter storage reset done!');
1325
1058
  }
1326
- // Stop matter storage
1327
1059
  await this.stopMatterStorage();
1328
- // Stop the frontend
1329
1060
  await this.frontend.stop();
1330
- // Close the matterbridge node storage and context
1061
+ this.frontend.destroy();
1062
+ this.plugins.destroy();
1063
+ this.devices.destroy();
1331
1064
  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
1065
  this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
1346
1066
  await this.nodeContext.close();
1347
1067
  this.nodeContext = undefined;
1348
- // Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
1349
1068
  for (const plugin of this.plugins) {
1350
1069
  if (plugin.nodeContext) {
1351
1070
  this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
@@ -1362,10 +1081,8 @@ export class Matterbridge extends EventEmitter {
1362
1081
  }
1363
1082
  this.plugins.clear();
1364
1083
  this.devices.clear();
1365
- // Factory reset
1366
1084
  if (message === 'shutting down with factory reset...') {
1367
1085
  try {
1368
- // Delete matter storage directory with its subdirectories and backup
1369
1086
  const dir = path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME);
1370
1087
  this.log.info(`Removing matter storage directory: ${dir}`);
1371
1088
  await fs.rm(dir, { recursive: true });
@@ -1374,13 +1091,11 @@ export class Matterbridge extends EventEmitter {
1374
1091
  await fs.rm(backup, { recursive: true });
1375
1092
  }
1376
1093
  catch (error) {
1377
- // istanbul ignore next if
1378
1094
  if (error instanceof Error && error.code !== 'ENOENT') {
1379
1095
  this.log.error(`Error removing matter storage directory: ${error}`);
1380
1096
  }
1381
1097
  }
1382
1098
  try {
1383
- // Delete matterbridge storage directory with its subdirectories and backup
1384
1099
  const dir = path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR);
1385
1100
  this.log.info(`Removing matterbridge storage directory: ${dir}`);
1386
1101
  await fs.rm(dir, { recursive: true });
@@ -1389,20 +1104,18 @@ export class Matterbridge extends EventEmitter {
1389
1104
  await fs.rm(backup, { recursive: true });
1390
1105
  }
1391
1106
  catch (error) {
1392
- // istanbul ignore next if
1393
1107
  if (error instanceof Error && error.code !== 'ENOENT') {
1394
1108
  this.log.error(`Error removing matterbridge storage directory: ${error}`);
1395
1109
  }
1396
1110
  }
1397
1111
  this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
1398
1112
  }
1399
- // Deregisters the process handlers
1400
1113
  this.deregisterProcessHandlers();
1401
1114
  if (restart) {
1402
1115
  if (message === 'updating...') {
1403
1116
  this.log.info('Cleanup completed. Updating...');
1404
1117
  Matterbridge.instance = undefined;
1405
- this.emit('update'); // Restart the process but the update has been done before. TODO move all updates to the cli
1118
+ this.emit('update');
1406
1119
  }
1407
1120
  else if (message === 'restarting...') {
1408
1121
  this.log.info('Cleanup completed. Restarting...');
@@ -1426,14 +1139,7 @@ export class Matterbridge extends EventEmitter {
1426
1139
  this.log.debug('Cleanup already started...');
1427
1140
  }
1428
1141
  }
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
1142
  async startBridge() {
1436
- // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1437
1143
  if (!this.matterStorageManager)
1438
1144
  throw new Error('No storage manager initialized');
1439
1145
  if (!this.matterbridgeContext)
@@ -1472,16 +1178,13 @@ export class Matterbridge extends EventEmitter {
1472
1178
  clearInterval(this.startMatterInterval);
1473
1179
  this.startMatterInterval = undefined;
1474
1180
  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'
1181
+ this.startServerNode(this.serverNode);
1478
1182
  for (const device of this.devices.array()) {
1479
1183
  if (device.mode === 'server' && device.serverNode) {
1480
1184
  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
1185
+ this.startServerNode(device.serverNode);
1482
1186
  }
1483
1187
  }
1484
- // Configure the plugins
1485
1188
  this.configureTimeout = setTimeout(async () => {
1486
1189
  for (const plugin of this.plugins.array()) {
1487
1190
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1499,40 +1202,28 @@ export class Matterbridge extends EventEmitter {
1499
1202
  }
1500
1203
  this.frontend.wssSendRefreshRequired('plugins');
1501
1204
  }, 30 * 1000).unref();
1502
- // Setting reachability to true
1503
1205
  this.reachabilityTimeout = setTimeout(() => {
1504
1206
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1505
1207
  if (this.aggregatorNode)
1506
1208
  this.setAggregatorReachability(this.aggregatorNode, true);
1507
1209
  }, 60 * 1000).unref();
1508
- // Logger.get('LogServerNode').info(this.serverNode);
1509
1210
  this.emit('bridge_started');
1510
1211
  this.log.notice('Matterbridge bridge started successfully');
1511
1212
  this.frontend.wssSendRefreshRequired('settings');
1512
1213
  this.frontend.wssSendRefreshRequired('plugins');
1513
1214
  }, this.startMatterIntervalMs);
1514
1215
  }
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
1216
  async startChildbridge(delay = 1000) {
1523
1217
  if (!this.matterStorageManager)
1524
1218
  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
1219
  this.log.debug('Loading all plugins in childbridge mode...');
1527
1220
  await this.startPlugins(true, false);
1528
- // Create server nodes for DynamicPlatform plugins and start all plugins in the background
1529
1221
  this.log.debug('Creating server nodes for DynamicPlatform plugins and starting all plugins in childbridge mode...');
1530
1222
  for (const plugin of this.plugins.array().filter((p) => p.enabled && !p.error)) {
1531
1223
  if (plugin.type === 'DynamicPlatform')
1532
1224
  await this.createDynamicPlugin(plugin);
1533
- this.plugins.start(plugin, 'Matterbridge is starting'); // Start the plugin in the background
1225
+ this.plugins.start(plugin, 'Matterbridge is starting');
1534
1226
  }
1535
- // Start the Matterbridge in childbridge mode when all plugins are loaded and started
1536
1227
  this.log.debug('Starting start matter interval in childbridge mode...');
1537
1228
  let failCount = 0;
1538
1229
  this.startMatterInterval = setInterval(async () => {
@@ -1566,9 +1257,8 @@ export class Matterbridge extends EventEmitter {
1566
1257
  clearInterval(this.startMatterInterval);
1567
1258
  this.startMatterInterval = undefined;
1568
1259
  if (delay > 0)
1569
- await wait(delay); // Wait for the specified delay to ensure all plugins server nodes are ready
1260
+ await wait(delay);
1570
1261
  this.log.debug('Cleared startMatterInterval interval in childbridge mode');
1571
- // Configure the plugins
1572
1262
  this.configureTimeout = setTimeout(async () => {
1573
1263
  for (const plugin of this.plugins.array()) {
1574
1264
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1593,7 +1283,6 @@ export class Matterbridge extends EventEmitter {
1593
1283
  this.log.error(`Plugin ${plg}${plugin.name}${er} didn't register any devices to Matterbridge. Verify the plugin configuration.`);
1594
1284
  continue;
1595
1285
  }
1596
- // istanbul ignore next if cause is just a safety check
1597
1286
  if (!plugin.serverNode) {
1598
1287
  this.log.error(`Server node not found for plugin ${plg}${plugin.name}${er}`);
1599
1288
  continue;
@@ -1606,252 +1295,28 @@ export class Matterbridge extends EventEmitter {
1606
1295
  this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
1607
1296
  continue;
1608
1297
  }
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
1298
+ this.startServerNode(plugin.serverNode);
1612
1299
  plugin.reachabilityTimeout = setTimeout(() => {
1613
1300
  this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf}`);
1614
1301
  if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
1615
1302
  this.setAggregatorReachability(plugin.aggregatorNode, true);
1616
1303
  }, 60 * 1000).unref();
1617
1304
  }
1618
- // Start the Matter server node of single devices in mode 'server'
1619
1305
  for (const device of this.devices.array()) {
1620
1306
  if (device.mode === 'server' && device.serverNode) {
1621
1307
  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
1308
+ this.startServerNode(device.serverNode);
1623
1309
  }
1624
1310
  }
1625
- // Logger.get('LogServerNode').info(this.serverNode);
1626
1311
  this.emit('childbridge_started');
1627
1312
  this.log.notice('Matterbridge childbridge started successfully');
1628
1313
  this.frontend.wssSendRefreshRequired('settings');
1629
1314
  this.frontend.wssSendRefreshRequired('plugins');
1630
1315
  }, this.startMatterIntervalMs);
1631
1316
  }
1632
- /**
1633
- * Starts the Matterbridge controller.
1634
- *
1635
- * @private
1636
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1637
- */
1638
1317
  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
1318
  }
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
1319
  async startMatterStorage() {
1854
- // Setup Matter storage
1855
1320
  this.log.info(`Starting matter node storage...`);
1856
1321
  this.matterStorageService = this.environment.get(StorageService);
1857
1322
  this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
@@ -1859,17 +1324,8 @@ export class Matterbridge extends EventEmitter {
1859
1324
  this.log.info('Matter node storage manager "Matterbridge" created');
1860
1325
  this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName, this.aggregatorSerialNumber, this.aggregatorUniqueId);
1861
1326
  this.log.info('Matter node storage started');
1862
- // Backup matter storage since it is created/opened correctly
1863
1327
  await this.backupMatterStorage(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME), path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME + '.backup'));
1864
1328
  }
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
1329
  async backupMatterStorage(storageName, backupName) {
1874
1330
  this.log.info('Creating matter node storage backup...');
1875
1331
  try {
@@ -1880,11 +1336,6 @@ export class Matterbridge extends EventEmitter {
1880
1336
  this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
1881
1337
  }
1882
1338
  }
1883
- /**
1884
- * Stops the matter storage.
1885
- *
1886
- * @returns {Promise<void>} A promise that resolves when the storage is stopped.
1887
- */
1888
1339
  async stopMatterStorage() {
1889
1340
  this.log.info('Closing matter node storage...');
1890
1341
  await this.matterStorageManager?.close();
@@ -1893,20 +1344,6 @@ export class Matterbridge extends EventEmitter {
1893
1344
  this.matterbridgeContext = undefined;
1894
1345
  this.log.info('Matter node storage closed');
1895
1346
  }
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
1347
  async createServerNodeContext(storeId, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId) {
1911
1348
  const { randomBytes } = await import('node:crypto');
1912
1349
  if (!this.matterStorageService)
@@ -1946,15 +1383,6 @@ export class Matterbridge extends EventEmitter {
1946
1383
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1947
1384
  return storageContext;
1948
1385
  }
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
1386
  async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
1959
1387
  const storeId = await storageContext.get('storeId');
1960
1388
  this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
@@ -1964,37 +1392,24 @@ export class Matterbridge extends EventEmitter {
1964
1392
  this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
1965
1393
  this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1966
1394
  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
1395
  const serverNode = await ServerNode.create({
1971
- // Required: Give the Node a unique ID which is used to store the state of this node
1972
1396
  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
1397
  network: {
1976
- listeningAddressIpv4: this.ipv4address,
1977
- listeningAddressIpv6: this.ipv6address,
1398
+ listeningAddressIpv4: this.ipv4Address,
1399
+ listeningAddressIpv6: this.ipv6Address,
1978
1400
  port,
1979
1401
  },
1980
- // Provide the certificate for the device
1981
1402
  operationalCredentials: {
1982
1403
  certification: this.certification,
1983
1404
  },
1984
- // Provide Commissioning relevant settings
1985
- // Optional for development/testing purposes
1986
1405
  commissioning: {
1987
1406
  passcode,
1988
1407
  discriminator,
1989
1408
  },
1990
- // Provide Node announcement settings
1991
- // Optional: If Ommitted some development defaults are used
1992
1409
  productDescription: {
1993
1410
  name: await storageContext.get('deviceName'),
1994
1411
  deviceType: DeviceTypeId(await storageContext.get('deviceType')),
1995
1412
  },
1996
- // Provide defaults for the BasicInformation cluster on the Root endpoint
1997
- // Optional: If Omitted some development defaults are used
1998
1413
  basicInformation: {
1999
1414
  vendorId: VendorId(await storageContext.get('vendorId')),
2000
1415
  vendorName: await storageContext.get('vendorName'),
@@ -2011,23 +1426,17 @@ export class Matterbridge extends EventEmitter {
2011
1426
  reachable: true,
2012
1427
  },
2013
1428
  });
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
1429
  serverNode.lifecycle.commissioned.on(() => {
2019
1430
  this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
2020
1431
  this.advertisingNodes.delete(storeId);
2021
1432
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
2022
1433
  });
2023
- /** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
2024
1434
  serverNode.lifecycle.decommissioned.on(() => {
2025
1435
  this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`);
2026
1436
  this.advertisingNodes.delete(storeId);
2027
1437
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
2028
1438
  this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
2029
1439
  });
2030
- /** This event is triggered when the device went online. This means that it is discoverable in the network. */
2031
1440
  serverNode.lifecycle.online.on(async () => {
2032
1441
  this.log.notice(`Server node for ${storeId} is online`);
2033
1442
  if (!serverNode.lifecycle.isCommissioned) {
@@ -2038,16 +1447,13 @@ export class Matterbridge extends EventEmitter {
2038
1447
  this.log.notice(`Manual pairing code: ${manualPairingCode}`);
2039
1448
  }
2040
1449
  else {
2041
- // istanbul ignore next
2042
1450
  this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
2043
- // istanbul ignore next
2044
1451
  this.advertisingNodes.delete(storeId);
2045
1452
  }
2046
1453
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
2047
1454
  this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
2048
1455
  this.emit('online', storeId);
2049
1456
  });
2050
- /** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
2051
1457
  serverNode.lifecycle.offline.on(() => {
2052
1458
  this.log.notice(`Server node for ${storeId} is offline`);
2053
1459
  this.advertisingNodes.delete(storeId);
@@ -2055,15 +1461,11 @@ export class Matterbridge extends EventEmitter {
2055
1461
  this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
2056
1462
  this.emit('offline', storeId);
2057
1463
  });
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
1464
  serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
2063
1465
  let action = '';
2064
1466
  switch (fabricAction) {
2065
1467
  case FabricAction.Added:
2066
- this.advertisingNodes.delete(storeId); // The advertising stops when a fabric is added
1468
+ this.advertisingNodes.delete(storeId);
2067
1469
  action = 'added';
2068
1470
  break;
2069
1471
  case FabricAction.Removed:
@@ -2076,22 +1478,14 @@ export class Matterbridge extends EventEmitter {
2076
1478
  this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
2077
1479
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
2078
1480
  });
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
1481
  serverNode.events.sessions.opened.on((session) => {
2084
1482
  this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
2085
1483
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
2086
1484
  });
2087
- /**
2088
- * This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
2089
- */
2090
1485
  serverNode.events.sessions.closed.on((session) => {
2091
1486
  this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
2092
1487
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
2093
1488
  });
2094
- /** This event is triggered when a subscription gets added or removed on an operative session. */
2095
1489
  serverNode.events.sessions.subscriptionsChanged.on((session) => {
2096
1490
  this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
2097
1491
  this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
@@ -2099,12 +1493,6 @@ export class Matterbridge extends EventEmitter {
2099
1493
  this.log.info(`Created server node for ${storeId}`);
2100
1494
  return serverNode;
2101
1495
  }
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
1496
  getServerNodeData(serverNode) {
2109
1497
  const advertiseTime = this.advertisingNodes.get(serverNode.id) || 0;
2110
1498
  return {
@@ -2121,25 +1509,12 @@ export class Matterbridge extends EventEmitter {
2121
1509
  serialNumber: serverNode.state.basicInformation.serialNumber,
2122
1510
  };
2123
1511
  }
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
1512
  async startServerNode(matterServerNode) {
2131
1513
  if (!matterServerNode)
2132
1514
  return;
2133
1515
  this.log.notice(`Starting ${matterServerNode.id} server node`);
2134
1516
  await matterServerNode.start();
2135
1517
  }
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
1518
  async stopServerNode(matterServerNode, timeout = 30000) {
2144
1519
  if (!matterServerNode)
2145
1520
  return;
@@ -2152,25 +1527,12 @@ export class Matterbridge extends EventEmitter {
2152
1527
  this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
2153
1528
  }
2154
1529
  }
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
1530
  async createAggregatorNode(storageContext) {
2162
1531
  this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
2163
1532
  const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
2164
1533
  this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
2165
1534
  return aggregatorNode;
2166
1535
  }
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
1536
  async createAccessoryPlugin(plugin, device) {
2175
1537
  if (!plugin.locked && device.deviceType && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
2176
1538
  plugin.locked = true;
@@ -2182,12 +1544,6 @@ export class Matterbridge extends EventEmitter {
2182
1544
  await plugin.serverNode.add(device);
2183
1545
  }
2184
1546
  }
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
1547
  async createDynamicPlugin(plugin) {
2192
1548
  if (!plugin.locked) {
2193
1549
  plugin.locked = true;
@@ -2200,13 +1556,6 @@ export class Matterbridge extends EventEmitter {
2200
1556
  await plugin.serverNode.add(plugin.aggregatorNode);
2201
1557
  }
2202
1558
  }
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
1559
  async createDeviceServerNode(plugin, device) {
2211
1560
  if (device.mode === 'server' && !device.serverNode && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
2212
1561
  this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
@@ -2217,15 +1566,7 @@ export class Matterbridge extends EventEmitter {
2217
1566
  this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
2218
1567
  }
2219
1568
  }
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
1569
  async addBridgedEndpoint(pluginName, device) {
2228
- // Check if the plugin is registered
2229
1570
  const plugin = this.plugins.get(pluginName);
2230
1571
  if (!plugin) {
2231
1572
  this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
@@ -2245,7 +1586,6 @@ export class Matterbridge extends EventEmitter {
2245
1586
  }
2246
1587
  else if (this.bridgeMode === 'bridge') {
2247
1588
  if (device.mode === 'matter') {
2248
- // Register and add the device to the matterbridge server node
2249
1589
  this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge server node...`);
2250
1590
  if (!this.serverNode) {
2251
1591
  this.log.error('Server node not found for Matterbridge');
@@ -2262,7 +1602,6 @@ export class Matterbridge extends EventEmitter {
2262
1602
  }
2263
1603
  }
2264
1604
  else {
2265
- // Register and add the device to the matterbridge aggregator node
2266
1605
  this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
2267
1606
  if (!this.aggregatorNode) {
2268
1607
  this.log.error('Aggregator node not found for Matterbridge');
@@ -2280,7 +1619,6 @@ export class Matterbridge extends EventEmitter {
2280
1619
  }
2281
1620
  }
2282
1621
  else if (this.bridgeMode === 'childbridge') {
2283
- // Register and add the device to the plugin server node
2284
1622
  if (plugin.type === 'AccessoryPlatform') {
2285
1623
  try {
2286
1624
  this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
@@ -2304,12 +1642,10 @@ export class Matterbridge extends EventEmitter {
2304
1642
  return;
2305
1643
  }
2306
1644
  }
2307
- // Register and add the device to the plugin aggregator node
2308
1645
  if (plugin.type === 'DynamicPlatform') {
2309
1646
  try {
2310
1647
  this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
2311
1648
  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
1649
  await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
2314
1650
  if (!plugin.aggregatorNode) {
2315
1651
  this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
@@ -2330,28 +1666,17 @@ export class Matterbridge extends EventEmitter {
2330
1666
  }
2331
1667
  if (plugin.registeredDevices !== undefined)
2332
1668
  plugin.registeredDevices++;
2333
- // Add the device to the DeviceManager
2334
1669
  this.devices.set(device);
2335
- // Subscribe to the attributes changed event
2336
1670
  await this.subscribeAttributeChanged(plugin, device);
2337
1671
  this.log.info(`Added and registered bridged endpoint (${plugin.registeredDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
2338
1672
  }
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
1673
  async removeBridgedEndpoint(pluginName, device) {
2347
1674
  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
1675
  const plugin = this.plugins.get(pluginName);
2350
1676
  if (!plugin) {
2351
1677
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
2352
1678
  return;
2353
1679
  }
2354
- // Register and add the device to the matterbridge aggregator node
2355
1680
  if (this.bridgeMode === 'bridge') {
2356
1681
  if (!this.aggregatorNode) {
2357
1682
  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 +1689,6 @@ export class Matterbridge extends EventEmitter {
2364
1689
  }
2365
1690
  else if (this.bridgeMode === 'childbridge') {
2366
1691
  if (plugin.type === 'AccessoryPlatform') {
2367
- // Nothing to do here since the server node has no aggregator node but only the device itself
2368
1692
  }
2369
1693
  else if (plugin.type === 'DynamicPlatform') {
2370
1694
  if (!plugin.aggregatorNode) {
@@ -2377,21 +1701,8 @@ export class Matterbridge extends EventEmitter {
2377
1701
  if (plugin.registeredDevices !== undefined)
2378
1702
  plugin.registeredDevices--;
2379
1703
  }
2380
- // Remove the device from the DeviceManager
2381
1704
  this.devices.remove(device);
2382
1705
  }
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
1706
  async removeAllBridgedEndpoints(pluginName, delay = 0) {
2396
1707
  this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
2397
1708
  for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
@@ -2402,24 +1713,13 @@ export class Matterbridge extends EventEmitter {
2402
1713
  if (delay > 0)
2403
1714
  await wait(2000);
2404
1715
  }
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
1716
  async subscribeAttributeChanged(plugin, device) {
2415
1717
  if (!plugin || !device || !device.plugin || !device.serialNumber || !device.uniqueId || !device.maybeNumber)
2416
1718
  return;
2417
1719
  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
1720
  if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
2420
1721
  plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
2421
1722
  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
1723
  this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, 'BasicInformation', 'reachable', reachable);
2424
1724
  });
2425
1725
  }
@@ -2467,7 +1767,6 @@ export class Matterbridge extends EventEmitter {
2467
1767
  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
1768
  await device.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
2469
1769
  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
1770
  this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, sub.cluster, sub.attribute, value);
2472
1771
  });
2473
1772
  }
@@ -2477,7 +1776,6 @@ export class Matterbridge extends EventEmitter {
2477
1776
  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
1777
  await child.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
2479
1778
  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
1779
  this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, child.number, child.id, sub.cluster, sub.attribute, value);
2482
1780
  });
2483
1781
  }
@@ -2485,12 +1783,6 @@ export class Matterbridge extends EventEmitter {
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