matterbridge 3.0.0 → 3.0.1-dev-20250501-4f463f9

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 (153) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +22 -4
  3. package/dist/cli.js +2 -37
  4. package/dist/cluster/export.js +0 -2
  5. package/dist/defaultConfigSchema.js +0 -23
  6. package/dist/deviceManager.js +1 -94
  7. package/dist/frontend.js +19 -352
  8. package/dist/index.js +1 -28
  9. package/dist/logger/export.js +0 -1
  10. package/dist/matter/behaviors.js +0 -2
  11. package/dist/matter/clusters.js +0 -2
  12. package/dist/matter/devices.js +0 -2
  13. package/dist/matter/endpoints.js +0 -2
  14. package/dist/matter/export.js +0 -2
  15. package/dist/matter/types.js +0 -2
  16. package/dist/matterbridge.js +46 -747
  17. package/dist/matterbridgeAccessoryPlatform.js +0 -34
  18. package/dist/matterbridgeBehaviors.js +1 -33
  19. package/dist/matterbridgeDeviceTypes.js +12 -431
  20. package/dist/matterbridgeDynamicPlatform.js +0 -34
  21. package/dist/matterbridgeEndpoint.js +11 -794
  22. package/dist/matterbridgeEndpointHelpers.js +9 -142
  23. package/dist/matterbridgePlatform.js +7 -225
  24. package/dist/matterbridgeTypes.js +0 -24
  25. package/dist/pluginManager.js +3 -262
  26. package/dist/shelly.js +6 -146
  27. package/dist/storage/export.js +0 -1
  28. package/dist/update.js +0 -52
  29. package/dist/utils/colorUtils.js +2 -205
  30. package/dist/utils/copyDirectory.js +1 -37
  31. package/dist/utils/createZip.js +2 -42
  32. package/dist/utils/deepCopy.js +0 -40
  33. package/dist/utils/deepEqual.js +1 -65
  34. package/dist/utils/export.js +0 -1
  35. package/dist/utils/isvalid.js +0 -86
  36. package/dist/utils/network.js +5 -76
  37. package/dist/utils/parameter.js +0 -53
  38. package/dist/utils/wait.js +5 -48
  39. package/frontend/build/asset-manifest.json +3 -3
  40. package/frontend/build/index.html +1 -1
  41. package/frontend/build/static/js/{main.1d983660.js → main.356788d7.js} +3 -3
  42. package/frontend/build/static/js/{main.1d983660.js.map → main.356788d7.js.map} +1 -1
  43. package/npm-shrinkwrap.json +2 -2
  44. package/package.json +1 -2
  45. package/dist/cli.d.ts +0 -29
  46. package/dist/cli.d.ts.map +0 -1
  47. package/dist/cli.js.map +0 -1
  48. package/dist/cluster/export.d.ts +0 -2
  49. package/dist/cluster/export.d.ts.map +0 -1
  50. package/dist/cluster/export.js.map +0 -1
  51. package/dist/defaultConfigSchema.d.ts +0 -27
  52. package/dist/defaultConfigSchema.d.ts.map +0 -1
  53. package/dist/defaultConfigSchema.js.map +0 -1
  54. package/dist/deviceManager.d.ts +0 -114
  55. package/dist/deviceManager.d.ts.map +0 -1
  56. package/dist/deviceManager.js.map +0 -1
  57. package/dist/frontend.d.ts +0 -222
  58. package/dist/frontend.d.ts.map +0 -1
  59. package/dist/frontend.js.map +0 -1
  60. package/dist/index.d.ts +0 -35
  61. package/dist/index.d.ts.map +0 -1
  62. package/dist/index.js.map +0 -1
  63. package/dist/logger/export.d.ts +0 -2
  64. package/dist/logger/export.d.ts.map +0 -1
  65. package/dist/logger/export.js.map +0 -1
  66. package/dist/matter/behaviors.d.ts +0 -2
  67. package/dist/matter/behaviors.d.ts.map +0 -1
  68. package/dist/matter/behaviors.js.map +0 -1
  69. package/dist/matter/clusters.d.ts +0 -2
  70. package/dist/matter/clusters.d.ts.map +0 -1
  71. package/dist/matter/clusters.js.map +0 -1
  72. package/dist/matter/devices.d.ts +0 -2
  73. package/dist/matter/devices.d.ts.map +0 -1
  74. package/dist/matter/devices.js.map +0 -1
  75. package/dist/matter/endpoints.d.ts +0 -2
  76. package/dist/matter/endpoints.d.ts.map +0 -1
  77. package/dist/matter/endpoints.js.map +0 -1
  78. package/dist/matter/export.d.ts +0 -5
  79. package/dist/matter/export.d.ts.map +0 -1
  80. package/dist/matter/export.js.map +0 -1
  81. package/dist/matter/types.d.ts +0 -3
  82. package/dist/matter/types.d.ts.map +0 -1
  83. package/dist/matter/types.js.map +0 -1
  84. package/dist/matterbridge.d.ts +0 -431
  85. package/dist/matterbridge.d.ts.map +0 -1
  86. package/dist/matterbridge.js.map +0 -1
  87. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -40
  88. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  89. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  90. package/dist/matterbridgeBehaviors.d.ts +0 -1514
  91. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  92. package/dist/matterbridgeBehaviors.js.map +0 -1
  93. package/dist/matterbridgeDeviceTypes.d.ts +0 -494
  94. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  95. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  96. package/dist/matterbridgeDynamicPlatform.d.ts +0 -40
  97. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  98. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  99. package/dist/matterbridgeEndpoint.d.ts +0 -943
  100. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  101. package/dist/matterbridgeEndpoint.js.map +0 -1
  102. package/dist/matterbridgeEndpointHelpers.d.ts +0 -2706
  103. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  104. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  105. package/dist/matterbridgePlatform.d.ts +0 -294
  106. package/dist/matterbridgePlatform.d.ts.map +0 -1
  107. package/dist/matterbridgePlatform.js.map +0 -1
  108. package/dist/matterbridgeTypes.d.ts +0 -187
  109. package/dist/matterbridgeTypes.d.ts.map +0 -1
  110. package/dist/matterbridgeTypes.js.map +0 -1
  111. package/dist/pluginManager.d.ts +0 -271
  112. package/dist/pluginManager.d.ts.map +0 -1
  113. package/dist/pluginManager.js.map +0 -1
  114. package/dist/shelly.d.ts +0 -92
  115. package/dist/shelly.d.ts.map +0 -1
  116. package/dist/shelly.js.map +0 -1
  117. package/dist/storage/export.d.ts +0 -2
  118. package/dist/storage/export.d.ts.map +0 -1
  119. package/dist/storage/export.js.map +0 -1
  120. package/dist/update.d.ts +0 -32
  121. package/dist/update.d.ts.map +0 -1
  122. package/dist/update.js.map +0 -1
  123. package/dist/utils/colorUtils.d.ts +0 -61
  124. package/dist/utils/colorUtils.d.ts.map +0 -1
  125. package/dist/utils/colorUtils.js.map +0 -1
  126. package/dist/utils/copyDirectory.d.ts +0 -32
  127. package/dist/utils/copyDirectory.d.ts.map +0 -1
  128. package/dist/utils/copyDirectory.js.map +0 -1
  129. package/dist/utils/createZip.d.ts +0 -38
  130. package/dist/utils/createZip.d.ts.map +0 -1
  131. package/dist/utils/createZip.js.map +0 -1
  132. package/dist/utils/deepCopy.d.ts +0 -31
  133. package/dist/utils/deepCopy.d.ts.map +0 -1
  134. package/dist/utils/deepCopy.js.map +0 -1
  135. package/dist/utils/deepEqual.d.ts +0 -53
  136. package/dist/utils/deepEqual.d.ts.map +0 -1
  137. package/dist/utils/deepEqual.js.map +0 -1
  138. package/dist/utils/export.d.ts +0 -10
  139. package/dist/utils/export.d.ts.map +0 -1
  140. package/dist/utils/export.js.map +0 -1
  141. package/dist/utils/isvalid.d.ts +0 -87
  142. package/dist/utils/isvalid.d.ts.map +0 -1
  143. package/dist/utils/isvalid.js.map +0 -1
  144. package/dist/utils/network.d.ts +0 -69
  145. package/dist/utils/network.d.ts.map +0 -1
  146. package/dist/utils/network.js.map +0 -1
  147. package/dist/utils/parameter.d.ts +0 -58
  148. package/dist/utils/parameter.d.ts.map +0 -1
  149. package/dist/utils/parameter.js.map +0 -1
  150. package/dist/utils/wait.d.ts +0 -43
  151. package/dist/utils/wait.d.ts.map +0 -1
  152. package/dist/utils/wait.js.map +0 -1
  153. /package/frontend/build/static/js/{main.1d983660.js.LICENSE.txt → main.356788d7.js.LICENSE.txt} +0 -0
@@ -1,36 +1,10 @@
1
- /**
2
- * This file contains the class Matterbridge.
3
- *
4
- * @file matterbridge.ts
5
- * @author Luca Liguori
6
- * @date 2023-12-29
7
- * @version 1.5.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
5
  import { inspect } from 'node:util';
29
- // AnsiLogger module
30
6
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from './logger/export.js';
31
- // NodeStorage module
32
7
  import { NodeStorageManager } from './storage/export.js';
33
- // Matterbridge
34
8
  import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout, waiter } from './utils/export.js';
35
9
  import { logInterfaces, getGlobalNodeModules } from './utils/network.js';
36
10
  import { PluginManager } from './pluginManager.js';
@@ -38,19 +12,14 @@ import { DeviceManager } from './deviceManager.js';
38
12
  import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
39
13
  import { bridge } from './matterbridgeDeviceTypes.js';
40
14
  import { Frontend } from './frontend.js';
41
- // @matter
42
15
  import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode } from '@matter/main';
43
16
  import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
44
17
  import { AggregatorEndpoint } from '@matter/main/endpoints';
45
18
  import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
46
19
  import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
47
- // Default colors
48
20
  const plg = '\u001B[38;5;33m';
49
21
  const dev = '\u001B[38;5;79m';
50
22
  const typ = '\u001B[38;5;207m';
51
- /**
52
- * Represents the Matterbridge application.
53
- */
54
23
  export class Matterbridge extends EventEmitter {
55
24
  systemInformation = {
56
25
  interfaceName: '',
@@ -97,7 +66,7 @@ export class Matterbridge extends EventEmitter {
97
66
  shellySysUpdate: false,
98
67
  shellyMainUpdate: false,
99
68
  profile: getParameter('profile'),
100
- loggerLevel: "info" /* LogLevel.INFO */,
69
+ loggerLevel: "info",
101
70
  fileLogger: false,
102
71
  matterLoggerLevel: MatterLogLevel.INFO,
103
72
  matterFileLogger: false,
@@ -136,11 +105,9 @@ export class Matterbridge extends EventEmitter {
136
105
  plugins;
137
106
  devices;
138
107
  frontend = new Frontend(this);
139
- // Matterbridge storage
140
108
  nodeStorage;
141
109
  nodeContext;
142
110
  nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
143
- // Cleanup
144
111
  hasCleanupStarted = false;
145
112
  initialized = false;
146
113
  execRunningCount = 0;
@@ -153,21 +120,18 @@ export class Matterbridge extends EventEmitter {
153
120
  sigtermHandler;
154
121
  exceptionHandler;
155
122
  rejectionHandler;
156
- // Matter environment
157
123
  environment = Environment.default;
158
- // Matter storage
159
124
  matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
160
125
  matterStorageService;
161
126
  matterStorageManager;
162
127
  matterbridgeContext;
163
128
  controllerContext;
164
- // Matter parameters
165
- mdnsInterface; // matter server node mdnsInterface: e.g. 'eth0' or 'wlan0' or 'WiFi'
166
- ipv4address; // matter server node listeningAddressIpv4
167
- ipv6address; // matter server node listeningAddressIpv6
168
- port; // first server node port
169
- passcode; // first server node passcode
170
- discriminator; // first server node discriminator
129
+ mdnsInterface;
130
+ ipv4address;
131
+ ipv6address;
132
+ port;
133
+ passcode;
134
+ discriminator;
171
135
  serverNode;
172
136
  aggregatorNode;
173
137
  aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
@@ -175,50 +139,21 @@ export class Matterbridge extends EventEmitter {
175
139
  aggregatorProductId = getIntParameter('productId') ?? 0x8000;
176
140
  aggregatorProductName = getParameter('productName') ?? 'Matterbridge aggregator';
177
141
  static instance;
178
- // We load asyncronously so is private
179
142
  constructor() {
180
143
  super();
181
144
  }
182
- /**
183
- * Emits an event of the specified type with the provided arguments.
184
- *
185
- * @template K - The type of the event.
186
- * @param {K} eventName - The name of the event to emit.
187
- * @param {...MatterbridgeEvent[K]} args - The arguments to pass to the event listeners.
188
- * @returns {boolean} - Returns true if the event had listeners, false otherwise.
189
- */
190
145
  emit(eventName, ...args) {
191
146
  return super.emit(eventName, ...args);
192
147
  }
193
- /**
194
- * Registers an event listener for the specified event type.
195
- *
196
- * @template K - The type of the event.
197
- * @param {K} eventName - The name of the event to listen for.
198
- * @param {(...args: MatterbridgeEvent[K]) => void} listener - The callback function to invoke when the event is emitted.
199
- * @returns {this} - Returns the instance of the Matterbridge class.
200
- */
201
148
  on(eventName, listener) {
202
149
  return super.on(eventName, listener);
203
150
  }
204
- /**
205
- * Retrieves the list of Matterbridge devices.
206
- * @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
207
- */
208
151
  getDevices() {
209
152
  return this.devices.array();
210
153
  }
211
- /**
212
- * Retrieves the list of registered plugins.
213
- * @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
214
- */
215
154
  getPlugins() {
216
155
  return this.plugins.array();
217
156
  }
218
- /**
219
- * Set the logger logLevel for the Matterbridge classes.
220
- * @param {LogLevel} logLevel The logger logLevel to set.
221
- */
222
157
  async setLogLevel(logLevel) {
223
158
  if (this.log)
224
159
  this.log.logLevel = logLevel;
@@ -232,31 +167,19 @@ export class Matterbridge extends EventEmitter {
232
167
  for (const plugin of this.plugins) {
233
168
  if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
234
169
  continue;
235
- plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel;
236
- await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel);
237
- }
238
- // Set the global logger callback for the WebSocketServer to the common minimum logLevel
239
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
240
- if (this.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
241
- callbackLogLevel = "info" /* LogLevel.INFO */;
242
- if (this.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
243
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
170
+ plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
171
+ await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
172
+ }
173
+ let callbackLogLevel = "notice";
174
+ if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
175
+ callbackLogLevel = "info";
176
+ if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
177
+ callbackLogLevel = "debug";
244
178
  AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
245
179
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
246
180
  }
247
- /** ***********************************************************************************************************************************/
248
- /** loadInstance() and cleanup() methods */
249
- /** ***********************************************************************************************************************************/
250
- /**
251
- * Loads an instance of the Matterbridge class.
252
- * If an instance already exists, return that instance.
253
- *
254
- * @param initialize - Whether to initialize the Matterbridge instance after loading.
255
- * @returns The loaded Matterbridge instance.
256
- */
257
181
  static async loadInstance(initialize = false) {
258
182
  if (!Matterbridge.instance) {
259
- // eslint-disable-next-line no-console
260
183
  if (hasParameter('debug'))
261
184
  console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
262
185
  Matterbridge.instance = new Matterbridge();
@@ -265,14 +188,8 @@ export class Matterbridge extends EventEmitter {
265
188
  }
266
189
  return Matterbridge.instance;
267
190
  }
268
- /**
269
- * Call cleanup().
270
- * @deprecated This method is deprecated and is only used for jest tests.
271
- *
272
- */
273
191
  async destroyInstance() {
274
192
  this.log.info(`Destroy instance...`);
275
- // Save server nodes to close
276
193
  const servers = [];
277
194
  if (this.bridgeMode === 'bridge') {
278
195
  if (this.serverNode)
@@ -284,81 +201,55 @@ export class Matterbridge extends EventEmitter {
284
201
  servers.push(plugin.serverNode);
285
202
  }
286
203
  }
287
- // Cleanup
288
204
  await this.cleanup('destroying instance...', false);
289
- // Close servers mdns service
290
205
  this.log.info(`Dispose ${servers.length} MdnsService...`);
291
206
  for (const server of servers) {
292
207
  await server.env.get(MdnsService)[Symbol.asyncDispose]();
293
208
  this.log.info(`Closed ${server.id} MdnsService`);
294
209
  }
295
- // Wait for the cleanup to finish
296
210
  await new Promise((resolve) => {
297
211
  setTimeout(resolve, 1000);
298
212
  });
299
213
  }
300
- /**
301
- * Initializes the Matterbridge application.
302
- *
303
- * @remarks
304
- * This method performs the necessary setup and initialization steps for the Matterbridge application.
305
- * It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
306
- * node version, registers signal handlers, initializes storage, and parses the command line.
307
- *
308
- * @returns A Promise that resolves when the initialization is complete.
309
- */
310
214
  async initialize() {
311
- // Set the restart mode
312
215
  if (hasParameter('service'))
313
216
  this.restartMode = 'service';
314
217
  if (hasParameter('docker'))
315
218
  this.restartMode = 'docker';
316
- // Set the matterbridge directory
317
219
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
318
220
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
319
- // Setup the matter environment
320
221
  this.environment.vars.set('log.level', MatterLogLevel.INFO);
321
222
  this.environment.vars.set('log.format', MatterLogFormat.ANSI);
322
223
  this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
323
224
  this.environment.vars.set('runtime.signals', false);
324
225
  this.environment.vars.set('runtime.exitcode', false);
325
- // Create the matterbridge logger
326
- this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
327
- // Register process handlers
226
+ this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
328
227
  this.registerProcessHandlers();
329
- // Initialize nodeStorage and nodeContext
330
228
  try {
331
229
  this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
332
230
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
333
231
  this.log.debug('Creating node storage context for matterbridge');
334
232
  this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
335
- // TODO: Remove this code when node-persist-manager is updated
336
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
337
233
  const keys = (await this.nodeStorage?.storage.keys());
338
234
  for (const key of keys) {
339
235
  this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
340
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
341
236
  await this.nodeStorage?.storage.get(key);
342
237
  }
343
238
  const storages = await this.nodeStorage.getStorageNames();
344
239
  for (const storage of storages) {
345
240
  this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
346
241
  const nodeContext = await this.nodeStorage?.createStorage(storage);
347
- // TODO: Remove this code when node-persist-manager is updated
348
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
349
242
  const keys = (await nodeContext?.storage.keys());
350
243
  keys.forEach(async (key) => {
351
244
  this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
352
245
  await nodeContext?.get(key);
353
246
  });
354
247
  }
355
- // Creating a backup of the node storage since it is not corrupted
356
248
  this.log.debug('Creating node storage backup...');
357
249
  await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
358
250
  this.log.debug('Created node storage backup');
359
251
  }
360
252
  catch (error) {
361
- // Restoring the backup of the node storage since it is corrupted
362
253
  this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
363
254
  if (hasParameter('norestore')) {
364
255
  this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
@@ -373,46 +264,41 @@ export class Matterbridge extends EventEmitter {
373
264
  this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
374
265
  throw new Error('Fatal error creating node storage manager and context for matterbridge');
375
266
  }
376
- // Set the first port to use for the commissioning server (will be incremented in childbridge mode)
377
267
  this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
378
- // Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
379
268
  this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
380
- // Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
381
269
  this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
382
270
  this.log.debug(`Initializing server node for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
383
- // Set matterbridge logger level (context: matterbridgeLogLevel)
384
271
  if (hasParameter('logger')) {
385
272
  const level = getParameter('logger');
386
273
  if (level === 'debug') {
387
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
274
+ this.log.logLevel = "debug";
388
275
  }
389
276
  else if (level === 'info') {
390
- this.log.logLevel = "info" /* LogLevel.INFO */;
277
+ this.log.logLevel = "info";
391
278
  }
392
279
  else if (level === 'notice') {
393
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
280
+ this.log.logLevel = "notice";
394
281
  }
395
282
  else if (level === 'warn') {
396
- this.log.logLevel = "warn" /* LogLevel.WARN */;
283
+ this.log.logLevel = "warn";
397
284
  }
398
285
  else if (level === 'error') {
399
- this.log.logLevel = "error" /* LogLevel.ERROR */;
286
+ this.log.logLevel = "error";
400
287
  }
401
288
  else if (level === 'fatal') {
402
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
289
+ this.log.logLevel = "fatal";
403
290
  }
404
291
  else {
405
292
  this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
406
- this.log.logLevel = "info" /* LogLevel.INFO */;
293
+ this.log.logLevel = "info";
407
294
  }
408
295
  }
409
296
  else {
410
- this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" /* LogLevel.NOTICE */ : "info" /* LogLevel.INFO */);
297
+ this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
411
298
  }
412
299
  this.frontend.logLevel = this.log.logLevel;
413
300
  MatterbridgeEndpoint.logLevel = this.log.logLevel;
414
301
  this.matterbridgeInformation.loggerLevel = this.log.logLevel;
415
- // Create the file logger for matterbridge (context: matterbridgeFileLog)
416
302
  if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
417
303
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
418
304
  this.matterbridgeInformation.fileLogger = true;
@@ -421,7 +307,6 @@ export class Matterbridge extends EventEmitter {
421
307
  this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
422
308
  if (this.profile !== undefined)
423
309
  this.log.debug(`Matterbridge profile: ${this.profile}.`);
424
- // Set matter.js logger level, format and logger (context: matterLogLevel)
425
310
  if (hasParameter('matterlogger')) {
426
311
  const level = getParameter('matterlogger');
427
312
  if (level === 'debug') {
@@ -453,7 +338,6 @@ export class Matterbridge extends EventEmitter {
453
338
  Logger.format = MatterLogFormat.ANSI;
454
339
  Logger.setLogger('default', this.createMatterLogger());
455
340
  this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
456
- // Create the file logger for matter.js (context: matterFileLog)
457
341
  if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
458
342
  this.matterbridgeInformation.matterFileLogger = true;
459
343
  Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
@@ -462,7 +346,6 @@ export class Matterbridge extends EventEmitter {
462
346
  });
463
347
  }
464
348
  this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
465
- // Log network interfaces
466
349
  const networkInterfaces = os.networkInterfaces();
467
350
  const availableAddresses = Object.entries(networkInterfaces);
468
351
  const availableInterfaces = Object.keys(networkInterfaces);
@@ -474,7 +357,6 @@ export class Matterbridge extends EventEmitter {
474
357
  });
475
358
  }
476
359
  }
477
- // Set the interface to use for matter server node mdnsInterface
478
360
  if (hasParameter('mdnsinterface')) {
479
361
  this.mdnsInterface = getParameter('mdnsinterface');
480
362
  }
@@ -483,7 +365,6 @@ export class Matterbridge extends EventEmitter {
483
365
  if (this.mdnsInterface === '')
484
366
  this.mdnsInterface = undefined;
485
367
  }
486
- // Validate mdnsInterface
487
368
  if (this.mdnsInterface) {
488
369
  if (!availableInterfaces.includes(this.mdnsInterface)) {
489
370
  this.log.error(`Invalid mdnsInterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
@@ -495,7 +376,6 @@ export class Matterbridge extends EventEmitter {
495
376
  }
496
377
  if (this.mdnsInterface)
497
378
  this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
498
- // Set the listeningAddressIpv4 for the matter commissioning server
499
379
  if (hasParameter('ipv4address')) {
500
380
  this.ipv4address = getParameter('ipv4address');
501
381
  }
@@ -504,7 +384,6 @@ export class Matterbridge extends EventEmitter {
504
384
  if (this.ipv4address === '')
505
385
  this.ipv4address = undefined;
506
386
  }
507
- // Validate ipv4address
508
387
  if (this.ipv4address) {
509
388
  let isValid = false;
510
389
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -519,7 +398,6 @@ export class Matterbridge extends EventEmitter {
519
398
  this.ipv4address = undefined;
520
399
  }
521
400
  }
522
- // Set the listeningAddressIpv6 for the matter commissioning server
523
401
  if (hasParameter('ipv6address')) {
524
402
  this.ipv6address = getParameter('ipv6address');
525
403
  }
@@ -528,7 +406,6 @@ export class Matterbridge extends EventEmitter {
528
406
  if (this.ipv6address === '')
529
407
  this.ipv6address = undefined;
530
408
  }
531
- // Validate ipv6address
532
409
  if (this.ipv6address) {
533
410
  let isValid = false;
534
411
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -548,19 +425,14 @@ export class Matterbridge extends EventEmitter {
548
425
  this.ipv6address = undefined;
549
426
  }
550
427
  }
551
- // Initialize PluginManager
552
428
  this.plugins = new PluginManager(this);
553
429
  await this.plugins.loadFromStorage();
554
430
  this.plugins.logLevel = this.log.logLevel;
555
- // Initialize DeviceManager
556
431
  this.devices = new DeviceManager(this, this.nodeContext);
557
432
  this.devices.logLevel = this.log.logLevel;
558
- // Get the plugins from node storage and create the plugins node storage contexts
559
433
  for (const plugin of this.plugins) {
560
434
  const packageJson = await this.plugins.parse(plugin);
561
435
  if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
562
- // Try to reinstall the plugin from npm (for Docker pull and external plugins)
563
- // We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
564
436
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
565
437
  try {
566
438
  await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
@@ -582,7 +454,6 @@ export class Matterbridge extends EventEmitter {
582
454
  await plugin.nodeContext.set('description', plugin.description);
583
455
  await plugin.nodeContext.set('author', plugin.author);
584
456
  }
585
- // Log system info and create .matterbridge directory
586
457
  await this.logNodeAndSystemInfo();
587
458
  this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
588
459
  `${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
@@ -590,7 +461,6 @@ export class Matterbridge extends EventEmitter {
590
461
  `${hasParameter('controller') ? 'mode controller ' : ''}` +
591
462
  `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
592
463
  `running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
593
- // Check node version and throw error
594
464
  const minNodeVersion = 18;
595
465
  const nodeVersion = process.versions.node;
596
466
  const versionMajor = parseInt(nodeVersion.split('.')[0]);
@@ -598,15 +468,9 @@ export class Matterbridge extends EventEmitter {
598
468
  this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
599
469
  throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
600
470
  }
601
- // Parse command line
602
471
  await this.parseCommandLine();
603
472
  this.initialized = true;
604
473
  }
605
- /**
606
- * Parses the command line arguments and performs the corresponding actions.
607
- * @private
608
- * @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
609
- */
610
474
  async parseCommandLine() {
611
475
  if (hasParameter('help')) {
612
476
  this.log.info(`\nUsage: matterbridge [options]\n
@@ -725,7 +589,6 @@ export class Matterbridge extends EventEmitter {
725
589
  this.shutdown = true;
726
590
  return;
727
591
  }
728
- // Start the matter storage and create the matterbridge context
729
592
  try {
730
593
  await this.startMatterStorage();
731
594
  }
@@ -733,14 +596,12 @@ export class Matterbridge extends EventEmitter {
733
596
  this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
734
597
  throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
735
598
  }
736
- // Clear the matterbridge context if the reset parameter is set
737
599
  if (hasParameter('reset') && getParameter('reset') === undefined) {
738
600
  this.initialized = true;
739
601
  await this.shutdownProcessAndReset();
740
602
  this.shutdown = true;
741
603
  return;
742
604
  }
743
- // Clear matterbridge plugin context if the reset parameter is set
744
605
  if (hasParameter('reset') && getParameter('reset') !== undefined) {
745
606
  this.log.debug(`Reset plugin ${getParameter('reset')}`);
746
607
  const plugin = this.plugins.get(getParameter('reset'));
@@ -765,37 +626,30 @@ export class Matterbridge extends EventEmitter {
765
626
  this.shutdown = true;
766
627
  return;
767
628
  }
768
- // Initialize frontend
769
629
  if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
770
630
  await this.frontend.start(getIntParameter('frontend'));
771
- // Check in 30 seconds the latest versions
772
631
  this.checkUpdateTimeout = setTimeout(async () => {
773
632
  const { checkUpdates } = await import('./update.js');
774
633
  checkUpdates(this);
775
634
  }, 30 * 1000).unref();
776
- // Check each 24 hours the latest versions
777
635
  this.checkUpdateInterval = setInterval(async () => {
778
636
  const { checkUpdates } = await import('./update.js');
779
637
  checkUpdates(this);
780
638
  }, 12 * 60 * 60 * 1000).unref();
781
- // Start the matterbridge in mode test
782
639
  if (hasParameter('test')) {
783
640
  this.bridgeMode = 'bridge';
784
641
  MatterbridgeEndpoint.bridgeMode = 'bridge';
785
642
  return;
786
643
  }
787
- // Start the matterbridge in mode controller
788
644
  if (hasParameter('controller')) {
789
645
  this.bridgeMode = 'controller';
790
646
  await this.startController();
791
647
  return;
792
648
  }
793
- // Check if the bridge mode is set and start matterbridge in bridge mode if not set
794
649
  if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
795
650
  this.log.info('Setting default matterbridge start mode to bridge');
796
651
  await this.nodeContext?.set('bridgeMode', 'bridge');
797
652
  }
798
- // Start matterbridge in bridge mode
799
653
  if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
800
654
  this.bridgeMode = 'bridge';
801
655
  MatterbridgeEndpoint.bridgeMode = 'bridge';
@@ -803,7 +657,6 @@ export class Matterbridge extends EventEmitter {
803
657
  await this.startBridge();
804
658
  return;
805
659
  }
806
- // Start matterbridge in childbridge mode
807
660
  if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
808
661
  this.bridgeMode = 'childbridge';
809
662
  MatterbridgeEndpoint.bridgeMode = 'childbridge';
@@ -812,20 +665,10 @@ export class Matterbridge extends EventEmitter {
812
665
  return;
813
666
  }
814
667
  }
815
- /**
816
- * Asynchronously loads and starts the registered plugins.
817
- *
818
- * This method is responsible for initializing and staarting all enabled plugins.
819
- * It ensures that each plugin is properly loaded and started before the bridge starts.
820
- *
821
- * @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
822
- */
823
668
  async startPlugins() {
824
- // Check, load and start the plugins
825
669
  for (const plugin of this.plugins) {
826
670
  plugin.configJson = await this.plugins.loadConfig(plugin);
827
671
  plugin.schemaJson = await this.plugins.loadSchema(plugin);
828
- // Check if the plugin is available
829
672
  if (!(await this.plugins.resolve(plugin.path))) {
830
673
  this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
831
674
  plugin.enabled = false;
@@ -845,14 +688,10 @@ export class Matterbridge extends EventEmitter {
845
688
  plugin.addedDevices = undefined;
846
689
  plugin.qrPairingCode = undefined;
847
690
  plugin.manualPairingCode = undefined;
848
- this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
691
+ this.plugins.load(plugin, true, 'Matterbridge is starting');
849
692
  }
850
693
  this.frontend.wssSendRefreshRequired('plugins');
851
694
  }
852
- /**
853
- * Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
854
- * When either of these signals are received, the cleanup method is called with an appropriate message.
855
- */
856
695
  registerProcessHandlers() {
857
696
  this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
858
697
  process.removeAllListeners('uncaughtException');
@@ -879,9 +718,6 @@ export class Matterbridge extends EventEmitter {
879
718
  };
880
719
  process.on('SIGTERM', this.sigtermHandler);
881
720
  }
882
- /**
883
- * Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
884
- */
885
721
  deregisterProcesslHandlers() {
886
722
  this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
887
723
  if (this.exceptionHandler)
@@ -898,17 +734,12 @@ export class Matterbridge extends EventEmitter {
898
734
  process.off('SIGTERM', this.sigtermHandler);
899
735
  this.sigtermHandler = undefined;
900
736
  }
901
- /**
902
- * Logs the node and system information.
903
- */
904
737
  async logNodeAndSystemInfo() {
905
- // IP address information
906
738
  const networkInterfaces = os.networkInterfaces();
907
739
  this.systemInformation.interfaceName = '';
908
740
  this.systemInformation.ipv4Address = '';
909
741
  this.systemInformation.ipv6Address = '';
910
742
  for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
911
- // this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
912
743
  if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
913
744
  continue;
914
745
  if (!interfaceDetails) {
@@ -934,22 +765,19 @@ export class Matterbridge extends EventEmitter {
934
765
  break;
935
766
  }
936
767
  }
937
- // Node information
938
768
  this.systemInformation.nodeVersion = process.versions.node;
939
769
  const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
940
770
  const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
941
771
  const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
942
- // Host system information
943
772
  this.systemInformation.hostname = os.hostname();
944
773
  this.systemInformation.user = os.userInfo().username;
945
- this.systemInformation.osType = os.type(); // "Windows_NT", "Darwin", etc.
946
- this.systemInformation.osRelease = os.release(); // Kernel version
947
- this.systemInformation.osPlatform = os.platform(); // "win32", "linux", "darwin", etc.
948
- this.systemInformation.osArch = os.arch(); // "x64", "arm", etc.
949
- this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
950
- this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
951
- this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours'; // Convert to hours
952
- // Log the system information
774
+ this.systemInformation.osType = os.type();
775
+ this.systemInformation.osRelease = os.release();
776
+ this.systemInformation.osPlatform = os.platform();
777
+ this.systemInformation.osArch = os.arch();
778
+ this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
779
+ this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
780
+ this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
953
781
  this.log.debug('Host System Information:');
954
782
  this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
955
783
  this.log.debug(`- User: ${this.systemInformation.user}`);
@@ -965,20 +793,16 @@ export class Matterbridge extends EventEmitter {
965
793
  this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
966
794
  this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
967
795
  this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
968
- // Home directory
969
796
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
970
797
  this.matterbridgeInformation.homeDirectory = this.homeDirectory;
971
798
  this.log.debug(`Home Directory: ${this.homeDirectory}`);
972
- // Package root directory
973
799
  const { fileURLToPath } = await import('node:url');
974
800
  const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
975
801
  this.rootDirectory = path.resolve(currentFileDirectory, '../');
976
802
  this.matterbridgeInformation.rootDirectory = this.rootDirectory;
977
803
  this.log.debug(`Root Directory: ${this.rootDirectory}`);
978
- // Global node_modules directory
979
804
  if (this.nodeContext)
980
805
  this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
981
- // First run of Matterbridge so the node storage is empty
982
806
  if (this.globalModulesDirectory === '') {
983
807
  try {
984
808
  this.execRunningCount++;
@@ -994,20 +818,6 @@ export class Matterbridge extends EventEmitter {
994
818
  }
995
819
  else
996
820
  this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
997
- /* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
998
- else {
999
- this.getGlobalNodeModules()
1000
- .then(async (globalModulesDirectory) => {
1001
- this.globalModulesDirectory = globalModulesDirectory;
1002
- this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
1003
- this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
1004
- await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
1005
- })
1006
- .catch((error) => {
1007
- this.log.error(`Error getting global node_modules directory: ${error}`);
1008
- });
1009
- }*/
1010
- // Create the data directory .matterbridge in the home directory
1011
821
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
1012
822
  this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
1013
823
  try {
@@ -1031,7 +841,6 @@ export class Matterbridge extends EventEmitter {
1031
841
  }
1032
842
  }
1033
843
  this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
1034
- // Create the plugin directory Matterbridge in the home directory
1035
844
  this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
1036
845
  this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
1037
846
  try {
@@ -1055,7 +864,6 @@ export class Matterbridge extends EventEmitter {
1055
864
  }
1056
865
  }
1057
866
  this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
1058
- // Create the matter cert directory in the home directory
1059
867
  this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
1060
868
  this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
1061
869
  try {
@@ -1079,68 +887,50 @@ export class Matterbridge extends EventEmitter {
1079
887
  }
1080
888
  }
1081
889
  this.log.debug(`Matterbridge Matter Cert Directory: ${this.matterbridgeCertDirectory}`);
1082
- // Matterbridge version
1083
890
  const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
1084
891
  this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
1085
892
  this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
1086
893
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
1087
- // Matterbridge latest version
1088
894
  if (this.nodeContext)
1089
895
  this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
1090
896
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
1091
- // this.getMatterbridgeLatestVersion();
1092
- // Current working directory
1093
897
  const currentDir = process.cwd();
1094
898
  this.log.debug(`Current Working Directory: ${currentDir}`);
1095
- // Command line arguments (excluding 'node' and the script name)
1096
899
  const cmdArgs = process.argv.slice(2).join(' ');
1097
900
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
1098
901
  }
1099
- /**
1100
- * Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
1101
- *
1102
- * @returns {Function} The MatterLogger function.
1103
- */
1104
902
  createMatterLogger() {
1105
- const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
903
+ const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
1106
904
  return (_level, formattedLog) => {
1107
905
  const logger = formattedLog.slice(44, 44 + 20).trim();
1108
906
  const message = formattedLog.slice(65);
1109
907
  matterLogger.logName = logger;
1110
908
  switch (_level) {
1111
909
  case MatterLogLevel.DEBUG:
1112
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
910
+ matterLogger.log("debug", message);
1113
911
  break;
1114
912
  case MatterLogLevel.INFO:
1115
- matterLogger.log("info" /* LogLevel.INFO */, message);
913
+ matterLogger.log("info", message);
1116
914
  break;
1117
915
  case MatterLogLevel.NOTICE:
1118
- matterLogger.log("notice" /* LogLevel.NOTICE */, message);
916
+ matterLogger.log("notice", message);
1119
917
  break;
1120
918
  case MatterLogLevel.WARN:
1121
- matterLogger.log("warn" /* LogLevel.WARN */, message);
919
+ matterLogger.log("warn", message);
1122
920
  break;
1123
921
  case MatterLogLevel.ERROR:
1124
- matterLogger.log("error" /* LogLevel.ERROR */, message);
922
+ matterLogger.log("error", message);
1125
923
  break;
1126
924
  case MatterLogLevel.FATAL:
1127
- matterLogger.log("fatal" /* LogLevel.FATAL */, message);
925
+ matterLogger.log("fatal", message);
1128
926
  break;
1129
927
  default:
1130
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
928
+ matterLogger.log("debug", message);
1131
929
  break;
1132
930
  }
1133
931
  };
1134
932
  }
1135
- /**
1136
- * Creates a Matter File Logger.
1137
- *
1138
- * @param {string} filePath - The path to the log file.
1139
- * @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
1140
- * @returns {Function} - A function that logs formatted messages to the log file.
1141
- */
1142
933
  async createMatterFileLogger(filePath, unlink = false) {
1143
- // 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
1144
934
  let fileSize = 0;
1145
935
  if (unlink) {
1146
936
  try {
@@ -1189,21 +979,12 @@ export class Matterbridge extends EventEmitter {
1189
979
  }
1190
980
  };
1191
981
  }
1192
- /**
1193
- * Restarts the process by exiting the current instance and loading a new instance.
1194
- */
1195
982
  async restartProcess() {
1196
983
  await this.cleanup('restarting...', true);
1197
984
  }
1198
- /**
1199
- * Shut down the process by exiting the current process.
1200
- */
1201
985
  async shutdownProcess() {
1202
986
  await this.cleanup('shutting down...', false);
1203
987
  }
1204
- /**
1205
- * Update matterbridge and and shut down the process.
1206
- */
1207
988
  async updateProcess() {
1208
989
  this.log.info('Updating matterbridge...');
1209
990
  try {
@@ -1216,72 +997,51 @@ export class Matterbridge extends EventEmitter {
1216
997
  this.frontend.wssSendRestartRequired();
1217
998
  await this.cleanup('updating...', false);
1218
999
  }
1219
- /**
1220
- * Unregister all devices and shut down the process.
1221
- */
1222
1000
  async unregisterAndShutdownProcess() {
1223
1001
  this.log.info('Unregistering all devices and shutting down...');
1224
1002
  for (const plugin of this.plugins) {
1225
1003
  await this.removeAllBridgedEndpoints(plugin.name, 250);
1226
1004
  }
1227
1005
  this.log.debug('Waiting for the MessageExchange to finish...');
1228
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second for MessageExchange to finish
1006
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1229
1007
  this.log.debug('Cleaning up and shutting down...');
1230
1008
  await this.cleanup('unregistered all devices and shutting down...', false);
1231
1009
  }
1232
- /**
1233
- * Reset commissioning and shut down the process.
1234
- */
1235
1010
  async shutdownProcessAndReset() {
1236
1011
  await this.cleanup('shutting down with reset...', false);
1237
1012
  }
1238
- /**
1239
- * Factory reset and shut down the process.
1240
- */
1241
1013
  async shutdownProcessAndFactoryReset() {
1242
1014
  await this.cleanup('shutting down with factory reset...', false);
1243
1015
  }
1244
- /**
1245
- * Cleans up the Matterbridge instance.
1246
- * @param message - The cleanup message.
1247
- * @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
1248
- * @returns A promise that resolves when the cleanup is completed.
1249
- */
1250
1016
  async cleanup(message, restart = false) {
1251
1017
  if (this.initialized && !this.hasCleanupStarted) {
1252
1018
  this.hasCleanupStarted = true;
1253
1019
  this.log.info(message);
1254
- // Clear the start matter interval
1255
1020
  if (this.startMatterInterval) {
1256
1021
  clearInterval(this.startMatterInterval);
1257
1022
  this.startMatterInterval = undefined;
1258
1023
  this.log.debug('Start matter interval cleared');
1259
1024
  }
1260
- // Clear the check update timeout
1261
1025
  if (this.checkUpdateTimeout) {
1262
1026
  clearInterval(this.checkUpdateTimeout);
1263
1027
  this.checkUpdateTimeout = undefined;
1264
1028
  this.log.debug('Check update timeout cleared');
1265
1029
  }
1266
- // Clear the check update interval
1267
1030
  if (this.checkUpdateInterval) {
1268
1031
  clearInterval(this.checkUpdateInterval);
1269
1032
  this.checkUpdateInterval = undefined;
1270
1033
  this.log.debug('Check update interval cleared');
1271
1034
  }
1272
- // Clear the configure timeout
1273
1035
  if (this.configureTimeout) {
1274
1036
  clearTimeout(this.configureTimeout);
1275
1037
  this.configureTimeout = undefined;
1276
1038
  this.log.debug('Matterbridge configure timeout cleared');
1277
1039
  }
1278
- // Clear the reachability timeout
1279
1040
  if (this.reachabilityTimeout) {
1280
1041
  clearTimeout(this.reachabilityTimeout);
1281
1042
  this.reachabilityTimeout = undefined;
1282
1043
  this.log.debug('Matterbridge reachability timeout cleared');
1283
1044
  }
1284
- // Calling the shutdown method of each plugin and clear the plugins reachability timeout
1285
1045
  for (const plugin of this.plugins) {
1286
1046
  if (!plugin.enabled || plugin.error)
1287
1047
  continue;
@@ -1292,10 +1052,9 @@ export class Matterbridge extends EventEmitter {
1292
1052
  this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
1293
1053
  }
1294
1054
  }
1295
- // Stopping matter server nodes
1296
1055
  this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
1297
1056
  this.log.debug('Waiting for the MessageExchange to finish...');
1298
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second for MessageExchange to finish
1057
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1299
1058
  if (this.bridgeMode === 'bridge') {
1300
1059
  if (this.serverNode) {
1301
1060
  await this.stopServerNode(this.serverNode);
@@ -1311,7 +1070,6 @@ export class Matterbridge extends EventEmitter {
1311
1070
  }
1312
1071
  }
1313
1072
  this.log.notice('Stopped matter server nodes');
1314
- // Matter commisioning reset
1315
1073
  if (message === 'shutting down with reset...') {
1316
1074
  this.log.info('Resetting Matterbridge commissioning information...');
1317
1075
  await this.matterStorageManager?.createContext('events')?.clearAll();
@@ -1321,37 +1079,17 @@ export class Matterbridge extends EventEmitter {
1321
1079
  await this.matterbridgeContext?.clearAll();
1322
1080
  this.log.info('Matter storage reset done! Remove the bridge from the controller.');
1323
1081
  }
1324
- // Stop matter storage
1325
1082
  await this.stopMatterStorage();
1326
- // Stop the frontend
1327
1083
  await this.frontend.stop();
1328
- // Remove the matterfilelogger
1329
1084
  try {
1330
1085
  Logger.removeLogger('matterfilelogger');
1331
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1332
1086
  }
1333
1087
  catch (error) {
1334
- // this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
1335
1088
  }
1336
- // Serialize registeredDevices
1337
1089
  if (this.nodeStorage && this.nodeContext) {
1338
- /*
1339
- TODO: Implement serialization of registered devices in edge mode
1340
- this.log.info('Saving registered devices...');
1341
- const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
1342
- this.devices.forEach(async (device) => {
1343
- const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
1344
- // this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
1345
- if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
1346
- });
1347
- await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
1348
- this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
1349
- */
1350
- // Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
1351
1090
  this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
1352
1091
  await this.nodeContext.close();
1353
1092
  this.nodeContext = undefined;
1354
- // Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
1355
1093
  for (const plugin of this.plugins) {
1356
1094
  if (plugin.nodeContext) {
1357
1095
  this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
@@ -1368,10 +1106,8 @@ export class Matterbridge extends EventEmitter {
1368
1106
  }
1369
1107
  this.plugins.clear();
1370
1108
  this.devices.clear();
1371
- // Factory reset
1372
1109
  if (message === 'shutting down with factory reset...') {
1373
1110
  try {
1374
- // Delete old matter storage file and backup
1375
1111
  const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
1376
1112
  this.log.info(`Unlinking old matter storage file: ${file}`);
1377
1113
  await fs.unlink(file);
@@ -1385,7 +1121,6 @@ export class Matterbridge extends EventEmitter {
1385
1121
  }
1386
1122
  }
1387
1123
  try {
1388
- // Delete matter node storage directory with its subdirectories and backup
1389
1124
  const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
1390
1125
  this.log.info(`Removing matter node storage directory: ${dir}`);
1391
1126
  await fs.rm(dir, { recursive: true });
@@ -1399,7 +1134,6 @@ export class Matterbridge extends EventEmitter {
1399
1134
  }
1400
1135
  }
1401
1136
  try {
1402
- // Delete node storage directory with its subdirectories and backup
1403
1137
  const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
1404
1138
  this.log.info(`Removing storage directory: ${dir}`);
1405
1139
  await fs.rm(dir, { recursive: true });
@@ -1414,13 +1148,12 @@ export class Matterbridge extends EventEmitter {
1414
1148
  }
1415
1149
  this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
1416
1150
  }
1417
- // Deregisters the process handlers
1418
1151
  this.deregisterProcesslHandlers();
1419
1152
  if (restart) {
1420
1153
  if (message === 'updating...') {
1421
1154
  this.log.info('Cleanup completed. Updating...');
1422
1155
  Matterbridge.instance = undefined;
1423
- this.emit('update'); // Restart the process but the update has been done before. TODO move all updates to the cli
1156
+ this.emit('update');
1424
1157
  }
1425
1158
  else if (message === 'restarting...') {
1426
1159
  this.log.info('Cleanup completed. Restarting...');
@@ -1440,14 +1173,6 @@ export class Matterbridge extends EventEmitter {
1440
1173
  this.log.debug('Cleanup already started...');
1441
1174
  }
1442
1175
  }
1443
- /**
1444
- * Creates and configures the server node for an accessory plugin for a given device.
1445
- *
1446
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1447
- * @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
1448
- * @param {boolean} [start=false] - Whether to start the server node after adding the device.
1449
- * @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
1450
- */
1451
1176
  async createAccessoryPlugin(plugin, device, start = false) {
1452
1177
  if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
1453
1178
  plugin.locked = true;
@@ -1461,13 +1186,6 @@ export class Matterbridge extends EventEmitter {
1461
1186
  await this.startServerNode(plugin.serverNode);
1462
1187
  }
1463
1188
  }
1464
- /**
1465
- * Creates and configures the server node for a dynamic plugin.
1466
- *
1467
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1468
- * @param {boolean} [start=false] - Whether to start the server node after adding the aggregator node.
1469
- * @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
1470
- */
1471
1189
  async createDynamicPlugin(plugin, start = false) {
1472
1190
  if (!plugin.locked) {
1473
1191
  plugin.locked = true;
@@ -1480,13 +1198,7 @@ export class Matterbridge extends EventEmitter {
1480
1198
  await this.startServerNode(plugin.serverNode);
1481
1199
  }
1482
1200
  }
1483
- /**
1484
- * Starts the Matterbridge in bridge mode.
1485
- * @private
1486
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1487
- */
1488
1201
  async startBridge() {
1489
- // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1490
1202
  if (!this.matterStorageManager)
1491
1203
  throw new Error('No storage manager initialized');
1492
1204
  if (!this.matterbridgeContext)
@@ -1524,9 +1236,7 @@ export class Matterbridge extends EventEmitter {
1524
1236
  clearInterval(this.startMatterInterval);
1525
1237
  this.startMatterInterval = undefined;
1526
1238
  this.log.debug('Cleared startMatterInterval interval for Matterbridge');
1527
- // Start the Matter server node
1528
1239
  this.startServerNode(this.serverNode);
1529
- // Configure the plugins
1530
1240
  this.configureTimeout = setTimeout(async () => {
1531
1241
  for (const plugin of this.plugins) {
1532
1242
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1544,7 +1254,6 @@ export class Matterbridge extends EventEmitter {
1544
1254
  }
1545
1255
  this.frontend.wssSendRefreshRequired('plugins');
1546
1256
  }, 30 * 1000);
1547
- // Setting reachability to true
1548
1257
  this.reachabilityTimeout = setTimeout(() => {
1549
1258
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1550
1259
  if (this.aggregatorNode)
@@ -1553,11 +1262,6 @@ export class Matterbridge extends EventEmitter {
1553
1262
  }, 60 * 1000);
1554
1263
  }, 1000);
1555
1264
  }
1556
- /**
1557
- * Starts the Matterbridge in childbridge mode.
1558
- * @private
1559
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1560
- */
1561
1265
  async startChildbridge() {
1562
1266
  if (!this.matterStorageManager)
1563
1267
  throw new Error('No storage manager initialized');
@@ -1595,7 +1299,6 @@ export class Matterbridge extends EventEmitter {
1595
1299
  clearInterval(this.startMatterInterval);
1596
1300
  this.startMatterInterval = undefined;
1597
1301
  this.log.debug('Cleared startMatterInterval interval in childbridge mode');
1598
- // Configure the plugins
1599
1302
  this.configureTimeout = setTimeout(async () => {
1600
1303
  for (const plugin of this.plugins) {
1601
1304
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1632,9 +1335,7 @@ export class Matterbridge extends EventEmitter {
1632
1335
  this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
1633
1336
  continue;
1634
1337
  }
1635
- // Start the Matter server node
1636
1338
  this.startServerNode(plugin.serverNode);
1637
- // Setting reachability to true
1638
1339
  plugin.reachabilityTimeout = setTimeout(() => {
1639
1340
  this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggregator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
1640
1341
  if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
@@ -1644,11 +1345,6 @@ export class Matterbridge extends EventEmitter {
1644
1345
  }
1645
1346
  }, 1000);
1646
1347
  }
1647
- /**
1648
- * Starts the Matterbridge controller.
1649
- * @private
1650
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1651
- */
1652
1348
  async startController() {
1653
1349
  if (!this.matterStorageManager) {
1654
1350
  this.log.error('No storage manager initialized');
@@ -1663,207 +1359,8 @@ export class Matterbridge extends EventEmitter {
1663
1359
  return;
1664
1360
  }
1665
1361
  this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1666
- /*
1667
- this.matterServer = await this.createMatterServer(this.storageManager);
1668
- this.log.info('Creating matter commissioning controller');
1669
- this.commissioningController = new CommissioningController({
1670
- autoConnect: false,
1671
- });
1672
- this.log.info('Adding matter commissioning controller to matter server');
1673
- await this.matterServer.addCommissioningController(this.commissioningController);
1674
-
1675
- this.log.info('Starting matter server');
1676
- await this.matterServer.start();
1677
- this.log.info('Matter server started');
1678
- const commissioningOptions: ControllerCommissioningFlowOptions = {
1679
- regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
1680
- regulatoryCountryCode: 'XX',
1681
- };
1682
- const commissioningController = new CommissioningController({
1683
- environment: {
1684
- environment,
1685
- id: uniqueId,
1686
- },
1687
- autoConnect: false, // Do not auto connect to the commissioned nodes
1688
- adminFabricLabel,
1689
- });
1690
-
1691
- if (hasParameter('pairingcode')) {
1692
- this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
1693
- const pairingCode = getParameter('pairingcode');
1694
- const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
1695
- const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
1696
-
1697
- let longDiscriminator, setupPin, shortDiscriminator;
1698
- if (pairingCode !== undefined) {
1699
- const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
1700
- shortDiscriminator = pairingCodeCodec.shortDiscriminator;
1701
- longDiscriminator = undefined;
1702
- setupPin = pairingCodeCodec.passcode;
1703
- this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
1704
- } else {
1705
- longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
1706
- if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
1707
- setupPin = this.controllerContext.get('pin', 20202021);
1708
- }
1709
- if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
1710
- throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
1711
- }
1712
-
1713
- const options = {
1714
- commissioning: commissioningOptions,
1715
- discovery: {
1716
- knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
1717
- identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
1718
- },
1719
- passcode: setupPin,
1720
- } as NodeCommissioningOptions;
1721
- this.log.info('Commissioning with options:', options);
1722
- const nodeId = await this.commissioningController.commissionNode(options);
1723
- this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
1724
- this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
1725
- } // (hasParameter('pairingcode'))
1726
-
1727
- if (hasParameter('unpairall')) {
1728
- this.log.info('***Commissioning controller unpairing all nodes...');
1729
- const nodeIds = this.commissioningController.getCommissionedNodes();
1730
- for (const nodeId of nodeIds) {
1731
- this.log.info('***Commissioning controller unpairing node:', nodeId);
1732
- await this.commissioningController.removeNode(nodeId);
1733
- }
1734
- return;
1735
- }
1736
-
1737
- if (hasParameter('discover')) {
1738
- // const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1739
- // console.log(discover);
1740
- }
1741
-
1742
- if (!this.commissioningController.isCommissioned()) {
1743
- this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
1744
- return;
1745
- }
1746
-
1747
- const nodeIds = this.commissioningController.getCommissionedNodes();
1748
- this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
1749
- for (const nodeId of nodeIds) {
1750
- this.log.info(`***Connecting to commissioned node: ${nodeId}`);
1751
-
1752
- const node = await this.commissioningController.connectNode(nodeId, {
1753
- autoSubscribe: false,
1754
- attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
1755
- this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
1756
- eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
1757
- this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
1758
- stateInformationCallback: (peerNodeId, info) => {
1759
- switch (info) {
1760
- case NodeStateInformation.Connected:
1761
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
1762
- break;
1763
- case NodeStateInformation.Disconnected:
1764
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
1765
- break;
1766
- case NodeStateInformation.Reconnecting:
1767
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
1768
- break;
1769
- case NodeStateInformation.WaitingForDeviceDiscovery:
1770
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
1771
- break;
1772
- case NodeStateInformation.StructureChanged:
1773
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
1774
- break;
1775
- case NodeStateInformation.Decommissioned:
1776
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
1777
- break;
1778
- default:
1779
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
1780
- break;
1781
- }
1782
- },
1783
- });
1784
-
1785
- node.logStructure();
1786
-
1787
- // Get the interaction client
1788
- this.log.info('Getting the interaction client');
1789
- const interactionClient = await node.getInteractionClient();
1790
- let cluster;
1791
- let attributes;
1792
-
1793
- // Log BasicInformationCluster
1794
- cluster = BasicInformationCluster;
1795
- attributes = await interactionClient.getMultipleAttributes({
1796
- attributes: [{ clusterId: cluster.id }],
1797
- });
1798
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1799
- attributes.forEach((attribute) => {
1800
- this.log.info(
1801
- `- 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}`,
1802
- );
1803
- });
1804
-
1805
- // Log PowerSourceCluster
1806
- cluster = PowerSourceCluster;
1807
- attributes = await interactionClient.getMultipleAttributes({
1808
- attributes: [{ clusterId: cluster.id }],
1809
- });
1810
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1811
- attributes.forEach((attribute) => {
1812
- this.log.info(
1813
- `- 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}`,
1814
- );
1815
- });
1816
-
1817
- // Log ThreadNetworkDiagnostics
1818
- cluster = ThreadNetworkDiagnosticsCluster;
1819
- attributes = await interactionClient.getMultipleAttributes({
1820
- attributes: [{ clusterId: cluster.id }],
1821
- });
1822
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1823
- attributes.forEach((attribute) => {
1824
- this.log.info(
1825
- `- 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}`,
1826
- );
1827
- });
1828
-
1829
- // Log SwitchCluster
1830
- cluster = SwitchCluster;
1831
- attributes = await interactionClient.getMultipleAttributes({
1832
- attributes: [{ clusterId: cluster.id }],
1833
- });
1834
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1835
- attributes.forEach((attribute) => {
1836
- this.log.info(
1837
- `- 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}`,
1838
- );
1839
- });
1840
-
1841
- this.log.info('Subscribing to all attributes and events');
1842
- await node.subscribeAllAttributesAndEvents({
1843
- ignoreInitialTriggers: false,
1844
- attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
1845
- this.log.info(
1846
- `***${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}`,
1847
- ),
1848
- eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
1849
- this.log.info(
1850
- `***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
1851
- );
1852
- },
1853
- });
1854
- this.log.info('Subscribed to all attributes and events');
1855
- }
1856
- */
1857
1362
  }
1858
- /** ***********************************************************************************************************************************/
1859
- /** Matter.js methods */
1860
- /** ***********************************************************************************************************************************/
1861
- /**
1862
- * Starts the matter storage process with name Matterbridge.
1863
- * @returns {Promise<void>} - A promise that resolves when the storage process is started.
1864
- */
1865
1363
  async startMatterStorage() {
1866
- // Setup Matter storage
1867
1364
  this.log.info(`Starting matter node storage...`);
1868
1365
  this.matterStorageService = this.environment.get(StorageService);
1869
1366
  this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
@@ -1872,25 +1369,13 @@ export class Matterbridge extends EventEmitter {
1872
1369
  this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName);
1873
1370
  this.matterbridgeInformation.matterbridgeSerialNumber = await this.matterbridgeContext.get('serialNumber', '');
1874
1371
  this.log.info('Matter node storage started');
1875
- // Backup matter storage since it is created/opened correctly
1876
1372
  await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
1877
1373
  }
1878
- /**
1879
- * Makes a backup copy of the specified matter storage directory.
1880
- *
1881
- * @param storageName - The name of the storage directory to be backed up.
1882
- * @param backupName - The name of the backup directory to be created.
1883
- * @returns {Promise<void>} A promise that resolves when the has been done.
1884
- */
1885
1374
  async backupMatterStorage(storageName, backupName) {
1886
1375
  this.log.info('Creating matter node storage backup...');
1887
1376
  await copyDirectory(storageName, backupName);
1888
1377
  this.log.info('Created matter node storage backup');
1889
1378
  }
1890
- /**
1891
- * Stops the matter storage.
1892
- * @returns {Promise<void>} A promise that resolves when the storage is stopped.
1893
- */
1894
1379
  async stopMatterStorage() {
1895
1380
  this.log.info('Closing matter node storage...');
1896
1381
  await this.matterStorageManager?.close();
@@ -1899,19 +1384,6 @@ export class Matterbridge extends EventEmitter {
1899
1384
  this.matterbridgeContext = undefined;
1900
1385
  this.log.info('Matter node storage closed');
1901
1386
  }
1902
- /**
1903
- * Creates a server node storage context.
1904
- *
1905
- * @param {string} pluginName - The name of the plugin.
1906
- * @param {string} deviceName - The name of the device.
1907
- * @param {DeviceTypeId} deviceType - The device type of the device.
1908
- * @param {number} vendorId - The vendor ID.
1909
- * @param {string} vendorName - The vendor name.
1910
- * @param {number} productId - The product ID.
1911
- * @param {string} productName - The product name.
1912
- * @param {string} [serialNumber] - The serial number of the device (optional).
1913
- * @returns {Promise<StorageContext>} The storage context for the commissioning server.
1914
- */
1915
1387
  async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
1916
1388
  const { randomBytes } = await import('node:crypto');
1917
1389
  if (!this.matterStorageService)
@@ -1945,15 +1417,6 @@ export class Matterbridge extends EventEmitter {
1945
1417
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1946
1418
  return storageContext;
1947
1419
  }
1948
- /**
1949
- * Creates a server node.
1950
- *
1951
- * @param {StorageContext} storageContext - The storage context for the server node.
1952
- * @param {number} [port=5540] - The port number for the server node. Defaults to 5540.
1953
- * @param {number} [passcode=20242025] - The passcode for the server node. Defaults to 20242025.
1954
- * @param {number} [discriminator=3850] - The discriminator for the server node. Defaults to 3850.
1955
- * @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
1956
- */
1957
1420
  async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
1958
1421
  const storeId = await storageContext.get('storeId');
1959
1422
  this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
@@ -1963,33 +1426,21 @@ export class Matterbridge extends EventEmitter {
1963
1426
  this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
1964
1427
  this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1965
1428
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1966
- /**
1967
- * Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
1968
- */
1969
1429
  const serverNode = await ServerNode.create({
1970
- // Required: Give the Node a unique ID which is used to store the state of this node
1971
1430
  id: storeId,
1972
- // Provide Network relevant configuration like the port
1973
- // Optional when operating only one device on a host, Default port is 5540
1974
1431
  network: {
1975
1432
  listeningAddressIpv4: this.ipv4address,
1976
1433
  listeningAddressIpv6: this.ipv6address,
1977
1434
  port,
1978
1435
  },
1979
- // Provide Commissioning relevant settings
1980
- // Optional for development/testing purposes
1981
1436
  commissioning: {
1982
1437
  passcode,
1983
1438
  discriminator,
1984
1439
  },
1985
- // Provide Node announcement settings
1986
- // Optional: If Ommitted some development defaults are used
1987
1440
  productDescription: {
1988
1441
  name: await storageContext.get('deviceName'),
1989
1442
  deviceType: DeviceTypeId(await storageContext.get('deviceType')),
1990
1443
  },
1991
- // Provide defaults for the BasicInformation cluster on the Root endpoint
1992
- // Optional: If Omitted some development defaults are used
1993
1444
  basicInformation: {
1994
1445
  vendorId: VendorId(await storageContext.get('vendorId')),
1995
1446
  vendorName: await storageContext.get('vendorName'),
@@ -2007,13 +1458,12 @@ export class Matterbridge extends EventEmitter {
2007
1458
  },
2008
1459
  });
2009
1460
  const sanitizeFabrics = (fabrics, resetSessions = false) => {
2010
- // New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
2011
1461
  const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
2012
1462
  this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
2013
1463
  if (this.bridgeMode === 'bridge') {
2014
1464
  this.matterbridgeFabricInformations = sanitizedFabrics;
2015
1465
  if (resetSessions)
2016
- this.matterbridgeSessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
1466
+ this.matterbridgeSessionInformations = undefined;
2017
1467
  this.matterbridgePaired = true;
2018
1468
  }
2019
1469
  if (this.bridgeMode === 'childbridge') {
@@ -2021,19 +1471,13 @@ export class Matterbridge extends EventEmitter {
2021
1471
  if (plugin) {
2022
1472
  plugin.fabricInformations = sanitizedFabrics;
2023
1473
  if (resetSessions)
2024
- plugin.sessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
1474
+ plugin.sessionInformations = undefined;
2025
1475
  plugin.paired = true;
2026
1476
  }
2027
1477
  }
2028
1478
  };
2029
- /**
2030
- * This event is triggered when the device is initially commissioned successfully.
2031
- * This means: It is added to the first fabric.
2032
- */
2033
1479
  serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
2034
- /** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
2035
1480
  serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
2036
- /** This event is triggered when the device went online. This means that it is discoverable in the network. */
2037
1481
  serverNode.lifecycle.online.on(async () => {
2038
1482
  this.log.notice(`Server node for ${storeId} is online`);
2039
1483
  if (!serverNode.lifecycle.isCommissioned) {
@@ -2102,7 +1546,6 @@ export class Matterbridge extends EventEmitter {
2102
1546
  this.frontend.wssSendRefreshRequired('settings');
2103
1547
  this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
2104
1548
  });
2105
- /** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
2106
1549
  serverNode.lifecycle.offline.on(() => {
2107
1550
  this.log.notice(`Server node for ${storeId} is offline`);
2108
1551
  if (this.bridgeMode === 'bridge') {
@@ -2126,10 +1569,6 @@ export class Matterbridge extends EventEmitter {
2126
1569
  this.frontend.wssSendRefreshRequired('settings');
2127
1570
  this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
2128
1571
  });
2129
- /**
2130
- * This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
2131
- * information is needed.
2132
- */
2133
1572
  serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
2134
1573
  let action = '';
2135
1574
  switch (fabricAction) {
@@ -2163,24 +1602,16 @@ export class Matterbridge extends EventEmitter {
2163
1602
  }
2164
1603
  }
2165
1604
  };
2166
- /**
2167
- * This event is triggered when an operative new session was opened by a Controller.
2168
- * It is not triggered for the initial commissioning process, just afterwards for real connections.
2169
- */
2170
1605
  serverNode.events.sessions.opened.on((session) => {
2171
1606
  this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
2172
1607
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2173
1608
  this.frontend.wssSendRefreshRequired('sessions');
2174
1609
  });
2175
- /**
2176
- * This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
2177
- */
2178
1610
  serverNode.events.sessions.closed.on((session) => {
2179
1611
  this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
2180
1612
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2181
1613
  this.frontend.wssSendRefreshRequired('sessions');
2182
1614
  });
2183
- /** This event is triggered when a subscription gets added or removed on an operative session. */
2184
1615
  serverNode.events.sessions.subscriptionsChanged.on((session) => {
2185
1616
  this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
2186
1617
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
@@ -2189,42 +1620,24 @@ export class Matterbridge extends EventEmitter {
2189
1620
  this.log.info(`Created server node for ${storeId}`);
2190
1621
  return serverNode;
2191
1622
  }
2192
- /**
2193
- * Starts the specified server node.
2194
- *
2195
- * @param {ServerNode} [matterServerNode] - The server node to start.
2196
- * @returns {Promise<void>} A promise that resolves when the server node has started.
2197
- */
2198
1623
  async startServerNode(matterServerNode) {
2199
1624
  if (!matterServerNode)
2200
1625
  return;
2201
1626
  this.log.notice(`Starting ${matterServerNode.id} server node`);
2202
1627
  await matterServerNode.start();
2203
1628
  }
2204
- /**
2205
- * Stops the specified server node.
2206
- *
2207
- * @param {ServerNode} matterServerNode - The server node to stop.
2208
- * @returns {Promise<void>} A promise that resolves when the server node has stopped.
2209
- */
2210
1629
  async stopServerNode(matterServerNode) {
2211
1630
  if (!matterServerNode)
2212
1631
  return;
2213
1632
  this.log.notice(`Closing ${matterServerNode.id} server node`);
2214
1633
  try {
2215
- await withTimeout(matterServerNode.close(), 30000); // 30 seconds timeout to allow slow devices to close gracefully
1634
+ await withTimeout(matterServerNode.close(), 30000);
2216
1635
  this.log.info(`Closed ${matterServerNode.id} server node`);
2217
1636
  }
2218
1637
  catch (error) {
2219
1638
  this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
2220
1639
  }
2221
1640
  }
2222
- /**
2223
- * Advertises the specified server node.
2224
- *
2225
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2226
- * @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.
2227
- */
2228
1641
  async advertiseServerNode(matterServerNode) {
2229
1642
  if (matterServerNode) {
2230
1643
  await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
@@ -2233,45 +1646,24 @@ export class Matterbridge extends EventEmitter {
2233
1646
  return { qrPairingCode, manualPairingCode };
2234
1647
  }
2235
1648
  }
2236
- /**
2237
- * Stop advertise the specified server node.
2238
- *
2239
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2240
- * @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
2241
- */
2242
1649
  async stopAdvertiseServerNode(matterServerNode) {
2243
1650
  if (matterServerNode && matterServerNode.lifecycle.isOnline) {
2244
1651
  await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
2245
1652
  this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
2246
1653
  }
2247
1654
  }
2248
- /**
2249
- * Creates an aggregator node with the specified storage context.
2250
- *
2251
- * @param {StorageContext} storageContext - The storage context for the aggregator node.
2252
- * @returns {Promise<EndpointNode<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
2253
- */
2254
1655
  async createAggregatorNode(storageContext) {
2255
1656
  this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
2256
1657
  const aggregatorNode = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
2257
1658
  return aggregatorNode;
2258
1659
  }
2259
- /**
2260
- * Adds a MatterbridgeEndpoint to the specified plugin.
2261
- *
2262
- * @param {string} pluginName - The name of the plugin.
2263
- * @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
2264
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
2265
- */
2266
1660
  async addBridgedEndpoint(pluginName, device) {
2267
- // Check if the plugin is registered
2268
1661
  const plugin = this.plugins.get(pluginName);
2269
1662
  if (!plugin) {
2270
1663
  this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
2271
1664
  return;
2272
1665
  }
2273
1666
  if (this.bridgeMode === 'bridge') {
2274
- // Register and add the device to the matterbridge aggregator node
2275
1667
  this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
2276
1668
  if (!this.aggregatorNode) {
2277
1669
  this.log.error('Aggregator node not found for Matterbridge');
@@ -2288,7 +1680,6 @@ export class Matterbridge extends EventEmitter {
2288
1680
  }
2289
1681
  }
2290
1682
  else if (this.bridgeMode === 'childbridge') {
2291
- // Register and add the device to the plugin server node
2292
1683
  if (plugin.type === 'AccessoryPlatform') {
2293
1684
  try {
2294
1685
  this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
@@ -2305,12 +1696,10 @@ export class Matterbridge extends EventEmitter {
2305
1696
  return;
2306
1697
  }
2307
1698
  }
2308
- // Register and add the device to the plugin aggregator node
2309
1699
  if (plugin.type === 'DynamicPlatform') {
2310
1700
  try {
2311
1701
  this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
2312
1702
  await this.createDynamicPlugin(plugin);
2313
- // Fast plugins can add another device before the server node is created
2314
1703
  await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
2315
1704
  if (!plugin.aggregatorNode) {
2316
1705
  this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
@@ -2330,28 +1719,17 @@ export class Matterbridge extends EventEmitter {
2330
1719
  plugin.registeredDevices++;
2331
1720
  if (plugin.addedDevices !== undefined)
2332
1721
  plugin.addedDevices++;
2333
- // Add the device to the DeviceManager
2334
1722
  this.devices.set(device);
2335
- // Subscribe to the reachable$Changed event
2336
1723
  await this.subscribeAttributeChanged(plugin, device);
2337
1724
  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}`);
2338
1725
  }
2339
- /**
2340
- * Removes a MatterbridgeEndpoint from the specified plugin.
2341
- *
2342
- * @param {string} pluginName - The name of the plugin.
2343
- * @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
2344
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
2345
- */
2346
1726
  async removeBridgedEndpoint(pluginName, device) {
2347
1727
  this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
2348
- // Check if the plugin is registered
2349
1728
  const plugin = this.plugins.get(pluginName);
2350
1729
  if (!plugin) {
2351
1730
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
2352
1731
  return;
2353
1732
  }
2354
- // Register and add the device to the matterbridge aggregator node
2355
1733
  if (this.bridgeMode === 'bridge') {
2356
1734
  if (!this.aggregatorNode) {
2357
1735
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
@@ -2366,7 +1744,6 @@ export class Matterbridge extends EventEmitter {
2366
1744
  }
2367
1745
  else if (this.bridgeMode === 'childbridge') {
2368
1746
  if (plugin.type === 'AccessoryPlatform') {
2369
- // Nothing to do here since the server node has no aggregator node but only the device itself
2370
1747
  }
2371
1748
  else if (plugin.type === 'DynamicPlatform') {
2372
1749
  if (!plugin.aggregatorNode) {
@@ -2381,21 +1758,8 @@ export class Matterbridge extends EventEmitter {
2381
1758
  if (plugin.addedDevices !== undefined)
2382
1759
  plugin.addedDevices--;
2383
1760
  }
2384
- // Remove the device from the DeviceManager
2385
1761
  this.devices.remove(device);
2386
1762
  }
2387
- /**
2388
- * Removes all bridged endpoints from the specified plugin.
2389
- *
2390
- * @param {string} pluginName - The name of the plugin.
2391
- * @param {number} [delay=0] - The delay in milliseconds between removing each bridged endpoint (default: 0).
2392
- * @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
2393
- *
2394
- * @remarks
2395
- * This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
2396
- * It also applies a delay between each removal if specified.
2397
- * The delay is useful to allow the controllers to receive a single subscription for each device removed.
2398
- */
2399
1763
  async removeAllBridgedEndpoints(pluginName, delay = 0) {
2400
1764
  this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
2401
1765
  for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
@@ -2406,25 +1770,9 @@ export class Matterbridge extends EventEmitter {
2406
1770
  if (delay > 0)
2407
1771
  await new Promise((resolve) => setTimeout(resolve, 2000));
2408
1772
  }
2409
- /**
2410
- * Subscribes to the attribute change event for the given device and plugin.
2411
- * Specifically, it listens for changes in the 'reachable' attribute of the
2412
- * BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
2413
- *
2414
- * @param {RegisteredPlugin} plugin - The plugin associated with the device.
2415
- * @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
2416
- * @returns {Promise<void>} A promise that resolves when the subscription is set up.
2417
- */
2418
1773
  async subscribeAttributeChanged(plugin, device) {
2419
1774
  this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
2420
1775
  if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
2421
- /*
2422
- this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) subscribed to reachable$Changed`);
2423
- setTimeout(async () => {
2424
- this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) changed to reachable false`);
2425
- await plugin.serverNode?.setStateOf(BasicInformationServer, { reachable: false });
2426
- }, 60000).unref();
2427
- */
2428
1776
  plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
2429
1777
  this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
2430
1778
  this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, 'BasicInformationServer', 'reachable', reachable);
@@ -2437,12 +1785,6 @@ export class Matterbridge extends EventEmitter {
2437
1785
  });
2438
1786
  }
2439
1787
  }
2440
- /**
2441
- * Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
2442
- *
2443
- * @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
2444
- * @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
2445
- */
2446
1788
  sanitizeFabricInformations(fabricInfo) {
2447
1789
  return fabricInfo.map((info) => {
2448
1790
  return {
@@ -2456,12 +1798,6 @@ export class Matterbridge extends EventEmitter {
2456
1798
  };
2457
1799
  });
2458
1800
  }
2459
- /**
2460
- * Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
2461
- *
2462
- * @param {SessionInformation[]} sessionInfo - The array of session information objects.
2463
- * @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
2464
- */
2465
1801
  sanitizeSessionInformation(sessionInfo) {
2466
1802
  return sessionInfo
2467
1803
  .filter((session) => session.isPeerActive)
@@ -2489,20 +1825,7 @@ export class Matterbridge extends EventEmitter {
2489
1825
  };
2490
1826
  });
2491
1827
  }
2492
- /**
2493
- * Sets the reachability of the specified aggregator node bridged devices and trigger.
2494
- * @param {EndpointNode<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
2495
- * @param {boolean} reachable - A boolean indicating the reachability status to set.
2496
- */
2497
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2498
1828
  async setAggregatorReachability(aggregatorNode, reachable) {
2499
- /*
2500
- for (const child of aggregatorNode.parts) {
2501
- this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
2502
- await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
2503
- child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
2504
- }
2505
- */
2506
1829
  }
2507
1830
  getVendorIdName = (vendorId) => {
2508
1831
  if (!vendorId)
@@ -2545,37 +1868,14 @@ export class Matterbridge extends EventEmitter {
2545
1868
  }
2546
1869
  return vendorName;
2547
1870
  };
2548
- /**
2549
- * Spawns a child process with the given command and arguments.
2550
- * @param {string} command - The command to execute.
2551
- * @param {string[]} args - The arguments to pass to the command (default: []).
2552
- * @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
2553
- */
2554
1871
  async spawnCommand(command, args = []) {
2555
1872
  const { spawn } = await import('node:child_process');
2556
- /*
2557
- npm > npm.cmd on windows
2558
- cmd.exe ['dir'] on windows
2559
- await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
2560
- process.on('unhandledRejection', (reason, promise) => {
2561
- this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
2562
- });
2563
-
2564
- spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
2565
- spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
2566
- debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
2567
- debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
2568
- */
2569
1873
  const cmdLine = command + ' ' + args.join(' ');
2570
1874
  if (process.platform === 'win32' && command === 'npm') {
2571
- // Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
2572
1875
  const argstring = 'npm ' + args.join(' ');
2573
1876
  args.splice(0, args.length, '/c', argstring);
2574
1877
  command = 'cmd.exe';
2575
1878
  }
2576
- // Decide when using sudo on linux
2577
- // When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
2578
- // When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
2579
1879
  if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
2580
1880
  args.unshift(command);
2581
1881
  command = 'sudo';
@@ -2634,4 +1934,3 @@ export class Matterbridge extends EventEmitter {
2634
1934
  });
2635
1935
  }
2636
1936
  }
2637
- //# sourceMappingURL=matterbridge.js.map