matterbridge 3.0.2 → 3.0.3-dev-20250517-720018f

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 (164) hide show
  1. package/CHANGELOG.md +22 -3
  2. package/dist/cli.js +2 -37
  3. package/dist/cluster/export.js +0 -2
  4. package/dist/defaultConfigSchema.js +0 -23
  5. package/dist/deviceManager.js +1 -94
  6. package/dist/frontend.js +71 -369
  7. package/dist/helpers.js +25 -56
  8. package/dist/index.js +2 -31
  9. package/dist/logger/export.js +0 -1
  10. package/dist/matter/behaviors.js +0 -2
  11. package/dist/matter/clusters.js +0 -2
  12. package/dist/matter/devices.js +0 -2
  13. package/dist/matter/endpoints.js +0 -2
  14. package/dist/matter/export.js +0 -2
  15. package/dist/matter/types.js +0 -2
  16. package/dist/matterbridge.js +143 -873
  17. package/dist/matterbridgeAccessoryPlatform.js +0 -34
  18. package/dist/matterbridgeBehaviors.js +4 -53
  19. package/dist/matterbridgeDeviceTypes.js +34 -431
  20. package/dist/matterbridgeDynamicPlatform.js +0 -34
  21. package/dist/matterbridgeEndpoint.js +11 -807
  22. package/dist/matterbridgeEndpointHelpers.js +9 -147
  23. package/dist/matterbridgePlatform.js +7 -225
  24. package/dist/matterbridgeTypes.js +0 -24
  25. package/dist/pluginManager.js +3 -264
  26. package/dist/roboticVacuumCleaner.js +3 -39
  27. package/dist/shelly.js +7 -155
  28. package/dist/storage/export.js +0 -1
  29. package/dist/update.js +0 -53
  30. package/dist/utils/colorUtils.js +2 -205
  31. package/dist/utils/commandLine.js +0 -53
  32. package/dist/utils/copyDirectory.js +1 -37
  33. package/dist/utils/createZip.js +2 -42
  34. package/dist/utils/deepCopy.js +0 -38
  35. package/dist/utils/deepEqual.js +1 -71
  36. package/dist/utils/export.js +0 -1
  37. package/dist/utils/hex.js +0 -57
  38. package/dist/utils/isvalid.js +0 -100
  39. package/dist/utils/network.js +5 -76
  40. package/dist/utils/wait.js +20 -56
  41. package/frontend/build/asset-manifest.json +3 -3
  42. package/frontend/build/index.html +1 -1
  43. package/frontend/build/static/js/{main.f6e0f736.js → main.2486c3e3.js} +3 -3
  44. package/frontend/build/static/js/{main.f6e0f736.js.map → main.2486c3e3.js.map} +1 -1
  45. package/npm-shrinkwrap.json +2 -2
  46. package/package.json +1 -2
  47. package/dist/cli.d.ts +0 -29
  48. package/dist/cli.d.ts.map +0 -1
  49. package/dist/cli.js.map +0 -1
  50. package/dist/cluster/export.d.ts +0 -2
  51. package/dist/cluster/export.d.ts.map +0 -1
  52. package/dist/cluster/export.js.map +0 -1
  53. package/dist/defaultConfigSchema.d.ts +0 -27
  54. package/dist/defaultConfigSchema.d.ts.map +0 -1
  55. package/dist/defaultConfigSchema.js.map +0 -1
  56. package/dist/deviceManager.d.ts +0 -114
  57. package/dist/deviceManager.d.ts.map +0 -1
  58. package/dist/deviceManager.js.map +0 -1
  59. package/dist/frontend.d.ts +0 -241
  60. package/dist/frontend.d.ts.map +0 -1
  61. package/dist/frontend.js.map +0 -1
  62. package/dist/helpers.d.ts +0 -46
  63. package/dist/helpers.d.ts.map +0 -1
  64. package/dist/helpers.js.map +0 -1
  65. package/dist/index.d.ts +0 -36
  66. package/dist/index.d.ts.map +0 -1
  67. package/dist/index.js.map +0 -1
  68. package/dist/logger/export.d.ts +0 -2
  69. package/dist/logger/export.d.ts.map +0 -1
  70. package/dist/logger/export.js.map +0 -1
  71. package/dist/matter/behaviors.d.ts +0 -2
  72. package/dist/matter/behaviors.d.ts.map +0 -1
  73. package/dist/matter/behaviors.js.map +0 -1
  74. package/dist/matter/clusters.d.ts +0 -2
  75. package/dist/matter/clusters.d.ts.map +0 -1
  76. package/dist/matter/clusters.js.map +0 -1
  77. package/dist/matter/devices.d.ts +0 -2
  78. package/dist/matter/devices.d.ts.map +0 -1
  79. package/dist/matter/devices.js.map +0 -1
  80. package/dist/matter/endpoints.d.ts +0 -2
  81. package/dist/matter/endpoints.d.ts.map +0 -1
  82. package/dist/matter/endpoints.js.map +0 -1
  83. package/dist/matter/export.d.ts +0 -5
  84. package/dist/matter/export.d.ts.map +0 -1
  85. package/dist/matter/export.js.map +0 -1
  86. package/dist/matter/types.d.ts +0 -3
  87. package/dist/matter/types.d.ts.map +0 -1
  88. package/dist/matter/types.js.map +0 -1
  89. package/dist/matterbridge.d.ts +0 -435
  90. package/dist/matterbridge.d.ts.map +0 -1
  91. package/dist/matterbridge.js.map +0 -1
  92. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -40
  93. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  94. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  95. package/dist/matterbridgeBehaviors.d.ts +0 -1188
  96. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  97. package/dist/matterbridgeBehaviors.js.map +0 -1
  98. package/dist/matterbridgeDeviceTypes.d.ts +0 -494
  99. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  100. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  101. package/dist/matterbridgeDynamicPlatform.d.ts +0 -40
  102. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  103. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  104. package/dist/matterbridgeEndpoint.d.ts +0 -965
  105. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  106. package/dist/matterbridgeEndpoint.js.map +0 -1
  107. package/dist/matterbridgeEndpointHelpers.d.ts +0 -2728
  108. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  109. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  110. package/dist/matterbridgePlatform.d.ts +0 -294
  111. package/dist/matterbridgePlatform.d.ts.map +0 -1
  112. package/dist/matterbridgePlatform.js.map +0 -1
  113. package/dist/matterbridgeTypes.d.ts +0 -187
  114. package/dist/matterbridgeTypes.d.ts.map +0 -1
  115. package/dist/matterbridgeTypes.js.map +0 -1
  116. package/dist/pluginManager.d.ts +0 -273
  117. package/dist/pluginManager.d.ts.map +0 -1
  118. package/dist/pluginManager.js.map +0 -1
  119. package/dist/roboticVacuumCleaner.d.ts +0 -43
  120. package/dist/roboticVacuumCleaner.d.ts.map +0 -1
  121. package/dist/roboticVacuumCleaner.js.map +0 -1
  122. package/dist/shelly.d.ts +0 -153
  123. package/dist/shelly.d.ts.map +0 -1
  124. package/dist/shelly.js.map +0 -1
  125. package/dist/storage/export.d.ts +0 -2
  126. package/dist/storage/export.d.ts.map +0 -1
  127. package/dist/storage/export.js.map +0 -1
  128. package/dist/update.d.ts +0 -58
  129. package/dist/update.d.ts.map +0 -1
  130. package/dist/update.js.map +0 -1
  131. package/dist/utils/colorUtils.d.ts +0 -61
  132. package/dist/utils/colorUtils.d.ts.map +0 -1
  133. package/dist/utils/colorUtils.js.map +0 -1
  134. package/dist/utils/commandLine.d.ts +0 -58
  135. package/dist/utils/commandLine.d.ts.map +0 -1
  136. package/dist/utils/commandLine.js.map +0 -1
  137. package/dist/utils/copyDirectory.d.ts +0 -32
  138. package/dist/utils/copyDirectory.d.ts.map +0 -1
  139. package/dist/utils/copyDirectory.js.map +0 -1
  140. package/dist/utils/createZip.d.ts +0 -38
  141. package/dist/utils/createZip.d.ts.map +0 -1
  142. package/dist/utils/createZip.js.map +0 -1
  143. package/dist/utils/deepCopy.d.ts +0 -31
  144. package/dist/utils/deepCopy.d.ts.map +0 -1
  145. package/dist/utils/deepCopy.js.map +0 -1
  146. package/dist/utils/deepEqual.d.ts +0 -53
  147. package/dist/utils/deepEqual.d.ts.map +0 -1
  148. package/dist/utils/deepEqual.js.map +0 -1
  149. package/dist/utils/export.d.ts +0 -11
  150. package/dist/utils/export.d.ts.map +0 -1
  151. package/dist/utils/export.js.map +0 -1
  152. package/dist/utils/hex.d.ts +0 -48
  153. package/dist/utils/hex.d.ts.map +0 -1
  154. package/dist/utils/hex.js.map +0 -1
  155. package/dist/utils/isvalid.d.ts +0 -102
  156. package/dist/utils/isvalid.d.ts.map +0 -1
  157. package/dist/utils/isvalid.js.map +0 -1
  158. package/dist/utils/network.d.ts +0 -69
  159. package/dist/utils/network.d.ts.map +0 -1
  160. package/dist/utils/network.js.map +0 -1
  161. package/dist/utils/wait.d.ts +0 -51
  162. package/dist/utils/wait.d.ts.map +0 -1
  163. package/dist/utils/wait.js.map +0 -1
  164. /package/frontend/build/static/js/{main.f6e0f736.js.LICENSE.txt → main.2486c3e3.js.LICENSE.txt} +0 -0
@@ -1,36 +1,10 @@
1
- /**
2
- * This file contains the class Matterbridge.
3
- *
4
- * @file matterbridge.ts
5
- * @author Luca Liguori
6
- * @date 2023-12-29
7
- * @version 1.5.3
8
- *
9
- * Copyright 2023, 2024, 2025 Luca Liguori.
10
- *
11
- * Licensed under the Apache License, Version 2.0 (the "License");
12
- * you may not use this file except in compliance with the License.
13
- * You may obtain a copy of the License at
14
- *
15
- * http://www.apache.org/licenses/LICENSE-2.0
16
- *
17
- * Unless required by applicable law or agreed to in writing, software
18
- * distributed under the License is distributed on an "AS IS" BASIS,
19
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
- * See the License for the specific language governing permissions and
21
- * limitations under the License. *
22
- */
23
- // Node.js modules
24
1
  import os from 'node:os';
25
2
  import path from 'node:path';
26
3
  import { promises as fs } from 'node:fs';
27
4
  import EventEmitter from 'node:events';
28
5
  import { inspect } from 'node:util';
29
- // AnsiLogger module
30
6
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from './logger/export.js';
31
- // NodeStorage module
32
7
  import { NodeStorageManager } from './storage/export.js';
33
- // Matterbridge
34
8
  import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout, waiter, isValidString, parseVersionString, isValidNumber } from './utils/export.js';
35
9
  import { logInterfaces, getGlobalNodeModules } from './utils/network.js';
36
10
  import { dev, plg, typ } from './matterbridgeTypes.js';
@@ -40,15 +14,11 @@ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
40
14
  import { bridge } from './matterbridgeDeviceTypes.js';
41
15
  import { Frontend } from './frontend.js';
42
16
  import { addVirtualDevices } from './helpers.js';
43
- // @matter
44
17
  import { DeviceTypeId, Endpoint, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode, UINT32_MAX, UINT16_MAX, } from '@matter/main';
45
18
  import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
46
19
  import { AggregatorEndpoint } from '@matter/main/endpoints';
47
20
  import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
48
21
  import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
49
- /**
50
- * Represents the Matterbridge application.
51
- */
52
22
  export class Matterbridge extends EventEmitter {
53
23
  systemInformation = {
54
24
  interfaceName: '',
@@ -90,12 +60,13 @@ export class Matterbridge extends EventEmitter {
90
60
  matterbridgeAdvertise: false,
91
61
  bridgeMode: '',
92
62
  restartMode: '',
63
+ virtualMode: 'outlet',
93
64
  readOnly: hasParameter('readonly') || hasParameter('shelly'),
94
65
  shellyBoard: hasParameter('shelly'),
95
66
  shellySysUpdate: false,
96
67
  shellyMainUpdate: false,
97
68
  profile: getParameter('profile'),
98
- loggerLevel: "info" /* LogLevel.INFO */,
69
+ loggerLevel: "info",
99
70
  fileLogger: false,
100
71
  matterLoggerLevel: MatterLogLevel.INFO,
101
72
  matterFileLogger: false,
@@ -134,11 +105,9 @@ export class Matterbridge extends EventEmitter {
134
105
  plugins;
135
106
  devices;
136
107
  frontend = new Frontend(this);
137
- // Matterbridge storage
138
108
  nodeStorage;
139
109
  nodeContext;
140
110
  nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
141
- // Cleanup
142
111
  hasCleanupStarted = false;
143
112
  initialized = false;
144
113
  execRunningCount = 0;
@@ -151,22 +120,19 @@ export class Matterbridge extends EventEmitter {
151
120
  sigtermHandler;
152
121
  exceptionHandler;
153
122
  rejectionHandler;
154
- // Matter environment
155
123
  environment = Environment.default;
156
- // Matter storage
157
124
  matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
158
125
  matterStorageService;
159
126
  matterStorageManager;
160
127
  matterbridgeContext;
161
128
  controllerContext;
162
- // Matter parameters
163
- mdnsInterface; // matter server node mdnsInterface: e.g. 'eth0' or 'wlan0' or 'WiFi'
164
- ipv4address; // matter server node listeningAddressIpv4
165
- ipv6address; // matter server node listeningAddressIpv6
166
- port; // first server node port
167
- passcode; // first server node passcode
168
- discriminator; // first server node discriminator
169
- certification; // device certification
129
+ mdnsInterface;
130
+ ipv4address;
131
+ ipv6address;
132
+ port;
133
+ passcode;
134
+ discriminator;
135
+ certification;
170
136
  serverNode;
171
137
  aggregatorNode;
172
138
  aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
@@ -174,50 +140,21 @@ export class Matterbridge extends EventEmitter {
174
140
  aggregatorProductId = getIntParameter('productId') ?? 0x8000;
175
141
  aggregatorProductName = getParameter('productName') ?? 'Matterbridge aggregator';
176
142
  static instance;
177
- // We load asyncronously so is private
178
143
  constructor() {
179
144
  super();
180
145
  }
181
- /**
182
- * Emits an event of the specified type with the provided arguments.
183
- *
184
- * @template K - The type of the event.
185
- * @param {K} eventName - The name of the event to emit.
186
- * @param {...MatterbridgeEvent[K]} args - The arguments to pass to the event listeners.
187
- * @returns {boolean} - Returns true if the event had listeners, false otherwise.
188
- */
189
146
  emit(eventName, ...args) {
190
147
  return super.emit(eventName, ...args);
191
148
  }
192
- /**
193
- * Registers an event listener for the specified event type.
194
- *
195
- * @template K - The type of the event.
196
- * @param {K} eventName - The name of the event to listen for.
197
- * @param {(...args: MatterbridgeEvent[K]) => void} listener - The callback function to invoke when the event is emitted.
198
- * @returns {this} - Returns the instance of the Matterbridge class.
199
- */
200
149
  on(eventName, listener) {
201
150
  return super.on(eventName, listener);
202
151
  }
203
- /**
204
- * Retrieves the list of Matterbridge devices.
205
- * @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
206
- */
207
152
  getDevices() {
208
153
  return this.devices.array();
209
154
  }
210
- /**
211
- * Retrieves the list of registered plugins.
212
- * @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
213
- */
214
155
  getPlugins() {
215
156
  return this.plugins.array();
216
157
  }
217
- /**
218
- * Set the logger logLevel for the Matterbridge classes.
219
- * @param {LogLevel} logLevel The logger logLevel to set.
220
- */
221
158
  async setLogLevel(logLevel) {
222
159
  if (this.log)
223
160
  this.log.logLevel = logLevel;
@@ -231,31 +168,19 @@ export class Matterbridge extends EventEmitter {
231
168
  for (const plugin of this.plugins) {
232
169
  if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
233
170
  continue;
234
- plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel;
235
- await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel);
236
- }
237
- // Set the global logger callback for the WebSocketServer to the common minimum logLevel
238
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
239
- if (this.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
240
- callbackLogLevel = "info" /* LogLevel.INFO */;
241
- if (this.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
242
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
171
+ plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
172
+ await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
173
+ }
174
+ let callbackLogLevel = "notice";
175
+ if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
176
+ callbackLogLevel = "info";
177
+ if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
178
+ callbackLogLevel = "debug";
243
179
  AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
244
180
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
245
181
  }
246
- /** ***********************************************************************************************************************************/
247
- /** loadInstance() and cleanup() methods */
248
- /** ***********************************************************************************************************************************/
249
- /**
250
- * Loads an instance of the Matterbridge class.
251
- * If an instance already exists, return that instance.
252
- *
253
- * @param initialize - Whether to initialize the Matterbridge instance after loading.
254
- * @returns The loaded Matterbridge instance.
255
- */
256
182
  static async loadInstance(initialize = false) {
257
183
  if (!Matterbridge.instance) {
258
- // eslint-disable-next-line no-console
259
184
  if (hasParameter('debug'))
260
185
  console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
261
186
  Matterbridge.instance = new Matterbridge();
@@ -264,14 +189,8 @@ export class Matterbridge extends EventEmitter {
264
189
  }
265
190
  return Matterbridge.instance;
266
191
  }
267
- /**
268
- * Call cleanup().
269
- * @deprecated This method is deprecated and is only used for jest tests.
270
- *
271
- */
272
192
  async destroyInstance() {
273
193
  this.log.info(`Destroy instance...`);
274
- // Save server nodes to close
275
194
  const servers = [];
276
195
  if (this.bridgeMode === 'bridge') {
277
196
  if (this.serverNode)
@@ -283,81 +202,71 @@ export class Matterbridge extends EventEmitter {
283
202
  servers.push(plugin.serverNode);
284
203
  }
285
204
  }
286
- // Cleanup
287
205
  await this.cleanup('destroying instance...', false);
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
- // Wait for the cleanup to finish
295
211
  await new Promise((resolve) => {
296
212
  setTimeout(resolve, 1000);
297
213
  });
298
214
  }
299
- /**
300
- * Initializes the Matterbridge application.
301
- *
302
- * @remarks
303
- * This method performs the necessary setup and initialization steps for the Matterbridge application.
304
- * It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
305
- * node version, registers signal handlers, initializes storage, and parses the command line.
306
- *
307
- * @returns A Promise that resolves when the initialization is complete.
308
- */
309
215
  async initialize() {
310
- // Set the restart mode
216
+ this.emit('initialize_started');
311
217
  if (hasParameter('service'))
312
218
  this.restartMode = 'service';
313
219
  if (hasParameter('docker'))
314
220
  this.restartMode = 'docker';
315
- // Set the matterbridge directory
316
221
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
222
+ this.matterbridgeInformation.homeDirectory = this.homeDirectory;
223
+ await this.createDirectory(this.homeDirectory, 'Matterbridge Home Directory');
317
224
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
318
- // Setup the matter environment
225
+ this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
226
+ await this.createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory');
227
+ await this.createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory');
228
+ this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
229
+ this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
230
+ await this.createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory');
231
+ this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
232
+ this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
233
+ await this.createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory');
234
+ const { fileURLToPath } = await import('node:url');
235
+ const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
236
+ this.rootDirectory = path.resolve(currentFileDirectory, '../');
237
+ this.matterbridgeInformation.rootDirectory = this.rootDirectory;
319
238
  this.environment.vars.set('log.level', MatterLogLevel.INFO);
320
239
  this.environment.vars.set('log.format', MatterLogFormat.ANSI);
321
240
  this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
322
241
  this.environment.vars.set('runtime.signals', false);
323
242
  this.environment.vars.set('runtime.exitcode', false);
324
- // Create the matterbridge logger
325
- this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
326
- // Register process handlers
243
+ this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
327
244
  this.registerProcessHandlers();
328
- // Initialize nodeStorage and nodeContext
329
245
  try {
330
246
  this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
331
247
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
332
248
  this.log.debug('Creating node storage context for matterbridge');
333
249
  this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
334
- // TODO: Remove this code when node-persist-manager is updated
335
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
336
250
  const keys = (await this.nodeStorage?.storage.keys());
337
251
  for (const key of keys) {
338
252
  this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
339
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
340
253
  await this.nodeStorage?.storage.get(key);
341
254
  }
342
255
  const storages = await this.nodeStorage.getStorageNames();
343
256
  for (const storage of storages) {
344
257
  this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
345
258
  const nodeContext = await this.nodeStorage?.createStorage(storage);
346
- // TODO: Remove this code when node-persist-manager is updated
347
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
348
259
  const keys = (await nodeContext?.storage.keys());
349
260
  keys.forEach(async (key) => {
350
261
  this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
351
262
  await nodeContext?.get(key);
352
263
  });
353
264
  }
354
- // Creating a backup of the node storage since it is not corrupted
355
265
  this.log.debug('Creating node storage backup...');
356
266
  await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
357
267
  this.log.debug('Created node storage backup');
358
268
  }
359
269
  catch (error) {
360
- // Restoring the backup of the node storage since it is corrupted
361
270
  this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
362
271
  if (hasParameter('norestore')) {
363
272
  this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
@@ -372,32 +281,24 @@ export class Matterbridge extends EventEmitter {
372
281
  this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
373
282
  throw new Error('Fatal error creating node storage manager and context for matterbridge');
374
283
  }
375
- // Set the first port to use for the commissioning server (will be incremented in childbridge mode)
376
284
  this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
377
- // Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
378
285
  this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
379
- // Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
380
286
  this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
381
- // Certificate management
382
- const pairingFilePath = path.join(this.homeDirectory, '.mattercert', 'pairing.json');
287
+ const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
383
288
  try {
384
289
  await fs.access(pairingFilePath, fs.constants.R_OK);
385
290
  const pairingFileContent = await fs.readFile(pairingFilePath, 'utf8');
386
291
  const pairingFileJson = JSON.parse(pairingFileContent);
387
- // Override the passcode and discriminator if they are present in the pairing file
388
292
  if (pairingFileJson.passcode && pairingFileJson.discriminator) {
389
293
  this.passcode = pairingFileJson.passcode;
390
294
  this.discriminator = pairingFileJson.discriminator;
391
- this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf}`);
295
+ this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
392
296
  }
393
- // Set the certification if it is present in the pairing file
394
297
  if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
395
298
  const hexStringToUint8Array = (hexString) => {
396
299
  const matches = hexString.match(/.{1,2}/g);
397
300
  return matches ? new Uint8Array(matches.map((byte) => parseInt(byte, 16))) : new Uint8Array();
398
301
  };
399
- // const hexString = Buffer.from('Test string', 'utf-8').toString('hex');
400
- // console.log(hexString, Buffer.from(hexStringToUint8Array(hexString)).toString('utf-8'));
401
302
  this.certification = {
402
303
  privateKey: hexStringToUint8Array(pairingFileJson.privateKey),
403
304
  certificate: hexStringToUint8Array(pairingFileJson.certificate),
@@ -410,44 +311,41 @@ export class Matterbridge extends EventEmitter {
410
311
  catch (error) {
411
312
  this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
412
313
  }
413
- // Store the passcode, discriminator and port in the node context
414
314
  await this.nodeContext.set('matterport', this.port);
415
315
  await this.nodeContext.set('matterpasscode', this.passcode);
416
316
  await this.nodeContext.set('matterdiscriminator', this.discriminator);
417
- this.log.debug(`Initializing server node for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
418
- // Set matterbridge logger level (context: matterbridgeLogLevel)
317
+ this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
419
318
  if (hasParameter('logger')) {
420
319
  const level = getParameter('logger');
421
320
  if (level === 'debug') {
422
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
321
+ this.log.logLevel = "debug";
423
322
  }
424
323
  else if (level === 'info') {
425
- this.log.logLevel = "info" /* LogLevel.INFO */;
324
+ this.log.logLevel = "info";
426
325
  }
427
326
  else if (level === 'notice') {
428
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
327
+ this.log.logLevel = "notice";
429
328
  }
430
329
  else if (level === 'warn') {
431
- this.log.logLevel = "warn" /* LogLevel.WARN */;
330
+ this.log.logLevel = "warn";
432
331
  }
433
332
  else if (level === 'error') {
434
- this.log.logLevel = "error" /* LogLevel.ERROR */;
333
+ this.log.logLevel = "error";
435
334
  }
436
335
  else if (level === 'fatal') {
437
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
336
+ this.log.logLevel = "fatal";
438
337
  }
439
338
  else {
440
339
  this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
441
- this.log.logLevel = "info" /* LogLevel.INFO */;
340
+ this.log.logLevel = "info";
442
341
  }
443
342
  }
444
343
  else {
445
- this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" /* LogLevel.NOTICE */ : "info" /* LogLevel.INFO */);
344
+ this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
446
345
  }
447
346
  this.frontend.logLevel = this.log.logLevel;
448
347
  MatterbridgeEndpoint.logLevel = this.log.logLevel;
449
348
  this.matterbridgeInformation.loggerLevel = this.log.logLevel;
450
- // Create the file logger for matterbridge (context: matterbridgeFileLog)
451
349
  if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
452
350
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
453
351
  this.matterbridgeInformation.fileLogger = true;
@@ -456,7 +354,6 @@ export class Matterbridge extends EventEmitter {
456
354
  this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
457
355
  if (this.profile !== undefined)
458
356
  this.log.debug(`Matterbridge profile: ${this.profile}.`);
459
- // Set matter.js logger level, format and logger (context: matterLogLevel)
460
357
  if (hasParameter('matterlogger')) {
461
358
  const level = getParameter('matterlogger');
462
359
  if (level === 'debug') {
@@ -488,7 +385,6 @@ export class Matterbridge extends EventEmitter {
488
385
  Logger.format = MatterLogFormat.ANSI;
489
386
  Logger.setLogger('default', this.createMatterLogger());
490
387
  this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
491
- // Create the file logger for matter.js (context: matterFileLog)
492
388
  if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
493
389
  this.matterbridgeInformation.matterFileLogger = true;
494
390
  Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
@@ -497,7 +393,6 @@ export class Matterbridge extends EventEmitter {
497
393
  });
498
394
  }
499
395
  this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
500
- // Log network interfaces
501
396
  const networkInterfaces = os.networkInterfaces();
502
397
  const availableAddresses = Object.entries(networkInterfaces);
503
398
  const availableInterfaces = Object.keys(networkInterfaces);
@@ -509,7 +404,6 @@ export class Matterbridge extends EventEmitter {
509
404
  });
510
405
  }
511
406
  }
512
- // Set the interface to use for matter server node mdnsInterface
513
407
  if (hasParameter('mdnsinterface')) {
514
408
  this.mdnsInterface = getParameter('mdnsinterface');
515
409
  }
@@ -518,7 +412,6 @@ export class Matterbridge extends EventEmitter {
518
412
  if (this.mdnsInterface === '')
519
413
  this.mdnsInterface = undefined;
520
414
  }
521
- // Validate mdnsInterface
522
415
  if (this.mdnsInterface) {
523
416
  if (!availableInterfaces.includes(this.mdnsInterface)) {
524
417
  this.log.error(`Invalid mdnsInterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
@@ -531,7 +424,6 @@ export class Matterbridge extends EventEmitter {
531
424
  }
532
425
  if (this.mdnsInterface)
533
426
  this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
534
- // Set the listeningAddressIpv4 for the matter commissioning server
535
427
  if (hasParameter('ipv4address')) {
536
428
  this.ipv4address = getParameter('ipv4address');
537
429
  }
@@ -540,7 +432,6 @@ export class Matterbridge extends EventEmitter {
540
432
  if (this.ipv4address === '')
541
433
  this.ipv4address = undefined;
542
434
  }
543
- // Validate ipv4address
544
435
  if (this.ipv4address) {
545
436
  let isValid = false;
546
437
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -556,7 +447,6 @@ export class Matterbridge extends EventEmitter {
556
447
  await this.nodeContext.remove('matteripv4address');
557
448
  }
558
449
  }
559
- // Set the listeningAddressIpv6 for the matter commissioning server
560
450
  if (hasParameter('ipv6address')) {
561
451
  this.ipv6address = getParameter('ipv6address');
562
452
  }
@@ -565,7 +455,6 @@ export class Matterbridge extends EventEmitter {
565
455
  if (this.ipv6address === '')
566
456
  this.ipv6address = undefined;
567
457
  }
568
- // Validate ipv6address
569
458
  if (this.ipv6address) {
570
459
  let isValid = false;
571
460
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -586,19 +475,22 @@ export class Matterbridge extends EventEmitter {
586
475
  await this.nodeContext.remove('matteripv6address');
587
476
  }
588
477
  }
589
- // Initialize PluginManager
478
+ if (hasParameter('novirtual')) {
479
+ this.matterbridgeInformation.virtualMode = 'disabled';
480
+ await this.nodeContext.set('virtualmode', 'disabled');
481
+ }
482
+ else {
483
+ this.matterbridgeInformation.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
484
+ }
485
+ this.log.debug(`Virtual mode ${this.matterbridgeInformation.virtualMode}.`);
590
486
  this.plugins = new PluginManager(this);
591
487
  await this.plugins.loadFromStorage();
592
488
  this.plugins.logLevel = this.log.logLevel;
593
- // Initialize DeviceManager
594
489
  this.devices = new DeviceManager(this, this.nodeContext);
595
490
  this.devices.logLevel = this.log.logLevel;
596
- // Get the plugins from node storage and create the plugins node storage contexts
597
491
  for (const plugin of this.plugins) {
598
492
  const packageJson = await this.plugins.parse(plugin);
599
493
  if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
600
- // Try to reinstall the plugin from npm (for Docker pull and external plugins)
601
- // We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
602
494
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
603
495
  try {
604
496
  await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
@@ -620,7 +512,6 @@ export class Matterbridge extends EventEmitter {
620
512
  await plugin.nodeContext.set('description', plugin.description);
621
513
  await plugin.nodeContext.set('author', plugin.author);
622
514
  }
623
- // Log system info and create .matterbridge directory
624
515
  await this.logNodeAndSystemInfo();
625
516
  this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
626
517
  `${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
@@ -628,7 +519,6 @@ export class Matterbridge extends EventEmitter {
628
519
  `${hasParameter('controller') ? 'mode controller ' : ''}` +
629
520
  `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
630
521
  `running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
631
- // Check node version and throw error
632
522
  const minNodeVersion = 18;
633
523
  const nodeVersion = process.versions.node;
634
524
  const versionMajor = parseInt(nodeVersion.split('.')[0]);
@@ -636,15 +526,10 @@ export class Matterbridge extends EventEmitter {
636
526
  this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
637
527
  throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
638
528
  }
639
- // Parse command line
640
529
  await this.parseCommandLine();
530
+ this.emit('initialize_completed');
641
531
  this.initialized = true;
642
532
  }
643
- /**
644
- * Parses the command line arguments and performs the corresponding actions.
645
- * @private
646
- * @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
647
- */
648
533
  async parseCommandLine() {
649
534
  if (hasParameter('help')) {
650
535
  this.log.info(`\nUsage: matterbridge [options]\n
@@ -658,7 +543,9 @@ export class Matterbridge extends EventEmitter {
658
543
  - ipv6address [address]: set the ipv6 interface address to use for the matter listener (default all interfaces)
659
544
  - frontend [port]: start the frontend on the given port (default 8283)
660
545
  - logger: set the matterbridge logger level: debug | info | notice | warn | error | fatal (default info)
546
+ - filelogger enable the matterbridge file logger (matterbridge.log)
661
547
  - matterlogger: set the matter.js logger level: debug | info | notice | warn | error | fatal (default info)
548
+ - matterfilelogger enable the matter.js file logger (matter.log)
662
549
  - reset: remove the commissioning for Matterbridge (bridge mode). Shutdown Matterbridge before using it!
663
550
  - factoryreset: remove all commissioning information and reset all internal storages. Shutdown Matterbridge before using it!
664
551
  - list: list the registered plugins
@@ -667,13 +554,14 @@ export class Matterbridge extends EventEmitter {
667
554
  - sudo: force the use of sudo to install or update packages if the internal logic fails
668
555
  - nosudo: force not to use sudo to install or update packages if the internal logic fails
669
556
  - norestore: force not to automatically restore the matterbridge node storage and the matter storage from backup if it is corrupted
557
+ - novirtual: disable the creation of the virtual devices Restart, Update and Reboot Matterbridge
670
558
  - ssl: enable SSL for the frontend and WebSockerServer (certificates in .matterbridge/certs directory cert.pem, key.pem and ca.pem (optional))
671
559
  - vendorId: override the default vendorId 0xfff1
672
560
  - vendorName: override the default vendorName "Matterbridge"
673
561
  - productId: override the default productId 0x8000
674
562
  - productName: override the default productName "Matterbridge aggregator"
675
563
  - service: enable the service mode (used in the systemctl configuration file)
676
- - docker: enable the docker mode (used in the Dockerfile to build the docker image)
564
+ - docker: enable the docker mode (used in the docker image)
677
565
  - homedir: override the home directory (default: os.homedir())
678
566
  - add [plugin path]: register the plugin from the given absolute or relative path
679
567
  - add [plugin name]: register the globally installed plugin with the given name
@@ -763,7 +651,6 @@ export class Matterbridge extends EventEmitter {
763
651
  this.shutdown = true;
764
652
  return;
765
653
  }
766
- // Start the matter storage and create the matterbridge context
767
654
  try {
768
655
  await this.startMatterStorage();
769
656
  }
@@ -771,14 +658,12 @@ export class Matterbridge extends EventEmitter {
771
658
  this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
772
659
  throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
773
660
  }
774
- // Clear the matterbridge context if the reset parameter is set
775
661
  if (hasParameter('reset') && getParameter('reset') === undefined) {
776
662
  this.initialized = true;
777
663
  await this.shutdownProcessAndReset();
778
664
  this.shutdown = true;
779
665
  return;
780
666
  }
781
- // Clear matterbridge plugin context if the reset parameter is set
782
667
  if (hasParameter('reset') && getParameter('reset') !== undefined) {
783
668
  this.log.debug(`Reset plugin ${getParameter('reset')}`);
784
669
  const plugin = this.plugins.get(getParameter('reset'));
@@ -788,11 +673,11 @@ export class Matterbridge extends EventEmitter {
788
673
  this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
789
674
  }
790
675
  else {
791
- await matterStorageManager?.createContext('events')?.clearAll();
792
- await matterStorageManager?.createContext('fabrics')?.clearAll();
793
- await matterStorageManager?.createContext('root')?.clearAll();
794
- await matterStorageManager?.createContext('sessions')?.clearAll();
795
- await matterStorageManager?.createContext('persist')?.clearAll();
676
+ await matterStorageManager.createContext('events')?.clearAll();
677
+ await matterStorageManager.createContext('fabrics')?.clearAll();
678
+ await matterStorageManager.createContext('root')?.clearAll();
679
+ await matterStorageManager.createContext('sessions')?.clearAll();
680
+ await matterStorageManager.createContext('persist')?.clearAll();
796
681
  this.log.info(`Reset commissionig for plugin ${plg}${plugin.name}${nf} done! Remove the device from the controller.`);
797
682
  }
798
683
  }
@@ -803,37 +688,30 @@ export class Matterbridge extends EventEmitter {
803
688
  this.shutdown = true;
804
689
  return;
805
690
  }
806
- // Initialize frontend
807
691
  if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
808
692
  await this.frontend.start(getIntParameter('frontend'));
809
- // Check in 30 seconds the latest versions
810
693
  this.checkUpdateTimeout = setTimeout(async () => {
811
694
  const { checkUpdates } = await import('./update.js');
812
695
  checkUpdates(this);
813
696
  }, 30 * 1000).unref();
814
- // Check each 24 hours the latest versions
815
697
  this.checkUpdateInterval = setInterval(async () => {
816
698
  const { checkUpdates } = await import('./update.js');
817
699
  checkUpdates(this);
818
700
  }, 12 * 60 * 60 * 1000).unref();
819
- // Start the matterbridge in mode test
820
701
  if (hasParameter('test')) {
821
702
  this.bridgeMode = 'bridge';
822
703
  MatterbridgeEndpoint.bridgeMode = 'bridge';
823
704
  return;
824
705
  }
825
- // Start the matterbridge in mode controller
826
706
  if (hasParameter('controller')) {
827
707
  this.bridgeMode = 'controller';
828
708
  await this.startController();
829
709
  return;
830
710
  }
831
- // Check if the bridge mode is set and start matterbridge in bridge mode if not set
832
711
  if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
833
712
  this.log.info('Setting default matterbridge start mode to bridge');
834
713
  await this.nodeContext?.set('bridgeMode', 'bridge');
835
714
  }
836
- // Start matterbridge in bridge mode
837
715
  if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
838
716
  this.bridgeMode = 'bridge';
839
717
  MatterbridgeEndpoint.bridgeMode = 'bridge';
@@ -841,7 +719,6 @@ export class Matterbridge extends EventEmitter {
841
719
  await this.startBridge();
842
720
  return;
843
721
  }
844
- // Start matterbridge in childbridge mode
845
722
  if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
846
723
  this.bridgeMode = 'childbridge';
847
724
  MatterbridgeEndpoint.bridgeMode = 'childbridge';
@@ -850,20 +727,10 @@ export class Matterbridge extends EventEmitter {
850
727
  return;
851
728
  }
852
729
  }
853
- /**
854
- * Asynchronously loads and starts the registered plugins.
855
- *
856
- * This method is responsible for initializing and staarting all enabled plugins.
857
- * It ensures that each plugin is properly loaded and started before the bridge starts.
858
- *
859
- * @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
860
- */
861
730
  async startPlugins() {
862
- // Check, load and start the plugins
863
731
  for (const plugin of this.plugins) {
864
732
  plugin.configJson = await this.plugins.loadConfig(plugin);
865
733
  plugin.schemaJson = await this.plugins.loadSchema(plugin);
866
- // Check if the plugin is available
867
734
  if (!(await this.plugins.resolve(plugin.path))) {
868
735
  this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
869
736
  plugin.enabled = false;
@@ -883,14 +750,10 @@ export class Matterbridge extends EventEmitter {
883
750
  plugin.addedDevices = undefined;
884
751
  plugin.qrPairingCode = undefined;
885
752
  plugin.manualPairingCode = undefined;
886
- this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
753
+ this.plugins.load(plugin, true, 'Matterbridge is starting');
887
754
  }
888
755
  this.frontend.wssSendRefreshRequired('plugins');
889
756
  }
890
- /**
891
- * Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
892
- * When either of these signals are received, the cleanup method is called with an appropriate message.
893
- */
894
757
  registerProcessHandlers() {
895
758
  this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
896
759
  process.removeAllListeners('uncaughtException');
@@ -917,10 +780,7 @@ export class Matterbridge extends EventEmitter {
917
780
  };
918
781
  process.on('SIGTERM', this.sigtermHandler);
919
782
  }
920
- /**
921
- * Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
922
- */
923
- deregisterProcesslHandlers() {
783
+ deregisterProcessHandlers() {
924
784
  this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
925
785
  if (this.exceptionHandler)
926
786
  process.off('uncaughtException', this.exceptionHandler);
@@ -936,17 +796,12 @@ export class Matterbridge extends EventEmitter {
936
796
  process.off('SIGTERM', this.sigtermHandler);
937
797
  this.sigtermHandler = undefined;
938
798
  }
939
- /**
940
- * Logs the node and system information.
941
- */
942
799
  async logNodeAndSystemInfo() {
943
- // IP address information
944
800
  const networkInterfaces = os.networkInterfaces();
945
801
  this.systemInformation.interfaceName = '';
946
802
  this.systemInformation.ipv4Address = '';
947
803
  this.systemInformation.ipv6Address = '';
948
804
  for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
949
- // this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
950
805
  if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
951
806
  continue;
952
807
  if (!interfaceDetails) {
@@ -972,22 +827,19 @@ export class Matterbridge extends EventEmitter {
972
827
  break;
973
828
  }
974
829
  }
975
- // Node information
976
830
  this.systemInformation.nodeVersion = process.versions.node;
977
831
  const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
978
832
  const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
979
833
  const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
980
- // Host system information
981
834
  this.systemInformation.hostname = os.hostname();
982
835
  this.systemInformation.user = os.userInfo().username;
983
- this.systemInformation.osType = os.type(); // "Windows_NT", "Darwin", etc.
984
- this.systemInformation.osRelease = os.release(); // Kernel version
985
- this.systemInformation.osPlatform = os.platform(); // "win32", "linux", "darwin", etc.
986
- this.systemInformation.osArch = os.arch(); // "x64", "arm", etc.
987
- this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
988
- this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
989
- this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours'; // Convert to hours
990
- // Log the system information
836
+ this.systemInformation.osType = os.type();
837
+ this.systemInformation.osRelease = os.release();
838
+ this.systemInformation.osPlatform = os.platform();
839
+ this.systemInformation.osArch = os.arch();
840
+ this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
841
+ this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
842
+ this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
991
843
  this.log.debug('Host System Information:');
992
844
  this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
993
845
  this.log.debug(`- User: ${this.systemInformation.user}`);
@@ -1003,26 +855,18 @@ export class Matterbridge extends EventEmitter {
1003
855
  this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
1004
856
  this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
1005
857
  this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
1006
- // Home directory
1007
- this.homeDirectory = getParameter('homedir') ?? os.homedir();
1008
- this.matterbridgeInformation.homeDirectory = this.homeDirectory;
1009
- this.log.debug(`Home Directory: ${this.homeDirectory}`);
1010
- // Package root directory
1011
- const { fileURLToPath } = await import('node:url');
1012
- const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
1013
- this.rootDirectory = path.resolve(currentFileDirectory, '../');
1014
- this.matterbridgeInformation.rootDirectory = this.rootDirectory;
1015
858
  this.log.debug(`Root Directory: ${this.rootDirectory}`);
1016
- // Global node_modules directory
859
+ this.log.debug(`Home Directory: ${this.homeDirectory}`);
860
+ this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
861
+ this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
862
+ this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
1017
863
  if (this.nodeContext)
1018
864
  this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
1019
- // First run of Matterbridge so the node storage is empty
1020
865
  if (this.globalModulesDirectory === '') {
1021
866
  try {
1022
867
  this.execRunningCount++;
1023
- this.globalModulesDirectory = await getGlobalNodeModules();
868
+ this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory = await getGlobalNodeModules();
1024
869
  this.execRunningCount--;
1025
- this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
1026
870
  this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
1027
871
  await this.nodeContext?.set('globalModulesDirectory', this.globalModulesDirectory);
1028
872
  }
@@ -1032,153 +876,53 @@ export class Matterbridge extends EventEmitter {
1032
876
  }
1033
877
  else
1034
878
  this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
1035
- /* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
1036
- else {
1037
- this.getGlobalNodeModules()
1038
- .then(async (globalModulesDirectory) => {
1039
- this.globalModulesDirectory = globalModulesDirectory;
1040
- this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
1041
- this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
1042
- await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
1043
- })
1044
- .catch((error) => {
1045
- this.log.error(`Error getting global node_modules directory: ${error}`);
1046
- });
1047
- }*/
1048
- // Create the data directory .matterbridge in the home directory
1049
- this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
1050
- this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
1051
- try {
1052
- await fs.access(this.matterbridgeDirectory);
1053
- }
1054
- catch (err) {
1055
- if (err instanceof Error) {
1056
- const nodeErr = err;
1057
- if (nodeErr.code === 'ENOENT') {
1058
- try {
1059
- await fs.mkdir(this.matterbridgeDirectory, { recursive: true });
1060
- this.log.info(`Created Matterbridge Directory: ${this.matterbridgeDirectory}`);
1061
- }
1062
- catch (err) {
1063
- this.log.error(`Error creating directory: ${err}`);
1064
- }
1065
- }
1066
- else {
1067
- this.log.error(`Error accessing directory: ${err}`);
1068
- }
1069
- }
1070
- }
1071
- this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
1072
- // Create the plugin directory Matterbridge in the home directory
1073
- this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
1074
- this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
1075
- try {
1076
- await fs.access(this.matterbridgePluginDirectory);
1077
- }
1078
- catch (err) {
1079
- if (err instanceof Error) {
1080
- const nodeErr = err;
1081
- if (nodeErr.code === 'ENOENT') {
1082
- try {
1083
- await fs.mkdir(this.matterbridgePluginDirectory, { recursive: true });
1084
- this.log.info(`Created Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
1085
- }
1086
- catch (err) {
1087
- this.log.error(`Error creating directory: ${err}`);
1088
- }
1089
- }
1090
- else {
1091
- this.log.error(`Error accessing directory: ${err}`);
1092
- }
1093
- }
1094
- }
1095
- this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
1096
- // Create the matter cert directory in the home directory
1097
- this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
1098
- this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
1099
- try {
1100
- await fs.access(this.matterbridgeCertDirectory);
1101
- }
1102
- catch (err) {
1103
- if (err instanceof Error) {
1104
- const nodeErr = err;
1105
- if (nodeErr.code === 'ENOENT') {
1106
- try {
1107
- await fs.mkdir(this.matterbridgeCertDirectory, { recursive: true });
1108
- this.log.info(`Created .mattercert directory: ${this.matterbridgeCertDirectory}`);
1109
- }
1110
- catch (err) {
1111
- this.log.error(`Error creating .mattercert directory: ${err}`);
1112
- }
1113
- }
1114
- else {
1115
- this.log.error(`Error accessing .mattercert directory: ${err}`);
1116
- }
1117
- }
1118
- }
1119
- this.log.debug(`Matterbridge Matter Cert Directory: ${this.matterbridgeCertDirectory}`);
1120
- // Matterbridge version
1121
879
  const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
1122
- this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
1123
- this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
880
+ this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
881
+ this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeDevVersion = packageJson.version;
1124
882
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
1125
- // Matterbridge latest version
1126
883
  if (this.nodeContext)
1127
- this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
884
+ this.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
1128
885
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
1129
- // this.getMatterbridgeLatestVersion();
1130
- // Current working directory
886
+ if (this.nodeContext)
887
+ this.matterbridgeDevVersion = this.matterbridgeInformation.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
888
+ this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
1131
889
  const currentDir = process.cwd();
1132
890
  this.log.debug(`Current Working Directory: ${currentDir}`);
1133
- // Command line arguments (excluding 'node' and the script name)
1134
891
  const cmdArgs = process.argv.slice(2).join(' ');
1135
892
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
1136
893
  }
1137
- /**
1138
- * Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
1139
- *
1140
- * @returns {Function} The MatterLogger function.
1141
- */
1142
894
  createMatterLogger() {
1143
- const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
1144
- return (_level, formattedLog) => {
895
+ const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
896
+ return (level, formattedLog) => {
1145
897
  const logger = formattedLog.slice(44, 44 + 20).trim();
1146
898
  const message = formattedLog.slice(65);
1147
899
  matterLogger.logName = logger;
1148
- switch (_level) {
900
+ switch (level) {
1149
901
  case MatterLogLevel.DEBUG:
1150
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
902
+ matterLogger.log("debug", message);
1151
903
  break;
1152
904
  case MatterLogLevel.INFO:
1153
- matterLogger.log("info" /* LogLevel.INFO */, message);
905
+ matterLogger.log("info", message);
1154
906
  break;
1155
907
  case MatterLogLevel.NOTICE:
1156
- matterLogger.log("notice" /* LogLevel.NOTICE */, message);
908
+ matterLogger.log("notice", message);
1157
909
  break;
1158
910
  case MatterLogLevel.WARN:
1159
- matterLogger.log("warn" /* LogLevel.WARN */, message);
911
+ matterLogger.log("warn", message);
1160
912
  break;
1161
913
  case MatterLogLevel.ERROR:
1162
- matterLogger.log("error" /* LogLevel.ERROR */, message);
914
+ matterLogger.log("error", message);
1163
915
  break;
1164
916
  case MatterLogLevel.FATAL:
1165
- matterLogger.log("fatal" /* LogLevel.FATAL */, message);
917
+ matterLogger.log("fatal", message);
1166
918
  break;
1167
919
  default:
1168
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
920
+ matterLogger.log("debug", message);
1169
921
  break;
1170
922
  }
1171
923
  };
1172
924
  }
1173
- /**
1174
- * Creates a Matter File Logger.
1175
- *
1176
- * @param {string} filePath - The path to the log file.
1177
- * @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
1178
- * @returns {Function} - A function that logs formatted messages to the log file.
1179
- */
1180
925
  async createMatterFileLogger(filePath, unlink = false) {
1181
- // 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
1182
926
  let fileSize = 0;
1183
927
  if (unlink) {
1184
928
  try {
@@ -1188,7 +932,7 @@ export class Matterbridge extends EventEmitter {
1188
932
  this.log.debug(`Error unlinking the log file ${CYAN}${filePath}${db}: ${error instanceof Error ? error.message : error}`);
1189
933
  }
1190
934
  }
1191
- return async (_level, formattedLog) => {
935
+ return async (level, formattedLog) => {
1192
936
  if (fileSize > 100000000)
1193
937
  return;
1194
938
  fileSize += formattedLog.length;
@@ -1202,7 +946,7 @@ export class Matterbridge extends EventEmitter {
1202
946
  const parts = message.split(' ');
1203
947
  const logger = parts[1];
1204
948
  const finalMessage = parts.slice(2).join(' ') + os.EOL;
1205
- switch (_level) {
949
+ switch (level) {
1206
950
  case MatterLogLevel.DEBUG:
1207
951
  await fs.appendFile(filePath, `[${timestamp}] [${logger}] [debug] ${finalMessage}`);
1208
952
  break;
@@ -1227,21 +971,12 @@ export class Matterbridge extends EventEmitter {
1227
971
  }
1228
972
  };
1229
973
  }
1230
- /**
1231
- * Restarts the process by exiting the current instance and loading a new instance.
1232
- */
1233
974
  async restartProcess() {
1234
975
  await this.cleanup('restarting...', true);
1235
976
  }
1236
- /**
1237
- * Shut down the process by exiting the current process.
1238
- */
1239
977
  async shutdownProcess() {
1240
978
  await this.cleanup('shutting down...', false);
1241
979
  }
1242
- /**
1243
- * Update matterbridge and and shut down the process.
1244
- */
1245
980
  async updateProcess() {
1246
981
  this.log.info('Updating matterbridge...');
1247
982
  try {
@@ -1254,73 +989,52 @@ export class Matterbridge extends EventEmitter {
1254
989
  this.frontend.wssSendRestartRequired();
1255
990
  await this.cleanup('updating...', false);
1256
991
  }
1257
- /**
1258
- * Unregister all devices and shut down the process.
1259
- */
1260
992
  async unregisterAndShutdownProcess() {
1261
993
  this.log.info('Unregistering all devices and shutting down...');
1262
994
  for (const plugin of this.plugins) {
1263
995
  await this.removeAllBridgedEndpoints(plugin.name, 250);
1264
996
  }
1265
997
  this.log.debug('Waiting for the MessageExchange to finish...');
1266
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second for MessageExchange to finish
998
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1267
999
  this.log.debug('Cleaning up and shutting down...');
1268
1000
  await this.cleanup('unregistered all devices and shutting down...', false);
1269
1001
  }
1270
- /**
1271
- * Reset commissioning and shut down the process.
1272
- */
1273
1002
  async shutdownProcessAndReset() {
1274
1003
  await this.cleanup('shutting down with reset...', false);
1275
1004
  }
1276
- /**
1277
- * Factory reset and shut down the process.
1278
- */
1279
1005
  async shutdownProcessAndFactoryReset() {
1280
1006
  await this.cleanup('shutting down with factory reset...', false);
1281
1007
  }
1282
- /**
1283
- * Cleans up the Matterbridge instance.
1284
- * @param message - The cleanup message.
1285
- * @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
1286
- * @returns A promise that resolves when the cleanup is completed.
1287
- */
1288
1008
  async cleanup(message, restart = false) {
1289
1009
  if (this.initialized && !this.hasCleanupStarted) {
1290
1010
  this.emit('cleanup_started');
1291
1011
  this.hasCleanupStarted = true;
1292
1012
  this.log.info(message);
1293
- // Clear the start matter interval
1294
1013
  if (this.startMatterInterval) {
1295
1014
  clearInterval(this.startMatterInterval);
1296
1015
  this.startMatterInterval = undefined;
1297
1016
  this.log.debug('Start matter interval cleared');
1298
1017
  }
1299
- // Clear the check update timeout
1300
1018
  if (this.checkUpdateTimeout) {
1301
1019
  clearInterval(this.checkUpdateTimeout);
1302
1020
  this.checkUpdateTimeout = undefined;
1303
1021
  this.log.debug('Check update timeout cleared');
1304
1022
  }
1305
- // Clear the check update interval
1306
1023
  if (this.checkUpdateInterval) {
1307
1024
  clearInterval(this.checkUpdateInterval);
1308
1025
  this.checkUpdateInterval = undefined;
1309
1026
  this.log.debug('Check update interval cleared');
1310
1027
  }
1311
- // Clear the configure timeout
1312
1028
  if (this.configureTimeout) {
1313
1029
  clearTimeout(this.configureTimeout);
1314
1030
  this.configureTimeout = undefined;
1315
1031
  this.log.debug('Matterbridge configure timeout cleared');
1316
1032
  }
1317
- // Clear the reachability timeout
1318
1033
  if (this.reachabilityTimeout) {
1319
1034
  clearTimeout(this.reachabilityTimeout);
1320
1035
  this.reachabilityTimeout = undefined;
1321
1036
  this.log.debug('Matterbridge reachability timeout cleared');
1322
1037
  }
1323
- // Calling the shutdown method of each plugin and clear the plugins reachability timeout
1324
1038
  for (const plugin of this.plugins) {
1325
1039
  if (!plugin.enabled || plugin.error)
1326
1040
  continue;
@@ -1331,10 +1045,9 @@ export class Matterbridge extends EventEmitter {
1331
1045
  this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
1332
1046
  }
1333
1047
  }
1334
- // Stop matter server nodes
1335
1048
  this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
1336
1049
  this.log.debug('Waiting for the MessageExchange to finish...');
1337
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second for MessageExchange to finish
1050
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1338
1051
  if (this.bridgeMode === 'bridge') {
1339
1052
  if (this.serverNode) {
1340
1053
  await this.stopServerNode(this.serverNode);
@@ -1350,7 +1063,6 @@ export class Matterbridge extends EventEmitter {
1350
1063
  }
1351
1064
  }
1352
1065
  this.log.notice('Stopped matter server nodes');
1353
- // Matter commisioning reset
1354
1066
  if (message === 'shutting down with reset...') {
1355
1067
  this.log.info('Resetting Matterbridge commissioning information...');
1356
1068
  await this.matterStorageManager?.createContext('events')?.clearAll();
@@ -1360,37 +1072,18 @@ export class Matterbridge extends EventEmitter {
1360
1072
  await this.matterbridgeContext?.clearAll();
1361
1073
  this.log.info('Matter storage reset done! Remove the bridge from the controller.');
1362
1074
  }
1363
- // Stop matter storage
1364
- await this.stopMatterStorage();
1365
- // Stop the frontend
1366
- await this.frontend.stop();
1367
- // Remove the matterfilelogger
1075
+ await withTimeout(this.stopMatterStorage(), 10000, false);
1076
+ await withTimeout(this.frontend.stop(), 10000, false);
1368
1077
  try {
1369
1078
  Logger.removeLogger('matterfilelogger');
1370
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1371
1079
  }
1372
1080
  catch (error) {
1373
- // this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
1081
+ this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${db}: ${error instanceof Error ? error.message : String(error)}`);
1374
1082
  }
1375
- // Serialize registeredDevices
1376
1083
  if (this.nodeStorage && this.nodeContext) {
1377
- /*
1378
- TODO: Implement serialization of registered devices in edge mode
1379
- this.log.info('Saving registered devices...');
1380
- const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
1381
- this.devices.forEach(async (device) => {
1382
- const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
1383
- // this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
1384
- if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
1385
- });
1386
- await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
1387
- this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
1388
- */
1389
- // Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
1390
1084
  this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
1391
1085
  await this.nodeContext.close();
1392
1086
  this.nodeContext = undefined;
1393
- // Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
1394
1087
  for (const plugin of this.plugins) {
1395
1088
  if (plugin.nodeContext) {
1396
1089
  this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
@@ -1403,63 +1096,45 @@ export class Matterbridge extends EventEmitter {
1403
1096
  this.nodeStorage = undefined;
1404
1097
  }
1405
1098
  else {
1406
- this.log.error('Error saving registered devices: nodeContext not found!');
1099
+ this.log.error('Error close the matterbridge node storage and context: nodeStorage or nodeContext not found!');
1407
1100
  }
1408
1101
  this.plugins.clear();
1409
1102
  this.devices.clear();
1410
- // Factory reset
1411
1103
  if (message === 'shutting down with factory reset...') {
1412
1104
  try {
1413
- // Delete old matter storage file and backup
1414
- const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
1415
- this.log.info(`Unlinking old matter storage file: ${file}`);
1416
- await fs.unlink(file);
1417
- const backup = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.backup.json');
1418
- this.log.info(`Unlinking old matter storage backup file: ${backup}`);
1419
- await fs.unlink(backup);
1420
- }
1421
- catch (err) {
1422
- if (err instanceof Error && err.code !== 'ENOENT') {
1423
- this.log.error(`Error unlinking old matter storage file: ${err}`);
1424
- }
1425
- }
1426
- try {
1427
- // Delete matter node storage directory with its subdirectories and backup
1428
- const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
1429
- this.log.info(`Removing matter node storage directory: ${dir}`);
1105
+ const dir = path.join(this.matterbridgeDirectory, this.matterStorageName);
1106
+ this.log.info(`Removing matter storage directory: ${dir}`);
1430
1107
  await fs.rm(dir, { recursive: true });
1431
- const backup = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.backup');
1432
- this.log.info(`Removing matter node storage backup directory: ${backup}`);
1108
+ const backup = path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup');
1109
+ this.log.info(`Removing matter storage backup directory: ${backup}`);
1433
1110
  await fs.rm(backup, { recursive: true });
1434
1111
  }
1435
- catch (err) {
1436
- if (err instanceof Error && err.code !== 'ENOENT') {
1437
- this.log.error(`Error removing matter storage directory: ${err}`);
1112
+ catch (error) {
1113
+ if (error instanceof Error && error.code !== 'ENOENT') {
1114
+ this.log.error(`Error removing matter storage directory: ${error}`);
1438
1115
  }
1439
1116
  }
1440
1117
  try {
1441
- // Delete node storage directory with its subdirectories and backup
1442
- const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
1443
- this.log.info(`Removing storage directory: ${dir}`);
1118
+ const dir = path.join(this.matterbridgeDirectory, this.nodeStorageName);
1119
+ this.log.info(`Removing matterbridge storage directory: ${dir}`);
1444
1120
  await fs.rm(dir, { recursive: true });
1445
- const backup = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.backup');
1446
- this.log.info(`Removing storage backup directory: ${backup}`);
1121
+ const backup = path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup');
1122
+ this.log.info(`Removing matterbridge storage backup directory: ${backup}`);
1447
1123
  await fs.rm(backup, { recursive: true });
1448
1124
  }
1449
- catch (err) {
1450
- if (err instanceof Error && err.code !== 'ENOENT') {
1451
- this.log.error(`Error removing storage directory: ${err}`);
1125
+ catch (error) {
1126
+ if (error instanceof Error && error.code !== 'ENOENT') {
1127
+ this.log.error(`Error removing matterbridge storage directory: ${error}`);
1452
1128
  }
1453
1129
  }
1454
1130
  this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
1455
1131
  }
1456
- // Deregisters the process handlers
1457
- this.deregisterProcesslHandlers();
1132
+ this.deregisterProcessHandlers();
1458
1133
  if (restart) {
1459
1134
  if (message === 'updating...') {
1460
1135
  this.log.info('Cleanup completed. Updating...');
1461
1136
  Matterbridge.instance = undefined;
1462
- this.emit('update'); // Restart the process but the update has been done before. TODO move all updates to the cli
1137
+ this.emit('update');
1463
1138
  }
1464
1139
  else if (message === 'restarting...') {
1465
1140
  this.log.info('Cleanup completed. Restarting...');
@@ -1480,14 +1155,6 @@ export class Matterbridge extends EventEmitter {
1480
1155
  this.log.debug('Cleanup already started...');
1481
1156
  }
1482
1157
  }
1483
- /**
1484
- * Creates and configures the server node for an accessory plugin for a given device.
1485
- *
1486
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1487
- * @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
1488
- * @param {boolean} [start=false] - Whether to start the server node after adding the device.
1489
- * @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
1490
- */
1491
1158
  async createAccessoryPlugin(plugin, device, start = false) {
1492
1159
  if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
1493
1160
  plugin.locked = true;
@@ -1501,13 +1168,6 @@ export class Matterbridge extends EventEmitter {
1501
1168
  await this.startServerNode(plugin.serverNode);
1502
1169
  }
1503
1170
  }
1504
- /**
1505
- * Creates and configures the server node for a dynamic plugin.
1506
- *
1507
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1508
- * @param {boolean} [start=false] - Whether to start the server node after adding the aggregator node.
1509
- * @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
1510
- */
1511
1171
  async createDynamicPlugin(plugin, start = false) {
1512
1172
  if (!plugin.locked) {
1513
1173
  plugin.locked = true;
@@ -1520,13 +1180,7 @@ export class Matterbridge extends EventEmitter {
1520
1180
  await this.startServerNode(plugin.serverNode);
1521
1181
  }
1522
1182
  }
1523
- /**
1524
- * Starts the Matterbridge in bridge mode.
1525
- * @private
1526
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1527
- */
1528
1183
  async startBridge() {
1529
- // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1530
1184
  if (!this.matterStorageManager)
1531
1185
  throw new Error('No storage manager initialized');
1532
1186
  if (!this.matterbridgeContext)
@@ -1565,9 +1219,7 @@ export class Matterbridge extends EventEmitter {
1565
1219
  clearInterval(this.startMatterInterval);
1566
1220
  this.startMatterInterval = undefined;
1567
1221
  this.log.debug('Cleared startMatterInterval interval for Matterbridge');
1568
- // Start the Matter server node
1569
1222
  this.startServerNode(this.serverNode);
1570
- // Configure the plugins
1571
1223
  this.configureTimeout = setTimeout(async () => {
1572
1224
  for (const plugin of this.plugins) {
1573
1225
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1585,7 +1237,6 @@ export class Matterbridge extends EventEmitter {
1585
1237
  }
1586
1238
  this.frontend.wssSendRefreshRequired('plugins');
1587
1239
  }, 30 * 1000);
1588
- // Setting reachability to true
1589
1240
  this.reachabilityTimeout = setTimeout(() => {
1590
1241
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1591
1242
  if (this.aggregatorNode)
@@ -1594,11 +1245,6 @@ export class Matterbridge extends EventEmitter {
1594
1245
  }, 60 * 1000);
1595
1246
  }, 1000);
1596
1247
  }
1597
- /**
1598
- * Starts the Matterbridge in childbridge mode.
1599
- * @private
1600
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1601
- */
1602
1248
  async startChildbridge() {
1603
1249
  if (!this.matterStorageManager)
1604
1250
  throw new Error('No storage manager initialized');
@@ -1636,7 +1282,6 @@ export class Matterbridge extends EventEmitter {
1636
1282
  clearInterval(this.startMatterInterval);
1637
1283
  this.startMatterInterval = undefined;
1638
1284
  this.log.debug('Cleared startMatterInterval interval in childbridge mode');
1639
- // Configure the plugins
1640
1285
  this.configureTimeout = setTimeout(async () => {
1641
1286
  for (const plugin of this.plugins) {
1642
1287
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1673,9 +1318,7 @@ export class Matterbridge extends EventEmitter {
1673
1318
  this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
1674
1319
  continue;
1675
1320
  }
1676
- // Start the Matter server node
1677
1321
  this.startServerNode(plugin.serverNode);
1678
- // Setting reachability to true
1679
1322
  plugin.reachabilityTimeout = setTimeout(() => {
1680
1323
  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}`);
1681
1324
  if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
@@ -1685,11 +1328,6 @@ export class Matterbridge extends EventEmitter {
1685
1328
  }
1686
1329
  }, 1000);
1687
1330
  }
1688
- /**
1689
- * Starts the Matterbridge controller.
1690
- * @private
1691
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1692
- */
1693
1331
  async startController() {
1694
1332
  if (!this.matterStorageManager) {
1695
1333
  this.log.error('No storage manager initialized');
@@ -1704,207 +1342,8 @@ export class Matterbridge extends EventEmitter {
1704
1342
  return;
1705
1343
  }
1706
1344
  this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1707
- /*
1708
- this.matterServer = await this.createMatterServer(this.storageManager);
1709
- this.log.info('Creating matter commissioning controller');
1710
- this.commissioningController = new CommissioningController({
1711
- autoConnect: false,
1712
- });
1713
- this.log.info('Adding matter commissioning controller to matter server');
1714
- await this.matterServer.addCommissioningController(this.commissioningController);
1715
-
1716
- this.log.info('Starting matter server');
1717
- await this.matterServer.start();
1718
- this.log.info('Matter server started');
1719
- const commissioningOptions: ControllerCommissioningFlowOptions = {
1720
- regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
1721
- regulatoryCountryCode: 'XX',
1722
- };
1723
- const commissioningController = new CommissioningController({
1724
- environment: {
1725
- environment,
1726
- id: uniqueId,
1727
- },
1728
- autoConnect: false, // Do not auto connect to the commissioned nodes
1729
- adminFabricLabel,
1730
- });
1731
-
1732
- if (hasParameter('pairingcode')) {
1733
- this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
1734
- const pairingCode = getParameter('pairingcode');
1735
- const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
1736
- const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
1737
-
1738
- let longDiscriminator, setupPin, shortDiscriminator;
1739
- if (pairingCode !== undefined) {
1740
- const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
1741
- shortDiscriminator = pairingCodeCodec.shortDiscriminator;
1742
- longDiscriminator = undefined;
1743
- setupPin = pairingCodeCodec.passcode;
1744
- this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
1745
- } else {
1746
- longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
1747
- if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
1748
- setupPin = this.controllerContext.get('pin', 20202021);
1749
- }
1750
- if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
1751
- throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
1752
- }
1753
-
1754
- const options = {
1755
- commissioning: commissioningOptions,
1756
- discovery: {
1757
- knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
1758
- identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
1759
- },
1760
- passcode: setupPin,
1761
- } as NodeCommissioningOptions;
1762
- this.log.info('Commissioning with options:', options);
1763
- const nodeId = await this.commissioningController.commissionNode(options);
1764
- this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
1765
- this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
1766
- } // (hasParameter('pairingcode'))
1767
-
1768
- if (hasParameter('unpairall')) {
1769
- this.log.info('***Commissioning controller unpairing all nodes...');
1770
- const nodeIds = this.commissioningController.getCommissionedNodes();
1771
- for (const nodeId of nodeIds) {
1772
- this.log.info('***Commissioning controller unpairing node:', nodeId);
1773
- await this.commissioningController.removeNode(nodeId);
1774
- }
1775
- return;
1776
- }
1777
-
1778
- if (hasParameter('discover')) {
1779
- // const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1780
- // console.log(discover);
1781
- }
1782
-
1783
- if (!this.commissioningController.isCommissioned()) {
1784
- this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
1785
- return;
1786
- }
1787
-
1788
- const nodeIds = this.commissioningController.getCommissionedNodes();
1789
- this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
1790
- for (const nodeId of nodeIds) {
1791
- this.log.info(`***Connecting to commissioned node: ${nodeId}`);
1792
-
1793
- const node = await this.commissioningController.connectNode(nodeId, {
1794
- autoSubscribe: false,
1795
- attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
1796
- this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
1797
- eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
1798
- this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
1799
- stateInformationCallback: (peerNodeId, info) => {
1800
- switch (info) {
1801
- case NodeStateInformation.Connected:
1802
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
1803
- break;
1804
- case NodeStateInformation.Disconnected:
1805
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
1806
- break;
1807
- case NodeStateInformation.Reconnecting:
1808
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
1809
- break;
1810
- case NodeStateInformation.WaitingForDeviceDiscovery:
1811
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
1812
- break;
1813
- case NodeStateInformation.StructureChanged:
1814
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
1815
- break;
1816
- case NodeStateInformation.Decommissioned:
1817
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
1818
- break;
1819
- default:
1820
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
1821
- break;
1822
- }
1823
- },
1824
- });
1825
-
1826
- node.logStructure();
1827
-
1828
- // Get the interaction client
1829
- this.log.info('Getting the interaction client');
1830
- const interactionClient = await node.getInteractionClient();
1831
- let cluster;
1832
- let attributes;
1833
-
1834
- // Log BasicInformationCluster
1835
- cluster = BasicInformationCluster;
1836
- attributes = await interactionClient.getMultipleAttributes({
1837
- attributes: [{ clusterId: cluster.id }],
1838
- });
1839
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1840
- attributes.forEach((attribute) => {
1841
- this.log.info(
1842
- `- 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}`,
1843
- );
1844
- });
1845
-
1846
- // Log PowerSourceCluster
1847
- cluster = PowerSourceCluster;
1848
- attributes = await interactionClient.getMultipleAttributes({
1849
- attributes: [{ clusterId: cluster.id }],
1850
- });
1851
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1852
- attributes.forEach((attribute) => {
1853
- this.log.info(
1854
- `- 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}`,
1855
- );
1856
- });
1857
-
1858
- // Log ThreadNetworkDiagnostics
1859
- cluster = ThreadNetworkDiagnosticsCluster;
1860
- attributes = await interactionClient.getMultipleAttributes({
1861
- attributes: [{ clusterId: cluster.id }],
1862
- });
1863
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1864
- attributes.forEach((attribute) => {
1865
- this.log.info(
1866
- `- 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}`,
1867
- );
1868
- });
1869
-
1870
- // Log SwitchCluster
1871
- cluster = SwitchCluster;
1872
- attributes = await interactionClient.getMultipleAttributes({
1873
- attributes: [{ clusterId: cluster.id }],
1874
- });
1875
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1876
- attributes.forEach((attribute) => {
1877
- this.log.info(
1878
- `- 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}`,
1879
- );
1880
- });
1881
-
1882
- this.log.info('Subscribing to all attributes and events');
1883
- await node.subscribeAllAttributesAndEvents({
1884
- ignoreInitialTriggers: false,
1885
- attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
1886
- this.log.info(
1887
- `***${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}`,
1888
- ),
1889
- eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
1890
- this.log.info(
1891
- `***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
1892
- );
1893
- },
1894
- });
1895
- this.log.info('Subscribed to all attributes and events');
1896
- }
1897
- */
1898
1345
  }
1899
- /** ***********************************************************************************************************************************/
1900
- /** Matter.js methods */
1901
- /** ***********************************************************************************************************************************/
1902
- /**
1903
- * Starts the matter storage process with name Matterbridge.
1904
- * @returns {Promise<void>} - A promise that resolves when the storage process is started.
1905
- */
1906
1346
  async startMatterStorage() {
1907
- // Setup Matter storage
1908
1347
  this.log.info(`Starting matter node storage...`);
1909
1348
  this.matterStorageService = this.environment.get(StorageService);
1910
1349
  this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
@@ -1913,25 +1352,13 @@ export class Matterbridge extends EventEmitter {
1913
1352
  this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName);
1914
1353
  this.matterbridgeInformation.matterbridgeSerialNumber = await this.matterbridgeContext.get('serialNumber', '');
1915
1354
  this.log.info('Matter node storage started');
1916
- // Backup matter storage since it is created/opened correctly
1917
1355
  await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
1918
1356
  }
1919
- /**
1920
- * Makes a backup copy of the specified matter storage directory.
1921
- *
1922
- * @param storageName - The name of the storage directory to be backed up.
1923
- * @param backupName - The name of the backup directory to be created.
1924
- * @returns {Promise<void>} A promise that resolves when the has been done.
1925
- */
1926
1357
  async backupMatterStorage(storageName, backupName) {
1927
1358
  this.log.info('Creating matter node storage backup...');
1928
1359
  await copyDirectory(storageName, backupName);
1929
1360
  this.log.info('Created matter node storage backup');
1930
1361
  }
1931
- /**
1932
- * Stops the matter storage.
1933
- * @returns {Promise<void>} A promise that resolves when the storage is stopped.
1934
- */
1935
1362
  async stopMatterStorage() {
1936
1363
  this.log.info('Closing matter node storage...');
1937
1364
  await this.matterStorageManager?.close();
@@ -1940,19 +1367,6 @@ export class Matterbridge extends EventEmitter {
1940
1367
  this.matterbridgeContext = undefined;
1941
1368
  this.log.info('Matter node storage closed');
1942
1369
  }
1943
- /**
1944
- * Creates a server node storage context.
1945
- *
1946
- * @param {string} pluginName - The name of the plugin.
1947
- * @param {string} deviceName - The name of the device.
1948
- * @param {DeviceTypeId} deviceType - The device type of the device.
1949
- * @param {number} vendorId - The vendor ID.
1950
- * @param {string} vendorName - The vendor name.
1951
- * @param {number} productId - The product ID.
1952
- * @param {string} productName - The product name.
1953
- * @param {string} [serialNumber] - The serial number of the device (optional).
1954
- * @returns {Promise<StorageContext>} The storage context for the commissioning server.
1955
- */
1956
1370
  async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
1957
1371
  const { randomBytes } = await import('node:crypto');
1958
1372
  if (!this.matterStorageService)
@@ -1986,15 +1400,6 @@ export class Matterbridge extends EventEmitter {
1986
1400
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1987
1401
  return storageContext;
1988
1402
  }
1989
- /**
1990
- * Creates a server node.
1991
- *
1992
- * @param {StorageContext} storageContext - The storage context for the server node.
1993
- * @param {number} [port=5540] - The port number for the server node. Defaults to 5540.
1994
- * @param {number} [passcode=20242025] - The passcode for the server node. Defaults to 20242025.
1995
- * @param {number} [discriminator=3850] - The discriminator for the server node. Defaults to 3850.
1996
- * @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
1997
- */
1998
1403
  async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
1999
1404
  const storeId = await storageContext.get('storeId');
2000
1405
  this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
@@ -2004,37 +1409,24 @@ export class Matterbridge extends EventEmitter {
2004
1409
  this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
2005
1410
  this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
2006
1411
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
2007
- /**
2008
- * Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
2009
- */
2010
1412
  const serverNode = await ServerNode.create({
2011
- // Required: Give the Node a unique ID which is used to store the state of this node
2012
1413
  id: storeId,
2013
- // Provide Network relevant configuration like the port
2014
- // Optional when operating only one device on a host, Default port is 5540
2015
1414
  network: {
2016
1415
  listeningAddressIpv4: this.ipv4address,
2017
1416
  listeningAddressIpv6: this.ipv6address,
2018
1417
  port,
2019
1418
  },
2020
- // Provide the certificate for the device
2021
1419
  operationalCredentials: {
2022
1420
  certification: this.certification,
2023
1421
  },
2024
- // Provide Commissioning relevant settings
2025
- // Optional for development/testing purposes
2026
1422
  commissioning: {
2027
1423
  passcode,
2028
1424
  discriminator,
2029
1425
  },
2030
- // Provide Node announcement settings
2031
- // Optional: If Ommitted some development defaults are used
2032
1426
  productDescription: {
2033
1427
  name: await storageContext.get('deviceName'),
2034
1428
  deviceType: DeviceTypeId(await storageContext.get('deviceType')),
2035
1429
  },
2036
- // Provide defaults for the BasicInformation cluster on the Root endpoint
2037
- // Optional: If Omitted some development defaults are used
2038
1430
  basicInformation: {
2039
1431
  vendorId: VendorId(await storageContext.get('vendorId')),
2040
1432
  vendorName: await storageContext.get('vendorName'),
@@ -2052,13 +1444,12 @@ export class Matterbridge extends EventEmitter {
2052
1444
  },
2053
1445
  });
2054
1446
  const sanitizeFabrics = (fabrics, resetSessions = false) => {
2055
- // New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
2056
1447
  const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
2057
1448
  this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
2058
1449
  if (this.bridgeMode === 'bridge') {
2059
1450
  this.matterbridgeFabricInformations = sanitizedFabrics;
2060
1451
  if (resetSessions)
2061
- this.matterbridgeSessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
1452
+ this.matterbridgeSessionInformations = undefined;
2062
1453
  this.matterbridgePaired = true;
2063
1454
  }
2064
1455
  if (this.bridgeMode === 'childbridge') {
@@ -2066,19 +1457,13 @@ export class Matterbridge extends EventEmitter {
2066
1457
  if (plugin) {
2067
1458
  plugin.fabricInformations = sanitizedFabrics;
2068
1459
  if (resetSessions)
2069
- plugin.sessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
1460
+ plugin.sessionInformations = undefined;
2070
1461
  plugin.paired = true;
2071
1462
  }
2072
1463
  }
2073
1464
  };
2074
- /**
2075
- * This event is triggered when the device is initially commissioned successfully.
2076
- * This means: It is added to the first fabric.
2077
- */
2078
1465
  serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
2079
- /** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
2080
1466
  serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
2081
- /** This event is triggered when the device went online. This means that it is discoverable in the network. */
2082
1467
  serverNode.lifecycle.online.on(async () => {
2083
1468
  this.log.notice(`Server node for ${storeId} is online`);
2084
1469
  if (!serverNode.lifecycle.isCommissioned) {
@@ -2147,7 +1532,6 @@ export class Matterbridge extends EventEmitter {
2147
1532
  this.frontend.wssSendRefreshRequired('settings');
2148
1533
  this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
2149
1534
  });
2150
- /** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
2151
1535
  serverNode.lifecycle.offline.on(() => {
2152
1536
  this.log.notice(`Server node for ${storeId} is offline`);
2153
1537
  if (this.bridgeMode === 'bridge') {
@@ -2171,10 +1555,6 @@ export class Matterbridge extends EventEmitter {
2171
1555
  this.frontend.wssSendRefreshRequired('settings');
2172
1556
  this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
2173
1557
  });
2174
- /**
2175
- * This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
2176
- * information is needed.
2177
- */
2178
1558
  serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
2179
1559
  let action = '';
2180
1560
  switch (fabricAction) {
@@ -2208,24 +1588,16 @@ export class Matterbridge extends EventEmitter {
2208
1588
  }
2209
1589
  }
2210
1590
  };
2211
- /**
2212
- * This event is triggered when an operative new session was opened by a Controller.
2213
- * It is not triggered for the initial commissioning process, just afterwards for real connections.
2214
- */
2215
1591
  serverNode.events.sessions.opened.on((session) => {
2216
1592
  this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
2217
1593
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2218
1594
  this.frontend.wssSendRefreshRequired('sessions');
2219
1595
  });
2220
- /**
2221
- * This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
2222
- */
2223
1596
  serverNode.events.sessions.closed.on((session) => {
2224
1597
  this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
2225
1598
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2226
1599
  this.frontend.wssSendRefreshRequired('sessions');
2227
1600
  });
2228
- /** This event is triggered when a subscription gets added or removed on an operative session. */
2229
1601
  serverNode.events.sessions.subscriptionsChanged.on((session) => {
2230
1602
  this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
2231
1603
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
@@ -2234,42 +1606,24 @@ export class Matterbridge extends EventEmitter {
2234
1606
  this.log.info(`Created server node for ${storeId}`);
2235
1607
  return serverNode;
2236
1608
  }
2237
- /**
2238
- * Starts the specified server node.
2239
- *
2240
- * @param {ServerNode} [matterServerNode] - The server node to start.
2241
- * @returns {Promise<void>} A promise that resolves when the server node has started.
2242
- */
2243
1609
  async startServerNode(matterServerNode) {
2244
1610
  if (!matterServerNode)
2245
1611
  return;
2246
1612
  this.log.notice(`Starting ${matterServerNode.id} server node`);
2247
1613
  await matterServerNode.start();
2248
1614
  }
2249
- /**
2250
- * Stops the specified server node.
2251
- *
2252
- * @param {ServerNode} matterServerNode - The server node to stop.
2253
- * @returns {Promise<void>} A promise that resolves when the server node has stopped.
2254
- */
2255
1615
  async stopServerNode(matterServerNode) {
2256
1616
  if (!matterServerNode)
2257
1617
  return;
2258
1618
  this.log.notice(`Closing ${matterServerNode.id} server node`);
2259
1619
  try {
2260
- await withTimeout(matterServerNode.close(), 30000); // 30 seconds timeout to allow slow devices to close gracefully
1620
+ await withTimeout(matterServerNode.close(), 30000);
2261
1621
  this.log.info(`Closed ${matterServerNode.id} server node`);
2262
1622
  }
2263
1623
  catch (error) {
2264
1624
  this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
2265
1625
  }
2266
1626
  }
2267
- /**
2268
- * Advertises the specified server node.
2269
- *
2270
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2271
- * @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.
2272
- */
2273
1627
  async advertiseServerNode(matterServerNode) {
2274
1628
  if (matterServerNode) {
2275
1629
  await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
@@ -2278,45 +1632,24 @@ export class Matterbridge extends EventEmitter {
2278
1632
  return { qrPairingCode, manualPairingCode };
2279
1633
  }
2280
1634
  }
2281
- /**
2282
- * Stop advertise the specified server node.
2283
- *
2284
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2285
- * @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
2286
- */
2287
1635
  async stopAdvertiseServerNode(matterServerNode) {
2288
1636
  if (matterServerNode && matterServerNode.lifecycle.isOnline) {
2289
1637
  await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
2290
1638
  this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
2291
1639
  }
2292
1640
  }
2293
- /**
2294
- * Creates an aggregator node with the specified storage context.
2295
- *
2296
- * @param {StorageContext} storageContext - The storage context for the aggregator node.
2297
- * @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
2298
- */
2299
1641
  async createAggregatorNode(storageContext) {
2300
- this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
1642
+ this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
2301
1643
  const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
2302
1644
  return aggregatorNode;
2303
1645
  }
2304
- /**
2305
- * Adds a MatterbridgeEndpoint to the specified plugin.
2306
- *
2307
- * @param {string} pluginName - The name of the plugin.
2308
- * @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
2309
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
2310
- */
2311
1646
  async addBridgedEndpoint(pluginName, device) {
2312
- // Check if the plugin is registered
2313
1647
  const plugin = this.plugins.get(pluginName);
2314
1648
  if (!plugin) {
2315
1649
  this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
2316
1650
  return;
2317
1651
  }
2318
1652
  if (this.bridgeMode === 'bridge') {
2319
- // Register and add the device to the matterbridge aggregator node
2320
1653
  this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
2321
1654
  if (!this.aggregatorNode) {
2322
1655
  this.log.error('Aggregator node not found for Matterbridge');
@@ -2333,7 +1666,6 @@ export class Matterbridge extends EventEmitter {
2333
1666
  }
2334
1667
  }
2335
1668
  else if (this.bridgeMode === 'childbridge') {
2336
- // Register and add the device to the plugin server node
2337
1669
  if (plugin.type === 'AccessoryPlatform') {
2338
1670
  try {
2339
1671
  this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
@@ -2350,12 +1682,10 @@ export class Matterbridge extends EventEmitter {
2350
1682
  return;
2351
1683
  }
2352
1684
  }
2353
- // Register and add the device to the plugin aggregator node
2354
1685
  if (plugin.type === 'DynamicPlatform') {
2355
1686
  try {
2356
1687
  this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
2357
1688
  await this.createDynamicPlugin(plugin);
2358
- // Fast plugins can add another device before the server node is created
2359
1689
  await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
2360
1690
  if (!plugin.aggregatorNode) {
2361
1691
  this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
@@ -2375,28 +1705,17 @@ export class Matterbridge extends EventEmitter {
2375
1705
  plugin.registeredDevices++;
2376
1706
  if (plugin.addedDevices !== undefined)
2377
1707
  plugin.addedDevices++;
2378
- // Add the device to the DeviceManager
2379
1708
  this.devices.set(device);
2380
- // Subscribe to the reachable$Changed event
2381
1709
  await this.subscribeAttributeChanged(plugin, device);
2382
1710
  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}`);
2383
1711
  }
2384
- /**
2385
- * Removes a MatterbridgeEndpoint from the specified plugin.
2386
- *
2387
- * @param {string} pluginName - The name of the plugin.
2388
- * @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
2389
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
2390
- */
2391
1712
  async removeBridgedEndpoint(pluginName, device) {
2392
1713
  this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
2393
- // Check if the plugin is registered
2394
1714
  const plugin = this.plugins.get(pluginName);
2395
1715
  if (!plugin) {
2396
1716
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
2397
1717
  return;
2398
1718
  }
2399
- // Register and add the device to the matterbridge aggregator node
2400
1719
  if (this.bridgeMode === 'bridge') {
2401
1720
  if (!this.aggregatorNode) {
2402
1721
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
@@ -2411,7 +1730,6 @@ export class Matterbridge extends EventEmitter {
2411
1730
  }
2412
1731
  else if (this.bridgeMode === 'childbridge') {
2413
1732
  if (plugin.type === 'AccessoryPlatform') {
2414
- // Nothing to do here since the server node has no aggregator node but only the device itself
2415
1733
  }
2416
1734
  else if (plugin.type === 'DynamicPlatform') {
2417
1735
  if (!plugin.aggregatorNode) {
@@ -2426,21 +1744,8 @@ export class Matterbridge extends EventEmitter {
2426
1744
  if (plugin.addedDevices !== undefined)
2427
1745
  plugin.addedDevices--;
2428
1746
  }
2429
- // Remove the device from the DeviceManager
2430
1747
  this.devices.remove(device);
2431
1748
  }
2432
- /**
2433
- * Removes all bridged endpoints from the specified plugin.
2434
- *
2435
- * @param {string} pluginName - The name of the plugin.
2436
- * @param {number} [delay=0] - The delay in milliseconds between removing each bridged endpoint (default: 0).
2437
- * @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
2438
- *
2439
- * @remarks
2440
- * This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
2441
- * It also applies a delay between each removal if specified.
2442
- * The delay is useful to allow the controllers to receive a single subscription for each device removed.
2443
- */
2444
1749
  async removeAllBridgedEndpoints(pluginName, delay = 0) {
2445
1750
  this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
2446
1751
  for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
@@ -2451,25 +1756,9 @@ export class Matterbridge extends EventEmitter {
2451
1756
  if (delay > 0)
2452
1757
  await new Promise((resolve) => setTimeout(resolve, 2000));
2453
1758
  }
2454
- /**
2455
- * Subscribes to the attribute change event for the given device and plugin.
2456
- * Specifically, it listens for changes in the 'reachable' attribute of the
2457
- * BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
2458
- *
2459
- * @param {RegisteredPlugin} plugin - The plugin associated with the device.
2460
- * @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
2461
- * @returns {Promise<void>} A promise that resolves when the subscription is set up.
2462
- */
2463
1759
  async subscribeAttributeChanged(plugin, device) {
2464
1760
  this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
2465
1761
  if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
2466
- /*
2467
- this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) subscribed to reachable$Changed`);
2468
- setTimeout(async () => {
2469
- this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) changed to reachable false`);
2470
- await plugin.serverNode?.setStateOf(BasicInformationServer, { reachable: false });
2471
- }, 60000).unref();
2472
- */
2473
1762
  plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
2474
1763
  this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
2475
1764
  this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, 'BasicInformationServer', 'reachable', reachable);
@@ -2482,12 +1771,6 @@ export class Matterbridge extends EventEmitter {
2482
1771
  });
2483
1772
  }
2484
1773
  }
2485
- /**
2486
- * Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
2487
- *
2488
- * @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
2489
- * @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
2490
- */
2491
1774
  sanitizeFabricInformations(fabricInfo) {
2492
1775
  return fabricInfo.map((info) => {
2493
1776
  return {
@@ -2501,12 +1784,6 @@ export class Matterbridge extends EventEmitter {
2501
1784
  };
2502
1785
  });
2503
1786
  }
2504
- /**
2505
- * Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
2506
- *
2507
- * @param {SessionInformation[]} sessionInfo - The array of session information objects.
2508
- * @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
2509
- */
2510
1787
  sanitizeSessionInformation(sessionInfo) {
2511
1788
  return sessionInfo
2512
1789
  .filter((session) => session.isPeerActive)
@@ -2534,20 +1811,7 @@ export class Matterbridge extends EventEmitter {
2534
1811
  };
2535
1812
  });
2536
1813
  }
2537
- /**
2538
- * Sets the reachability of the specified aggregator node bridged devices and trigger.
2539
- * @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
2540
- * @param {boolean} reachable - A boolean indicating the reachability status to set.
2541
- */
2542
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2543
1814
  async setAggregatorReachability(aggregatorNode, reachable) {
2544
- /*
2545
- for (const child of aggregatorNode.parts) {
2546
- this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
2547
- await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
2548
- child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
2549
- }
2550
- */
2551
1815
  }
2552
1816
  getVendorIdName = (vendorId) => {
2553
1817
  if (!vendorId)
@@ -2590,29 +1854,14 @@ export class Matterbridge extends EventEmitter {
2590
1854
  }
2591
1855
  return vendorName;
2592
1856
  };
2593
- /**
2594
- * Spawns a child process with the given command and arguments.
2595
- * @param {string} command - The command to execute.
2596
- * @param {string[]} args - The arguments to pass to the command (default: []).
2597
- * @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
2598
- */
2599
1857
  async spawnCommand(command, args = []) {
2600
1858
  const { spawn } = await import('node:child_process');
2601
- /*
2602
- npm > npm.cmd on windows
2603
- cmd.exe ['dir'] on windows
2604
- await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
2605
- */
2606
1859
  const cmdLine = command + ' ' + args.join(' ');
2607
1860
  if (process.platform === 'win32' && command === 'npm') {
2608
- // Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
2609
1861
  const argstring = 'npm ' + args.join(' ');
2610
1862
  args.splice(0, args.length, '/c', argstring);
2611
1863
  command = 'cmd.exe';
2612
1864
  }
2613
- // Decide when using sudo on linux
2614
- // When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
2615
- // When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
2616
1865
  if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
2617
1866
  args.unshift(command);
2618
1867
  command = 'sudo';
@@ -2670,5 +1919,26 @@ export class Matterbridge extends EventEmitter {
2670
1919
  }
2671
1920
  });
2672
1921
  }
1922
+ async createDirectory(path, name) {
1923
+ try {
1924
+ await fs.access(path);
1925
+ }
1926
+ catch (err) {
1927
+ if (err instanceof Error) {
1928
+ const nodeErr = err;
1929
+ if (nodeErr.code === 'ENOENT') {
1930
+ try {
1931
+ await fs.mkdir(path, { recursive: true });
1932
+ this.log.info(`Created ${name}: ${path}`);
1933
+ }
1934
+ catch (err) {
1935
+ this.log.error(`Error creating ${name}: ${err}`);
1936
+ }
1937
+ }
1938
+ else {
1939
+ this.log.error(`Error accessing ${name}: ${err}`);
1940
+ }
1941
+ }
1942
+ }
1943
+ }
2673
1944
  }
2674
- //# sourceMappingURL=matterbridge.js.map