matterbridge 3.1.3 → 3.1.4-dev-20250715-04049c4

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 (208) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cli.js +2 -91
  3. package/dist/cliEmitter.js +0 -30
  4. package/dist/clusters/export.js +0 -2
  5. package/dist/defaultConfigSchema.js +0 -24
  6. package/dist/deviceManager.js +1 -94
  7. package/dist/devices/batteryStorage.js +1 -48
  8. package/dist/devices/evse.js +10 -74
  9. package/dist/devices/export.js +0 -2
  10. package/dist/devices/heatPump.js +2 -50
  11. package/dist/devices/laundryDryer.js +6 -83
  12. package/dist/devices/laundryWasher.js +7 -91
  13. package/dist/devices/roboticVacuumCleaner.js +6 -89
  14. package/dist/devices/solarPower.js +0 -38
  15. package/dist/devices/waterHeater.js +2 -82
  16. package/dist/frontend.js +18 -423
  17. package/dist/globalMatterbridge.js +0 -47
  18. package/dist/helpers.js +0 -53
  19. package/dist/index.js +1 -30
  20. package/dist/logger/export.js +0 -1
  21. package/dist/matter/behaviors.js +0 -2
  22. package/dist/matter/clusters.js +0 -2
  23. package/dist/matter/devices.js +0 -2
  24. package/dist/matter/endpoints.js +0 -2
  25. package/dist/matter/export.js +0 -3
  26. package/dist/matter/types.js +0 -3
  27. package/dist/matterbridge.js +51 -784
  28. package/dist/matterbridgeAccessoryPlatform.js +0 -36
  29. package/dist/matterbridgeBehaviors.js +1 -61
  30. package/dist/matterbridgeDeviceTypes.js +15 -579
  31. package/dist/matterbridgeDynamicPlatform.js +0 -36
  32. package/dist/matterbridgeEndpoint.js +42 -1106
  33. package/dist/matterbridgeEndpointHelpers.js +12 -322
  34. package/dist/matterbridgePlatform.js +0 -233
  35. package/dist/matterbridgeTypes.js +0 -25
  36. package/dist/pluginManager.js +3 -269
  37. package/dist/shelly.js +7 -168
  38. package/dist/storage/export.js +0 -1
  39. package/dist/update.js +0 -54
  40. package/dist/utils/colorUtils.js +2 -263
  41. package/dist/utils/commandLine.js +0 -54
  42. package/dist/utils/copyDirectory.js +1 -38
  43. package/dist/utils/createDirectory.js +0 -33
  44. package/dist/utils/createZip.js +2 -47
  45. package/dist/utils/deepCopy.js +0 -39
  46. package/dist/utils/deepEqual.js +1 -72
  47. package/dist/utils/export.js +0 -1
  48. package/dist/utils/hex.js +0 -58
  49. package/dist/utils/isvalid.js +0 -101
  50. package/dist/utils/network.js +5 -83
  51. package/dist/utils/spawn.js +0 -18
  52. package/dist/utils/wait.js +9 -62
  53. package/npm-shrinkwrap.json +3 -3
  54. package/package.json +2 -3
  55. package/dist/cli.d.ts +0 -26
  56. package/dist/cli.d.ts.map +0 -1
  57. package/dist/cli.js.map +0 -1
  58. package/dist/cliEmitter.d.ts +0 -34
  59. package/dist/cliEmitter.d.ts.map +0 -1
  60. package/dist/cliEmitter.js.map +0 -1
  61. package/dist/clusters/export.d.ts +0 -2
  62. package/dist/clusters/export.d.ts.map +0 -1
  63. package/dist/clusters/export.js.map +0 -1
  64. package/dist/defaultConfigSchema.d.ts +0 -28
  65. package/dist/defaultConfigSchema.d.ts.map +0 -1
  66. package/dist/defaultConfigSchema.js.map +0 -1
  67. package/dist/deviceManager.d.ts +0 -112
  68. package/dist/deviceManager.d.ts.map +0 -1
  69. package/dist/deviceManager.js.map +0 -1
  70. package/dist/devices/batteryStorage.d.ts +0 -48
  71. package/dist/devices/batteryStorage.d.ts.map +0 -1
  72. package/dist/devices/batteryStorage.js.map +0 -1
  73. package/dist/devices/evse.d.ts +0 -75
  74. package/dist/devices/evse.d.ts.map +0 -1
  75. package/dist/devices/evse.js.map +0 -1
  76. package/dist/devices/export.d.ts +0 -9
  77. package/dist/devices/export.d.ts.map +0 -1
  78. package/dist/devices/export.js.map +0 -1
  79. package/dist/devices/heatPump.d.ts +0 -47
  80. package/dist/devices/heatPump.d.ts.map +0 -1
  81. package/dist/devices/heatPump.js.map +0 -1
  82. package/dist/devices/laundryDryer.d.ts +0 -87
  83. package/dist/devices/laundryDryer.d.ts.map +0 -1
  84. package/dist/devices/laundryDryer.js.map +0 -1
  85. package/dist/devices/laundryWasher.d.ts +0 -242
  86. package/dist/devices/laundryWasher.d.ts.map +0 -1
  87. package/dist/devices/laundryWasher.js.map +0 -1
  88. package/dist/devices/roboticVacuumCleaner.d.ts +0 -110
  89. package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
  90. package/dist/devices/roboticVacuumCleaner.js.map +0 -1
  91. package/dist/devices/solarPower.d.ts +0 -40
  92. package/dist/devices/solarPower.d.ts.map +0 -1
  93. package/dist/devices/solarPower.js.map +0 -1
  94. package/dist/devices/waterHeater.d.ts +0 -111
  95. package/dist/devices/waterHeater.d.ts.map +0 -1
  96. package/dist/devices/waterHeater.js.map +0 -1
  97. package/dist/frontend.d.ts +0 -303
  98. package/dist/frontend.d.ts.map +0 -1
  99. package/dist/frontend.js.map +0 -1
  100. package/dist/globalMatterbridge.d.ts +0 -59
  101. package/dist/globalMatterbridge.d.ts.map +0 -1
  102. package/dist/globalMatterbridge.js.map +0 -1
  103. package/dist/helpers.d.ts +0 -48
  104. package/dist/helpers.d.ts.map +0 -1
  105. package/dist/helpers.js.map +0 -1
  106. package/dist/index.d.ts +0 -33
  107. package/dist/index.d.ts.map +0 -1
  108. package/dist/index.js.map +0 -1
  109. package/dist/logger/export.d.ts +0 -2
  110. package/dist/logger/export.d.ts.map +0 -1
  111. package/dist/logger/export.js.map +0 -1
  112. package/dist/matter/behaviors.d.ts +0 -2
  113. package/dist/matter/behaviors.d.ts.map +0 -1
  114. package/dist/matter/behaviors.js.map +0 -1
  115. package/dist/matter/clusters.d.ts +0 -2
  116. package/dist/matter/clusters.d.ts.map +0 -1
  117. package/dist/matter/clusters.js.map +0 -1
  118. package/dist/matter/devices.d.ts +0 -2
  119. package/dist/matter/devices.d.ts.map +0 -1
  120. package/dist/matter/devices.js.map +0 -1
  121. package/dist/matter/endpoints.d.ts +0 -2
  122. package/dist/matter/endpoints.d.ts.map +0 -1
  123. package/dist/matter/endpoints.js.map +0 -1
  124. package/dist/matter/export.d.ts +0 -5
  125. package/dist/matter/export.d.ts.map +0 -1
  126. package/dist/matter/export.js.map +0 -1
  127. package/dist/matter/types.d.ts +0 -3
  128. package/dist/matter/types.d.ts.map +0 -1
  129. package/dist/matter/types.js.map +0 -1
  130. package/dist/matterbridge.d.ts +0 -444
  131. package/dist/matterbridge.d.ts.map +0 -1
  132. package/dist/matterbridge.js.map +0 -1
  133. package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
  134. package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
  135. package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
  136. package/dist/matterbridgeBehaviors.d.ts +0 -1340
  137. package/dist/matterbridgeBehaviors.d.ts.map +0 -1
  138. package/dist/matterbridgeBehaviors.js.map +0 -1
  139. package/dist/matterbridgeDeviceTypes.d.ts +0 -709
  140. package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
  141. package/dist/matterbridgeDeviceTypes.js.map +0 -1
  142. package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
  143. package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
  144. package/dist/matterbridgeDynamicPlatform.js.map +0 -1
  145. package/dist/matterbridgeEndpoint.d.ts +0 -1250
  146. package/dist/matterbridgeEndpoint.d.ts.map +0 -1
  147. package/dist/matterbridgeEndpoint.js.map +0 -1
  148. package/dist/matterbridgeEndpointHelpers.d.ts +0 -3198
  149. package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
  150. package/dist/matterbridgeEndpointHelpers.js.map +0 -1
  151. package/dist/matterbridgePlatform.d.ts +0 -310
  152. package/dist/matterbridgePlatform.d.ts.map +0 -1
  153. package/dist/matterbridgePlatform.js.map +0 -1
  154. package/dist/matterbridgeTypes.d.ts +0 -195
  155. package/dist/matterbridgeTypes.d.ts.map +0 -1
  156. package/dist/matterbridgeTypes.js.map +0 -1
  157. package/dist/pluginManager.d.ts +0 -291
  158. package/dist/pluginManager.d.ts.map +0 -1
  159. package/dist/pluginManager.js.map +0 -1
  160. package/dist/shelly.d.ts +0 -174
  161. package/dist/shelly.d.ts.map +0 -1
  162. package/dist/shelly.js.map +0 -1
  163. package/dist/storage/export.d.ts +0 -2
  164. package/dist/storage/export.d.ts.map +0 -1
  165. package/dist/storage/export.js.map +0 -1
  166. package/dist/update.d.ts +0 -59
  167. package/dist/update.d.ts.map +0 -1
  168. package/dist/update.js.map +0 -1
  169. package/dist/utils/colorUtils.d.ts +0 -117
  170. package/dist/utils/colorUtils.d.ts.map +0 -1
  171. package/dist/utils/colorUtils.js.map +0 -1
  172. package/dist/utils/commandLine.d.ts +0 -59
  173. package/dist/utils/commandLine.d.ts.map +0 -1
  174. package/dist/utils/commandLine.js.map +0 -1
  175. package/dist/utils/copyDirectory.d.ts +0 -33
  176. package/dist/utils/copyDirectory.d.ts.map +0 -1
  177. package/dist/utils/copyDirectory.js.map +0 -1
  178. package/dist/utils/createDirectory.d.ts +0 -34
  179. package/dist/utils/createDirectory.d.ts.map +0 -1
  180. package/dist/utils/createDirectory.js.map +0 -1
  181. package/dist/utils/createZip.d.ts +0 -39
  182. package/dist/utils/createZip.d.ts.map +0 -1
  183. package/dist/utils/createZip.js.map +0 -1
  184. package/dist/utils/deepCopy.d.ts +0 -32
  185. package/dist/utils/deepCopy.d.ts.map +0 -1
  186. package/dist/utils/deepCopy.js.map +0 -1
  187. package/dist/utils/deepEqual.d.ts +0 -54
  188. package/dist/utils/deepEqual.d.ts.map +0 -1
  189. package/dist/utils/deepEqual.js.map +0 -1
  190. package/dist/utils/export.d.ts +0 -12
  191. package/dist/utils/export.d.ts.map +0 -1
  192. package/dist/utils/export.js.map +0 -1
  193. package/dist/utils/hex.d.ts +0 -49
  194. package/dist/utils/hex.d.ts.map +0 -1
  195. package/dist/utils/hex.js.map +0 -1
  196. package/dist/utils/isvalid.d.ts +0 -103
  197. package/dist/utils/isvalid.d.ts.map +0 -1
  198. package/dist/utils/isvalid.js.map +0 -1
  199. package/dist/utils/network.d.ts +0 -76
  200. package/dist/utils/network.d.ts.map +0 -1
  201. package/dist/utils/network.js.map +0 -1
  202. package/dist/utils/spawn.d.ts +0 -11
  203. package/dist/utils/spawn.d.ts.map +0 -1
  204. package/dist/utils/spawn.js.map +0 -1
  205. package/dist/utils/wait.d.ts +0 -56
  206. package/dist/utils/wait.d.ts.map +0 -1
  207. package/dist/utils/wait.js.map +0 -1
  208. /package/bin/{matterbridge → matterbridge.js} +0 -0
@@ -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, nt } 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 { dev, plg, typ } from './matterbridgeTypes.js';
43
15
  import { PluginManager } from './pluginManager.js';
@@ -46,9 +18,6 @@ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
46
18
  import { bridge } from './matterbridgeDeviceTypes.js';
47
19
  import { Frontend } from './frontend.js';
48
20
  import { addVirtualDevices } from './helpers.js';
49
- /**
50
- * Represents the Matterbridge application.
51
- */
52
21
  export class Matterbridge extends EventEmitter {
53
22
  systemInformation = {
54
23
  interfaceName: '',
@@ -97,7 +66,7 @@ export class Matterbridge extends EventEmitter {
97
66
  shellySysUpdate: false,
98
67
  shellyMainUpdate: false,
99
68
  profile: getParameter('profile'),
100
- loggerLevel: "info" /* LogLevel.INFO */,
69
+ loggerLevel: "info",
101
70
  fileLogger: false,
102
71
  matterLoggerLevel: MatterLogLevel.INFO,
103
72
  matterFileLogger: false,
@@ -125,18 +94,15 @@ export class Matterbridge extends EventEmitter {
125
94
  shutdown = false;
126
95
  edge = true;
127
96
  failCountLimit = hasParameter('shelly') ? 600 : 120;
128
- // Matterbridge log files
129
- log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
97
+ log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
130
98
  matterbridgeLoggerFile = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
131
99
  matterLoggerFile = 'matter' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
132
100
  plugins;
133
101
  devices;
134
102
  frontend = new Frontend(this);
135
- // Matterbridge storage
136
103
  nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
137
104
  nodeStorage;
138
105
  nodeContext;
139
- // Cleanup
140
106
  hasCleanupStarted = false;
141
107
  initialized = false;
142
108
  execRunningCount = 0;
@@ -150,23 +116,19 @@ export class Matterbridge extends EventEmitter {
150
116
  sigtermHandler;
151
117
  exceptionHandler;
152
118
  rejectionHandler;
153
- // Matter environment
154
119
  environment = Environment.default;
155
- // Matter storage
156
120
  matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
157
121
  matterStorageService;
158
122
  matterStorageManager;
159
123
  matterbridgeContext;
160
124
  controllerContext;
161
- // Matter parameters
162
- mdnsInterface; // matter server node mdnsInterface: e.g. 'eth0' or 'wlan0' or 'WiFi'
163
- ipv4address; // matter server node listeningAddressIpv4
164
- ipv6address; // matter server node listeningAddressIpv6
165
- port; // first server node port
166
- passcode; // first server node passcode
167
- discriminator; // first server node discriminator
168
- certification; // device certification
169
- // Matter nodes
125
+ mdnsInterface;
126
+ ipv4address;
127
+ ipv6address;
128
+ port;
129
+ passcode;
130
+ discriminator;
131
+ certification;
170
132
  serverNode;
171
133
  aggregatorNode;
172
134
  aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
@@ -174,31 +136,15 @@ export class Matterbridge extends EventEmitter {
174
136
  aggregatorProductId = getIntParameter('productId') ?? 0x8000;
175
137
  aggregatorProductName = getParameter('productName') ?? 'Matterbridge aggregator';
176
138
  static instance;
177
- // We load asyncronously so is private
178
139
  constructor() {
179
140
  super();
180
141
  }
181
- /**
182
- * Retrieves the list of Matterbridge devices.
183
- *
184
- * @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
185
- */
186
142
  getDevices() {
187
143
  return this.devices.array();
188
144
  }
189
- /**
190
- * Retrieves the list of registered plugins.
191
- *
192
- * @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
193
- */
194
145
  getPlugins() {
195
146
  return this.plugins.array();
196
147
  }
197
- /**
198
- * Set the logger logLevel for the Matterbridge classes and call onChangeLoggerLevel() for each plugin.
199
- *
200
- * @param {LogLevel} logLevel The logger logLevel to set.
201
- */
202
148
  async setLogLevel(logLevel) {
203
149
  if (this.log)
204
150
  this.log.logLevel = logLevel;
@@ -212,31 +158,19 @@ export class Matterbridge extends EventEmitter {
212
158
  for (const plugin of this.plugins) {
213
159
  if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
214
160
  continue;
215
- plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel;
216
- await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" /* LogLevel.DEBUG */ : this.log.logLevel);
217
- }
218
- // Set the global logger callback for the WebSocketServer to the common minimum logLevel
219
- let callbackLogLevel = "notice" /* LogLevel.NOTICE */;
220
- if (this.matterbridgeInformation.loggerLevel === "info" /* LogLevel.INFO */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
221
- callbackLogLevel = "info" /* LogLevel.INFO */;
222
- if (this.matterbridgeInformation.loggerLevel === "debug" /* LogLevel.DEBUG */ || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
223
- callbackLogLevel = "debug" /* LogLevel.DEBUG */;
161
+ plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
162
+ await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
163
+ }
164
+ let callbackLogLevel = "notice";
165
+ if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
166
+ callbackLogLevel = "info";
167
+ if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
168
+ callbackLogLevel = "debug";
224
169
  AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
225
170
  this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
226
171
  }
227
- //* ************************************************************************************************************************************ */
228
- // loadInstance() and cleanup() methods */
229
- //* ************************************************************************************************************************************ */
230
- /**
231
- * Loads an instance of the Matterbridge class.
232
- * If an instance already exists, return that instance.
233
- *
234
- * @param {boolean} initialize - Whether to initialize the Matterbridge instance after loading. Defaults to false.
235
- * @returns {Matterbridge} A promise that resolves to the Matterbridge instance.
236
- */
237
172
  static async loadInstance(initialize = false) {
238
173
  if (!Matterbridge.instance) {
239
- // eslint-disable-next-line no-console
240
174
  if (hasParameter('debug'))
241
175
  console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
242
176
  Matterbridge.instance = new Matterbridge();
@@ -245,17 +179,8 @@ export class Matterbridge extends EventEmitter {
245
179
  }
246
180
  return Matterbridge.instance;
247
181
  }
248
- /**
249
- * Call cleanup() and dispose MdnsService.
250
- *
251
- * @param {number} [timeout] - The timeout duration to wait for the cleanup to complete in milliseconds. Default is 1000.
252
- * @param {number} [pause] - The pause duration after the cleanup in milliseconds. Default is 250.
253
- *
254
- * @deprecated This method is deprecated and is ONLY used for jest tests.
255
- */
256
182
  async destroyInstance(timeout = 1000, pause = 250) {
257
183
  this.log.info(`Destroy instance...`);
258
- // Save server nodes to close
259
184
  const servers = [];
260
185
  if (this.bridgeMode === 'bridge') {
261
186
  if (this.serverNode)
@@ -273,109 +198,76 @@ export class Matterbridge extends EventEmitter {
273
198
  servers.push(device.serverNode);
274
199
  }
275
200
  }
276
- // Let any already‐queued microtasks run first
277
201
  await Promise.resolve();
278
- // Wait for the cleanup to finish
279
202
  await new Promise((resolve) => {
280
203
  setTimeout(resolve, pause);
281
204
  });
282
- // Cleanup
283
205
  await this.cleanup('destroying instance...', false, timeout);
284
- // Close servers mdns service
285
206
  this.log.info(`Dispose ${servers.length} MdnsService...`);
286
207
  for (const server of servers) {
287
208
  await server.env.get(MdnsService)[Symbol.asyncDispose]();
288
209
  this.log.info(`Closed ${server.id} MdnsService`);
289
210
  }
290
- // Let any already‐queued microtasks run first
291
211
  await Promise.resolve();
292
- // Wait for the cleanup to finish
293
212
  await new Promise((resolve) => {
294
213
  setTimeout(resolve, pause);
295
214
  });
296
215
  }
297
- /**
298
- * Initializes the Matterbridge application.
299
- *
300
- * @remarks
301
- * This method performs the necessary setup and initialization steps for the Matterbridge application.
302
- * It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
303
- * node version, registers signal handlers, initializes storage, and parses the command line.
304
- *
305
- * @returns {Promise<void>} A Promise that resolves when the initialization is complete.
306
- */
307
216
  async initialize() {
308
- // Emit the initialize_started event
309
217
  this.emit('initialize_started');
310
- // Set the restart mode
311
218
  if (hasParameter('service'))
312
219
  this.restartMode = 'service';
313
220
  if (hasParameter('docker'))
314
221
  this.restartMode = 'docker';
315
- // Set the matterbridge home directory
316
222
  this.homeDirectory = getParameter('homedir') ?? os.homedir();
317
223
  this.matterbridgeInformation.homeDirectory = this.homeDirectory;
318
224
  await createDirectory(this.homeDirectory, 'Matterbridge Home Directory', this.log);
319
- // Set the matterbridge directory
320
225
  this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
321
226
  this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
322
227
  await createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory', this.log);
323
228
  await createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory', this.log);
324
229
  await createDirectory(path.join(this.matterbridgeDirectory, 'uploads'), 'Matterbridge Frontend Uploads Directory', this.log);
325
- // Set the matterbridge plugin directory
326
230
  this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
327
231
  this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
328
232
  await createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory', this.log);
329
- // Set the matterbridge cert directory
330
233
  this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
331
234
  this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
332
235
  await createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory', this.log);
333
- // Set the matterbridge root directory
334
236
  const { fileURLToPath } = await import('node:url');
335
237
  const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
336
238
  this.rootDirectory = path.resolve(currentFileDirectory, '../');
337
239
  this.matterbridgeInformation.rootDirectory = this.rootDirectory;
338
- // Setup the matter environment
339
240
  this.environment.vars.set('log.level', MatterLogLevel.INFO);
340
241
  this.environment.vars.set('log.format', MatterLogFormat.ANSI);
341
242
  this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
342
243
  this.environment.vars.set('runtime.signals', false);
343
244
  this.environment.vars.set('runtime.exitcode', false);
344
- // Register process handlers
345
245
  this.registerProcessHandlers();
346
- // Initialize nodeStorage and nodeContext
347
246
  try {
348
247
  this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
349
248
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
350
249
  this.log.debug('Creating node storage context for matterbridge');
351
250
  this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
352
- // TODO: Remove this code when node-persist-manager is updated
353
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
354
251
  const keys = (await this.nodeStorage?.storage.keys());
355
252
  for (const key of keys) {
356
253
  this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
357
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
358
254
  await this.nodeStorage?.storage.get(key);
359
255
  }
360
256
  const storages = await this.nodeStorage.getStorageNames();
361
257
  for (const storage of storages) {
362
258
  this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
363
259
  const nodeContext = await this.nodeStorage?.createStorage(storage);
364
- // TODO: Remove this code when node-persist-manager is updated
365
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
366
260
  const keys = (await nodeContext?.storage.keys());
367
261
  keys.forEach(async (key) => {
368
262
  this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
369
263
  await nodeContext?.get(key);
370
264
  });
371
265
  }
372
- // Creating a backup of the node storage since it is not corrupted
373
266
  this.log.debug('Creating node storage backup...');
374
267
  await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
375
268
  this.log.debug('Created node storage backup');
376
269
  }
377
270
  catch (error) {
378
- // Restoring the backup of the node storage since it is corrupted
379
271
  this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
380
272
  if (hasParameter('norestore')) {
381
273
  this.log.fatal(`The matterbridge storage is corrupted. Found -norestore parameter: exiting...`);
@@ -389,20 +281,14 @@ export class Matterbridge extends EventEmitter {
389
281
  if (!this.nodeStorage || !this.nodeContext) {
390
282
  throw new Error('Fatal error creating node storage manager and context for matterbridge');
391
283
  }
392
- // Set the first port to use for the commissioning server (will be incremented in childbridge mode)
393
284
  this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
394
- // Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
395
285
  this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode(this.environment.get(Crypto));
396
- // Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
397
286
  this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
398
- // Certificate management
399
287
  const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
400
288
  try {
401
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
402
289
  await fs.access(pairingFilePath, fs.constants.R_OK);
403
290
  const pairingFileContent = await fs.readFile(pairingFilePath, 'utf8');
404
291
  const pairingFileJson = JSON.parse(pairingFileContent);
405
- // Set the vendorId, vendorName, productId and productName if they are present in the pairing file
406
292
  if (isValidNumber(pairingFileJson.vendorId))
407
293
  this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
408
294
  if (isValidString(pairingFileJson.vendorName, 3))
@@ -411,14 +297,11 @@ export class Matterbridge extends EventEmitter {
411
297
  this.aggregatorProductId = pairingFileJson.productId;
412
298
  if (isValidString(pairingFileJson.productName, 3))
413
299
  this.aggregatorProductName = pairingFileJson.productName;
414
- // Override the passcode and discriminator if they are present in the pairing file
415
300
  if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
416
301
  this.passcode = pairingFileJson.passcode;
417
302
  this.discriminator = pairingFileJson.discriminator;
418
303
  this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
419
304
  }
420
- // Set the certification if it is present in the pairing file
421
- /* istanbul ignore next if */
422
305
  if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
423
306
  const hexStringToUint8Array = (hexString) => {
424
307
  const matches = hexString.match(/.{1,2}/g);
@@ -436,44 +319,41 @@ export class Matterbridge extends EventEmitter {
436
319
  catch (error) {
437
320
  this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
438
321
  }
439
- // Store the passcode, discriminator and port in the node context
440
322
  await this.nodeContext.set('matterport', this.port);
441
323
  await this.nodeContext.set('matterpasscode', this.passcode);
442
324
  await this.nodeContext.set('matterdiscriminator', this.discriminator);
443
325
  this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
444
- // Set matterbridge logger level (context: matterbridgeLogLevel)
445
326
  if (hasParameter('logger')) {
446
327
  const level = getParameter('logger');
447
328
  if (level === 'debug') {
448
- this.log.logLevel = "debug" /* LogLevel.DEBUG */;
329
+ this.log.logLevel = "debug";
449
330
  }
450
331
  else if (level === 'info') {
451
- this.log.logLevel = "info" /* LogLevel.INFO */;
332
+ this.log.logLevel = "info";
452
333
  }
453
334
  else if (level === 'notice') {
454
- this.log.logLevel = "notice" /* LogLevel.NOTICE */;
335
+ this.log.logLevel = "notice";
455
336
  }
456
337
  else if (level === 'warn') {
457
- this.log.logLevel = "warn" /* LogLevel.WARN */;
338
+ this.log.logLevel = "warn";
458
339
  }
459
340
  else if (level === 'error') {
460
- this.log.logLevel = "error" /* LogLevel.ERROR */;
341
+ this.log.logLevel = "error";
461
342
  }
462
343
  else if (level === 'fatal') {
463
- this.log.logLevel = "fatal" /* LogLevel.FATAL */;
344
+ this.log.logLevel = "fatal";
464
345
  }
465
346
  else {
466
347
  this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
467
- this.log.logLevel = "info" /* LogLevel.INFO */;
348
+ this.log.logLevel = "info";
468
349
  }
469
350
  }
470
351
  else {
471
- this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" /* LogLevel.NOTICE */ : "info" /* LogLevel.INFO */);
352
+ this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
472
353
  }
473
354
  this.frontend.logLevel = this.log.logLevel;
474
355
  MatterbridgeEndpoint.logLevel = this.log.logLevel;
475
356
  this.matterbridgeInformation.loggerLevel = this.log.logLevel;
476
- // Create the file logger for matterbridge (context: matterbridgeFileLog)
477
357
  if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
478
358
  AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbridgeLoggerFile), this.log.logLevel, true);
479
359
  this.matterbridgeInformation.fileLogger = true;
@@ -482,7 +362,6 @@ export class Matterbridge extends EventEmitter {
482
362
  this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
483
363
  if (this.profile !== undefined)
484
364
  this.log.debug(`Matterbridge profile: ${this.profile}.`);
485
- // Set matter.js logger level, format and logger (context: matterLogLevel)
486
365
  if (hasParameter('matterlogger')) {
487
366
  const level = getParameter('matterlogger');
488
367
  if (level === 'debug') {
@@ -513,9 +392,7 @@ export class Matterbridge extends EventEmitter {
513
392
  }
514
393
  Logger.format = MatterLogFormat.ANSI;
515
394
  Logger.setLogger('default', this.createMatterLogger());
516
- // Logger.destinations.default.write = this.createMatterLogger();
517
395
  this.matterbridgeInformation.matterLoggerLevel = Logger.level;
518
- // Create the file logger for matter.js (context: matterFileLog)
519
396
  if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
520
397
  this.matterbridgeInformation.matterFileLogger = true;
521
398
  Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
@@ -524,9 +401,7 @@ export class Matterbridge extends EventEmitter {
524
401
  });
525
402
  }
526
403
  this.log.debug(`Matter logLevel: ${Logger.level} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
527
- // Log network interfaces
528
404
  const networkInterfaces = os.networkInterfaces();
529
- // console.log(`Network interfaces:`, networkInterfaces);
530
405
  const availableAddresses = Object.entries(networkInterfaces);
531
406
  const availableInterfaces = Object.keys(networkInterfaces);
532
407
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -537,7 +412,6 @@ export class Matterbridge extends EventEmitter {
537
412
  });
538
413
  }
539
414
  }
540
- // Set the interface to use for matter server node mdnsInterface
541
415
  if (hasParameter('mdnsinterface')) {
542
416
  this.mdnsInterface = getParameter('mdnsinterface');
543
417
  }
@@ -546,7 +420,6 @@ export class Matterbridge extends EventEmitter {
546
420
  if (this.mdnsInterface === '')
547
421
  this.mdnsInterface = undefined;
548
422
  }
549
- // Validate mdnsInterface
550
423
  if (this.mdnsInterface) {
551
424
  if (!availableInterfaces.includes(this.mdnsInterface)) {
552
425
  this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
@@ -559,7 +432,6 @@ export class Matterbridge extends EventEmitter {
559
432
  }
560
433
  if (this.mdnsInterface)
561
434
  this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
562
- // Set the listeningAddressIpv4 for the matter commissioning server
563
435
  if (hasParameter('ipv4address')) {
564
436
  this.ipv4address = getParameter('ipv4address');
565
437
  }
@@ -568,7 +440,6 @@ export class Matterbridge extends EventEmitter {
568
440
  if (this.ipv4address === '')
569
441
  this.ipv4address = undefined;
570
442
  }
571
- // Validate ipv4address
572
443
  if (this.ipv4address) {
573
444
  let isValid = false;
574
445
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -584,7 +455,6 @@ export class Matterbridge extends EventEmitter {
584
455
  await this.nodeContext.remove('matteripv4address');
585
456
  }
586
457
  }
587
- // Set the listeningAddressIpv6 for the matter commissioning server
588
458
  if (hasParameter('ipv6address')) {
589
459
  this.ipv6address = getParameter('ipv6address');
590
460
  }
@@ -593,7 +463,6 @@ export class Matterbridge extends EventEmitter {
593
463
  if (this.ipv6address === '')
594
464
  this.ipv6address = undefined;
595
465
  }
596
- // Validate ipv6address
597
466
  if (this.ipv6address) {
598
467
  let isValid = false;
599
468
  for (const [ifaceName, ifaces] of availableAddresses) {
@@ -602,7 +471,6 @@ export class Matterbridge extends EventEmitter {
602
471
  isValid = true;
603
472
  break;
604
473
  }
605
- /* istanbul ignore next */
606
474
  if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6address)) {
607
475
  this.log.info(`Using ipv6address ${CYAN}${this.ipv6address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
608
476
  isValid = true;
@@ -615,7 +483,6 @@ export class Matterbridge extends EventEmitter {
615
483
  await this.nodeContext.remove('matteripv6address');
616
484
  }
617
485
  }
618
- // Initialize the virtual mode
619
486
  if (hasParameter('novirtual')) {
620
487
  this.matterbridgeInformation.virtualMode = 'disabled';
621
488
  await this.nodeContext.set('virtualmode', 'disabled');
@@ -624,19 +491,14 @@ export class Matterbridge extends EventEmitter {
624
491
  this.matterbridgeInformation.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
625
492
  }
626
493
  this.log.debug(`Virtual mode ${this.matterbridgeInformation.virtualMode}.`);
627
- // Initialize PluginManager
628
494
  this.plugins = new PluginManager(this);
629
495
  await this.plugins.loadFromStorage();
630
496
  this.plugins.logLevel = this.log.logLevel;
631
- // Initialize DeviceManager
632
497
  this.devices = new DeviceManager(this);
633
498
  this.devices.logLevel = this.log.logLevel;
634
- // Get the plugins from node storage and create the plugins node storage contexts
635
499
  for (const plugin of this.plugins) {
636
500
  const packageJson = await this.plugins.parse(plugin);
637
501
  if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
638
- // Try to reinstall the plugin from npm (for Docker pull and external plugins)
639
- // We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
640
502
  this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
641
503
  try {
642
504
  const { spawnCommand } = await import('./utils/spawn.js');
@@ -659,7 +521,6 @@ export class Matterbridge extends EventEmitter {
659
521
  await plugin.nodeContext.set('description', plugin.description);
660
522
  await plugin.nodeContext.set('author', plugin.author);
661
523
  }
662
- // Log system info and create .matterbridge directory
663
524
  await this.logNodeAndSystemInfo();
664
525
  this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
665
526
  `${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
@@ -667,7 +528,6 @@ export class Matterbridge extends EventEmitter {
667
528
  `${hasParameter('controller') ? 'mode controller ' : ''}` +
668
529
  `${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
669
530
  `running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
670
- // Check node version and throw error
671
531
  const minNodeVersion = 18;
672
532
  const nodeVersion = process.versions.node;
673
533
  const versionMajor = parseInt(nodeVersion.split('.')[0]);
@@ -675,18 +535,10 @@ export class Matterbridge extends EventEmitter {
675
535
  this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
676
536
  throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
677
537
  }
678
- // Parse command line
679
538
  await this.parseCommandLine();
680
- // Emit the initialize_completed event
681
539
  this.emit('initialize_completed');
682
540
  this.initialized = true;
683
541
  }
684
- /**
685
- * Parses the command line arguments and performs the corresponding actions.
686
- *
687
- * @private
688
- * @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
689
- */
690
542
  async parseCommandLine() {
691
543
  if (hasParameter('help')) {
692
544
  this.log.info(`\nUsage: matterbridge [options]\n
@@ -747,19 +599,6 @@ export class Matterbridge extends EventEmitter {
747
599
  }
748
600
  index++;
749
601
  }
750
- /*
751
- const serializedRegisteredDevices = await this.nodeContext?.get<SerializedMatterbridgeEndpoint[]>('devices', []);
752
- this.log.info(`│ Registered devices (${serializedRegisteredDevices?.length})`);
753
- serializedRegisteredDevices?.forEach((device, index) => {
754
- if (index !== serializedRegisteredDevices.length - 1) {
755
- this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
756
- this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
757
- } else {
758
- this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
759
- this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
760
- }
761
- });
762
- */
763
602
  this.shutdown = true;
764
603
  return;
765
604
  }
@@ -810,7 +649,6 @@ export class Matterbridge extends EventEmitter {
810
649
  this.shutdown = true;
811
650
  return;
812
651
  }
813
- // Start the matter storage and create the matterbridge context
814
652
  try {
815
653
  await this.startMatterStorage();
816
654
  }
@@ -818,21 +656,18 @@ export class Matterbridge extends EventEmitter {
818
656
  this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
819
657
  throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
820
658
  }
821
- // Clear the matterbridge context if the reset parameter is set
822
659
  if (hasParameter('reset') && getParameter('reset') === undefined) {
823
660
  this.initialized = true;
824
661
  await this.shutdownProcessAndReset();
825
662
  this.shutdown = true;
826
663
  return;
827
664
  }
828
- // Clear matterbridge plugin context if the reset parameter is set
829
665
  if (hasParameter('reset') && getParameter('reset') !== undefined) {
830
666
  this.log.debug(`Reset plugin ${getParameter('reset')}`);
831
667
  const plugin = this.plugins.get(getParameter('reset'));
832
668
  if (plugin) {
833
669
  const matterStorageManager = await this.matterStorageService?.open(plugin.name);
834
670
  if (!matterStorageManager) {
835
- /* istanbul ignore next */
836
671
  this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
837
672
  }
838
673
  else {
@@ -851,39 +686,32 @@ export class Matterbridge extends EventEmitter {
851
686
  this.shutdown = true;
852
687
  return;
853
688
  }
854
- // Initialize frontend
855
689
  if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
856
690
  await this.frontend.start(getIntParameter('frontend'));
857
- // Check in 30 seconds the latest and dev versions of matterbridge and the plugins
858
691
  clearTimeout(this.checkUpdateTimeout);
859
692
  this.checkUpdateTimeout = setTimeout(async () => {
860
693
  const { checkUpdates } = await import('./update.js');
861
694
  checkUpdates(this);
862
695
  }, 30 * 1000).unref();
863
- // Check each 12 hours the latest and dev versions of matterbridge and the plugins
864
696
  clearInterval(this.checkUpdateInterval);
865
697
  this.checkUpdateInterval = setInterval(async () => {
866
698
  const { checkUpdates } = await import('./update.js');
867
699
  checkUpdates(this);
868
700
  }, 12 * 60 * 60 * 1000).unref();
869
- // Start the matterbridge in mode test
870
701
  if (hasParameter('test')) {
871
702
  this.bridgeMode = 'bridge';
872
703
  MatterbridgeEndpoint.bridgeMode = 'bridge';
873
704
  return;
874
705
  }
875
- // Start the matterbridge in mode controller
876
706
  if (hasParameter('controller')) {
877
707
  this.bridgeMode = 'controller';
878
708
  await this.startController();
879
709
  return;
880
710
  }
881
- // Check if the bridge mode is set and start matterbridge in bridge mode if not set
882
711
  if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
883
712
  this.log.info('Setting default matterbridge start mode to bridge');
884
713
  await this.nodeContext?.set('bridgeMode', 'bridge');
885
714
  }
886
- // Start matterbridge in bridge mode
887
715
  if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
888
716
  this.bridgeMode = 'bridge';
889
717
  MatterbridgeEndpoint.bridgeMode = 'bridge';
@@ -891,7 +719,6 @@ export class Matterbridge extends EventEmitter {
891
719
  await this.startBridge();
892
720
  return;
893
721
  }
894
- // Start matterbridge in childbridge mode
895
722
  if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
896
723
  this.bridgeMode = 'childbridge';
897
724
  MatterbridgeEndpoint.bridgeMode = 'childbridge';
@@ -900,20 +727,10 @@ export class Matterbridge extends EventEmitter {
900
727
  return;
901
728
  }
902
729
  }
903
- /**
904
- * Asynchronously loads and starts the registered plugins.
905
- *
906
- * This method is responsible for initializing and starting all enabled plugins.
907
- * It ensures that each plugin is properly loaded and started before the bridge starts.
908
- *
909
- * @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
910
- */
911
730
  async startPlugins() {
912
- // Check, load and start the plugins
913
731
  for (const plugin of this.plugins) {
914
732
  plugin.configJson = await this.plugins.loadConfig(plugin);
915
733
  plugin.schemaJson = await this.plugins.loadSchema(plugin);
916
- // Check if the plugin is available
917
734
  if (!(await this.plugins.resolve(plugin.path))) {
918
735
  this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
919
736
  plugin.enabled = false;
@@ -931,14 +748,10 @@ export class Matterbridge extends EventEmitter {
931
748
  plugin.configured = false;
932
749
  plugin.registeredDevices = undefined;
933
750
  plugin.addedDevices = undefined;
934
- this.plugins.load(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
751
+ this.plugins.load(plugin, true, 'Matterbridge is starting');
935
752
  }
936
753
  this.frontend.wssSendRefreshRequired('plugins');
937
754
  }
938
- /**
939
- * Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
940
- * When either of these signals are received, the cleanup method is called with an appropriate message.
941
- */
942
755
  registerProcessHandlers() {
943
756
  this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
944
757
  process.removeAllListeners('uncaughtException');
@@ -965,9 +778,6 @@ export class Matterbridge extends EventEmitter {
965
778
  };
966
779
  process.on('SIGTERM', this.sigtermHandler);
967
780
  }
968
- /**
969
- * Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
970
- */
971
781
  deregisterProcessHandlers() {
972
782
  this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
973
783
  if (this.exceptionHandler)
@@ -984,17 +794,12 @@ export class Matterbridge extends EventEmitter {
984
794
  process.off('SIGTERM', this.sigtermHandler);
985
795
  this.sigtermHandler = undefined;
986
796
  }
987
- /**
988
- * Logs the node and system information.
989
- */
990
797
  async logNodeAndSystemInfo() {
991
- // IP address information
992
798
  const networkInterfaces = os.networkInterfaces();
993
799
  this.systemInformation.interfaceName = '';
994
800
  this.systemInformation.ipv4Address = '';
995
801
  this.systemInformation.ipv6Address = '';
996
802
  for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
997
- // this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
998
803
  if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
999
804
  continue;
1000
805
  if (!interfaceDetails) {
@@ -1020,22 +825,19 @@ export class Matterbridge extends EventEmitter {
1020
825
  break;
1021
826
  }
1022
827
  }
1023
- // Node information
1024
828
  this.systemInformation.nodeVersion = process.versions.node;
1025
829
  const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
1026
830
  const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
1027
831
  const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
1028
- // Host system information
1029
832
  this.systemInformation.hostname = os.hostname();
1030
833
  this.systemInformation.user = os.userInfo().username;
1031
- this.systemInformation.osType = os.type(); // "Windows_NT", "Darwin", etc.
1032
- this.systemInformation.osRelease = os.release(); // Kernel version
1033
- this.systemInformation.osPlatform = os.platform(); // "win32", "linux", "darwin", etc.
1034
- this.systemInformation.osArch = os.arch(); // "x64", "arm", etc.
1035
- this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
1036
- this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB'; // Convert to GB
1037
- this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours'; // Convert to hours
1038
- // Log the system information
834
+ this.systemInformation.osType = os.type();
835
+ this.systemInformation.osRelease = os.release();
836
+ this.systemInformation.osPlatform = os.platform();
837
+ this.systemInformation.osArch = os.arch();
838
+ this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
839
+ this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
840
+ this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
1039
841
  this.log.debug('Host System Information:');
1040
842
  this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
1041
843
  this.log.debug(`- User: ${this.systemInformation.user}`);
@@ -1051,17 +853,14 @@ export class Matterbridge extends EventEmitter {
1051
853
  this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
1052
854
  this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
1053
855
  this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
1054
- // Log directories
1055
856
  this.log.debug(`Root Directory: ${this.rootDirectory}`);
1056
857
  this.log.debug(`Home Directory: ${this.homeDirectory}`);
1057
858
  this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
1058
859
  this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
1059
860
  this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
1060
- // Global node_modules directory
1061
861
  if (this.nodeContext)
1062
862
  this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
1063
863
  if (this.globalModulesDirectory === '') {
1064
- // First run of Matterbridge so the node storage is empty
1065
864
  try {
1066
865
  const { getGlobalNodeModules } = await import('./utils/network.js');
1067
866
  this.execRunningCount++;
@@ -1076,81 +875,50 @@ export class Matterbridge extends EventEmitter {
1076
875
  }
1077
876
  else
1078
877
  this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
1079
- /* removed cause is too expensive for the shelly board and not really needed. Why should the globalModulesDirectory change?
1080
- else {
1081
- this.getGlobalNodeModules()
1082
- .then(async (globalModulesDirectory) => {
1083
- this.globalModulesDirectory = globalModulesDirectory;
1084
- this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
1085
- this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
1086
- await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
1087
- })
1088
- .catch((error) => {
1089
- this.log.error(`Error getting global node_modules directory: ${error}`);
1090
- });
1091
- }*/
1092
- // Matterbridge version
1093
878
  const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
1094
879
  this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
1095
880
  this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeDevVersion = packageJson.version;
1096
881
  this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
1097
- // Matterbridge latest version (will be set in the checkUpdate function)
1098
882
  if (this.nodeContext)
1099
883
  this.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
1100
884
  this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
1101
- // Matterbridge dev version (will be set in the checkUpdate function)
1102
885
  if (this.nodeContext)
1103
886
  this.matterbridgeDevVersion = this.matterbridgeInformation.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
1104
887
  this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
1105
- // Current working directory
1106
888
  const currentDir = process.cwd();
1107
889
  this.log.debug(`Current Working Directory: ${currentDir}`);
1108
- // Command line arguments (excluding 'node' and the script name)
1109
890
  const cmdArgs = process.argv.slice(2).join(' ');
1110
891
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
1111
892
  }
1112
- /**
1113
- * Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
1114
- *
1115
- * @returns {Function} The MatterLogger function.
1116
- */
1117
893
  createMatterLogger() {
1118
- const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
894
+ const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
1119
895
  return (level, formattedLog) => {
1120
896
  const logger = formattedLog.slice(44, 44 + 20).trim();
1121
897
  const message = formattedLog.slice(65);
1122
898
  matterLogger.logName = logger;
1123
899
  switch (level) {
1124
900
  case MatterLogLevel.DEBUG:
1125
- matterLogger.log("debug" /* LogLevel.DEBUG */, message);
901
+ matterLogger.log("debug", message);
1126
902
  break;
1127
903
  case MatterLogLevel.INFO:
1128
- matterLogger.log("info" /* LogLevel.INFO */, message);
904
+ matterLogger.log("info", message);
1129
905
  break;
1130
906
  case MatterLogLevel.NOTICE:
1131
- matterLogger.log("notice" /* LogLevel.NOTICE */, message);
907
+ matterLogger.log("notice", message);
1132
908
  break;
1133
909
  case MatterLogLevel.WARN:
1134
- matterLogger.log("warn" /* LogLevel.WARN */, message);
910
+ matterLogger.log("warn", message);
1135
911
  break;
1136
912
  case MatterLogLevel.ERROR:
1137
- matterLogger.log("error" /* LogLevel.ERROR */, message);
913
+ matterLogger.log("error", message);
1138
914
  break;
1139
915
  case MatterLogLevel.FATAL:
1140
- matterLogger.log("fatal" /* LogLevel.FATAL */, message);
916
+ matterLogger.log("fatal", message);
1141
917
  break;
1142
918
  }
1143
919
  };
1144
920
  }
1145
- /**
1146
- * Creates a Matter File Logger.
1147
- *
1148
- * @param {string} filePath - The path to the log file.
1149
- * @param {boolean} [unlink] - Whether to unlink the log file before creating a new one.
1150
- * @returns {Function} - A function that logs formatted messages to the log file.
1151
- */
1152
921
  async createMatterFileLogger(filePath, unlink = false) {
1153
- // 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
1154
922
  let fileSize = 0;
1155
923
  if (unlink) {
1156
924
  try {
@@ -1161,12 +929,10 @@ export class Matterbridge extends EventEmitter {
1161
929
  }
1162
930
  }
1163
931
  return async (level, formattedLog) => {
1164
- /* istanbul ignore if */
1165
932
  if (fileSize > 100000000) {
1166
- return; // Stop logging if the file size is greater than 100MB
933
+ return;
1167
934
  }
1168
935
  fileSize += formattedLog.length;
1169
- /* istanbul ignore if */
1170
936
  if (fileSize > 100000000) {
1171
937
  await fs.appendFile(filePath, `Logging on file has been stopped because the file size is greater than 100MB.` + os.EOL);
1172
938
  return;
@@ -1199,21 +965,12 @@ export class Matterbridge extends EventEmitter {
1199
965
  }
1200
966
  };
1201
967
  }
1202
- /**
1203
- * Restarts the process by exiting the current instance and loading a new instance.
1204
- */
1205
968
  async restartProcess() {
1206
969
  await this.cleanup('restarting...', true);
1207
970
  }
1208
- /**
1209
- * Shut down the process.
1210
- */
1211
971
  async shutdownProcess() {
1212
972
  await this.cleanup('shutting down...', false);
1213
973
  }
1214
- /**
1215
- * Update matterbridge and shut down the process.
1216
- */
1217
974
  async updateProcess() {
1218
975
  this.log.info('Updating matterbridge...');
1219
976
  try {
@@ -1227,75 +984,52 @@ export class Matterbridge extends EventEmitter {
1227
984
  this.frontend.wssSendRestartRequired();
1228
985
  await this.cleanup('updating...', false);
1229
986
  }
1230
- /**
1231
- * Unregister all devices and shut down the process.
1232
- */
1233
987
  async unregisterAndShutdownProcess() {
1234
988
  this.log.info('Unregistering all devices and shutting down...');
1235
989
  for (const plugin of this.plugins) {
1236
990
  await this.removeAllBridgedEndpoints(plugin.name, 250);
1237
991
  }
1238
992
  this.log.debug('Waiting for the MessageExchange to finish...');
1239
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second for MessageExchange to finish
993
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1240
994
  this.log.debug('Cleaning up and shutting down...');
1241
995
  await this.cleanup('unregistered all devices and shutting down...', false);
1242
996
  }
1243
- /**
1244
- * Reset commissioning and shut down the process.
1245
- */
1246
997
  async shutdownProcessAndReset() {
1247
998
  await this.cleanup('shutting down with reset...', false);
1248
999
  }
1249
- /**
1250
- * Factory reset and shut down the process.
1251
- */
1252
1000
  async shutdownProcessAndFactoryReset() {
1253
1001
  await this.cleanup('shutting down with factory reset...', false);
1254
1002
  }
1255
- /**
1256
- * Cleans up the Matterbridge instance.
1257
- *
1258
- * @param {string} message - The cleanup message.
1259
- * @param {boolean} [restart] - Indicates whether to restart the instance after cleanup. Default is `false`.
1260
- * @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
1261
- * @returns {Promise<void>} A promise that resolves when the cleanup is completed.
1262
- */
1263
1003
  async cleanup(message, restart = false, timeout = 1000) {
1264
1004
  if (this.initialized && !this.hasCleanupStarted) {
1265
1005
  this.emit('cleanup_started');
1266
1006
  this.hasCleanupStarted = true;
1267
1007
  this.log.info(message);
1268
- // Clear the start matter interval
1269
1008
  if (this.startMatterInterval) {
1270
1009
  clearInterval(this.startMatterInterval);
1271
1010
  this.startMatterInterval = undefined;
1272
1011
  this.log.debug('Start matter interval cleared');
1273
1012
  }
1274
- // Clear the check update timeout
1275
1013
  if (this.checkUpdateTimeout) {
1276
1014
  clearTimeout(this.checkUpdateTimeout);
1277
1015
  this.checkUpdateTimeout = undefined;
1278
1016
  this.log.debug('Check update timeout cleared');
1279
1017
  }
1280
- // Clear the check update interval
1281
1018
  if (this.checkUpdateInterval) {
1282
1019
  clearInterval(this.checkUpdateInterval);
1283
1020
  this.checkUpdateInterval = undefined;
1284
1021
  this.log.debug('Check update interval cleared');
1285
1022
  }
1286
- // Clear the configure timeout
1287
1023
  if (this.configureTimeout) {
1288
1024
  clearTimeout(this.configureTimeout);
1289
1025
  this.configureTimeout = undefined;
1290
1026
  this.log.debug('Matterbridge configure timeout cleared');
1291
1027
  }
1292
- // Clear the reachability timeout
1293
1028
  if (this.reachabilityTimeout) {
1294
1029
  clearTimeout(this.reachabilityTimeout);
1295
1030
  this.reachabilityTimeout = undefined;
1296
1031
  this.log.debug('Matterbridge reachability timeout cleared');
1297
1032
  }
1298
- // Call the shutdown method of each plugin and clear the plugins reachability timeout
1299
1033
  for (const plugin of this.plugins) {
1300
1034
  if (!plugin.enabled || plugin.error)
1301
1035
  continue;
@@ -1306,10 +1040,9 @@ export class Matterbridge extends EventEmitter {
1306
1040
  this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
1307
1041
  }
1308
1042
  }
1309
- // Stop matter server nodes
1310
1043
  this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
1311
1044
  this.log.debug('Waiting for the MessageExchange to finish...');
1312
- await new Promise((resolve) => setTimeout(resolve, timeout)); // Wait for MessageExchange to finish
1045
+ await new Promise((resolve) => setTimeout(resolve, timeout));
1313
1046
  if (this.bridgeMode === 'bridge') {
1314
1047
  if (this.serverNode) {
1315
1048
  await this.stopServerNode(this.serverNode);
@@ -1331,7 +1064,6 @@ export class Matterbridge extends EventEmitter {
1331
1064
  }
1332
1065
  }
1333
1066
  this.log.notice('Stopped matter server nodes');
1334
- // Matter commisioning reset
1335
1067
  if (message === 'shutting down with reset...') {
1336
1068
  this.log.info('Resetting Matterbridge commissioning information...');
1337
1069
  await this.matterStorageManager?.createContext('events')?.clearAll();
@@ -1341,36 +1073,18 @@ export class Matterbridge extends EventEmitter {
1341
1073
  await this.matterbridgeContext?.clearAll();
1342
1074
  this.log.info('Matter storage reset done! Remove the bridge from the controller.');
1343
1075
  }
1344
- // Stop matter storage
1345
1076
  await this.stopMatterStorage();
1346
- // Stop the frontend
1347
1077
  await this.frontend.stop();
1348
- // Remove the matterfilelogger
1349
1078
  try {
1350
1079
  Logger.removeLogger('matterfilelogger');
1351
1080
  }
1352
1081
  catch (error) {
1353
1082
  this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${db}: ${error instanceof Error ? error.message : String(error)}`);
1354
1083
  }
1355
- // Close the matterbridge node storage and context
1356
1084
  if (this.nodeStorage && this.nodeContext) {
1357
- /*
1358
- TODO: Implement serialization of registered devices in edge mode
1359
- this.log.info('Saving registered devices...');
1360
- const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
1361
- this.devices.forEach(async (device) => {
1362
- const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
1363
- // this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
1364
- if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
1365
- });
1366
- await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
1367
- this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
1368
- */
1369
- // Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
1370
1085
  this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
1371
1086
  await this.nodeContext.close();
1372
1087
  this.nodeContext = undefined;
1373
- // Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
1374
1088
  for (const plugin of this.plugins) {
1375
1089
  if (plugin.nodeContext) {
1376
1090
  this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
@@ -1387,10 +1101,8 @@ export class Matterbridge extends EventEmitter {
1387
1101
  }
1388
1102
  this.plugins.clear();
1389
1103
  this.devices.clear();
1390
- // Factory reset
1391
1104
  if (message === 'shutting down with factory reset...') {
1392
1105
  try {
1393
- // Delete matter storage directory with its subdirectories and backup
1394
1106
  const dir = path.join(this.matterbridgeDirectory, this.matterStorageName);
1395
1107
  this.log.info(`Removing matter storage directory: ${dir}`);
1396
1108
  await fs.rm(dir, { recursive: true });
@@ -1404,7 +1116,6 @@ export class Matterbridge extends EventEmitter {
1404
1116
  }
1405
1117
  }
1406
1118
  try {
1407
- // Delete matterbridge storage directory with its subdirectories and backup
1408
1119
  const dir = path.join(this.matterbridgeDirectory, this.nodeStorageName);
1409
1120
  this.log.info(`Removing matterbridge storage directory: ${dir}`);
1410
1121
  await fs.rm(dir, { recursive: true });
@@ -1419,13 +1130,12 @@ export class Matterbridge extends EventEmitter {
1419
1130
  }
1420
1131
  this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
1421
1132
  }
1422
- // Deregisters the process handlers
1423
1133
  this.deregisterProcessHandlers();
1424
1134
  if (restart) {
1425
1135
  if (message === 'updating...') {
1426
1136
  this.log.info('Cleanup completed. Updating...');
1427
1137
  Matterbridge.instance = undefined;
1428
- this.emit('update'); // Restart the process but the update has been done before. TODO move all updates to the cli
1138
+ this.emit('update');
1429
1139
  }
1430
1140
  else if (message === 'restarting...') {
1431
1141
  this.log.info('Cleanup completed. Restarting...');
@@ -1446,13 +1156,6 @@ export class Matterbridge extends EventEmitter {
1446
1156
  this.log.debug('Cleanup already started...');
1447
1157
  }
1448
1158
  }
1449
- /**
1450
- * Creates and configures the server node for a single not bridged device.
1451
- *
1452
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1453
- * @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
1454
- * @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
1455
- */
1456
1159
  async createDeviceServerNode(plugin, device) {
1457
1160
  if (device.mode === 'server' && !device.serverNode && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
1458
1161
  this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
@@ -1463,14 +1166,6 @@ export class Matterbridge extends EventEmitter {
1463
1166
  this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
1464
1167
  }
1465
1168
  }
1466
- /**
1467
- * Creates and configures the server node for an accessory plugin for a given device.
1468
- *
1469
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1470
- * @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
1471
- * @param {boolean} [start] - Whether to start the server node after adding the device. Default is `false`.
1472
- * @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
1473
- */
1474
1169
  async createAccessoryPlugin(plugin, device, start = false) {
1475
1170
  if (!plugin.locked && device.deviceType && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
1476
1171
  plugin.locked = true;
@@ -1484,12 +1179,6 @@ export class Matterbridge extends EventEmitter {
1484
1179
  await this.startServerNode(plugin.serverNode);
1485
1180
  }
1486
1181
  }
1487
- /**
1488
- * Creates and configures the server node and the aggregator node for a dynamic plugin.
1489
- *
1490
- * @param {RegisteredPlugin} plugin - The plugin to configure.
1491
- * @returns {Promise<void>} A promise that resolves when the server node and the aggregator node for the dynamic plugin is created and configured.
1492
- */
1493
1182
  async createDynamicPlugin(plugin) {
1494
1183
  if (!plugin.locked) {
1495
1184
  plugin.locked = true;
@@ -1500,14 +1189,7 @@ export class Matterbridge extends EventEmitter {
1500
1189
  await plugin.serverNode.add(plugin.aggregatorNode);
1501
1190
  }
1502
1191
  }
1503
- /**
1504
- * Starts the Matterbridge in bridge mode.
1505
- *
1506
- * @private
1507
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1508
- */
1509
1192
  async startBridge() {
1510
- // Plugins are configured by a timer when matter server is started and plugin.configured is set to true
1511
1193
  if (!this.matterStorageManager)
1512
1194
  throw new Error('No storage manager initialized');
1513
1195
  if (!this.matterbridgeContext)
@@ -1546,16 +1228,13 @@ export class Matterbridge extends EventEmitter {
1546
1228
  clearInterval(this.startMatterInterval);
1547
1229
  this.startMatterInterval = undefined;
1548
1230
  this.log.debug('Cleared startMatterInterval interval for Matterbridge');
1549
- // Start the Matter server node
1550
- this.startServerNode(this.serverNode); // We don't await this, because the server node is started in the background
1551
- // Start the Matter server node of single devices in mode 'server'
1231
+ this.startServerNode(this.serverNode);
1552
1232
  for (const device of this.devices.array()) {
1553
1233
  if (device.mode === 'server' && device.serverNode) {
1554
1234
  this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
1555
- this.startServerNode(device.serverNode); // We don't await this, because the server node is started in the background
1235
+ this.startServerNode(device.serverNode);
1556
1236
  }
1557
1237
  }
1558
- // Configure the plugins
1559
1238
  this.configureTimeout = setTimeout(async () => {
1560
1239
  for (const plugin of this.plugins) {
1561
1240
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1573,24 +1252,16 @@ export class Matterbridge extends EventEmitter {
1573
1252
  }
1574
1253
  this.frontend.wssSendRefreshRequired('plugins');
1575
1254
  }, 30 * 1000).unref();
1576
- // Setting reachability to true
1577
1255
  this.reachabilityTimeout = setTimeout(() => {
1578
1256
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1579
1257
  if (this.aggregatorNode)
1580
1258
  this.setAggregatorReachability(this.aggregatorNode, true);
1581
1259
  this.frontend.wssSendRefreshRequired('reachability');
1582
1260
  }, 60 * 1000).unref();
1583
- // Logger.get('LogServerNode').info(this.serverNode);
1584
1261
  this.emit('bridge_started');
1585
1262
  this.log.notice('Matterbridge bridge started successfully');
1586
1263
  }, 1000);
1587
1264
  }
1588
- /**
1589
- * Starts the Matterbridge in childbridge mode.
1590
- *
1591
- * @private
1592
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1593
- */
1594
1265
  async startChildbridge() {
1595
1266
  if (!this.matterStorageManager)
1596
1267
  throw new Error('No storage manager initialized');
@@ -1627,9 +1298,8 @@ export class Matterbridge extends EventEmitter {
1627
1298
  return;
1628
1299
  clearInterval(this.startMatterInterval);
1629
1300
  this.startMatterInterval = undefined;
1630
- await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second to ensure all plugins server nodes are ready
1301
+ await new Promise((resolve) => setTimeout(resolve, 1000));
1631
1302
  this.log.debug('Cleared startMatterInterval interval in childbridge mode');
1632
- // Configure the plugins
1633
1303
  this.configureTimeout = setTimeout(async () => {
1634
1304
  for (const plugin of this.plugins) {
1635
1305
  if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
@@ -1666,9 +1336,7 @@ export class Matterbridge extends EventEmitter {
1666
1336
  this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
1667
1337
  continue;
1668
1338
  }
1669
- // Start the Matter server node
1670
- this.startServerNode(plugin.serverNode); // We don't await this, because the server node is started in the background
1671
- // Setting reachability to true
1339
+ this.startServerNode(plugin.serverNode);
1672
1340
  plugin.reachabilityTimeout = setTimeout(() => {
1673
1341
  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}`);
1674
1342
  if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
@@ -1676,241 +1344,19 @@ export class Matterbridge extends EventEmitter {
1676
1344
  this.frontend.wssSendRefreshRequired('reachability');
1677
1345
  }, 60 * 1000).unref();
1678
1346
  }
1679
- // Start the Matter server node of single devices in mode 'server'
1680
1347
  for (const device of this.devices.array()) {
1681
1348
  if (device.mode === 'server' && device.serverNode) {
1682
1349
  this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
1683
- this.startServerNode(device.serverNode); // We don't await this, because the server node is started in the background
1350
+ this.startServerNode(device.serverNode);
1684
1351
  }
1685
1352
  }
1686
- // Logger.get('LogServerNode').info(this.serverNode);
1687
1353
  this.emit('childbridge_started');
1688
1354
  this.log.notice('Matterbridge childbridge started successfully');
1689
1355
  }, 1000);
1690
1356
  }
1691
- /**
1692
- * Starts the Matterbridge controller.
1693
- *
1694
- * @private
1695
- * @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
1696
- */
1697
1357
  async startController() {
1698
- /*
1699
- if (!this.matterStorageManager) {
1700
- this.log.error('No storage manager initialized');
1701
- await this.cleanup('No storage manager initialized');
1702
- return;
1703
- }
1704
- this.log.info('Creating context: mattercontrollerContext');
1705
- this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
1706
- if (!this.controllerContext) {
1707
- this.log.error('No storage context mattercontrollerContext initialized');
1708
- await this.cleanup('No storage context mattercontrollerContext initialized');
1709
- return;
1710
- }
1711
-
1712
- this.log.debug('Starting matterbridge in mode', this.bridgeMode);
1713
- this.matterServer = await this.createMatterServer(this.storageManager);
1714
- this.log.info('Creating matter commissioning controller');
1715
- this.commissioningController = new CommissioningController({
1716
- autoConnect: false,
1717
- });
1718
- this.log.info('Adding matter commissioning controller to matter server');
1719
- await this.matterServer.addCommissioningController(this.commissioningController);
1720
-
1721
- this.log.info('Starting matter server');
1722
- await this.matterServer.start();
1723
- this.log.info('Matter server started');
1724
- const commissioningOptions: ControllerCommissioningFlowOptions = {
1725
- regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
1726
- regulatoryCountryCode: 'XX',
1727
- };
1728
- const commissioningController = new CommissioningController({
1729
- environment: {
1730
- environment,
1731
- id: uniqueId,
1732
- },
1733
- autoConnect: false, // Do not auto connect to the commissioned nodes
1734
- adminFabricLabel,
1735
- });
1736
-
1737
- if (hasParameter('pairingcode')) {
1738
- this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
1739
- const pairingCode = getParameter('pairingcode');
1740
- const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
1741
- const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
1742
-
1743
- let longDiscriminator, setupPin, shortDiscriminator;
1744
- if (pairingCode !== undefined) {
1745
- const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
1746
- shortDiscriminator = pairingCodeCodec.shortDiscriminator;
1747
- longDiscriminator = undefined;
1748
- setupPin = pairingCodeCodec.passcode;
1749
- this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
1750
- } else {
1751
- longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
1752
- if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
1753
- setupPin = this.controllerContext.get('pin', 20202021);
1754
- }
1755
- if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
1756
- throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
1757
- }
1758
-
1759
- const options = {
1760
- commissioning: commissioningOptions,
1761
- discovery: {
1762
- knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
1763
- identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
1764
- },
1765
- passcode: setupPin,
1766
- } as NodeCommissioningOptions;
1767
- this.log.info('Commissioning with options:', options);
1768
- const nodeId = await this.commissioningController.commissionNode(options);
1769
- this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
1770
- this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
1771
- } // (hasParameter('pairingcode'))
1772
-
1773
- if (hasParameter('unpairall')) {
1774
- this.log.info('***Commissioning controller unpairing all nodes...');
1775
- const nodeIds = this.commissioningController.getCommissionedNodes();
1776
- for (const nodeId of nodeIds) {
1777
- this.log.info('***Commissioning controller unpairing node:', nodeId);
1778
- await this.commissioningController.removeNode(nodeId);
1779
- }
1780
- return;
1781
- }
1782
-
1783
- if (hasParameter('discover')) {
1784
- // const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
1785
- // console.log(discover);
1786
- }
1787
-
1788
- if (!this.commissioningController.isCommissioned()) {
1789
- this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
1790
- return;
1791
- }
1792
-
1793
- const nodeIds = this.commissioningController.getCommissionedNodes();
1794
- this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
1795
- for (const nodeId of nodeIds) {
1796
- this.log.info(`***Connecting to commissioned node: ${nodeId}`);
1797
-
1798
- const node = await this.commissioningController.connectNode(nodeId, {
1799
- autoSubscribe: false,
1800
- attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
1801
- this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
1802
- eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
1803
- this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
1804
- stateInformationCallback: (peerNodeId, info) => {
1805
- switch (info) {
1806
- case NodeStateInformation.Connected:
1807
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
1808
- break;
1809
- case NodeStateInformation.Disconnected:
1810
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
1811
- break;
1812
- case NodeStateInformation.Reconnecting:
1813
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
1814
- break;
1815
- case NodeStateInformation.WaitingForDeviceDiscovery:
1816
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
1817
- break;
1818
- case NodeStateInformation.StructureChanged:
1819
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
1820
- break;
1821
- case NodeStateInformation.Decommissioned:
1822
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
1823
- break;
1824
- default:
1825
- this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
1826
- break;
1827
- }
1828
- },
1829
- });
1830
-
1831
- node.logStructure();
1832
-
1833
- // Get the interaction client
1834
- this.log.info('Getting the interaction client');
1835
- const interactionClient = await node.getInteractionClient();
1836
- let cluster;
1837
- let attributes;
1838
-
1839
- // Log BasicInformationCluster
1840
- cluster = BasicInformationCluster;
1841
- attributes = await interactionClient.getMultipleAttributes({
1842
- attributes: [{ clusterId: cluster.id }],
1843
- });
1844
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1845
- attributes.forEach((attribute) => {
1846
- this.log.info(
1847
- `- 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}`,
1848
- );
1849
- });
1850
-
1851
- // Log PowerSourceCluster
1852
- cluster = PowerSourceCluster;
1853
- attributes = await interactionClient.getMultipleAttributes({
1854
- attributes: [{ clusterId: cluster.id }],
1855
- });
1856
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1857
- attributes.forEach((attribute) => {
1858
- this.log.info(
1859
- `- 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}`,
1860
- );
1861
- });
1862
-
1863
- // Log ThreadNetworkDiagnostics
1864
- cluster = ThreadNetworkDiagnosticsCluster;
1865
- attributes = await interactionClient.getMultipleAttributes({
1866
- attributes: [{ clusterId: cluster.id }],
1867
- });
1868
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1869
- attributes.forEach((attribute) => {
1870
- this.log.info(
1871
- `- 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}`,
1872
- );
1873
- });
1874
-
1875
- // Log SwitchCluster
1876
- cluster = SwitchCluster;
1877
- attributes = await interactionClient.getMultipleAttributes({
1878
- attributes: [{ clusterId: cluster.id }],
1879
- });
1880
- if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
1881
- attributes.forEach((attribute) => {
1882
- this.log.info(
1883
- `- 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}`,
1884
- );
1885
- });
1886
-
1887
- this.log.info('Subscribing to all attributes and events');
1888
- await node.subscribeAllAttributesAndEvents({
1889
- ignoreInitialTriggers: false,
1890
- attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
1891
- this.log.info(
1892
- `***${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}`,
1893
- ),
1894
- eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
1895
- this.log.info(
1896
- `***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
1897
- );
1898
- },
1899
- });
1900
- this.log.info('Subscribed to all attributes and events');
1901
- }
1902
- */
1903
1358
  }
1904
- /** */
1905
- /** Matter.js methods */
1906
- /** */
1907
- /**
1908
- * Starts the matter storage with name Matterbridge, create the matterbridge context and performs a backup.
1909
- *
1910
- * @returns {Promise<void>} - A promise that resolves when the storage is started.
1911
- */
1912
1359
  async startMatterStorage() {
1913
- // Setup Matter storage
1914
1360
  this.log.info(`Starting matter node storage...`);
1915
1361
  this.matterStorageService = this.environment.get(StorageService);
1916
1362
  this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
@@ -1919,17 +1365,8 @@ export class Matterbridge extends EventEmitter {
1919
1365
  this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName);
1920
1366
  this.matterbridgeInformation.matterbridgeSerialNumber = await this.matterbridgeContext.get('serialNumber', '');
1921
1367
  this.log.info('Matter node storage started');
1922
- // Backup matter storage since it is created/opened correctly
1923
1368
  await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
1924
1369
  }
1925
- /**
1926
- * Makes a backup copy of the specified matter storage directory.
1927
- *
1928
- * @param {string} storageName - The name of the storage directory to be backed up.
1929
- * @param {string} backupName - The name of the backup directory to be created.
1930
- * @private
1931
- * @returns {Promise<void>} A promise that resolves when the has been done.
1932
- */
1933
1370
  async backupMatterStorage(storageName, backupName) {
1934
1371
  this.log.info('Creating matter node storage backup...');
1935
1372
  try {
@@ -1940,11 +1377,6 @@ export class Matterbridge extends EventEmitter {
1940
1377
  this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
1941
1378
  }
1942
1379
  }
1943
- /**
1944
- * Stops the matter storage.
1945
- *
1946
- * @returns {Promise<void>} A promise that resolves when the storage is stopped.
1947
- */
1948
1380
  async stopMatterStorage() {
1949
1381
  this.log.info('Closing matter node storage...');
1950
1382
  await this.matterStorageManager?.close();
@@ -1953,19 +1385,6 @@ export class Matterbridge extends EventEmitter {
1953
1385
  this.matterbridgeContext = undefined;
1954
1386
  this.log.info('Matter node storage closed');
1955
1387
  }
1956
- /**
1957
- * Creates a server node storage context.
1958
- *
1959
- * @param {string} pluginName - The name of the plugin.
1960
- * @param {string} deviceName - The name of the device.
1961
- * @param {DeviceTypeId} deviceType - The device type of the device.
1962
- * @param {number} vendorId - The vendor ID.
1963
- * @param {string} vendorName - The vendor name.
1964
- * @param {number} productId - The product ID.
1965
- * @param {string} productName - The product name.
1966
- * @param {string} [serialNumber] - The serial number of the device (optional).
1967
- * @returns {Promise<StorageContext>} The storage context for the commissioning server.
1968
- */
1969
1388
  async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
1970
1389
  const { randomBytes } = await import('node:crypto');
1971
1390
  if (!this.matterStorageService)
@@ -1999,15 +1418,6 @@ export class Matterbridge extends EventEmitter {
1999
1418
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
2000
1419
  return storageContext;
2001
1420
  }
2002
- /**
2003
- * Creates a server node.
2004
- *
2005
- * @param {StorageContext} storageContext - The storage context for the server node.
2006
- * @param {number} [port] - The port number for the server node. Defaults to 5540.
2007
- * @param {number} [passcode] - The passcode for the server node. Defaults to 20242025.
2008
- * @param {number} [discriminator] - The discriminator for the server node. Defaults to 3850.
2009
- * @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
2010
- */
2011
1421
  async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
2012
1422
  const storeId = await storageContext.get('storeId');
2013
1423
  this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
@@ -2017,37 +1427,24 @@ export class Matterbridge extends EventEmitter {
2017
1427
  this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
2018
1428
  this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
2019
1429
  this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
2020
- /**
2021
- * Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
2022
- */
2023
1430
  const serverNode = await ServerNode.create({
2024
- // Required: Give the Node a unique ID which is used to store the state of this node
2025
1431
  id: storeId,
2026
- // Provide Network relevant configuration like the port
2027
- // Optional when operating only one device on a host, Default port is 5540
2028
1432
  network: {
2029
1433
  listeningAddressIpv4: this.ipv4address,
2030
1434
  listeningAddressIpv6: this.ipv6address,
2031
1435
  port,
2032
1436
  },
2033
- // Provide the certificate for the device
2034
1437
  operationalCredentials: {
2035
1438
  certification: this.certification,
2036
1439
  },
2037
- // Provide Commissioning relevant settings
2038
- // Optional for development/testing purposes
2039
1440
  commissioning: {
2040
1441
  passcode,
2041
1442
  discriminator,
2042
1443
  },
2043
- // Provide Node announcement settings
2044
- // Optional: If Ommitted some development defaults are used
2045
1444
  productDescription: {
2046
1445
  name: await storageContext.get('deviceName'),
2047
1446
  deviceType: DeviceTypeId(await storageContext.get('deviceType')),
2048
1447
  },
2049
- // Provide defaults for the BasicInformation cluster on the Root endpoint
2050
- // Optional: If Omitted some development defaults are used
2051
1448
  basicInformation: {
2052
1449
  vendorId: VendorId(await storageContext.get('vendorId')),
2053
1450
  vendorName: await storageContext.get('vendorName'),
@@ -2064,20 +1461,14 @@ export class Matterbridge extends EventEmitter {
2064
1461
  reachable: true,
2065
1462
  },
2066
1463
  });
2067
- /**
2068
- * This event is triggered when the device is initially commissioned successfully.
2069
- * This means: It is added to the first fabric.
2070
- */
2071
1464
  serverNode.lifecycle.commissioned.on(() => {
2072
1465
  this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
2073
1466
  clearTimeout(this.endAdvertiseTimeout);
2074
1467
  });
2075
- /** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
2076
1468
  serverNode.lifecycle.decommissioned.on(() => {
2077
1469
  this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`);
2078
1470
  clearTimeout(this.endAdvertiseTimeout);
2079
1471
  });
2080
- /** This event is triggered when the device went online. This means that it is discoverable in the network. */
2081
1472
  serverNode.lifecycle.online.on(async () => {
2082
1473
  this.log.notice(`Server node for ${storeId} is online`);
2083
1474
  if (!serverNode.lifecycle.isCommissioned) {
@@ -2085,7 +1476,6 @@ export class Matterbridge extends EventEmitter {
2085
1476
  const { qrPairingCode, manualPairingCode } = serverNode.state.commissioning.pairingCodes;
2086
1477
  this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
2087
1478
  this.log.notice(`Manual pairing code: ${manualPairingCode}`);
2088
- // Set a timeout to show that advertising stops after 15 minutes if not commissioned
2089
1479
  this.startEndAdvertiseTimer(serverNode);
2090
1480
  }
2091
1481
  else {
@@ -2096,19 +1486,14 @@ export class Matterbridge extends EventEmitter {
2096
1486
  this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
2097
1487
  this.emit('online', storeId);
2098
1488
  });
2099
- /** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
2100
1489
  serverNode.lifecycle.offline.on(() => {
2101
1490
  this.log.notice(`Server node for ${storeId} is offline`);
2102
- this.matterbridgeInformation.matterbridgeEndAdvertise = true; // Set the end advertise flag to true, so the frontend won't show the QR code anymore
1491
+ this.matterbridgeInformation.matterbridgeEndAdvertise = true;
2103
1492
  this.frontend.wssSendRefreshRequired('plugins');
2104
1493
  this.frontend.wssSendRefreshRequired('settings');
2105
1494
  this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
2106
1495
  this.emit('offline', storeId);
2107
1496
  });
2108
- /**
2109
- * This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
2110
- * information is needed.
2111
- */
2112
1497
  serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
2113
1498
  let action = '';
2114
1499
  switch (fabricAction) {
@@ -2125,22 +1510,14 @@ export class Matterbridge extends EventEmitter {
2125
1510
  this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
2126
1511
  this.frontend.wssSendRefreshRequired('fabrics');
2127
1512
  });
2128
- /**
2129
- * This event is triggered when an operative new session was opened by a Controller.
2130
- * It is not triggered for the initial commissioning process, just afterwards for real connections.
2131
- */
2132
1513
  serverNode.events.sessions.opened.on((session) => {
2133
1514
  this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
2134
1515
  this.frontend.wssSendRefreshRequired('sessions');
2135
1516
  });
2136
- /**
2137
- * This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
2138
- */
2139
1517
  serverNode.events.sessions.closed.on((session) => {
2140
1518
  this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
2141
1519
  this.frontend.wssSendRefreshRequired('sessions');
2142
1520
  });
2143
- /** This event is triggered when a subscription gets added or removed on an operative session. */
2144
1521
  serverNode.events.sessions.subscriptionsChanged.on((session) => {
2145
1522
  this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
2146
1523
  this.frontend.wssSendRefreshRequired('sessions');
@@ -2148,11 +1525,6 @@ export class Matterbridge extends EventEmitter {
2148
1525
  this.log.info(`Created server node for ${storeId}`);
2149
1526
  return serverNode;
2150
1527
  }
2151
- /**
2152
- * Starts the 15 minutes timer to advice that advertising for the specified server node is ended.
2153
- *
2154
- * @param {ServerNode} [matterServerNode] - The server node to start.
2155
- */
2156
1528
  startEndAdvertiseTimer(matterServerNode) {
2157
1529
  if (this.endAdvertiseTimeout) {
2158
1530
  this.log.debug(`Clear ${matterServerNode.id} server node end advertise timer`);
@@ -2171,25 +1543,12 @@ export class Matterbridge extends EventEmitter {
2171
1543
  this.log.notice(`Advertising on server node for ${matterServerNode.id} stopped. Restart to commission.`);
2172
1544
  }, 15 * 60 * 1000).unref();
2173
1545
  }
2174
- /**
2175
- * Starts the specified server node.
2176
- *
2177
- * @param {ServerNode} [matterServerNode] - The server node to start.
2178
- * @returns {Promise<void>} A promise that resolves when the server node has started.
2179
- */
2180
1546
  async startServerNode(matterServerNode) {
2181
1547
  if (!matterServerNode)
2182
1548
  return;
2183
1549
  this.log.notice(`Starting ${matterServerNode.id} server node`);
2184
1550
  await matterServerNode.start();
2185
1551
  }
2186
- /**
2187
- * Stops the specified server node.
2188
- *
2189
- * @param {ServerNode} matterServerNode - The server node to stop.
2190
- * @param {number} [timeout] - The timeout in milliseconds for stopping the server node. Defaults to 30 seconds.
2191
- * @returns {Promise<void>} A promise that resolves when the server node has stopped.
2192
- */
2193
1552
  async stopServerNode(matterServerNode, timeout = 30000) {
2194
1553
  if (!matterServerNode)
2195
1554
  return;
@@ -2202,12 +1561,6 @@ export class Matterbridge extends EventEmitter {
2202
1561
  this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
2203
1562
  }
2204
1563
  }
2205
- /**
2206
- * Advertises the specified server node.
2207
- *
2208
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2209
- * @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.
2210
- */
2211
1564
  async advertiseServerNode(matterServerNode) {
2212
1565
  if (matterServerNode) {
2213
1566
  await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
@@ -2216,39 +1569,19 @@ export class Matterbridge extends EventEmitter {
2216
1569
  return { qrPairingCode, manualPairingCode };
2217
1570
  }
2218
1571
  }
2219
- /**
2220
- * Stop advertise the specified server node.
2221
- *
2222
- * @param {ServerNode} [matterServerNode] - The server node to advertise.
2223
- * @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
2224
- */
2225
1572
  async stopAdvertiseServerNode(matterServerNode) {
2226
1573
  if (matterServerNode && matterServerNode.lifecycle.isOnline) {
2227
1574
  await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
2228
1575
  this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
2229
1576
  }
2230
1577
  }
2231
- /**
2232
- * Creates an aggregator node with the specified storage context.
2233
- *
2234
- * @param {StorageContext} storageContext - The storage context for the aggregator node.
2235
- * @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
2236
- */
2237
1578
  async createAggregatorNode(storageContext) {
2238
1579
  this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
2239
1580
  const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
2240
1581
  this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
2241
1582
  return aggregatorNode;
2242
1583
  }
2243
- /**
2244
- * Adds a MatterbridgeEndpoint to the specified plugin.
2245
- *
2246
- * @param {string} pluginName - The name of the plugin.
2247
- * @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
2248
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
2249
- */
2250
1584
  async addBridgedEndpoint(pluginName, device) {
2251
- // Check if the plugin is registered
2252
1585
  const plugin = this.plugins.get(pluginName);
2253
1586
  if (!plugin) {
2254
1587
  this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
@@ -2268,7 +1601,6 @@ export class Matterbridge extends EventEmitter {
2268
1601
  }
2269
1602
  else if (this.bridgeMode === 'bridge') {
2270
1603
  if (device.mode === 'matter') {
2271
- // Register and add the device to the matterbridge server node
2272
1604
  this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge server node...`);
2273
1605
  if (!this.serverNode) {
2274
1606
  this.log.error('Server node not found for Matterbridge');
@@ -2285,7 +1617,6 @@ export class Matterbridge extends EventEmitter {
2285
1617
  }
2286
1618
  }
2287
1619
  else {
2288
- // Register and add the device to the matterbridge aggregator node
2289
1620
  this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
2290
1621
  if (!this.aggregatorNode) {
2291
1622
  this.log.error('Aggregator node not found for Matterbridge');
@@ -2303,7 +1634,6 @@ export class Matterbridge extends EventEmitter {
2303
1634
  }
2304
1635
  }
2305
1636
  else if (this.bridgeMode === 'childbridge') {
2306
- // Register and add the device to the plugin server node
2307
1637
  if (plugin.type === 'AccessoryPlatform') {
2308
1638
  try {
2309
1639
  this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
@@ -2327,12 +1657,10 @@ export class Matterbridge extends EventEmitter {
2327
1657
  return;
2328
1658
  }
2329
1659
  }
2330
- // Register and add the device to the plugin aggregator node
2331
1660
  if (plugin.type === 'DynamicPlatform') {
2332
1661
  try {
2333
1662
  this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
2334
1663
  await this.createDynamicPlugin(plugin);
2335
- // Fast plugins can add another device before the server node is ready, so we wait for the server node to be ready
2336
1664
  await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
2337
1665
  if (!plugin.aggregatorNode) {
2338
1666
  this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
@@ -2355,28 +1683,17 @@ export class Matterbridge extends EventEmitter {
2355
1683
  plugin.registeredDevices++;
2356
1684
  if (plugin.addedDevices !== undefined)
2357
1685
  plugin.addedDevices++;
2358
- // Add the device to the DeviceManager
2359
1686
  this.devices.set(device);
2360
- // Subscribe to the reachable$Changed event
2361
1687
  await this.subscribeAttributeChanged(plugin, device);
2362
1688
  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}`);
2363
1689
  }
2364
- /**
2365
- * Removes a MatterbridgeEndpoint from the specified plugin.
2366
- *
2367
- * @param {string} pluginName - The name of the plugin.
2368
- * @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
2369
- * @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
2370
- */
2371
1690
  async removeBridgedEndpoint(pluginName, device) {
2372
1691
  this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
2373
- // Check if the plugin is registered
2374
1692
  const plugin = this.plugins.get(pluginName);
2375
1693
  if (!plugin) {
2376
1694
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
2377
1695
  return;
2378
1696
  }
2379
- // Register and add the device to the matterbridge aggregator node
2380
1697
  if (this.bridgeMode === 'bridge') {
2381
1698
  if (!this.aggregatorNode) {
2382
1699
  this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
@@ -2391,7 +1708,6 @@ export class Matterbridge extends EventEmitter {
2391
1708
  }
2392
1709
  else if (this.bridgeMode === 'childbridge') {
2393
1710
  if (plugin.type === 'AccessoryPlatform') {
2394
- // Nothing to do here since the server node has no aggregator node but only the device itself
2395
1711
  }
2396
1712
  else if (plugin.type === 'DynamicPlatform') {
2397
1713
  if (!plugin.aggregatorNode) {
@@ -2406,21 +1722,8 @@ export class Matterbridge extends EventEmitter {
2406
1722
  if (plugin.addedDevices !== undefined)
2407
1723
  plugin.addedDevices--;
2408
1724
  }
2409
- // Remove the device from the DeviceManager
2410
1725
  this.devices.remove(device);
2411
1726
  }
2412
- /**
2413
- * Removes all bridged endpoints from the specified plugin.
2414
- *
2415
- * @param {string} pluginName - The name of the plugin.
2416
- * @param {number} [delay] - The delay in milliseconds between removing each bridged endpoint (default: 0).
2417
- * @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
2418
- *
2419
- * @remarks
2420
- * This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
2421
- * It also applies a delay between each removal if specified.
2422
- * The delay is useful to allow the controllers to receive a single subscription for each device removed.
2423
- */
2424
1727
  async removeAllBridgedEndpoints(pluginName, delay = 0) {
2425
1728
  this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
2426
1729
  for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
@@ -2431,15 +1734,6 @@ export class Matterbridge extends EventEmitter {
2431
1734
  if (delay > 0)
2432
1735
  await new Promise((resolve) => setTimeout(resolve, 2000));
2433
1736
  }
2434
- /**
2435
- * Subscribes to the attribute change event for the given device and plugin.
2436
- * Specifically, it listens for changes in the 'reachable' attribute of the
2437
- * BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
2438
- *
2439
- * @param {RegisteredPlugin} plugin - The plugin associated with the device.
2440
- * @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
2441
- * @returns {Promise<void>} A promise that resolves when the subscription is set up.
2442
- */
2443
1737
  async subscribeAttributeChanged(plugin, device) {
2444
1738
  this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
2445
1739
  if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
@@ -2455,12 +1749,6 @@ export class Matterbridge extends EventEmitter {
2455
1749
  });
2456
1750
  }
2457
1751
  }
2458
- /**
2459
- * Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
2460
- *
2461
- * @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
2462
- * @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
2463
- */
2464
1752
  sanitizeFabricInformations(fabricInfo) {
2465
1753
  return fabricInfo.map((info) => {
2466
1754
  return {
@@ -2474,12 +1762,6 @@ export class Matterbridge extends EventEmitter {
2474
1762
  };
2475
1763
  });
2476
1764
  }
2477
- /**
2478
- * Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
2479
- *
2480
- * @param {SessionsBehavior.Session[]} sessions - The array of session information objects.
2481
- * @returns {SanitizedSession[]} An array of sanitized session information objects.
2482
- */
2483
1765
  sanitizeSessionInformation(sessions) {
2484
1766
  return sessions
2485
1767
  .filter((session) => session.isPeerActive)
@@ -2506,21 +1788,7 @@ export class Matterbridge extends EventEmitter {
2506
1788
  };
2507
1789
  });
2508
1790
  }
2509
- /**
2510
- * Sets the reachability of the specified aggregator node bridged devices and trigger.
2511
- *
2512
- * @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
2513
- * @param {boolean} reachable - A boolean indicating the reachability status to set.
2514
- */
2515
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
2516
1791
  async setAggregatorReachability(aggregatorNode, reachable) {
2517
- /*
2518
- for (const child of aggregatorNode.parts) {
2519
- this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
2520
- await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
2521
- child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
2522
- }
2523
- */
2524
1792
  }
2525
1793
  getVendorIdName = (vendorId) => {
2526
1794
  if (!vendorId)
@@ -2560,11 +1828,10 @@ export class Matterbridge extends EventEmitter {
2560
1828
  case 0x1488:
2561
1829
  vendorName = '(ShortcutLabsFlic)';
2562
1830
  break;
2563
- case 65521: // 0xFFF1
1831
+ case 65521:
2564
1832
  vendorName = '(MatterTest)';
2565
1833
  break;
2566
1834
  }
2567
1835
  return vendorName;
2568
1836
  };
2569
1837
  }
2570
- //# sourceMappingURL=matterbridge.js.map