matterbridge 3.1.2 → 3.1.3-dev-20250714-c9b85b3

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 (208) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +8 -0
  3. package/dist/cli.js +2 -91
  4. package/dist/cliEmitter.js +0 -30
  5. package/dist/clusters/export.js +0 -2
  6. package/dist/defaultConfigSchema.js +0 -24
  7. package/dist/deviceManager.js +1 -94
  8. package/dist/devices/batteryStorage.js +1 -48
  9. package/dist/devices/evse.js +10 -74
  10. package/dist/devices/export.js +0 -2
  11. package/dist/devices/heatPump.js +2 -50
  12. package/dist/devices/laundryDryer.js +6 -83
  13. package/dist/devices/laundryWasher.js +7 -91
  14. package/dist/devices/roboticVacuumCleaner.js +6 -89
  15. package/dist/devices/solarPower.js +0 -38
  16. package/dist/devices/waterHeater.js +2 -82
  17. package/dist/frontend.js +33 -430
  18. package/dist/globalMatterbridge.js +0 -47
  19. package/dist/helpers.js +0 -53
  20. package/dist/index.js +1 -30
  21. package/dist/logger/export.js +0 -1
  22. package/dist/matter/behaviors.js +0 -2
  23. package/dist/matter/clusters.js +0 -2
  24. package/dist/matter/devices.js +0 -2
  25. package/dist/matter/endpoints.js +0 -2
  26. package/dist/matter/export.js +0 -3
  27. package/dist/matter/types.js +0 -3
  28. package/dist/matterbridge.js +77 -911
  29. package/dist/matterbridgeAccessoryPlatform.js +0 -36
  30. package/dist/matterbridgeBehaviors.js +1 -61
  31. package/dist/matterbridgeDeviceTypes.js +15 -579
  32. package/dist/matterbridgeDynamicPlatform.js +0 -36
  33. package/dist/matterbridgeEndpoint.js +51 -1053
  34. package/dist/matterbridgeEndpointHelpers.js +12 -322
  35. package/dist/matterbridgePlatform.js +0 -233
  36. package/dist/matterbridgeTypes.js +0 -25
  37. package/dist/pluginManager.js +3 -271
  38. package/dist/shelly.js +7 -168
  39. package/dist/storage/export.js +0 -1
  40. package/dist/update.js +0 -54
  41. package/dist/utils/colorUtils.js +2 -263
  42. package/dist/utils/commandLine.js +0 -54
  43. package/dist/utils/copyDirectory.js +1 -38
  44. package/dist/utils/createDirectory.js +0 -33
  45. package/dist/utils/createZip.js +2 -47
  46. package/dist/utils/deepCopy.js +0 -39
  47. package/dist/utils/deepEqual.js +1 -72
  48. package/dist/utils/export.js +0 -1
  49. package/dist/utils/hex.js +0 -58
  50. package/dist/utils/isvalid.js +0 -101
  51. package/dist/utils/network.js +5 -83
  52. package/dist/utils/spawn.js +0 -18
  53. package/dist/utils/wait.js +9 -62
  54. package/npm-shrinkwrap.json +5 -5
  55. package/package.json +1 -2
  56. package/dist/cli.d.ts +0 -26
  57. package/dist/cli.d.ts.map +0 -1
  58. package/dist/cli.js.map +0 -1
  59. package/dist/cliEmitter.d.ts +0 -34
  60. package/dist/cliEmitter.d.ts.map +0 -1
  61. package/dist/cliEmitter.js.map +0 -1
  62. package/dist/clusters/export.d.ts +0 -2
  63. package/dist/clusters/export.d.ts.map +0 -1
  64. package/dist/clusters/export.js.map +0 -1
  65. package/dist/defaultConfigSchema.d.ts +0 -28
  66. package/dist/defaultConfigSchema.d.ts.map +0 -1
  67. package/dist/defaultConfigSchema.js.map +0 -1
  68. package/dist/deviceManager.d.ts +0 -112
  69. package/dist/deviceManager.d.ts.map +0 -1
  70. package/dist/deviceManager.js.map +0 -1
  71. package/dist/devices/batteryStorage.d.ts +0 -48
  72. package/dist/devices/batteryStorage.d.ts.map +0 -1
  73. package/dist/devices/batteryStorage.js.map +0 -1
  74. package/dist/devices/evse.d.ts +0 -75
  75. package/dist/devices/evse.d.ts.map +0 -1
  76. package/dist/devices/evse.js.map +0 -1
  77. package/dist/devices/export.d.ts +0 -9
  78. package/dist/devices/export.d.ts.map +0 -1
  79. package/dist/devices/export.js.map +0 -1
  80. package/dist/devices/heatPump.d.ts +0 -47
  81. package/dist/devices/heatPump.d.ts.map +0 -1
  82. package/dist/devices/heatPump.js.map +0 -1
  83. package/dist/devices/laundryDryer.d.ts +0 -87
  84. package/dist/devices/laundryDryer.d.ts.map +0 -1
  85. package/dist/devices/laundryDryer.js.map +0 -1
  86. package/dist/devices/laundryWasher.d.ts +0 -242
  87. package/dist/devices/laundryWasher.d.ts.map +0 -1
  88. package/dist/devices/laundryWasher.js.map +0 -1
  89. package/dist/devices/roboticVacuumCleaner.d.ts +0 -110
  90. package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
  91. package/dist/devices/roboticVacuumCleaner.js.map +0 -1
  92. package/dist/devices/solarPower.d.ts +0 -40
  93. package/dist/devices/solarPower.d.ts.map +0 -1
  94. package/dist/devices/solarPower.js.map +0 -1
  95. package/dist/devices/waterHeater.d.ts +0 -111
  96. package/dist/devices/waterHeater.d.ts.map +0 -1
  97. package/dist/devices/waterHeater.js.map +0 -1
  98. package/dist/frontend.d.ts +0 -303
  99. package/dist/frontend.d.ts.map +0 -1
  100. package/dist/frontend.js.map +0 -1
  101. package/dist/globalMatterbridge.d.ts +0 -59
  102. package/dist/globalMatterbridge.d.ts.map +0 -1
  103. package/dist/globalMatterbridge.js.map +0 -1
  104. package/dist/helpers.d.ts +0 -48
  105. package/dist/helpers.d.ts.map +0 -1
  106. package/dist/helpers.js.map +0 -1
  107. package/dist/index.d.ts +0 -33
  108. package/dist/index.d.ts.map +0 -1
  109. package/dist/index.js.map +0 -1
  110. package/dist/logger/export.d.ts +0 -2
  111. package/dist/logger/export.d.ts.map +0 -1
  112. package/dist/logger/export.js.map +0 -1
  113. package/dist/matter/behaviors.d.ts +0 -2
  114. package/dist/matter/behaviors.d.ts.map +0 -1
  115. package/dist/matter/behaviors.js.map +0 -1
  116. package/dist/matter/clusters.d.ts +0 -2
  117. package/dist/matter/clusters.d.ts.map +0 -1
  118. package/dist/matter/clusters.js.map +0 -1
  119. package/dist/matter/devices.d.ts +0 -2
  120. package/dist/matter/devices.d.ts.map +0 -1
  121. package/dist/matter/devices.js.map +0 -1
  122. package/dist/matter/endpoints.d.ts +0 -2
  123. package/dist/matter/endpoints.d.ts.map +0 -1
  124. package/dist/matter/endpoints.js.map +0 -1
  125. package/dist/matter/export.d.ts +0 -5
  126. package/dist/matter/export.d.ts.map +0 -1
  127. package/dist/matter/export.js.map +0 -1
  128. package/dist/matter/types.d.ts +0 -3
  129. package/dist/matter/types.d.ts.map +0 -1
  130. package/dist/matter/types.js.map +0 -1
  131. package/dist/matterbridge.d.ts +0 -450
  132. package/dist/matterbridge.d.ts.map +0 -1
  133. package/dist/matterbridge.js.map +0 -1
  134. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
  135. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  136. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  137. package/dist/matterbridgeBehaviors.d.ts +0 -1340
  138. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  139. package/dist/matterbridgeBehaviors.js.map +0 -1
  140. package/dist/matterbridgeDeviceTypes.d.ts +0 -709
  141. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  142. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  143. package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
  144. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  145. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  146. package/dist/matterbridgeEndpoint.d.ts +0 -1196
  147. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  148. package/dist/matterbridgeEndpoint.js.map +0 -1
  149. package/dist/matterbridgeEndpointHelpers.d.ts +0 -3198
  150. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  151. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  152. package/dist/matterbridgePlatform.d.ts +0 -310
  153. package/dist/matterbridgePlatform.d.ts.map +0 -1
  154. package/dist/matterbridgePlatform.js.map +0 -1
  155. package/dist/matterbridgeTypes.d.ts +0 -192
  156. package/dist/matterbridgeTypes.d.ts.map +0 -1
  157. package/dist/matterbridgeTypes.js.map +0 -1
  158. package/dist/pluginManager.d.ts +0 -291
  159. package/dist/pluginManager.d.ts.map +0 -1
  160. package/dist/pluginManager.js.map +0 -1
  161. package/dist/shelly.d.ts +0 -174
  162. package/dist/shelly.d.ts.map +0 -1
  163. package/dist/shelly.js.map +0 -1
  164. package/dist/storage/export.d.ts +0 -2
  165. package/dist/storage/export.d.ts.map +0 -1
  166. package/dist/storage/export.js.map +0 -1
  167. package/dist/update.d.ts +0 -59
  168. package/dist/update.d.ts.map +0 -1
  169. package/dist/update.js.map +0 -1
  170. package/dist/utils/colorUtils.d.ts +0 -117
  171. package/dist/utils/colorUtils.d.ts.map +0 -1
  172. package/dist/utils/colorUtils.js.map +0 -1
  173. package/dist/utils/commandLine.d.ts +0 -59
  174. package/dist/utils/commandLine.d.ts.map +0 -1
  175. package/dist/utils/commandLine.js.map +0 -1
  176. package/dist/utils/copyDirectory.d.ts +0 -33
  177. package/dist/utils/copyDirectory.d.ts.map +0 -1
  178. package/dist/utils/copyDirectory.js.map +0 -1
  179. package/dist/utils/createDirectory.d.ts +0 -34
  180. package/dist/utils/createDirectory.d.ts.map +0 -1
  181. package/dist/utils/createDirectory.js.map +0 -1
  182. package/dist/utils/createZip.d.ts +0 -39
  183. package/dist/utils/createZip.d.ts.map +0 -1
  184. package/dist/utils/createZip.js.map +0 -1
  185. package/dist/utils/deepCopy.d.ts +0 -32
  186. package/dist/utils/deepCopy.d.ts.map +0 -1
  187. package/dist/utils/deepCopy.js.map +0 -1
  188. package/dist/utils/deepEqual.d.ts +0 -54
  189. package/dist/utils/deepEqual.d.ts.map +0 -1
  190. package/dist/utils/deepEqual.js.map +0 -1
  191. package/dist/utils/export.d.ts +0 -12
  192. package/dist/utils/export.d.ts.map +0 -1
  193. package/dist/utils/export.js.map +0 -1
  194. package/dist/utils/hex.d.ts +0 -49
  195. package/dist/utils/hex.d.ts.map +0 -1
  196. package/dist/utils/hex.js.map +0 -1
  197. package/dist/utils/isvalid.d.ts +0 -103
  198. package/dist/utils/isvalid.d.ts.map +0 -1
  199. package/dist/utils/isvalid.js.map +0 -1
  200. package/dist/utils/network.d.ts +0 -76
  201. package/dist/utils/network.d.ts.map +0 -1
  202. package/dist/utils/network.js.map +0 -1
  203. package/dist/utils/spawn.d.ts +0 -11
  204. package/dist/utils/spawn.d.ts.map +0 -1
  205. package/dist/utils/spawn.js.map +0 -1
  206. package/dist/utils/wait.d.ts +0 -56
  207. package/dist/utils/wait.d.ts.map +0 -1
  208. package/dist/utils/wait.js.map +0 -1
@@ -1,43 +1,15 @@
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 } 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 { DeviceCommissioner, 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
12
  import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
40
- // Matterbridge
41
13
  import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout, waiter, isValidString, parseVersionString, isValidNumber, createDirectory } from './utils/export.js';
42
14
  import { dev, plg, typ } from './matterbridgeTypes.js';
43
15
  import { PluginManager } from './pluginManager.js';
@@ -46,9 +18,6 @@ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
46
18
  import { bridge } from './matterbridgeDeviceTypes.js';
47
19
  import { Frontend } from './frontend.js';
48
20
  import { addVirtualDevices } from './helpers.js';
49
- /**
50
- * Represents the Matterbridge application.
51
- */
52
21
  export class Matterbridge extends EventEmitter {
53
22
  systemInformation = {
54
23
  interfaceName: '',
@@ -88,6 +57,7 @@ export class Matterbridge extends EventEmitter {
88
57
  matterbridgeSessionInformations: [],
89
58
  matterbridgePaired: false,
90
59
  matterbridgeAdvertise: false,
60
+ matterbridgeEndAdvertise: false,
91
61
  bridgeMode: '',
92
62
  restartMode: '',
93
63
  virtualMode: 'outlet',
@@ -96,7 +66,7 @@ export class Matterbridge extends EventEmitter {
96
66
  shellySysUpdate: false,
97
67
  shellyMainUpdate: false,
98
68
  profile: getParameter('profile'),
99
- loggerLevel: "info" /* LogLevel.INFO */,
69
+ loggerLevel: "info",
100
70
  fileLogger: false,
101
71
  matterLoggerLevel: MatterLogLevel.INFO,
102
72
  matterFileLogger: false,
@@ -118,29 +88,21 @@ export class Matterbridge extends EventEmitter {
118
88
  matterbridgeVersion = '';
119
89
  matterbridgeLatestVersion = '';
120
90
  matterbridgeDevVersion = '';
121
- matterbridgeQrPairingCode;
122
- matterbridgeManualPairingCode;
123
- matterbridgeFabricInformations;
124
- matterbridgeSessionInformations;
125
- matterbridgePaired;
126
91
  bridgeMode = '';
127
92
  restartMode = '';
128
93
  profile = getParameter('profile');
129
94
  shutdown = false;
130
95
  edge = true;
131
96
  failCountLimit = hasParameter('shelly') ? 600 : 120;
132
- // Matterbridge log files
133
- log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
97
+ log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
134
98
  matterbridgeLoggerFile = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
135
99
  matterLoggerFile = 'matter' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
136
100
  plugins;
137
101
  devices;
138
102
  frontend = new Frontend(this);
139
- // Matterbridge storage
140
103
  nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
141
104
  nodeStorage;
142
105
  nodeContext;
143
- // Cleanup
144
106
  hasCleanupStarted = false;
145
107
  initialized = false;
146
108
  execRunningCount = 0;
@@ -154,23 +116,19 @@ export class Matterbridge extends EventEmitter {
154
116
  sigtermHandler;
155
117
  exceptionHandler;
156
118
  rejectionHandler;
157
- // Matter environment
158
119
  environment = Environment.default;
159
- // Matter storage
160
120
  matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
161
121
  matterStorageService;
162
122
  matterStorageManager;
163
123
  matterbridgeContext;
164
124
  controllerContext;
165
- // Matter parameters
166
- mdnsInterface; // matter server node mdnsInterface: e.g. 'eth0' or 'wlan0' or 'WiFi'
167
- ipv4address; // matter server node listeningAddressIpv4
168
- ipv6address; // matter server node listeningAddressIpv6
169
- port; // first server node port
170
- passcode; // first server node passcode
171
- discriminator; // first server node discriminator
172
- certification; // device certification
173
- // Matter nodes
125
+ mdnsInterface;
126
+ ipv4address;
127
+ ipv6address;
128
+ port;
129
+ passcode;
130
+ discriminator;
131
+ certification;
174
132
  serverNode;
175
133
  aggregatorNode;
176
134
  aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
@@ -178,31 +136,15 @@ export class Matterbridge extends EventEmitter {
178
136
  aggregatorProductId = getIntParameter('productId') ?? 0x8000;
179
137
  aggregatorProductName = getParameter('productName') ?? 'Matterbridge aggregator';
180
138
  static instance;
181
- // We load asyncronously so is private
182
139
  constructor() {
183
140
  super();
184
141
  }
185
- /**
186
- * Retrieves the list of Matterbridge devices.
187
- *
188
- * @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
189
- */
190
142
  getDevices() {
191
143
  return this.devices.array();
192
144
  }
193
- /**
194
- * Retrieves the list of registered plugins.
195
- *
196
- * @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
197
- */
198
145
  getPlugins() {
199
146
  return this.plugins.array();
200
147
  }
201
- /**
202
- * Set the logger logLevel for the Matterbridge classes and call onChangeLoggerLevel() for each plugin.
203
- *
204
- * @param {LogLevel} logLevel The logger logLevel to set.
205
- */
206
148
  async setLogLevel(logLevel) {
207
149
  if (this.log)
208
150
  this.log.logLevel = logLevel;
@@ -216,31 +158,19 @@ export class Matterbridge extends EventEmitter {
216
158
  for (const plugin of this.plugins) {
217
159
  if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
218
160
  continue;
219
- plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel;
220
- await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel);
221
- }
222
- // Set the global logger callback for the WebSocketServer to the common minimum logLevel
223
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
224
- if (this.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
225
- callbackLogLevel = "info" /* LogLevel.INFO */;
226
- if (this.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
227
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
161
+ plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
162
+ await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
163
+ }
164
+ let callbackLogLevel = "notice";
165
+ if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
166
+ callbackLogLevel = "info";
167
+ if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
168
+ callbackLogLevel = "debug";
228
169
  AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
229
170
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
230
171
  }
231
- //* ************************************************************************************************************************************ */
232
- // loadInstance() and cleanup() methods */
233
- //* ************************************************************************************************************************************ */
234
- /**
235
- * Loads an instance of the Matterbridge class.
236
- * If an instance already exists, return that instance.
237
- *
238
- * @param {boolean} initialize - Whether to initialize the Matterbridge instance after loading. Defaults to false.
239
- * @returns {Matterbridge} A promise that resolves to the Matterbridge instance.
240
- */
241
172
  static async loadInstance(initialize = false) {
242
173
  if (!Matterbridge.instance) {
243
- // eslint-disable-next-line no-console
244
174
  if (hasParameter('debug'))
245
175
  console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
246
176
  Matterbridge.instance = new Matterbridge();
@@ -249,17 +179,8 @@ export class Matterbridge extends EventEmitter {
249
179
  }
250
180
  return Matterbridge.instance;
251
181
  }
252
- /**
253
- * Call cleanup() and dispose MdnsService.
254
- *
255
- * @param {number} [timeout] - The timeout duration to wait for the cleanup to complete in milliseconds. Default is 1000.
256
- * @param {number} [pause] - The pause duration after the cleanup in milliseconds. Default is 250.
257
- *
258
- * @deprecated This method is deprecated and is ONLY used for jest tests.
259
- */
260
182
  async destroyInstance(timeout = 1000, pause = 250) {
261
183
  this.log.info(`Destroy instance...`);
262
- // Save server nodes to close
263
184
  const servers = [];
264
185
  if (this.bridgeMode === 'bridge') {
265
186
  if (this.serverNode)
@@ -277,109 +198,76 @@ export class Matterbridge extends EventEmitter {
277
198
  servers.push(device.serverNode);
278
199
  }
279
200
  }
280
- // Let any already‐queued microtasks run first
281
201
  await Promise.resolve();
282
- // Wait for the cleanup to finish
283
202
  await new Promise((resolve) => {
284
203
  setTimeout(resolve, pause);
285
204
  });
286
- // Cleanup
287
205
  await this.cleanup('destroying instance...', false, timeout);
288
- // Close servers mdns service
289
206
  this.log.info(`Dispose ${servers.length} MdnsService...`);
290
207
  for (const server of servers) {
291
208
  await server.env.get(MdnsService)[Symbol.asyncDispose]();
292
209
  this.log.info(`Closed ${server.id} MdnsService`);
293
210
  }
294
- // Let any already‐queued microtasks run first
295
211
  await Promise.resolve();
296
- // Wait for the cleanup to finish
297
212
  await new Promise((resolve) => {
298
213
  setTimeout(resolve, pause);
299
214
  });
300
215
  }
301
- /**
302
- * Initializes the Matterbridge application.
303
- *
304
- * @remarks
305
- * This method performs the necessary setup and initialization steps for the Matterbridge application.
306
- * It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
307
- * node version, registers signal handlers, initializes storage, and parses the command line.
308
- *
309
- * @returns {Promise<void>} A Promise that resolves when the initialization is complete.
310
- */
311
216
  async initialize() {
312
- // Emit the initialize_started event
313
217
  this.emit('initialize_started');
314
- // Set the restart mode
315
218
  if (hasParameter('service'))
316
219
  this.restartMode = 'service';
317
220
  if (hasParameter('docker'))
318
221
  this.restartMode = 'docker';
319
- // Set the matterbridge home directory
320
222
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
321
223
  this.matterbridgeInformation.homeDirectory = this.homeDirectory;
322
224
  await createDirectory(this.homeDirectory, 'Matterbridge Home Directory', this.log);
323
- // Set the matterbridge directory
324
225
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
325
226
  this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
326
227
  await createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory', this.log);
327
228
  await createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory', this.log);
328
229
  await createDirectory(path.join(this.matterbridgeDirectory, 'uploads'), 'Matterbridge Frontend Uploads Directory', this.log);
329
- // Set the matterbridge plugin directory
330
230
  this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
331
231
  this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
332
232
  await createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory', this.log);
333
- // Set the matterbridge cert directory
334
233
  this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
335
234
  this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
336
235
  await createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory', this.log);
337
- // Set the matterbridge root directory
338
236
  const { fileURLToPath } = await import('node:url');
339
237
  const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
340
238
  this.rootDirectory = path.resolve(currentFileDirectory, '../');
341
239
  this.matterbridgeInformation.rootDirectory = this.rootDirectory;
342
- // Setup the matter environment
343
240
  this.environment.vars.set('log.level', MatterLogLevel.INFO);
344
241
  this.environment.vars.set('log.format', MatterLogFormat.ANSI);
345
242
  this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
346
243
  this.environment.vars.set('runtime.signals', false);
347
244
  this.environment.vars.set('runtime.exitcode', false);
348
- // Register process handlers
349
245
  this.registerProcessHandlers();
350
- // Initialize nodeStorage and nodeContext
351
246
  try {
352
247
  this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
353
248
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
354
249
  this.log.debug('Creating node storage context for matterbridge');
355
250
  this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
356
- // TODO: Remove this code when node-persist-manager is updated
357
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
358
251
  const keys = (await this.nodeStorage?.storage.keys());
359
252
  for (const key of keys) {
360
253
  this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
361
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
362
254
  await this.nodeStorage?.storage.get(key);
363
255
  }
364
256
  const storages = await this.nodeStorage.getStorageNames();
365
257
  for (const storage of storages) {
366
258
  this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
367
259
  const nodeContext = await this.nodeStorage?.createStorage(storage);
368
- // TODO: Remove this code when node-persist-manager is updated
369
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
370
260
  const keys = (await nodeContext?.storage.keys());
371
261
  keys.forEach(async (key) => {
372
262
  this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
373
263
  await nodeContext?.get(key);
374
264
  });
375
265
  }
376
- // Creating a backup of the node storage since it is not corrupted
377
266
  this.log.debug('Creating node storage backup...');
378
267
  await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
379
268
  this.log.debug('Created node storage backup');
380
269
  }
381
270
  catch (error) {
382
- // Restoring the backup of the node storage since it is corrupted
383
271
  this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
384
272
  if (hasParameter('norestore')) {
385
273
  this.log.fatal(`The matterbridge storage is corrupted. Found -norestore parameter: exiting...`);
@@ -393,20 +281,14 @@ export class Matterbridge extends EventEmitter {
393
281
  if (!this.nodeStorage || !this.nodeContext) {
394
282
  throw new Error('Fatal error creating node storage manager and context for matterbridge');
395
283
  }
396
- // Set the first port to use for the commissioning server (will be incremented in childbridge mode)
397
284
  this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
398
- // Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
399
285
  this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode(this.environment.get(Crypto));
400
- // Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
401
286
  this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
402
- // Certificate management
403
287
  const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
404
288
  try {
405
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
406
289
  await fs.access(pairingFilePath, fs.constants.R_OK);
407
290
  const pairingFileContent = await fs.readFile(pairingFilePath, 'utf8');
408
291
  const pairingFileJson = JSON.parse(pairingFileContent);
409
- // Set the vendorId, vendorName, productId and productName if they are present in the pairing file
410
292
  if (isValidNumber(pairingFileJson.vendorId))
411
293
  this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
412
294
  if (isValidString(pairingFileJson.vendorName, 3))
@@ -415,73 +297,63 @@ export class Matterbridge extends EventEmitter {
415
297
  this.aggregatorProductId = pairingFileJson.productId;
416
298
  if (isValidString(pairingFileJson.productName, 3))
417
299
  this.aggregatorProductName = pairingFileJson.productName;
418
- // Override the passcode and discriminator if they are present in the pairing file
419
300
  if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
420
301
  this.passcode = pairingFileJson.passcode;
421
302
  this.discriminator = pairingFileJson.discriminator;
422
303
  this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
423
304
  }
424
- // Set the certification if it is present in the pairing file
425
- /*
426
305
  if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
427
- const hexStringToUint8Array = (hexString: string) => {
428
- const matches = hexString.match(/.{1,2}/g);
429
- return matches ? new Uint8Array(matches.map((byte) => parseInt(byte, 16))) : new Uint8Array();
430
- };
431
- // const hexString = Buffer.from('Test string', 'utf-8').toString('hex');
432
- // console.log(hexString, Buffer.from(hexStringToUint8Array(hexString)).toString('utf-8'));
433
-
434
- this.certification = {
435
- privateKey: hexStringToUint8Array(pairingFileJson.privateKey),
436
- certificate: hexStringToUint8Array(pairingFileJson.certificate),
437
- intermediateCertificate: hexStringToUint8Array(pairingFileJson.intermediateCertificate),
438
- declaration: hexStringToUint8Array(pairingFileJson.declaration),
439
- };
440
- this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using privateKey, certificate, intermediateCertificate and declaration from pairing file.`);
441
- }
442
- */
306
+ const hexStringToUint8Array = (hexString) => {
307
+ const matches = hexString.match(/.{1,2}/g);
308
+ return matches ? new Uint8Array(matches.map((byte) => parseInt(byte, 16))) : new Uint8Array();
309
+ };
310
+ this.certification = {
311
+ privateKey: hexStringToUint8Array(pairingFileJson.privateKey),
312
+ certificate: hexStringToUint8Array(pairingFileJson.certificate),
313
+ intermediateCertificate: hexStringToUint8Array(pairingFileJson.intermediateCertificate),
314
+ declaration: hexStringToUint8Array(pairingFileJson.declaration),
315
+ };
316
+ this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using privateKey, certificate, intermediateCertificate and declaration from pairing file.`);
317
+ }
443
318
  }
444
319
  catch (error) {
445
320
  this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
446
321
  }
447
- // Store the passcode, discriminator and port in the node context
448
322
  await this.nodeContext.set('matterport', this.port);
449
323
  await this.nodeContext.set('matterpasscode', this.passcode);
450
324
  await this.nodeContext.set('matterdiscriminator', this.discriminator);
451
325
  this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
452
- // Set matterbridge logger level (context: matterbridgeLogLevel)
453
326
  if (hasParameter('logger')) {
454
327
  const level = getParameter('logger');
455
328
  if (level === 'debug') {
456
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
329
+ this.log.logLevel = "debug";
457
330
  }
458
331
  else if (level === 'info') {
459
- this.log.logLevel = "info" /* LogLevel.INFO */;
332
+ this.log.logLevel = "info";
460
333
  }
461
334
  else if (level === 'notice') {
462
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
335
+ this.log.logLevel = "notice";
463
336
  }
464
337
  else if (level === 'warn') {
465
- this.log.logLevel = "warn" /* LogLevel.WARN */;
338
+ this.log.logLevel = "warn";
466
339
  }
467
340
  else if (level === 'error') {
468
- this.log.logLevel = "error" /* LogLevel.ERROR */;
341
+ this.log.logLevel = "error";
469
342
  }
470
343
  else if (level === 'fatal') {
471
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
344
+ this.log.logLevel = "fatal";
472
345
  }
473
346
  else {
474
347
  this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
475
- this.log.logLevel = "info" /* LogLevel.INFO */;
348
+ this.log.logLevel = "info";
476
349
  }
477
350
  }
478
351
  else {
479
- this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" /* LogLevel.NOTICE */ : "info" /* LogLevel.INFO */);
352
+ this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
480
353
  }
481
354
  this.frontend.logLevel = this.log.logLevel;
482
355
  MatterbridgeEndpoint.logLevel = this.log.logLevel;
483
356
  this.matterbridgeInformation.loggerLevel = this.log.logLevel;
484
- // Create the file logger for matterbridge (context: matterbridgeFileLog)
485
357
  if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
486
358
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbridgeLoggerFile), this.log.logLevel, true);
487
359
  this.matterbridgeInformation.fileLogger = true;
@@ -490,7 +362,6 @@ export class Matterbridge extends EventEmitter {
490
362
  this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
491
363
  if (this.profile !== undefined)
492
364
  this.log.debug(`Matterbridge profile: ${this.profile}.`);
493
- // Set matter.js logger level, format and logger (context: matterLogLevel)
494
365
  if (hasParameter('matterlogger')) {
495
366
  const level = getParameter('matterlogger');
496
367
  if (level === 'debug') {
@@ -521,9 +392,7 @@ export class Matterbridge extends EventEmitter {
521
392
  }
522
393
  Logger.format = MatterLogFormat.ANSI;
523
394
  Logger.setLogger('default', this.createMatterLogger());
524
- // Logger.destinations.default.write = this.createMatterLogger();
525
395
  this.matterbridgeInformation.matterLoggerLevel = Logger.level;
526
- // Create the file logger for matter.js (context: matterFileLog)
527
396
  if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
528
397
  this.matterbridgeInformation.matterFileLogger = true;
529
398
  Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
@@ -532,9 +401,7 @@ export class Matterbridge extends EventEmitter {
532
401
  });
533
402
  }
534
403
  this.log.debug(`Matter logLevel: ${Logger.level} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
535
- // Log network interfaces
536
404
  const networkInterfaces = os.networkInterfaces();
537
- // console.log(`Network interfaces:`, networkInterfaces);
538
405
  const availableAddresses = Object.entries(networkInterfaces);
539
406
  const availableInterfaces = Object.keys(networkInterfaces);
540
407
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -545,7 +412,6 @@ export class Matterbridge extends EventEmitter {
545
412
  });
546
413
  }
547
414
  }
548
- // Set the interface to use for matter server node mdnsInterface
549
415
  if (hasParameter('mdnsinterface')) {
550
416
  this.mdnsInterface = getParameter('mdnsinterface');
551
417
  }
@@ -554,7 +420,6 @@ export class Matterbridge extends EventEmitter {
554
420
  if (this.mdnsInterface === '')
555
421
  this.mdnsInterface = undefined;
556
422
  }
557
- // Validate mdnsInterface
558
423
  if (this.mdnsInterface) {
559
424
  if (!availableInterfaces.includes(this.mdnsInterface)) {
560
425
  this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
@@ -567,7 +432,6 @@ export class Matterbridge extends EventEmitter {
567
432
  }
568
433
  if (this.mdnsInterface)
569
434
  this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
570
- // Set the listeningAddressIpv4 for the matter commissioning server
571
435
  if (hasParameter('ipv4address')) {
572
436
  this.ipv4address = getParameter('ipv4address');
573
437
  }
@@ -576,7 +440,6 @@ export class Matterbridge extends EventEmitter {
576
440
  if (this.ipv4address === '')
577
441
  this.ipv4address = undefined;
578
442
  }
579
- // Validate ipv4address
580
443
  if (this.ipv4address) {
581
444
  let isValid = false;
582
445
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -592,7 +455,6 @@ export class Matterbridge extends EventEmitter {
592
455
  await this.nodeContext.remove('matteripv4address');
593
456
  }
594
457
  }
595
- // Set the listeningAddressIpv6 for the matter commissioning server
596
458
  if (hasParameter('ipv6address')) {
597
459
  this.ipv6address = getParameter('ipv6address');
598
460
  }
@@ -601,7 +463,6 @@ export class Matterbridge extends EventEmitter {
601
463
  if (this.ipv6address === '')
602
464
  this.ipv6address = undefined;
603
465
  }
604
- // Validate ipv6address
605
466
  if (this.ipv6address) {
606
467
  let isValid = false;
607
468
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -610,7 +471,6 @@ export class Matterbridge extends EventEmitter {
610
471
  isValid = true;
611
472
  break;
612
473
  }
613
- /* istanbul ignore next */
614
474
  if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6address)) {
615
475
  this.log.info(`Using ipv6address ${CYAN}${this.ipv6address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
616
476
  isValid = true;
@@ -623,7 +483,6 @@ export class Matterbridge extends EventEmitter {
623
483
  await this.nodeContext.remove('matteripv6address');
624
484
  }
625
485
  }
626
- // Initialize the virtual mode
627
486
  if (hasParameter('novirtual')) {
628
487
  this.matterbridgeInformation.virtualMode = 'disabled';
629
488
  await this.nodeContext.set('virtualmode', 'disabled');
@@ -632,19 +491,14 @@ export class Matterbridge extends EventEmitter {
632
491
  this.matterbridgeInformation.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
633
492
  }
634
493
  this.log.debug(`Virtual mode ${this.matterbridgeInformation.virtualMode}.`);
635
- // Initialize PluginManager
636
494
  this.plugins = new PluginManager(this);
637
495
  await this.plugins.loadFromStorage();
638
496
  this.plugins.logLevel = this.log.logLevel;
639
- // Initialize DeviceManager
640
497
  this.devices = new DeviceManager(this);
641
498
  this.devices.logLevel = this.log.logLevel;
642
- // Get the plugins from node storage and create the plugins node storage contexts
643
499
  for (const plugin of this.plugins) {
644
500
  const packageJson = await this.plugins.parse(plugin);
645
501
  if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
646
- // Try to reinstall the plugin from npm (for Docker pull and external plugins)
647
- // We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
648
502
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
649
503
  try {
650
504
  const { spawnCommand } = await import('./utils/spawn.js');
@@ -667,7 +521,6 @@ export class Matterbridge extends EventEmitter {
667
521
  await plugin.nodeContext.set('description', plugin.description);
668
522
  await plugin.nodeContext.set('author', plugin.author);
669
523
  }
670
- // Log system info and create .matterbridge directory
671
524
  await this.logNodeAndSystemInfo();
672
525
  this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
673
526
  `${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
@@ -675,7 +528,6 @@ export class Matterbridge extends EventEmitter {
675
528
  `${hasParameter('controller') ? 'mode controller ' : ''}` +
676
529
  `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
677
530
  `running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
678
- // Check node version and throw error
679
531
  const minNodeVersion = 18;
680
532
  const nodeVersion = process.versions.node;
681
533
  const versionMajor = parseInt(nodeVersion.split('.')[0]);
@@ -683,18 +535,10 @@ export class Matterbridge extends EventEmitter {
683
535
  this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
684
536
  throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
685
537
  }
686
- // Parse command line
687
538
  await this.parseCommandLine();
688
- // Emit the initialize_completed event
689
539
  this.emit('initialize_completed');
690
540
  this.initialized = true;
691
541
  }
692
- /**
693
- * Parses the command line arguments and performs the corresponding actions.
694
- *
695
- * @private
696
- * @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
697
- */
698
542
  async parseCommandLine() {
699
543
  if (hasParameter('help')) {
700
544
  this.log.info(`\nUsage: matterbridge [options]\n
@@ -746,28 +590,15 @@ export class Matterbridge extends EventEmitter {
746
590
  let index = 0;
747
591
  for (const plugin of this.plugins) {
748
592
  if (index !== this.plugins.length - 1) {
749
- this.log.info(`├─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${plugin.enabled ? GREEN : RED}enabled ${plugin.paired ? GREEN : RED}paired${nf}`);
593
+ this.log.info(`├─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${plugin.enabled ? GREEN : RED}enabled${nf}`);
750
594
  this.log.info(`│ └─ entry ${UNDERLINE}${db}${plugin.path}${UNDERLINEOFF}${db}`);
751
595
  }
752
596
  else {
753
- this.log.info(`└─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${plugin.enabled ? GREEN : RED}enabled ${plugin.paired ? GREEN : RED}paired${nf}`);
597
+ this.log.info(`└─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${plugin.enabled ? GREEN : RED}enabled${nf}`);
754
598
  this.log.info(` └─ entry ${UNDERLINE}${db}${plugin.path}${UNDERLINEOFF}${db}`);
755
599
  }
756
600
  index++;
757
601
  }
758
- /*
759
- const serializedRegisteredDevices = await this.nodeContext?.get<SerializedMatterbridgeEndpoint[]>('devices', []);
760
- this.log.info(`│ Registered devices (${serializedRegisteredDevices?.length})`);
761
- serializedRegisteredDevices?.forEach((device, index) => {
762
- if (index !== serializedRegisteredDevices.length - 1) {
763
- this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
764
- this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
765
- } else {
766
- this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
767
- this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
768
- }
769
- });
770
- */
771
602
  this.shutdown = true;
772
603
  return;
773
604
  }
@@ -818,7 +649,6 @@ export class Matterbridge extends EventEmitter {
818
649
  this.shutdown = true;
819
650
  return;
820
651
  }
821
- // Start the matter storage and create the matterbridge context
822
652
  try {
823
653
  await this.startMatterStorage();
824
654
  }
@@ -826,21 +656,18 @@ export class Matterbridge extends EventEmitter {
826
656
  this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
827
657
  throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
828
658
  }
829
- // Clear the matterbridge context if the reset parameter is set
830
659
  if (hasParameter('reset') && getParameter('reset') === undefined) {
831
660
  this.initialized = true;
832
661
  await this.shutdownProcessAndReset();
833
662
  this.shutdown = true;
834
663
  return;
835
664
  }
836
- // Clear matterbridge plugin context if the reset parameter is set
837
665
  if (hasParameter('reset') && getParameter('reset') !== undefined) {
838
666
  this.log.debug(`Reset plugin ${getParameter('reset')}`);
839
667
  const plugin = this.plugins.get(getParameter('reset'));
840
668
  if (plugin) {
841
669
  const matterStorageManager = await this.matterStorageService?.open(plugin.name);
842
670
  if (!matterStorageManager) {
843
- /* istanbul ignore next */
844
671
  this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
845
672
  }
846
673
  else {
@@ -859,39 +686,32 @@ export class Matterbridge extends EventEmitter {
859
686
  this.shutdown = true;
860
687
  return;
861
688
  }
862
- // Initialize frontend
863
689
  if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
864
690
  await this.frontend.start(getIntParameter('frontend'));
865
- // Check in 30 seconds the latest and dev versions of matterbridge and the plugins
866
691
  clearTimeout(this.checkUpdateTimeout);
867
692
  this.checkUpdateTimeout = setTimeout(async () => {
868
693
  const { checkUpdates } = await import('./update.js');
869
694
  checkUpdates(this);
870
695
  }, 30 * 1000).unref();
871
- // Check each 12 hours the latest and dev versions of matterbridge and the plugins
872
696
  clearInterval(this.checkUpdateInterval);
873
697
  this.checkUpdateInterval = setInterval(async () => {
874
698
  const { checkUpdates } = await import('./update.js');
875
699
  checkUpdates(this);
876
700
  }, 12 * 60 * 60 * 1000).unref();
877
- // Start the matterbridge in mode test
878
701
  if (hasParameter('test')) {
879
702
  this.bridgeMode = 'bridge';
880
703
  MatterbridgeEndpoint.bridgeMode = 'bridge';
881
704
  return;
882
705
  }
883
- // Start the matterbridge in mode controller
884
706
  if (hasParameter('controller')) {
885
707
  this.bridgeMode = 'controller';
886
708
  await this.startController();
887
709
  return;
888
710
  }
889
- // Check if the bridge mode is set and start matterbridge in bridge mode if not set
890
711
  if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
891
712
  this.log.info('Setting default matterbridge start mode to bridge');
892
713
  await this.nodeContext?.set('bridgeMode', 'bridge');
893
714
  }
894
- // Start matterbridge in bridge mode
895
715
  if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
896
716
  this.bridgeMode = 'bridge';
897
717
  MatterbridgeEndpoint.bridgeMode = 'bridge';
@@ -899,7 +719,6 @@ export class Matterbridge extends EventEmitter {
899
719
  await this.startBridge();
900
720
  return;
901
721
  }
902
- // Start matterbridge in childbridge mode
903
722
  if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
904
723
  this.bridgeMode = 'childbridge';
905
724
  MatterbridgeEndpoint.bridgeMode = 'childbridge';
@@ -908,20 +727,10 @@ export class Matterbridge extends EventEmitter {
908
727
  return;
909
728
  }
910
729
  }
911
- /**
912
- * Asynchronously loads and starts the registered plugins.
913
- *
914
- * This method is responsible for initializing and starting all enabled plugins.
915
- * It ensures that each plugin is properly loaded and started before the bridge starts.
916
- *
917
- * @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
918
- */
919
730
  async startPlugins() {
920
- // Check, load and start the plugins
921
731
  for (const plugin of this.plugins) {
922
732
  plugin.configJson = await this.plugins.loadConfig(plugin);
923
733
  plugin.schemaJson = await this.plugins.loadSchema(plugin);
924
- // Check if the plugin is available
925
734
  if (!(await this.plugins.resolve(plugin.path))) {
926
735
  this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
927
736
  plugin.enabled = false;
@@ -939,16 +748,10 @@ export class Matterbridge extends EventEmitter {
939
748
  plugin.configured = false;
940
749
  plugin.registeredDevices = undefined;
941
750
  plugin.addedDevices = undefined;
942
- plugin.qrPairingCode = undefined;
943
- plugin.manualPairingCode = undefined;
944
- this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
751
+ this.plugins.load(plugin, true, 'Matterbridge is starting');
945
752
  }
946
753
  this.frontend.wssSendRefreshRequired('plugins');
947
754
  }
948
- /**
949
- * Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
950
- * When either of these signals are received, the cleanup method is called with an appropriate message.
951
- */
952
755
  registerProcessHandlers() {
953
756
  this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
954
757
  process.removeAllListeners('uncaughtException');
@@ -975,9 +778,6 @@ export class Matterbridge extends EventEmitter {
975
778
  };
976
779
  process.on('SIGTERM', this.sigtermHandler);
977
780
  }
978
- /**
979
- * Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
980
- */
981
781
  deregisterProcessHandlers() {
982
782
  this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
983
783
  if (this.exceptionHandler)
@@ -994,17 +794,12 @@ export class Matterbridge extends EventEmitter {
994
794
  process.off('SIGTERM', this.sigtermHandler);
995
795
  this.sigtermHandler = undefined;
996
796
  }
997
- /**
998
- * Logs the node and system information.
999
- */
1000
797
  async logNodeAndSystemInfo() {
1001
- // IP address information
1002
798
  const networkInterfaces = os.networkInterfaces();
1003
799
  this.systemInformation.interfaceName = '';
1004
800
  this.systemInformation.ipv4Address = '';
1005
801
  this.systemInformation.ipv6Address = '';
1006
802
  for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
1007
- // this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
1008
803
  if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
1009
804
  continue;
1010
805
  if (!interfaceDetails) {
@@ -1030,22 +825,19 @@ export class Matterbridge extends EventEmitter {
1030
825
  break;
1031
826
  }
1032
827
  }
1033
- // Node information
1034
828
  this.systemInformation.nodeVersion = process.versions.node;
1035
829
  const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
1036
830
  const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
1037
831
  const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
1038
- // Host system information
1039
832
  this.systemInformation.hostname = os.hostname();
1040
833
  this.systemInformation.user = os.userInfo().username;
1041
- this.systemInformation.osType = os.type(); // "Windows_NT", "Darwin", etc.
1042
- this.systemInformation.osRelease = os.release(); // Kernel version
1043
- this.systemInformation.osPlatform = os.platform(); // "win32", "linux", "darwin", etc.
1044
- this.systemInformation.osArch = os.arch(); // "x64", "arm", etc.
1045
- this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
1046
- this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
1047
- this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours'; // Convert to hours
1048
- // Log the system information
834
+ this.systemInformation.osType = os.type();
835
+ this.systemInformation.osRelease = os.release();
836
+ this.systemInformation.osPlatform = os.platform();
837
+ this.systemInformation.osArch = os.arch();
838
+ this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
839
+ this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
840
+ this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
1049
841
  this.log.debug('Host System Information:');
1050
842
  this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
1051
843
  this.log.debug(`- User: ${this.systemInformation.user}`);
@@ -1061,17 +853,14 @@ export class Matterbridge extends EventEmitter {
1061
853
  this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
1062
854
  this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
1063
855
  this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
1064
- // Log directories
1065
856
  this.log.debug(`Root Directory: ${this.rootDirectory}`);
1066
857
  this.log.debug(`Home Directory: ${this.homeDirectory}`);
1067
858
  this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
1068
859
  this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
1069
860
  this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
1070
- // Global node_modules directory
1071
861
  if (this.nodeContext)
1072
862
  this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
1073
863
  if (this.globalModulesDirectory === '') {
1074
- // First run of Matterbridge so the node storage is empty
1075
864
  try {
1076
865
  const { getGlobalNodeModules } = await import('./utils/network.js');
1077
866
  this.execRunningCount++;
@@ -1086,81 +875,50 @@ export class Matterbridge extends EventEmitter {
1086
875
  }
1087
876
  else
1088
877
  this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
1089
- /* removed cause is too expensive for the shelly board and not really needed. Why should the globalModulesDirectory change?
1090
- else {
1091
- this.getGlobalNodeModules()
1092
- .then(async (globalModulesDirectory) => {
1093
- this.globalModulesDirectory = globalModulesDirectory;
1094
- this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
1095
- this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
1096
- await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
1097
- })
1098
- .catch((error) => {
1099
- this.log.error(`Error getting global node_modules directory: ${error}`);
1100
- });
1101
- }*/
1102
- // Matterbridge version
1103
878
  const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
1104
879
  this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
1105
880
  this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeDevVersion = packageJson.version;
1106
881
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
1107
- // Matterbridge latest version (will be set in the checkUpdate function)
1108
882
  if (this.nodeContext)
1109
883
  this.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
1110
884
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
1111
- // Matterbridge dev version (will be set in the checkUpdate function)
1112
885
  if (this.nodeContext)
1113
886
  this.matterbridgeDevVersion = this.matterbridgeInformation.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
1114
887
  this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
1115
- // Current working directory
1116
888
  const currentDir = process.cwd();
1117
889
  this.log.debug(`Current Working Directory: ${currentDir}`);
1118
- // Command line arguments (excluding 'node' and the script name)
1119
890
  const cmdArgs = process.argv.slice(2).join(' ');
1120
891
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
1121
892
  }
1122
- /**
1123
- * Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
1124
- *
1125
- * @returns {Function} The MatterLogger function.
1126
- */
1127
893
  createMatterLogger() {
1128
- const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
894
+ const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
1129
895
  return (level, formattedLog) => {
1130
896
  const logger = formattedLog.slice(44, 44 + 20).trim();
1131
897
  const message = formattedLog.slice(65);
1132
898
  matterLogger.logName = logger;
1133
899
  switch (level) {
1134
900
  case MatterLogLevel.DEBUG:
1135
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
901
+ matterLogger.log("debug", message);
1136
902
  break;
1137
903
  case MatterLogLevel.INFO:
1138
- matterLogger.log("info" /* LogLevel.INFO */, message);
904
+ matterLogger.log("info", message);
1139
905
  break;
1140
906
  case MatterLogLevel.NOTICE:
1141
- matterLogger.log("notice" /* LogLevel.NOTICE */, message);
907
+ matterLogger.log("notice", message);
1142
908
  break;
1143
909
  case MatterLogLevel.WARN:
1144
- matterLogger.log("warn" /* LogLevel.WARN */, message);
910
+ matterLogger.log("warn", message);
1145
911
  break;
1146
912
  case MatterLogLevel.ERROR:
1147
- matterLogger.log("error" /* LogLevel.ERROR */, message);
913
+ matterLogger.log("error", message);
1148
914
  break;
1149
915
  case MatterLogLevel.FATAL:
1150
- matterLogger.log("fatal" /* LogLevel.FATAL */, message);
916
+ matterLogger.log("fatal", message);
1151
917
  break;
1152
918
  }
1153
919
  };
1154
920
  }
1155
- /**
1156
- * Creates a Matter File Logger.
1157
- *
1158
- * @param {string} filePath - The path to the log file.
1159
- * @param {boolean} [unlink] - Whether to unlink the log file before creating a new one.
1160
- * @returns {Function} - A function that logs formatted messages to the log file.
1161
- */
1162
921
  async createMatterFileLogger(filePath, unlink = false) {
1163
- // 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
1164
922
  let fileSize = 0;
1165
923
  if (unlink) {
1166
924
  try {
@@ -1171,12 +929,10 @@ export class Matterbridge extends EventEmitter {
1171
929
  }
1172
930
  }
1173
931
  return async (level, formattedLog) => {
1174
- /* istanbul ignore if */
1175
932
  if (fileSize > 100000000) {
1176
- return; // Stop logging if the file size is greater than 100MB
933
+ return;
1177
934
  }
1178
935
  fileSize += formattedLog.length;
1179
- /* istanbul ignore if */
1180
936
  if (fileSize > 100000000) {
1181
937
  await fs.appendFile(filePath, `Logging on file has been stopped because the file size is greater than 100MB.` + os.EOL);
1182
938
  return;
@@ -1209,21 +965,12 @@ export class Matterbridge extends EventEmitter {
1209
965
  }
1210
966
  };
1211
967
  }
1212
- /**
1213
- * Restarts the process by exiting the current instance and loading a new instance.
1214
- */
1215
968
  async restartProcess() {
1216
969
  await this.cleanup('restarting...', true);
1217
970
  }
1218
- /**
1219
- * Shut down the process.
1220
- */
1221
971
  async shutdownProcess() {
1222
972
  await this.cleanup('shutting down...', false);
1223
973
  }
1224
- /**
1225
- * Update matterbridge and shut down the process.
1226
- */
1227
974
  async updateProcess() {
1228
975
  this.log.info('Updating matterbridge...');
1229
976
  try {
@@ -1237,75 +984,52 @@ export class Matterbridge extends EventEmitter {
1237
984
  this.frontend.wssSendRestartRequired();
1238
985
  await this.cleanup('updating...', false);
1239
986
  }
1240
- /**
1241
- * Unregister all devices and shut down the process.
1242
- */
1243
987
  async unregisterAndShutdownProcess() {
1244
988
  this.log.info('Unregistering all devices and shutting down...');
1245
989
  for (const plugin of this.plugins) {
1246
990
  await this.removeAllBridgedEndpoints(plugin.name, 250);
1247
991
  }
1248
992
  this.log.debug('Waiting for the MessageExchange to finish...');
1249
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second for MessageExchange to finish
993
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1250
994
  this.log.debug('Cleaning up and shutting down...');
1251
995
  await this.cleanup('unregistered all devices and shutting down...', false);
1252
996
  }
1253
- /**
1254
- * Reset commissioning and shut down the process.
1255
- */
1256
997
  async shutdownProcessAndReset() {
1257
998
  await this.cleanup('shutting down with reset...', false);
1258
999
  }
1259
- /**
1260
- * Factory reset and shut down the process.
1261
- */
1262
1000
  async shutdownProcessAndFactoryReset() {
1263
1001
  await this.cleanup('shutting down with factory reset...', false);
1264
1002
  }
1265
- /**
1266
- * Cleans up the Matterbridge instance.
1267
- *
1268
- * @param {string} message - The cleanup message.
1269
- * @param {boolean} [restart] - Indicates whether to restart the instance after cleanup. Default is `false`.
1270
- * @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
1271
- * @returns {Promise<void>} A promise that resolves when the cleanup is completed.
1272
- */
1273
1003
  async cleanup(message, restart = false, timeout = 1000) {
1274
1004
  if (this.initialized && !this.hasCleanupStarted) {
1275
1005
  this.emit('cleanup_started');
1276
1006
  this.hasCleanupStarted = true;
1277
1007
  this.log.info(message);
1278
- // Clear the start matter interval
1279
1008
  if (this.startMatterInterval) {
1280
1009
  clearInterval(this.startMatterInterval);
1281
1010
  this.startMatterInterval = undefined;
1282
1011
  this.log.debug('Start matter interval cleared');
1283
1012
  }
1284
- // Clear the check update timeout
1285
1013
  if (this.checkUpdateTimeout) {
1286
1014
  clearTimeout(this.checkUpdateTimeout);
1287
1015
  this.checkUpdateTimeout = undefined;
1288
1016
  this.log.debug('Check update timeout cleared');
1289
1017
  }
1290
- // Clear the check update interval
1291
1018
  if (this.checkUpdateInterval) {
1292
1019
  clearInterval(this.checkUpdateInterval);
1293
1020
  this.checkUpdateInterval = undefined;
1294
1021
  this.log.debug('Check update interval cleared');
1295
1022
  }
1296
- // Clear the configure timeout
1297
1023
  if (this.configureTimeout) {
1298
1024
  clearTimeout(this.configureTimeout);
1299
1025
  this.configureTimeout = undefined;
1300
1026
  this.log.debug('Matterbridge configure timeout cleared');
1301
1027
  }
1302
- // Clear the reachability timeout
1303
1028
  if (this.reachabilityTimeout) {
1304
1029
  clearTimeout(this.reachabilityTimeout);
1305
1030
  this.reachabilityTimeout = undefined;
1306
1031
  this.log.debug('Matterbridge reachability timeout cleared');
1307
1032
  }
1308
- // Call the shutdown method of each plugin and clear the plugins reachability timeout
1309
1033
  for (const plugin of this.plugins) {
1310
1034
  if (!plugin.enabled || plugin.error)
1311
1035
  continue;
@@ -1316,10 +1040,9 @@ export class Matterbridge extends EventEmitter {
1316
1040
  this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
1317
1041
  }
1318
1042
  }
1319
- // Stop matter server nodes
1320
1043
  this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
1321
1044
  this.log.debug('Waiting for the MessageExchange to finish...');
1322
- await new Promise((resolve) => setTimeout(resolve, timeout)); // Wait for MessageExchange to finish
1045
+ await new Promise((resolve) => setTimeout(resolve, timeout));
1323
1046
  if (this.bridgeMode === 'bridge') {
1324
1047
  if (this.serverNode) {
1325
1048
  await this.stopServerNode(this.serverNode);
@@ -1341,7 +1064,6 @@ export class Matterbridge extends EventEmitter {
1341
1064
  }
1342
1065
  }
1343
1066
  this.log.notice('Stopped matter server nodes');
1344
- // Matter commisioning reset
1345
1067
  if (message === 'shutting down with reset...') {
1346
1068
  this.log.info('Resetting Matterbridge commissioning information...');
1347
1069
  await this.matterStorageManager?.createContext('events')?.clearAll();
@@ -1351,36 +1073,18 @@ export class Matterbridge extends EventEmitter {
1351
1073
  await this.matterbridgeContext?.clearAll();
1352
1074
  this.log.info('Matter storage reset done! Remove the bridge from the controller.');
1353
1075
  }
1354
- // Stop matter storage
1355
1076
  await this.stopMatterStorage();
1356
- // Stop the frontend
1357
1077
  await this.frontend.stop();
1358
- // Remove the matterfilelogger
1359
1078
  try {
1360
1079
  Logger.removeLogger('matterfilelogger');
1361
1080
  }
1362
1081
  catch (error) {
1363
1082
  this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${db}: ${error instanceof Error ? error.message : String(error)}`);
1364
1083
  }
1365
- // Close the matterbridge node storage and context
1366
1084
  if (this.nodeStorage && this.nodeContext) {
1367
- /*
1368
- TODO: Implement serialization of registered devices in edge mode
1369
- this.log.info('Saving registered devices...');
1370
- const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
1371
- this.devices.forEach(async (device) => {
1372
- const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
1373
- // this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
1374
- if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
1375
- });
1376
- await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
1377
- this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
1378
- */
1379
- // Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
1380
1085
  this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
1381
1086
  await this.nodeContext.close();
1382
1087
  this.nodeContext = undefined;
1383
- // Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
1384
1088
  for (const plugin of this.plugins) {
1385
1089
  if (plugin.nodeContext) {
1386
1090
  this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
@@ -1397,10 +1101,8 @@ export class Matterbridge extends EventEmitter {
1397
1101
  }
1398
1102
  this.plugins.clear();
1399
1103
  this.devices.clear();
1400
- // Factory reset
1401
1104
  if (message === 'shutting down with factory reset...') {
1402
1105
  try {
1403
- // Delete matter storage directory with its subdirectories and backup
1404
1106
  const dir = path.join(this.matterbridgeDirectory, this.matterStorageName);
1405
1107
  this.log.info(`Removing matter storage directory: ${dir}`);
1406
1108
  await fs.rm(dir, { recursive: true });
@@ -1414,7 +1116,6 @@ export class Matterbridge extends EventEmitter {
1414
1116
  }
1415
1117
  }
1416
1118
  try {
1417
- // Delete matterbridge storage directory with its subdirectories and backup
1418
1119
  const dir = path.join(this.matterbridgeDirectory, this.nodeStorageName);
1419
1120
  this.log.info(`Removing matterbridge storage directory: ${dir}`);
1420
1121
  await fs.rm(dir, { recursive: true });
@@ -1429,13 +1130,12 @@ export class Matterbridge extends EventEmitter {
1429
1130
  }
1430
1131
  this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
1431
1132
  }
1432
- // Deregisters the process handlers
1433
1133
  this.deregisterProcessHandlers();
1434
1134
  if (restart) {
1435
1135
  if (message === 'updating...') {
1436
1136
  this.log.info('Cleanup completed. Updating...');
1437
1137
  Matterbridge.instance = undefined;
1438
- this.emit('update'); // Restart the process but the update has been done before. TODO move all updates to the cli
1138
+ this.emit('update');
1439
1139
  }
1440
1140
  else if (message === 'restarting...') {
1441
1141
  this.log.info('Cleanup completed. Restarting...');
@@ -1456,13 +1156,6 @@ export class Matterbridge extends EventEmitter {
1456
1156
  this.log.debug('Cleanup already started...');
1457
1157
  }
1458
1158
  }
1459
- /**
1460
- * Creates and configures the server node for a single not bridged device.
1461
- *
1462
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1463
- * @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
1464
- * @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
1465
- */
1466
1159
  async createDeviceServerNode(plugin, device) {
1467
1160
  if (device.mode === 'server' && !device.serverNode && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
1468
1161
  this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
@@ -1473,14 +1166,6 @@ export class Matterbridge extends EventEmitter {
1473
1166
  this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
1474
1167
  }
1475
1168
  }
1476
- /**
1477
- * Creates and configures the server node for an accessory plugin for a given device.
1478
- *
1479
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1480
- * @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
1481
- * @param {boolean} [start] - Whether to start the server node after adding the device. Default is `false`.
1482
- * @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
1483
- */
1484
1169
  async createAccessoryPlugin(plugin, device, start = false) {
1485
1170
  if (!plugin.locked && device.deviceType && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
1486
1171
  plugin.locked = true;
@@ -1494,14 +1179,7 @@ export class Matterbridge extends EventEmitter {
1494
1179
  await this.startServerNode(plugin.serverNode);
1495
1180
  }
1496
1181
  }
1497
- /**
1498
- * Creates and configures the server node and the aggregator node for a dynamic plugin.
1499
- *
1500
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1501
- * @param {boolean} [start] - Whether to start the server node after adding the aggregator node.
1502
- * @returns {Promise<void>} A promise that resolves when the server node and the aggregator node for the dynamic plugin is created and configured.
1503
- */
1504
- async createDynamicPlugin(plugin, start = false) {
1182
+ async createDynamicPlugin(plugin) {
1505
1183
  if (!plugin.locked) {
1506
1184
  plugin.locked = true;
1507
1185
  plugin.storageContext = await this.createServerNodeContext(plugin.name, 'Matterbridge', bridge.code, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, plugin.description);
@@ -1509,18 +1187,9 @@ export class Matterbridge extends EventEmitter {
1509
1187
  plugin.aggregatorNode = await this.createAggregatorNode(plugin.storageContext);
1510
1188
  plugin.serialNumber = await plugin.storageContext.get('serialNumber', '');
1511
1189
  await plugin.serverNode.add(plugin.aggregatorNode);
1512
- if (start)
1513
- await this.startServerNode(plugin.serverNode);
1514
1190
  }
1515
1191
  }
1516
- /**
1517
- * Starts the Matterbridge in bridge mode.
1518
- *
1519
- * @private
1520
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1521
- */
1522
1192
  async startBridge() {
1523
- // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1524
1193
  if (!this.matterStorageManager)
1525
1194
  throw new Error('No storage manager initialized');
1526
1195
  if (!this.matterbridgeContext)
@@ -1559,16 +1228,13 @@ export class Matterbridge extends EventEmitter {
1559
1228
  clearInterval(this.startMatterInterval);
1560
1229
  this.startMatterInterval = undefined;
1561
1230
  this.log.debug('Cleared startMatterInterval interval for Matterbridge');
1562
- // Start the Matter server node
1563
- this.startServerNode(this.serverNode); // We don't await this, because the server node is started in the background
1564
- // Start the Matter server node of single devices in mode 'server'
1231
+ this.startServerNode(this.serverNode);
1565
1232
  for (const device of this.devices.array()) {
1566
1233
  if (device.mode === 'server' && device.serverNode) {
1567
1234
  this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
1568
- this.startServerNode(device.serverNode); // We don't await this, because the server node is started in the background
1235
+ this.startServerNode(device.serverNode);
1569
1236
  }
1570
1237
  }
1571
- // Configure the plugins
1572
1238
  this.configureTimeout = setTimeout(async () => {
1573
1239
  for (const plugin of this.plugins) {
1574
1240
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1586,24 +1252,16 @@ export class Matterbridge extends EventEmitter {
1586
1252
  }
1587
1253
  this.frontend.wssSendRefreshRequired('plugins');
1588
1254
  }, 30 * 1000).unref();
1589
- // Setting reachability to true
1590
1255
  this.reachabilityTimeout = setTimeout(() => {
1591
1256
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1592
1257
  if (this.aggregatorNode)
1593
1258
  this.setAggregatorReachability(this.aggregatorNode, true);
1594
1259
  this.frontend.wssSendRefreshRequired('reachability');
1595
1260
  }, 60 * 1000).unref();
1596
- // Logger.get('LogServerNode').info(this.serverNode);
1597
1261
  this.emit('bridge_started');
1598
1262
  this.log.notice('Matterbridge bridge started successfully');
1599
1263
  }, 1000);
1600
1264
  }
1601
- /**
1602
- * Starts the Matterbridge in childbridge mode.
1603
- *
1604
- * @private
1605
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1606
- */
1607
1265
  async startChildbridge() {
1608
1266
  if (!this.matterStorageManager)
1609
1267
  throw new Error('No storage manager initialized');
@@ -1640,8 +1298,8 @@ export class Matterbridge extends EventEmitter {
1640
1298
  return;
1641
1299
  clearInterval(this.startMatterInterval);
1642
1300
  this.startMatterInterval = undefined;
1301
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1643
1302
  this.log.debug('Cleared startMatterInterval interval in childbridge mode');
1644
- // Configure the plugins
1645
1303
  this.configureTimeout = setTimeout(async () => {
1646
1304
  for (const plugin of this.plugins) {
1647
1305
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1678,9 +1336,7 @@ export class Matterbridge extends EventEmitter {
1678
1336
  this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
1679
1337
  continue;
1680
1338
  }
1681
- // Start the Matter server node
1682
- this.startServerNode(plugin.serverNode); // We don't await this, because the server node is started in the background
1683
- // Setting reachability to true
1339
+ this.startServerNode(plugin.serverNode);
1684
1340
  plugin.reachabilityTimeout = setTimeout(() => {
1685
1341
  this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggregator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
1686
1342
  if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
@@ -1688,241 +1344,19 @@ export class Matterbridge extends EventEmitter {
1688
1344
  this.frontend.wssSendRefreshRequired('reachability');
1689
1345
  }, 60 * 1000).unref();
1690
1346
  }
1691
- // Start the Matter server node of single devices in mode 'server'
1692
1347
  for (const device of this.devices.array()) {
1693
1348
  if (device.mode === 'server' && device.serverNode) {
1694
1349
  this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
1695
- this.startServerNode(device.serverNode); // We don't await this, because the server node is started in the background
1350
+ this.startServerNode(device.serverNode);
1696
1351
  }
1697
1352
  }
1698
- // Logger.get('LogServerNode').info(this.serverNode);
1699
1353
  this.emit('childbridge_started');
1700
1354
  this.log.notice('Matterbridge childbridge started successfully');
1701
1355
  }, 1000);
1702
1356
  }
1703
- /**
1704
- * Starts the Matterbridge controller.
1705
- *
1706
- * @private
1707
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1708
- */
1709
1357
  async startController() {
1710
- /*
1711
- if (!this.matterStorageManager) {
1712
- this.log.error('No storage manager initialized');
1713
- await this.cleanup('No storage manager initialized');
1714
- return;
1715
- }
1716
- this.log.info('Creating context: mattercontrollerContext');
1717
- this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
1718
- if (!this.controllerContext) {
1719
- this.log.error('No storage context mattercontrollerContext initialized');
1720
- await this.cleanup('No storage context mattercontrollerContext initialized');
1721
- return;
1722
- }
1723
-
1724
- this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1725
- this.matterServer = await this.createMatterServer(this.storageManager);
1726
- this.log.info('Creating matter commissioning controller');
1727
- this.commissioningController = new CommissioningController({
1728
- autoConnect: false,
1729
- });
1730
- this.log.info('Adding matter commissioning controller to matter server');
1731
- await this.matterServer.addCommissioningController(this.commissioningController);
1732
-
1733
- this.log.info('Starting matter server');
1734
- await this.matterServer.start();
1735
- this.log.info('Matter server started');
1736
- const commissioningOptions: ControllerCommissioningFlowOptions = {
1737
- regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
1738
- regulatoryCountryCode: 'XX',
1739
- };
1740
- const commissioningController = new CommissioningController({
1741
- environment: {
1742
- environment,
1743
- id: uniqueId,
1744
- },
1745
- autoConnect: false, // Do not auto connect to the commissioned nodes
1746
- adminFabricLabel,
1747
- });
1748
-
1749
- if (hasParameter('pairingcode')) {
1750
- this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
1751
- const pairingCode = getParameter('pairingcode');
1752
- const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
1753
- const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
1754
-
1755
- let longDiscriminator, setupPin, shortDiscriminator;
1756
- if (pairingCode !== undefined) {
1757
- const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
1758
- shortDiscriminator = pairingCodeCodec.shortDiscriminator;
1759
- longDiscriminator = undefined;
1760
- setupPin = pairingCodeCodec.passcode;
1761
- this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
1762
- } else {
1763
- longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
1764
- if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
1765
- setupPin = this.controllerContext.get('pin', 20202021);
1766
- }
1767
- if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
1768
- throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
1769
- }
1770
-
1771
- const options = {
1772
- commissioning: commissioningOptions,
1773
- discovery: {
1774
- knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
1775
- identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
1776
- },
1777
- passcode: setupPin,
1778
- } as NodeCommissioningOptions;
1779
- this.log.info('Commissioning with options:', options);
1780
- const nodeId = await this.commissioningController.commissionNode(options);
1781
- this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
1782
- this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
1783
- } // (hasParameter('pairingcode'))
1784
-
1785
- if (hasParameter('unpairall')) {
1786
- this.log.info('***Commissioning controller unpairing all nodes...');
1787
- const nodeIds = this.commissioningController.getCommissionedNodes();
1788
- for (const nodeId of nodeIds) {
1789
- this.log.info('***Commissioning controller unpairing node:', nodeId);
1790
- await this.commissioningController.removeNode(nodeId);
1791
- }
1792
- return;
1793
- }
1794
-
1795
- if (hasParameter('discover')) {
1796
- // const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1797
- // console.log(discover);
1798
- }
1799
-
1800
- if (!this.commissioningController.isCommissioned()) {
1801
- this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
1802
- return;
1803
- }
1804
-
1805
- const nodeIds = this.commissioningController.getCommissionedNodes();
1806
- this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
1807
- for (const nodeId of nodeIds) {
1808
- this.log.info(`***Connecting to commissioned node: ${nodeId}`);
1809
-
1810
- const node = await this.commissioningController.connectNode(nodeId, {
1811
- autoSubscribe: false,
1812
- attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
1813
- this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
1814
- eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
1815
- this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
1816
- stateInformationCallback: (peerNodeId, info) => {
1817
- switch (info) {
1818
- case NodeStateInformation.Connected:
1819
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
1820
- break;
1821
- case NodeStateInformation.Disconnected:
1822
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
1823
- break;
1824
- case NodeStateInformation.Reconnecting:
1825
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
1826
- break;
1827
- case NodeStateInformation.WaitingForDeviceDiscovery:
1828
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
1829
- break;
1830
- case NodeStateInformation.StructureChanged:
1831
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
1832
- break;
1833
- case NodeStateInformation.Decommissioned:
1834
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
1835
- break;
1836
- default:
1837
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
1838
- break;
1839
- }
1840
- },
1841
- });
1842
-
1843
- node.logStructure();
1844
-
1845
- // Get the interaction client
1846
- this.log.info('Getting the interaction client');
1847
- const interactionClient = await node.getInteractionClient();
1848
- let cluster;
1849
- let attributes;
1850
-
1851
- // Log BasicInformationCluster
1852
- cluster = BasicInformationCluster;
1853
- attributes = await interactionClient.getMultipleAttributes({
1854
- attributes: [{ clusterId: cluster.id }],
1855
- });
1856
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1857
- attributes.forEach((attribute) => {
1858
- this.log.info(
1859
- `- 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}`,
1860
- );
1861
- });
1862
-
1863
- // Log PowerSourceCluster
1864
- cluster = PowerSourceCluster;
1865
- attributes = await interactionClient.getMultipleAttributes({
1866
- attributes: [{ clusterId: cluster.id }],
1867
- });
1868
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1869
- attributes.forEach((attribute) => {
1870
- this.log.info(
1871
- `- 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}`,
1872
- );
1873
- });
1874
-
1875
- // Log ThreadNetworkDiagnostics
1876
- cluster = ThreadNetworkDiagnosticsCluster;
1877
- attributes = await interactionClient.getMultipleAttributes({
1878
- attributes: [{ clusterId: cluster.id }],
1879
- });
1880
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1881
- attributes.forEach((attribute) => {
1882
- this.log.info(
1883
- `- 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}`,
1884
- );
1885
- });
1886
-
1887
- // Log SwitchCluster
1888
- cluster = SwitchCluster;
1889
- attributes = await interactionClient.getMultipleAttributes({
1890
- attributes: [{ clusterId: cluster.id }],
1891
- });
1892
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1893
- attributes.forEach((attribute) => {
1894
- this.log.info(
1895
- `- 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}`,
1896
- );
1897
- });
1898
-
1899
- this.log.info('Subscribing to all attributes and events');
1900
- await node.subscribeAllAttributesAndEvents({
1901
- ignoreInitialTriggers: false,
1902
- attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
1903
- this.log.info(
1904
- `***${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}`,
1905
- ),
1906
- eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
1907
- this.log.info(
1908
- `***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
1909
- );
1910
- },
1911
- });
1912
- this.log.info('Subscribed to all attributes and events');
1913
- }
1914
- */
1915
1358
  }
1916
- /** */
1917
- /** Matter.js methods */
1918
- /** */
1919
- /**
1920
- * Starts the matter storage with name Matterbridge, create the matterbridge context and performs a backup.
1921
- *
1922
- * @returns {Promise<void>} - A promise that resolves when the storage is started.
1923
- */
1924
1359
  async startMatterStorage() {
1925
- // Setup Matter storage
1926
1360
  this.log.info(`Starting matter node storage...`);
1927
1361
  this.matterStorageService = this.environment.get(StorageService);
1928
1362
  this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
@@ -1931,17 +1365,8 @@ export class Matterbridge extends EventEmitter {
1931
1365
  this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName);
1932
1366
  this.matterbridgeInformation.matterbridgeSerialNumber = await this.matterbridgeContext.get('serialNumber', '');
1933
1367
  this.log.info('Matter node storage started');
1934
- // Backup matter storage since it is created/opened correctly
1935
1368
  await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
1936
1369
  }
1937
- /**
1938
- * Makes a backup copy of the specified matter storage directory.
1939
- *
1940
- * @param {string} storageName - The name of the storage directory to be backed up.
1941
- * @param {string} backupName - The name of the backup directory to be created.
1942
- * @private
1943
- * @returns {Promise<void>} A promise that resolves when the has been done.
1944
- */
1945
1370
  async backupMatterStorage(storageName, backupName) {
1946
1371
  this.log.info('Creating matter node storage backup...');
1947
1372
  try {
@@ -1952,11 +1377,6 @@ export class Matterbridge extends EventEmitter {
1952
1377
  this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
1953
1378
  }
1954
1379
  }
1955
- /**
1956
- * Stops the matter storage.
1957
- *
1958
- * @returns {Promise<void>} A promise that resolves when the storage is stopped.
1959
- */
1960
1380
  async stopMatterStorage() {
1961
1381
  this.log.info('Closing matter node storage...');
1962
1382
  await this.matterStorageManager?.close();
@@ -1965,19 +1385,6 @@ export class Matterbridge extends EventEmitter {
1965
1385
  this.matterbridgeContext = undefined;
1966
1386
  this.log.info('Matter node storage closed');
1967
1387
  }
1968
- /**
1969
- * Creates a server node storage context.
1970
- *
1971
- * @param {string} pluginName - The name of the plugin.
1972
- * @param {string} deviceName - The name of the device.
1973
- * @param {DeviceTypeId} deviceType - The device type of the device.
1974
- * @param {number} vendorId - The vendor ID.
1975
- * @param {string} vendorName - The vendor name.
1976
- * @param {number} productId - The product ID.
1977
- * @param {string} productName - The product name.
1978
- * @param {string} [serialNumber] - The serial number of the device (optional).
1979
- * @returns {Promise<StorageContext>} The storage context for the commissioning server.
1980
- */
1981
1388
  async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
1982
1389
  const { randomBytes } = await import('node:crypto');
1983
1390
  if (!this.matterStorageService)
@@ -2011,15 +1418,6 @@ export class Matterbridge extends EventEmitter {
2011
1418
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
2012
1419
  return storageContext;
2013
1420
  }
2014
- /**
2015
- * Creates a server node.
2016
- *
2017
- * @param {StorageContext} storageContext - The storage context for the server node.
2018
- * @param {number} [port] - The port number for the server node. Defaults to 5540.
2019
- * @param {number} [passcode] - The passcode for the server node. Defaults to 20242025.
2020
- * @param {number} [discriminator] - The discriminator for the server node. Defaults to 3850.
2021
- * @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
2022
- */
2023
1421
  async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
2024
1422
  const storeId = await storageContext.get('storeId');
2025
1423
  this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
@@ -2029,37 +1427,24 @@ export class Matterbridge extends EventEmitter {
2029
1427
  this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
2030
1428
  this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
2031
1429
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
2032
- /**
2033
- * Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
2034
- */
2035
1430
  const serverNode = await ServerNode.create({
2036
- // Required: Give the Node a unique ID which is used to store the state of this node
2037
1431
  id: storeId,
2038
- // Provide Network relevant configuration like the port
2039
- // Optional when operating only one device on a host, Default port is 5540
2040
1432
  network: {
2041
1433
  listeningAddressIpv4: this.ipv4address,
2042
1434
  listeningAddressIpv6: this.ipv6address,
2043
1435
  port,
2044
1436
  },
2045
- // Provide the certificate for the device
2046
1437
  operationalCredentials: {
2047
1438
  certification: this.certification,
2048
1439
  },
2049
- // Provide Commissioning relevant settings
2050
- // Optional for development/testing purposes
2051
1440
  commissioning: {
2052
1441
  passcode,
2053
1442
  discriminator,
2054
1443
  },
2055
- // Provide Node announcement settings
2056
- // Optional: If Ommitted some development defaults are used
2057
1444
  productDescription: {
2058
1445
  name: await storageContext.get('deviceName'),
2059
1446
  deviceType: DeviceTypeId(await storageContext.get('deviceType')),
2060
1447
  },
2061
- // Provide defaults for the BasicInformation cluster on the Root endpoint
2062
- // Optional: If Omitted some development defaults are used
2063
1448
  basicInformation: {
2064
1449
  vendorId: VendorId(await storageContext.get('vendorId')),
2065
1450
  vendorName: await storageContext.get('vendorName'),
@@ -2076,116 +1461,39 @@ export class Matterbridge extends EventEmitter {
2076
1461
  reachable: true,
2077
1462
  },
2078
1463
  });
2079
- const sanitizeFabrics = (fabrics, resetSessions = false) => {
2080
- // New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
2081
- const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
2082
- this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
2083
- if (this.bridgeMode === 'bridge') {
2084
- this.matterbridgeFabricInformations = sanitizedFabrics;
2085
- if (resetSessions)
2086
- this.matterbridgeSessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
2087
- this.matterbridgePaired = true;
2088
- }
2089
- if (this.bridgeMode === 'childbridge') {
2090
- const plugin = this.plugins.get(storeId);
2091
- if (plugin) {
2092
- plugin.fabricInformations = sanitizedFabrics;
2093
- if (resetSessions)
2094
- plugin.sessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
2095
- plugin.paired = true;
2096
- }
2097
- }
2098
- };
2099
- /**
2100
- * This event is triggered when the device is initially commissioned successfully.
2101
- * This means: It is added to the first fabric.
2102
- */
2103
1464
  serverNode.lifecycle.commissioned.on(() => {
2104
1465
  this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
2105
1466
  clearTimeout(this.endAdvertiseTimeout);
2106
1467
  });
2107
- /** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
2108
- serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
2109
- /** This event is triggered when the device went online. This means that it is discoverable in the network. */
1468
+ serverNode.lifecycle.decommissioned.on(() => {
1469
+ this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`);
1470
+ clearTimeout(this.endAdvertiseTimeout);
1471
+ });
2110
1472
  serverNode.lifecycle.online.on(async () => {
2111
1473
  this.log.notice(`Server node for ${storeId} is online`);
2112
1474
  if (!serverNode.lifecycle.isCommissioned) {
2113
1475
  this.log.notice(`Server node for ${storeId} is not commissioned. Pair to commission ...`);
2114
1476
  const { qrPairingCode, manualPairingCode } = serverNode.state.commissioning.pairingCodes;
2115
- if (this.bridgeMode === 'bridge') {
2116
- this.matterbridgeQrPairingCode = qrPairingCode;
2117
- this.matterbridgeManualPairingCode = manualPairingCode;
2118
- this.matterbridgeFabricInformations = undefined;
2119
- this.matterbridgeSessionInformations = undefined;
2120
- this.matterbridgePaired = false;
2121
- this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
2122
- this.log.notice(`Manual pairing code: ${manualPairingCode}`);
2123
- if (this.matterStorageService) {
2124
- const storageManager = await this.matterStorageService.open(storeId);
2125
- const storageContext = storageManager.createContext('persist');
2126
- await storageContext.set('qrPairingCode', qrPairingCode);
2127
- await storageContext.set('manualPairingCode', manualPairingCode);
2128
- }
2129
- }
2130
- if (this.bridgeMode === 'childbridge') {
2131
- const plugin = this.plugins.get(storeId);
2132
- if (plugin) {
2133
- plugin.qrPairingCode = qrPairingCode;
2134
- plugin.manualPairingCode = manualPairingCode;
2135
- plugin.fabricInformations = undefined;
2136
- plugin.sessionInformations = undefined;
2137
- plugin.paired = false;
2138
- this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
2139
- this.log.notice(`Manual pairing code: ${manualPairingCode}`);
2140
- if (this.matterStorageService) {
2141
- const storageManager = await this.matterStorageService.open(storeId);
2142
- const storageContext = storageManager.createContext('persist');
2143
- await storageContext.set('qrPairingCode', qrPairingCode);
2144
- await storageContext.set('manualPairingCode', manualPairingCode);
2145
- }
2146
- }
2147
- }
2148
- // Set a timeout to show that advertising stops after 15 minutes if not commissioned
1477
+ this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
1478
+ this.log.notice(`Manual pairing code: ${manualPairingCode}`);
2149
1479
  this.startEndAdvertiseTimer(serverNode);
2150
1480
  }
2151
1481
  else {
2152
1482
  this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
2153
- sanitizeFabrics(serverNode.state.commissioning.fabrics, true);
2154
1483
  }
2155
1484
  this.frontend.wssSendRefreshRequired('plugins');
2156
1485
  this.frontend.wssSendRefreshRequired('settings');
2157
1486
  this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
2158
1487
  this.emit('online', storeId);
2159
1488
  });
2160
- /** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
2161
1489
  serverNode.lifecycle.offline.on(() => {
2162
1490
  this.log.notice(`Server node for ${storeId} is offline`);
2163
- if (this.bridgeMode === 'bridge') {
2164
- this.matterbridgeQrPairingCode = undefined;
2165
- this.matterbridgeManualPairingCode = undefined;
2166
- this.matterbridgeFabricInformations = undefined;
2167
- this.matterbridgeSessionInformations = undefined;
2168
- this.matterbridgePaired = undefined;
2169
- }
2170
- if (this.bridgeMode === 'childbridge') {
2171
- const plugin = this.plugins.get(storeId);
2172
- if (plugin) {
2173
- plugin.qrPairingCode = undefined;
2174
- plugin.manualPairingCode = undefined;
2175
- plugin.fabricInformations = undefined;
2176
- plugin.sessionInformations = undefined;
2177
- plugin.paired = undefined;
2178
- }
2179
- }
1491
+ this.matterbridgeInformation.matterbridgeEndAdvertise = true;
2180
1492
  this.frontend.wssSendRefreshRequired('plugins');
2181
1493
  this.frontend.wssSendRefreshRequired('settings');
2182
1494
  this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
2183
1495
  this.emit('offline', storeId);
2184
1496
  });
2185
- /**
2186
- * This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
2187
- * information is needed.
2188
- */
2189
1497
  serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
2190
1498
  let action = '';
2191
1499
  switch (fabricAction) {
@@ -2200,53 +1508,23 @@ export class Matterbridge extends EventEmitter {
2200
1508
  break;
2201
1509
  }
2202
1510
  this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
2203
- sanitizeFabrics(serverNode.state.commissioning.fabrics);
2204
1511
  this.frontend.wssSendRefreshRequired('fabrics');
2205
1512
  });
2206
- const sanitizeSessions = (sessions) => {
2207
- const sanitizedSessions = this.sanitizeSessionInformation(sessions);
2208
- this.log.debug(`Sessions: ${debugStringify(sanitizedSessions)}`);
2209
- if (this.bridgeMode === 'bridge') {
2210
- this.matterbridgeSessionInformations = sanitizedSessions;
2211
- }
2212
- if (this.bridgeMode === 'childbridge') {
2213
- const plugin = this.plugins.get(storeId);
2214
- if (plugin) {
2215
- plugin.sessionInformations = sanitizedSessions;
2216
- }
2217
- }
2218
- };
2219
- /**
2220
- * This event is triggered when an operative new session was opened by a Controller.
2221
- * It is not triggered for the initial commissioning process, just afterwards for real connections.
2222
- */
2223
1513
  serverNode.events.sessions.opened.on((session) => {
2224
1514
  this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
2225
- sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2226
1515
  this.frontend.wssSendRefreshRequired('sessions');
2227
1516
  });
2228
- /**
2229
- * This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
2230
- */
2231
1517
  serverNode.events.sessions.closed.on((session) => {
2232
1518
  this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
2233
- sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2234
1519
  this.frontend.wssSendRefreshRequired('sessions');
2235
1520
  });
2236
- /** This event is triggered when a subscription gets added or removed on an operative session. */
2237
1521
  serverNode.events.sessions.subscriptionsChanged.on((session) => {
2238
1522
  this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
2239
- sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2240
1523
  this.frontend.wssSendRefreshRequired('sessions');
2241
1524
  });
2242
1525
  this.log.info(`Created server node for ${storeId}`);
2243
1526
  return serverNode;
2244
1527
  }
2245
- /**
2246
- * Starts the 15 minutes timer to advice that advertising for the specified server node is ended.
2247
- *
2248
- * @param {ServerNode} [matterServerNode] - The server node to start.
2249
- */
2250
1528
  startEndAdvertiseTimer(matterServerNode) {
2251
1529
  if (this.endAdvertiseTimeout) {
2252
1530
  this.log.debug(`Clear ${matterServerNode.id} server node end advertise timer`);
@@ -2256,17 +1534,7 @@ export class Matterbridge extends EventEmitter {
2256
1534
  this.endAdvertiseTimeout = setTimeout(() => {
2257
1535
  if (matterServerNode.lifecycle.isCommissioned)
2258
1536
  return;
2259
- if (this.bridgeMode === 'bridge') {
2260
- this.matterbridgeQrPairingCode = undefined;
2261
- this.matterbridgeManualPairingCode = undefined;
2262
- }
2263
- if (this.bridgeMode === 'childbridge') {
2264
- const plugin = this.plugins.get(matterServerNode.id);
2265
- if (plugin) {
2266
- plugin.qrPairingCode = undefined;
2267
- plugin.manualPairingCode = undefined;
2268
- }
2269
- }
1537
+ this.matterbridgeInformation.matterbridgeEndAdvertise = true;
2270
1538
  this.frontend.wssSendRefreshRequired('plugins');
2271
1539
  this.frontend.wssSendRefreshRequired('settings');
2272
1540
  this.frontend.wssSendRefreshRequired('fabrics');
@@ -2275,25 +1543,12 @@ export class Matterbridge extends EventEmitter {
2275
1543
  this.log.notice(`Advertising on server node for ${matterServerNode.id} stopped. Restart to commission.`);
2276
1544
  }, 15 * 60 * 1000).unref();
2277
1545
  }
2278
- /**
2279
- * Starts the specified server node.
2280
- *
2281
- * @param {ServerNode} [matterServerNode] - The server node to start.
2282
- * @returns {Promise<void>} A promise that resolves when the server node has started.
2283
- */
2284
1546
  async startServerNode(matterServerNode) {
2285
1547
  if (!matterServerNode)
2286
1548
  return;
2287
1549
  this.log.notice(`Starting ${matterServerNode.id} server node`);
2288
1550
  await matterServerNode.start();
2289
1551
  }
2290
- /**
2291
- * Stops the specified server node.
2292
- *
2293
- * @param {ServerNode} matterServerNode - The server node to stop.
2294
- * @param {number} [timeout] - The timeout in milliseconds for stopping the server node. Defaults to 30 seconds.
2295
- * @returns {Promise<void>} A promise that resolves when the server node has stopped.
2296
- */
2297
1552
  async stopServerNode(matterServerNode, timeout = 30000) {
2298
1553
  if (!matterServerNode)
2299
1554
  return;
@@ -2306,12 +1561,6 @@ export class Matterbridge extends EventEmitter {
2306
1561
  this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
2307
1562
  }
2308
1563
  }
2309
- /**
2310
- * Advertises the specified server node.
2311
- *
2312
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2313
- * @returns {Promise<{ qrPairingCode: string, manualPairingCode: string } | undefined>} A promise that resolves to the pairing codes if the server node is advertised, or undefined if not.
2314
- */
2315
1564
  async advertiseServerNode(matterServerNode) {
2316
1565
  if (matterServerNode) {
2317
1566
  await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
@@ -2320,39 +1569,19 @@ export class Matterbridge extends EventEmitter {
2320
1569
  return { qrPairingCode, manualPairingCode };
2321
1570
  }
2322
1571
  }
2323
- /**
2324
- * Stop advertise the specified server node.
2325
- *
2326
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2327
- * @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
2328
- */
2329
1572
  async stopAdvertiseServerNode(matterServerNode) {
2330
1573
  if (matterServerNode && matterServerNode.lifecycle.isOnline) {
2331
1574
  await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
2332
1575
  this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
2333
1576
  }
2334
1577
  }
2335
- /**
2336
- * Creates an aggregator node with the specified storage context.
2337
- *
2338
- * @param {StorageContext} storageContext - The storage context for the aggregator node.
2339
- * @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
2340
- */
2341
1578
  async createAggregatorNode(storageContext) {
2342
1579
  this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
2343
1580
  const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
2344
1581
  this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
2345
1582
  return aggregatorNode;
2346
1583
  }
2347
- /**
2348
- * Adds a MatterbridgeEndpoint to the specified plugin.
2349
- *
2350
- * @param {string} pluginName - The name of the plugin.
2351
- * @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
2352
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
2353
- */
2354
1584
  async addBridgedEndpoint(pluginName, device) {
2355
- // Check if the plugin is registered
2356
1585
  const plugin = this.plugins.get(pluginName);
2357
1586
  if (!plugin) {
2358
1587
  this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
@@ -2372,7 +1601,6 @@ export class Matterbridge extends EventEmitter {
2372
1601
  }
2373
1602
  else if (this.bridgeMode === 'bridge') {
2374
1603
  if (device.mode === 'matter') {
2375
- // Register and add the device to the matterbridge server node
2376
1604
  this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge server node...`);
2377
1605
  if (!this.serverNode) {
2378
1606
  this.log.error('Server node not found for Matterbridge');
@@ -2389,7 +1617,6 @@ export class Matterbridge extends EventEmitter {
2389
1617
  }
2390
1618
  }
2391
1619
  else {
2392
- // Register and add the device to the matterbridge aggregator node
2393
1620
  this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
2394
1621
  if (!this.aggregatorNode) {
2395
1622
  this.log.error('Aggregator node not found for Matterbridge');
@@ -2407,7 +1634,6 @@ export class Matterbridge extends EventEmitter {
2407
1634
  }
2408
1635
  }
2409
1636
  else if (this.bridgeMode === 'childbridge') {
2410
- // Register and add the device to the plugin server node
2411
1637
  if (plugin.type === 'AccessoryPlatform') {
2412
1638
  try {
2413
1639
  this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
@@ -2431,12 +1657,10 @@ export class Matterbridge extends EventEmitter {
2431
1657
  return;
2432
1658
  }
2433
1659
  }
2434
- // Register and add the device to the plugin aggregator node
2435
1660
  if (plugin.type === 'DynamicPlatform') {
2436
1661
  try {
2437
1662
  this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
2438
1663
  await this.createDynamicPlugin(plugin);
2439
- // Fast plugins can add another device before the server node is ready, so we wait for the server node to be ready
2440
1664
  await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
2441
1665
  if (!plugin.aggregatorNode) {
2442
1666
  this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
@@ -2459,28 +1683,17 @@ export class Matterbridge extends EventEmitter {
2459
1683
  plugin.registeredDevices++;
2460
1684
  if (plugin.addedDevices !== undefined)
2461
1685
  plugin.addedDevices++;
2462
- // Add the device to the DeviceManager
2463
1686
  this.devices.set(device);
2464
- // Subscribe to the reachable$Changed event
2465
1687
  await this.subscribeAttributeChanged(plugin, device);
2466
1688
  this.log.info(`Added and registered bridged endpoint (${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
2467
1689
  }
2468
- /**
2469
- * Removes a MatterbridgeEndpoint from the specified plugin.
2470
- *
2471
- * @param {string} pluginName - The name of the plugin.
2472
- * @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
2473
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
2474
- */
2475
1690
  async removeBridgedEndpoint(pluginName, device) {
2476
1691
  this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
2477
- // Check if the plugin is registered
2478
1692
  const plugin = this.plugins.get(pluginName);
2479
1693
  if (!plugin) {
2480
1694
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
2481
1695
  return;
2482
1696
  }
2483
- // Register and add the device to the matterbridge aggregator node
2484
1697
  if (this.bridgeMode === 'bridge') {
2485
1698
  if (!this.aggregatorNode) {
2486
1699
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
@@ -2495,7 +1708,6 @@ export class Matterbridge extends EventEmitter {
2495
1708
  }
2496
1709
  else if (this.bridgeMode === 'childbridge') {
2497
1710
  if (plugin.type === 'AccessoryPlatform') {
2498
- // Nothing to do here since the server node has no aggregator node but only the device itself
2499
1711
  }
2500
1712
  else if (plugin.type === 'DynamicPlatform') {
2501
1713
  if (!plugin.aggregatorNode) {
@@ -2510,21 +1722,8 @@ export class Matterbridge extends EventEmitter {
2510
1722
  if (plugin.addedDevices !== undefined)
2511
1723
  plugin.addedDevices--;
2512
1724
  }
2513
- // Remove the device from the DeviceManager
2514
1725
  this.devices.remove(device);
2515
1726
  }
2516
- /**
2517
- * Removes all bridged endpoints from the specified plugin.
2518
- *
2519
- * @param {string} pluginName - The name of the plugin.
2520
- * @param {number} [delay] - The delay in milliseconds between removing each bridged endpoint (default: 0).
2521
- * @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
2522
- *
2523
- * @remarks
2524
- * This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
2525
- * It also applies a delay between each removal if specified.
2526
- * The delay is useful to allow the controllers to receive a single subscription for each device removed.
2527
- */
2528
1727
  async removeAllBridgedEndpoints(pluginName, delay = 0) {
2529
1728
  this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
2530
1729
  for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
@@ -2535,15 +1734,6 @@ export class Matterbridge extends EventEmitter {
2535
1734
  if (delay > 0)
2536
1735
  await new Promise((resolve) => setTimeout(resolve, 2000));
2537
1736
  }
2538
- /**
2539
- * Subscribes to the attribute change event for the given device and plugin.
2540
- * Specifically, it listens for changes in the 'reachable' attribute of the
2541
- * BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
2542
- *
2543
- * @param {RegisteredPlugin} plugin - The plugin associated with the device.
2544
- * @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
2545
- * @returns {Promise<void>} A promise that resolves when the subscription is set up.
2546
- */
2547
1737
  async subscribeAttributeChanged(plugin, device) {
2548
1738
  this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
2549
1739
  if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
@@ -2559,12 +1749,6 @@ export class Matterbridge extends EventEmitter {
2559
1749
  });
2560
1750
  }
2561
1751
  }
2562
- /**
2563
- * Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
2564
- *
2565
- * @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
2566
- * @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
2567
- */
2568
1752
  sanitizeFabricInformations(fabricInfo) {
2569
1753
  return fabricInfo.map((info) => {
2570
1754
  return {
@@ -2578,12 +1762,6 @@ export class Matterbridge extends EventEmitter {
2578
1762
  };
2579
1763
  });
2580
1764
  }
2581
- /**
2582
- * Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
2583
- *
2584
- * @param {SessionsBehavior.Session[]} sessions - The array of session information objects.
2585
- * @returns {SanitizedSession[]} An array of sanitized session information objects.
2586
- */
2587
1765
  sanitizeSessionInformation(sessions) {
2588
1766
  return sessions
2589
1767
  .filter((session) => session.isPeerActive)
@@ -2610,21 +1788,7 @@ export class Matterbridge extends EventEmitter {
2610
1788
  };
2611
1789
  });
2612
1790
  }
2613
- /**
2614
- * Sets the reachability of the specified aggregator node bridged devices and trigger.
2615
- *
2616
- * @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
2617
- * @param {boolean} reachable - A boolean indicating the reachability status to set.
2618
- */
2619
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2620
1791
  async setAggregatorReachability(aggregatorNode, reachable) {
2621
- /*
2622
- for (const child of aggregatorNode.parts) {
2623
- this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
2624
- await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
2625
- child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
2626
- }
2627
- */
2628
1792
  }
2629
1793
  getVendorIdName = (vendorId) => {
2630
1794
  if (!vendorId)
@@ -2661,11 +1825,13 @@ export class Matterbridge extends EventEmitter {
2661
1825
  case 5264:
2662
1826
  vendorName = '(Shelly)';
2663
1827
  break;
1828
+ case 0x1488:
1829
+ vendorName = '(ShortcutLabsFlic)';
1830
+ break;
2664
1831
  case 65521:
2665
- vendorName = '(MatterServer)';
1832
+ vendorName = '(MatterTest)';
2666
1833
  break;
2667
1834
  }
2668
1835
  return vendorName;
2669
1836
  };
2670
1837
  }
2671
- //# sourceMappingURL=matterbridge.js.map