matterbridge 2.2.8 → 2.2.9-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/dist/cli.js +2 -37
  3. package/dist/cluster/export.js +0 -2
  4. package/dist/defaultConfigSchema.js +0 -23
  5. package/dist/deviceManager.js +1 -94
  6. package/dist/frontend.js +19 -326
  7. package/dist/index.js +1 -28
  8. package/dist/logger/export.js +0 -1
  9. package/dist/matter/behaviors.js +0 -2
  10. package/dist/matter/clusters.js +0 -2
  11. package/dist/matter/devices.js +0 -2
  12. package/dist/matter/endpoints.js +0 -2
  13. package/dist/matter/export.js +0 -2
  14. package/dist/matter/types.js +0 -2
  15. package/dist/matterbridge.js +51 -747
  16. package/dist/matterbridgeAccessoryPlatform.js +0 -33
  17. package/dist/matterbridgeBehaviors.js +1 -32
  18. package/dist/matterbridgeDeviceTypes.js +11 -112
  19. package/dist/matterbridgeDynamicPlatform.js +0 -33
  20. package/dist/matterbridgeEndpoint.js +6 -720
  21. package/dist/matterbridgeEndpointHelpers.js +9 -118
  22. package/dist/matterbridgePlatform.js +7 -216
  23. package/dist/matterbridgeTypes.js +0 -24
  24. package/dist/pluginManager.js +3 -262
  25. package/dist/shelly.js +6 -146
  26. package/dist/storage/export.js +0 -1
  27. package/dist/update.js +0 -45
  28. package/dist/utils/colorUtils.js +2 -205
  29. package/dist/utils/copyDirectory.js +1 -37
  30. package/dist/utils/createZip.js +2 -42
  31. package/dist/utils/deepCopy.js +0 -40
  32. package/dist/utils/deepEqual.js +1 -65
  33. package/dist/utils/export.js +0 -1
  34. package/dist/utils/isvalid.js +0 -86
  35. package/dist/utils/network.js +5 -76
  36. package/dist/utils/parameter.js +0 -41
  37. package/dist/utils/wait.js +5 -48
  38. package/frontend/build/asset-manifest.json +3 -3
  39. package/frontend/build/index.html +1 -1
  40. package/frontend/build/static/js/{main.e11d6bb4.js → main.ff47208e.js} +3 -3
  41. package/frontend/build/static/js/{main.e11d6bb4.js.map → main.ff47208e.js.map} +1 -1
  42. package/npm-shrinkwrap.json +9 -9
  43. package/package.json +1 -2
  44. package/dist/cli.d.ts +0 -29
  45. package/dist/cli.d.ts.map +0 -1
  46. package/dist/cli.js.map +0 -1
  47. package/dist/cluster/export.d.ts +0 -2
  48. package/dist/cluster/export.d.ts.map +0 -1
  49. package/dist/cluster/export.js.map +0 -1
  50. package/dist/defaultConfigSchema.d.ts +0 -27
  51. package/dist/defaultConfigSchema.d.ts.map +0 -1
  52. package/dist/defaultConfigSchema.js.map +0 -1
  53. package/dist/deviceManager.d.ts +0 -114
  54. package/dist/deviceManager.d.ts.map +0 -1
  55. package/dist/deviceManager.js.map +0 -1
  56. package/dist/frontend.d.ts +0 -221
  57. package/dist/frontend.d.ts.map +0 -1
  58. package/dist/frontend.js.map +0 -1
  59. package/dist/index.d.ts +0 -35
  60. package/dist/index.d.ts.map +0 -1
  61. package/dist/index.js.map +0 -1
  62. package/dist/logger/export.d.ts +0 -2
  63. package/dist/logger/export.d.ts.map +0 -1
  64. package/dist/logger/export.js.map +0 -1
  65. package/dist/matter/behaviors.d.ts +0 -2
  66. package/dist/matter/behaviors.d.ts.map +0 -1
  67. package/dist/matter/behaviors.js.map +0 -1
  68. package/dist/matter/clusters.d.ts +0 -2
  69. package/dist/matter/clusters.d.ts.map +0 -1
  70. package/dist/matter/clusters.js.map +0 -1
  71. package/dist/matter/devices.d.ts +0 -2
  72. package/dist/matter/devices.d.ts.map +0 -1
  73. package/dist/matter/devices.js.map +0 -1
  74. package/dist/matter/endpoints.d.ts +0 -2
  75. package/dist/matter/endpoints.d.ts.map +0 -1
  76. package/dist/matter/endpoints.js.map +0 -1
  77. package/dist/matter/export.d.ts +0 -5
  78. package/dist/matter/export.d.ts.map +0 -1
  79. package/dist/matter/export.js.map +0 -1
  80. package/dist/matter/types.d.ts +0 -3
  81. package/dist/matter/types.d.ts.map +0 -1
  82. package/dist/matter/types.js.map +0 -1
  83. package/dist/matterbridge.d.ts +0 -425
  84. package/dist/matterbridge.d.ts.map +0 -1
  85. package/dist/matterbridge.js.map +0 -1
  86. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
  87. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  88. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  89. package/dist/matterbridgeBehaviors.d.ts +0 -1056
  90. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  91. package/dist/matterbridgeBehaviors.js.map +0 -1
  92. package/dist/matterbridgeDeviceTypes.d.ts +0 -177
  93. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  94. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  95. package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
  96. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  97. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  98. package/dist/matterbridgeEndpoint.d.ts +0 -867
  99. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  100. package/dist/matterbridgeEndpoint.js.map +0 -1
  101. package/dist/matterbridgeEndpointHelpers.d.ts +0 -2275
  102. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  103. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  104. package/dist/matterbridgePlatform.d.ts +0 -285
  105. package/dist/matterbridgePlatform.d.ts.map +0 -1
  106. package/dist/matterbridgePlatform.js.map +0 -1
  107. package/dist/matterbridgeTypes.d.ts +0 -183
  108. package/dist/matterbridgeTypes.d.ts.map +0 -1
  109. package/dist/matterbridgeTypes.js.map +0 -1
  110. package/dist/pluginManager.d.ts +0 -271
  111. package/dist/pluginManager.d.ts.map +0 -1
  112. package/dist/pluginManager.js.map +0 -1
  113. package/dist/shelly.d.ts +0 -92
  114. package/dist/shelly.d.ts.map +0 -1
  115. package/dist/shelly.js.map +0 -1
  116. package/dist/storage/export.d.ts +0 -2
  117. package/dist/storage/export.d.ts.map +0 -1
  118. package/dist/storage/export.js.map +0 -1
  119. package/dist/update.d.ts +0 -32
  120. package/dist/update.d.ts.map +0 -1
  121. package/dist/update.js.map +0 -1
  122. package/dist/utils/colorUtils.d.ts +0 -61
  123. package/dist/utils/colorUtils.d.ts.map +0 -1
  124. package/dist/utils/colorUtils.js.map +0 -1
  125. package/dist/utils/copyDirectory.d.ts +0 -32
  126. package/dist/utils/copyDirectory.d.ts.map +0 -1
  127. package/dist/utils/copyDirectory.js.map +0 -1
  128. package/dist/utils/createZip.d.ts +0 -38
  129. package/dist/utils/createZip.d.ts.map +0 -1
  130. package/dist/utils/createZip.js.map +0 -1
  131. package/dist/utils/deepCopy.d.ts +0 -31
  132. package/dist/utils/deepCopy.d.ts.map +0 -1
  133. package/dist/utils/deepCopy.js.map +0 -1
  134. package/dist/utils/deepEqual.d.ts +0 -53
  135. package/dist/utils/deepEqual.d.ts.map +0 -1
  136. package/dist/utils/deepEqual.js.map +0 -1
  137. package/dist/utils/export.d.ts +0 -10
  138. package/dist/utils/export.d.ts.map +0 -1
  139. package/dist/utils/export.js.map +0 -1
  140. package/dist/utils/isvalid.d.ts +0 -87
  141. package/dist/utils/isvalid.d.ts.map +0 -1
  142. package/dist/utils/isvalid.js.map +0 -1
  143. package/dist/utils/network.d.ts +0 -69
  144. package/dist/utils/network.d.ts.map +0 -1
  145. package/dist/utils/network.js.map +0 -1
  146. package/dist/utils/parameter.d.ts +0 -44
  147. package/dist/utils/parameter.d.ts.map +0 -1
  148. package/dist/utils/parameter.js.map +0 -1
  149. package/dist/utils/wait.d.ts +0 -43
  150. package/dist/utils/wait.d.ts.map +0 -1
  151. package/dist/utils/wait.js.map +0 -1
  152. /package/frontend/build/static/js/{main.e11d6bb4.js.LICENSE.txt → main.ff47208e.js.LICENSE.txt} +0 -0
@@ -1,35 +1,9 @@
1
- /**
2
- * This file contains the class Matterbridge.
3
- *
4
- * @file matterbridge.ts
5
- * @author Luca Liguori
6
- * @date 2023-12-29
7
- * @version 1.5.2
8
- *
9
- * Copyright 2023, 2024, 2025 Luca Liguori.
10
- *
11
- * Licensed under the Apache License, Version 2.0 (the "License");
12
- * you may not use this file except in compliance with the License.
13
- * You may obtain a copy of the License at
14
- *
15
- * http://www.apache.org/licenses/LICENSE-2.0
16
- *
17
- * Unless required by applicable law or agreed to in writing, software
18
- * distributed under the License is distributed on an "AS IS" BASIS,
19
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
- * See the License for the specific language governing permissions and
21
- * limitations under the License. *
22
- */
23
- // Node.js modules
24
1
  import os from 'node:os';
25
2
  import path from 'node:path';
26
3
  import { promises as fs } from 'node:fs';
27
4
  import EventEmitter from 'node:events';
28
- // AnsiLogger module
29
5
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from './logger/export.js';
30
- // NodeStorage module
31
6
  import { NodeStorageManager } from './storage/export.js';
32
- // Matterbridge
33
7
  import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout } from './utils/export.js';
34
8
  import { logInterfaces, getGlobalNodeModules } from './utils/network.js';
35
9
  import { PluginManager } from './pluginManager.js';
@@ -37,19 +11,14 @@ import { DeviceManager } from './deviceManager.js';
37
11
  import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
38
12
  import { bridge } from './matterbridgeDeviceTypes.js';
39
13
  import { Frontend } from './frontend.js';
40
- // @matter
41
14
  import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode } from '@matter/main';
42
15
  import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
43
16
  import { AggregatorEndpoint } from '@matter/main/endpoints';
44
17
  import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
45
18
  import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
46
- // Default colors
47
19
  const plg = '\u001B[38;5;33m';
48
20
  const dev = '\u001B[38;5;79m';
49
21
  const typ = '\u001B[38;5;207m';
50
- /**
51
- * Represents the Matterbridge application.
52
- */
53
22
  export class Matterbridge extends EventEmitter {
54
23
  systemInformation = {
55
24
  interfaceName: '',
@@ -94,7 +63,7 @@ export class Matterbridge extends EventEmitter {
94
63
  shellySysUpdate: false,
95
64
  shellyMainUpdate: false,
96
65
  profile: getParameter('profile'),
97
- loggerLevel: "info" /* LogLevel.INFO */,
66
+ loggerLevel: "info",
98
67
  fileLogger: false,
99
68
  matterLoggerLevel: MatterLogLevel.INFO,
100
69
  matterFileLogger: false,
@@ -132,11 +101,9 @@ export class Matterbridge extends EventEmitter {
132
101
  plugins;
133
102
  devices;
134
103
  frontend = new Frontend(this);
135
- // Matterbridge storage
136
104
  nodeStorage;
137
105
  nodeContext;
138
106
  nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
139
- // Cleanup
140
107
  hasCleanupStarted = false;
141
108
  initialized = false;
142
109
  execRunningCount = 0;
@@ -149,21 +116,18 @@ export class Matterbridge extends EventEmitter {
149
116
  sigtermHandler;
150
117
  exceptionHandler;
151
118
  rejectionHandler;
152
- // Matter environment
153
119
  environment = Environment.default;
154
- // Matter storage
155
120
  matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
156
121
  matterStorageService;
157
122
  matterStorageManager;
158
123
  matterbridgeContext;
159
124
  mattercontrollerContext;
160
- // Matter parameters
161
- mdnsInterface; // matter server node mdnsInterface: e.g. 'eth0' or 'wlan0' or 'WiFi'
162
- ipv4address; // matter server node listeningAddressIpv4
163
- ipv6address; // matter server node listeningAddressIpv6
164
- port; // first server node port
165
- passcode; // first server node passcode
166
- discriminator; // first server node discriminator
125
+ mdnsInterface;
126
+ ipv4address;
127
+ ipv6address;
128
+ port;
129
+ passcode;
130
+ discriminator;
167
131
  serverNode;
168
132
  aggregatorNode;
169
133
  aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
@@ -171,50 +135,21 @@ export class Matterbridge extends EventEmitter {
171
135
  aggregatorProductId = getIntParameter('productId') ?? 0x8000;
172
136
  aggregatorProductName = getParameter('productName') ?? 'Matterbridge aggregator';
173
137
  static instance;
174
- // We load asyncronously so is private
175
138
  constructor() {
176
139
  super();
177
140
  }
178
- /**
179
- * Emits an event of the specified type with the provided arguments.
180
- *
181
- * @template K - The type of the event.
182
- * @param {K} eventName - The name of the event to emit.
183
- * @param {...MatterbridgeEvent[K]} args - The arguments to pass to the event listeners.
184
- * @returns {boolean} - Returns true if the event had listeners, false otherwise.
185
- */
186
141
  emit(eventName, ...args) {
187
142
  return super.emit(eventName, ...args);
188
143
  }
189
- /**
190
- * Registers an event listener for the specified event type.
191
- *
192
- * @template K - The type of the event.
193
- * @param {K} eventName - The name of the event to listen for.
194
- * @param {(...args: MatterbridgeEvent[K]) => void} listener - The callback function to invoke when the event is emitted.
195
- * @returns {this} - Returns the instance of the Matterbridge class.
196
- */
197
144
  on(eventName, listener) {
198
145
  return super.on(eventName, listener);
199
146
  }
200
- /**
201
- * Retrieves the list of Matterbridge devices.
202
- * @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
203
- */
204
147
  getDevices() {
205
148
  return this.devices.array();
206
149
  }
207
- /**
208
- * Retrieves the list of registered plugins.
209
- * @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
210
- */
211
150
  getPlugins() {
212
151
  return this.plugins.array();
213
152
  }
214
- /**
215
- * Set the logger logLevel for the Matterbridge classes.
216
- * @param {LogLevel} logLevel The logger logLevel to set.
217
- */
218
153
  async setLogLevel(logLevel) {
219
154
  if (this.log)
220
155
  this.log.logLevel = logLevel;
@@ -228,31 +163,19 @@ export class Matterbridge extends EventEmitter {
228
163
  for (const plugin of this.plugins) {
229
164
  if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
230
165
  continue;
231
- plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel;
232
- await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel);
233
- }
234
- // Set the global logger callback for the WebSocketServer to the common minimum logLevel
235
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
236
- if (this.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
237
- callbackLogLevel = "info" /* LogLevel.INFO */;
238
- if (this.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
239
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
166
+ plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
167
+ await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
168
+ }
169
+ let callbackLogLevel = "notice";
170
+ if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
171
+ callbackLogLevel = "info";
172
+ if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
173
+ callbackLogLevel = "debug";
240
174
  AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
241
175
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
242
176
  }
243
- /** ***********************************************************************************************************************************/
244
- /** loadInstance() and cleanup() methods */
245
- /** ***********************************************************************************************************************************/
246
- /**
247
- * Loads an instance of the Matterbridge class.
248
- * If an instance already exists, return that instance.
249
- *
250
- * @param initialize - Whether to initialize the Matterbridge instance after loading.
251
- * @returns The loaded Matterbridge instance.
252
- */
253
177
  static async loadInstance(initialize = false) {
254
178
  if (!Matterbridge.instance) {
255
- // eslint-disable-next-line no-console
256
179
  if (hasParameter('debug'))
257
180
  console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
258
181
  Matterbridge.instance = new Matterbridge();
@@ -261,14 +184,8 @@ export class Matterbridge extends EventEmitter {
261
184
  }
262
185
  return Matterbridge.instance;
263
186
  }
264
- /**
265
- * Call cleanup().
266
- * @deprecated This method is deprecated and is only used for jest tests.
267
- *
268
- */
269
187
  async destroyInstance() {
270
188
  this.log.info(`Destroy instance...`);
271
- // Save server nodes to close
272
189
  const servers = [];
273
190
  if (this.bridgeMode === 'bridge') {
274
191
  if (this.serverNode)
@@ -280,81 +197,55 @@ export class Matterbridge extends EventEmitter {
280
197
  servers.push(plugin.serverNode);
281
198
  }
282
199
  }
283
- // Cleanup
284
200
  await this.cleanup('destroying instance...', false);
285
- // Close servers mdns service
286
201
  this.log.info(`Dispose ${servers.length} MdnsService...`);
287
202
  for (const server of servers) {
288
203
  await server.env.get(MdnsService)[Symbol.asyncDispose]();
289
204
  this.log.info(`Closed ${server.id} MdnsService`);
290
205
  }
291
- // Wait for the cleanup to finish
292
206
  await new Promise((resolve) => {
293
207
  setTimeout(resolve, 1000);
294
208
  });
295
209
  }
296
- /**
297
- * Initializes the Matterbridge application.
298
- *
299
- * @remarks
300
- * This method performs the necessary setup and initialization steps for the Matterbridge application.
301
- * It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
302
- * node version, registers signal handlers, initializes storage, and parses the command line.
303
- *
304
- * @returns A Promise that resolves when the initialization is complete.
305
- */
306
210
  async initialize() {
307
- // Set the restart mode
308
211
  if (hasParameter('service'))
309
212
  this.restartMode = 'service';
310
213
  if (hasParameter('docker'))
311
214
  this.restartMode = 'docker';
312
- // Set the matterbridge directory
313
215
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
314
216
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
315
- // Setup the matter environment
316
217
  this.environment.vars.set('log.level', MatterLogLevel.INFO);
317
218
  this.environment.vars.set('log.format', MatterLogFormat.ANSI);
318
219
  this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
319
220
  this.environment.vars.set('runtime.signals', false);
320
221
  this.environment.vars.set('runtime.exitcode', false);
321
- // Create the matterbridge logger
322
- this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
323
- // Register process handlers
222
+ this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
324
223
  this.registerProcessHandlers();
325
- // Initialize nodeStorage and nodeContext
326
224
  try {
327
225
  this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
328
226
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
329
227
  this.log.debug('Creating node storage context for matterbridge');
330
228
  this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
331
- // TODO: Remove this code when node-persist-manager is updated
332
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
333
229
  const keys = (await this.nodeStorage?.storage.keys());
334
230
  for (const key of keys) {
335
231
  this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
336
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
337
232
  await this.nodeStorage?.storage.get(key);
338
233
  }
339
234
  const storages = await this.nodeStorage.getStorageNames();
340
235
  for (const storage of storages) {
341
236
  this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
342
237
  const nodeContext = await this.nodeStorage?.createStorage(storage);
343
- // TODO: Remove this code when node-persist-manager is updated
344
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
345
238
  const keys = (await nodeContext?.storage.keys());
346
239
  keys.forEach(async (key) => {
347
240
  this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
348
241
  await nodeContext?.get(key);
349
242
  });
350
243
  }
351
- // Creating a backup of the node storage since it is not corrupted
352
244
  this.log.debug('Creating node storage backup...');
353
245
  await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
354
246
  this.log.debug('Created node storage backup');
355
247
  }
356
248
  catch (error) {
357
- // Restoring the backup of the node storage since it is corrupted
358
249
  this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
359
250
  if (hasParameter('norestore')) {
360
251
  this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
@@ -369,46 +260,41 @@ export class Matterbridge extends EventEmitter {
369
260
  this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
370
261
  throw new Error('Fatal error creating node storage manager and context for matterbridge');
371
262
  }
372
- // Set the first port to use for the commissioning server (will be incremented in childbridge mode)
373
263
  this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
374
- // Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
375
264
  this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
376
- // Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
377
265
  this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
378
266
  this.log.debug(`Initializing server node for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
379
- // Set matterbridge logger level (context: matterbridgeLogLevel)
380
267
  if (hasParameter('logger')) {
381
268
  const level = getParameter('logger');
382
269
  if (level === 'debug') {
383
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
270
+ this.log.logLevel = "debug";
384
271
  }
385
272
  else if (level === 'info') {
386
- this.log.logLevel = "info" /* LogLevel.INFO */;
273
+ this.log.logLevel = "info";
387
274
  }
388
275
  else if (level === 'notice') {
389
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
276
+ this.log.logLevel = "notice";
390
277
  }
391
278
  else if (level === 'warn') {
392
- this.log.logLevel = "warn" /* LogLevel.WARN */;
279
+ this.log.logLevel = "warn";
393
280
  }
394
281
  else if (level === 'error') {
395
- this.log.logLevel = "error" /* LogLevel.ERROR */;
282
+ this.log.logLevel = "error";
396
283
  }
397
284
  else if (level === 'fatal') {
398
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
285
+ this.log.logLevel = "fatal";
399
286
  }
400
287
  else {
401
288
  this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
402
- this.log.logLevel = "info" /* LogLevel.INFO */;
289
+ this.log.logLevel = "info";
403
290
  }
404
291
  }
405
292
  else {
406
- this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" /* LogLevel.NOTICE */ : "info" /* LogLevel.INFO */);
293
+ this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
407
294
  }
408
295
  this.frontend.logLevel = this.log.logLevel;
409
296
  MatterbridgeEndpoint.logLevel = this.log.logLevel;
410
297
  this.matterbridgeInformation.loggerLevel = this.log.logLevel;
411
- // Create the file logger for matterbridge (context: matterbridgeFileLog)
412
298
  if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
413
299
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
414
300
  this.matterbridgeInformation.fileLogger = true;
@@ -417,7 +303,6 @@ export class Matterbridge extends EventEmitter {
417
303
  this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
418
304
  if (this.profile !== undefined)
419
305
  this.log.debug(`Matterbridge profile: ${this.profile}.`);
420
- // Set matter.js logger level, format and logger (context: matterLogLevel)
421
306
  if (hasParameter('matterlogger')) {
422
307
  const level = getParameter('matterlogger');
423
308
  if (level === 'debug') {
@@ -449,7 +334,6 @@ export class Matterbridge extends EventEmitter {
449
334
  Logger.format = MatterLogFormat.ANSI;
450
335
  Logger.setLogger('default', this.createMatterLogger());
451
336
  this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
452
- // Create the file logger for matter.js (context: matterFileLog)
453
337
  if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
454
338
  this.matterbridgeInformation.matterFileLogger = true;
455
339
  Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
@@ -458,7 +342,6 @@ export class Matterbridge extends EventEmitter {
458
342
  });
459
343
  }
460
344
  this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
461
- // Log network interfaces
462
345
  const networkInterfaces = os.networkInterfaces();
463
346
  const availableAddresses = Object.entries(networkInterfaces);
464
347
  const availableInterfaces = Object.keys(networkInterfaces);
@@ -470,7 +353,6 @@ export class Matterbridge extends EventEmitter {
470
353
  });
471
354
  }
472
355
  }
473
- // Set the interface to use for matter server node mdnsInterface
474
356
  if (hasParameter('mdnsinterface')) {
475
357
  this.mdnsInterface = getParameter('mdnsinterface');
476
358
  }
@@ -479,7 +361,6 @@ export class Matterbridge extends EventEmitter {
479
361
  if (this.mdnsInterface === '')
480
362
  this.mdnsInterface = undefined;
481
363
  }
482
- // Validate mdnsInterface
483
364
  if (this.mdnsInterface) {
484
365
  if (!availableInterfaces.includes(this.mdnsInterface)) {
485
366
  this.log.error(`Invalid mdnsInterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
@@ -491,7 +372,6 @@ export class Matterbridge extends EventEmitter {
491
372
  }
492
373
  if (this.mdnsInterface)
493
374
  this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
494
- // Set the listeningAddressIpv4 for the matter commissioning server
495
375
  if (hasParameter('ipv4address')) {
496
376
  this.ipv4address = getParameter('ipv4address');
497
377
  }
@@ -500,7 +380,6 @@ export class Matterbridge extends EventEmitter {
500
380
  if (this.ipv4address === '')
501
381
  this.ipv4address = undefined;
502
382
  }
503
- // Validate ipv4address
504
383
  if (this.ipv4address) {
505
384
  let isValid = false;
506
385
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -515,7 +394,6 @@ export class Matterbridge extends EventEmitter {
515
394
  this.ipv4address = undefined;
516
395
  }
517
396
  }
518
- // Set the listeningAddressIpv6 for the matter commissioning server
519
397
  if (hasParameter('ipv6address')) {
520
398
  this.ipv6address = getParameter('ipv6address');
521
399
  }
@@ -524,7 +402,6 @@ export class Matterbridge extends EventEmitter {
524
402
  if (this.ipv6address === '')
525
403
  this.ipv6address = undefined;
526
404
  }
527
- // Validate ipv6address
528
405
  if (this.ipv6address) {
529
406
  let isValid = false;
530
407
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -544,19 +421,14 @@ export class Matterbridge extends EventEmitter {
544
421
  this.ipv6address = undefined;
545
422
  }
546
423
  }
547
- // Initialize PluginManager
548
424
  this.plugins = new PluginManager(this);
549
425
  await this.plugins.loadFromStorage();
550
426
  this.plugins.logLevel = this.log.logLevel;
551
- // Initialize DeviceManager
552
427
  this.devices = new DeviceManager(this, this.nodeContext);
553
428
  this.devices.logLevel = this.log.logLevel;
554
- // Get the plugins from node storage and create the plugins node storage contexts
555
429
  for (const plugin of this.plugins) {
556
430
  const packageJson = await this.plugins.parse(plugin);
557
431
  if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
558
- // Try to reinstall the plugin from npm (for Docker pull and external plugins)
559
- // We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
560
432
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
561
433
  try {
562
434
  await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
@@ -578,7 +450,6 @@ export class Matterbridge extends EventEmitter {
578
450
  await plugin.nodeContext.set('description', plugin.description);
579
451
  await plugin.nodeContext.set('author', plugin.author);
580
452
  }
581
- // Log system info and create .matterbridge directory
582
453
  await this.logNodeAndSystemInfo();
583
454
  this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
584
455
  `${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
@@ -586,7 +457,6 @@ export class Matterbridge extends EventEmitter {
586
457
  `${hasParameter('controller') ? 'mode controller ' : ''}` +
587
458
  `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
588
459
  `running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
589
- // Check node version and throw error
590
460
  const minNodeVersion = 18;
591
461
  const nodeVersion = process.versions.node;
592
462
  const versionMajor = parseInt(nodeVersion.split('.')[0]);
@@ -594,15 +464,9 @@ export class Matterbridge extends EventEmitter {
594
464
  this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
595
465
  throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
596
466
  }
597
- // Parse command line
598
467
  await this.parseCommandLine();
599
468
  this.initialized = true;
600
469
  }
601
- /**
602
- * Parses the command line arguments and performs the corresponding actions.
603
- * @private
604
- * @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
605
- */
606
470
  async parseCommandLine() {
607
471
  if (hasParameter('help')) {
608
472
  this.log.info(`\nUsage: matterbridge [options]\n
@@ -714,7 +578,6 @@ export class Matterbridge extends EventEmitter {
714
578
  this.shutdown = true;
715
579
  return;
716
580
  }
717
- // Start the matter storage and create the matterbridge context
718
581
  try {
719
582
  await this.startMatterStorage();
720
583
  }
@@ -722,14 +585,12 @@ export class Matterbridge extends EventEmitter {
722
585
  this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
723
586
  throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
724
587
  }
725
- // Clear the matterbridge context if the reset parameter is set
726
588
  if (hasParameter('reset') && getParameter('reset') === undefined) {
727
589
  this.initialized = true;
728
590
  await this.shutdownProcessAndReset();
729
591
  this.shutdown = true;
730
592
  return;
731
593
  }
732
- // Clear matterbridge plugin context if the reset parameter is set
733
594
  if (hasParameter('reset') && getParameter('reset') !== undefined) {
734
595
  this.log.debug(`Reset plugin ${getParameter('reset')}`);
735
596
  const plugin = this.plugins.get(getParameter('reset'));
@@ -754,37 +615,30 @@ export class Matterbridge extends EventEmitter {
754
615
  this.shutdown = true;
755
616
  return;
756
617
  }
757
- // Initialize frontend
758
618
  if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
759
619
  await this.frontend.start(getIntParameter('frontend'));
760
- // Check in 30 seconds the latest versions
761
620
  this.checkUpdateTimeout = setTimeout(async () => {
762
621
  const { checkUpdates } = await import('./update.js');
763
622
  checkUpdates(this);
764
623
  }, 30 * 1000).unref();
765
- // Check each 24 hours the latest versions
766
624
  this.checkUpdateInterval = setInterval(async () => {
767
625
  const { checkUpdates } = await import('./update.js');
768
626
  checkUpdates(this);
769
627
  }, 24 * 60 * 60 * 1000).unref();
770
- // Start the matterbridge in mode test
771
628
  if (hasParameter('test')) {
772
629
  this.bridgeMode = 'bridge';
773
630
  MatterbridgeEndpoint.bridgeMode = 'bridge';
774
631
  return;
775
632
  }
776
- // Start the matterbridge in mode controller
777
633
  if (hasParameter('controller')) {
778
634
  this.bridgeMode = 'controller';
779
635
  await this.startController();
780
636
  return;
781
637
  }
782
- // Check if the bridge mode is set and start matterbridge in bridge mode if not set
783
638
  if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
784
639
  this.log.info('Setting default matterbridge start mode to bridge');
785
640
  await this.nodeContext?.set('bridgeMode', 'bridge');
786
641
  }
787
- // Start matterbridge in bridge mode
788
642
  if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
789
643
  this.bridgeMode = 'bridge';
790
644
  MatterbridgeEndpoint.bridgeMode = 'bridge';
@@ -792,7 +646,6 @@ export class Matterbridge extends EventEmitter {
792
646
  await this.startBridge();
793
647
  return;
794
648
  }
795
- // Start matterbridge in childbridge mode
796
649
  if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
797
650
  this.bridgeMode = 'childbridge';
798
651
  MatterbridgeEndpoint.bridgeMode = 'childbridge';
@@ -801,20 +654,10 @@ export class Matterbridge extends EventEmitter {
801
654
  return;
802
655
  }
803
656
  }
804
- /**
805
- * Asynchronously loads and starts the registered plugins.
806
- *
807
- * This method is responsible for initializing and staarting all enabled plugins.
808
- * It ensures that each plugin is properly loaded and started before the bridge starts.
809
- *
810
- * @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
811
- */
812
657
  async startPlugins() {
813
- // Check, load and start the plugins
814
658
  for (const plugin of this.plugins) {
815
659
  plugin.configJson = await this.plugins.loadConfig(plugin);
816
660
  plugin.schemaJson = await this.plugins.loadSchema(plugin);
817
- // Check if the plugin is available
818
661
  if (!(await this.plugins.resolve(plugin.path))) {
819
662
  this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
820
663
  plugin.enabled = false;
@@ -834,26 +677,20 @@ export class Matterbridge extends EventEmitter {
834
677
  plugin.addedDevices = undefined;
835
678
  plugin.qrPairingCode = undefined;
836
679
  plugin.manualPairingCode = undefined;
837
- this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
680
+ this.plugins.load(plugin, true, 'Matterbridge is starting');
838
681
  }
839
682
  this.frontend.wssSendRefreshRequired('plugins');
840
683
  }
841
- /**
842
- * Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
843
- * When either of these signals are received, the cleanup method is called with an appropriate message.
844
- */
845
684
  registerProcessHandlers() {
846
685
  this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
847
686
  process.removeAllListeners('uncaughtException');
848
687
  process.removeAllListeners('unhandledRejection');
849
688
  this.exceptionHandler = async (error) => {
850
689
  this.log.error('Unhandled Exception detected at:', error.stack || error, rs);
851
- // await this.cleanup('Unhandled Exception detected, cleaning up...');
852
690
  };
853
691
  process.on('uncaughtException', this.exceptionHandler);
854
692
  this.rejectionHandler = async (reason, promise) => {
855
693
  this.log.error('Unhandled Rejection detected at:', promise, 'reason:', reason instanceof Error ? reason.stack : reason, rs);
856
- // await this.cleanup('Unhandled Rejection detected, cleaning up...');
857
694
  };
858
695
  process.on('unhandledRejection', this.rejectionHandler);
859
696
  this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
@@ -866,9 +703,6 @@ export class Matterbridge extends EventEmitter {
866
703
  };
867
704
  process.on('SIGTERM', this.sigtermHandler);
868
705
  }
869
- /**
870
- * Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
871
- */
872
706
  deregisterProcesslHandlers() {
873
707
  this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
874
708
  if (this.exceptionHandler)
@@ -885,17 +719,12 @@ export class Matterbridge extends EventEmitter {
885
719
  process.off('SIGTERM', this.sigtermHandler);
886
720
  this.sigtermHandler = undefined;
887
721
  }
888
- /**
889
- * Logs the node and system information.
890
- */
891
722
  async logNodeAndSystemInfo() {
892
- // IP address information
893
723
  const networkInterfaces = os.networkInterfaces();
894
724
  this.systemInformation.interfaceName = '';
895
725
  this.systemInformation.ipv4Address = '';
896
726
  this.systemInformation.ipv6Address = '';
897
727
  for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
898
- // this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
899
728
  if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
900
729
  continue;
901
730
  if (!interfaceDetails) {
@@ -921,22 +750,19 @@ export class Matterbridge extends EventEmitter {
921
750
  break;
922
751
  }
923
752
  }
924
- // Node information
925
753
  this.systemInformation.nodeVersion = process.versions.node;
926
754
  const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
927
755
  const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
928
756
  const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
929
- // Host system information
930
757
  this.systemInformation.hostname = os.hostname();
931
758
  this.systemInformation.user = os.userInfo().username;
932
- this.systemInformation.osType = os.type(); // "Windows_NT", "Darwin", etc.
933
- this.systemInformation.osRelease = os.release(); // Kernel version
934
- this.systemInformation.osPlatform = os.platform(); // "win32", "linux", "darwin", etc.
935
- this.systemInformation.osArch = os.arch(); // "x64", "arm", etc.
936
- this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
937
- this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
938
- this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours'; // Convert to hours
939
- // Log the system information
759
+ this.systemInformation.osType = os.type();
760
+ this.systemInformation.osRelease = os.release();
761
+ this.systemInformation.osPlatform = os.platform();
762
+ this.systemInformation.osArch = os.arch();
763
+ this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
764
+ this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
765
+ this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
940
766
  this.log.debug('Host System Information:');
941
767
  this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
942
768
  this.log.debug(`- User: ${this.systemInformation.user}`);
@@ -952,20 +778,16 @@ export class Matterbridge extends EventEmitter {
952
778
  this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
953
779
  this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
954
780
  this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
955
- // Home directory
956
781
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
957
782
  this.matterbridgeInformation.homeDirectory = this.homeDirectory;
958
783
  this.log.debug(`Home Directory: ${this.homeDirectory}`);
959
- // Package root directory
960
784
  const { fileURLToPath } = await import('node:url');
961
785
  const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
962
786
  this.rootDirectory = path.resolve(currentFileDirectory, '../');
963
787
  this.matterbridgeInformation.rootDirectory = this.rootDirectory;
964
788
  this.log.debug(`Root Directory: ${this.rootDirectory}`);
965
- // Global node_modules directory
966
789
  if (this.nodeContext)
967
790
  this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
968
- // First run of Matterbridge so the node storage is empty
969
791
  if (this.globalModulesDirectory === '') {
970
792
  try {
971
793
  this.execRunningCount++;
@@ -981,20 +803,6 @@ export class Matterbridge extends EventEmitter {
981
803
  }
982
804
  else
983
805
  this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
984
- /* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
985
- else {
986
- this.getGlobalNodeModules()
987
- .then(async (globalModulesDirectory) => {
988
- this.globalModulesDirectory = globalModulesDirectory;
989
- this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
990
- this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
991
- await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
992
- })
993
- .catch((error) => {
994
- this.log.error(`Error getting global node_modules directory: ${error}`);
995
- });
996
- }*/
997
- // Create the data directory .matterbridge in the home directory
998
806
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
999
807
  this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
1000
808
  try {
@@ -1018,7 +826,6 @@ export class Matterbridge extends EventEmitter {
1018
826
  }
1019
827
  }
1020
828
  this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
1021
- // Create the plugin directory Matterbridge in the home directory
1022
829
  this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
1023
830
  this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
1024
831
  try {
@@ -1042,7 +849,6 @@ export class Matterbridge extends EventEmitter {
1042
849
  }
1043
850
  }
1044
851
  this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
1045
- // Create the matter cert directory in the home directory
1046
852
  this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
1047
853
  this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
1048
854
  try {
@@ -1066,68 +872,50 @@ export class Matterbridge extends EventEmitter {
1066
872
  }
1067
873
  }
1068
874
  this.log.debug(`Matterbridge Matter Cert Directory: ${this.matterbridgeCertDirectory}`);
1069
- // Matterbridge version
1070
875
  const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
1071
876
  this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
1072
877
  this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
1073
878
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
1074
- // Matterbridge latest version
1075
879
  if (this.nodeContext)
1076
880
  this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
1077
881
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
1078
- // this.getMatterbridgeLatestVersion();
1079
- // Current working directory
1080
882
  const currentDir = process.cwd();
1081
883
  this.log.debug(`Current Working Directory: ${currentDir}`);
1082
- // Command line arguments (excluding 'node' and the script name)
1083
884
  const cmdArgs = process.argv.slice(2).join(' ');
1084
885
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
1085
886
  }
1086
- /**
1087
- * Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
1088
- *
1089
- * @returns {Function} The MatterLogger function.
1090
- */
1091
887
  createMatterLogger() {
1092
- const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
888
+ const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
1093
889
  return (_level, formattedLog) => {
1094
890
  const logger = formattedLog.slice(44, 44 + 20).trim();
1095
891
  const message = formattedLog.slice(65);
1096
892
  matterLogger.logName = logger;
1097
893
  switch (_level) {
1098
894
  case MatterLogLevel.DEBUG:
1099
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
895
+ matterLogger.log("debug", message);
1100
896
  break;
1101
897
  case MatterLogLevel.INFO:
1102
- matterLogger.log("info" /* LogLevel.INFO */, message);
898
+ matterLogger.log("info", message);
1103
899
  break;
1104
900
  case MatterLogLevel.NOTICE:
1105
- matterLogger.log("notice" /* LogLevel.NOTICE */, message);
901
+ matterLogger.log("notice", message);
1106
902
  break;
1107
903
  case MatterLogLevel.WARN:
1108
- matterLogger.log("warn" /* LogLevel.WARN */, message);
904
+ matterLogger.log("warn", message);
1109
905
  break;
1110
906
  case MatterLogLevel.ERROR:
1111
- matterLogger.log("error" /* LogLevel.ERROR */, message);
907
+ matterLogger.log("error", message);
1112
908
  break;
1113
909
  case MatterLogLevel.FATAL:
1114
- matterLogger.log("fatal" /* LogLevel.FATAL */, message);
910
+ matterLogger.log("fatal", message);
1115
911
  break;
1116
912
  default:
1117
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
913
+ matterLogger.log("debug", message);
1118
914
  break;
1119
915
  }
1120
916
  };
1121
917
  }
1122
- /**
1123
- * Creates a Matter File Logger.
1124
- *
1125
- * @param {string} filePath - The path to the log file.
1126
- * @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
1127
- * @returns {Function} - A function that logs formatted messages to the log file.
1128
- */
1129
918
  async createMatterFileLogger(filePath, unlink = false) {
1130
- // 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
1131
919
  let fileSize = 0;
1132
920
  if (unlink) {
1133
921
  try {
@@ -1176,21 +964,12 @@ export class Matterbridge extends EventEmitter {
1176
964
  }
1177
965
  };
1178
966
  }
1179
- /**
1180
- * Restarts the process by exiting the current instance and loading a new instance.
1181
- */
1182
967
  async restartProcess() {
1183
968
  await this.cleanup('restarting...', true);
1184
969
  }
1185
- /**
1186
- * Shut down the process by exiting the current process.
1187
- */
1188
970
  async shutdownProcess() {
1189
971
  await this.cleanup('shutting down...', false);
1190
972
  }
1191
- /**
1192
- * Update matterbridge and and shut down the process.
1193
- */
1194
973
  async updateProcess() {
1195
974
  this.log.info('Updating matterbridge...');
1196
975
  try {
@@ -1203,72 +982,51 @@ export class Matterbridge extends EventEmitter {
1203
982
  this.frontend.wssSendRestartRequired();
1204
983
  await this.cleanup('updating...', false);
1205
984
  }
1206
- /**
1207
- * Unregister all devices and shut down the process.
1208
- */
1209
985
  async unregisterAndShutdownProcess() {
1210
986
  this.log.info('Unregistering all devices and shutting down...');
1211
987
  for (const plugin of this.plugins) {
1212
988
  await this.removeAllBridgedEndpoints(plugin.name, 250);
1213
989
  }
1214
990
  this.log.debug('Waiting for the MessageExchange to finish...');
1215
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second for MessageExchange to finish
991
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1216
992
  this.log.debug('Cleaning up and shutting down...');
1217
993
  await this.cleanup('unregistered all devices and shutting down...', false);
1218
994
  }
1219
- /**
1220
- * Reset commissioning and shut down the process.
1221
- */
1222
995
  async shutdownProcessAndReset() {
1223
996
  await this.cleanup('shutting down with reset...', false);
1224
997
  }
1225
- /**
1226
- * Factory reset and shut down the process.
1227
- */
1228
998
  async shutdownProcessAndFactoryReset() {
1229
999
  await this.cleanup('shutting down with factory reset...', false);
1230
1000
  }
1231
- /**
1232
- * Cleans up the Matterbridge instance.
1233
- * @param message - The cleanup message.
1234
- * @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
1235
- * @returns A promise that resolves when the cleanup is completed.
1236
- */
1237
1001
  async cleanup(message, restart = false) {
1238
1002
  if (this.initialized && !this.hasCleanupStarted) {
1239
1003
  this.hasCleanupStarted = true;
1240
1004
  this.log.info(message);
1241
- // Clear the start matter interval
1242
1005
  if (this.startMatterInterval) {
1243
1006
  clearInterval(this.startMatterInterval);
1244
1007
  this.startMatterInterval = undefined;
1245
1008
  this.log.debug('Start matter interval cleared');
1246
1009
  }
1247
- // Clear the check update timeout
1248
1010
  if (this.checkUpdateTimeout) {
1249
1011
  clearInterval(this.checkUpdateTimeout);
1250
1012
  this.checkUpdateTimeout = undefined;
1251
1013
  this.log.debug('Check update timeout cleared');
1252
1014
  }
1253
- // Clear the check update interval
1254
1015
  if (this.checkUpdateInterval) {
1255
1016
  clearInterval(this.checkUpdateInterval);
1256
1017
  this.checkUpdateInterval = undefined;
1257
1018
  this.log.debug('Check update interval cleared');
1258
1019
  }
1259
- // Clear the configure timeout
1260
1020
  if (this.configureTimeout) {
1261
1021
  clearTimeout(this.configureTimeout);
1262
1022
  this.configureTimeout = undefined;
1263
1023
  this.log.debug('Matterbridge configure timeout cleared');
1264
1024
  }
1265
- // Clear the reachability timeout
1266
1025
  if (this.reachabilityTimeout) {
1267
1026
  clearTimeout(this.reachabilityTimeout);
1268
1027
  this.reachabilityTimeout = undefined;
1269
1028
  this.log.debug('Matterbridge reachability timeout cleared');
1270
1029
  }
1271
- // Calling the shutdown method of each plugin and clear the plugins reachability timeout
1272
1030
  for (const plugin of this.plugins) {
1273
1031
  if (!plugin.enabled || plugin.error)
1274
1032
  continue;
@@ -1279,10 +1037,9 @@ export class Matterbridge extends EventEmitter {
1279
1037
  this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
1280
1038
  }
1281
1039
  }
1282
- // Stopping matter server nodes
1283
1040
  this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
1284
1041
  this.log.debug('Waiting for the MessageExchange to finish...');
1285
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second for MessageExchange to finish
1042
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1286
1043
  if (this.bridgeMode === 'bridge') {
1287
1044
  if (this.serverNode) {
1288
1045
  await this.stopServerNode(this.serverNode);
@@ -1298,7 +1055,6 @@ export class Matterbridge extends EventEmitter {
1298
1055
  }
1299
1056
  }
1300
1057
  this.log.notice('Stopped matter server nodes');
1301
- // Matter commisioning reset
1302
1058
  if (message === 'shutting down with reset...') {
1303
1059
  this.log.info('Resetting Matterbridge commissioning information...');
1304
1060
  await this.matterStorageManager?.createContext('events')?.clearAll();
@@ -1308,37 +1064,17 @@ export class Matterbridge extends EventEmitter {
1308
1064
  await this.matterbridgeContext?.clearAll();
1309
1065
  this.log.info('Matter storage reset done! Remove the bridge from the controller.');
1310
1066
  }
1311
- // Stop matter storage
1312
1067
  await this.stopMatterStorage();
1313
- // Stop the frontend
1314
1068
  await this.frontend.stop();
1315
- // Remove the matterfilelogger
1316
1069
  try {
1317
1070
  Logger.removeLogger('matterfilelogger');
1318
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1319
1071
  }
1320
1072
  catch (error) {
1321
- // this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
1322
1073
  }
1323
- // Serialize registeredDevices
1324
1074
  if (this.nodeStorage && this.nodeContext) {
1325
- /*
1326
- TODO: Implement serialization of registered devices in edge mode
1327
- this.log.info('Saving registered devices...');
1328
- const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
1329
- this.devices.forEach(async (device) => {
1330
- const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
1331
- // this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
1332
- if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
1333
- });
1334
- await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
1335
- this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
1336
- */
1337
- // Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
1338
1075
  this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
1339
1076
  await this.nodeContext.close();
1340
1077
  this.nodeContext = undefined;
1341
- // Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
1342
1078
  for (const plugin of this.plugins) {
1343
1079
  if (plugin.nodeContext) {
1344
1080
  this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
@@ -1355,10 +1091,8 @@ export class Matterbridge extends EventEmitter {
1355
1091
  }
1356
1092
  this.plugins.clear();
1357
1093
  this.devices.clear();
1358
- // Factory reset
1359
1094
  if (message === 'shutting down with factory reset...') {
1360
1095
  try {
1361
- // Delete old matter storage file and backup
1362
1096
  const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
1363
1097
  this.log.info(`Unlinking old matter storage file: ${file}`);
1364
1098
  await fs.unlink(file);
@@ -1372,7 +1106,6 @@ export class Matterbridge extends EventEmitter {
1372
1106
  }
1373
1107
  }
1374
1108
  try {
1375
- // Delete matter node storage directory with its subdirectories and backup
1376
1109
  const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
1377
1110
  this.log.info(`Removing matter node storage directory: ${dir}`);
1378
1111
  await fs.rm(dir, { recursive: true });
@@ -1386,7 +1119,6 @@ export class Matterbridge extends EventEmitter {
1386
1119
  }
1387
1120
  }
1388
1121
  try {
1389
- // Delete node storage directory with its subdirectories and backup
1390
1122
  const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
1391
1123
  this.log.info(`Removing storage directory: ${dir}`);
1392
1124
  await fs.rm(dir, { recursive: true });
@@ -1401,13 +1133,12 @@ export class Matterbridge extends EventEmitter {
1401
1133
  }
1402
1134
  this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
1403
1135
  }
1404
- // Deregisters the process handlers
1405
1136
  this.deregisterProcesslHandlers();
1406
1137
  if (restart) {
1407
1138
  if (message === 'updating...') {
1408
1139
  this.log.info('Cleanup completed. Updating...');
1409
1140
  Matterbridge.instance = undefined;
1410
- this.emit('update'); // Restart the process but the update has been done before. TODO move all updates to the cli
1141
+ this.emit('update');
1411
1142
  }
1412
1143
  else if (message === 'restarting...') {
1413
1144
  this.log.info('Cleanup completed. Restarting...');
@@ -1427,14 +1158,6 @@ export class Matterbridge extends EventEmitter {
1427
1158
  this.log.debug('Cleanup already started...');
1428
1159
  }
1429
1160
  }
1430
- /**
1431
- * Creates and configures the server node for an accessory plugin for a given device.
1432
- *
1433
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1434
- * @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
1435
- * @param {boolean} [start=false] - Whether to start the server node after adding the device.
1436
- * @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
1437
- */
1438
1161
  async createAccessoryPlugin(plugin, device, start = false) {
1439
1162
  if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
1440
1163
  plugin.locked = true;
@@ -1447,13 +1170,6 @@ export class Matterbridge extends EventEmitter {
1447
1170
  await this.startServerNode(plugin.serverNode);
1448
1171
  }
1449
1172
  }
1450
- /**
1451
- * Creates and configures the server node for a dynamic plugin.
1452
- *
1453
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1454
- * @param {boolean} [start=false] - Whether to start the server node after adding the aggregator node.
1455
- * @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
1456
- */
1457
1173
  async createDynamicPlugin(plugin, start = false) {
1458
1174
  if (!plugin.locked) {
1459
1175
  plugin.locked = true;
@@ -1465,13 +1181,7 @@ export class Matterbridge extends EventEmitter {
1465
1181
  await this.startServerNode(plugin.serverNode);
1466
1182
  }
1467
1183
  }
1468
- /**
1469
- * Starts the Matterbridge in bridge mode.
1470
- * @private
1471
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1472
- */
1473
1184
  async startBridge() {
1474
- // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1475
1185
  if (!this.matterStorageManager)
1476
1186
  throw new Error('No storage manager initialized');
1477
1187
  if (!this.matterbridgeContext)
@@ -1509,9 +1219,7 @@ export class Matterbridge extends EventEmitter {
1509
1219
  clearInterval(this.startMatterInterval);
1510
1220
  this.startMatterInterval = undefined;
1511
1221
  this.log.debug('Cleared startMatterInterval interval for Matterbridge');
1512
- // Start the Matter server node
1513
1222
  this.startServerNode(this.serverNode);
1514
- // Configure the plugins
1515
1223
  this.configureTimeout = setTimeout(async () => {
1516
1224
  for (const plugin of this.plugins) {
1517
1225
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1529,7 +1237,6 @@ export class Matterbridge extends EventEmitter {
1529
1237
  }
1530
1238
  this.frontend.wssSendRefreshRequired('plugins');
1531
1239
  }, 30 * 1000);
1532
- // Setting reachability to true
1533
1240
  this.reachabilityTimeout = setTimeout(() => {
1534
1241
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1535
1242
  if (this.aggregatorNode)
@@ -1538,11 +1245,6 @@ export class Matterbridge extends EventEmitter {
1538
1245
  }, 60 * 1000);
1539
1246
  }, 1000);
1540
1247
  }
1541
- /**
1542
- * Starts the Matterbridge in childbridge mode.
1543
- * @private
1544
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1545
- */
1546
1248
  async startChildbridge() {
1547
1249
  if (!this.matterStorageManager)
1548
1250
  throw new Error('No storage manager initialized');
@@ -1587,7 +1289,6 @@ export class Matterbridge extends EventEmitter {
1587
1289
  clearInterval(this.startMatterInterval);
1588
1290
  this.startMatterInterval = undefined;
1589
1291
  this.log.debug('Cleared startMatterInterval interval in childbridge mode');
1590
- // Configure the plugins
1591
1292
  this.configureTimeout = setTimeout(async () => {
1592
1293
  for (const plugin of this.plugins) {
1593
1294
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1624,9 +1325,7 @@ export class Matterbridge extends EventEmitter {
1624
1325
  this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
1625
1326
  continue;
1626
1327
  }
1627
- // Start the Matter server node
1628
1328
  this.startServerNode(plugin.serverNode);
1629
- // Setting reachability to true
1630
1329
  plugin.reachabilityTimeout = setTimeout(() => {
1631
1330
  this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggragator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
1632
1331
  if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
@@ -1636,219 +1335,9 @@ export class Matterbridge extends EventEmitter {
1636
1335
  }
1637
1336
  }, 1000);
1638
1337
  }
1639
- /**
1640
- * Starts the Matterbridge controller.
1641
- * @private
1642
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1643
- */
1644
1338
  async startController() {
1645
- /*
1646
- if (!this.storageManager) {
1647
- this.log.error('No storage manager initialized');
1648
- await this.cleanup('No storage manager initialized');
1649
- return;
1650
- }
1651
- this.log.info('Creating context: mattercontrollerContext');
1652
- this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
1653
- if (!this.mattercontrollerContext) {
1654
- this.log.error('No storage context mattercontrollerContext initialized');
1655
- await this.cleanup('No storage context mattercontrollerContext initialized');
1656
- return;
1657
- }
1658
-
1659
- this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1660
- this.matterServer = await this.createMatterServer(this.storageManager);
1661
- this.log.info('Creating matter commissioning controller');
1662
- this.commissioningController = new CommissioningController({
1663
- autoConnect: false,
1664
- });
1665
- this.log.info('Adding matter commissioning controller to matter server');
1666
- await this.matterServer.addCommissioningController(this.commissioningController);
1667
-
1668
- this.log.info('Starting matter server');
1669
- await this.matterServer.start();
1670
- this.log.info('Matter server started');
1671
-
1672
- if (hasParameter('pairingcode')) {
1673
- this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
1674
- const pairingCode = getParameter('pairingcode');
1675
- const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get<string>('ip') : undefined;
1676
- const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get<number>('port') : undefined;
1677
-
1678
- let longDiscriminator, setupPin, shortDiscriminator;
1679
- if (pairingCode !== undefined) {
1680
- const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
1681
- shortDiscriminator = pairingCodeCodec.shortDiscriminator;
1682
- longDiscriminator = undefined;
1683
- setupPin = pairingCodeCodec.passcode;
1684
- this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
1685
- } else {
1686
- longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
1687
- if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
1688
- setupPin = this.mattercontrollerContext.get('pin', 20202021);
1689
- }
1690
- if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
1691
- throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
1692
- }
1693
-
1694
- const commissioningOptions: ControllerCommissioningFlowOptions = {
1695
- regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
1696
- regulatoryCountryCode: 'XX',
1697
- };
1698
- const options = {
1699
- commissioning: commissioningOptions,
1700
- discovery: {
1701
- knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
1702
- identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
1703
- },
1704
- passcode: setupPin,
1705
- } as NodeCommissioningOptions;
1706
- this.log.info('Commissioning with options:', options);
1707
- const nodeId = await this.commissioningController.commissionNode(options);
1708
- this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
1709
- this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
1710
- } // (hasParameter('pairingcode'))
1711
-
1712
- if (hasParameter('unpairall')) {
1713
- this.log.info('***Commissioning controller unpairing all nodes...');
1714
- const nodeIds = this.commissioningController.getCommissionedNodes();
1715
- for (const nodeId of nodeIds) {
1716
- this.log.info('***Commissioning controller unpairing node:', nodeId);
1717
- await this.commissioningController.removeNode(nodeId);
1718
- }
1719
- return;
1720
- }
1721
-
1722
- if (hasParameter('discover')) {
1723
- // const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1724
- // console.log(discover);
1725
- }
1726
-
1727
- if (!this.commissioningController.isCommissioned()) {
1728
- this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
1729
- return;
1730
- }
1731
-
1732
- const nodeIds = this.commissioningController.getCommissionedNodes();
1733
- this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
1734
- for (const nodeId of nodeIds) {
1735
- this.log.info(`***Connecting to commissioned node: ${nodeId}`);
1736
-
1737
- const node = await this.commissioningController.connectNode(nodeId, {
1738
- autoSubscribe: false,
1739
- attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
1740
- this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
1741
- eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
1742
- this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
1743
- stateInformationCallback: (peerNodeId, info) => {
1744
- switch (info) {
1745
- case NodeStateInformation.Connected:
1746
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
1747
- break;
1748
- case NodeStateInformation.Disconnected:
1749
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
1750
- break;
1751
- case NodeStateInformation.Reconnecting:
1752
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
1753
- break;
1754
- case NodeStateInformation.WaitingForDeviceDiscovery:
1755
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
1756
- break;
1757
- case NodeStateInformation.StructureChanged:
1758
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
1759
- break;
1760
- case NodeStateInformation.Decommissioned:
1761
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
1762
- break;
1763
- default:
1764
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
1765
- break;
1766
- }
1767
- },
1768
- });
1769
-
1770
- node.logStructure();
1771
-
1772
- // Get the interaction client
1773
- this.log.info('Getting the interaction client');
1774
- const interactionClient = await node.getInteractionClient();
1775
- let cluster;
1776
- let attributes;
1777
-
1778
- // Log BasicInformationCluster
1779
- cluster = BasicInformationCluster;
1780
- attributes = await interactionClient.getMultipleAttributes({
1781
- attributes: [{ clusterId: cluster.id }],
1782
- });
1783
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1784
- attributes.forEach((attribute) => {
1785
- this.log.info(
1786
- `- 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}`,
1787
- );
1788
- });
1789
-
1790
- // Log PowerSourceCluster
1791
- cluster = PowerSourceCluster;
1792
- attributes = await interactionClient.getMultipleAttributes({
1793
- attributes: [{ clusterId: cluster.id }],
1794
- });
1795
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1796
- attributes.forEach((attribute) => {
1797
- this.log.info(
1798
- `- 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}`,
1799
- );
1800
- });
1801
-
1802
- // Log ThreadNetworkDiagnostics
1803
- cluster = ThreadNetworkDiagnosticsCluster;
1804
- attributes = await interactionClient.getMultipleAttributes({
1805
- attributes: [{ clusterId: cluster.id }],
1806
- });
1807
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1808
- attributes.forEach((attribute) => {
1809
- this.log.info(
1810
- `- 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}`,
1811
- );
1812
- });
1813
-
1814
- // Log SwitchCluster
1815
- cluster = SwitchCluster;
1816
- attributes = await interactionClient.getMultipleAttributes({
1817
- attributes: [{ clusterId: cluster.id }],
1818
- });
1819
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1820
- attributes.forEach((attribute) => {
1821
- this.log.info(
1822
- `- 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}`,
1823
- );
1824
- });
1825
-
1826
- this.log.info('Subscribing to all attributes and events');
1827
- await node.subscribeAllAttributesAndEvents({
1828
- ignoreInitialTriggers: false,
1829
- attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
1830
- this.log.info(
1831
- `***${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}`,
1832
- ),
1833
- eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
1834
- this.log.info(
1835
- `***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
1836
- );
1837
- },
1838
- });
1839
- this.log.info('Subscribed to all attributes and events');
1840
- }
1841
- */
1842
1339
  }
1843
- /** ***********************************************************************************************************************************/
1844
- /** Matter.js methods */
1845
- /** ***********************************************************************************************************************************/
1846
- /**
1847
- * Starts the matter storage process with name Matterbridge.
1848
- * @returns {Promise<void>} - A promise that resolves when the storage process is started.
1849
- */
1850
1340
  async startMatterStorage() {
1851
- // Setup Matter storage
1852
1341
  this.log.info(`Starting matter node storage...`);
1853
1342
  this.matterStorageService = this.environment.get(StorageService);
1854
1343
  this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
@@ -1856,25 +1345,13 @@ export class Matterbridge extends EventEmitter {
1856
1345
  this.log.info('Matter node storage manager "Matterbridge" created');
1857
1346
  this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName);
1858
1347
  this.log.info('Matter node storage started');
1859
- // Backup matter storage since it is created/opened correctly
1860
1348
  await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
1861
1349
  }
1862
- /**
1863
- * Makes a backup copy of the specified matter storage directory.
1864
- *
1865
- * @param storageName - The name of the storage directory to be backed up.
1866
- * @param backupName - The name of the backup directory to be created.
1867
- * @returns {Promise<void>} A promise that resolves when the has been done.
1868
- */
1869
1350
  async backupMatterStorage(storageName, backupName) {
1870
1351
  this.log.info('Creating matter node storage backup...');
1871
1352
  await copyDirectory(storageName, backupName);
1872
1353
  this.log.info('Created matter node storage backup');
1873
1354
  }
1874
- /**
1875
- * Stops the matter storage.
1876
- * @returns {Promise<void>} A promise that resolves when the storage is stopped.
1877
- */
1878
1355
  async stopMatterStorage() {
1879
1356
  this.log.info('Closing matter node storage...');
1880
1357
  this.matterStorageManager?.close();
@@ -1883,19 +1360,6 @@ export class Matterbridge extends EventEmitter {
1883
1360
  this.matterbridgeContext = undefined;
1884
1361
  this.log.info('Matter node storage closed');
1885
1362
  }
1886
- /**
1887
- * Creates a server node storage context.
1888
- *
1889
- * @param {string} pluginName - The name of the plugin.
1890
- * @param {string} deviceName - The name of the device.
1891
- * @param {DeviceTypeId} deviceType - The device type of the device.
1892
- * @param {number} vendorId - The vendor ID.
1893
- * @param {string} vendorName - The vendor name.
1894
- * @param {number} productId - The product ID.
1895
- * @param {string} productName - The product name.
1896
- * @param {string} [serialNumber] - The serial number of the device (optional).
1897
- * @returns {Promise<StorageContext>} The storage context for the commissioning server.
1898
- */
1899
1363
  async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
1900
1364
  const { randomBytes } = await import('node:crypto');
1901
1365
  if (!this.matterStorageService)
@@ -1929,15 +1393,6 @@ export class Matterbridge extends EventEmitter {
1929
1393
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1930
1394
  return storageContext;
1931
1395
  }
1932
- /**
1933
- * Creates a server node.
1934
- *
1935
- * @param {StorageContext} storageContext - The storage context for the server node.
1936
- * @param {number} [port=5540] - The port number for the server node. Defaults to 5540.
1937
- * @param {number} [passcode=20242025] - The passcode for the server node. Defaults to 20242025.
1938
- * @param {number} [discriminator=3850] - The discriminator for the server node. Defaults to 3850.
1939
- * @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
1940
- */
1941
1396
  async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
1942
1397
  const storeId = await storageContext.get('storeId');
1943
1398
  this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
@@ -1947,33 +1402,21 @@ export class Matterbridge extends EventEmitter {
1947
1402
  this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
1948
1403
  this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1949
1404
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1950
- /**
1951
- * Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
1952
- */
1953
1405
  const serverNode = await ServerNode.create({
1954
- // Required: Give the Node a unique ID which is used to store the state of this node
1955
1406
  id: storeId,
1956
- // Provide Network relevant configuration like the port
1957
- // Optional when operating only one device on a host, Default port is 5540
1958
1407
  network: {
1959
1408
  listeningAddressIpv4: this.ipv4address,
1960
1409
  listeningAddressIpv6: this.ipv6address,
1961
1410
  port,
1962
1411
  },
1963
- // Provide Commissioning relevant settings
1964
- // Optional for development/testing purposes
1965
1412
  commissioning: {
1966
1413
  passcode,
1967
1414
  discriminator,
1968
1415
  },
1969
- // Provide Node announcement settings
1970
- // Optional: If Ommitted some development defaults are used
1971
1416
  productDescription: {
1972
1417
  name: await storageContext.get('deviceName'),
1973
1418
  deviceType: DeviceTypeId(await storageContext.get('deviceType')),
1974
1419
  },
1975
- // Provide defaults for the BasicInformation cluster on the Root endpoint
1976
- // Optional: If Omitted some development defaults are used
1977
1420
  basicInformation: {
1978
1421
  vendorId: VendorId(await storageContext.get('vendorId')),
1979
1422
  vendorName: await storageContext.get('vendorName'),
@@ -1990,13 +1433,12 @@ export class Matterbridge extends EventEmitter {
1990
1433
  },
1991
1434
  });
1992
1435
  const sanitizeFabrics = (fabrics, resetSessions = false) => {
1993
- // New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
1994
1436
  const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
1995
1437
  this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
1996
1438
  if (this.bridgeMode === 'bridge') {
1997
1439
  this.matterbridgeFabricInformations = sanitizedFabrics;
1998
1440
  if (resetSessions)
1999
- this.matterbridgeSessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
1441
+ this.matterbridgeSessionInformations = undefined;
2000
1442
  this.matterbridgePaired = true;
2001
1443
  }
2002
1444
  if (this.bridgeMode === 'childbridge') {
@@ -2004,19 +1446,13 @@ export class Matterbridge extends EventEmitter {
2004
1446
  if (plugin) {
2005
1447
  plugin.fabricInformations = sanitizedFabrics;
2006
1448
  if (resetSessions)
2007
- plugin.sessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
1449
+ plugin.sessionInformations = undefined;
2008
1450
  plugin.paired = true;
2009
1451
  }
2010
1452
  }
2011
1453
  };
2012
- /**
2013
- * This event is triggered when the device is initially commissioned successfully.
2014
- * This means: It is added to the first fabric.
2015
- */
2016
1454
  serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
2017
- /** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
2018
1455
  serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
2019
- /** This event is triggered when the device went online. This means that it is discoverable in the network. */
2020
1456
  serverNode.lifecycle.online.on(async () => {
2021
1457
  this.log.notice(`Server node for ${storeId} is online`);
2022
1458
  if (!serverNode.lifecycle.isCommissioned) {
@@ -2056,6 +1492,8 @@ export class Matterbridge extends EventEmitter {
2056
1492
  }
2057
1493
  }
2058
1494
  setTimeout(() => {
1495
+ if (serverNode.lifecycle.isCommissioned)
1496
+ return;
2059
1497
  if (this.bridgeMode === 'bridge') {
2060
1498
  this.matterbridgeQrPairingCode = undefined;
2061
1499
  this.matterbridgeManualPairingCode = undefined;
@@ -2069,6 +1507,9 @@ export class Matterbridge extends EventEmitter {
2069
1507
  }
2070
1508
  }
2071
1509
  this.frontend.wssSendRefreshRequired('settings');
1510
+ this.frontend.wssSendRefreshRequired('plugins');
1511
+ this.frontend.wssSendRefreshRequired('fabrics');
1512
+ this.frontend.wssSendRefreshRequired('sessions');
2072
1513
  this.frontend.wssSendSnackbarMessage(`Advertising on server node for ${storeId} stopped. Restart to commission.`, 0);
2073
1514
  this.log.notice(`Advertising on server node for ${storeId} stopped. Restart to commission.`);
2074
1515
  }, 15 * 60 * 1000).unref();
@@ -2081,7 +1522,6 @@ export class Matterbridge extends EventEmitter {
2081
1522
  this.frontend.wssSendRefreshRequired('settings');
2082
1523
  this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
2083
1524
  });
2084
- /** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
2085
1525
  serverNode.lifecycle.offline.on(() => {
2086
1526
  this.log.notice(`Server node for ${storeId} is offline`);
2087
1527
  if (this.bridgeMode === 'bridge') {
@@ -2105,10 +1545,6 @@ export class Matterbridge extends EventEmitter {
2105
1545
  this.frontend.wssSendRefreshRequired('settings');
2106
1546
  this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
2107
1547
  });
2108
- /**
2109
- * This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
2110
- * information is needed.
2111
- */
2112
1548
  serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
2113
1549
  let action = '';
2114
1550
  switch (fabricAction) {
@@ -2142,24 +1578,16 @@ export class Matterbridge extends EventEmitter {
2142
1578
  }
2143
1579
  }
2144
1580
  };
2145
- /**
2146
- * This event is triggered when an operative new session was opened by a Controller.
2147
- * It is not triggered for the initial commissioning process, just afterwards for real connections.
2148
- */
2149
1581
  serverNode.events.sessions.opened.on((session) => {
2150
1582
  this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
2151
1583
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2152
1584
  this.frontend.wssSendRefreshRequired('sessions');
2153
1585
  });
2154
- /**
2155
- * This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
2156
- */
2157
1586
  serverNode.events.sessions.closed.on((session) => {
2158
1587
  this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
2159
1588
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2160
1589
  this.frontend.wssSendRefreshRequired('sessions');
2161
1590
  });
2162
- /** This event is triggered when a subscription gets added or removed on an operative session. */
2163
1591
  serverNode.events.sessions.subscriptionsChanged.on((session) => {
2164
1592
  this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
2165
1593
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
@@ -2168,42 +1596,24 @@ export class Matterbridge extends EventEmitter {
2168
1596
  this.log.info(`Created server node for ${storeId}`);
2169
1597
  return serverNode;
2170
1598
  }
2171
- /**
2172
- * Starts the specified server node.
2173
- *
2174
- * @param {ServerNode} [matterServerNode] - The server node to start.
2175
- * @returns {Promise<void>} A promise that resolves when the server node has started.
2176
- */
2177
1599
  async startServerNode(matterServerNode) {
2178
1600
  if (!matterServerNode)
2179
1601
  return;
2180
1602
  this.log.notice(`Starting ${matterServerNode.id} server node`);
2181
1603
  await matterServerNode.start();
2182
1604
  }
2183
- /**
2184
- * Stops the specified server node.
2185
- *
2186
- * @param {ServerNode} matterServerNode - The server node to stop.
2187
- * @returns {Promise<void>} A promise that resolves when the server node has stopped.
2188
- */
2189
1605
  async stopServerNode(matterServerNode) {
2190
1606
  if (!matterServerNode)
2191
1607
  return;
2192
1608
  this.log.notice(`Closing ${matterServerNode.id} server node`);
2193
1609
  try {
2194
- await withTimeout(matterServerNode.close(), 30000); // 30 seconds timeout to allow slow devices to close gracefully
1610
+ await withTimeout(matterServerNode.close(), 30000);
2195
1611
  this.log.info(`Closed ${matterServerNode.id} server node`);
2196
1612
  }
2197
1613
  catch (error) {
2198
1614
  this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
2199
1615
  }
2200
1616
  }
2201
- /**
2202
- * Advertises the specified server node.
2203
- *
2204
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2205
- * @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.
2206
- */
2207
1617
  async advertiseServerNode(matterServerNode) {
2208
1618
  if (matterServerNode) {
2209
1619
  await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
@@ -2212,44 +1622,23 @@ export class Matterbridge extends EventEmitter {
2212
1622
  return { qrPairingCode, manualPairingCode };
2213
1623
  }
2214
1624
  }
2215
- /**
2216
- * Stop advertise the specified server node.
2217
- *
2218
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2219
- * @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
2220
- */
2221
1625
  async stopAdvertiseServerNode(matterServerNode) {
2222
1626
  if (matterServerNode && matterServerNode.lifecycle.isOnline) {
2223
1627
  await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
2224
1628
  this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
2225
1629
  }
2226
1630
  }
2227
- /**
2228
- * Creates an aggregator node with the specified storage context.
2229
- *
2230
- * @param {StorageContext} storageContext - The storage context for the aggregator node.
2231
- * @returns {Promise<EndpointNode<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
2232
- */
2233
1631
  async createAggregatorNode(storageContext) {
2234
1632
  this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
2235
1633
  const aggregatorNode = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
2236
1634
  return aggregatorNode;
2237
1635
  }
2238
- /**
2239
- * Adds a MatterbridgeEndpoint to the specified plugin.
2240
- *
2241
- * @param {string} pluginName - The name of the plugin.
2242
- * @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
2243
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
2244
- */
2245
1636
  async addBridgedEndpoint(pluginName, device) {
2246
- // Check if the plugin is registered
2247
1637
  const plugin = this.plugins.get(pluginName);
2248
1638
  if (!plugin) {
2249
1639
  this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
2250
1640
  return;
2251
1641
  }
2252
- // Register and add the device to the matterbridge aggregator node
2253
1642
  if (this.bridgeMode === 'bridge') {
2254
1643
  this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
2255
1644
  if (!this.aggregatorNode)
@@ -2272,28 +1661,17 @@ export class Matterbridge extends EventEmitter {
2272
1661
  plugin.registeredDevices++;
2273
1662
  if (plugin.addedDevices !== undefined)
2274
1663
  plugin.addedDevices++;
2275
- // Add the device to the DeviceManager
2276
1664
  this.devices.set(device);
2277
- // Subscribe to the reachable$Changed event
2278
1665
  await this.subscribeAttributeChanged(plugin, device);
2279
1666
  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}`);
2280
1667
  }
2281
- /**
2282
- * Removes a MatterbridgeEndpoint from the specified plugin.
2283
- *
2284
- * @param {string} pluginName - The name of the plugin.
2285
- * @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
2286
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
2287
- */
2288
1668
  async removeBridgedEndpoint(pluginName, device) {
2289
1669
  this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
2290
- // Check if the plugin is registered
2291
1670
  const plugin = this.plugins.get(pluginName);
2292
1671
  if (!plugin) {
2293
1672
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
2294
1673
  return;
2295
1674
  }
2296
- // Register and add the device to the matterbridge aggregator node
2297
1675
  if (this.bridgeMode === 'bridge') {
2298
1676
  if (!this.aggregatorNode) {
2299
1677
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
@@ -2308,7 +1686,6 @@ export class Matterbridge extends EventEmitter {
2308
1686
  }
2309
1687
  else if (this.bridgeMode === 'childbridge') {
2310
1688
  if (plugin.type === 'AccessoryPlatform') {
2311
- // Nothing to do here since the server node has no aggregator node but only the device itself
2312
1689
  }
2313
1690
  else if (plugin.type === 'DynamicPlatform') {
2314
1691
  if (!plugin.aggregatorNode) {
@@ -2323,16 +1700,8 @@ export class Matterbridge extends EventEmitter {
2323
1700
  if (plugin.addedDevices !== undefined)
2324
1701
  plugin.addedDevices--;
2325
1702
  }
2326
- // Remove the device from the DeviceManager
2327
1703
  this.devices.remove(device);
2328
1704
  }
2329
- /**
2330
- * Removes all bridged endpoints from the specified plugin.
2331
- *
2332
- * @param {string} pluginName - The name of the plugin.
2333
- * @param {number} [delay=0] - The delay in milliseconds between removing each bridged endpoint (default: 0).
2334
- * @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
2335
- */
2336
1705
  async removeAllBridgedEndpoints(pluginName, delay = 0) {
2337
1706
  this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}`);
2338
1707
  for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
@@ -2341,25 +1710,9 @@ export class Matterbridge extends EventEmitter {
2341
1710
  await new Promise((resolve) => setTimeout(resolve, delay));
2342
1711
  }
2343
1712
  }
2344
- /**
2345
- * Subscribes to the attribute change event for the given device and plugin.
2346
- * Specifically, it listens for changes in the 'reachable' attribute of the
2347
- * BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
2348
- *
2349
- * @param {RegisteredPlugin} plugin - The plugin associated with the device.
2350
- * @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
2351
- * @returns {Promise<void>} A promise that resolves when the subscription is set up.
2352
- */
2353
1713
  async subscribeAttributeChanged(plugin, device) {
2354
1714
  this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
2355
1715
  if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
2356
- /*
2357
- this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) subscribed to reachable$Changed`);
2358
- setTimeout(async () => {
2359
- this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) changed to reachable false`);
2360
- await plugin.serverNode?.setStateOf(BasicInformationServer, { reachable: false });
2361
- }, 60000).unref();
2362
- */
2363
1716
  plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
2364
1717
  this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
2365
1718
  this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, 'BasicInformationServer', 'reachable', reachable);
@@ -2372,12 +1725,6 @@ export class Matterbridge extends EventEmitter {
2372
1725
  });
2373
1726
  }
2374
1727
  }
2375
- /**
2376
- * Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
2377
- *
2378
- * @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
2379
- * @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
2380
- */
2381
1728
  sanitizeFabricInformations(fabricInfo) {
2382
1729
  return fabricInfo.map((info) => {
2383
1730
  return {
@@ -2391,12 +1738,6 @@ export class Matterbridge extends EventEmitter {
2391
1738
  };
2392
1739
  });
2393
1740
  }
2394
- /**
2395
- * Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
2396
- *
2397
- * @param {SessionInformation[]} sessionInfo - The array of session information objects.
2398
- * @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
2399
- */
2400
1741
  sanitizeSessionInformation(sessionInfo) {
2401
1742
  return sessionInfo
2402
1743
  .filter((session) => session.isPeerActive)
@@ -2424,20 +1765,7 @@ export class Matterbridge extends EventEmitter {
2424
1765
  };
2425
1766
  });
2426
1767
  }
2427
- /**
2428
- * Sets the reachability of the specified aggregator node bridged devices and trigger.
2429
- * @param {EndpointNode<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
2430
- * @param {boolean} reachable - A boolean indicating the reachability status to set.
2431
- */
2432
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2433
1768
  async setAggregatorReachability(aggregatorNode, reachable) {
2434
- /*
2435
- for (const child of aggregatorNode.parts) {
2436
- this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
2437
- await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
2438
- child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
2439
- }
2440
- */
2441
1769
  }
2442
1770
  getVendorIdName = (vendorId) => {
2443
1771
  if (!vendorId)
@@ -2480,37 +1808,14 @@ export class Matterbridge extends EventEmitter {
2480
1808
  }
2481
1809
  return vendorName;
2482
1810
  };
2483
- /**
2484
- * Spawns a child process with the given command and arguments.
2485
- * @param {string} command - The command to execute.
2486
- * @param {string[]} args - The arguments to pass to the command (default: []).
2487
- * @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
2488
- */
2489
1811
  async spawnCommand(command, args = []) {
2490
1812
  const { spawn } = await import('node:child_process');
2491
- /*
2492
- npm > npm.cmd on windows
2493
- cmd.exe ['dir'] on windows
2494
- await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
2495
- process.on('unhandledRejection', (reason, promise) => {
2496
- this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
2497
- });
2498
-
2499
- spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
2500
- spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
2501
- debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
2502
- debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
2503
- */
2504
1813
  const cmdLine = command + ' ' + args.join(' ');
2505
1814
  if (process.platform === 'win32' && command === 'npm') {
2506
- // Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
2507
1815
  const argstring = 'npm ' + args.join(' ');
2508
1816
  args.splice(0, args.length, '/c', argstring);
2509
1817
  command = 'cmd.exe';
2510
1818
  }
2511
- // Decide when using sudo on linux
2512
- // When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
2513
- // When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
2514
1819
  if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
2515
1820
  args.unshift(command);
2516
1821
  command = 'sudo';
@@ -2569,4 +1874,3 @@ export class Matterbridge extends EventEmitter {
2569
1874
  });
2570
1875
  }
2571
1876
  }
2572
- //# sourceMappingURL=matterbridge.js.map