matterbridge 3.1.0 → 3.1.1-dev-20250629-cfe9124

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 (194) hide show
  1. package/CHANGELOG.md +25 -4
  2. package/README-DEV.md +17 -3
  3. package/README-DOCKER.md +3 -1
  4. package/README-NGINX.md +30 -22
  5. package/README-PODMAN.md +3 -1
  6. package/README-SERVICE.md +8 -2
  7. package/dist/batteryStorage.js +24 -0
  8. package/dist/cli.js +2 -91
  9. package/dist/clusters/export.js +0 -2
  10. package/dist/defaultConfigSchema.js +0 -24
  11. package/dist/deviceManager.js +1 -94
  12. package/dist/devices/export.js +2 -2
  13. package/dist/evse.js +9 -70
  14. package/dist/frontend.js +16 -413
  15. package/dist/globalMatterbridge.js +0 -47
  16. package/dist/helpers.js +0 -53
  17. package/dist/index.js +3 -32
  18. package/dist/laundryWasher.js +7 -92
  19. package/dist/logger/export.js +0 -1
  20. package/dist/matter/behaviors.js +0 -2
  21. package/dist/matter/clusters.js +0 -2
  22. package/dist/matter/devices.js +0 -2
  23. package/dist/matter/endpoints.js +0 -2
  24. package/dist/matter/export.js +0 -3
  25. package/dist/matter/types.js +0 -3
  26. package/dist/matterbridge.js +50 -797
  27. package/dist/matterbridgeAccessoryPlatform.js +0 -36
  28. package/dist/matterbridgeBehaviors.js +16 -55
  29. package/dist/matterbridgeDeviceTypes.js +15 -579
  30. package/dist/matterbridgeDynamicPlatform.js +0 -36
  31. package/dist/matterbridgeEndpoint.js +66 -1025
  32. package/dist/matterbridgeEndpointHelpers.js +12 -322
  33. package/dist/matterbridgePlatform.js +0 -233
  34. package/dist/matterbridgeTypes.js +0 -25
  35. package/dist/pluginManager.js +3 -269
  36. package/dist/roboticVacuumCleaner.js +6 -83
  37. package/dist/shelly.js +7 -168
  38. package/dist/solarPower.js +20 -0
  39. package/dist/storage/export.js +0 -1
  40. package/dist/update.js +0 -54
  41. package/dist/utils/colorUtils.js +2 -263
  42. package/dist/utils/commandLine.js +0 -54
  43. package/dist/utils/copyDirectory.js +1 -38
  44. package/dist/utils/createDirectory.js +0 -33
  45. package/dist/utils/createZip.js +2 -47
  46. package/dist/utils/deepCopy.js +0 -39
  47. package/dist/utils/deepEqual.js +1 -72
  48. package/dist/utils/export.js +0 -1
  49. package/dist/utils/hex.js +0 -58
  50. package/dist/utils/isvalid.js +0 -101
  51. package/dist/utils/network.js +5 -83
  52. package/dist/utils/spawn.js +0 -18
  53. package/dist/utils/wait.js +9 -62
  54. package/dist/waterHeater.js +2 -77
  55. package/npm-shrinkwrap.json +2 -2
  56. package/package.json +1 -2
  57. package/dist/cli.d.ts +0 -29
  58. package/dist/cli.d.ts.map +0 -1
  59. package/dist/cli.js.map +0 -1
  60. package/dist/clusters/export.d.ts +0 -2
  61. package/dist/clusters/export.d.ts.map +0 -1
  62. package/dist/clusters/export.js.map +0 -1
  63. package/dist/defaultConfigSchema.d.ts +0 -28
  64. package/dist/defaultConfigSchema.d.ts.map +0 -1
  65. package/dist/defaultConfigSchema.js.map +0 -1
  66. package/dist/deviceManager.d.ts +0 -112
  67. package/dist/deviceManager.d.ts.map +0 -1
  68. package/dist/deviceManager.js.map +0 -1
  69. package/dist/devices/export.d.ts +0 -5
  70. package/dist/devices/export.d.ts.map +0 -1
  71. package/dist/devices/export.js.map +0 -1
  72. package/dist/evse.d.ts +0 -72
  73. package/dist/evse.d.ts.map +0 -1
  74. package/dist/evse.js.map +0 -1
  75. package/dist/frontend.d.ts +0 -285
  76. package/dist/frontend.d.ts.map +0 -1
  77. package/dist/frontend.js.map +0 -1
  78. package/dist/globalMatterbridge.d.ts +0 -59
  79. package/dist/globalMatterbridge.d.ts.map +0 -1
  80. package/dist/globalMatterbridge.js.map +0 -1
  81. package/dist/helpers.d.ts +0 -48
  82. package/dist/helpers.d.ts.map +0 -1
  83. package/dist/helpers.js.map +0 -1
  84. package/dist/index.d.ts +0 -38
  85. package/dist/index.d.ts.map +0 -1
  86. package/dist/index.js.map +0 -1
  87. package/dist/laundryWasher.d.ts +0 -243
  88. package/dist/laundryWasher.d.ts.map +0 -1
  89. package/dist/laundryWasher.js.map +0 -1
  90. package/dist/logger/export.d.ts +0 -2
  91. package/dist/logger/export.d.ts.map +0 -1
  92. package/dist/logger/export.js.map +0 -1
  93. package/dist/matter/behaviors.d.ts +0 -2
  94. package/dist/matter/behaviors.d.ts.map +0 -1
  95. package/dist/matter/behaviors.js.map +0 -1
  96. package/dist/matter/clusters.d.ts +0 -2
  97. package/dist/matter/clusters.d.ts.map +0 -1
  98. package/dist/matter/clusters.js.map +0 -1
  99. package/dist/matter/devices.d.ts +0 -2
  100. package/dist/matter/devices.d.ts.map +0 -1
  101. package/dist/matter/devices.js.map +0 -1
  102. package/dist/matter/endpoints.d.ts +0 -2
  103. package/dist/matter/endpoints.d.ts.map +0 -1
  104. package/dist/matter/endpoints.js.map +0 -1
  105. package/dist/matter/export.d.ts +0 -5
  106. package/dist/matter/export.d.ts.map +0 -1
  107. package/dist/matter/export.js.map +0 -1
  108. package/dist/matter/types.d.ts +0 -3
  109. package/dist/matter/types.d.ts.map +0 -1
  110. package/dist/matter/types.js.map +0 -1
  111. package/dist/matterbridge.d.ts +0 -450
  112. package/dist/matterbridge.d.ts.map +0 -1
  113. package/dist/matterbridge.js.map +0 -1
  114. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
  115. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  116. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  117. package/dist/matterbridgeBehaviors.d.ts +0 -1334
  118. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  119. package/dist/matterbridgeBehaviors.js.map +0 -1
  120. package/dist/matterbridgeDeviceTypes.d.ts +0 -709
  121. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  122. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  123. package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
  124. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  125. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  126. package/dist/matterbridgeEndpoint.d.ts +0 -1173
  127. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  128. package/dist/matterbridgeEndpoint.js.map +0 -1
  129. package/dist/matterbridgeEndpointHelpers.d.ts +0 -3198
  130. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  131. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  132. package/dist/matterbridgePlatform.d.ts +0 -310
  133. package/dist/matterbridgePlatform.d.ts.map +0 -1
  134. package/dist/matterbridgePlatform.js.map +0 -1
  135. package/dist/matterbridgeTypes.d.ts +0 -184
  136. package/dist/matterbridgeTypes.d.ts.map +0 -1
  137. package/dist/matterbridgeTypes.js.map +0 -1
  138. package/dist/pluginManager.d.ts +0 -291
  139. package/dist/pluginManager.d.ts.map +0 -1
  140. package/dist/pluginManager.js.map +0 -1
  141. package/dist/roboticVacuumCleaner.d.ts +0 -104
  142. package/dist/roboticVacuumCleaner.d.ts.map +0 -1
  143. package/dist/roboticVacuumCleaner.js.map +0 -1
  144. package/dist/shelly.d.ts +0 -174
  145. package/dist/shelly.d.ts.map +0 -1
  146. package/dist/shelly.js.map +0 -1
  147. package/dist/storage/export.d.ts +0 -2
  148. package/dist/storage/export.d.ts.map +0 -1
  149. package/dist/storage/export.js.map +0 -1
  150. package/dist/update.d.ts +0 -59
  151. package/dist/update.d.ts.map +0 -1
  152. package/dist/update.js.map +0 -1
  153. package/dist/utils/colorUtils.d.ts +0 -117
  154. package/dist/utils/colorUtils.d.ts.map +0 -1
  155. package/dist/utils/colorUtils.js.map +0 -1
  156. package/dist/utils/commandLine.d.ts +0 -59
  157. package/dist/utils/commandLine.d.ts.map +0 -1
  158. package/dist/utils/commandLine.js.map +0 -1
  159. package/dist/utils/copyDirectory.d.ts +0 -33
  160. package/dist/utils/copyDirectory.d.ts.map +0 -1
  161. package/dist/utils/copyDirectory.js.map +0 -1
  162. package/dist/utils/createDirectory.d.ts +0 -34
  163. package/dist/utils/createDirectory.d.ts.map +0 -1
  164. package/dist/utils/createDirectory.js.map +0 -1
  165. package/dist/utils/createZip.d.ts +0 -39
  166. package/dist/utils/createZip.d.ts.map +0 -1
  167. package/dist/utils/createZip.js.map +0 -1
  168. package/dist/utils/deepCopy.d.ts +0 -32
  169. package/dist/utils/deepCopy.d.ts.map +0 -1
  170. package/dist/utils/deepCopy.js.map +0 -1
  171. package/dist/utils/deepEqual.d.ts +0 -54
  172. package/dist/utils/deepEqual.d.ts.map +0 -1
  173. package/dist/utils/deepEqual.js.map +0 -1
  174. package/dist/utils/export.d.ts +0 -12
  175. package/dist/utils/export.d.ts.map +0 -1
  176. package/dist/utils/export.js.map +0 -1
  177. package/dist/utils/hex.d.ts +0 -49
  178. package/dist/utils/hex.d.ts.map +0 -1
  179. package/dist/utils/hex.js.map +0 -1
  180. package/dist/utils/isvalid.d.ts +0 -103
  181. package/dist/utils/isvalid.d.ts.map +0 -1
  182. package/dist/utils/isvalid.js.map +0 -1
  183. package/dist/utils/network.d.ts +0 -76
  184. package/dist/utils/network.d.ts.map +0 -1
  185. package/dist/utils/network.js.map +0 -1
  186. package/dist/utils/spawn.d.ts +0 -14
  187. package/dist/utils/spawn.d.ts.map +0 -1
  188. package/dist/utils/spawn.js.map +0 -1
  189. package/dist/utils/wait.d.ts +0 -56
  190. package/dist/utils/wait.d.ts.map +0 -1
  191. package/dist/utils/wait.js.map +0 -1
  192. package/dist/waterHeater.d.ts +0 -106
  193. package/dist/waterHeater.d.ts.map +0 -1
  194. package/dist/waterHeater.js.map +0 -1
@@ -1,43 +1,15 @@
1
- /**
2
- * This file contains the class Matterbridge.
3
- *
4
- * @file matterbridge.ts
5
- * @author Luca Liguori
6
- * @created 2023-12-29
7
- * @version 1.6.0
8
- * @license Apache-2.0
9
- *
10
- * Copyright 2023, 2024, 2025 Luca Liguori.
11
- *
12
- * Licensed under the Apache License, Version 2.0 (the "License");
13
- * you may not use this file except in compliance with the License.
14
- * You may obtain a copy of the License at
15
- *
16
- * http://www.apache.org/licenses/LICENSE-2.0
17
- *
18
- * Unless required by applicable law or agreed to in writing, software
19
- * distributed under the License is distributed on an "AS IS" BASIS,
20
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
- * See the License for the specific language governing permissions and
22
- * limitations under the License.
23
- */
24
- // Node.js modules
25
1
  import os from 'node:os';
26
2
  import path from 'node:path';
27
3
  import { promises as fs } from 'node:fs';
28
4
  import EventEmitter from 'node:events';
29
5
  import { inspect } from 'node:util';
30
- // AnsiLogger module
31
6
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from 'node-ansi-logger';
32
- // NodeStorage module
33
7
  import { NodeStorageManager } from 'node-persist-manager';
34
- // @matter
35
8
  import { DeviceTypeId, Endpoint, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode, UINT32_MAX, UINT16_MAX, Crypto, } from '@matter/main';
36
9
  import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
37
10
  import { AggregatorEndpoint } from '@matter/main/endpoints';
38
11
  import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
39
12
  import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
40
- // Matterbridge
41
13
  import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout, waiter, isValidString, parseVersionString, isValidNumber, createDirectory } from './utils/export.js';
42
14
  import { logInterfaces, getGlobalNodeModules } from './utils/network.js';
43
15
  import { dev, plg, typ } from './matterbridgeTypes.js';
@@ -48,9 +20,6 @@ import { bridge } from './matterbridgeDeviceTypes.js';
48
20
  import { Frontend } from './frontend.js';
49
21
  import { addVirtualDevices } from './helpers.js';
50
22
  import spawn from './utils/spawn.js';
51
- /**
52
- * Represents the Matterbridge application.
53
- */
54
23
  export class Matterbridge extends EventEmitter {
55
24
  systemInformation = {
56
25
  interfaceName: '',
@@ -98,7 +67,7 @@ export class Matterbridge extends EventEmitter {
98
67
  shellySysUpdate: false,
99
68
  shellyMainUpdate: false,
100
69
  profile: getParameter('profile'),
101
- loggerLevel: "info" /* LogLevel.INFO */,
70
+ loggerLevel: "info",
102
71
  fileLogger: false,
103
72
  matterLoggerLevel: MatterLogLevel.INFO,
104
73
  matterFileLogger: false,
@@ -131,18 +100,15 @@ export class Matterbridge extends EventEmitter {
131
100
  shutdown = false;
132
101
  edge = true;
133
102
  failCountLimit = hasParameter('shelly') ? 600 : 120;
134
- // Matterbridge log files
135
- log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
103
+ log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
136
104
  matterbrideLoggerFile = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
137
105
  matterLoggerFile = 'matter' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
138
106
  plugins;
139
107
  devices;
140
108
  frontend = new Frontend(this);
141
- // Matterbridge storage
142
109
  nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
143
110
  nodeStorage;
144
111
  nodeContext;
145
- // Cleanup
146
112
  hasCleanupStarted = false;
147
113
  initialized = false;
148
114
  execRunningCount = 0;
@@ -156,23 +122,19 @@ export class Matterbridge extends EventEmitter {
156
122
  sigtermHandler;
157
123
  exceptionHandler;
158
124
  rejectionHandler;
159
- // Matter environment
160
125
  environment = Environment.default;
161
- // Matter storage
162
126
  matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
163
127
  matterStorageService;
164
128
  matterStorageManager;
165
129
  matterbridgeContext;
166
130
  controllerContext;
167
- // Matter parameters
168
- mdnsInterface; // matter server node mdnsInterface: e.g. 'eth0' or 'wlan0' or 'WiFi'
169
- ipv4address; // matter server node listeningAddressIpv4
170
- ipv6address; // matter server node listeningAddressIpv6
171
- port; // first server node port
172
- passcode; // first server node passcode
173
- discriminator; // first server node discriminator
174
- certification; // device certification
175
- // Matter nodes
131
+ mdnsInterface;
132
+ ipv4address;
133
+ ipv6address;
134
+ port;
135
+ passcode;
136
+ discriminator;
137
+ certification;
176
138
  serverNode;
177
139
  aggregatorNode;
178
140
  aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
@@ -180,31 +142,15 @@ export class Matterbridge extends EventEmitter {
180
142
  aggregatorProductId = getIntParameter('productId') ?? 0x8000;
181
143
  aggregatorProductName = getParameter('productName') ?? 'Matterbridge aggregator';
182
144
  static instance;
183
- // We load asyncronously so is private
184
145
  constructor() {
185
146
  super();
186
147
  }
187
- /**
188
- * Retrieves the list of Matterbridge devices.
189
- *
190
- * @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
191
- */
192
148
  getDevices() {
193
149
  return this.devices.array();
194
150
  }
195
- /**
196
- * Retrieves the list of registered plugins.
197
- *
198
- * @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
199
- */
200
151
  getPlugins() {
201
152
  return this.plugins.array();
202
153
  }
203
- /**
204
- * Set the logger logLevel for the Matterbridge classes and call onChangeLoggerLevel() for each plugin.
205
- *
206
- * @param {LogLevel} logLevel The logger logLevel to set.
207
- */
208
154
  async setLogLevel(logLevel) {
209
155
  if (this.log)
210
156
  this.log.logLevel = logLevel;
@@ -218,31 +164,19 @@ export class Matterbridge extends EventEmitter {
218
164
  for (const plugin of this.plugins) {
219
165
  if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
220
166
  continue;
221
- plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel;
222
- await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel);
223
- }
224
- // Set the global logger callback for the WebSocketServer to the common minimum logLevel
225
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
226
- if (this.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
227
- callbackLogLevel = "info" /* LogLevel.INFO */;
228
- if (this.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
229
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
167
+ plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
168
+ await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
169
+ }
170
+ let callbackLogLevel = "notice";
171
+ if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
172
+ callbackLogLevel = "info";
173
+ if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
174
+ callbackLogLevel = "debug";
230
175
  AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
231
176
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
232
177
  }
233
- //* ************************************************************************************************************************************ */
234
- // loadInstance() and cleanup() methods */
235
- //* ************************************************************************************************************************************ */
236
- /**
237
- * Loads an instance of the Matterbridge class.
238
- * If an instance already exists, return that instance.
239
- *
240
- * @param {boolean} initialize - Whether to initialize the Matterbridge instance after loading. Defaults to false.
241
- * @returns {Matterbridge} A promise that resolves to the Matterbridge instance.
242
- */
243
178
  static async loadInstance(initialize = false) {
244
179
  if (!Matterbridge.instance) {
245
- // eslint-disable-next-line no-console
246
180
  if (hasParameter('debug'))
247
181
  console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
248
182
  Matterbridge.instance = new Matterbridge();
@@ -251,17 +185,8 @@ export class Matterbridge extends EventEmitter {
251
185
  }
252
186
  return Matterbridge.instance;
253
187
  }
254
- /**
255
- * Call cleanup() and dispose MdnsService.
256
- *
257
- * @param {number} [timeout] - The timeout duration to wait for the cleanup to complete in milliseconds. Default is 1000.
258
- * @param {number} [pause] - The pause duration after the cleanup in milliseconds. Default is 500.
259
- *
260
- * @deprecated This method is deprecated and is ONLY used for jest tests.
261
- */
262
188
  async destroyInstance(timeout = 1000, pause = 250) {
263
189
  this.log.info(`Destroy instance...`);
264
- // Save server nodes to close
265
190
  const servers = [];
266
191
  if (this.bridgeMode === 'bridge') {
267
192
  if (this.serverNode)
@@ -279,109 +204,76 @@ export class Matterbridge extends EventEmitter {
279
204
  servers.push(device.serverNode);
280
205
  }
281
206
  }
282
- // Let any already‐queued microtasks run first
283
207
  await Promise.resolve();
284
- // Wait for the cleanup to finish
285
208
  await new Promise((resolve) => {
286
209
  setTimeout(resolve, pause);
287
210
  });
288
- // Cleanup
289
211
  await this.cleanup('destroying instance...', false, timeout);
290
- // Close servers mdns service
291
212
  this.log.info(`Dispose ${servers.length} MdnsService...`);
292
213
  for (const server of servers) {
293
214
  await server.env.get(MdnsService)[Symbol.asyncDispose]();
294
215
  this.log.info(`Closed ${server.id} MdnsService`);
295
216
  }
296
- // Let any already‐queued microtasks run first
297
217
  await Promise.resolve();
298
- // Wait for the cleanup to finish
299
218
  await new Promise((resolve) => {
300
219
  setTimeout(resolve, pause);
301
220
  });
302
221
  }
303
- /**
304
- * Initializes the Matterbridge application.
305
- *
306
- * @remarks
307
- * This method performs the necessary setup and initialization steps for the Matterbridge application.
308
- * It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
309
- * node version, registers signal handlers, initializes storage, and parses the command line.
310
- *
311
- * @returns {Promise<void>} A Promise that resolves when the initialization is complete.
312
- */
313
222
  async initialize() {
314
- // Emit the initialize_started event
315
223
  this.emit('initialize_started');
316
- // Set the restart mode
317
224
  if (hasParameter('service'))
318
225
  this.restartMode = 'service';
319
226
  if (hasParameter('docker'))
320
227
  this.restartMode = 'docker';
321
- // Set the matterbridge home directory
322
228
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
323
229
  this.matterbridgeInformation.homeDirectory = this.homeDirectory;
324
230
  await createDirectory(this.homeDirectory, 'Matterbridge Home Directory', this.log);
325
- // Set the matterbridge directory
326
231
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
327
232
  this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
328
233
  await createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory', this.log);
329
234
  await createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory', this.log);
330
235
  await createDirectory(path.join(this.matterbridgeDirectory, 'uploads'), 'Matterbridge Frontend Uploads Directory', this.log);
331
- // Set the matterbridge plugin directory
332
236
  this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
333
237
  this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
334
238
  await createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory', this.log);
335
- // Set the matterbridge cert directory
336
239
  this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
337
240
  this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
338
241
  await createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory', this.log);
339
- // Set the matterbridge root directory
340
242
  const { fileURLToPath } = await import('node:url');
341
243
  const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
342
244
  this.rootDirectory = path.resolve(currentFileDirectory, '../');
343
245
  this.matterbridgeInformation.rootDirectory = this.rootDirectory;
344
- // Setup the matter environment
345
246
  this.environment.vars.set('log.level', MatterLogLevel.INFO);
346
247
  this.environment.vars.set('log.format', MatterLogFormat.ANSI);
347
248
  this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
348
249
  this.environment.vars.set('runtime.signals', false);
349
250
  this.environment.vars.set('runtime.exitcode', false);
350
- // Register process handlers
351
251
  this.registerProcessHandlers();
352
- // Initialize nodeStorage and nodeContext
353
252
  try {
354
253
  this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
355
254
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
356
255
  this.log.debug('Creating node storage context for matterbridge');
357
256
  this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
358
- // TODO: Remove this code when node-persist-manager is updated
359
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
360
257
  const keys = (await this.nodeStorage?.storage.keys());
361
258
  for (const key of keys) {
362
259
  this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
363
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
364
260
  await this.nodeStorage?.storage.get(key);
365
261
  }
366
262
  const storages = await this.nodeStorage.getStorageNames();
367
263
  for (const storage of storages) {
368
264
  this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
369
265
  const nodeContext = await this.nodeStorage?.createStorage(storage);
370
- // TODO: Remove this code when node-persist-manager is updated
371
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
372
266
  const keys = (await nodeContext?.storage.keys());
373
267
  keys.forEach(async (key) => {
374
268
  this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
375
269
  await nodeContext?.get(key);
376
270
  });
377
271
  }
378
- // Creating a backup of the node storage since it is not corrupted
379
272
  this.log.debug('Creating node storage backup...');
380
273
  await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
381
274
  this.log.debug('Created node storage backup');
382
275
  }
383
276
  catch (error) {
384
- // Restoring the backup of the node storage since it is corrupted
385
277
  this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
386
278
  if (hasParameter('norestore')) {
387
279
  this.log.fatal(`The matterbridge storage is corrupted. Found -norestore parameter: exiting...`);
@@ -395,20 +287,14 @@ export class Matterbridge extends EventEmitter {
395
287
  if (!this.nodeStorage || !this.nodeContext) {
396
288
  throw new Error('Fatal error creating node storage manager and context for matterbridge');
397
289
  }
398
- // Set the first port to use for the commissioning server (will be incremented in childbridge mode)
399
290
  this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
400
- // Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
401
291
  this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode(this.environment.get(Crypto));
402
- // Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
403
292
  this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
404
- // Certificate management
405
293
  const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
406
294
  try {
407
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
408
295
  await fs.access(pairingFilePath, fs.constants.R_OK);
409
296
  const pairingFileContent = await fs.readFile(pairingFilePath, 'utf8');
410
297
  const pairingFileJson = JSON.parse(pairingFileContent);
411
- // Set the vendorId, vendorName, productId and productName if they are present in the pairing file
412
298
  if (isValidNumber(pairingFileJson.vendorId))
413
299
  this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
414
300
  if (isValidString(pairingFileJson.vendorName, 3))
@@ -417,73 +303,50 @@ export class Matterbridge extends EventEmitter {
417
303
  this.aggregatorProductId = pairingFileJson.productId;
418
304
  if (isValidString(pairingFileJson.productName, 3))
419
305
  this.aggregatorProductName = pairingFileJson.productName;
420
- // Override the passcode and discriminator if they are present in the pairing file
421
306
  if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
422
307
  this.passcode = pairingFileJson.passcode;
423
308
  this.discriminator = pairingFileJson.discriminator;
424
309
  this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
425
310
  }
426
- // Set the certification if it is present in the pairing file
427
- /*
428
- if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
429
- const hexStringToUint8Array = (hexString: string) => {
430
- const matches = hexString.match(/.{1,2}/g);
431
- return matches ? new Uint8Array(matches.map((byte) => parseInt(byte, 16))) : new Uint8Array();
432
- };
433
- // const hexString = Buffer.from('Test string', 'utf-8').toString('hex');
434
- // console.log(hexString, Buffer.from(hexStringToUint8Array(hexString)).toString('utf-8'));
435
-
436
- this.certification = {
437
- privateKey: hexStringToUint8Array(pairingFileJson.privateKey),
438
- certificate: hexStringToUint8Array(pairingFileJson.certificate),
439
- intermediateCertificate: hexStringToUint8Array(pairingFileJson.intermediateCertificate),
440
- declaration: hexStringToUint8Array(pairingFileJson.declaration),
441
- };
442
- this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using privateKey, certificate, intermediateCertificate and declaration from pairing file.`);
443
- }
444
- */
445
311
  }
446
312
  catch (error) {
447
313
  this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
448
314
  }
449
- // Store the passcode, discriminator and port in the node context
450
315
  await this.nodeContext.set('matterport', this.port);
451
316
  await this.nodeContext.set('matterpasscode', this.passcode);
452
317
  await this.nodeContext.set('matterdiscriminator', this.discriminator);
453
318
  this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
454
- // Set matterbridge logger level (context: matterbridgeLogLevel)
455
319
  if (hasParameter('logger')) {
456
320
  const level = getParameter('logger');
457
321
  if (level === 'debug') {
458
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
322
+ this.log.logLevel = "debug";
459
323
  }
460
324
  else if (level === 'info') {
461
- this.log.logLevel = "info" /* LogLevel.INFO */;
325
+ this.log.logLevel = "info";
462
326
  }
463
327
  else if (level === 'notice') {
464
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
328
+ this.log.logLevel = "notice";
465
329
  }
466
330
  else if (level === 'warn') {
467
- this.log.logLevel = "warn" /* LogLevel.WARN */;
331
+ this.log.logLevel = "warn";
468
332
  }
469
333
  else if (level === 'error') {
470
- this.log.logLevel = "error" /* LogLevel.ERROR */;
334
+ this.log.logLevel = "error";
471
335
  }
472
336
  else if (level === 'fatal') {
473
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
337
+ this.log.logLevel = "fatal";
474
338
  }
475
339
  else {
476
340
  this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
477
- this.log.logLevel = "info" /* LogLevel.INFO */;
341
+ this.log.logLevel = "info";
478
342
  }
479
343
  }
480
344
  else {
481
- this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" /* LogLevel.NOTICE */ : "info" /* LogLevel.INFO */);
345
+ this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
482
346
  }
483
347
  this.frontend.logLevel = this.log.logLevel;
484
348
  MatterbridgeEndpoint.logLevel = this.log.logLevel;
485
349
  this.matterbridgeInformation.loggerLevel = this.log.logLevel;
486
- // Create the file logger for matterbridge (context: matterbridgeFileLog)
487
350
  if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
488
351
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
489
352
  this.matterbridgeInformation.fileLogger = true;
@@ -492,7 +355,6 @@ export class Matterbridge extends EventEmitter {
492
355
  this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
493
356
  if (this.profile !== undefined)
494
357
  this.log.debug(`Matterbridge profile: ${this.profile}.`);
495
- // Set matter.js logger level, format and logger (context: matterLogLevel)
496
358
  if (hasParameter('matterlogger')) {
497
359
  const level = getParameter('matterlogger');
498
360
  if (level === 'debug') {
@@ -524,7 +386,6 @@ export class Matterbridge extends EventEmitter {
524
386
  Logger.format = MatterLogFormat.ANSI;
525
387
  Logger.setLogger('default', this.createMatterLogger());
526
388
  this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
527
- // Create the file logger for matter.js (context: matterFileLog)
528
389
  if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
529
390
  this.matterbridgeInformation.matterFileLogger = true;
530
391
  Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
@@ -533,7 +394,6 @@ export class Matterbridge extends EventEmitter {
533
394
  });
534
395
  }
535
396
  this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
536
- // Log network interfaces
537
397
  const networkInterfaces = os.networkInterfaces();
538
398
  const availableAddresses = Object.entries(networkInterfaces);
539
399
  const availableInterfaces = Object.keys(networkInterfaces);
@@ -545,7 +405,6 @@ export class Matterbridge extends EventEmitter {
545
405
  });
546
406
  }
547
407
  }
548
- // Set the interface to use for matter server node mdnsInterface
549
408
  if (hasParameter('mdnsinterface')) {
550
409
  this.mdnsInterface = getParameter('mdnsinterface');
551
410
  }
@@ -554,7 +413,6 @@ export class Matterbridge extends EventEmitter {
554
413
  if (this.mdnsInterface === '')
555
414
  this.mdnsInterface = undefined;
556
415
  }
557
- // Validate mdnsInterface
558
416
  if (this.mdnsInterface) {
559
417
  if (!availableInterfaces.includes(this.mdnsInterface)) {
560
418
  this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
@@ -567,7 +425,6 @@ export class Matterbridge extends EventEmitter {
567
425
  }
568
426
  if (this.mdnsInterface)
569
427
  this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
570
- // Set the listeningAddressIpv4 for the matter commissioning server
571
428
  if (hasParameter('ipv4address')) {
572
429
  this.ipv4address = getParameter('ipv4address');
573
430
  }
@@ -576,7 +433,6 @@ export class Matterbridge extends EventEmitter {
576
433
  if (this.ipv4address === '')
577
434
  this.ipv4address = undefined;
578
435
  }
579
- // Validate ipv4address
580
436
  if (this.ipv4address) {
581
437
  let isValid = false;
582
438
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -592,7 +448,6 @@ export class Matterbridge extends EventEmitter {
592
448
  await this.nodeContext.remove('matteripv4address');
593
449
  }
594
450
  }
595
- // Set the listeningAddressIpv6 for the matter commissioning server
596
451
  if (hasParameter('ipv6address')) {
597
452
  this.ipv6address = getParameter('ipv6address');
598
453
  }
@@ -601,7 +456,6 @@ export class Matterbridge extends EventEmitter {
601
456
  if (this.ipv6address === '')
602
457
  this.ipv6address = undefined;
603
458
  }
604
- // Validate ipv6address
605
459
  if (this.ipv6address) {
606
460
  let isValid = false;
607
461
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -610,7 +464,6 @@ export class Matterbridge extends EventEmitter {
610
464
  isValid = true;
611
465
  break;
612
466
  }
613
- /* istanbul ignore next */
614
467
  if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6address)) {
615
468
  this.log.info(`Using ipv6address ${CYAN}${this.ipv6address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
616
469
  isValid = true;
@@ -623,7 +476,6 @@ export class Matterbridge extends EventEmitter {
623
476
  await this.nodeContext.remove('matteripv6address');
624
477
  }
625
478
  }
626
- // Initialize the virtual mode
627
479
  if (hasParameter('novirtual')) {
628
480
  this.matterbridgeInformation.virtualMode = 'disabled';
629
481
  await this.nodeContext.set('virtualmode', 'disabled');
@@ -632,19 +484,14 @@ export class Matterbridge extends EventEmitter {
632
484
  this.matterbridgeInformation.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
633
485
  }
634
486
  this.log.debug(`Virtual mode ${this.matterbridgeInformation.virtualMode}.`);
635
- // Initialize PluginManager
636
487
  this.plugins = new PluginManager(this);
637
488
  await this.plugins.loadFromStorage();
638
489
  this.plugins.logLevel = this.log.logLevel;
639
- // Initialize DeviceManager
640
490
  this.devices = new DeviceManager(this);
641
491
  this.devices.logLevel = this.log.logLevel;
642
- // Get the plugins from node storage and create the plugins node storage contexts
643
492
  for (const plugin of this.plugins) {
644
493
  const packageJson = await this.plugins.parse(plugin);
645
494
  if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
646
- // Try to reinstall the plugin from npm (for Docker pull and external plugins)
647
- // We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
648
495
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
649
496
  try {
650
497
  await spawn.spawnCommand(this, 'npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
@@ -666,7 +513,6 @@ export class Matterbridge extends EventEmitter {
666
513
  await plugin.nodeContext.set('description', plugin.description);
667
514
  await plugin.nodeContext.set('author', plugin.author);
668
515
  }
669
- // Log system info and create .matterbridge directory
670
516
  await this.logNodeAndSystemInfo();
671
517
  this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
672
518
  `${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
@@ -674,7 +520,6 @@ export class Matterbridge extends EventEmitter {
674
520
  `${hasParameter('controller') ? 'mode controller ' : ''}` +
675
521
  `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
676
522
  `running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
677
- // Check node version and throw error
678
523
  const minNodeVersion = 18;
679
524
  const nodeVersion = process.versions.node;
680
525
  const versionMajor = parseInt(nodeVersion.split('.')[0]);
@@ -682,18 +527,10 @@ export class Matterbridge extends EventEmitter {
682
527
  this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
683
528
  throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
684
529
  }
685
- // Parse command line
686
530
  await this.parseCommandLine();
687
- // Emit the initialize_completed event
688
531
  this.emit('initialize_completed');
689
532
  this.initialized = true;
690
533
  }
691
- /**
692
- * Parses the command line arguments and performs the corresponding actions.
693
- *
694
- * @private
695
- * @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
696
- */
697
534
  async parseCommandLine() {
698
535
  if (hasParameter('help')) {
699
536
  this.log.info(`\nUsage: matterbridge [options]\n
@@ -754,19 +591,6 @@ export class Matterbridge extends EventEmitter {
754
591
  }
755
592
  index++;
756
593
  }
757
- /*
758
- const serializedRegisteredDevices = await this.nodeContext?.get<SerializedMatterbridgeEndpoint[]>('devices', []);
759
- this.log.info(`│ Registered devices (${serializedRegisteredDevices?.length})`);
760
- serializedRegisteredDevices?.forEach((device, index) => {
761
- if (index !== serializedRegisteredDevices.length - 1) {
762
- this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
763
- this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
764
- } else {
765
- this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
766
- this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
767
- }
768
- });
769
- */
770
594
  this.shutdown = true;
771
595
  return;
772
596
  }
@@ -816,7 +640,6 @@ export class Matterbridge extends EventEmitter {
816
640
  this.shutdown = true;
817
641
  return;
818
642
  }
819
- // Start the matter storage and create the matterbridge context
820
643
  try {
821
644
  await this.startMatterStorage();
822
645
  }
@@ -824,14 +647,12 @@ export class Matterbridge extends EventEmitter {
824
647
  this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
825
648
  throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
826
649
  }
827
- // Clear the matterbridge context if the reset parameter is set
828
650
  if (hasParameter('reset') && getParameter('reset') === undefined) {
829
651
  this.initialized = true;
830
652
  await this.shutdownProcessAndReset();
831
653
  this.shutdown = true;
832
654
  return;
833
655
  }
834
- // Clear matterbridge plugin context if the reset parameter is set
835
656
  if (hasParameter('reset') && getParameter('reset') !== undefined) {
836
657
  this.log.debug(`Reset plugin ${getParameter('reset')}`);
837
658
  const plugin = this.plugins.get(getParameter('reset'));
@@ -856,37 +677,30 @@ export class Matterbridge extends EventEmitter {
856
677
  this.shutdown = true;
857
678
  return;
858
679
  }
859
- // Initialize frontend
860
680
  if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
861
681
  await this.frontend.start(getIntParameter('frontend'));
862
- // Check in 30 seconds the latest and dev versions of matterbridge and the plugins
863
682
  this.checkUpdateTimeout = setTimeout(async () => {
864
683
  const { checkUpdates } = await import('./update.js');
865
684
  checkUpdates(this);
866
685
  }, 30 * 1000).unref();
867
- // Check each 12 hours the latest and dev versions of matterbridge and the plugins
868
686
  this.checkUpdateInterval = setInterval(async () => {
869
687
  const { checkUpdates } = await import('./update.js');
870
688
  checkUpdates(this);
871
689
  }, 12 * 60 * 60 * 1000).unref();
872
- // Start the matterbridge in mode test
873
690
  if (hasParameter('test')) {
874
691
  this.bridgeMode = 'bridge';
875
692
  MatterbridgeEndpoint.bridgeMode = 'bridge';
876
693
  return;
877
694
  }
878
- // Start the matterbridge in mode controller
879
695
  if (hasParameter('controller')) {
880
696
  this.bridgeMode = 'controller';
881
697
  await this.startController();
882
698
  return;
883
699
  }
884
- // Check if the bridge mode is set and start matterbridge in bridge mode if not set
885
700
  if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
886
701
  this.log.info('Setting default matterbridge start mode to bridge');
887
702
  await this.nodeContext?.set('bridgeMode', 'bridge');
888
703
  }
889
- // Start matterbridge in bridge mode
890
704
  if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
891
705
  this.bridgeMode = 'bridge';
892
706
  MatterbridgeEndpoint.bridgeMode = 'bridge';
@@ -894,7 +708,6 @@ export class Matterbridge extends EventEmitter {
894
708
  await this.startBridge();
895
709
  return;
896
710
  }
897
- // Start matterbridge in childbridge mode
898
711
  if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
899
712
  this.bridgeMode = 'childbridge';
900
713
  MatterbridgeEndpoint.bridgeMode = 'childbridge';
@@ -903,20 +716,10 @@ export class Matterbridge extends EventEmitter {
903
716
  return;
904
717
  }
905
718
  }
906
- /**
907
- * Asynchronously loads and starts the registered plugins.
908
- *
909
- * This method is responsible for initializing and starting all enabled plugins.
910
- * It ensures that each plugin is properly loaded and started before the bridge starts.
911
- *
912
- * @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
913
- */
914
719
  async startPlugins() {
915
- // Check, load and start the plugins
916
720
  for (const plugin of this.plugins) {
917
721
  plugin.configJson = await this.plugins.loadConfig(plugin);
918
722
  plugin.schemaJson = await this.plugins.loadSchema(plugin);
919
- // Check if the plugin is available
920
723
  if (!(await this.plugins.resolve(plugin.path))) {
921
724
  this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
922
725
  plugin.enabled = false;
@@ -936,14 +739,10 @@ export class Matterbridge extends EventEmitter {
936
739
  plugin.addedDevices = undefined;
937
740
  plugin.qrPairingCode = undefined;
938
741
  plugin.manualPairingCode = undefined;
939
- this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
742
+ this.plugins.load(plugin, true, 'Matterbridge is starting');
940
743
  }
941
744
  this.frontend.wssSendRefreshRequired('plugins');
942
745
  }
943
- /**
944
- * Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
945
- * When either of these signals are received, the cleanup method is called with an appropriate message.
946
- */
947
746
  registerProcessHandlers() {
948
747
  this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
949
748
  process.removeAllListeners('uncaughtException');
@@ -970,9 +769,6 @@ export class Matterbridge extends EventEmitter {
970
769
  };
971
770
  process.on('SIGTERM', this.sigtermHandler);
972
771
  }
973
- /**
974
- * Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
975
- */
976
772
  deregisterProcessHandlers() {
977
773
  this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
978
774
  if (this.exceptionHandler)
@@ -989,17 +785,12 @@ export class Matterbridge extends EventEmitter {
989
785
  process.off('SIGTERM', this.sigtermHandler);
990
786
  this.sigtermHandler = undefined;
991
787
  }
992
- /**
993
- * Logs the node and system information.
994
- */
995
788
  async logNodeAndSystemInfo() {
996
- // IP address information
997
789
  const networkInterfaces = os.networkInterfaces();
998
790
  this.systemInformation.interfaceName = '';
999
791
  this.systemInformation.ipv4Address = '';
1000
792
  this.systemInformation.ipv6Address = '';
1001
793
  for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
1002
- // this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
1003
794
  if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
1004
795
  continue;
1005
796
  if (!interfaceDetails) {
@@ -1025,22 +816,19 @@ export class Matterbridge extends EventEmitter {
1025
816
  break;
1026
817
  }
1027
818
  }
1028
- // Node information
1029
819
  this.systemInformation.nodeVersion = process.versions.node;
1030
820
  const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
1031
821
  const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
1032
822
  const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
1033
- // Host system information
1034
823
  this.systemInformation.hostname = os.hostname();
1035
824
  this.systemInformation.user = os.userInfo().username;
1036
- this.systemInformation.osType = os.type(); // "Windows_NT", "Darwin", etc.
1037
- this.systemInformation.osRelease = os.release(); // Kernel version
1038
- this.systemInformation.osPlatform = os.platform(); // "win32", "linux", "darwin", etc.
1039
- this.systemInformation.osArch = os.arch(); // "x64", "arm", etc.
1040
- this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
1041
- this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
1042
- this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours'; // Convert to hours
1043
- // Log the system information
825
+ this.systemInformation.osType = os.type();
826
+ this.systemInformation.osRelease = os.release();
827
+ this.systemInformation.osPlatform = os.platform();
828
+ this.systemInformation.osArch = os.arch();
829
+ this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
830
+ this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
831
+ this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
1044
832
  this.log.debug('Host System Information:');
1045
833
  this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
1046
834
  this.log.debug(`- User: ${this.systemInformation.user}`);
@@ -1056,17 +844,14 @@ export class Matterbridge extends EventEmitter {
1056
844
  this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
1057
845
  this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
1058
846
  this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
1059
- // Log directories
1060
847
  this.log.debug(`Root Directory: ${this.rootDirectory}`);
1061
848
  this.log.debug(`Home Directory: ${this.homeDirectory}`);
1062
849
  this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
1063
850
  this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
1064
851
  this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
1065
- // Global node_modules directory
1066
852
  if (this.nodeContext)
1067
853
  this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
1068
854
  if (this.globalModulesDirectory === '') {
1069
- // First run of Matterbridge so the node storage is empty
1070
855
  try {
1071
856
  this.execRunningCount++;
1072
857
  this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory = await getGlobalNodeModules();
@@ -1080,84 +865,53 @@ export class Matterbridge extends EventEmitter {
1080
865
  }
1081
866
  else
1082
867
  this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
1083
- /* removed cause is too expensive for the shelly board and not really needed. Why should the globalModulesDirectory change?
1084
- else {
1085
- this.getGlobalNodeModules()
1086
- .then(async (globalModulesDirectory) => {
1087
- this.globalModulesDirectory = globalModulesDirectory;
1088
- this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
1089
- this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
1090
- await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
1091
- })
1092
- .catch((error) => {
1093
- this.log.error(`Error getting global node_modules directory: ${error}`);
1094
- });
1095
- }*/
1096
- // Matterbridge version
1097
868
  const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
1098
869
  this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
1099
870
  this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeDevVersion = packageJson.version;
1100
871
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
1101
- // Matterbridge latest version (will be set in the checkUpdate function)
1102
872
  if (this.nodeContext)
1103
873
  this.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
1104
874
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
1105
- // Matterbridge dev version (will be set in the checkUpdate function)
1106
875
  if (this.nodeContext)
1107
876
  this.matterbridgeDevVersion = this.matterbridgeInformation.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
1108
877
  this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
1109
- // Current working directory
1110
878
  const currentDir = process.cwd();
1111
879
  this.log.debug(`Current Working Directory: ${currentDir}`);
1112
- // Command line arguments (excluding 'node' and the script name)
1113
880
  const cmdArgs = process.argv.slice(2).join(' ');
1114
881
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
1115
882
  }
1116
- /**
1117
- * Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
1118
- *
1119
- * @returns {Function} The MatterLogger function.
1120
- */
1121
883
  createMatterLogger() {
1122
- const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
884
+ const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
1123
885
  return (level, formattedLog) => {
1124
886
  const logger = formattedLog.slice(44, 44 + 20).trim();
1125
887
  const message = formattedLog.slice(65);
1126
888
  matterLogger.logName = logger;
1127
889
  switch (level) {
1128
890
  case MatterLogLevel.DEBUG:
1129
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
891
+ matterLogger.log("debug", message);
1130
892
  break;
1131
893
  case MatterLogLevel.INFO:
1132
- matterLogger.log("info" /* LogLevel.INFO */, message);
894
+ matterLogger.log("info", message);
1133
895
  break;
1134
896
  case MatterLogLevel.NOTICE:
1135
- matterLogger.log("notice" /* LogLevel.NOTICE */, message);
897
+ matterLogger.log("notice", message);
1136
898
  break;
1137
899
  case MatterLogLevel.WARN:
1138
- matterLogger.log("warn" /* LogLevel.WARN */, message);
900
+ matterLogger.log("warn", message);
1139
901
  break;
1140
902
  case MatterLogLevel.ERROR:
1141
- matterLogger.log("error" /* LogLevel.ERROR */, message);
903
+ matterLogger.log("error", message);
1142
904
  break;
1143
905
  case MatterLogLevel.FATAL:
1144
- matterLogger.log("fatal" /* LogLevel.FATAL */, message);
906
+ matterLogger.log("fatal", message);
1145
907
  break;
1146
908
  default:
1147
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
909
+ matterLogger.log("debug", message);
1148
910
  break;
1149
911
  }
1150
912
  };
1151
913
  }
1152
- /**
1153
- * Creates a Matter File Logger.
1154
- *
1155
- * @param {string} filePath - The path to the log file.
1156
- * @param {boolean} [unlink] - Whether to unlink the log file before creating a new one.
1157
- * @returns {Function} - A function that logs formatted messages to the log file.
1158
- */
1159
914
  async createMatterFileLogger(filePath, unlink = false) {
1160
- // 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
1161
915
  let fileSize = 0;
1162
916
  if (unlink) {
1163
917
  try {
@@ -1206,21 +960,12 @@ export class Matterbridge extends EventEmitter {
1206
960
  }
1207
961
  };
1208
962
  }
1209
- /**
1210
- * Restarts the process by exiting the current instance and loading a new instance.
1211
- */
1212
963
  async restartProcess() {
1213
964
  await this.cleanup('restarting...', true);
1214
965
  }
1215
- /**
1216
- * Shut down the process by exiting the current process.
1217
- */
1218
966
  async shutdownProcess() {
1219
967
  await this.cleanup('shutting down...', false);
1220
968
  }
1221
- /**
1222
- * Update matterbridge and and shut down the process.
1223
- */
1224
969
  async updateProcess() {
1225
970
  this.log.info('Updating matterbridge...');
1226
971
  try {
@@ -1233,75 +978,52 @@ export class Matterbridge extends EventEmitter {
1233
978
  this.frontend.wssSendRestartRequired();
1234
979
  await this.cleanup('updating...', false);
1235
980
  }
1236
- /**
1237
- * Unregister all devices and shut down the process.
1238
- */
1239
981
  async unregisterAndShutdownProcess() {
1240
982
  this.log.info('Unregistering all devices and shutting down...');
1241
983
  for (const plugin of this.plugins) {
1242
984
  await this.removeAllBridgedEndpoints(plugin.name, 250);
1243
985
  }
1244
986
  this.log.debug('Waiting for the MessageExchange to finish...');
1245
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second for MessageExchange to finish
987
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1246
988
  this.log.debug('Cleaning up and shutting down...');
1247
989
  await this.cleanup('unregistered all devices and shutting down...', false);
1248
990
  }
1249
- /**
1250
- * Reset commissioning and shut down the process.
1251
- */
1252
991
  async shutdownProcessAndReset() {
1253
992
  await this.cleanup('shutting down with reset...', false);
1254
993
  }
1255
- /**
1256
- * Factory reset and shut down the process.
1257
- */
1258
994
  async shutdownProcessAndFactoryReset() {
1259
995
  await this.cleanup('shutting down with factory reset...', false);
1260
996
  }
1261
- /**
1262
- * Cleans up the Matterbridge instance.
1263
- *
1264
- * @param {string} message - The cleanup message.
1265
- * @param {boolean} [restart] - Indicates whether to restart the instance after cleanup. Default is `false`.
1266
- * @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
1267
- * @returns {Promise<void>} A promise that resolves when the cleanup is completed.
1268
- */
1269
997
  async cleanup(message, restart = false, timeout = 1000) {
1270
998
  if (this.initialized && !this.hasCleanupStarted) {
1271
999
  this.emit('cleanup_started');
1272
1000
  this.hasCleanupStarted = true;
1273
1001
  this.log.info(message);
1274
- // Clear the start matter interval
1275
1002
  if (this.startMatterInterval) {
1276
1003
  clearInterval(this.startMatterInterval);
1277
1004
  this.startMatterInterval = undefined;
1278
1005
  this.log.debug('Start matter interval cleared');
1279
1006
  }
1280
- // Clear the check update timeout
1281
1007
  if (this.checkUpdateTimeout) {
1282
1008
  clearTimeout(this.checkUpdateTimeout);
1283
1009
  this.checkUpdateTimeout = undefined;
1284
1010
  this.log.debug('Check update timeout cleared');
1285
1011
  }
1286
- // Clear the check update interval
1287
1012
  if (this.checkUpdateInterval) {
1288
1013
  clearInterval(this.checkUpdateInterval);
1289
1014
  this.checkUpdateInterval = undefined;
1290
1015
  this.log.debug('Check update interval cleared');
1291
1016
  }
1292
- // Clear the configure timeout
1293
1017
  if (this.configureTimeout) {
1294
1018
  clearTimeout(this.configureTimeout);
1295
1019
  this.configureTimeout = undefined;
1296
1020
  this.log.debug('Matterbridge configure timeout cleared');
1297
1021
  }
1298
- // Clear the reachability timeout
1299
1022
  if (this.reachabilityTimeout) {
1300
1023
  clearTimeout(this.reachabilityTimeout);
1301
1024
  this.reachabilityTimeout = undefined;
1302
1025
  this.log.debug('Matterbridge reachability timeout cleared');
1303
1026
  }
1304
- // Calling the shutdown method of each plugin and clear the plugins reachability timeout
1305
1027
  for (const plugin of this.plugins) {
1306
1028
  if (!plugin.enabled || plugin.error)
1307
1029
  continue;
@@ -1312,10 +1034,9 @@ export class Matterbridge extends EventEmitter {
1312
1034
  this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
1313
1035
  }
1314
1036
  }
1315
- // Stop matter server nodes
1316
1037
  this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
1317
1038
  this.log.debug('Waiting for the MessageExchange to finish...');
1318
- await new Promise((resolve) => setTimeout(resolve, timeout)); // Wait for MessageExchange to finish
1039
+ await new Promise((resolve) => setTimeout(resolve, timeout));
1319
1040
  if (this.bridgeMode === 'bridge') {
1320
1041
  if (this.serverNode) {
1321
1042
  await this.stopServerNode(this.serverNode);
@@ -1338,7 +1059,6 @@ export class Matterbridge extends EventEmitter {
1338
1059
  }
1339
1060
  }
1340
1061
  this.log.notice('Stopped matter server nodes');
1341
- // Matter commisioning reset
1342
1062
  if (message === 'shutting down with reset...') {
1343
1063
  this.log.info('Resetting Matterbridge commissioning information...');
1344
1064
  await this.matterStorageManager?.createContext('events')?.clearAll();
@@ -1348,36 +1068,18 @@ export class Matterbridge extends EventEmitter {
1348
1068
  await this.matterbridgeContext?.clearAll();
1349
1069
  this.log.info('Matter storage reset done! Remove the bridge from the controller.');
1350
1070
  }
1351
- // Stop matter storage
1352
1071
  await this.stopMatterStorage();
1353
- // Stop the frontend
1354
1072
  await this.frontend.stop();
1355
- // Remove the matterfilelogger
1356
1073
  try {
1357
1074
  Logger.removeLogger('matterfilelogger');
1358
1075
  }
1359
1076
  catch (error) {
1360
1077
  this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${db}: ${error instanceof Error ? error.message : String(error)}`);
1361
1078
  }
1362
- // Close the matterbridge node storage and context
1363
1079
  if (this.nodeStorage && this.nodeContext) {
1364
- /*
1365
- TODO: Implement serialization of registered devices in edge mode
1366
- this.log.info('Saving registered devices...');
1367
- const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
1368
- this.devices.forEach(async (device) => {
1369
- const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
1370
- // this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
1371
- if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
1372
- });
1373
- await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
1374
- this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
1375
- */
1376
- // Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
1377
1080
  this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
1378
1081
  await this.nodeContext.close();
1379
1082
  this.nodeContext = undefined;
1380
- // Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
1381
1083
  for (const plugin of this.plugins) {
1382
1084
  if (plugin.nodeContext) {
1383
1085
  this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
@@ -1394,10 +1096,8 @@ export class Matterbridge extends EventEmitter {
1394
1096
  }
1395
1097
  this.plugins.clear();
1396
1098
  this.devices.clear();
1397
- // Factory reset
1398
1099
  if (message === 'shutting down with factory reset...') {
1399
1100
  try {
1400
- // Delete matter storage directory with its subdirectories and backup
1401
1101
  const dir = path.join(this.matterbridgeDirectory, this.matterStorageName);
1402
1102
  this.log.info(`Removing matter storage directory: ${dir}`);
1403
1103
  await fs.rm(dir, { recursive: true });
@@ -1411,7 +1111,6 @@ export class Matterbridge extends EventEmitter {
1411
1111
  }
1412
1112
  }
1413
1113
  try {
1414
- // Delete matterbridge storage directory with its subdirectories and backup
1415
1114
  const dir = path.join(this.matterbridgeDirectory, this.nodeStorageName);
1416
1115
  this.log.info(`Removing matterbridge storage directory: ${dir}`);
1417
1116
  await fs.rm(dir, { recursive: true });
@@ -1426,13 +1125,12 @@ export class Matterbridge extends EventEmitter {
1426
1125
  }
1427
1126
  this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
1428
1127
  }
1429
- // Deregisters the process handlers
1430
1128
  this.deregisterProcessHandlers();
1431
1129
  if (restart) {
1432
1130
  if (message === 'updating...') {
1433
1131
  this.log.info('Cleanup completed. Updating...');
1434
1132
  Matterbridge.instance = undefined;
1435
- this.emit('update'); // Restart the process but the update has been done before. TODO move all updates to the cli
1133
+ this.emit('update');
1436
1134
  }
1437
1135
  else if (message === 'restarting...') {
1438
1136
  this.log.info('Cleanup completed. Restarting...');
@@ -1453,13 +1151,6 @@ export class Matterbridge extends EventEmitter {
1453
1151
  this.log.debug('Cleanup already started...');
1454
1152
  }
1455
1153
  }
1456
- /**
1457
- * Creates and configures the server node for a single not bridged device.
1458
- *
1459
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1460
- * @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
1461
- * @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
1462
- */
1463
1154
  async createDeviceServerNode(plugin, device) {
1464
1155
  if (device.mode === 'server' && !device.serverNode && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
1465
1156
  this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
@@ -1470,14 +1161,6 @@ export class Matterbridge extends EventEmitter {
1470
1161
  this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
1471
1162
  }
1472
1163
  }
1473
- /**
1474
- * Creates and configures the server node for an accessory plugin for a given device.
1475
- *
1476
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1477
- * @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
1478
- * @param {boolean} [start] - Whether to start the server node after adding the device. Default is `false`.
1479
- * @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
1480
- */
1481
1164
  async createAccessoryPlugin(plugin, device, start = false) {
1482
1165
  if (!plugin.locked && device.deviceType && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
1483
1166
  plugin.locked = true;
@@ -1491,13 +1174,6 @@ export class Matterbridge extends EventEmitter {
1491
1174
  await this.startServerNode(plugin.serverNode);
1492
1175
  }
1493
1176
  }
1494
- /**
1495
- * Creates and configures the server node for a dynamic plugin.
1496
- *
1497
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1498
- * @param {boolean} [start] - Whether to start the server node after adding the aggregator node.
1499
- * @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
1500
- */
1501
1177
  async createDynamicPlugin(plugin, start = false) {
1502
1178
  if (!plugin.locked) {
1503
1179
  plugin.locked = true;
@@ -1510,14 +1186,7 @@ export class Matterbridge extends EventEmitter {
1510
1186
  await this.startServerNode(plugin.serverNode);
1511
1187
  }
1512
1188
  }
1513
- /**
1514
- * Starts the Matterbridge in bridge mode.
1515
- *
1516
- * @private
1517
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1518
- */
1519
1189
  async startBridge() {
1520
- // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1521
1190
  if (!this.matterStorageManager)
1522
1191
  throw new Error('No storage manager initialized');
1523
1192
  if (!this.matterbridgeContext)
@@ -1556,16 +1225,13 @@ export class Matterbridge extends EventEmitter {
1556
1225
  clearInterval(this.startMatterInterval);
1557
1226
  this.startMatterInterval = undefined;
1558
1227
  this.log.debug('Cleared startMatterInterval interval for Matterbridge');
1559
- // Start the Matter server node
1560
- this.startServerNode(this.serverNode); // We don't await this, because the server node is started in the background
1561
- // Start the Matter server node of single devices in mode 'server'
1228
+ this.startServerNode(this.serverNode);
1562
1229
  for (const device of this.devices.array()) {
1563
1230
  if (device.mode === 'server' && device.serverNode) {
1564
1231
  this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
1565
- this.startServerNode(device.serverNode); // We don't await this, because the server node is started in the background
1232
+ this.startServerNode(device.serverNode);
1566
1233
  }
1567
1234
  }
1568
- // Configure the plugins
1569
1235
  this.configureTimeout = setTimeout(async () => {
1570
1236
  for (const plugin of this.plugins) {
1571
1237
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1583,24 +1249,16 @@ export class Matterbridge extends EventEmitter {
1583
1249
  }
1584
1250
  this.frontend.wssSendRefreshRequired('plugins');
1585
1251
  }, 30 * 1000).unref();
1586
- // Setting reachability to true
1587
1252
  this.reachabilityTimeout = setTimeout(() => {
1588
1253
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1589
1254
  if (this.aggregatorNode)
1590
1255
  this.setAggregatorReachability(this.aggregatorNode, true);
1591
1256
  this.frontend.wssSendRefreshRequired('reachability');
1592
1257
  }, 60 * 1000).unref();
1593
- // Logger.get('LogServerNode').info(this.serverNode);
1594
1258
  this.emit('bridge_started');
1595
1259
  this.log.notice('Matterbridge bridge started successfully');
1596
1260
  }, 1000);
1597
1261
  }
1598
- /**
1599
- * Starts the Matterbridge in childbridge mode.
1600
- *
1601
- * @private
1602
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1603
- */
1604
1262
  async startChildbridge() {
1605
1263
  if (!this.matterStorageManager)
1606
1264
  throw new Error('No storage manager initialized');
@@ -1638,7 +1296,6 @@ export class Matterbridge extends EventEmitter {
1638
1296
  clearInterval(this.startMatterInterval);
1639
1297
  this.startMatterInterval = undefined;
1640
1298
  this.log.debug('Cleared startMatterInterval interval in childbridge mode');
1641
- // Configure the plugins
1642
1299
  this.configureTimeout = setTimeout(async () => {
1643
1300
  for (const plugin of this.plugins) {
1644
1301
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1675,9 +1332,7 @@ export class Matterbridge extends EventEmitter {
1675
1332
  this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
1676
1333
  continue;
1677
1334
  }
1678
- // Start the Matter server node
1679
- this.startServerNode(plugin.serverNode); // We don't await this, because the server node is started in the background
1680
- // Setting reachability to true
1335
+ this.startServerNode(plugin.serverNode);
1681
1336
  plugin.reachabilityTimeout = setTimeout(() => {
1682
1337
  this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggregator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
1683
1338
  if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
@@ -1685,241 +1340,19 @@ export class Matterbridge extends EventEmitter {
1685
1340
  this.frontend.wssSendRefreshRequired('reachability');
1686
1341
  }, 60 * 1000).unref();
1687
1342
  }
1688
- // Start the Matter server node of single devices in mode 'server'
1689
1343
  for (const device of this.devices.array()) {
1690
1344
  if (device.mode === 'server' && device.serverNode) {
1691
1345
  this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
1692
- this.startServerNode(device.serverNode); // We don't await this, because the server node is started in the background
1346
+ this.startServerNode(device.serverNode);
1693
1347
  }
1694
1348
  }
1695
- // Logger.get('LogServerNode').info(this.serverNode);
1696
1349
  this.emit('childbridge_started');
1697
1350
  this.log.notice('Matterbridge childbridge started successfully');
1698
1351
  }, 1000);
1699
1352
  }
1700
- /**
1701
- * Starts the Matterbridge controller.
1702
- *
1703
- * @private
1704
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1705
- */
1706
1353
  async startController() {
1707
- /*
1708
- if (!this.matterStorageManager) {
1709
- this.log.error('No storage manager initialized');
1710
- await this.cleanup('No storage manager initialized');
1711
- return;
1712
- }
1713
- this.log.info('Creating context: mattercontrollerContext');
1714
- this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
1715
- if (!this.controllerContext) {
1716
- this.log.error('No storage context mattercontrollerContext initialized');
1717
- await this.cleanup('No storage context mattercontrollerContext initialized');
1718
- return;
1719
- }
1720
-
1721
- this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1722
- this.matterServer = await this.createMatterServer(this.storageManager);
1723
- this.log.info('Creating matter commissioning controller');
1724
- this.commissioningController = new CommissioningController({
1725
- autoConnect: false,
1726
- });
1727
- this.log.info('Adding matter commissioning controller to matter server');
1728
- await this.matterServer.addCommissioningController(this.commissioningController);
1729
-
1730
- this.log.info('Starting matter server');
1731
- await this.matterServer.start();
1732
- this.log.info('Matter server started');
1733
- const commissioningOptions: ControllerCommissioningFlowOptions = {
1734
- regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
1735
- regulatoryCountryCode: 'XX',
1736
- };
1737
- const commissioningController = new CommissioningController({
1738
- environment: {
1739
- environment,
1740
- id: uniqueId,
1741
- },
1742
- autoConnect: false, // Do not auto connect to the commissioned nodes
1743
- adminFabricLabel,
1744
- });
1745
-
1746
- if (hasParameter('pairingcode')) {
1747
- this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
1748
- const pairingCode = getParameter('pairingcode');
1749
- const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
1750
- const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
1751
-
1752
- let longDiscriminator, setupPin, shortDiscriminator;
1753
- if (pairingCode !== undefined) {
1754
- const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
1755
- shortDiscriminator = pairingCodeCodec.shortDiscriminator;
1756
- longDiscriminator = undefined;
1757
- setupPin = pairingCodeCodec.passcode;
1758
- this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
1759
- } else {
1760
- longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
1761
- if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
1762
- setupPin = this.controllerContext.get('pin', 20202021);
1763
- }
1764
- if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
1765
- throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
1766
- }
1767
-
1768
- const options = {
1769
- commissioning: commissioningOptions,
1770
- discovery: {
1771
- knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
1772
- identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
1773
- },
1774
- passcode: setupPin,
1775
- } as NodeCommissioningOptions;
1776
- this.log.info('Commissioning with options:', options);
1777
- const nodeId = await this.commissioningController.commissionNode(options);
1778
- this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
1779
- this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
1780
- } // (hasParameter('pairingcode'))
1781
-
1782
- if (hasParameter('unpairall')) {
1783
- this.log.info('***Commissioning controller unpairing all nodes...');
1784
- const nodeIds = this.commissioningController.getCommissionedNodes();
1785
- for (const nodeId of nodeIds) {
1786
- this.log.info('***Commissioning controller unpairing node:', nodeId);
1787
- await this.commissioningController.removeNode(nodeId);
1788
- }
1789
- return;
1790
- }
1791
-
1792
- if (hasParameter('discover')) {
1793
- // const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1794
- // console.log(discover);
1795
- }
1796
-
1797
- if (!this.commissioningController.isCommissioned()) {
1798
- this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
1799
- return;
1800
- }
1801
-
1802
- const nodeIds = this.commissioningController.getCommissionedNodes();
1803
- this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
1804
- for (const nodeId of nodeIds) {
1805
- this.log.info(`***Connecting to commissioned node: ${nodeId}`);
1806
-
1807
- const node = await this.commissioningController.connectNode(nodeId, {
1808
- autoSubscribe: false,
1809
- attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
1810
- this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
1811
- eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
1812
- this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
1813
- stateInformationCallback: (peerNodeId, info) => {
1814
- switch (info) {
1815
- case NodeStateInformation.Connected:
1816
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
1817
- break;
1818
- case NodeStateInformation.Disconnected:
1819
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
1820
- break;
1821
- case NodeStateInformation.Reconnecting:
1822
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
1823
- break;
1824
- case NodeStateInformation.WaitingForDeviceDiscovery:
1825
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
1826
- break;
1827
- case NodeStateInformation.StructureChanged:
1828
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
1829
- break;
1830
- case NodeStateInformation.Decommissioned:
1831
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
1832
- break;
1833
- default:
1834
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
1835
- break;
1836
- }
1837
- },
1838
- });
1839
-
1840
- node.logStructure();
1841
-
1842
- // Get the interaction client
1843
- this.log.info('Getting the interaction client');
1844
- const interactionClient = await node.getInteractionClient();
1845
- let cluster;
1846
- let attributes;
1847
-
1848
- // Log BasicInformationCluster
1849
- cluster = BasicInformationCluster;
1850
- attributes = await interactionClient.getMultipleAttributes({
1851
- attributes: [{ clusterId: cluster.id }],
1852
- });
1853
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1854
- attributes.forEach((attribute) => {
1855
- this.log.info(
1856
- `- 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}`,
1857
- );
1858
- });
1859
-
1860
- // Log PowerSourceCluster
1861
- cluster = PowerSourceCluster;
1862
- attributes = await interactionClient.getMultipleAttributes({
1863
- attributes: [{ clusterId: cluster.id }],
1864
- });
1865
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1866
- attributes.forEach((attribute) => {
1867
- this.log.info(
1868
- `- 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}`,
1869
- );
1870
- });
1871
-
1872
- // Log ThreadNetworkDiagnostics
1873
- cluster = ThreadNetworkDiagnosticsCluster;
1874
- attributes = await interactionClient.getMultipleAttributes({
1875
- attributes: [{ clusterId: cluster.id }],
1876
- });
1877
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1878
- attributes.forEach((attribute) => {
1879
- this.log.info(
1880
- `- 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}`,
1881
- );
1882
- });
1883
-
1884
- // Log SwitchCluster
1885
- cluster = SwitchCluster;
1886
- attributes = await interactionClient.getMultipleAttributes({
1887
- attributes: [{ clusterId: cluster.id }],
1888
- });
1889
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1890
- attributes.forEach((attribute) => {
1891
- this.log.info(
1892
- `- 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}`,
1893
- );
1894
- });
1895
-
1896
- this.log.info('Subscribing to all attributes and events');
1897
- await node.subscribeAllAttributesAndEvents({
1898
- ignoreInitialTriggers: false,
1899
- attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
1900
- this.log.info(
1901
- `***${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}`,
1902
- ),
1903
- eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
1904
- this.log.info(
1905
- `***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
1906
- );
1907
- },
1908
- });
1909
- this.log.info('Subscribed to all attributes and events');
1910
- }
1911
- */
1912
1354
  }
1913
- /** */
1914
- /** Matter.js methods */
1915
- /** */
1916
- /**
1917
- * Starts the matter storage with name Matterbridge, create the matterbridge context and performs a backup.
1918
- *
1919
- * @returns {Promise<void>} - A promise that resolves when the storage is started.
1920
- */
1921
1355
  async startMatterStorage() {
1922
- // Setup Matter storage
1923
1356
  this.log.info(`Starting matter node storage...`);
1924
1357
  this.matterStorageService = this.environment.get(StorageService);
1925
1358
  this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
@@ -1928,17 +1361,8 @@ export class Matterbridge extends EventEmitter {
1928
1361
  this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName);
1929
1362
  this.matterbridgeInformation.matterbridgeSerialNumber = await this.matterbridgeContext.get('serialNumber', '');
1930
1363
  this.log.info('Matter node storage started');
1931
- // Backup matter storage since it is created/opened correctly
1932
1364
  await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
1933
1365
  }
1934
- /**
1935
- * Makes a backup copy of the specified matter storage directory.
1936
- *
1937
- * @param {string} storageName - The name of the storage directory to be backed up.
1938
- * @param {string} backupName - The name of the backup directory to be created.
1939
- * @private
1940
- * @returns {Promise<void>} A promise that resolves when the has been done.
1941
- */
1942
1366
  async backupMatterStorage(storageName, backupName) {
1943
1367
  this.log.info('Creating matter node storage backup...');
1944
1368
  try {
@@ -1949,11 +1373,6 @@ export class Matterbridge extends EventEmitter {
1949
1373
  this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
1950
1374
  }
1951
1375
  }
1952
- /**
1953
- * Stops the matter storage.
1954
- *
1955
- * @returns {Promise<void>} A promise that resolves when the storage is stopped.
1956
- */
1957
1376
  async stopMatterStorage() {
1958
1377
  this.log.info('Closing matter node storage...');
1959
1378
  await this.matterStorageManager?.close();
@@ -1962,19 +1381,6 @@ export class Matterbridge extends EventEmitter {
1962
1381
  this.matterbridgeContext = undefined;
1963
1382
  this.log.info('Matter node storage closed');
1964
1383
  }
1965
- /**
1966
- * Creates a server node storage context.
1967
- *
1968
- * @param {string} pluginName - The name of the plugin.
1969
- * @param {string} deviceName - The name of the device.
1970
- * @param {DeviceTypeId} deviceType - The device type of the device.
1971
- * @param {number} vendorId - The vendor ID.
1972
- * @param {string} vendorName - The vendor name.
1973
- * @param {number} productId - The product ID.
1974
- * @param {string} productName - The product name.
1975
- * @param {string} [serialNumber] - The serial number of the device (optional).
1976
- * @returns {Promise<StorageContext>} The storage context for the commissioning server.
1977
- */
1978
1384
  async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
1979
1385
  const { randomBytes } = await import('node:crypto');
1980
1386
  if (!this.matterStorageService)
@@ -2008,15 +1414,6 @@ export class Matterbridge extends EventEmitter {
2008
1414
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
2009
1415
  return storageContext;
2010
1416
  }
2011
- /**
2012
- * Creates a server node.
2013
- *
2014
- * @param {StorageContext} storageContext - The storage context for the server node.
2015
- * @param {number} [port] - The port number for the server node. Defaults to 5540.
2016
- * @param {number} [passcode] - The passcode for the server node. Defaults to 20242025.
2017
- * @param {number} [discriminator] - The discriminator for the server node. Defaults to 3850.
2018
- * @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
2019
- */
2020
1417
  async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
2021
1418
  const storeId = await storageContext.get('storeId');
2022
1419
  this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
@@ -2026,37 +1423,24 @@ export class Matterbridge extends EventEmitter {
2026
1423
  this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
2027
1424
  this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
2028
1425
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
2029
- /**
2030
- * Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
2031
- */
2032
1426
  const serverNode = await ServerNode.create({
2033
- // Required: Give the Node a unique ID which is used to store the state of this node
2034
1427
  id: storeId,
2035
- // Provide Network relevant configuration like the port
2036
- // Optional when operating only one device on a host, Default port is 5540
2037
1428
  network: {
2038
1429
  listeningAddressIpv4: this.ipv4address,
2039
1430
  listeningAddressIpv6: this.ipv6address,
2040
1431
  port,
2041
1432
  },
2042
- // Provide the certificate for the device
2043
1433
  operationalCredentials: {
2044
1434
  certification: this.certification,
2045
1435
  },
2046
- // Provide Commissioning relevant settings
2047
- // Optional for development/testing purposes
2048
1436
  commissioning: {
2049
1437
  passcode,
2050
1438
  discriminator,
2051
1439
  },
2052
- // Provide Node announcement settings
2053
- // Optional: If Ommitted some development defaults are used
2054
1440
  productDescription: {
2055
1441
  name: await storageContext.get('deviceName'),
2056
1442
  deviceType: DeviceTypeId(await storageContext.get('deviceType')),
2057
1443
  },
2058
- // Provide defaults for the BasicInformation cluster on the Root endpoint
2059
- // Optional: If Omitted some development defaults are used
2060
1444
  basicInformation: {
2061
1445
  vendorId: VendorId(await storageContext.get('vendorId')),
2062
1446
  vendorName: await storageContext.get('vendorName'),
@@ -2074,13 +1458,12 @@ export class Matterbridge extends EventEmitter {
2074
1458
  },
2075
1459
  });
2076
1460
  const sanitizeFabrics = (fabrics, resetSessions = false) => {
2077
- // New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
2078
1461
  const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
2079
1462
  this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
2080
1463
  if (this.bridgeMode === 'bridge') {
2081
1464
  this.matterbridgeFabricInformations = sanitizedFabrics;
2082
1465
  if (resetSessions)
2083
- this.matterbridgeSessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
1466
+ this.matterbridgeSessionInformations = undefined;
2084
1467
  this.matterbridgePaired = true;
2085
1468
  }
2086
1469
  if (this.bridgeMode === 'childbridge') {
@@ -2088,22 +1471,16 @@ export class Matterbridge extends EventEmitter {
2088
1471
  if (plugin) {
2089
1472
  plugin.fabricInformations = sanitizedFabrics;
2090
1473
  if (resetSessions)
2091
- plugin.sessionInformations = undefined; // Changed cause Invoke Matterbridge.operationalCredentials.updateFabricLabel is sent after the session is created
1474
+ plugin.sessionInformations = undefined;
2092
1475
  plugin.paired = true;
2093
1476
  }
2094
1477
  }
2095
1478
  };
2096
- /**
2097
- * This event is triggered when the device is initially commissioned successfully.
2098
- * This means: It is added to the first fabric.
2099
- */
2100
1479
  serverNode.lifecycle.commissioned.on(() => {
2101
1480
  this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
2102
1481
  clearTimeout(this.endAdvertiseTimeout);
2103
1482
  });
2104
- /** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
2105
1483
  serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
2106
- /** This event is triggered when the device went online. This means that it is discoverable in the network. */
2107
1484
  serverNode.lifecycle.online.on(async () => {
2108
1485
  this.log.notice(`Server node for ${storeId} is online`);
2109
1486
  if (!serverNode.lifecycle.isCommissioned) {
@@ -2142,7 +1519,6 @@ export class Matterbridge extends EventEmitter {
2142
1519
  }
2143
1520
  }
2144
1521
  }
2145
- // Set a timeout to show that advertising stops after 15 minutes if not commissioned
2146
1522
  this.startEndAdvertiseTimer(serverNode);
2147
1523
  }
2148
1524
  else {
@@ -2154,7 +1530,6 @@ export class Matterbridge extends EventEmitter {
2154
1530
  this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
2155
1531
  this.emit('online', storeId);
2156
1532
  });
2157
- /** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
2158
1533
  serverNode.lifecycle.offline.on(() => {
2159
1534
  this.log.notice(`Server node for ${storeId} is offline`);
2160
1535
  if (this.bridgeMode === 'bridge') {
@@ -2179,10 +1554,6 @@ export class Matterbridge extends EventEmitter {
2179
1554
  this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
2180
1555
  this.emit('offline', storeId);
2181
1556
  });
2182
- /**
2183
- * This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
2184
- * information is needed.
2185
- */
2186
1557
  serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
2187
1558
  let action = '';
2188
1559
  switch (fabricAction) {
@@ -2213,24 +1584,16 @@ export class Matterbridge extends EventEmitter {
2213
1584
  }
2214
1585
  }
2215
1586
  };
2216
- /**
2217
- * This event is triggered when an operative new session was opened by a Controller.
2218
- * It is not triggered for the initial commissioning process, just afterwards for real connections.
2219
- */
2220
1587
  serverNode.events.sessions.opened.on((session) => {
2221
1588
  this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
2222
1589
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2223
1590
  this.frontend.wssSendRefreshRequired('sessions');
2224
1591
  });
2225
- /**
2226
- * This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
2227
- */
2228
1592
  serverNode.events.sessions.closed.on((session) => {
2229
1593
  this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
2230
1594
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
2231
1595
  this.frontend.wssSendRefreshRequired('sessions');
2232
1596
  });
2233
- /** This event is triggered when a subscription gets added or removed on an operative session. */
2234
1597
  serverNode.events.sessions.subscriptionsChanged.on((session) => {
2235
1598
  this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
2236
1599
  sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
@@ -2239,11 +1602,6 @@ export class Matterbridge extends EventEmitter {
2239
1602
  this.log.info(`Created server node for ${storeId}`);
2240
1603
  return serverNode;
2241
1604
  }
2242
- /**
2243
- * Starts the 15 minutes timer to advice that advertising for the specified server node is ended.
2244
- *
2245
- * @param {ServerNode} [matterServerNode] - The server node to start.
2246
- */
2247
1605
  startEndAdvertiseTimer(matterServerNode) {
2248
1606
  if (this.endAdvertiseTimeout) {
2249
1607
  this.log.debug(`Clear ${matterServerNode.id} server node end advertise timer`);
@@ -2272,25 +1630,12 @@ export class Matterbridge extends EventEmitter {
2272
1630
  this.log.notice(`Advertising on server node for ${matterServerNode.id} stopped. Restart to commission.`);
2273
1631
  }, 15 * 60 * 1000).unref();
2274
1632
  }
2275
- /**
2276
- * Starts the specified server node.
2277
- *
2278
- * @param {ServerNode} [matterServerNode] - The server node to start.
2279
- * @returns {Promise<void>} A promise that resolves when the server node has started.
2280
- */
2281
1633
  async startServerNode(matterServerNode) {
2282
1634
  if (!matterServerNode)
2283
1635
  return;
2284
1636
  this.log.notice(`Starting ${matterServerNode.id} server node`);
2285
1637
  await matterServerNode.start();
2286
1638
  }
2287
- /**
2288
- * Stops the specified server node.
2289
- *
2290
- * @param {ServerNode} matterServerNode - The server node to stop.
2291
- * @param {number} [timeout] - The timeout in milliseconds for stopping the server node. Defaults to 30 seconds.
2292
- * @returns {Promise<void>} A promise that resolves when the server node has stopped.
2293
- */
2294
1639
  async stopServerNode(matterServerNode, timeout = 30000) {
2295
1640
  if (!matterServerNode)
2296
1641
  return;
@@ -2303,12 +1648,6 @@ export class Matterbridge extends EventEmitter {
2303
1648
  this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
2304
1649
  }
2305
1650
  }
2306
- /**
2307
- * Advertises the specified server node.
2308
- *
2309
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2310
- * @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.
2311
- */
2312
1651
  async advertiseServerNode(matterServerNode) {
2313
1652
  if (matterServerNode) {
2314
1653
  await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
@@ -2317,39 +1656,19 @@ export class Matterbridge extends EventEmitter {
2317
1656
  return { qrPairingCode, manualPairingCode };
2318
1657
  }
2319
1658
  }
2320
- /**
2321
- * Stop advertise the specified server node.
2322
- *
2323
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2324
- * @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
2325
- */
2326
1659
  async stopAdvertiseServerNode(matterServerNode) {
2327
1660
  if (matterServerNode && matterServerNode.lifecycle.isOnline) {
2328
1661
  await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
2329
1662
  this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
2330
1663
  }
2331
1664
  }
2332
- /**
2333
- * Creates an aggregator node with the specified storage context.
2334
- *
2335
- * @param {StorageContext} storageContext - The storage context for the aggregator node.
2336
- * @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
2337
- */
2338
1665
  async createAggregatorNode(storageContext) {
2339
1666
  this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
2340
1667
  const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
2341
1668
  this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
2342
1669
  return aggregatorNode;
2343
1670
  }
2344
- /**
2345
- * Adds a MatterbridgeEndpoint to the specified plugin.
2346
- *
2347
- * @param {string} pluginName - The name of the plugin.
2348
- * @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
2349
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
2350
- */
2351
1671
  async addBridgedEndpoint(pluginName, device) {
2352
- // Check if the plugin is registered
2353
1672
  const plugin = this.plugins.get(pluginName);
2354
1673
  if (!plugin) {
2355
1674
  this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
@@ -2369,7 +1688,6 @@ export class Matterbridge extends EventEmitter {
2369
1688
  }
2370
1689
  else if (this.bridgeMode === 'bridge') {
2371
1690
  if (device.mode === 'matter') {
2372
- // Register and add the device to the matterbridge server node
2373
1691
  this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge server node...`);
2374
1692
  if (!this.serverNode) {
2375
1693
  this.log.error('Server node not found for Matterbridge');
@@ -2386,7 +1704,6 @@ export class Matterbridge extends EventEmitter {
2386
1704
  }
2387
1705
  }
2388
1706
  else {
2389
- // Register and add the device to the matterbridge aggregator node
2390
1707
  this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
2391
1708
  if (!this.aggregatorNode) {
2392
1709
  this.log.error('Aggregator node not found for Matterbridge');
@@ -2404,7 +1721,6 @@ export class Matterbridge extends EventEmitter {
2404
1721
  }
2405
1722
  }
2406
1723
  else if (this.bridgeMode === 'childbridge') {
2407
- // Register and add the device to the plugin server node
2408
1724
  if (plugin.type === 'AccessoryPlatform') {
2409
1725
  try {
2410
1726
  this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
@@ -2428,12 +1744,10 @@ export class Matterbridge extends EventEmitter {
2428
1744
  return;
2429
1745
  }
2430
1746
  }
2431
- // Register and add the device to the plugin aggregator node
2432
1747
  if (plugin.type === 'DynamicPlatform') {
2433
1748
  try {
2434
1749
  this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
2435
1750
  await this.createDynamicPlugin(plugin);
2436
- // Fast plugins can add another device before the server node is ready, so we wait for the server node to be ready
2437
1751
  await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
2438
1752
  if (!plugin.aggregatorNode) {
2439
1753
  this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
@@ -2456,28 +1770,17 @@ export class Matterbridge extends EventEmitter {
2456
1770
  plugin.registeredDevices++;
2457
1771
  if (plugin.addedDevices !== undefined)
2458
1772
  plugin.addedDevices++;
2459
- // Add the device to the DeviceManager
2460
1773
  this.devices.set(device);
2461
- // Subscribe to the reachable$Changed event
2462
1774
  await this.subscribeAttributeChanged(plugin, device);
2463
1775
  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}`);
2464
1776
  }
2465
- /**
2466
- * Removes a MatterbridgeEndpoint from the specified plugin.
2467
- *
2468
- * @param {string} pluginName - The name of the plugin.
2469
- * @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
2470
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
2471
- */
2472
1777
  async removeBridgedEndpoint(pluginName, device) {
2473
1778
  this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
2474
- // Check if the plugin is registered
2475
1779
  const plugin = this.plugins.get(pluginName);
2476
1780
  if (!plugin) {
2477
1781
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
2478
1782
  return;
2479
1783
  }
2480
- // Register and add the device to the matterbridge aggregator node
2481
1784
  if (this.bridgeMode === 'bridge') {
2482
1785
  if (!this.aggregatorNode) {
2483
1786
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
@@ -2492,7 +1795,6 @@ export class Matterbridge extends EventEmitter {
2492
1795
  }
2493
1796
  else if (this.bridgeMode === 'childbridge') {
2494
1797
  if (plugin.type === 'AccessoryPlatform') {
2495
- // Nothing to do here since the server node has no aggregator node but only the device itself
2496
1798
  }
2497
1799
  else if (plugin.type === 'DynamicPlatform') {
2498
1800
  if (!plugin.aggregatorNode) {
@@ -2507,21 +1809,8 @@ export class Matterbridge extends EventEmitter {
2507
1809
  if (plugin.addedDevices !== undefined)
2508
1810
  plugin.addedDevices--;
2509
1811
  }
2510
- // Remove the device from the DeviceManager
2511
1812
  this.devices.remove(device);
2512
1813
  }
2513
- /**
2514
- * Removes all bridged endpoints from the specified plugin.
2515
- *
2516
- * @param {string} pluginName - The name of the plugin.
2517
- * @param {number} [delay] - The delay in milliseconds between removing each bridged endpoint (default: 0).
2518
- * @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
2519
- *
2520
- * @remarks
2521
- * This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
2522
- * It also applies a delay between each removal if specified.
2523
- * The delay is useful to allow the controllers to receive a single subscription for each device removed.
2524
- */
2525
1814
  async removeAllBridgedEndpoints(pluginName, delay = 0) {
2526
1815
  this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
2527
1816
  for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
@@ -2532,15 +1821,6 @@ export class Matterbridge extends EventEmitter {
2532
1821
  if (delay > 0)
2533
1822
  await new Promise((resolve) => setTimeout(resolve, 2000));
2534
1823
  }
2535
- /**
2536
- * Subscribes to the attribute change event for the given device and plugin.
2537
- * Specifically, it listens for changes in the 'reachable' attribute of the
2538
- * BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
2539
- *
2540
- * @param {RegisteredPlugin} plugin - The plugin associated with the device.
2541
- * @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
2542
- * @returns {Promise<void>} A promise that resolves when the subscription is set up.
2543
- */
2544
1824
  async subscribeAttributeChanged(plugin, device) {
2545
1825
  this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
2546
1826
  if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
@@ -2556,12 +1836,6 @@ export class Matterbridge extends EventEmitter {
2556
1836
  });
2557
1837
  }
2558
1838
  }
2559
- /**
2560
- * Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
2561
- *
2562
- * @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
2563
- * @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
2564
- */
2565
1839
  sanitizeFabricInformations(fabricInfo) {
2566
1840
  return fabricInfo.map((info) => {
2567
1841
  return {
@@ -2575,12 +1849,6 @@ export class Matterbridge extends EventEmitter {
2575
1849
  };
2576
1850
  });
2577
1851
  }
2578
- /**
2579
- * Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
2580
- *
2581
- * @param {SessionsBehavior.Session[]} session - The array of session information objects.
2582
- * @returns {SanitizedSession[]} An array of sanitized session information objects.
2583
- */
2584
1852
  sanitizeSessionInformation(session) {
2585
1853
  return session
2586
1854
  .filter((session) => session.isPeerActive)
@@ -2607,21 +1875,7 @@ export class Matterbridge extends EventEmitter {
2607
1875
  };
2608
1876
  });
2609
1877
  }
2610
- /**
2611
- * Sets the reachability of the specified aggregator node bridged devices and trigger.
2612
- *
2613
- * @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
2614
- * @param {boolean} reachable - A boolean indicating the reachability status to set.
2615
- */
2616
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2617
1878
  async setAggregatorReachability(aggregatorNode, reachable) {
2618
- /*
2619
- for (const child of aggregatorNode.parts) {
2620
- this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
2621
- await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
2622
- child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
2623
- }
2624
- */
2625
1879
  }
2626
1880
  getVendorIdName = (vendorId) => {
2627
1881
  if (!vendorId)
@@ -2665,4 +1919,3 @@ export class Matterbridge extends EventEmitter {
2665
1919
  return vendorName;
2666
1920
  };
2667
1921
  }
2668
- //# sourceMappingURL=matterbridge.js.map