matterbridge 3.0.1 → 3.0.2-dev-20250509-ae61aa7

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