matterbridge 2.2.9 → 3.0.0-edge.10

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 (147) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/cli.js +6 -38
  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 +20 -328
  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 +104 -761
  16. package/dist/matterbridgeAccessoryPlatform.js +0 -33
  17. package/dist/matterbridgeBehaviors.js +46 -72
  18. package/dist/matterbridgeDeviceTypes.js +249 -233
  19. package/dist/matterbridgeDynamicPlatform.js +0 -33
  20. package/dist/matterbridgeEndpoint.js +85 -759
  21. package/dist/matterbridgeEndpointHelpers.js +30 -136
  22. package/dist/matterbridgePlatform.js +9 -218
  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/npm-shrinkwrap.json +289 -351
  39. package/package.json +3 -4
  40. package/dist/cli.d.ts +0 -29
  41. package/dist/cli.d.ts.map +0 -1
  42. package/dist/cli.js.map +0 -1
  43. package/dist/cluster/export.d.ts +0 -2
  44. package/dist/cluster/export.d.ts.map +0 -1
  45. package/dist/cluster/export.js.map +0 -1
  46. package/dist/defaultConfigSchema.d.ts +0 -27
  47. package/dist/defaultConfigSchema.d.ts.map +0 -1
  48. package/dist/defaultConfigSchema.js.map +0 -1
  49. package/dist/deviceManager.d.ts +0 -114
  50. package/dist/deviceManager.d.ts.map +0 -1
  51. package/dist/deviceManager.js.map +0 -1
  52. package/dist/frontend.d.ts +0 -221
  53. package/dist/frontend.d.ts.map +0 -1
  54. package/dist/frontend.js.map +0 -1
  55. package/dist/index.d.ts +0 -35
  56. package/dist/index.d.ts.map +0 -1
  57. package/dist/index.js.map +0 -1
  58. package/dist/logger/export.d.ts +0 -2
  59. package/dist/logger/export.d.ts.map +0 -1
  60. package/dist/logger/export.js.map +0 -1
  61. package/dist/matter/behaviors.d.ts +0 -2
  62. package/dist/matter/behaviors.d.ts.map +0 -1
  63. package/dist/matter/behaviors.js.map +0 -1
  64. package/dist/matter/clusters.d.ts +0 -2
  65. package/dist/matter/clusters.d.ts.map +0 -1
  66. package/dist/matter/clusters.js.map +0 -1
  67. package/dist/matter/devices.d.ts +0 -2
  68. package/dist/matter/devices.d.ts.map +0 -1
  69. package/dist/matter/devices.js.map +0 -1
  70. package/dist/matter/endpoints.d.ts +0 -2
  71. package/dist/matter/endpoints.d.ts.map +0 -1
  72. package/dist/matter/endpoints.js.map +0 -1
  73. package/dist/matter/export.d.ts +0 -5
  74. package/dist/matter/export.d.ts.map +0 -1
  75. package/dist/matter/export.js.map +0 -1
  76. package/dist/matter/types.d.ts +0 -3
  77. package/dist/matter/types.d.ts.map +0 -1
  78. package/dist/matter/types.js.map +0 -1
  79. package/dist/matterbridge.d.ts +0 -425
  80. package/dist/matterbridge.d.ts.map +0 -1
  81. package/dist/matterbridge.js.map +0 -1
  82. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
  83. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  84. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  85. package/dist/matterbridgeBehaviors.d.ts +0 -1056
  86. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  87. package/dist/matterbridgeBehaviors.js.map +0 -1
  88. package/dist/matterbridgeDeviceTypes.d.ts +0 -178
  89. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  90. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  91. package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
  92. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  93. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  94. package/dist/matterbridgeEndpoint.d.ts +0 -867
  95. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  96. package/dist/matterbridgeEndpoint.js.map +0 -1
  97. package/dist/matterbridgeEndpointHelpers.d.ts +0 -2275
  98. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  99. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  100. package/dist/matterbridgePlatform.d.ts +0 -285
  101. package/dist/matterbridgePlatform.d.ts.map +0 -1
  102. package/dist/matterbridgePlatform.js.map +0 -1
  103. package/dist/matterbridgeTypes.d.ts +0 -183
  104. package/dist/matterbridgeTypes.d.ts.map +0 -1
  105. package/dist/matterbridgeTypes.js.map +0 -1
  106. package/dist/pluginManager.d.ts +0 -271
  107. package/dist/pluginManager.d.ts.map +0 -1
  108. package/dist/pluginManager.js.map +0 -1
  109. package/dist/shelly.d.ts +0 -92
  110. package/dist/shelly.d.ts.map +0 -1
  111. package/dist/shelly.js.map +0 -1
  112. package/dist/storage/export.d.ts +0 -2
  113. package/dist/storage/export.d.ts.map +0 -1
  114. package/dist/storage/export.js.map +0 -1
  115. package/dist/update.d.ts +0 -32
  116. package/dist/update.d.ts.map +0 -1
  117. package/dist/update.js.map +0 -1
  118. package/dist/utils/colorUtils.d.ts +0 -61
  119. package/dist/utils/colorUtils.d.ts.map +0 -1
  120. package/dist/utils/colorUtils.js.map +0 -1
  121. package/dist/utils/copyDirectory.d.ts +0 -32
  122. package/dist/utils/copyDirectory.d.ts.map +0 -1
  123. package/dist/utils/copyDirectory.js.map +0 -1
  124. package/dist/utils/createZip.d.ts +0 -38
  125. package/dist/utils/createZip.d.ts.map +0 -1
  126. package/dist/utils/createZip.js.map +0 -1
  127. package/dist/utils/deepCopy.d.ts +0 -31
  128. package/dist/utils/deepCopy.d.ts.map +0 -1
  129. package/dist/utils/deepCopy.js.map +0 -1
  130. package/dist/utils/deepEqual.d.ts +0 -53
  131. package/dist/utils/deepEqual.d.ts.map +0 -1
  132. package/dist/utils/deepEqual.js.map +0 -1
  133. package/dist/utils/export.d.ts +0 -10
  134. package/dist/utils/export.d.ts.map +0 -1
  135. package/dist/utils/export.js.map +0 -1
  136. package/dist/utils/isvalid.d.ts +0 -87
  137. package/dist/utils/isvalid.d.ts.map +0 -1
  138. package/dist/utils/isvalid.js.map +0 -1
  139. package/dist/utils/network.d.ts +0 -69
  140. package/dist/utils/network.d.ts.map +0 -1
  141. package/dist/utils/network.js.map +0 -1
  142. package/dist/utils/parameter.d.ts +0 -44
  143. package/dist/utils/parameter.d.ts.map +0 -1
  144. package/dist/utils/parameter.js.map +0 -1
  145. package/dist/utils/wait.d.ts +0 -43
  146. package/dist/utils/wait.d.ts.map +0 -1
  147. package/dist/utils/wait.js.map +0 -1
@@ -1,35 +1,10 @@
1
- /**
2
- * This file contains the class Matterbridge.
3
- *
4
- * @file matterbridge.ts
5
- * @author Luca Liguori
6
- * @date 2023-12-29
7
- * @version 1.5.2
8
- *
9
- * Copyright 2023, 2024, 2025 Luca Liguori.
10
- *
11
- * Licensed under the Apache License, Version 2.0 (the "License");
12
- * you may not use this file except in compliance with the License.
13
- * You may obtain a copy of the License at
14
- *
15
- * http://www.apache.org/licenses/LICENSE-2.0
16
- *
17
- * Unless required by applicable law or agreed to in writing, software
18
- * distributed under the License is distributed on an "AS IS" BASIS,
19
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
- * See the License for the specific language governing permissions and
21
- * limitations under the License. *
22
- */
23
- // Node.js modules
24
1
  import os from 'node:os';
25
2
  import path from 'node:path';
26
3
  import { promises as fs } from 'node:fs';
27
4
  import EventEmitter from 'node:events';
28
- // AnsiLogger module
5
+ import { inspect } from 'node:util';
29
6
  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
7
  import { NodeStorageManager } from './storage/export.js';
32
- // Matterbridge
33
8
  import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout } from './utils/export.js';
34
9
  import { logInterfaces, getGlobalNodeModules } from './utils/network.js';
35
10
  import { PluginManager } from './pluginManager.js';
@@ -37,19 +12,14 @@ import { DeviceManager } from './deviceManager.js';
37
12
  import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
38
13
  import { bridge } from './matterbridgeDeviceTypes.js';
39
14
  import { Frontend } from './frontend.js';
40
- // @matter
41
15
  import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode } from '@matter/main';
42
16
  import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
43
17
  import { AggregatorEndpoint } from '@matter/main/endpoints';
44
- import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
45
18
  import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
46
- // Default colors
19
+ import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
47
20
  const plg = '\u001B[38;5;33m';
48
21
  const dev = '\u001B[38;5;79m';
49
22
  const typ = '\u001B[38;5;207m';
50
- /**
51
- * Represents the Matterbridge application.
52
- */
53
23
  export class Matterbridge extends EventEmitter {
54
24
  systemInformation = {
55
25
  interfaceName: '',
@@ -94,7 +64,7 @@ export class Matterbridge extends EventEmitter {
94
64
  shellySysUpdate: false,
95
65
  shellyMainUpdate: false,
96
66
  profile: getParameter('profile'),
97
- loggerLevel: "info" /* LogLevel.INFO */,
67
+ loggerLevel: "info",
98
68
  fileLogger: false,
99
69
  matterLoggerLevel: MatterLogLevel.INFO,
100
70
  matterFileLogger: false,
@@ -132,11 +102,9 @@ export class Matterbridge extends EventEmitter {
132
102
  plugins;
133
103
  devices;
134
104
  frontend = new Frontend(this);
135
- // Matterbridge storage
136
105
  nodeStorage;
137
106
  nodeContext;
138
107
  nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
139
- // Cleanup
140
108
  hasCleanupStarted = false;
141
109
  initialized = false;
142
110
  execRunningCount = 0;
@@ -149,21 +117,18 @@ export class Matterbridge extends EventEmitter {
149
117
  sigtermHandler;
150
118
  exceptionHandler;
151
119
  rejectionHandler;
152
- // Matter environment
153
120
  environment = Environment.default;
154
- // Matter storage
155
121
  matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
156
122
  matterStorageService;
157
123
  matterStorageManager;
158
124
  matterbridgeContext;
159
- 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
+ controllerContext;
126
+ mdnsInterface;
127
+ ipv4address;
128
+ ipv6address;
129
+ port;
130
+ passcode;
131
+ discriminator;
167
132
  serverNode;
168
133
  aggregatorNode;
169
134
  aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
@@ -171,50 +136,21 @@ export class Matterbridge extends EventEmitter {
171
136
  aggregatorProductId = getIntParameter('productId') ?? 0x8000;
172
137
  aggregatorProductName = getParameter('productName') ?? 'Matterbridge aggregator';
173
138
  static instance;
174
- // We load asyncronously so is private
175
139
  constructor() {
176
140
  super();
177
141
  }
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
142
  emit(eventName, ...args) {
187
143
  return super.emit(eventName, ...args);
188
144
  }
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
145
  on(eventName, listener) {
198
146
  return super.on(eventName, listener);
199
147
  }
200
- /**
201
- * Retrieves the list of Matterbridge devices.
202
- * @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
203
- */
204
148
  getDevices() {
205
149
  return this.devices.array();
206
150
  }
207
- /**
208
- * Retrieves the list of registered plugins.
209
- * @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
210
- */
211
151
  getPlugins() {
212
152
  return this.plugins.array();
213
153
  }
214
- /**
215
- * Set the logger logLevel for the Matterbridge classes.
216
- * @param {LogLevel} logLevel The logger logLevel to set.
217
- */
218
154
  async setLogLevel(logLevel) {
219
155
  if (this.log)
220
156
  this.log.logLevel = logLevel;
@@ -228,31 +164,19 @@ export class Matterbridge extends EventEmitter {
228
164
  for (const plugin of this.plugins) {
229
165
  if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
230
166
  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 */;
167
+ plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
168
+ await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
169
+ }
170
+ let callbackLogLevel = "notice";
171
+ if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
172
+ callbackLogLevel = "info";
173
+ if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
174
+ callbackLogLevel = "debug";
240
175
  AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
241
176
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
242
177
  }
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
178
  static async loadInstance(initialize = false) {
254
179
  if (!Matterbridge.instance) {
255
- // eslint-disable-next-line no-console
256
180
  if (hasParameter('debug'))
257
181
  console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
258
182
  Matterbridge.instance = new Matterbridge();
@@ -261,14 +185,8 @@ export class Matterbridge extends EventEmitter {
261
185
  }
262
186
  return Matterbridge.instance;
263
187
  }
264
- /**
265
- * Call cleanup().
266
- * @deprecated This method is deprecated and is only used for jest tests.
267
- *
268
- */
269
188
  async destroyInstance() {
270
189
  this.log.info(`Destroy instance...`);
271
- // Save server nodes to close
272
190
  const servers = [];
273
191
  if (this.bridgeMode === 'bridge') {
274
192
  if (this.serverNode)
@@ -280,81 +198,55 @@ export class Matterbridge extends EventEmitter {
280
198
  servers.push(plugin.serverNode);
281
199
  }
282
200
  }
283
- // Cleanup
284
201
  await this.cleanup('destroying instance...', false);
285
- // Close servers mdns service
286
202
  this.log.info(`Dispose ${servers.length} MdnsService...`);
287
203
  for (const server of servers) {
288
204
  await server.env.get(MdnsService)[Symbol.asyncDispose]();
289
205
  this.log.info(`Closed ${server.id} MdnsService`);
290
206
  }
291
- // Wait for the cleanup to finish
292
207
  await new Promise((resolve) => {
293
208
  setTimeout(resolve, 1000);
294
209
  });
295
210
  }
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
211
  async initialize() {
307
- // Set the restart mode
308
212
  if (hasParameter('service'))
309
213
  this.restartMode = 'service';
310
214
  if (hasParameter('docker'))
311
215
  this.restartMode = 'docker';
312
- // Set the matterbridge directory
313
216
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
314
217
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
315
- // Setup the matter environment
316
218
  this.environment.vars.set('log.level', MatterLogLevel.INFO);
317
219
  this.environment.vars.set('log.format', MatterLogFormat.ANSI);
318
220
  this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
319
221
  this.environment.vars.set('runtime.signals', false);
320
222
  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
223
+ this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
324
224
  this.registerProcessHandlers();
325
- // Initialize nodeStorage and nodeContext
326
225
  try {
327
226
  this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
328
227
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
329
228
  this.log.debug('Creating node storage context for matterbridge');
330
229
  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
230
  const keys = (await this.nodeStorage?.storage.keys());
334
231
  for (const key of keys) {
335
232
  this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
336
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
337
233
  await this.nodeStorage?.storage.get(key);
338
234
  }
339
235
  const storages = await this.nodeStorage.getStorageNames();
340
236
  for (const storage of storages) {
341
237
  this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
342
238
  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
239
  const keys = (await nodeContext?.storage.keys());
346
240
  keys.forEach(async (key) => {
347
241
  this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
348
242
  await nodeContext?.get(key);
349
243
  });
350
244
  }
351
- // Creating a backup of the node storage since it is not corrupted
352
245
  this.log.debug('Creating node storage backup...');
353
246
  await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
354
247
  this.log.debug('Created node storage backup');
355
248
  }
356
249
  catch (error) {
357
- // Restoring the backup of the node storage since it is corrupted
358
250
  this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
359
251
  if (hasParameter('norestore')) {
360
252
  this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
@@ -369,46 +261,41 @@ export class Matterbridge extends EventEmitter {
369
261
  this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
370
262
  throw new Error('Fatal error creating node storage manager and context for matterbridge');
371
263
  }
372
- // Set the first port to use for the commissioning server (will be incremented in childbridge mode)
373
264
  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
265
  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
266
  this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
378
267
  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
268
  if (hasParameter('logger')) {
381
269
  const level = getParameter('logger');
382
270
  if (level === 'debug') {
383
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
271
+ this.log.logLevel = "debug";
384
272
  }
385
273
  else if (level === 'info') {
386
- this.log.logLevel = "info" /* LogLevel.INFO */;
274
+ this.log.logLevel = "info";
387
275
  }
388
276
  else if (level === 'notice') {
389
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
277
+ this.log.logLevel = "notice";
390
278
  }
391
279
  else if (level === 'warn') {
392
- this.log.logLevel = "warn" /* LogLevel.WARN */;
280
+ this.log.logLevel = "warn";
393
281
  }
394
282
  else if (level === 'error') {
395
- this.log.logLevel = "error" /* LogLevel.ERROR */;
283
+ this.log.logLevel = "error";
396
284
  }
397
285
  else if (level === 'fatal') {
398
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
286
+ this.log.logLevel = "fatal";
399
287
  }
400
288
  else {
401
289
  this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
402
- this.log.logLevel = "info" /* LogLevel.INFO */;
290
+ this.log.logLevel = "info";
403
291
  }
404
292
  }
405
293
  else {
406
- this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" /* LogLevel.NOTICE */ : "info" /* LogLevel.INFO */);
294
+ this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
407
295
  }
408
296
  this.frontend.logLevel = this.log.logLevel;
409
297
  MatterbridgeEndpoint.logLevel = this.log.logLevel;
410
298
  this.matterbridgeInformation.loggerLevel = this.log.logLevel;
411
- // Create the file logger for matterbridge (context: matterbridgeFileLog)
412
299
  if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
413
300
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
414
301
  this.matterbridgeInformation.fileLogger = true;
@@ -417,39 +304,37 @@ export class Matterbridge extends EventEmitter {
417
304
  this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
418
305
  if (this.profile !== undefined)
419
306
  this.log.debug(`Matterbridge profile: ${this.profile}.`);
420
- // Set matter.js logger level, format and logger (context: matterLogLevel)
421
307
  if (hasParameter('matterlogger')) {
422
308
  const level = getParameter('matterlogger');
423
309
  if (level === 'debug') {
424
- Logger.defaultLogLevel = MatterLogLevel.DEBUG;
310
+ Logger.level = MatterLogLevel.DEBUG;
425
311
  }
426
312
  else if (level === 'info') {
427
- Logger.defaultLogLevel = MatterLogLevel.INFO;
313
+ Logger.level = MatterLogLevel.INFO;
428
314
  }
429
315
  else if (level === 'notice') {
430
- Logger.defaultLogLevel = MatterLogLevel.NOTICE;
316
+ Logger.level = MatterLogLevel.NOTICE;
431
317
  }
432
318
  else if (level === 'warn') {
433
- Logger.defaultLogLevel = MatterLogLevel.WARN;
319
+ Logger.level = MatterLogLevel.WARN;
434
320
  }
435
321
  else if (level === 'error') {
436
- Logger.defaultLogLevel = MatterLogLevel.ERROR;
322
+ Logger.level = MatterLogLevel.ERROR;
437
323
  }
438
324
  else if (level === 'fatal') {
439
- Logger.defaultLogLevel = MatterLogLevel.FATAL;
325
+ Logger.level = MatterLogLevel.FATAL;
440
326
  }
441
327
  else {
442
328
  this.log.warn(`Invalid matter.js logger level: ${level}. Using default level "info".`);
443
- Logger.defaultLogLevel = MatterLogLevel.INFO;
329
+ Logger.level = MatterLogLevel.INFO;
444
330
  }
445
331
  }
446
332
  else {
447
- Logger.defaultLogLevel = await this.nodeContext.get('matterLogLevel', this.matterbridgeInformation.shellyBoard ? MatterLogLevel.NOTICE : MatterLogLevel.INFO);
333
+ Logger.level = (await this.nodeContext.get('matterLogLevel', this.matterbridgeInformation.shellyBoard ? MatterLogLevel.NOTICE : MatterLogLevel.INFO));
448
334
  }
449
335
  Logger.format = MatterLogFormat.ANSI;
450
336
  Logger.setLogger('default', this.createMatterLogger());
451
337
  this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
452
- // Create the file logger for matter.js (context: matterFileLog)
453
338
  if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
454
339
  this.matterbridgeInformation.matterFileLogger = true;
455
340
  Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
@@ -458,7 +343,6 @@ export class Matterbridge extends EventEmitter {
458
343
  });
459
344
  }
460
345
  this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
461
- // Log network interfaces
462
346
  const networkInterfaces = os.networkInterfaces();
463
347
  const availableAddresses = Object.entries(networkInterfaces);
464
348
  const availableInterfaces = Object.keys(networkInterfaces);
@@ -470,7 +354,6 @@ export class Matterbridge extends EventEmitter {
470
354
  });
471
355
  }
472
356
  }
473
- // Set the interface to use for matter server node mdnsInterface
474
357
  if (hasParameter('mdnsinterface')) {
475
358
  this.mdnsInterface = getParameter('mdnsinterface');
476
359
  }
@@ -479,7 +362,6 @@ export class Matterbridge extends EventEmitter {
479
362
  if (this.mdnsInterface === '')
480
363
  this.mdnsInterface = undefined;
481
364
  }
482
- // Validate mdnsInterface
483
365
  if (this.mdnsInterface) {
484
366
  if (!availableInterfaces.includes(this.mdnsInterface)) {
485
367
  this.log.error(`Invalid mdnsInterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
@@ -491,7 +373,6 @@ export class Matterbridge extends EventEmitter {
491
373
  }
492
374
  if (this.mdnsInterface)
493
375
  this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
494
- // Set the listeningAddressIpv4 for the matter commissioning server
495
376
  if (hasParameter('ipv4address')) {
496
377
  this.ipv4address = getParameter('ipv4address');
497
378
  }
@@ -500,7 +381,6 @@ export class Matterbridge extends EventEmitter {
500
381
  if (this.ipv4address === '')
501
382
  this.ipv4address = undefined;
502
383
  }
503
- // Validate ipv4address
504
384
  if (this.ipv4address) {
505
385
  let isValid = false;
506
386
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -515,7 +395,6 @@ export class Matterbridge extends EventEmitter {
515
395
  this.ipv4address = undefined;
516
396
  }
517
397
  }
518
- // Set the listeningAddressIpv6 for the matter commissioning server
519
398
  if (hasParameter('ipv6address')) {
520
399
  this.ipv6address = getParameter('ipv6address');
521
400
  }
@@ -524,7 +403,6 @@ export class Matterbridge extends EventEmitter {
524
403
  if (this.ipv6address === '')
525
404
  this.ipv6address = undefined;
526
405
  }
527
- // Validate ipv6address
528
406
  if (this.ipv6address) {
529
407
  let isValid = false;
530
408
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -544,19 +422,14 @@ export class Matterbridge extends EventEmitter {
544
422
  this.ipv6address = undefined;
545
423
  }
546
424
  }
547
- // Initialize PluginManager
548
425
  this.plugins = new PluginManager(this);
549
426
  await this.plugins.loadFromStorage();
550
427
  this.plugins.logLevel = this.log.logLevel;
551
- // Initialize DeviceManager
552
428
  this.devices = new DeviceManager(this, this.nodeContext);
553
429
  this.devices.logLevel = this.log.logLevel;
554
- // Get the plugins from node storage and create the plugins node storage contexts
555
430
  for (const plugin of this.plugins) {
556
431
  const packageJson = await this.plugins.parse(plugin);
557
432
  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
433
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
561
434
  try {
562
435
  await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
@@ -578,7 +451,6 @@ export class Matterbridge extends EventEmitter {
578
451
  await plugin.nodeContext.set('description', plugin.description);
579
452
  await plugin.nodeContext.set('author', plugin.author);
580
453
  }
581
- // Log system info and create .matterbridge directory
582
454
  await this.logNodeAndSystemInfo();
583
455
  this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
584
456
  `${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
@@ -586,7 +458,6 @@ export class Matterbridge extends EventEmitter {
586
458
  `${hasParameter('controller') ? 'mode controller ' : ''}` +
587
459
  `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
588
460
  `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
461
  const minNodeVersion = 18;
591
462
  const nodeVersion = process.versions.node;
592
463
  const versionMajor = parseInt(nodeVersion.split('.')[0]);
@@ -594,15 +465,9 @@ export class Matterbridge extends EventEmitter {
594
465
  this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
595
466
  throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
596
467
  }
597
- // Parse command line
598
468
  await this.parseCommandLine();
599
469
  this.initialized = true;
600
470
  }
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
471
  async parseCommandLine() {
607
472
  if (hasParameter('help')) {
608
473
  this.log.info(`\nUsage: matterbridge [options]\n
@@ -714,7 +579,6 @@ export class Matterbridge extends EventEmitter {
714
579
  this.shutdown = true;
715
580
  return;
716
581
  }
717
- // Start the matter storage and create the matterbridge context
718
582
  try {
719
583
  await this.startMatterStorage();
720
584
  }
@@ -722,14 +586,12 @@ export class Matterbridge extends EventEmitter {
722
586
  this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
723
587
  throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
724
588
  }
725
- // Clear the matterbridge context if the reset parameter is set
726
589
  if (hasParameter('reset') && getParameter('reset') === undefined) {
727
590
  this.initialized = true;
728
591
  await this.shutdownProcessAndReset();
729
592
  this.shutdown = true;
730
593
  return;
731
594
  }
732
- // Clear matterbridge plugin context if the reset parameter is set
733
595
  if (hasParameter('reset') && getParameter('reset') !== undefined) {
734
596
  this.log.debug(`Reset plugin ${getParameter('reset')}`);
735
597
  const plugin = this.plugins.get(getParameter('reset'));
@@ -754,37 +616,30 @@ export class Matterbridge extends EventEmitter {
754
616
  this.shutdown = true;
755
617
  return;
756
618
  }
757
- // Initialize frontend
758
619
  if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
759
620
  await this.frontend.start(getIntParameter('frontend'));
760
- // Check in 30 seconds the latest versions
761
621
  this.checkUpdateTimeout = setTimeout(async () => {
762
622
  const { checkUpdates } = await import('./update.js');
763
623
  checkUpdates(this);
764
624
  }, 30 * 1000).unref();
765
- // Check each 24 hours the latest versions
766
625
  this.checkUpdateInterval = setInterval(async () => {
767
626
  const { checkUpdates } = await import('./update.js');
768
627
  checkUpdates(this);
769
628
  }, 24 * 60 * 60 * 1000).unref();
770
- // Start the matterbridge in mode test
771
629
  if (hasParameter('test')) {
772
630
  this.bridgeMode = 'bridge';
773
631
  MatterbridgeEndpoint.bridgeMode = 'bridge';
774
632
  return;
775
633
  }
776
- // Start the matterbridge in mode controller
777
634
  if (hasParameter('controller')) {
778
635
  this.bridgeMode = 'controller';
779
636
  await this.startController();
780
637
  return;
781
638
  }
782
- // Check if the bridge mode is set and start matterbridge in bridge mode if not set
783
639
  if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
784
640
  this.log.info('Setting default matterbridge start mode to bridge');
785
641
  await this.nodeContext?.set('bridgeMode', 'bridge');
786
642
  }
787
- // Start matterbridge in bridge mode
788
643
  if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
789
644
  this.bridgeMode = 'bridge';
790
645
  MatterbridgeEndpoint.bridgeMode = 'bridge';
@@ -792,7 +647,6 @@ export class Matterbridge extends EventEmitter {
792
647
  await this.startBridge();
793
648
  return;
794
649
  }
795
- // Start matterbridge in childbridge mode
796
650
  if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
797
651
  this.bridgeMode = 'childbridge';
798
652
  MatterbridgeEndpoint.bridgeMode = 'childbridge';
@@ -801,20 +655,10 @@ export class Matterbridge extends EventEmitter {
801
655
  return;
802
656
  }
803
657
  }
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
658
  async startPlugins() {
813
- // Check, load and start the plugins
814
659
  for (const plugin of this.plugins) {
815
660
  plugin.configJson = await this.plugins.loadConfig(plugin);
816
661
  plugin.schemaJson = await this.plugins.loadSchema(plugin);
817
- // Check if the plugin is available
818
662
  if (!(await this.plugins.resolve(plugin.path))) {
819
663
  this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
820
664
  plugin.enabled = false;
@@ -834,26 +678,20 @@ export class Matterbridge extends EventEmitter {
834
678
  plugin.addedDevices = undefined;
835
679
  plugin.qrPairingCode = undefined;
836
680
  plugin.manualPairingCode = undefined;
837
- this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
681
+ this.plugins.load(plugin, true, 'Matterbridge is starting');
838
682
  }
839
683
  this.frontend.wssSendRefreshRequired('plugins');
840
684
  }
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
685
  registerProcessHandlers() {
846
686
  this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
847
687
  process.removeAllListeners('uncaughtException');
848
688
  process.removeAllListeners('unhandledRejection');
849
689
  this.exceptionHandler = async (error) => {
850
690
  this.log.error('Unhandled Exception detected at:', error.stack || error, rs);
851
- // await this.cleanup('Unhandled Exception detected, cleaning up...');
852
691
  };
853
692
  process.on('uncaughtException', this.exceptionHandler);
854
693
  this.rejectionHandler = async (reason, promise) => {
855
694
  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
695
  };
858
696
  process.on('unhandledRejection', this.rejectionHandler);
859
697
  this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
@@ -866,9 +704,6 @@ export class Matterbridge extends EventEmitter {
866
704
  };
867
705
  process.on('SIGTERM', this.sigtermHandler);
868
706
  }
869
- /**
870
- * Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
871
- */
872
707
  deregisterProcesslHandlers() {
873
708
  this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
874
709
  if (this.exceptionHandler)
@@ -885,17 +720,12 @@ export class Matterbridge extends EventEmitter {
885
720
  process.off('SIGTERM', this.sigtermHandler);
886
721
  this.sigtermHandler = undefined;
887
722
  }
888
- /**
889
- * Logs the node and system information.
890
- */
891
723
  async logNodeAndSystemInfo() {
892
- // IP address information
893
724
  const networkInterfaces = os.networkInterfaces();
894
725
  this.systemInformation.interfaceName = '';
895
726
  this.systemInformation.ipv4Address = '';
896
727
  this.systemInformation.ipv6Address = '';
897
728
  for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
898
- // this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
899
729
  if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
900
730
  continue;
901
731
  if (!interfaceDetails) {
@@ -921,22 +751,19 @@ export class Matterbridge extends EventEmitter {
921
751
  break;
922
752
  }
923
753
  }
924
- // Node information
925
754
  this.systemInformation.nodeVersion = process.versions.node;
926
755
  const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
927
756
  const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
928
757
  const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
929
- // Host system information
930
758
  this.systemInformation.hostname = os.hostname();
931
759
  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
760
+ this.systemInformation.osType = os.type();
761
+ this.systemInformation.osRelease = os.release();
762
+ this.systemInformation.osPlatform = os.platform();
763
+ this.systemInformation.osArch = os.arch();
764
+ this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
765
+ this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
766
+ this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
940
767
  this.log.debug('Host System Information:');
941
768
  this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
942
769
  this.log.debug(`- User: ${this.systemInformation.user}`);
@@ -952,20 +779,16 @@ export class Matterbridge extends EventEmitter {
952
779
  this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
953
780
  this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
954
781
  this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
955
- // Home directory
956
782
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
957
783
  this.matterbridgeInformation.homeDirectory = this.homeDirectory;
958
784
  this.log.debug(`Home Directory: ${this.homeDirectory}`);
959
- // Package root directory
960
785
  const { fileURLToPath } = await import('node:url');
961
786
  const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
962
787
  this.rootDirectory = path.resolve(currentFileDirectory, '../');
963
788
  this.matterbridgeInformation.rootDirectory = this.rootDirectory;
964
789
  this.log.debug(`Root Directory: ${this.rootDirectory}`);
965
- // Global node_modules directory
966
790
  if (this.nodeContext)
967
791
  this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
968
- // First run of Matterbridge so the node storage is empty
969
792
  if (this.globalModulesDirectory === '') {
970
793
  try {
971
794
  this.execRunningCount++;
@@ -981,20 +804,6 @@ export class Matterbridge extends EventEmitter {
981
804
  }
982
805
  else
983
806
  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
807
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
999
808
  this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
1000
809
  try {
@@ -1018,7 +827,6 @@ export class Matterbridge extends EventEmitter {
1018
827
  }
1019
828
  }
1020
829
  this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
1021
- // Create the plugin directory Matterbridge in the home directory
1022
830
  this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
1023
831
  this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
1024
832
  try {
@@ -1042,7 +850,6 @@ export class Matterbridge extends EventEmitter {
1042
850
  }
1043
851
  }
1044
852
  this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
1045
- // Create the matter cert directory in the home directory
1046
853
  this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
1047
854
  this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
1048
855
  try {
@@ -1066,68 +873,50 @@ export class Matterbridge extends EventEmitter {
1066
873
  }
1067
874
  }
1068
875
  this.log.debug(`Matterbridge Matter Cert Directory: ${this.matterbridgeCertDirectory}`);
1069
- // Matterbridge version
1070
876
  const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
1071
877
  this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
1072
878
  this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
1073
879
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
1074
- // Matterbridge latest version
1075
880
  if (this.nodeContext)
1076
881
  this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
1077
882
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
1078
- // this.getMatterbridgeLatestVersion();
1079
- // Current working directory
1080
883
  const currentDir = process.cwd();
1081
884
  this.log.debug(`Current Working Directory: ${currentDir}`);
1082
- // Command line arguments (excluding 'node' and the script name)
1083
885
  const cmdArgs = process.argv.slice(2).join(' ');
1084
886
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
1085
887
  }
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
888
  createMatterLogger() {
1092
- const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
889
+ const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
1093
890
  return (_level, formattedLog) => {
1094
891
  const logger = formattedLog.slice(44, 44 + 20).trim();
1095
892
  const message = formattedLog.slice(65);
1096
893
  matterLogger.logName = logger;
1097
894
  switch (_level) {
1098
895
  case MatterLogLevel.DEBUG:
1099
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
896
+ matterLogger.log("debug", message);
1100
897
  break;
1101
898
  case MatterLogLevel.INFO:
1102
- matterLogger.log("info" /* LogLevel.INFO */, message);
899
+ matterLogger.log("info", message);
1103
900
  break;
1104
901
  case MatterLogLevel.NOTICE:
1105
- matterLogger.log("notice" /* LogLevel.NOTICE */, message);
902
+ matterLogger.log("notice", message);
1106
903
  break;
1107
904
  case MatterLogLevel.WARN:
1108
- matterLogger.log("warn" /* LogLevel.WARN */, message);
905
+ matterLogger.log("warn", message);
1109
906
  break;
1110
907
  case MatterLogLevel.ERROR:
1111
- matterLogger.log("error" /* LogLevel.ERROR */, message);
908
+ matterLogger.log("error", message);
1112
909
  break;
1113
910
  case MatterLogLevel.FATAL:
1114
- matterLogger.log("fatal" /* LogLevel.FATAL */, message);
911
+ matterLogger.log("fatal", message);
1115
912
  break;
1116
913
  default:
1117
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
914
+ matterLogger.log("debug", message);
1118
915
  break;
1119
916
  }
1120
917
  };
1121
918
  }
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
919
  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
920
  let fileSize = 0;
1132
921
  if (unlink) {
1133
922
  try {
@@ -1176,21 +965,12 @@ export class Matterbridge extends EventEmitter {
1176
965
  }
1177
966
  };
1178
967
  }
1179
- /**
1180
- * Restarts the process by exiting the current instance and loading a new instance.
1181
- */
1182
968
  async restartProcess() {
1183
969
  await this.cleanup('restarting...', true);
1184
970
  }
1185
- /**
1186
- * Shut down the process by exiting the current process.
1187
- */
1188
971
  async shutdownProcess() {
1189
972
  await this.cleanup('shutting down...', false);
1190
973
  }
1191
- /**
1192
- * Update matterbridge and and shut down the process.
1193
- */
1194
974
  async updateProcess() {
1195
975
  this.log.info('Updating matterbridge...');
1196
976
  try {
@@ -1203,72 +983,51 @@ export class Matterbridge extends EventEmitter {
1203
983
  this.frontend.wssSendRestartRequired();
1204
984
  await this.cleanup('updating...', false);
1205
985
  }
1206
- /**
1207
- * Unregister all devices and shut down the process.
1208
- */
1209
986
  async unregisterAndShutdownProcess() {
1210
987
  this.log.info('Unregistering all devices and shutting down...');
1211
988
  for (const plugin of this.plugins) {
1212
989
  await this.removeAllBridgedEndpoints(plugin.name, 250);
1213
990
  }
1214
991
  this.log.debug('Waiting for the MessageExchange to finish...');
1215
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second for MessageExchange to finish
992
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1216
993
  this.log.debug('Cleaning up and shutting down...');
1217
994
  await this.cleanup('unregistered all devices and shutting down...', false);
1218
995
  }
1219
- /**
1220
- * Reset commissioning and shut down the process.
1221
- */
1222
996
  async shutdownProcessAndReset() {
1223
997
  await this.cleanup('shutting down with reset...', false);
1224
998
  }
1225
- /**
1226
- * Factory reset and shut down the process.
1227
- */
1228
999
  async shutdownProcessAndFactoryReset() {
1229
1000
  await this.cleanup('shutting down with factory reset...', false);
1230
1001
  }
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
1002
  async cleanup(message, restart = false) {
1238
1003
  if (this.initialized && !this.hasCleanupStarted) {
1239
1004
  this.hasCleanupStarted = true;
1240
1005
  this.log.info(message);
1241
- // Clear the start matter interval
1242
1006
  if (this.startMatterInterval) {
1243
1007
  clearInterval(this.startMatterInterval);
1244
1008
  this.startMatterInterval = undefined;
1245
1009
  this.log.debug('Start matter interval cleared');
1246
1010
  }
1247
- // Clear the check update timeout
1248
1011
  if (this.checkUpdateTimeout) {
1249
1012
  clearInterval(this.checkUpdateTimeout);
1250
1013
  this.checkUpdateTimeout = undefined;
1251
1014
  this.log.debug('Check update timeout cleared');
1252
1015
  }
1253
- // Clear the check update interval
1254
1016
  if (this.checkUpdateInterval) {
1255
1017
  clearInterval(this.checkUpdateInterval);
1256
1018
  this.checkUpdateInterval = undefined;
1257
1019
  this.log.debug('Check update interval cleared');
1258
1020
  }
1259
- // Clear the configure timeout
1260
1021
  if (this.configureTimeout) {
1261
1022
  clearTimeout(this.configureTimeout);
1262
1023
  this.configureTimeout = undefined;
1263
1024
  this.log.debug('Matterbridge configure timeout cleared');
1264
1025
  }
1265
- // Clear the reachability timeout
1266
1026
  if (this.reachabilityTimeout) {
1267
1027
  clearTimeout(this.reachabilityTimeout);
1268
1028
  this.reachabilityTimeout = undefined;
1269
1029
  this.log.debug('Matterbridge reachability timeout cleared');
1270
1030
  }
1271
- // Calling the shutdown method of each plugin and clear the plugins reachability timeout
1272
1031
  for (const plugin of this.plugins) {
1273
1032
  if (!plugin.enabled || plugin.error)
1274
1033
  continue;
@@ -1279,10 +1038,9 @@ export class Matterbridge extends EventEmitter {
1279
1038
  this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
1280
1039
  }
1281
1040
  }
1282
- // Stopping matter server nodes
1283
1041
  this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
1284
1042
  this.log.debug('Waiting for the MessageExchange to finish...');
1285
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second for MessageExchange to finish
1043
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1286
1044
  if (this.bridgeMode === 'bridge') {
1287
1045
  if (this.serverNode) {
1288
1046
  await this.stopServerNode(this.serverNode);
@@ -1298,7 +1056,6 @@ export class Matterbridge extends EventEmitter {
1298
1056
  }
1299
1057
  }
1300
1058
  this.log.notice('Stopped matter server nodes');
1301
- // Matter commisioning reset
1302
1059
  if (message === 'shutting down with reset...') {
1303
1060
  this.log.info('Resetting Matterbridge commissioning information...');
1304
1061
  await this.matterStorageManager?.createContext('events')?.clearAll();
@@ -1308,37 +1065,17 @@ export class Matterbridge extends EventEmitter {
1308
1065
  await this.matterbridgeContext?.clearAll();
1309
1066
  this.log.info('Matter storage reset done! Remove the bridge from the controller.');
1310
1067
  }
1311
- // Stop matter storage
1312
1068
  await this.stopMatterStorage();
1313
- // Stop the frontend
1314
1069
  await this.frontend.stop();
1315
- // Remove the matterfilelogger
1316
1070
  try {
1317
1071
  Logger.removeLogger('matterfilelogger');
1318
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1319
1072
  }
1320
1073
  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
1074
  }
1323
- // Serialize registeredDevices
1324
1075
  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
1076
  this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
1339
1077
  await this.nodeContext.close();
1340
1078
  this.nodeContext = undefined;
1341
- // Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
1342
1079
  for (const plugin of this.plugins) {
1343
1080
  if (plugin.nodeContext) {
1344
1081
  this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
@@ -1355,10 +1092,8 @@ export class Matterbridge extends EventEmitter {
1355
1092
  }
1356
1093
  this.plugins.clear();
1357
1094
  this.devices.clear();
1358
- // Factory reset
1359
1095
  if (message === 'shutting down with factory reset...') {
1360
1096
  try {
1361
- // Delete old matter storage file and backup
1362
1097
  const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
1363
1098
  this.log.info(`Unlinking old matter storage file: ${file}`);
1364
1099
  await fs.unlink(file);
@@ -1372,7 +1107,6 @@ export class Matterbridge extends EventEmitter {
1372
1107
  }
1373
1108
  }
1374
1109
  try {
1375
- // Delete matter node storage directory with its subdirectories and backup
1376
1110
  const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
1377
1111
  this.log.info(`Removing matter node storage directory: ${dir}`);
1378
1112
  await fs.rm(dir, { recursive: true });
@@ -1386,7 +1120,6 @@ export class Matterbridge extends EventEmitter {
1386
1120
  }
1387
1121
  }
1388
1122
  try {
1389
- // Delete node storage directory with its subdirectories and backup
1390
1123
  const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
1391
1124
  this.log.info(`Removing storage directory: ${dir}`);
1392
1125
  await fs.rm(dir, { recursive: true });
@@ -1401,13 +1134,12 @@ export class Matterbridge extends EventEmitter {
1401
1134
  }
1402
1135
  this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
1403
1136
  }
1404
- // Deregisters the process handlers
1405
1137
  this.deregisterProcesslHandlers();
1406
1138
  if (restart) {
1407
1139
  if (message === 'updating...') {
1408
1140
  this.log.info('Cleanup completed. Updating...');
1409
1141
  Matterbridge.instance = undefined;
1410
- this.emit('update'); // Restart the process but the update has been done before. TODO move all updates to the cli
1142
+ this.emit('update');
1411
1143
  }
1412
1144
  else if (message === 'restarting...') {
1413
1145
  this.log.info('Cleanup completed. Restarting...');
@@ -1427,14 +1159,6 @@ export class Matterbridge extends EventEmitter {
1427
1159
  this.log.debug('Cleanup already started...');
1428
1160
  }
1429
1161
  }
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
1162
  async createAccessoryPlugin(plugin, device, start = false) {
1439
1163
  if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
1440
1164
  plugin.locked = true;
@@ -1447,13 +1171,6 @@ export class Matterbridge extends EventEmitter {
1447
1171
  await this.startServerNode(plugin.serverNode);
1448
1172
  }
1449
1173
  }
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
1174
  async createDynamicPlugin(plugin, start = false) {
1458
1175
  if (!plugin.locked) {
1459
1176
  plugin.locked = true;
@@ -1465,13 +1182,7 @@ export class Matterbridge extends EventEmitter {
1465
1182
  await this.startServerNode(plugin.serverNode);
1466
1183
  }
1467
1184
  }
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
1185
  async startBridge() {
1474
- // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1475
1186
  if (!this.matterStorageManager)
1476
1187
  throw new Error('No storage manager initialized');
1477
1188
  if (!this.matterbridgeContext)
@@ -1509,9 +1220,7 @@ export class Matterbridge extends EventEmitter {
1509
1220
  clearInterval(this.startMatterInterval);
1510
1221
  this.startMatterInterval = undefined;
1511
1222
  this.log.debug('Cleared startMatterInterval interval for Matterbridge');
1512
- // Start the Matter server node
1513
1223
  this.startServerNode(this.serverNode);
1514
- // Configure the plugins
1515
1224
  this.configureTimeout = setTimeout(async () => {
1516
1225
  for (const plugin of this.plugins) {
1517
1226
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1529,7 +1238,6 @@ export class Matterbridge extends EventEmitter {
1529
1238
  }
1530
1239
  this.frontend.wssSendRefreshRequired('plugins');
1531
1240
  }, 30 * 1000);
1532
- // Setting reachability to true
1533
1241
  this.reachabilityTimeout = setTimeout(() => {
1534
1242
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1535
1243
  if (this.aggregatorNode)
@@ -1538,11 +1246,6 @@ export class Matterbridge extends EventEmitter {
1538
1246
  }, 60 * 1000);
1539
1247
  }, 1000);
1540
1248
  }
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
1249
  async startChildbridge() {
1547
1250
  if (!this.matterStorageManager)
1548
1251
  throw new Error('No storage manager initialized');
@@ -1587,7 +1290,6 @@ export class Matterbridge extends EventEmitter {
1587
1290
  clearInterval(this.startMatterInterval);
1588
1291
  this.startMatterInterval = undefined;
1589
1292
  this.log.debug('Cleared startMatterInterval interval in childbridge mode');
1590
- // Configure the plugins
1591
1293
  this.configureTimeout = setTimeout(async () => {
1592
1294
  for (const plugin of this.plugins) {
1593
1295
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1624,11 +1326,9 @@ export class Matterbridge extends EventEmitter {
1624
1326
  this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
1625
1327
  continue;
1626
1328
  }
1627
- // Start the Matter server node
1628
1329
  this.startServerNode(plugin.serverNode);
1629
- // Setting reachability to true
1630
1330
  plugin.reachabilityTimeout = setTimeout(() => {
1631
- 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}`);
1331
+ this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggregator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
1632
1332
  if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
1633
1333
  this.setAggregatorReachability(plugin.aggregatorNode, true);
1634
1334
  this.frontend.wssSendRefreshRequired('reachability');
@@ -1636,219 +1336,22 @@ export class Matterbridge extends EventEmitter {
1636
1336
  }
1637
1337
  }, 1000);
1638
1338
  }
1639
- /**
1640
- * Starts the Matterbridge controller.
1641
- * @private
1642
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1643
- */
1644
1339
  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;
1340
+ if (!this.matterStorageManager) {
1341
+ this.log.error('No storage manager initialized');
1342
+ await this.cleanup('No storage manager initialized');
1343
+ return;
1650
1344
  }
1651
1345
  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;
1346
+ this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
1347
+ if (!this.controllerContext) {
1348
+ this.log.error('No storage context mattercontrollerContext initialized');
1349
+ await this.cleanup('No storage context mattercontrollerContext initialized');
1350
+ return;
1657
1351
  }
1658
-
1659
1352
  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
1353
  }
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
1354
  async startMatterStorage() {
1851
- // Setup Matter storage
1852
1355
  this.log.info(`Starting matter node storage...`);
1853
1356
  this.matterStorageService = this.environment.get(StorageService);
1854
1357
  this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
@@ -1856,25 +1359,13 @@ export class Matterbridge extends EventEmitter {
1856
1359
  this.log.info('Matter node storage manager "Matterbridge" created');
1857
1360
  this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName);
1858
1361
  this.log.info('Matter node storage started');
1859
- // Backup matter storage since it is created/opened correctly
1860
1362
  await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
1861
1363
  }
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
1364
  async backupMatterStorage(storageName, backupName) {
1870
1365
  this.log.info('Creating matter node storage backup...');
1871
1366
  await copyDirectory(storageName, backupName);
1872
1367
  this.log.info('Created matter node storage backup');
1873
1368
  }
1874
- /**
1875
- * Stops the matter storage.
1876
- * @returns {Promise<void>} A promise that resolves when the storage is stopped.
1877
- */
1878
1369
  async stopMatterStorage() {
1879
1370
  this.log.info('Closing matter node storage...');
1880
1371
  this.matterStorageManager?.close();
@@ -1883,19 +1374,6 @@ export class Matterbridge extends EventEmitter {
1883
1374
  this.matterbridgeContext = undefined;
1884
1375
  this.log.info('Matter node storage closed');
1885
1376
  }
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
1377
  async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
1900
1378
  const { randomBytes } = await import('node:crypto');
1901
1379
  if (!this.matterStorageService)
@@ -1929,15 +1407,6 @@ export class Matterbridge extends EventEmitter {
1929
1407
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
1930
1408
  return storageContext;
1931
1409
  }
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
1410
  async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
1942
1411
  const storeId = await storageContext.get('storeId');
1943
1412
  this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
@@ -1947,33 +1416,21 @@ export class Matterbridge extends EventEmitter {
1947
1416
  this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
1948
1417
  this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
1949
1418
  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
1419
  const serverNode = await ServerNode.create({
1954
- // Required: Give the Node a unique ID which is used to store the state of this node
1955
1420
  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
1421
  network: {
1959
1422
  listeningAddressIpv4: this.ipv4address,
1960
1423
  listeningAddressIpv6: this.ipv6address,
1961
1424
  port,
1962
1425
  },
1963
- // Provide Commissioning relevant settings
1964
- // Optional for development/testing purposes
1965
1426
  commissioning: {
1966
1427
  passcode,
1967
1428
  discriminator,
1968
1429
  },
1969
- // Provide Node announcement settings
1970
- // Optional: If Ommitted some development defaults are used
1971
1430
  productDescription: {
1972
1431
  name: await storageContext.get('deviceName'),
1973
1432
  deviceType: DeviceTypeId(await storageContext.get('deviceType')),
1974
1433
  },
1975
- // Provide defaults for the BasicInformation cluster on the Root endpoint
1976
- // Optional: If Omitted some development defaults are used
1977
1434
  basicInformation: {
1978
1435
  vendorId: VendorId(await storageContext.get('vendorId')),
1979
1436
  vendorName: await storageContext.get('vendorName'),
@@ -1987,16 +1444,16 @@ export class Matterbridge extends EventEmitter {
1987
1444
  softwareVersionString: await storageContext.get('softwareVersionString'),
1988
1445
  hardwareVersion: await storageContext.get('hardwareVersion'),
1989
1446
  hardwareVersionString: await storageContext.get('hardwareVersionString'),
1447
+ reachable: true,
1990
1448
  },
1991
1449
  });
1992
1450
  const sanitizeFabrics = (fabrics, resetSessions = false) => {
1993
- // New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
1994
1451
  const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
1995
1452
  this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
1996
1453
  if (this.bridgeMode === 'bridge') {
1997
1454
  this.matterbridgeFabricInformations = sanitizedFabrics;
1998
1455
  if (resetSessions)
1999
- this.matterbridgeSessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
1456
+ this.matterbridgeSessionInformations = undefined;
2000
1457
  this.matterbridgePaired = true;
2001
1458
  }
2002
1459
  if (this.bridgeMode === 'childbridge') {
@@ -2004,19 +1461,13 @@ export class Matterbridge extends EventEmitter {
2004
1461
  if (plugin) {
2005
1462
  plugin.fabricInformations = sanitizedFabrics;
2006
1463
  if (resetSessions)
2007
- plugin.sessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
1464
+ plugin.sessionInformations = undefined;
2008
1465
  plugin.paired = true;
2009
1466
  }
2010
1467
  }
2011
1468
  };
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
1469
  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
1470
  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
1471
  serverNode.lifecycle.online.on(async () => {
2021
1472
  this.log.notice(`Server node for ${storeId} is online`);
2022
1473
  if (!serverNode.lifecycle.isCommissioned) {
@@ -2067,11 +1518,10 @@ export class Matterbridge extends EventEmitter {
2067
1518
  if (plugin) {
2068
1519
  plugin.qrPairingCode = undefined;
2069
1520
  plugin.manualPairingCode = undefined;
2070
- this.frontend.wssSendRefreshRequired('plugins');
2071
1521
  }
2072
1522
  }
2073
- this.frontend.wssSendRefreshRequired('settings');
2074
1523
  this.frontend.wssSendRefreshRequired('plugins');
1524
+ this.frontend.wssSendRefreshRequired('settings');
2075
1525
  this.frontend.wssSendRefreshRequired('fabrics');
2076
1526
  this.frontend.wssSendRefreshRequired('sessions');
2077
1527
  this.frontend.wssSendSnackbarMessage(`Advertising on server node for ${storeId} stopped. Restart to commission.`, 0);
@@ -2086,7 +1536,6 @@ export class Matterbridge extends EventEmitter {
2086
1536
  this.frontend.wssSendRefreshRequired('settings');
2087
1537
  this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
2088
1538
  });
2089
- /** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
2090
1539
  serverNode.lifecycle.offline.on(() => {
2091
1540
  this.log.notice(`Server node for ${storeId} is offline`);
2092
1541
  if (this.bridgeMode === 'bridge') {
@@ -2110,10 +1559,6 @@ export class Matterbridge extends EventEmitter {
2110
1559
  this.frontend.wssSendRefreshRequired('settings');
2111
1560
  this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
2112
1561
  });
2113
- /**
2114
- * This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
2115
- * information is needed.
2116
- */
2117
1562
  serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
2118
1563
  let action = '';
2119
1564
  switch (fabricAction) {
@@ -2147,24 +1592,16 @@ export class Matterbridge extends EventEmitter {
2147
1592
  }
2148
1593
  }
2149
1594
  };
2150
- /**
2151
- * This event is triggered when an operative new session was opened by a Controller.
2152
- * It is not triggered for the initial commissioning process, just afterwards for real connections.
2153
- */
2154
1595
  serverNode.events.sessions.opened.on((session) => {
2155
1596
  this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
2156
1597
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2157
1598
  this.frontend.wssSendRefreshRequired('sessions');
2158
1599
  });
2159
- /**
2160
- * This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
2161
- */
2162
1600
  serverNode.events.sessions.closed.on((session) => {
2163
1601
  this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
2164
1602
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2165
1603
  this.frontend.wssSendRefreshRequired('sessions');
2166
1604
  });
2167
- /** This event is triggered when a subscription gets added or removed on an operative session. */
2168
1605
  serverNode.events.sessions.subscriptionsChanged.on((session) => {
2169
1606
  this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
2170
1607
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
@@ -2173,42 +1610,24 @@ export class Matterbridge extends EventEmitter {
2173
1610
  this.log.info(`Created server node for ${storeId}`);
2174
1611
  return serverNode;
2175
1612
  }
2176
- /**
2177
- * Starts the specified server node.
2178
- *
2179
- * @param {ServerNode} [matterServerNode] - The server node to start.
2180
- * @returns {Promise<void>} A promise that resolves when the server node has started.
2181
- */
2182
1613
  async startServerNode(matterServerNode) {
2183
1614
  if (!matterServerNode)
2184
1615
  return;
2185
1616
  this.log.notice(`Starting ${matterServerNode.id} server node`);
2186
1617
  await matterServerNode.start();
2187
1618
  }
2188
- /**
2189
- * Stops the specified server node.
2190
- *
2191
- * @param {ServerNode} matterServerNode - The server node to stop.
2192
- * @returns {Promise<void>} A promise that resolves when the server node has stopped.
2193
- */
2194
1619
  async stopServerNode(matterServerNode) {
2195
1620
  if (!matterServerNode)
2196
1621
  return;
2197
1622
  this.log.notice(`Closing ${matterServerNode.id} server node`);
2198
1623
  try {
2199
- await withTimeout(matterServerNode.close(), 30000); // 30 seconds timeout to allow slow devices to close gracefully
1624
+ await withTimeout(matterServerNode.close(), 30000);
2200
1625
  this.log.info(`Closed ${matterServerNode.id} server node`);
2201
1626
  }
2202
1627
  catch (error) {
2203
1628
  this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
2204
1629
  }
2205
1630
  }
2206
- /**
2207
- * Advertises the specified server node.
2208
- *
2209
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2210
- * @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.
2211
- */
2212
1631
  async advertiseServerNode(matterServerNode) {
2213
1632
  if (matterServerNode) {
2214
1633
  await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
@@ -2217,88 +1636,84 @@ export class Matterbridge extends EventEmitter {
2217
1636
  return { qrPairingCode, manualPairingCode };
2218
1637
  }
2219
1638
  }
2220
- /**
2221
- * Stop advertise the specified server node.
2222
- *
2223
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2224
- * @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
2225
- */
2226
1639
  async stopAdvertiseServerNode(matterServerNode) {
2227
1640
  if (matterServerNode && matterServerNode.lifecycle.isOnline) {
2228
1641
  await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
2229
1642
  this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
2230
1643
  }
2231
1644
  }
2232
- /**
2233
- * Creates an aggregator node with the specified storage context.
2234
- *
2235
- * @param {StorageContext} storageContext - The storage context for the aggregator node.
2236
- * @returns {Promise<EndpointNode<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
2237
- */
2238
1645
  async createAggregatorNode(storageContext) {
2239
1646
  this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
2240
1647
  const aggregatorNode = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
2241
1648
  return aggregatorNode;
2242
1649
  }
2243
- /**
2244
- * Adds a MatterbridgeEndpoint to the specified plugin.
2245
- *
2246
- * @param {string} pluginName - The name of the plugin.
2247
- * @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
2248
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
2249
- */
2250
1650
  async addBridgedEndpoint(pluginName, device) {
2251
- // Check if the plugin is registered
2252
1651
  const plugin = this.plugins.get(pluginName);
2253
1652
  if (!plugin) {
2254
1653
  this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
2255
1654
  return;
2256
1655
  }
2257
- // Register and add the device to the matterbridge aggregator node
2258
1656
  if (this.bridgeMode === 'bridge') {
2259
1657
  this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
2260
1658
  if (!this.aggregatorNode)
2261
1659
  this.log.error('Aggregator node not found for Matterbridge');
2262
- await this.aggregatorNode?.add(device);
1660
+ try {
1661
+ await this.aggregatorNode?.add(device);
1662
+ }
1663
+ catch (error) {
1664
+ const errorMessage = error instanceof Error ? error.message : '';
1665
+ const errorStack = error instanceof Error ? error.stack : '';
1666
+ const errorDebug = inspect(error, { depth: 10 });
1667
+ this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) for plugin ${plg}${pluginName}${er}: ${error} ${errorMessage} ${errorStack} ${errorDebug}`);
1668
+ return;
1669
+ }
2263
1670
  }
2264
1671
  else if (this.bridgeMode === 'childbridge') {
2265
1672
  if (plugin.type === 'AccessoryPlatform') {
2266
- await this.createAccessoryPlugin(plugin, device);
1673
+ try {
1674
+ this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
1675
+ await this.createAccessoryPlugin(plugin, device);
1676
+ }
1677
+ catch (error) {
1678
+ const errorMessage = error instanceof Error ? error.message : '';
1679
+ const errorStack = error instanceof Error ? error.stack : '';
1680
+ const errorDebug = inspect(error, { depth: 10 });
1681
+ this.log.error(`Error creating endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) for AccessoryPlatform plugin ${plg}${pluginName}${er} server node: ${error} ${errorMessage} ${errorStack} ${errorDebug}`);
1682
+ return;
1683
+ }
2267
1684
  }
2268
1685
  if (plugin.type === 'DynamicPlatform') {
2269
1686
  plugin.locked = true;
2270
- this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to ${plg}${plugin.name}${db} aggregator node`);
1687
+ this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
2271
1688
  if (!plugin.aggregatorNode)
2272
1689
  this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${db}`);
2273
- await plugin.aggregatorNode?.add(device);
1690
+ try {
1691
+ await plugin.aggregatorNode?.add(device);
1692
+ }
1693
+ catch (error) {
1694
+ const errorMessage = error instanceof Error ? error.message : '';
1695
+ const errorStack = error instanceof Error ? error.stack : '';
1696
+ const errorDebug = inspect(error, { depth: 10 });
1697
+ this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) for DynamicPlatform plugin ${plg}${pluginName}${er} aggregator node: ${error} ${errorMessage} ${errorStack} ${errorDebug}`);
1698
+ return;
1699
+ }
2274
1700
  }
2275
1701
  }
2276
1702
  if (plugin.registeredDevices !== undefined)
2277
1703
  plugin.registeredDevices++;
2278
1704
  if (plugin.addedDevices !== undefined)
2279
1705
  plugin.addedDevices++;
2280
- // Add the device to the DeviceManager
2281
1706
  this.devices.set(device);
2282
- // Subscribe to the reachable$Changed event
2283
1707
  await this.subscribeAttributeChanged(plugin, device);
2284
1708
  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}`);
2285
1709
  }
2286
- /**
2287
- * Removes a MatterbridgeEndpoint from the specified plugin.
2288
- *
2289
- * @param {string} pluginName - The name of the plugin.
2290
- * @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
2291
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
2292
- */
2293
1710
  async removeBridgedEndpoint(pluginName, device) {
2294
1711
  this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
2295
- // Check if the plugin is registered
2296
1712
  const plugin = this.plugins.get(pluginName);
2297
1713
  if (!plugin) {
2298
1714
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
2299
1715
  return;
2300
1716
  }
2301
- // Register and add the device to the matterbridge aggregator node
2302
1717
  if (this.bridgeMode === 'bridge') {
2303
1718
  if (!this.aggregatorNode) {
2304
1719
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
@@ -2313,7 +1728,6 @@ export class Matterbridge extends EventEmitter {
2313
1728
  }
2314
1729
  else if (this.bridgeMode === 'childbridge') {
2315
1730
  if (plugin.type === 'AccessoryPlatform') {
2316
- // Nothing to do here since the server node has no aggregator node but only the device itself
2317
1731
  }
2318
1732
  else if (plugin.type === 'DynamicPlatform') {
2319
1733
  if (!plugin.aggregatorNode) {
@@ -2328,43 +1742,21 @@ export class Matterbridge extends EventEmitter {
2328
1742
  if (plugin.addedDevices !== undefined)
2329
1743
  plugin.addedDevices--;
2330
1744
  }
2331
- // Remove the device from the DeviceManager
2332
1745
  this.devices.remove(device);
2333
1746
  }
2334
- /**
2335
- * Removes all bridged endpoints from the specified plugin.
2336
- *
2337
- * @param {string} pluginName - The name of the plugin.
2338
- * @param {number} [delay=0] - The delay in milliseconds between removing each bridged endpoint (default: 0).
2339
- * @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
2340
- */
2341
1747
  async removeAllBridgedEndpoints(pluginName, delay = 0) {
2342
- this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}`);
1748
+ this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
2343
1749
  for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
2344
1750
  await this.removeBridgedEndpoint(pluginName, device);
2345
1751
  if (delay > 0)
2346
1752
  await new Promise((resolve) => setTimeout(resolve, delay));
2347
1753
  }
1754
+ if (delay > 0)
1755
+ await new Promise((resolve) => setTimeout(resolve, 2000));
2348
1756
  }
2349
- /**
2350
- * Subscribes to the attribute change event for the given device and plugin.
2351
- * Specifically, it listens for changes in the 'reachable' attribute of the
2352
- * BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
2353
- *
2354
- * @param {RegisteredPlugin} plugin - The plugin associated with the device.
2355
- * @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
2356
- * @returns {Promise<void>} A promise that resolves when the subscription is set up.
2357
- */
2358
1757
  async subscribeAttributeChanged(plugin, device) {
2359
1758
  this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
2360
1759
  if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
2361
- /*
2362
- this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) subscribed to reachable$Changed`);
2363
- setTimeout(async () => {
2364
- this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) changed to reachable false`);
2365
- await plugin.serverNode?.setStateOf(BasicInformationServer, { reachable: false });
2366
- }, 60000).unref();
2367
- */
2368
1760
  plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
2369
1761
  this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
2370
1762
  this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, 'BasicInformationServer', 'reachable', reachable);
@@ -2377,12 +1769,6 @@ export class Matterbridge extends EventEmitter {
2377
1769
  });
2378
1770
  }
2379
1771
  }
2380
- /**
2381
- * Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
2382
- *
2383
- * @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
2384
- * @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
2385
- */
2386
1772
  sanitizeFabricInformations(fabricInfo) {
2387
1773
  return fabricInfo.map((info) => {
2388
1774
  return {
@@ -2396,12 +1782,6 @@ export class Matterbridge extends EventEmitter {
2396
1782
  };
2397
1783
  });
2398
1784
  }
2399
- /**
2400
- * Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
2401
- *
2402
- * @param {SessionInformation[]} sessionInfo - The array of session information objects.
2403
- * @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
2404
- */
2405
1785
  sanitizeSessionInformation(sessionInfo) {
2406
1786
  return sessionInfo
2407
1787
  .filter((session) => session.isPeerActive)
@@ -2429,20 +1809,7 @@ export class Matterbridge extends EventEmitter {
2429
1809
  };
2430
1810
  });
2431
1811
  }
2432
- /**
2433
- * Sets the reachability of the specified aggregator node bridged devices and trigger.
2434
- * @param {EndpointNode<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
2435
- * @param {boolean} reachable - A boolean indicating the reachability status to set.
2436
- */
2437
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2438
1812
  async setAggregatorReachability(aggregatorNode, reachable) {
2439
- /*
2440
- for (const child of aggregatorNode.parts) {
2441
- this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
2442
- await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
2443
- child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
2444
- }
2445
- */
2446
1813
  }
2447
1814
  getVendorIdName = (vendorId) => {
2448
1815
  if (!vendorId)
@@ -2485,37 +1852,14 @@ export class Matterbridge extends EventEmitter {
2485
1852
  }
2486
1853
  return vendorName;
2487
1854
  };
2488
- /**
2489
- * Spawns a child process with the given command and arguments.
2490
- * @param {string} command - The command to execute.
2491
- * @param {string[]} args - The arguments to pass to the command (default: []).
2492
- * @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
2493
- */
2494
1855
  async spawnCommand(command, args = []) {
2495
1856
  const { spawn } = await import('node:child_process');
2496
- /*
2497
- npm > npm.cmd on windows
2498
- cmd.exe ['dir'] on windows
2499
- await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
2500
- process.on('unhandledRejection', (reason, promise) => {
2501
- this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
2502
- });
2503
-
2504
- spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
2505
- spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
2506
- debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
2507
- debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
2508
- */
2509
1857
  const cmdLine = command + ' ' + args.join(' ');
2510
1858
  if (process.platform === 'win32' && command === 'npm') {
2511
- // Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
2512
1859
  const argstring = 'npm ' + args.join(' ');
2513
1860
  args.splice(0, args.length, '/c', argstring);
2514
1861
  command = 'cmd.exe';
2515
1862
  }
2516
- // Decide when using sudo on linux
2517
- // When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
2518
- // When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
2519
1863
  if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
2520
1864
  args.unshift(command);
2521
1865
  command = 'sudo';
@@ -2574,4 +1918,3 @@ export class Matterbridge extends EventEmitter {
2574
1918
  });
2575
1919
  }
2576
1920
  }
2577
- //# sourceMappingURL=matterbridge.js.map