matterbridge 2.2.5 → 2.2.6-dev.2

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 (152) hide show
  1. package/CHANGELOG.md +104 -65
  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 +37 -340
  7. package/dist/index.js +1 -28
  8. package/dist/logger/export.js +0 -1
  9. package/dist/matter/behaviors.js +0 -2
  10. package/dist/matter/clusters.js +0 -2
  11. package/dist/matter/devices.js +0 -2
  12. package/dist/matter/endpoints.js +0 -2
  13. package/dist/matter/export.js +0 -2
  14. package/dist/matter/types.js +0 -2
  15. package/dist/matterbridge.js +108 -746
  16. package/dist/matterbridgeAccessoryPlatform.js +0 -33
  17. package/dist/matterbridgeBehaviors.js +1 -32
  18. package/dist/matterbridgeDeviceTypes.js +11 -112
  19. package/dist/matterbridgeDynamicPlatform.js +0 -33
  20. package/dist/matterbridgeEndpoint.js +27 -711
  21. package/dist/matterbridgeEndpointHelpers.js +9 -118
  22. package/dist/matterbridgePlatform.js +11 -210
  23. package/dist/matterbridgeTypes.js +0 -24
  24. package/dist/pluginManager.js +3 -229
  25. package/dist/shelly.js +6 -121
  26. package/dist/storage/export.js +0 -1
  27. package/dist/update.js +0 -45
  28. package/dist/utils/colorUtils.js +2 -205
  29. package/dist/utils/copyDirectory.js +1 -37
  30. package/dist/utils/createZip.js +2 -42
  31. package/dist/utils/deepCopy.js +0 -40
  32. package/dist/utils/deepEqual.js +1 -65
  33. package/dist/utils/export.js +0 -1
  34. package/dist/utils/isvalid.js +0 -86
  35. package/dist/utils/network.js +5 -77
  36. package/dist/utils/parameter.js +0 -41
  37. package/dist/utils/wait.js +5 -48
  38. package/frontend/build/asset-manifest.json +3 -3
  39. package/frontend/build/index.html +1 -1
  40. package/frontend/build/static/js/{main.66936fef.js → main.a45801ac.js} +3 -3
  41. package/frontend/build/static/js/{main.66936fef.js.map → main.a45801ac.js.map} +1 -1
  42. package/npm-shrinkwrap.json +51 -51
  43. package/package.json +3 -4
  44. package/dist/cli.d.ts +0 -29
  45. package/dist/cli.d.ts.map +0 -1
  46. package/dist/cli.js.map +0 -1
  47. package/dist/cluster/export.d.ts +0 -2
  48. package/dist/cluster/export.d.ts.map +0 -1
  49. package/dist/cluster/export.js.map +0 -1
  50. package/dist/defaultConfigSchema.d.ts +0 -27
  51. package/dist/defaultConfigSchema.d.ts.map +0 -1
  52. package/dist/defaultConfigSchema.js.map +0 -1
  53. package/dist/deviceManager.d.ts +0 -114
  54. package/dist/deviceManager.d.ts.map +0 -1
  55. package/dist/deviceManager.js.map +0 -1
  56. package/dist/frontend.d.ts +0 -221
  57. package/dist/frontend.d.ts.map +0 -1
  58. package/dist/frontend.js.map +0 -1
  59. package/dist/index.d.ts +0 -35
  60. package/dist/index.d.ts.map +0 -1
  61. package/dist/index.js.map +0 -1
  62. package/dist/logger/export.d.ts +0 -2
  63. package/dist/logger/export.d.ts.map +0 -1
  64. package/dist/logger/export.js.map +0 -1
  65. package/dist/matter/behaviors.d.ts +0 -2
  66. package/dist/matter/behaviors.d.ts.map +0 -1
  67. package/dist/matter/behaviors.js.map +0 -1
  68. package/dist/matter/clusters.d.ts +0 -2
  69. package/dist/matter/clusters.d.ts.map +0 -1
  70. package/dist/matter/clusters.js.map +0 -1
  71. package/dist/matter/devices.d.ts +0 -2
  72. package/dist/matter/devices.d.ts.map +0 -1
  73. package/dist/matter/devices.js.map +0 -1
  74. package/dist/matter/endpoints.d.ts +0 -2
  75. package/dist/matter/endpoints.d.ts.map +0 -1
  76. package/dist/matter/endpoints.js.map +0 -1
  77. package/dist/matter/export.d.ts +0 -5
  78. package/dist/matter/export.d.ts.map +0 -1
  79. package/dist/matter/export.js.map +0 -1
  80. package/dist/matter/types.d.ts +0 -3
  81. package/dist/matter/types.d.ts.map +0 -1
  82. package/dist/matter/types.js.map +0 -1
  83. package/dist/matterbridge.d.ts +0 -422
  84. package/dist/matterbridge.d.ts.map +0 -1
  85. package/dist/matterbridge.js.map +0 -1
  86. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
  87. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  88. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  89. package/dist/matterbridgeBehaviors.d.ts +0 -1056
  90. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  91. package/dist/matterbridgeBehaviors.js.map +0 -1
  92. package/dist/matterbridgeDeviceTypes.d.ts +0 -177
  93. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  94. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  95. package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
  96. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  97. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  98. package/dist/matterbridgeEndpoint.d.ts +0 -835
  99. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  100. package/dist/matterbridgeEndpoint.js.map +0 -1
  101. package/dist/matterbridgeEndpointHelpers.d.ts +0 -2275
  102. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  103. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  104. package/dist/matterbridgePlatform.d.ts +0 -278
  105. package/dist/matterbridgePlatform.d.ts.map +0 -1
  106. package/dist/matterbridgePlatform.js.map +0 -1
  107. package/dist/matterbridgeTypes.d.ts +0 -178
  108. package/dist/matterbridgeTypes.d.ts.map +0 -1
  109. package/dist/matterbridgeTypes.js.map +0 -1
  110. package/dist/pluginManager.d.ts +0 -236
  111. package/dist/pluginManager.d.ts.map +0 -1
  112. package/dist/pluginManager.js.map +0 -1
  113. package/dist/shelly.d.ts +0 -77
  114. package/dist/shelly.d.ts.map +0 -1
  115. package/dist/shelly.js.map +0 -1
  116. package/dist/storage/export.d.ts +0 -2
  117. package/dist/storage/export.d.ts.map +0 -1
  118. package/dist/storage/export.js.map +0 -1
  119. package/dist/update.d.ts +0 -32
  120. package/dist/update.d.ts.map +0 -1
  121. package/dist/update.js.map +0 -1
  122. package/dist/utils/colorUtils.d.ts +0 -61
  123. package/dist/utils/colorUtils.d.ts.map +0 -1
  124. package/dist/utils/colorUtils.js.map +0 -1
  125. package/dist/utils/copyDirectory.d.ts +0 -32
  126. package/dist/utils/copyDirectory.d.ts.map +0 -1
  127. package/dist/utils/copyDirectory.js.map +0 -1
  128. package/dist/utils/createZip.d.ts +0 -38
  129. package/dist/utils/createZip.d.ts.map +0 -1
  130. package/dist/utils/createZip.js.map +0 -1
  131. package/dist/utils/deepCopy.d.ts +0 -31
  132. package/dist/utils/deepCopy.d.ts.map +0 -1
  133. package/dist/utils/deepCopy.js.map +0 -1
  134. package/dist/utils/deepEqual.d.ts +0 -53
  135. package/dist/utils/deepEqual.d.ts.map +0 -1
  136. package/dist/utils/deepEqual.js.map +0 -1
  137. package/dist/utils/export.d.ts +0 -10
  138. package/dist/utils/export.d.ts.map +0 -1
  139. package/dist/utils/export.js.map +0 -1
  140. package/dist/utils/isvalid.d.ts +0 -87
  141. package/dist/utils/isvalid.d.ts.map +0 -1
  142. package/dist/utils/isvalid.js.map +0 -1
  143. package/dist/utils/network.d.ts +0 -70
  144. package/dist/utils/network.d.ts.map +0 -1
  145. package/dist/utils/network.js.map +0 -1
  146. package/dist/utils/parameter.d.ts +0 -44
  147. package/dist/utils/parameter.d.ts.map +0 -1
  148. package/dist/utils/parameter.js.map +0 -1
  149. package/dist/utils/wait.d.ts +0 -43
  150. package/dist/utils/wait.d.ts.map +0 -1
  151. package/dist/utils/wait.js.map +0 -1
  152. /package/frontend/build/static/js/{main.66936fef.js.LICENSE.txt → main.a45801ac.js.LICENSE.txt} +0 -0
@@ -1,35 +1,9 @@
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.2
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
- // AnsiLogger module
29
5
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from './logger/export.js';
30
- // NodeStorage module
31
6
  import { NodeStorageManager } from './storage/export.js';
32
- // Matterbridge
33
7
  import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout } from './utils/export.js';
34
8
  import { logInterfaces, getGlobalNodeModules } from './utils/network.js';
35
9
  import { PluginManager } from './pluginManager.js';
@@ -37,19 +11,14 @@ import { DeviceManager } from './deviceManager.js';
37
11
  import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
38
12
  import { bridge } from './matterbridgeDeviceTypes.js';
39
13
  import { Frontend } from './frontend.js';
40
- // @matter
41
14
  import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode } from '@matter/main';
42
15
  import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
43
16
  import { AggregatorEndpoint } from '@matter/main/endpoints';
44
17
  import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
45
18
  import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
46
- // Default colors
47
19
  const plg = '\u001B[38;5;33m';
48
20
  const dev = '\u001B[38;5;79m';
49
21
  const typ = '\u001B[38;5;207m';
50
- /**
51
- * Represents the Matterbridge application.
52
- */
53
22
  export class Matterbridge extends EventEmitter {
54
23
  systemInformation = {
55
24
  interfaceName: '',
@@ -93,7 +62,7 @@ export class Matterbridge extends EventEmitter {
93
62
  shellySysUpdate: false,
94
63
  shellyMainUpdate: false,
95
64
  profile: getParameter('profile'),
96
- loggerLevel: "info" /* LogLevel.INFO */,
65
+ loggerLevel: "info",
97
66
  fileLogger: false,
98
67
  matterLoggerLevel: MatterLogLevel.INFO,
99
68
  matterFileLogger: false,
@@ -130,11 +99,9 @@ export class Matterbridge extends EventEmitter {
130
99
  plugins;
131
100
  devices;
132
101
  frontend = new Frontend(this);
133
- // Matterbridge storage
134
102
  nodeStorage;
135
103
  nodeContext;
136
104
  nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
137
- // Cleanup
138
105
  hasCleanupStarted = false;
139
106
  initialized = false;
140
107
  execRunningCount = 0;
@@ -147,70 +114,38 @@ export class Matterbridge extends EventEmitter {
147
114
  sigtermHandler;
148
115
  exceptionHandler;
149
116
  rejectionHandler;
150
- // Matter environment
151
117
  environment = Environment.default;
152
- // Matter storage
153
118
  matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
154
119
  matterStorageService;
155
120
  matterStorageManager;
156
121
  matterbridgeContext;
157
122
  mattercontrollerContext;
158
- // Matter parameters
159
- mdnsInterface; // matter server node mdnsInterface: e.g. 'eth0' or 'wlan0' or 'WiFi'
160
- ipv4address; // matter server node listeningAddressIpv4
161
- ipv6address; // matter server node listeningAddressIpv6
162
- port; // first server node port
163
- passcode; // first server node passcode
164
- discriminator; // first server node discriminator
123
+ mdnsInterface;
124
+ ipv4address;
125
+ ipv6address;
126
+ port;
127
+ passcode;
128
+ discriminator;
165
129
  serverNode;
166
130
  aggregatorNode;
167
131
  aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
168
132
  aggregatorProductId = getIntParameter('productId') ?? 0x8000;
169
133
  static instance;
170
- // We load asyncronously so is private
171
134
  constructor() {
172
135
  super();
173
136
  }
174
- /**
175
- * Emits an event of the specified type with the provided arguments.
176
- *
177
- * @template K - The type of the event.
178
- * @param {K} eventName - The name of the event to emit.
179
- * @param {...MatterbridgeEvent[K]} args - The arguments to pass to the event listeners.
180
- * @returns {boolean} - Returns true if the event had listeners, false otherwise.
181
- */
182
137
  emit(eventName, ...args) {
183
138
  return super.emit(eventName, ...args);
184
139
  }
185
- /**
186
- * Registers an event listener for the specified event type.
187
- *
188
- * @template K - The type of the event.
189
- * @param {K} eventName - The name of the event to listen for.
190
- * @param {(...args: MatterbridgeEvent[K]) => void} listener - The callback function to invoke when the event is emitted.
191
- * @returns {this} - Returns the instance of the Matterbridge class.
192
- */
193
140
  on(eventName, listener) {
194
141
  return super.on(eventName, listener);
195
142
  }
196
- /**
197
- * Retrieves the list of Matterbridge devices.
198
- * @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
199
- */
200
143
  getDevices() {
201
144
  return this.devices.array();
202
145
  }
203
- /**
204
- * Retrieves the list of registered plugins.
205
- * @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
206
- */
207
146
  getPlugins() {
208
147
  return this.plugins.array();
209
148
  }
210
- /**
211
- * Set the logger logLevel for the Matterbridge classes.
212
- * @param {LogLevel} logLevel The logger logLevel to set.
213
- */
214
149
  async setLogLevel(logLevel) {
215
150
  if (this.log)
216
151
  this.log.logLevel = logLevel;
@@ -224,31 +159,19 @@ export class Matterbridge extends EventEmitter {
224
159
  for (const plugin of this.plugins) {
225
160
  if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
226
161
  continue;
227
- plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel;
228
- await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel);
229
- }
230
- // Set the global logger callback for the WebSocketServer to the common minimum logLevel
231
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
232
- if (this.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
233
- callbackLogLevel = "info" /* LogLevel.INFO */;
234
- if (this.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
235
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
162
+ plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
163
+ await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
164
+ }
165
+ let callbackLogLevel = "notice";
166
+ if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
167
+ callbackLogLevel = "info";
168
+ if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
169
+ callbackLogLevel = "debug";
236
170
  AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
237
171
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
238
172
  }
239
- /** ***********************************************************************************************************************************/
240
- /** loadInstance() and cleanup() methods */
241
- /** ***********************************************************************************************************************************/
242
- /**
243
- * Loads an instance of the Matterbridge class.
244
- * If an instance already exists, return that instance.
245
- *
246
- * @param initialize - Whether to initialize the Matterbridge instance after loading.
247
- * @returns The loaded Matterbridge instance.
248
- */
249
173
  static async loadInstance(initialize = false) {
250
174
  if (!Matterbridge.instance) {
251
- // eslint-disable-next-line no-console
252
175
  if (hasParameter('debug'))
253
176
  console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
254
177
  Matterbridge.instance = new Matterbridge();
@@ -257,14 +180,8 @@ export class Matterbridge extends EventEmitter {
257
180
  }
258
181
  return Matterbridge.instance;
259
182
  }
260
- /**
261
- * Call cleanup().
262
- * @deprecated This method is deprecated and is only used for jest tests.
263
- *
264
- */
265
183
  async destroyInstance() {
266
184
  this.log.info(`Destroy instance...`);
267
- // Save server nodes to close
268
185
  const servers = [];
269
186
  if (this.bridgeMode === 'bridge') {
270
187
  if (this.serverNode)
@@ -276,81 +193,55 @@ export class Matterbridge extends EventEmitter {
276
193
  servers.push(plugin.serverNode);
277
194
  }
278
195
  }
279
- // Cleanup
280
196
  await this.cleanup('destroying instance...', false);
281
- // Close servers mdns service
282
197
  this.log.info(`Dispose ${servers.length} MdnsService...`);
283
198
  for (const server of servers) {
284
199
  await server.env.get(MdnsService)[Symbol.asyncDispose]();
285
200
  this.log.info(`Closed ${server.id} MdnsService`);
286
201
  }
287
- // Wait for the cleanup to finish
288
202
  await new Promise((resolve) => {
289
203
  setTimeout(resolve, 1000);
290
204
  });
291
205
  }
292
- /**
293
- * Initializes the Matterbridge application.
294
- *
295
- * @remarks
296
- * This method performs the necessary setup and initialization steps for the Matterbridge application.
297
- * It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
298
- * node version, registers signal handlers, initializes storage, and parses the command line.
299
- *
300
- * @returns A Promise that resolves when the initialization is complete.
301
- */
302
206
  async initialize() {
303
- // Set the restart mode
304
207
  if (hasParameter('service'))
305
208
  this.restartMode = 'service';
306
209
  if (hasParameter('docker'))
307
210
  this.restartMode = 'docker';
308
- // Set the matterbridge directory
309
211
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
310
212
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
311
- // Setup the matter environment
312
213
  this.environment.vars.set('log.level', MatterLogLevel.INFO);
313
214
  this.environment.vars.set('log.format', MatterLogFormat.ANSI);
314
215
  this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
315
216
  this.environment.vars.set('runtime.signals', false);
316
217
  this.environment.vars.set('runtime.exitcode', false);
317
- // Create the matterbridge logger
318
- this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
319
- // Register process handlers
218
+ this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
320
219
  this.registerProcessHandlers();
321
- // Initialize nodeStorage and nodeContext
322
220
  try {
323
221
  this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
324
222
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
325
223
  this.log.debug('Creating node storage context for matterbridge');
326
224
  this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
327
- // TODO: Remove this code when node-persist-manager is updated
328
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
329
225
  const keys = (await this.nodeStorage?.storage.keys());
330
226
  for (const key of keys) {
331
227
  this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
332
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
333
228
  await this.nodeStorage?.storage.get(key);
334
229
  }
335
230
  const storages = await this.nodeStorage.getStorageNames();
336
231
  for (const storage of storages) {
337
232
  this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
338
233
  const nodeContext = await this.nodeStorage?.createStorage(storage);
339
- // TODO: Remove this code when node-persist-manager is updated
340
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
341
234
  const keys = (await nodeContext?.storage.keys());
342
235
  keys.forEach(async (key) => {
343
236
  this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
344
237
  await nodeContext?.get(key);
345
238
  });
346
239
  }
347
- // Creating a backup of the node storage since it is not corrupted
348
240
  this.log.debug('Creating node storage backup...');
349
241
  await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
350
242
  this.log.debug('Created node storage backup');
351
243
  }
352
244
  catch (error) {
353
- // Restoring the backup of the node storage since it is corrupted
354
245
  this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
355
246
  if (hasParameter('norestore')) {
356
247
  this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
@@ -365,46 +256,41 @@ export class Matterbridge extends EventEmitter {
365
256
  this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
366
257
  throw new Error('Fatal error creating node storage manager and context for matterbridge');
367
258
  }
368
- // Set the first port to use for the commissioning server (will be incremented in childbridge mode)
369
259
  this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
370
- // Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
371
260
  this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
372
- // Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
373
261
  this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
374
262
  this.log.debug(`Initializing server node for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
375
- // Set matterbridge logger level (context: matterbridgeLogLevel)
376
263
  if (hasParameter('logger')) {
377
264
  const level = getParameter('logger');
378
265
  if (level === 'debug') {
379
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
266
+ this.log.logLevel = "debug";
380
267
  }
381
268
  else if (level === 'info') {
382
- this.log.logLevel = "info" /* LogLevel.INFO */;
269
+ this.log.logLevel = "info";
383
270
  }
384
271
  else if (level === 'notice') {
385
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
272
+ this.log.logLevel = "notice";
386
273
  }
387
274
  else if (level === 'warn') {
388
- this.log.logLevel = "warn" /* LogLevel.WARN */;
275
+ this.log.logLevel = "warn";
389
276
  }
390
277
  else if (level === 'error') {
391
- this.log.logLevel = "error" /* LogLevel.ERROR */;
278
+ this.log.logLevel = "error";
392
279
  }
393
280
  else if (level === 'fatal') {
394
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
281
+ this.log.logLevel = "fatal";
395
282
  }
396
283
  else {
397
284
  this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
398
- this.log.logLevel = "info" /* LogLevel.INFO */;
285
+ this.log.logLevel = "info";
399
286
  }
400
287
  }
401
288
  else {
402
- this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" /* LogLevel.NOTICE */ : "info" /* LogLevel.INFO */);
289
+ this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
403
290
  }
404
291
  this.frontend.logLevel = this.log.logLevel;
405
292
  MatterbridgeEndpoint.logLevel = this.log.logLevel;
406
293
  this.matterbridgeInformation.loggerLevel = this.log.logLevel;
407
- // Create the file logger for matterbridge (context: matterbridgeFileLog)
408
294
  if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
409
295
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
410
296
  this.matterbridgeInformation.fileLogger = true;
@@ -413,7 +299,6 @@ export class Matterbridge extends EventEmitter {
413
299
  this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
414
300
  if (this.profile !== undefined)
415
301
  this.log.debug(`Matterbridge profile: ${this.profile}.`);
416
- // Set matter.js logger level, format and logger (context: matterLogLevel)
417
302
  if (hasParameter('matterlogger')) {
418
303
  const level = getParameter('matterlogger');
419
304
  if (level === 'debug') {
@@ -445,7 +330,6 @@ export class Matterbridge extends EventEmitter {
445
330
  Logger.format = MatterLogFormat.ANSI;
446
331
  Logger.setLogger('default', this.createMatterLogger());
447
332
  this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
448
- // Create the file logger for matter.js (context: matterFileLog)
449
333
  if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
450
334
  this.matterbridgeInformation.matterFileLogger = true;
451
335
  Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
@@ -454,7 +338,17 @@ export class Matterbridge extends EventEmitter {
454
338
  });
455
339
  }
456
340
  this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
457
- // Set the interface to use for matter server node mdnsInterface
341
+ const networkInterfaces = os.networkInterfaces();
342
+ const availableAddresses = Object.entries(networkInterfaces);
343
+ const availableInterfaces = Object.keys(networkInterfaces);
344
+ for (const [ifaceName, ifaces] of availableAddresses) {
345
+ if (ifaces && ifaces.length > 0) {
346
+ this.log.debug(`Network interface: ${CYAN}${ifaceName}${db}:`);
347
+ ifaces.forEach((iface) => {
348
+ this.log.debug(`- ${CYAN}${iface.family}${db} address ${CYAN}${iface.address}${db} netmask ${CYAN}${iface.netmask}${db} mac ${CYAN}${iface.mac}${db} scopeid ${CYAN}${iface.scopeid}${db} ${iface.internal ? 'internal' : 'external'}`);
349
+ });
350
+ }
351
+ }
458
352
  if (hasParameter('mdnsinterface')) {
459
353
  this.mdnsInterface = getParameter('mdnsinterface');
460
354
  }
@@ -463,21 +357,17 @@ export class Matterbridge extends EventEmitter {
463
357
  if (this.mdnsInterface === '')
464
358
  this.mdnsInterface = undefined;
465
359
  }
466
- // Validate mdnsInterface
467
360
  if (this.mdnsInterface) {
468
- const networkInterfaces = os.networkInterfaces();
469
- const availableInterfaces = Object.keys(networkInterfaces);
470
361
  if (!availableInterfaces.includes(this.mdnsInterface)) {
471
362
  this.log.error(`Invalid mdnsInterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
472
363
  this.mdnsInterface = undefined;
473
364
  }
474
365
  else {
475
- this.log.info(`Using mdnsInterface '${this.mdnsInterface}' for the Matter server MdnsBroadcaster.`);
366
+ this.log.info(`Using mdnsInterface ${CYAN}${this.mdnsInterface}${nf} for the Matter MdnsBroadcaster.`);
476
367
  }
477
368
  }
478
369
  if (this.mdnsInterface)
479
370
  this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
480
- // Set the listeningAddressIpv4 for the matter commissioning server
481
371
  if (hasParameter('ipv4address')) {
482
372
  this.ipv4address = getParameter('ipv4address');
483
373
  }
@@ -486,7 +376,20 @@ export class Matterbridge extends EventEmitter {
486
376
  if (this.ipv4address === '')
487
377
  this.ipv4address = undefined;
488
378
  }
489
- // Set the listeningAddressIpv6 for the matter commissioning server
379
+ if (this.ipv4address) {
380
+ let isValid = false;
381
+ for (const [ifaceName, ifaces] of availableAddresses) {
382
+ if (ifaces && ifaces.find((iface) => iface.address === this.ipv4address)) {
383
+ this.log.info(`Using ipv4address ${CYAN}${this.ipv4address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
384
+ isValid = true;
385
+ break;
386
+ }
387
+ }
388
+ if (!isValid) {
389
+ this.log.error(`Invalid ipv4address: ${this.ipv4address}. Using all available addresses.`);
390
+ this.ipv4address = undefined;
391
+ }
392
+ }
490
393
  if (hasParameter('ipv6address')) {
491
394
  this.ipv6address = getParameter('ipv6address');
492
395
  }
@@ -495,19 +398,33 @@ export class Matterbridge extends EventEmitter {
495
398
  if (this.ipv6address === '')
496
399
  this.ipv6address = undefined;
497
400
  }
498
- // Initialize PluginManager
401
+ if (this.ipv6address) {
402
+ let isValid = false;
403
+ for (const [ifaceName, ifaces] of availableAddresses) {
404
+ if (ifaces && ifaces.find((iface) => (iface.scopeid === undefined || iface.scopeid === 0) && iface.address === this.ipv6address)) {
405
+ this.log.info(`Using ipv6address ${CYAN}${this.ipv6address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
406
+ isValid = true;
407
+ break;
408
+ }
409
+ if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6address)) {
410
+ this.log.info(`Using ipv6address ${CYAN}${this.ipv6address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
411
+ isValid = true;
412
+ break;
413
+ }
414
+ }
415
+ if (!isValid) {
416
+ this.log.error(`Invalid ipv6address: ${this.ipv6address}. Using all available addresses.`);
417
+ this.ipv6address = undefined;
418
+ }
419
+ }
499
420
  this.plugins = new PluginManager(this);
500
421
  await this.plugins.loadFromStorage();
501
422
  this.plugins.logLevel = this.log.logLevel;
502
- // Initialize DeviceManager
503
423
  this.devices = new DeviceManager(this, this.nodeContext);
504
424
  this.devices.logLevel = this.log.logLevel;
505
- // Get the plugins from node storage and create the plugins node storage contexts
506
425
  for (const plugin of this.plugins) {
507
426
  const packageJson = await this.plugins.parse(plugin);
508
427
  if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
509
- // Try to reinstall the plugin from npm (for Docker pull and external plugins)
510
- // We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
511
428
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
512
429
  try {
513
430
  await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
@@ -529,7 +446,6 @@ export class Matterbridge extends EventEmitter {
529
446
  await plugin.nodeContext.set('description', plugin.description);
530
447
  await plugin.nodeContext.set('author', plugin.author);
531
448
  }
532
- // Log system info and create .matterbridge directory
533
449
  await this.logNodeAndSystemInfo();
534
450
  this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
535
451
  `${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
@@ -537,7 +453,6 @@ export class Matterbridge extends EventEmitter {
537
453
  `${hasParameter('controller') ? 'mode controller ' : ''}` +
538
454
  `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
539
455
  `running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
540
- // Check node version and throw error
541
456
  const minNodeVersion = 18;
542
457
  const nodeVersion = process.versions.node;
543
458
  const versionMajor = parseInt(nodeVersion.split('.')[0]);
@@ -545,15 +460,9 @@ export class Matterbridge extends EventEmitter {
545
460
  this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
546
461
  throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
547
462
  }
548
- // Parse command line
549
463
  await this.parseCommandLine();
550
464
  this.initialized = true;
551
465
  }
552
- /**
553
- * Parses the command line arguments and performs the corresponding actions.
554
- * @private
555
- * @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
556
- */
557
466
  async parseCommandLine() {
558
467
  if (hasParameter('help')) {
559
468
  this.log.info(`\nUsage: matterbridge [options]\n
@@ -665,7 +574,6 @@ export class Matterbridge extends EventEmitter {
665
574
  this.shutdown = true;
666
575
  return;
667
576
  }
668
- // Start the matter storage and create the matterbridge context
669
577
  try {
670
578
  await this.startMatterStorage();
671
579
  }
@@ -673,14 +581,12 @@ export class Matterbridge extends EventEmitter {
673
581
  this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
674
582
  throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
675
583
  }
676
- // Clear the matterbridge context if the reset parameter is set
677
584
  if (hasParameter('reset') && getParameter('reset') === undefined) {
678
585
  this.initialized = true;
679
586
  await this.shutdownProcessAndReset();
680
587
  this.shutdown = true;
681
588
  return;
682
589
  }
683
- // Clear matterbridge plugin context if the reset parameter is set
684
590
  if (hasParameter('reset') && getParameter('reset') !== undefined) {
685
591
  this.log.debug(`Reset plugin ${getParameter('reset')}`);
686
592
  const plugin = this.plugins.get(getParameter('reset'));
@@ -705,37 +611,30 @@ export class Matterbridge extends EventEmitter {
705
611
  this.shutdown = true;
706
612
  return;
707
613
  }
708
- // Initialize frontend
709
614
  if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
710
615
  await this.frontend.start(getIntParameter('frontend'));
711
- // Check in 30 seconds the latest versions
712
616
  this.checkUpdateTimeout = setTimeout(async () => {
713
617
  const { checkUpdates } = await import('./update.js');
714
618
  checkUpdates(this);
715
619
  }, 30 * 1000).unref();
716
- // Check each 24 hours the latest versions
717
620
  this.checkUpdateInterval = setInterval(async () => {
718
621
  const { checkUpdates } = await import('./update.js');
719
622
  checkUpdates(this);
720
623
  }, 24 * 60 * 60 * 1000).unref();
721
- // Start the matterbridge in mode test
722
624
  if (hasParameter('test')) {
723
625
  this.bridgeMode = 'bridge';
724
626
  MatterbridgeEndpoint.bridgeMode = 'bridge';
725
627
  return;
726
628
  }
727
- // Start the matterbridge in mode controller
728
629
  if (hasParameter('controller')) {
729
630
  this.bridgeMode = 'controller';
730
631
  await this.startController();
731
632
  return;
732
633
  }
733
- // Check if the bridge mode is set and start matterbridge in bridge mode if not set
734
634
  if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
735
635
  this.log.info('Setting default matterbridge start mode to bridge');
736
636
  await this.nodeContext?.set('bridgeMode', 'bridge');
737
637
  }
738
- // Start matterbridge in bridge mode
739
638
  if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
740
639
  this.bridgeMode = 'bridge';
741
640
  MatterbridgeEndpoint.bridgeMode = 'bridge';
@@ -743,7 +642,6 @@ export class Matterbridge extends EventEmitter {
743
642
  await this.startBridge();
744
643
  return;
745
644
  }
746
- // Start matterbridge in childbridge mode
747
645
  if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
748
646
  this.bridgeMode = 'childbridge';
749
647
  MatterbridgeEndpoint.bridgeMode = 'childbridge';
@@ -752,20 +650,10 @@ export class Matterbridge extends EventEmitter {
752
650
  return;
753
651
  }
754
652
  }
755
- /**
756
- * Asynchronously loads and starts the registered plugins.
757
- *
758
- * This method is responsible for initializing and staarting all enabled plugins.
759
- * It ensures that each plugin is properly loaded and started before the bridge starts.
760
- *
761
- * @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
762
- */
763
653
  async startPlugins() {
764
- // Check, load and start the plugins
765
654
  for (const plugin of this.plugins) {
766
655
  plugin.configJson = await this.plugins.loadConfig(plugin);
767
656
  plugin.schemaJson = await this.plugins.loadSchema(plugin);
768
- // Check if the plugin is available
769
657
  if (!(await this.plugins.resolve(plugin.path))) {
770
658
  this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
771
659
  plugin.enabled = false;
@@ -785,26 +673,20 @@ export class Matterbridge extends EventEmitter {
785
673
  plugin.addedDevices = undefined;
786
674
  plugin.qrPairingCode = undefined;
787
675
  plugin.manualPairingCode = undefined;
788
- this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
676
+ this.plugins.load(plugin, true, 'Matterbridge is starting');
789
677
  }
790
678
  this.frontend.wssSendRefreshRequired('plugins');
791
679
  }
792
- /**
793
- * Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
794
- * When either of these signals are received, the cleanup method is called with an appropriate message.
795
- */
796
680
  registerProcessHandlers() {
797
681
  this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
798
682
  process.removeAllListeners('uncaughtException');
799
683
  process.removeAllListeners('unhandledRejection');
800
684
  this.exceptionHandler = async (error) => {
801
685
  this.log.error('Unhandled Exception detected at:', error.stack || error, rs);
802
- // await this.cleanup('Unhandled Exception detected, cleaning up...');
803
686
  };
804
687
  process.on('uncaughtException', this.exceptionHandler);
805
688
  this.rejectionHandler = async (reason, promise) => {
806
689
  this.log.error('Unhandled Rejection detected at:', promise, 'reason:', reason instanceof Error ? reason.stack : reason, rs);
807
- // await this.cleanup('Unhandled Rejection detected, cleaning up...');
808
690
  };
809
691
  process.on('unhandledRejection', this.rejectionHandler);
810
692
  this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
@@ -817,9 +699,6 @@ export class Matterbridge extends EventEmitter {
817
699
  };
818
700
  process.on('SIGTERM', this.sigtermHandler);
819
701
  }
820
- /**
821
- * Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
822
- */
823
702
  deregisterProcesslHandlers() {
824
703
  this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
825
704
  if (this.exceptionHandler)
@@ -836,17 +715,12 @@ export class Matterbridge extends EventEmitter {
836
715
  process.off('SIGTERM', this.sigtermHandler);
837
716
  this.sigtermHandler = undefined;
838
717
  }
839
- /**
840
- * Logs the node and system information.
841
- */
842
718
  async logNodeAndSystemInfo() {
843
- // IP address information
844
719
  const networkInterfaces = os.networkInterfaces();
845
720
  this.systemInformation.interfaceName = '';
846
721
  this.systemInformation.ipv4Address = '';
847
722
  this.systemInformation.ipv6Address = '';
848
723
  for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
849
- // this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
850
724
  if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
851
725
  continue;
852
726
  if (!interfaceDetails) {
@@ -872,22 +746,19 @@ export class Matterbridge extends EventEmitter {
872
746
  break;
873
747
  }
874
748
  }
875
- // Node information
876
749
  this.systemInformation.nodeVersion = process.versions.node;
877
750
  const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
878
751
  const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
879
752
  const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
880
- // Host system information
881
753
  this.systemInformation.hostname = os.hostname();
882
754
  this.systemInformation.user = os.userInfo().username;
883
- this.systemInformation.osType = os.type(); // "Windows_NT", "Darwin", etc.
884
- this.systemInformation.osRelease = os.release(); // Kernel version
885
- this.systemInformation.osPlatform = os.platform(); // "win32", "linux", "darwin", etc.
886
- this.systemInformation.osArch = os.arch(); // "x64", "arm", etc.
887
- this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
888
- this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
889
- this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours'; // Convert to hours
890
- // Log the system information
755
+ this.systemInformation.osType = os.type();
756
+ this.systemInformation.osRelease = os.release();
757
+ this.systemInformation.osPlatform = os.platform();
758
+ this.systemInformation.osArch = os.arch();
759
+ this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
760
+ this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
761
+ this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
891
762
  this.log.debug('Host System Information:');
892
763
  this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
893
764
  this.log.debug(`- User: ${this.systemInformation.user}`);
@@ -903,20 +774,16 @@ export class Matterbridge extends EventEmitter {
903
774
  this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
904
775
  this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
905
776
  this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
906
- // Home directory
907
777
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
908
778
  this.matterbridgeInformation.homeDirectory = this.homeDirectory;
909
779
  this.log.debug(`Home Directory: ${this.homeDirectory}`);
910
- // Package root directory
911
780
  const { fileURLToPath } = await import('node:url');
912
781
  const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
913
782
  this.rootDirectory = path.resolve(currentFileDirectory, '../');
914
783
  this.matterbridgeInformation.rootDirectory = this.rootDirectory;
915
784
  this.log.debug(`Root Directory: ${this.rootDirectory}`);
916
- // Global node_modules directory
917
785
  if (this.nodeContext)
918
786
  this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
919
- // First run of Matterbridge so the node storage is empty
920
787
  if (this.globalModulesDirectory === '') {
921
788
  try {
922
789
  this.execRunningCount++;
@@ -932,20 +799,6 @@ export class Matterbridge extends EventEmitter {
932
799
  }
933
800
  else
934
801
  this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
935
- /* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
936
- else {
937
- this.getGlobalNodeModules()
938
- .then(async (globalModulesDirectory) => {
939
- this.globalModulesDirectory = globalModulesDirectory;
940
- this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
941
- this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
942
- await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
943
- })
944
- .catch((error) => {
945
- this.log.error(`Error getting global node_modules directory: ${error}`);
946
- });
947
- }*/
948
- // Create the data directory .matterbridge in the home directory
949
802
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
950
803
  this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
951
804
  try {
@@ -969,7 +822,6 @@ export class Matterbridge extends EventEmitter {
969
822
  }
970
823
  }
971
824
  this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
972
- // Create the plugin directory Matterbridge in the home directory
973
825
  this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
974
826
  this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
975
827
  try {
@@ -993,68 +845,50 @@ export class Matterbridge extends EventEmitter {
993
845
  }
994
846
  }
995
847
  this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
996
- // Matterbridge version
997
848
  const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
998
849
  this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
999
850
  this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
1000
851
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
1001
- // Matterbridge latest version
1002
852
  if (this.nodeContext)
1003
853
  this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
1004
854
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
1005
- // this.getMatterbridgeLatestVersion();
1006
- // Current working directory
1007
855
  const currentDir = process.cwd();
1008
856
  this.log.debug(`Current Working Directory: ${currentDir}`);
1009
- // Command line arguments (excluding 'node' and the script name)
1010
857
  const cmdArgs = process.argv.slice(2).join(' ');
1011
858
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
1012
859
  }
1013
- /**
1014
- * Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
1015
- *
1016
- * @returns {Function} The MatterLogger function.
1017
- */
1018
860
  createMatterLogger() {
1019
- const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
861
+ const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
1020
862
  return (_level, formattedLog) => {
1021
863
  const logger = formattedLog.slice(44, 44 + 20).trim();
1022
864
  const message = formattedLog.slice(65);
1023
865
  matterLogger.logName = logger;
1024
866
  switch (_level) {
1025
867
  case MatterLogLevel.DEBUG:
1026
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
868
+ matterLogger.log("debug", message);
1027
869
  break;
1028
870
  case MatterLogLevel.INFO:
1029
- matterLogger.log("info" /* LogLevel.INFO */, message);
871
+ matterLogger.log("info", message);
1030
872
  break;
1031
873
  case MatterLogLevel.NOTICE:
1032
- matterLogger.log("notice" /* LogLevel.NOTICE */, message);
874
+ matterLogger.log("notice", message);
1033
875
  break;
1034
876
  case MatterLogLevel.WARN:
1035
- matterLogger.log("warn" /* LogLevel.WARN */, message);
877
+ matterLogger.log("warn", message);
1036
878
  break;
1037
879
  case MatterLogLevel.ERROR:
1038
- matterLogger.log("error" /* LogLevel.ERROR */, message);
880
+ matterLogger.log("error", message);
1039
881
  break;
1040
882
  case MatterLogLevel.FATAL:
1041
- matterLogger.log("fatal" /* LogLevel.FATAL */, message);
883
+ matterLogger.log("fatal", message);
1042
884
  break;
1043
885
  default:
1044
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
886
+ matterLogger.log("debug", message);
1045
887
  break;
1046
888
  }
1047
889
  };
1048
890
  }
1049
- /**
1050
- * Creates a Matter File Logger.
1051
- *
1052
- * @param {string} filePath - The path to the log file.
1053
- * @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
1054
- * @returns {Function} - A function that logs formatted messages to the log file.
1055
- */
1056
891
  async createMatterFileLogger(filePath, unlink = false) {
1057
- // 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
1058
892
  let fileSize = 0;
1059
893
  if (unlink) {
1060
894
  try {
@@ -1103,21 +937,12 @@ export class Matterbridge extends EventEmitter {
1103
937
  }
1104
938
  };
1105
939
  }
1106
- /**
1107
- * Restarts the process by exiting the current instance and loading a new instance.
1108
- */
1109
940
  async restartProcess() {
1110
941
  await this.cleanup('restarting...', true);
1111
942
  }
1112
- /**
1113
- * Shut down the process by exiting the current process.
1114
- */
1115
943
  async shutdownProcess() {
1116
944
  await this.cleanup('shutting down...', false);
1117
945
  }
1118
- /**
1119
- * Update matterbridge and and shut down the process.
1120
- */
1121
946
  async updateProcess() {
1122
947
  this.log.info('Updating matterbridge...');
1123
948
  try {
@@ -1130,72 +955,51 @@ export class Matterbridge extends EventEmitter {
1130
955
  this.frontend.wssSendRestartRequired();
1131
956
  await this.cleanup('updating...', false);
1132
957
  }
1133
- /**
1134
- * Unregister all devices and shut down the process.
1135
- */
1136
958
  async unregisterAndShutdownProcess() {
1137
959
  this.log.info('Unregistering all devices and shutting down...');
1138
960
  for (const plugin of this.plugins) {
1139
961
  await this.removeAllBridgedEndpoints(plugin.name, 250);
1140
962
  }
1141
963
  this.log.debug('Waiting for the MessageExchange to finish...');
1142
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second for MessageExchange to finish
964
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1143
965
  this.log.debug('Cleaning up and shutting down...');
1144
966
  await this.cleanup('unregistered all devices and shutting down...', false);
1145
967
  }
1146
- /**
1147
- * Reset commissioning and shut down the process.
1148
- */
1149
968
  async shutdownProcessAndReset() {
1150
969
  await this.cleanup('shutting down with reset...', false);
1151
970
  }
1152
- /**
1153
- * Factory reset and shut down the process.
1154
- */
1155
971
  async shutdownProcessAndFactoryReset() {
1156
972
  await this.cleanup('shutting down with factory reset...', false);
1157
973
  }
1158
- /**
1159
- * Cleans up the Matterbridge instance.
1160
- * @param message - The cleanup message.
1161
- * @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
1162
- * @returns A promise that resolves when the cleanup is completed.
1163
- */
1164
974
  async cleanup(message, restart = false) {
1165
975
  if (this.initialized && !this.hasCleanupStarted) {
1166
976
  this.hasCleanupStarted = true;
1167
977
  this.log.info(message);
1168
- // Clear the start matter interval
1169
978
  if (this.startMatterInterval) {
1170
979
  clearInterval(this.startMatterInterval);
1171
980
  this.startMatterInterval = undefined;
1172
981
  this.log.debug('Start matter interval cleared');
1173
982
  }
1174
- // Clear the check update timeout
1175
983
  if (this.checkUpdateTimeout) {
1176
984
  clearInterval(this.checkUpdateTimeout);
1177
985
  this.checkUpdateTimeout = undefined;
1178
986
  this.log.debug('Check update timeout cleared');
1179
987
  }
1180
- // Clear the check update interval
1181
988
  if (this.checkUpdateInterval) {
1182
989
  clearInterval(this.checkUpdateInterval);
1183
990
  this.checkUpdateInterval = undefined;
1184
991
  this.log.debug('Check update interval cleared');
1185
992
  }
1186
- // Clear the configure timeout
1187
993
  if (this.configureTimeout) {
1188
994
  clearTimeout(this.configureTimeout);
1189
995
  this.configureTimeout = undefined;
1190
996
  this.log.debug('Matterbridge configure timeout cleared');
1191
997
  }
1192
- // Clear the reachability timeout
1193
998
  if (this.reachabilityTimeout) {
1194
999
  clearTimeout(this.reachabilityTimeout);
1195
1000
  this.reachabilityTimeout = undefined;
1196
1001
  this.log.debug('Matterbridge reachability timeout cleared');
1197
1002
  }
1198
- // Calling the shutdown method of each plugin and clear the plugins reachability timeout
1199
1003
  for (const plugin of this.plugins) {
1200
1004
  if (!plugin.enabled || plugin.error)
1201
1005
  continue;
@@ -1206,10 +1010,9 @@ export class Matterbridge extends EventEmitter {
1206
1010
  this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
1207
1011
  }
1208
1012
  }
1209
- // Stopping matter server nodes
1210
1013
  this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
1211
1014
  this.log.debug('Waiting for the MessageExchange to finish...');
1212
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second for MessageExchange to finish
1015
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1213
1016
  if (this.bridgeMode === 'bridge') {
1214
1017
  if (this.serverNode) {
1215
1018
  await this.stopServerNode(this.serverNode);
@@ -1225,7 +1028,6 @@ export class Matterbridge extends EventEmitter {
1225
1028
  }
1226
1029
  }
1227
1030
  this.log.notice('Stopped matter server nodes');
1228
- // Matter commisioning reset
1229
1031
  if (message === 'shutting down with reset...') {
1230
1032
  this.log.info('Resetting Matterbridge commissioning information...');
1231
1033
  await this.matterStorageManager?.createContext('events')?.clearAll();
@@ -1235,37 +1037,17 @@ export class Matterbridge extends EventEmitter {
1235
1037
  await this.matterbridgeContext?.clearAll();
1236
1038
  this.log.info('Matter storage reset done! Remove the bridge from the controller.');
1237
1039
  }
1238
- // Stop matter storage
1239
1040
  await this.stopMatterStorage();
1240
- // Stop the frontend
1241
1041
  await this.frontend.stop();
1242
- // Remove the matterfilelogger
1243
1042
  try {
1244
1043
  Logger.removeLogger('matterfilelogger');
1245
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1246
1044
  }
1247
1045
  catch (error) {
1248
- // this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
1249
1046
  }
1250
- // Serialize registeredDevices
1251
1047
  if (this.nodeStorage && this.nodeContext) {
1252
- /*
1253
- TODO: Implement serialization of registered devices in edge mode
1254
- this.log.info('Saving registered devices...');
1255
- const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
1256
- this.devices.forEach(async (device) => {
1257
- const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
1258
- // this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
1259
- if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
1260
- });
1261
- await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
1262
- this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
1263
- */
1264
- // Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
1265
1048
  this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
1266
1049
  await this.nodeContext.close();
1267
1050
  this.nodeContext = undefined;
1268
- // Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
1269
1051
  for (const plugin of this.plugins) {
1270
1052
  if (plugin.nodeContext) {
1271
1053
  this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
@@ -1282,10 +1064,8 @@ export class Matterbridge extends EventEmitter {
1282
1064
  }
1283
1065
  this.plugins.clear();
1284
1066
  this.devices.clear();
1285
- // Factory reset
1286
1067
  if (message === 'shutting down with factory reset...') {
1287
1068
  try {
1288
- // Delete old matter storage file and backup
1289
1069
  const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
1290
1070
  this.log.info(`Unlinking old matter storage file: ${file}`);
1291
1071
  await fs.unlink(file);
@@ -1299,7 +1079,6 @@ export class Matterbridge extends EventEmitter {
1299
1079
  }
1300
1080
  }
1301
1081
  try {
1302
- // Delete matter node storage directory with its subdirectories and backup
1303
1082
  const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
1304
1083
  this.log.info(`Removing matter node storage directory: ${dir}`);
1305
1084
  await fs.rm(dir, { recursive: true });
@@ -1313,7 +1092,6 @@ export class Matterbridge extends EventEmitter {
1313
1092
  }
1314
1093
  }
1315
1094
  try {
1316
- // Delete node storage directory with its subdirectories and backup
1317
1095
  const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
1318
1096
  this.log.info(`Removing storage directory: ${dir}`);
1319
1097
  await fs.rm(dir, { recursive: true });
@@ -1328,13 +1106,12 @@ export class Matterbridge extends EventEmitter {
1328
1106
  }
1329
1107
  this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
1330
1108
  }
1331
- // Deregisters the process handlers
1332
1109
  this.deregisterProcesslHandlers();
1333
1110
  if (restart) {
1334
1111
  if (message === 'updating...') {
1335
1112
  this.log.info('Cleanup completed. Updating...');
1336
1113
  Matterbridge.instance = undefined;
1337
- this.emit('update'); // Restart the process but the update has been done before. TODO move all updates to the cli
1114
+ this.emit('update');
1338
1115
  }
1339
1116
  else if (message === 'restarting...') {
1340
1117
  this.log.info('Cleanup completed. Restarting...');
@@ -1354,14 +1131,6 @@ export class Matterbridge extends EventEmitter {
1354
1131
  this.log.debug('Cleanup already started...');
1355
1132
  }
1356
1133
  }
1357
- /**
1358
- * Creates and configures the server node for an accessory plugin for a given device.
1359
- *
1360
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1361
- * @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
1362
- * @param {boolean} [start=false] - Whether to start the server node after adding the device.
1363
- * @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
1364
- */
1365
1134
  async createAccessoryPlugin(plugin, device, start = false) {
1366
1135
  if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
1367
1136
  plugin.locked = true;
@@ -1374,13 +1143,6 @@ export class Matterbridge extends EventEmitter {
1374
1143
  await this.startServerNode(plugin.serverNode);
1375
1144
  }
1376
1145
  }
1377
- /**
1378
- * Creates and configures the server node for a dynamic plugin.
1379
- *
1380
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1381
- * @param {boolean} [start=false] - Whether to start the server node after adding the aggregator node.
1382
- * @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
1383
- */
1384
1146
  async createDynamicPlugin(plugin, start = false) {
1385
1147
  if (!plugin.locked) {
1386
1148
  plugin.locked = true;
@@ -1392,13 +1154,7 @@ export class Matterbridge extends EventEmitter {
1392
1154
  await this.startServerNode(plugin.serverNode);
1393
1155
  }
1394
1156
  }
1395
- /**
1396
- * Starts the Matterbridge in bridge mode.
1397
- * @private
1398
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1399
- */
1400
1157
  async startBridge() {
1401
- // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1402
1158
  if (!this.matterStorageManager)
1403
1159
  throw new Error('No storage manager initialized');
1404
1160
  if (!this.matterbridgeContext)
@@ -1436,9 +1192,7 @@ export class Matterbridge extends EventEmitter {
1436
1192
  clearInterval(this.startMatterInterval);
1437
1193
  this.startMatterInterval = undefined;
1438
1194
  this.log.debug('Cleared startMatterInterval interval for Matterbridge');
1439
- // Start the Matter server node
1440
1195
  this.startServerNode(this.serverNode);
1441
- // Configure the plugins
1442
1196
  this.configureTimeout = setTimeout(async () => {
1443
1197
  for (const plugin of this.plugins) {
1444
1198
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1456,7 +1210,6 @@ export class Matterbridge extends EventEmitter {
1456
1210
  }
1457
1211
  this.frontend.wssSendRefreshRequired('plugins');
1458
1212
  }, 30 * 1000);
1459
- // Setting reachability to true
1460
1213
  this.reachabilityTimeout = setTimeout(() => {
1461
1214
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1462
1215
  if (this.aggregatorNode)
@@ -1465,11 +1218,6 @@ export class Matterbridge extends EventEmitter {
1465
1218
  }, 60 * 1000);
1466
1219
  }, 1000);
1467
1220
  }
1468
- /**
1469
- * Starts the Matterbridge in childbridge mode.
1470
- * @private
1471
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1472
- */
1473
1221
  async startChildbridge() {
1474
1222
  if (!this.matterStorageManager)
1475
1223
  throw new Error('No storage manager initialized');
@@ -1514,7 +1262,6 @@ export class Matterbridge extends EventEmitter {
1514
1262
  clearInterval(this.startMatterInterval);
1515
1263
  this.startMatterInterval = undefined;
1516
1264
  this.log.debug('Cleared startMatterInterval interval in childbridge mode');
1517
- // Configure the plugins
1518
1265
  this.configureTimeout = setTimeout(async () => {
1519
1266
  for (const plugin of this.plugins) {
1520
1267
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1551,9 +1298,7 @@ export class Matterbridge extends EventEmitter {
1551
1298
  this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
1552
1299
  continue;
1553
1300
  }
1554
- // Start the Matter server node
1555
1301
  this.startServerNode(plugin.serverNode);
1556
- // Setting reachability to true
1557
1302
  plugin.reachabilityTimeout = setTimeout(() => {
1558
1303
  this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggragator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
1559
1304
  if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
@@ -1563,219 +1308,9 @@ export class Matterbridge extends EventEmitter {
1563
1308
  }
1564
1309
  }, 1000);
1565
1310
  }
1566
- /**
1567
- * Starts the Matterbridge controller.
1568
- * @private
1569
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1570
- */
1571
1311
  async startController() {
1572
- /*
1573
- if (!this.storageManager) {
1574
- this.log.error('No storage manager initialized');
1575
- await this.cleanup('No storage manager initialized');
1576
- return;
1577
- }
1578
- this.log.info('Creating context: mattercontrollerContext');
1579
- this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
1580
- if (!this.mattercontrollerContext) {
1581
- this.log.error('No storage context mattercontrollerContext initialized');
1582
- await this.cleanup('No storage context mattercontrollerContext initialized');
1583
- return;
1584
- }
1585
-
1586
- this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1587
- this.matterServer = await this.createMatterServer(this.storageManager);
1588
- this.log.info('Creating matter commissioning controller');
1589
- this.commissioningController = new CommissioningController({
1590
- autoConnect: false,
1591
- });
1592
- this.log.info('Adding matter commissioning controller to matter server');
1593
- await this.matterServer.addCommissioningController(this.commissioningController);
1594
-
1595
- this.log.info('Starting matter server');
1596
- await this.matterServer.start();
1597
- this.log.info('Matter server started');
1598
-
1599
- if (hasParameter('pairingcode')) {
1600
- this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
1601
- const pairingCode = getParameter('pairingcode');
1602
- const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get<string>('ip') : undefined;
1603
- const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get<number>('port') : undefined;
1604
-
1605
- let longDiscriminator, setupPin, shortDiscriminator;
1606
- if (pairingCode !== undefined) {
1607
- const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
1608
- shortDiscriminator = pairingCodeCodec.shortDiscriminator;
1609
- longDiscriminator = undefined;
1610
- setupPin = pairingCodeCodec.passcode;
1611
- this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
1612
- } else {
1613
- longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
1614
- if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
1615
- setupPin = this.mattercontrollerContext.get('pin', 20202021);
1616
- }
1617
- if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
1618
- throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
1619
- }
1620
-
1621
- const commissioningOptions: ControllerCommissioningFlowOptions = {
1622
- regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
1623
- regulatoryCountryCode: 'XX',
1624
- };
1625
- const options = {
1626
- commissioning: commissioningOptions,
1627
- discovery: {
1628
- knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
1629
- identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
1630
- },
1631
- passcode: setupPin,
1632
- } as NodeCommissioningOptions;
1633
- this.log.info('Commissioning with options:', options);
1634
- const nodeId = await this.commissioningController.commissionNode(options);
1635
- this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
1636
- this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
1637
- } // (hasParameter('pairingcode'))
1638
-
1639
- if (hasParameter('unpairall')) {
1640
- this.log.info('***Commissioning controller unpairing all nodes...');
1641
- const nodeIds = this.commissioningController.getCommissionedNodes();
1642
- for (const nodeId of nodeIds) {
1643
- this.log.info('***Commissioning controller unpairing node:', nodeId);
1644
- await this.commissioningController.removeNode(nodeId);
1645
- }
1646
- return;
1647
- }
1648
-
1649
- if (hasParameter('discover')) {
1650
- // const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1651
- // console.log(discover);
1652
- }
1653
-
1654
- if (!this.commissioningController.isCommissioned()) {
1655
- this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
1656
- return;
1657
- }
1658
-
1659
- const nodeIds = this.commissioningController.getCommissionedNodes();
1660
- this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
1661
- for (const nodeId of nodeIds) {
1662
- this.log.info(`***Connecting to commissioned node: ${nodeId}`);
1663
-
1664
- const node = await this.commissioningController.connectNode(nodeId, {
1665
- autoSubscribe: false,
1666
- attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
1667
- this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
1668
- eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
1669
- this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
1670
- stateInformationCallback: (peerNodeId, info) => {
1671
- switch (info) {
1672
- case NodeStateInformation.Connected:
1673
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
1674
- break;
1675
- case NodeStateInformation.Disconnected:
1676
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
1677
- break;
1678
- case NodeStateInformation.Reconnecting:
1679
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
1680
- break;
1681
- case NodeStateInformation.WaitingForDeviceDiscovery:
1682
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
1683
- break;
1684
- case NodeStateInformation.StructureChanged:
1685
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
1686
- break;
1687
- case NodeStateInformation.Decommissioned:
1688
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
1689
- break;
1690
- default:
1691
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
1692
- break;
1693
- }
1694
- },
1695
- });
1696
-
1697
- node.logStructure();
1698
-
1699
- // Get the interaction client
1700
- this.log.info('Getting the interaction client');
1701
- const interactionClient = await node.getInteractionClient();
1702
- let cluster;
1703
- let attributes;
1704
-
1705
- // Log BasicInformationCluster
1706
- cluster = BasicInformationCluster;
1707
- attributes = await interactionClient.getMultipleAttributes({
1708
- attributes: [{ clusterId: cluster.id }],
1709
- });
1710
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1711
- attributes.forEach((attribute) => {
1712
- this.log.info(
1713
- `- 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}`,
1714
- );
1715
- });
1716
-
1717
- // Log PowerSourceCluster
1718
- cluster = PowerSourceCluster;
1719
- attributes = await interactionClient.getMultipleAttributes({
1720
- attributes: [{ clusterId: cluster.id }],
1721
- });
1722
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1723
- attributes.forEach((attribute) => {
1724
- this.log.info(
1725
- `- 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}`,
1726
- );
1727
- });
1728
-
1729
- // Log ThreadNetworkDiagnostics
1730
- cluster = ThreadNetworkDiagnosticsCluster;
1731
- attributes = await interactionClient.getMultipleAttributes({
1732
- attributes: [{ clusterId: cluster.id }],
1733
- });
1734
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1735
- attributes.forEach((attribute) => {
1736
- this.log.info(
1737
- `- 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}`,
1738
- );
1739
- });
1740
-
1741
- // Log SwitchCluster
1742
- cluster = SwitchCluster;
1743
- attributes = await interactionClient.getMultipleAttributes({
1744
- attributes: [{ clusterId: cluster.id }],
1745
- });
1746
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1747
- attributes.forEach((attribute) => {
1748
- this.log.info(
1749
- `- 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}`,
1750
- );
1751
- });
1752
-
1753
- this.log.info('Subscribing to all attributes and events');
1754
- await node.subscribeAllAttributesAndEvents({
1755
- ignoreInitialTriggers: false,
1756
- attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
1757
- this.log.info(
1758
- `***${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}`,
1759
- ),
1760
- eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
1761
- this.log.info(
1762
- `***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
1763
- );
1764
- },
1765
- });
1766
- this.log.info('Subscribed to all attributes and events');
1767
- }
1768
- */
1769
1312
  }
1770
- /** ***********************************************************************************************************************************/
1771
- /** Matter.js methods */
1772
- /** ***********************************************************************************************************************************/
1773
- /**
1774
- * Starts the matter storage process with name Matterbridge.
1775
- * @returns {Promise<void>} - A promise that resolves when the storage process is started.
1776
- */
1777
1313
  async startMatterStorage() {
1778
- // Setup Matter storage
1779
1314
  this.log.info(`Starting matter node storage...`);
1780
1315
  this.matterStorageService = this.environment.get(StorageService);
1781
1316
  this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
@@ -1783,25 +1318,13 @@ export class Matterbridge extends EventEmitter {
1783
1318
  this.log.info('Matter node storage manager "Matterbridge" created');
1784
1319
  this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, 'Matterbridge', this.aggregatorProductId, 'Matterbridge aggregator');
1785
1320
  this.log.info('Matter node storage started');
1786
- // Backup matter storage since it is created/opened correctly
1787
1321
  await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
1788
1322
  }
1789
- /**
1790
- * Makes a backup copy of the specified matter storage directory.
1791
- *
1792
- * @param storageName - The name of the storage directory to be backed up.
1793
- * @param backupName - The name of the backup directory to be created.
1794
- * @returns {Promise<void>} A promise that resolves when the has been done.
1795
- */
1796
1323
  async backupMatterStorage(storageName, backupName) {
1797
1324
  this.log.info('Creating matter node storage backup...');
1798
1325
  await copyDirectory(storageName, backupName);
1799
1326
  this.log.info('Created matter node storage backup');
1800
1327
  }
1801
- /**
1802
- * Stops the matter storage.
1803
- * @returns {Promise<void>} A promise that resolves when the storage is stopped.
1804
- */
1805
1328
  async stopMatterStorage() {
1806
1329
  this.log.info('Closing matter node storage...');
1807
1330
  this.matterStorageManager?.close();
@@ -1810,19 +1333,6 @@ export class Matterbridge extends EventEmitter {
1810
1333
  this.matterbridgeContext = undefined;
1811
1334
  this.log.info('Matter node storage closed');
1812
1335
  }
1813
- /**
1814
- * Creates a server node storage context.
1815
- *
1816
- * @param {string} pluginName - The name of the plugin.
1817
- * @param {string} deviceName - The name of the device.
1818
- * @param {DeviceTypeId} deviceType - The device type of the device.
1819
- * @param {number} vendorId - The vendor ID.
1820
- * @param {string} vendorName - The vendor name.
1821
- * @param {number} productId - The product ID.
1822
- * @param {string} productName - The product name.
1823
- * @param {string} [serialNumber] - The serial number of the device (optional).
1824
- * @returns {Promise<StorageContext>} The storage context for the commissioning server.
1825
- */
1826
1336
  async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
1827
1337
  const { randomBytes } = await import('node:crypto');
1828
1338
  if (!this.matterStorageService)
@@ -1856,15 +1366,6 @@ export class Matterbridge extends EventEmitter {
1856
1366
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1857
1367
  return storageContext;
1858
1368
  }
1859
- /**
1860
- * Creates a server node.
1861
- *
1862
- * @param {StorageContext} storageContext - The storage context for the server node.
1863
- * @param {number} [port=5540] - The port number for the server node. Defaults to 5540.
1864
- * @param {number} [passcode=20242025] - The passcode for the server node. Defaults to 20242025.
1865
- * @param {number} [discriminator=3850] - The discriminator for the server node. Defaults to 3850.
1866
- * @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
1867
- */
1868
1369
  async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
1869
1370
  const storeId = await storageContext.get('storeId');
1870
1371
  this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
@@ -1874,33 +1375,21 @@ export class Matterbridge extends EventEmitter {
1874
1375
  this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
1875
1376
  this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1876
1377
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1877
- /**
1878
- * Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
1879
- */
1880
1378
  const serverNode = await ServerNode.create({
1881
- // Required: Give the Node a unique ID which is used to store the state of this node
1882
1379
  id: storeId,
1883
- // Provide Network relevant configuration like the port
1884
- // Optional when operating only one device on a host, Default port is 5540
1885
1380
  network: {
1886
1381
  listeningAddressIpv4: this.ipv4address,
1887
1382
  listeningAddressIpv6: this.ipv6address,
1888
1383
  port,
1889
1384
  },
1890
- // Provide Commissioning relevant settings
1891
- // Optional for development/testing purposes
1892
1385
  commissioning: {
1893
1386
  passcode,
1894
1387
  discriminator,
1895
1388
  },
1896
- // Provide Node announcement settings
1897
- // Optional: If Ommitted some development defaults are used
1898
1389
  productDescription: {
1899
1390
  name: await storageContext.get('deviceName'),
1900
1391
  deviceType: DeviceTypeId(await storageContext.get('deviceType')),
1901
1392
  },
1902
- // Provide defaults for the BasicInformation cluster on the Root endpoint
1903
- // Optional: If Omitted some development defaults are used
1904
1393
  basicInformation: {
1905
1394
  vendorId: VendorId(await storageContext.get('vendorId')),
1906
1395
  vendorName: await storageContext.get('vendorName'),
@@ -1917,13 +1406,12 @@ export class Matterbridge extends EventEmitter {
1917
1406
  },
1918
1407
  });
1919
1408
  const sanitizeFabrics = (fabrics, resetSessions = false) => {
1920
- // New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
1921
1409
  const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
1922
1410
  this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
1923
1411
  if (this.bridgeMode === 'bridge') {
1924
1412
  this.matterbridgeFabricInformations = sanitizedFabrics;
1925
1413
  if (resetSessions)
1926
- this.matterbridgeSessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
1414
+ this.matterbridgeSessionInformations = undefined;
1927
1415
  this.matterbridgePaired = true;
1928
1416
  }
1929
1417
  if (this.bridgeMode === 'childbridge') {
@@ -1931,19 +1419,13 @@ export class Matterbridge extends EventEmitter {
1931
1419
  if (plugin) {
1932
1420
  plugin.fabricInformations = sanitizedFabrics;
1933
1421
  if (resetSessions)
1934
- plugin.sessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
1422
+ plugin.sessionInformations = undefined;
1935
1423
  plugin.paired = true;
1936
1424
  }
1937
1425
  }
1938
1426
  };
1939
- /**
1940
- * This event is triggered when the device is initially commissioned successfully.
1941
- * This means: It is added to the first fabric.
1942
- */
1943
1427
  serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
1944
- /** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
1945
1428
  serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
1946
- /** This event is triggered when the device went online. This means that it is discoverable in the network. */
1947
1429
  serverNode.lifecycle.online.on(async () => {
1948
1430
  this.log.notice(`Server node for ${storeId} is online`);
1949
1431
  if (!serverNode.lifecycle.isCommissioned) {
@@ -1982,6 +1464,23 @@ export class Matterbridge extends EventEmitter {
1982
1464
  }
1983
1465
  }
1984
1466
  }
1467
+ setTimeout(() => {
1468
+ if (this.bridgeMode === 'bridge') {
1469
+ this.matterbridgeQrPairingCode = undefined;
1470
+ this.matterbridgeManualPairingCode = undefined;
1471
+ }
1472
+ if (this.bridgeMode === 'childbridge') {
1473
+ const plugin = this.plugins.get(storeId);
1474
+ if (plugin) {
1475
+ plugin.qrPairingCode = undefined;
1476
+ plugin.manualPairingCode = undefined;
1477
+ this.frontend.wssSendRefreshRequired('plugins');
1478
+ }
1479
+ }
1480
+ this.frontend.wssSendRefreshRequired('settings');
1481
+ this.frontend.wssSendSnackbarMessage(`Advertising on server node for ${storeId} stopped. Restart to commission.`, 0);
1482
+ this.log.notice(`Advertising on server node for ${storeId} stopped. Restart to commission.`);
1483
+ }, 15 * 60 * 1000).unref();
1985
1484
  }
1986
1485
  else {
1987
1486
  this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
@@ -1991,7 +1490,6 @@ export class Matterbridge extends EventEmitter {
1991
1490
  this.frontend.wssSendRefreshRequired('settings');
1992
1491
  this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
1993
1492
  });
1994
- /** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
1995
1493
  serverNode.lifecycle.offline.on(() => {
1996
1494
  this.log.notice(`Server node for ${storeId} is offline`);
1997
1495
  if (this.bridgeMode === 'bridge') {
@@ -2015,10 +1513,6 @@ export class Matterbridge extends EventEmitter {
2015
1513
  this.frontend.wssSendRefreshRequired('settings');
2016
1514
  this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
2017
1515
  });
2018
- /**
2019
- * This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
2020
- * information is needed.
2021
- */
2022
1516
  serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
2023
1517
  let action = '';
2024
1518
  switch (fabricAction) {
@@ -2052,24 +1546,16 @@ export class Matterbridge extends EventEmitter {
2052
1546
  }
2053
1547
  }
2054
1548
  };
2055
- /**
2056
- * This event is triggered when an operative new session was opened by a Controller.
2057
- * It is not triggered for the initial commissioning process, just afterwards for real connections.
2058
- */
2059
1549
  serverNode.events.sessions.opened.on((session) => {
2060
1550
  this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
2061
1551
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2062
1552
  this.frontend.wssSendRefreshRequired('sessions');
2063
1553
  });
2064
- /**
2065
- * This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
2066
- */
2067
1554
  serverNode.events.sessions.closed.on((session) => {
2068
1555
  this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
2069
1556
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2070
1557
  this.frontend.wssSendRefreshRequired('sessions');
2071
1558
  });
2072
- /** This event is triggered when a subscription gets added or removed on an operative session. */
2073
1559
  serverNode.events.sessions.subscriptionsChanged.on((session) => {
2074
1560
  this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
2075
1561
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
@@ -2078,42 +1564,24 @@ export class Matterbridge extends EventEmitter {
2078
1564
  this.log.info(`Created server node for ${storeId}`);
2079
1565
  return serverNode;
2080
1566
  }
2081
- /**
2082
- * Starts the specified server node.
2083
- *
2084
- * @param {ServerNode} [matterServerNode] - The server node to start.
2085
- * @returns {Promise<void>} A promise that resolves when the server node has started.
2086
- */
2087
1567
  async startServerNode(matterServerNode) {
2088
1568
  if (!matterServerNode)
2089
1569
  return;
2090
1570
  this.log.notice(`Starting ${matterServerNode.id} server node`);
2091
1571
  await matterServerNode.start();
2092
1572
  }
2093
- /**
2094
- * Stops the specified server node.
2095
- *
2096
- * @param {ServerNode} matterServerNode - The server node to stop.
2097
- * @returns {Promise<void>} A promise that resolves when the server node has stopped.
2098
- */
2099
1573
  async stopServerNode(matterServerNode) {
2100
1574
  if (!matterServerNode)
2101
1575
  return;
2102
1576
  this.log.notice(`Closing ${matterServerNode.id} server node`);
2103
1577
  try {
2104
- await withTimeout(matterServerNode.close(), 30000); // 30 seconds timeout to allow slow devices to close gracefully
1578
+ await withTimeout(matterServerNode.close(), 30000);
2105
1579
  this.log.info(`Closed ${matterServerNode.id} server node`);
2106
1580
  }
2107
1581
  catch (error) {
2108
1582
  this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
2109
1583
  }
2110
1584
  }
2111
- /**
2112
- * Advertises the specified server node.
2113
- *
2114
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2115
- * @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.
2116
- */
2117
1585
  async advertiseServerNode(matterServerNode) {
2118
1586
  if (matterServerNode) {
2119
1587
  await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
@@ -2122,44 +1590,23 @@ export class Matterbridge extends EventEmitter {
2122
1590
  return { qrPairingCode, manualPairingCode };
2123
1591
  }
2124
1592
  }
2125
- /**
2126
- * Stop advertise the specified server node.
2127
- *
2128
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2129
- * @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
2130
- */
2131
1593
  async stopAdvertiseServerNode(matterServerNode) {
2132
1594
  if (matterServerNode && matterServerNode.lifecycle.isOnline) {
2133
1595
  await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
2134
1596
  this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
2135
1597
  }
2136
1598
  }
2137
- /**
2138
- * Creates an aggregator node with the specified storage context.
2139
- *
2140
- * @param {StorageContext} storageContext - The storage context for the aggregator node.
2141
- * @returns {Promise<EndpointNode<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
2142
- */
2143
1599
  async createAggregatorNode(storageContext) {
2144
1600
  this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
2145
1601
  const aggregatorNode = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
2146
1602
  return aggregatorNode;
2147
1603
  }
2148
- /**
2149
- * Adds a MatterbridgeEndpoint to the specified plugin.
2150
- *
2151
- * @param {string} pluginName - The name of the plugin.
2152
- * @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
2153
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
2154
- */
2155
1604
  async addBridgedEndpoint(pluginName, device) {
2156
- // Check if the plugin is registered
2157
1605
  const plugin = this.plugins.get(pluginName);
2158
1606
  if (!plugin) {
2159
1607
  this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
2160
1608
  return;
2161
1609
  }
2162
- // Register and add the device to the matterbridge aggregator node
2163
1610
  if (this.bridgeMode === 'bridge') {
2164
1611
  this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
2165
1612
  if (!this.aggregatorNode)
@@ -2182,28 +1629,17 @@ export class Matterbridge extends EventEmitter {
2182
1629
  plugin.registeredDevices++;
2183
1630
  if (plugin.addedDevices !== undefined)
2184
1631
  plugin.addedDevices++;
2185
- // Add the device to the DeviceManager
2186
1632
  this.devices.set(device);
2187
- // Subscribe to the reachable$Changed event
2188
1633
  await this.subscribeAttributeChanged(plugin, device);
2189
1634
  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}`);
2190
1635
  }
2191
- /**
2192
- * Removes a MatterbridgeEndpoint from the specified plugin.
2193
- *
2194
- * @param {string} pluginName - The name of the plugin.
2195
- * @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
2196
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
2197
- */
2198
1636
  async removeBridgedEndpoint(pluginName, device) {
2199
1637
  this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
2200
- // Check if the plugin is registered
2201
1638
  const plugin = this.plugins.get(pluginName);
2202
1639
  if (!plugin) {
2203
1640
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
2204
1641
  return;
2205
1642
  }
2206
- // Register and add the device to the matterbridge aggregator node
2207
1643
  if (this.bridgeMode === 'bridge') {
2208
1644
  if (!this.aggregatorNode) {
2209
1645
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
@@ -2218,7 +1654,6 @@ export class Matterbridge extends EventEmitter {
2218
1654
  }
2219
1655
  else if (this.bridgeMode === 'childbridge') {
2220
1656
  if (plugin.type === 'AccessoryPlatform') {
2221
- // Nothing to do here since the server node has no aggregator node but only the device itself
2222
1657
  }
2223
1658
  else if (plugin.type === 'DynamicPlatform') {
2224
1659
  if (!plugin.aggregatorNode) {
@@ -2233,16 +1668,8 @@ export class Matterbridge extends EventEmitter {
2233
1668
  if (plugin.addedDevices !== undefined)
2234
1669
  plugin.addedDevices--;
2235
1670
  }
2236
- // Remove the device from the DeviceManager
2237
1671
  this.devices.remove(device);
2238
1672
  }
2239
- /**
2240
- * Removes all bridged endpoints from the specified plugin.
2241
- *
2242
- * @param {string} pluginName - The name of the plugin.
2243
- * @param {number} [delay=0] - The delay in milliseconds between removing each bridged endpoint (default: 0).
2244
- * @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
2245
- */
2246
1673
  async removeAllBridgedEndpoints(pluginName, delay = 0) {
2247
1674
  this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}`);
2248
1675
  for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
@@ -2251,25 +1678,9 @@ export class Matterbridge extends EventEmitter {
2251
1678
  await new Promise((resolve) => setTimeout(resolve, delay));
2252
1679
  }
2253
1680
  }
2254
- /**
2255
- * Subscribes to the attribute change event for the given device and plugin.
2256
- * Specifically, it listens for changes in the 'reachable' attribute of the
2257
- * BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
2258
- *
2259
- * @param {RegisteredPlugin} plugin - The plugin associated with the device.
2260
- * @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
2261
- * @returns {Promise<void>} A promise that resolves when the subscription is set up.
2262
- */
2263
1681
  async subscribeAttributeChanged(plugin, device) {
2264
1682
  this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
2265
1683
  if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
2266
- /*
2267
- this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) subscribed to reachable$Changed`);
2268
- setTimeout(async () => {
2269
- this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) changed to reachable false`);
2270
- await plugin.serverNode?.setStateOf(BasicInformationServer, { reachable: false });
2271
- }, 60000).unref();
2272
- */
2273
1684
  plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
2274
1685
  this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
2275
1686
  this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, 'BasicInformationServer', 'reachable', reachable);
@@ -2282,12 +1693,6 @@ export class Matterbridge extends EventEmitter {
2282
1693
  });
2283
1694
  }
2284
1695
  }
2285
- /**
2286
- * Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
2287
- *
2288
- * @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
2289
- * @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
2290
- */
2291
1696
  sanitizeFabricInformations(fabricInfo) {
2292
1697
  return fabricInfo.map((info) => {
2293
1698
  return {
@@ -2301,12 +1706,6 @@ export class Matterbridge extends EventEmitter {
2301
1706
  };
2302
1707
  });
2303
1708
  }
2304
- /**
2305
- * Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
2306
- *
2307
- * @param {SessionInformation[]} sessionInfo - The array of session information objects.
2308
- * @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
2309
- */
2310
1709
  sanitizeSessionInformation(sessionInfo) {
2311
1710
  return sessionInfo
2312
1711
  .filter((session) => session.isPeerActive)
@@ -2334,20 +1733,7 @@ export class Matterbridge extends EventEmitter {
2334
1733
  };
2335
1734
  });
2336
1735
  }
2337
- /**
2338
- * Sets the reachability of the specified aggregator node bridged devices and trigger.
2339
- * @param {EndpointNode<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
2340
- * @param {boolean} reachable - A boolean indicating the reachability status to set.
2341
- */
2342
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2343
1736
  async setAggregatorReachability(aggregatorNode, reachable) {
2344
- /*
2345
- for (const child of aggregatorNode.parts) {
2346
- this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
2347
- await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
2348
- child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
2349
- }
2350
- */
2351
1737
  }
2352
1738
  getVendorIdName = (vendorId) => {
2353
1739
  if (!vendorId)
@@ -2387,37 +1773,14 @@ export class Matterbridge extends EventEmitter {
2387
1773
  }
2388
1774
  return vendorName;
2389
1775
  };
2390
- /**
2391
- * Spawns a child process with the given command and arguments.
2392
- * @param {string} command - The command to execute.
2393
- * @param {string[]} args - The arguments to pass to the command (default: []).
2394
- * @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
2395
- */
2396
1776
  async spawnCommand(command, args = []) {
2397
1777
  const { spawn } = await import('node:child_process');
2398
- /*
2399
- npm > npm.cmd on windows
2400
- cmd.exe ['dir'] on windows
2401
- await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
2402
- process.on('unhandledRejection', (reason, promise) => {
2403
- this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
2404
- });
2405
-
2406
- spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
2407
- spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
2408
- debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
2409
- debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
2410
- */
2411
1778
  const cmdLine = command + ' ' + args.join(' ');
2412
1779
  if (process.platform === 'win32' && command === 'npm') {
2413
- // Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
2414
1780
  const argstring = 'npm ' + args.join(' ');
2415
1781
  args.splice(0, args.length, '/c', argstring);
2416
1782
  command = 'cmd.exe';
2417
1783
  }
2418
- // Decide when using sudo on linux
2419
- // When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
2420
- // When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
2421
1784
  if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
2422
1785
  args.unshift(command);
2423
1786
  command = 'sudo';
@@ -2476,4 +1839,3 @@ export class Matterbridge extends EventEmitter {
2476
1839
  });
2477
1840
  }
2478
1841
  }
2479
- //# sourceMappingURL=matterbridge.js.map