matterbridge 2.2.3 → 2.2.4-dev.2

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