matterbridge 1.1.4 → 1.1.6

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 (38) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +12 -9
  3. package/dist/AirQualityCluster.d.ts.map +1 -1
  4. package/dist/AirQualityCluster.js +1 -7
  5. package/dist/AirQualityCluster.js.map +1 -1
  6. package/dist/TvocCluster.d.ts.map +1 -1
  7. package/dist/TvocCluster.js +1 -2
  8. package/dist/TvocCluster.js.map +1 -1
  9. package/dist/matterbridge.d.ts +14 -5
  10. package/dist/matterbridge.d.ts.map +1 -1
  11. package/dist/matterbridge.js +408 -236
  12. package/dist/matterbridge.js.map +1 -1
  13. package/dist/matterbridgeAccessoryPlatform.d.ts +7 -13
  14. package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -1
  15. package/dist/matterbridgeAccessoryPlatform.js +9 -18
  16. package/dist/matterbridgeAccessoryPlatform.js.map +1 -1
  17. package/dist/matterbridgeController.d.ts +23 -1
  18. package/dist/matterbridgeController.d.ts.map +1 -1
  19. package/dist/matterbridgeController.js +364 -293
  20. package/dist/matterbridgeController.js.map +1 -1
  21. package/dist/matterbridgeDevice.d.ts +16 -0
  22. package/dist/matterbridgeDevice.d.ts.map +1 -1
  23. package/dist/matterbridgeDevice.js +34 -13
  24. package/dist/matterbridgeDevice.js.map +1 -1
  25. package/dist/matterbridgeDynamicPlatform.d.ts +7 -13
  26. package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -1
  27. package/dist/matterbridgeDynamicPlatform.js +9 -18
  28. package/dist/matterbridgeDynamicPlatform.js.map +1 -1
  29. package/frontend/build/asset-manifest.json +3 -3
  30. package/frontend/build/index.html +1 -1
  31. package/frontend/build/manifest.json +2 -2
  32. package/frontend/build/matterbridge 32x32.png +0 -0
  33. package/frontend/build/matterbridge 64x64.png +0 -0
  34. package/frontend/build/static/js/{main.b5a876cf.js → main.e5888ebb.js} +3 -3
  35. package/frontend/build/static/js/main.e5888ebb.js.map +1 -0
  36. package/package.json +71 -69
  37. package/frontend/build/static/js/main.b5a876cf.js.map +0 -1
  38. /package/frontend/build/static/js/{main.b5a876cf.js.LICENSE.txt → main.e5888ebb.js.LICENSE.txt} +0 -0
@@ -20,8 +20,9 @@
20
20
  * See the License for the specific language governing permissions and
21
21
  * limitations under the License. *
22
22
  */
23
+ import { MatterbridgeDevice } from './matterbridgeDevice.js';
23
24
  import { NodeStorageManager } from 'node-persist-manager';
24
- import { AnsiLogger, BRIGHT, GREEN, RESET, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, er, nf, rs, wr } from 'node-ansi-logger';
25
+ import { AnsiLogger, BRIGHT, RESET, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, er, nf, rs, wr } from 'node-ansi-logger';
25
26
  import { fileURLToPath, pathToFileURL } from 'url';
26
27
  import { promises as fs } from 'fs';
27
28
  import express from 'express';
@@ -38,6 +39,7 @@ import { requireMinNodeVersion, getParameter, getIntParameter, hasParameter } fr
38
39
  import { CryptoNode } from '@project-chip/matter-node.js/crypto';
39
40
  const plg = '\u001B[38;5;33m';
40
41
  const dev = '\u001B[38;5;79m';
42
+ const typ = '\u001B[38;5;207m';
41
43
  /**
42
44
  * Represents the Matterbridge application.
43
45
  */
@@ -55,16 +57,18 @@ export class Matterbridge {
55
57
  freeMemory: '',
56
58
  systemUptime: '',
57
59
  };
58
- homeDirectory;
59
- rootDirectory;
60
- matterbridgeDirectory;
60
+ homeDirectory = '';
61
+ rootDirectory = '';
62
+ matterbridgeDirectory = '';
63
+ matterbridgeVersion = '';
61
64
  bridgeMode = '';
65
+ debugEnabled = false;
62
66
  log;
63
67
  hasCleanupStarted = false;
64
68
  registeredPlugins = [];
65
69
  registeredDevices = [];
66
- nodeStorage = undefined;
67
- nodeContext = undefined;
70
+ nodeStorage;
71
+ nodeContext;
68
72
  app;
69
73
  storageManager;
70
74
  matterbridgeContext;
@@ -79,7 +83,7 @@ export class Matterbridge {
79
83
  }
80
84
  /**
81
85
  * Loads an instance of the Matterbridge class.
82
- * If an instance already exists, an error will be thrown.
86
+ * If an instance already exists, return that instance.
83
87
  * @returns The loaded instance of the Matterbridge class.
84
88
  */
85
89
  static async loadInstance(cli = false) {
@@ -118,6 +122,7 @@ export class Matterbridge {
118
122
  - bridge: start Matterbridge in bridge mode
119
123
  - childbridge: start Matterbridge in childbridge mode
120
124
  - frontend [port]: start the frontend on the given port (default 3000)
125
+ - debug: enable debug mode (default false)
121
126
  - list: list the registered plugins
122
127
  - add [plugin path]: register the plugin
123
128
  - remove [plugin path]: remove the plugin
@@ -126,29 +131,38 @@ export class Matterbridge {
126
131
  process.exit(0);
127
132
  }
128
133
  // set Matterbridge logger
129
- this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */ });
130
- this.log.info('Matterbridge is running...');
134
+ if (hasParameter('debug'))
135
+ this.debugEnabled = true;
136
+ this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logDebug: this.debugEnabled });
137
+ this.log.debug('Matterbridge is starting...');
131
138
  // log system info and create .matterbridge directory
132
139
  await this.logNodeAndSystemInfo();
140
+ this.log.info(
141
+ // eslint-disable-next-line max-len
142
+ `Matterbridge version ${this.matterbridgeVersion} mode ${hasParameter('bridge') ? 'bridge' : ''}${hasParameter('childbridge') ? 'childbridge' : ''} running on ${this.systemInformation.osType} ${this.systemInformation.osRelease} ${this.systemInformation.osPlatform} ${this.systemInformation.osArch}`);
133
143
  // check node version and throw error
134
144
  requireMinNodeVersion(18);
135
145
  // register SIGINT SIGTERM signal handlers
136
146
  this.registerSignalHandlers();
137
147
  // set matter.js logger level and format
138
- Logger.defaultLogLevel = Level.DEBUG;
148
+ Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
139
149
  Logger.format = Format.ANSI;
140
150
  // Initialize NodeStorage
141
151
  this.log.debug('Creating node storage manager');
142
- this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'storage') });
152
+ this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'storage'), logging: false });
143
153
  this.log.debug('Creating node storage context for matterbridge');
144
154
  this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
145
155
  this.registeredPlugins = await this.nodeContext.get('plugins', []);
146
- /*
147
- this.registeredPlugins.forEach(async (plugin) => {
148
- this.log.debug(`Creating node storage context for plugin ${plg}${plugin.name}${db}`);
149
- plugin.nodeContext = await this.nodeStorage?.createStorage(plugin.name);
150
- });
151
- */
156
+ for (const plugin of this.registeredPlugins) {
157
+ this.log.debug(`Creating node storage context for plugin ${plugin.name}`);
158
+ plugin.nodeContext = await this.nodeStorage?.createStorage(plugin.name);
159
+ await plugin.nodeContext?.set('name', plugin.name);
160
+ await plugin.nodeContext?.set('type', plugin.type);
161
+ await plugin.nodeContext?.set('path', plugin.path);
162
+ await plugin.nodeContext?.set('version', plugin.version);
163
+ await plugin.nodeContext?.set('description', plugin.description);
164
+ await plugin.nodeContext?.set('author', plugin.author);
165
+ }
152
166
  // Parse command line
153
167
  this.parseCommandLine();
154
168
  }
@@ -160,31 +174,36 @@ export class Matterbridge {
160
174
  async parseCommandLine() {
161
175
  if (hasParameter('list')) {
162
176
  this.log.info('Registered plugins:');
163
- this.registeredPlugins.forEach((plugin) => {
164
- this.log.info(`- ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" version: ${plugin.version}` +
165
- ` author: "${plugin.author}" type: ${GREEN}${plugin.type}${nf} ${YELLOW}${plugin.enabled ? 'enabled' : 'disabled'}${nf}`);
166
- // loaded: ${plugin.loaded} started: ${plugin.started} paired: ${plugin.paired} connected: ${plugin.connected}
177
+ this.registeredPlugins.forEach((plugin, index) => {
178
+ if (index === this.registeredPlugins.length - 1) {
179
+ this.log.info(`└─┬─ ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${YELLOW}${plugin.enabled ? 'enabled' : 'disabled'}${nf}`);
180
+ this.log.info(` └─ ${db}${plugin.path}${db}`);
181
+ }
182
+ else {
183
+ this.log.info(`├─┬─ ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${YELLOW}${plugin.enabled ? 'enabled' : 'disabled'}${nf}`);
184
+ this.log.info(`│ └─ ${db}${plugin.path}${db}`);
185
+ }
167
186
  });
168
187
  process.exit(0);
169
188
  }
170
189
  if (getParameter('add')) {
171
190
  this.log.debug(`Registering plugin ${getParameter('add')}`);
172
- await this.loadPlugin(getParameter('add'), 'add');
191
+ await this.executeCommandLine(getParameter('add'), 'add');
173
192
  process.exit(0);
174
193
  }
175
194
  if (getParameter('remove')) {
176
195
  this.log.debug(`Unregistering plugin ${getParameter('remove')}`);
177
- await this.loadPlugin(getParameter('remove'), 'remove');
196
+ await this.executeCommandLine(getParameter('remove'), 'remove');
178
197
  process.exit(0);
179
198
  }
180
199
  if (getParameter('enable')) {
181
200
  this.log.debug(`Enable plugin ${getParameter('enable')}`);
182
- await this.loadPlugin(getParameter('enable'), 'enable');
201
+ await this.executeCommandLine(getParameter('enable'), 'enable');
183
202
  process.exit(0);
184
203
  }
185
204
  if (getParameter('disable')) {
186
205
  this.log.debug(`Disable plugin ${getParameter('disable')}`);
187
- await this.loadPlugin(getParameter('disable'), 'disable');
206
+ await this.executeCommandLine(getParameter('disable'), 'disable');
188
207
  process.exit(0);
189
208
  }
190
209
  // Start the storage (we need it now for frontend and later for matterbridge)
@@ -193,128 +212,112 @@ export class Matterbridge {
193
212
  this.matterbridgeContext = this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
194
213
  // Initialize frontend
195
214
  await this.initializeFrontend(getIntParameter('frontend'));
196
- if (hasParameter('childbridge')) {
215
+ if (hasParameter('test')) {
197
216
  this.bridgeMode = 'childbridge';
198
- this.registeredPlugins.forEach(async (plugin) => {
199
- if (!plugin.enabled)
200
- return;
201
- this.log.info(`Loading registered plugin ${plg}${plugin.name}${nf} type ${GREEN}${plugin.type}${nf}`);
202
- await this.loadPlugin(plugin.path, 'load');
203
- });
204
- await this.startMatterBridge();
217
+ MatterbridgeDevice.bridgeMode = 'childbridge';
218
+ this.testStartMatterBridge(); // No await do it asyncronously
205
219
  }
206
220
  if (hasParameter('bridge')) {
207
221
  this.bridgeMode = 'bridge';
208
- this.registeredPlugins.forEach(async (plugin) => {
222
+ MatterbridgeDevice.bridgeMode = 'bridge';
223
+ for (const plugin of this.registeredPlugins) {
209
224
  if (!plugin.enabled)
210
- return;
211
- this.log.info(`Loading registered plugin ${plg}${plugin.name}${nf} type ${GREEN}${plugin.type}${nf}`);
212
- await this.loadPlugin(plugin.path, 'load');
213
- });
225
+ continue;
226
+ plugin.loaded = false;
227
+ plugin.started = false;
228
+ plugin.configured = false;
229
+ plugin.connected = undefined;
230
+ this.loadPlugin(plugin); // No await do it asyncronously
231
+ }
232
+ await this.startMatterBridge();
233
+ }
234
+ if (hasParameter('childbridge')) {
235
+ this.bridgeMode = 'childbridge';
236
+ MatterbridgeDevice.bridgeMode = 'childbridge';
237
+ for (const plugin of this.registeredPlugins) {
238
+ if (!plugin.enabled)
239
+ continue;
240
+ plugin.loaded = false;
241
+ plugin.started = false;
242
+ plugin.configured = false;
243
+ plugin.connected = false;
244
+ this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
245
+ }
214
246
  await this.startMatterBridge();
215
247
  }
216
248
  }
217
249
  /**
218
250
  * Loads a plugin from the specified package.json file path.
219
251
  * @param packageJsonPath - The path to the package.json file of the plugin.
220
- * @param mode - The mode of operation. Possible values are 'load', 'add', 'remove', 'enable', 'disable'.
252
+ * @param mode - The mode of operation. Possible values are 'add', 'remove', 'enable', 'disable'.
221
253
  * @returns A Promise that resolves when the plugin is loaded successfully, or rejects with an error if loading fails.
222
254
  */
223
- async loadPlugin(packageJsonPath, mode = 'load') {
255
+ async executeCommandLine(packageJsonPath, mode) {
224
256
  if (!packageJsonPath.endsWith('package.json'))
225
257
  packageJsonPath = path.join(packageJsonPath, 'package.json');
258
+ // Resolve the package.json of the plugin
226
259
  packageJsonPath = path.resolve(packageJsonPath);
227
260
  this.log.debug(`Loading plugin from ${plg}${packageJsonPath}${db}`);
228
261
  try {
229
262
  // Load the package.json of the plugin
230
263
  const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
231
- const plugin = this.registeredPlugins.find((plugin) => plugin.name === packageJson.name);
232
- if (plugin && plugin.platform) {
233
- this.log.error(`Plugin ${plg}${plugin.name}${er} already loaded`);
234
- }
235
- // Resolve the main module path relative to package.json
236
- const pluginPath = path.resolve(path.dirname(packageJsonPath), packageJson.main);
237
- // Convert the file path to a URL
238
- const pluginUrl = pathToFileURL(pluginPath);
239
- // Dynamically import the plugin
240
- this.log.debug(`Importing plugin ${plg}${plugin?.name}${db} from ${pluginUrl.href}`);
241
- const pluginInstance = await import(pluginUrl.href);
242
- this.log.debug(`Imported plugin ${plg}${plugin?.name}${db} from ${pluginUrl.href}`);
243
- // Call the default export function of the plugin, passing this MatterBridge instance
244
- if (pluginInstance.default) {
245
- const platform = pluginInstance.default(this, new AnsiLogger({ logName: packageJson.description, logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */ }));
246
- platform.name = packageJson.name;
247
- if (mode === 'load') {
248
- this.log.info(`Plugin ${plg}${plugin?.name}${nf} type ${GREEN}${platform.type}${nf} loaded (entrypoint ${UNDERLINE}${pluginPath}${UNDERLINEOFF})`);
249
- // Update plugin info
250
- if (plugin) {
251
- plugin.path = packageJsonPath;
252
- plugin.name = packageJson.name;
253
- plugin.description = packageJson.description;
254
- plugin.version = packageJson.version;
255
- plugin.author = packageJson.author;
256
- plugin.type = platform.type;
257
- plugin.loaded = true;
258
- plugin.platform = platform;
264
+ if (mode === 'add') {
265
+ if (!this.registeredPlugins.find((plugin) => plugin.name === packageJson.name)) {
266
+ const plugin = { path: packageJsonPath, type: '', name: packageJson.name, version: packageJson.version, description: packageJson.description, author: packageJson.author, enabled: true };
267
+ if (await this.loadPlugin(plugin)) {
268
+ this.registeredPlugins.push(plugin);
269
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
270
+ this.log.info(`Plugin ${plg}${packageJsonPath}${nf} type ${plugin.type} added to matterbridge`);
259
271
  }
260
272
  else {
261
- this.log.error(`Plugin ${plg}${packageJson.name}${er} not found`);
273
+ this.log.error(`Plugin ${plg}${packageJsonPath}${wr} not added to matterbridge error`);
262
274
  }
263
275
  }
264
- else if (mode === 'add') {
265
- if (!this.registeredPlugins.find((plugin) => plugin.name === packageJson.name)) {
266
- this.registeredPlugins.push({
267
- path: packageJsonPath,
268
- type: platform.type,
269
- name: packageJson.name,
270
- version: packageJson.version,
271
- description: packageJson.description,
272
- author: packageJson.author,
273
- enabled: true,
274
- });
275
- await this.nodeContext?.set('plugins', this.registeredPlugins);
276
- this.log.info(`Plugin ${plg}${packageJsonPath}${nf} type ${platform.type} added to matterbridge`);
277
- }
278
- else {
279
- this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} already added to matterbridge`);
280
- }
276
+ else {
277
+ this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} already added to matterbridge`);
281
278
  }
282
- else if (mode === 'remove') {
283
- if (this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name)) {
284
- this.registeredPlugins.splice(this.registeredPlugins.findIndex((registeredPlugin) => registeredPlugin.name === packageJson.name), 1);
285
- await this.nodeContext?.set('plugins', this.registeredPlugins);
286
- this.log.info(`Plugin ${plg}${packageJsonPath}${nf} removed from matterbridge`);
287
- }
288
- else {
289
- this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
290
- }
279
+ }
280
+ else if (mode === 'remove') {
281
+ if (this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name)) {
282
+ this.registeredPlugins.splice(this.registeredPlugins.findIndex((registeredPlugin) => registeredPlugin.name === packageJson.name), 1);
283
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
284
+ this.log.info(`Plugin ${plg}${packageJsonPath}${nf} removed from matterbridge`);
291
285
  }
292
- else if (mode === 'enable') {
293
- const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
294
- if (plugin) {
295
- plugin.enabled = true;
296
- await this.nodeContext?.set('plugins', this.registeredPlugins);
297
- this.log.info(`Plugin ${plg}${packageJsonPath}${nf} enabled`);
298
- }
299
- else {
300
- this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
301
- }
286
+ else {
287
+ this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
302
288
  }
303
- else if (mode === 'disable') {
304
- const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
305
- if (plugin) {
306
- plugin.enabled = false;
307
- await this.nodeContext?.set('plugins', this.registeredPlugins);
308
- this.log.info(`Plugin ${plg}${packageJsonPath}${nf} disabled`);
309
- }
310
- else {
311
- this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
312
- }
289
+ }
290
+ else if (mode === 'enable') {
291
+ const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
292
+ if (plugin) {
293
+ plugin.enabled = true;
294
+ plugin.loaded = undefined;
295
+ plugin.started = undefined;
296
+ plugin.configured = undefined;
297
+ plugin.connected = undefined;
298
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
299
+ this.log.info(`Plugin ${plg}${packageJsonPath}${nf} enabled`);
313
300
  }
314
301
  else {
315
- this.log.error(`Plugin at ${plg}${pluginPath}${er} does not provide a default export`);
302
+ this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
316
303
  }
317
304
  }
305
+ else if (mode === 'disable') {
306
+ const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
307
+ if (plugin) {
308
+ plugin.enabled = false;
309
+ plugin.loaded = undefined;
310
+ plugin.started = undefined;
311
+ plugin.configured = undefined;
312
+ plugin.connected = undefined;
313
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
314
+ this.log.info(`Plugin ${plg}${packageJsonPath}${nf} disabled`);
315
+ }
316
+ else {
317
+ this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
318
+ }
319
+ }
320
+ //}
318
321
  }
319
322
  catch (err) {
320
323
  this.log.error(`Failed to load plugin from ${plg}${packageJsonPath}${er}: ${err}`);
@@ -339,13 +342,12 @@ export class Matterbridge {
339
342
  async cleanup(message) {
340
343
  if (!this.hasCleanupStarted) {
341
344
  this.hasCleanupStarted = true;
342
- this.log.debug(message);
345
+ this.log.info(message);
343
346
  // Callint the shutdown functions with a reason
344
- this.registeredPlugins.forEach((plugin) => {
345
- if (plugin.platform) {
346
- plugin.platform.onShutdown('Matterbridge is closing: ' + message);
347
- }
348
- });
347
+ for (const plugin of this.registeredPlugins) {
348
+ if (plugin.platform)
349
+ await plugin.platform.onShutdown('Matterbridge is closing: ' + message);
350
+ }
349
351
  // Set reachability to false
350
352
  /*
351
353
  this.log.debug(`*Changing reachability to false for ${this.registeredDevices.length} devices (${this.bridgeMode} mode):`);
@@ -368,10 +370,18 @@ export class Matterbridge {
368
370
  await this.stopMatter();
369
371
  // Closing storage
370
372
  await this.stopStorage();
371
- //await this.context?.set<RegisteredDevice[]>('plugins', this.registeredDevices);
372
- this.log.debug('Cleanup completed.');
373
- process.exit(0);
374
- }, 2 * 1000);
373
+ // Serialize registeredDevices
374
+ const serializedRegisteredDevices = [];
375
+ this.registeredDevices.forEach((registeredDevice) => {
376
+ serializedRegisteredDevices.push(registeredDevice.device.serialize(registeredDevice.plugin));
377
+ });
378
+ //console.log('serializedRegisteredDevices:', serializedRegisteredDevices);
379
+ await this.nodeContext?.set('devices', serializedRegisteredDevices);
380
+ setTimeout(() => {
381
+ this.log.info('Cleanup completed.');
382
+ process.exit(0);
383
+ }, 2 * 1000);
384
+ }, 3 * 1000);
375
385
  }
376
386
  }
377
387
  /**
@@ -392,6 +402,7 @@ export class Matterbridge {
392
402
  * Adds a device to the Matterbridge.
393
403
  * @param pluginName - The name of the plugin.
394
404
  * @param device - The device to be added.
405
+ * @returns A Promise that resolves when the device is added successfully.
395
406
  */
396
407
  async addDevice(pluginName, device) {
397
408
  this.log.info(`Adding device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
@@ -403,20 +414,19 @@ export class Matterbridge {
403
414
  }
404
415
  // Add and register the device to the matterbridge in bridge mode
405
416
  if (this.bridgeMode === 'bridge') {
406
- const basic = device.getClusterServerById(BasicInformationCluster.id);
407
- if (!basic) {
408
- this.log.error(`addDevice error: cannot find the BasicInformationCluster device ${dev}${device.name}${nf} plugin ${plg}${pluginName}${nf}`);
409
- return;
410
- }
411
- device.createDefaultBridgedDeviceBasicInformationClusterServer(basic.getNodeLabelAttribute(), basic.getSerialNumberAttribute(), basic.getVendorIdAttribute(), basic.getVendorNameAttribute(), basic.getProductNameAttribute(), basic.getSoftwareVersionAttribute(), basic.getSoftwareVersionStringAttribute(), basic.getHardwareVersionAttribute(), basic.getHardwareVersionStringAttribute());
412
- //console.log(basic.getSoftwareVersionAttribute(), basic.getSoftwareVersionStringAttribute());
413
417
  this.matterAggregator.addBridgedDevice(device);
414
418
  this.registeredDevices.push({ plugin: pluginName, device, added: true });
415
- this.log.info(`Added and registered device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
419
+ if (plugin.registeredDevices !== undefined)
420
+ plugin.registeredDevices++;
421
+ if (plugin.addedDevices !== undefined)
422
+ plugin.addedDevices++;
423
+ this.log.info(`Added and registered device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
416
424
  }
417
425
  // Only register the device in childbridge mode
418
426
  if (this.bridgeMode === 'childbridge') {
419
427
  this.registeredDevices.push({ plugin: pluginName, device, added: false });
428
+ if (plugin.registeredDevices !== undefined)
429
+ plugin.registeredDevices++;
420
430
  this.log.info(`Registered device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
421
431
  }
422
432
  }
@@ -424,6 +434,7 @@ export class Matterbridge {
424
434
  * Adds a bridged device to the Matterbridge.
425
435
  * @param pluginName - The name of the plugin.
426
436
  * @param device - The bridged device to add.
437
+ * @returns {Promise<void>} - A promise that resolves when the storage process is started.
427
438
  */
428
439
  async addBridgedDevice(pluginName, device) {
429
440
  this.log.info(`Adding bridged device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
@@ -437,14 +448,18 @@ export class Matterbridge {
437
448
  if (this.bridgeMode === 'bridge') {
438
449
  this.matterAggregator.addBridgedDevice(device);
439
450
  this.registeredDevices.push({ plugin: pluginName, device, added: true });
440
- this.log.info(`Added and registered bridged device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
451
+ if (plugin.registeredDevices !== undefined)
452
+ plugin.registeredDevices++;
453
+ if (plugin.addedDevices !== undefined)
454
+ plugin.addedDevices++;
455
+ this.log.info(`Added and registered bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
441
456
  }
442
457
  // Only register the device in childbridge mode
443
458
  if (this.bridgeMode === 'childbridge') {
444
459
  this.registeredDevices.push({ plugin: pluginName, device, added: false });
460
+ if (plugin.registeredDevices !== undefined)
461
+ plugin.registeredDevices++;
445
462
  this.log.info(`Registered bridged device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
446
- //const basic = device.getClusterServerById(BridgedDeviceBasicInformationCluster.id);
447
- //console.log(JSON.stringify(basic, null, 2));
448
463
  }
449
464
  }
450
465
  /**
@@ -513,6 +528,146 @@ export class Matterbridge {
513
528
  await this.storageManager.close();
514
529
  this.log.debug('Storage closed');
515
530
  }
531
+ async testStartMatterBridge() {
532
+ for (const plugin of this.registeredPlugins) {
533
+ if (!plugin.enabled)
534
+ continue;
535
+ // No await do it asyncronously
536
+ this.loadPlugin(plugin)
537
+ .then(() => {
538
+ // No await do it asyncronously
539
+ this.startPlugin(plugin)
540
+ .then(() => { })
541
+ .catch((err) => {
542
+ this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
543
+ });
544
+ })
545
+ .catch((err) => {
546
+ this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
547
+ });
548
+ }
549
+ for (const plugin of this.registeredPlugins) {
550
+ if (!plugin.enabled)
551
+ continue;
552
+ // Start the interval to check if the plugin is loaded and started
553
+ let times = 0;
554
+ const interval = setInterval(() => {
555
+ times++;
556
+ this.log.info(`Waiting ${times} secs for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) and send devices ...`);
557
+ if (!plugin.loaded || !plugin.started)
558
+ return;
559
+ this.log.info(`Plugin ${plg}${plugin.name}${db} sent ${plugin.registeredDevices} devices`);
560
+ clearInterval(interval);
561
+ }, 1000);
562
+ }
563
+ }
564
+ async startPlugin(plugin, message, configure = false) {
565
+ if (!plugin.loaded || !plugin.platform) {
566
+ this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded or no platform`);
567
+ return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} not loaded or no platform`));
568
+ }
569
+ if (plugin.started) {
570
+ this.log.error(`Plugin ${plg}${plugin.name}${er} already started`);
571
+ return Promise.resolve();
572
+ }
573
+ this.log.info(`Starting plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
574
+ try {
575
+ plugin.platform
576
+ .onStart(message)
577
+ .then(() => {
578
+ plugin.started = true;
579
+ this.log.info(`Started plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
580
+ if (configure)
581
+ this.configurePlugin(plugin); // No await do it asyncronously
582
+ return Promise.resolve();
583
+ })
584
+ .catch((err) => {
585
+ this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
586
+ return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
587
+ });
588
+ }
589
+ catch (err) {
590
+ this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
591
+ return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
592
+ }
593
+ }
594
+ async configurePlugin(plugin) {
595
+ if (!plugin.loaded || !plugin.started || !plugin.platform) {
596
+ this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded or not started or not platform`);
597
+ return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} not loaded or not started or not platform`));
598
+ }
599
+ if (plugin.configured) {
600
+ this.log.error(`Plugin ${plg}${plugin.name}${er} already configured`);
601
+ return Promise.resolve();
602
+ }
603
+ this.log.info(`Configuring plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
604
+ try {
605
+ plugin.platform
606
+ .onConfigure()
607
+ .then(() => {
608
+ plugin.configured = true;
609
+ this.log.info(`Configured plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
610
+ return Promise.resolve();
611
+ })
612
+ .catch((err) => {
613
+ this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`);
614
+ return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
615
+ });
616
+ }
617
+ catch (err) {
618
+ this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`);
619
+ return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
620
+ }
621
+ }
622
+ async loadPlugin(plugin, start = false, message = '') {
623
+ if (!plugin.enabled) {
624
+ this.log.error(`Plugin ${plg}${plugin.name}${er} not enabled`);
625
+ return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} not enabled`));
626
+ }
627
+ if (plugin.platform) {
628
+ this.log.error(`Plugin ${plg}${plugin.name}${er} already loaded`);
629
+ return Promise.resolve(plugin.platform);
630
+ }
631
+ this.log.info(`Loading plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
632
+ try {
633
+ // Load the package.json of the plugin
634
+ const packageJson = JSON.parse(await fs.readFile(plugin.path, 'utf8'));
635
+ // Resolve the main module path relative to package.json
636
+ const pluginEntry = path.resolve(path.dirname(plugin.path), packageJson.main);
637
+ // Dynamically import the plugin
638
+ const pluginUrl = pathToFileURL(pluginEntry);
639
+ this.log.debug(`Importing plugin ${plg}${plugin.name}${db} from ${pluginUrl.href}`);
640
+ const pluginInstance = await import(pluginUrl.href);
641
+ this.log.debug(`Imported plugin ${plg}${plugin.name}${db} from ${pluginUrl.href}`);
642
+ // Call the default export function of the plugin, passing this MatterBridge instance
643
+ if (pluginInstance.default) {
644
+ const platform = pluginInstance.default(this, new AnsiLogger({ logName: plugin.description, logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logDebug: this.debugEnabled }));
645
+ platform.name = packageJson.name;
646
+ plugin.name = packageJson.name;
647
+ plugin.description = packageJson.description;
648
+ plugin.version = packageJson.version;
649
+ plugin.author = packageJson.author;
650
+ plugin.type = platform.type;
651
+ plugin.platform = platform;
652
+ plugin.loaded = true;
653
+ plugin.registeredDevices = 0;
654
+ plugin.addedDevices = 0;
655
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
656
+ this.log.info(`Loaded plugin ${plg}${plugin.name}${db} type ${typ}${platform.type}${db} (entrypoint ${UNDERLINE}${pluginEntry}${UNDERLINEOFF})`);
657
+ if (start)
658
+ this.startPlugin(plugin, message); // No await do it asyncronously
659
+ return Promise.resolve(platform);
660
+ }
661
+ else {
662
+ this.log.error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`);
663
+ return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`));
664
+ }
665
+ }
666
+ catch (err) {
667
+ this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
668
+ return Promise.reject(new Error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`));
669
+ }
670
+ }
516
671
  /**
517
672
  * Starts the Matterbridge based on the bridge mode.
518
673
  * If the bridge mode is 'bridge', it creates a commissioning server, matter aggregator,
@@ -527,7 +682,7 @@ export class Matterbridge {
527
682
  this.createMatterServer(this.storageManager);
528
683
  if (this.bridgeMode === 'bridge') {
529
684
  // Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
530
- // Plugins are started by callback when Matterbridge is commissioned and plugin.started is set to true
685
+ // Plugins are started and configured by callback when Matterbridge is commissioned and plugin.started is set to true
531
686
  this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
532
687
  this.matterbridgeContext = this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
533
688
  this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
@@ -540,53 +695,37 @@ export class Matterbridge {
540
695
  await this.matterServer.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
541
696
  this.log.debug('Starting matter server');
542
697
  await this.startMatterServer();
543
- this.registeredPlugins.forEach(async (plugin) => {
544
- if (plugin.platform)
545
- await plugin.platform.onMatterStarted();
546
- });
547
698
  this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, 'Matterbridge');
548
699
  }
549
700
  if (this.bridgeMode === 'childbridge') {
550
- // Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
551
- // Plugins are started here and plugin.started is set to true.
701
+ // Plugins are loaded and started by loadPlugin on startup and plugin.loaded is set to true and plugin.started is set to true
552
702
  // addDevice and addBridgedDeevice just register the devices that are added here to the plugin commissioning server for Accessory Platform
553
703
  // or to the plugin aggregator for Dynamic Platform after the commissioning is done
704
+ // Plugins are configured by callback when the plugin is commissioned
554
705
  this.registeredPlugins.forEach(async (plugin) => {
555
706
  if (!plugin.enabled)
556
707
  return;
557
- // Start the interval to check if the plugin is loaded
558
- const loadedInterval = setInterval(async () => {
559
- this.log.debug(`Waiting in load interval for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) and send devices ...`);
560
- if (!plugin.loaded)
561
- return;
562
- plugin.started = true;
563
- plugin.registeredDevices = 0;
564
- clearInterval(loadedInterval);
565
- }, 1000);
566
708
  // Start the interval to check if the plugins is started
567
- const startedInterval = setInterval(async () => {
568
- this.log.debug(`Waiting in started interval for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) and send devices ...`);
569
- if (!plugin.started)
709
+ // TODO set a counter or a timeout
710
+ this.log.info(`**Starting startMatterBridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
711
+ const startInterval = setInterval(async () => {
712
+ if (!plugin.loaded || !plugin.started) {
713
+ this.log.info(`***Returning in startMatterBridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
570
714
  return;
715
+ }
571
716
  if (plugin.type === 'AccessoryPlatform') {
572
- this.log.debug(`Starting accessory platform for plugin ${plg}${plugin.name}${db}`);
573
- if (plugin.platform)
574
- await plugin.platform.onStart('Matterbridge Accessory platform has started commissioning');
575
- else
576
- this.log.error(`Platform not found for plugin ${plg}${plugin.name}${er}`);
577
- this.registeredDevices.forEach(async (registeredDevice) => {
578
- if (registeredDevice.plugin === plugin.name) {
717
+ this.registeredDevices
718
+ .filter((registeredDevice) => registeredDevice.plugin === plugin.name)
719
+ .forEach((registeredDevice) => {
720
+ if (!plugin.storageContext)
579
721
  plugin.storageContext = this.importCommissioningServerContext(plugin.name, registeredDevice.device); // Generate serialNumber and uniqueId
722
+ if (!plugin.commissioningServer)
580
723
  plugin.commissioningServer = this.createCommisioningServer(plugin.storageContext, plugin.name);
581
- this.log.debug(`Adding device ${dev}${registeredDevice.device.name}${db} to commissioning server for plugin ${plg}${plugin.name}${db}`);
582
- plugin.commissioningServer.addDevice(registeredDevice.device);
583
- if (plugin.registeredDevices !== undefined)
584
- plugin.registeredDevices++;
585
- this.log.debug(`Adding commissioning server to matter server for plugin ${plg}${plugin.name}${db} `);
586
- await this.matterServer.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
587
- return;
588
- }
724
+ this.log.debug(`Adding device ${dev}${registeredDevice.device.name}${db} to commissioning server for plugin ${plg}${plugin.name}${db}`);
725
+ plugin.commissioningServer.addDevice(registeredDevice.device);
589
726
  });
727
+ this.log.debug(`Adding commissioning server to matter server for plugin ${plg}${plugin.name}${db}`);
728
+ await this.matterServer.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
590
729
  }
591
730
  if (plugin.type === 'DynamicPlatform') {
592
731
  plugin.storageContext = this.createCommissioningServerContext(
@@ -595,31 +734,29 @@ export class Matterbridge {
595
734
  plugin.commissioningServer = this.createCommisioningServer(plugin.storageContext, plugin.name);
596
735
  this.log.debug(`Creating aggregator for plugin ${plg}${plugin.name}${db}`);
597
736
  plugin.aggregator = this.createMatterAggregator(plugin.storageContext); // Generate serialNumber and uniqueId
598
- this.log.debug(`Starting dynamic platform for plugin ${plg}${plugin.name}${db}`);
599
- if (plugin.platform)
600
- await plugin.platform.onStart('Matterbridge Dynamic platform has started commissioning');
601
- else
602
- this.log.error(`Platform not found for plugin ${plg}${plugin.name}${er}`);
603
737
  this.log.debug(`Adding matter aggregator to commissioning server for plugin ${plg}${plugin.name}${db}`);
604
738
  plugin.commissioningServer.addDevice(plugin.aggregator);
605
739
  this.log.debug(`Adding commissioning server to matter server for plugin ${plg}${plugin.name}${db}`);
606
740
  await this.matterServer.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
607
741
  }
608
- clearInterval(startedInterval);
742
+ clearInterval(startInterval);
609
743
  }, 1000);
610
744
  });
611
745
  // Start the interval to check if all plugins are loaded and started and so start the matter server
746
+ // TODO set a counter or a timeout
747
+ this.log.info('**Starting start matter interval...');
612
748
  const startMatterInterval = setInterval(async () => {
613
749
  let allStarted = true;
614
750
  this.registeredPlugins.forEach((plugin) => {
615
751
  if (!plugin.enabled)
616
752
  return;
617
- this.log.debug(`Waiting in start matter server interval for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) and send devices ...`);
753
+ this.log.info(`**Waiting in start matter server interval for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) ...`);
618
754
  if (plugin.enabled && (!plugin.loaded || !plugin.started))
619
755
  allStarted = false;
620
756
  });
621
757
  if (!allStarted)
622
758
  return;
759
+ this.log.info('**Starting matter server in start matter server interval...');
623
760
  // Setting reachability to true
624
761
  this.registeredPlugins.forEach((plugin) => {
625
762
  if (!plugin.enabled)
@@ -635,20 +772,18 @@ export class Matterbridge {
635
772
  }
636
773
  });
637
774
  });
638
- this.log.debug('Starting matter server');
639
775
  await this.startMatterServer();
640
- this.registeredPlugins.forEach(async (plugin) => {
641
- if (plugin.platform)
642
- await plugin.platform.onMatterStarted();
776
+ for (const plugin of this.registeredPlugins) {
643
777
  this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.name);
644
- });
645
- Logger.defaultLogLevel = Level.DEBUG;
778
+ }
779
+ Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
646
780
  clearInterval(startMatterInterval);
647
781
  }, 1000);
648
782
  return;
649
783
  }
650
784
  }
651
785
  async startMatterServer() {
786
+ this.log.debug('Starting matter server');
652
787
  await this.matterServer.start();
653
788
  this.log.debug('Started matter server');
654
789
  }
@@ -715,25 +850,34 @@ export class Matterbridge {
715
850
  *
716
851
  * @param commissioningServer - The commissioning server to show the QR code for.
717
852
  * @param storageContext - The storage context to store the pairing codes.
718
- * @param name - The name of the commissioning server.
853
+ * @param pluginName - The name of the commissioning server.
719
854
  */
720
- showCommissioningQRCode(commissioningServer, storageContext, name) {
721
- if (!commissioningServer || !storageContext || !name)
855
+ async showCommissioningQRCode(commissioningServer, storageContext, pluginName) {
856
+ if (!commissioningServer || !storageContext || !pluginName)
722
857
  return;
723
858
  if (!commissioningServer.isCommissioned()) {
724
- this.log.info(`***The commissioning server for ${plg}${name}${nf} is not commissioned. Pair it scanning the QR code ...`);
859
+ this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code ...`);
725
860
  const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
726
861
  storageContext.set('qrPairingCode', qrPairingCode);
727
862
  storageContext.set('manualPairingCode', manualPairingCode);
728
863
  const QrCode = new QrCodeSchema();
729
- this.log.debug(`Pairing code\n\n${QrCode.encode(qrPairingCode)}\nManual pairing code: ${manualPairingCode}\n`);
864
+ this.log.info(`Pairing code:\n\n${QrCode.encode(qrPairingCode)}\nManual pairing code: ${manualPairingCode}\n`);
865
+ if (this.bridgeMode === 'childbridge') {
866
+ const plugin = this.findPlugin(pluginName);
867
+ if (plugin) {
868
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
869
+ plugin.paired = false;
870
+ }
871
+ }
730
872
  }
731
873
  else {
732
- this.log.info(`***The commissioning server for ${plg}${name}${nf} is already commissioned. Waiting for controllers to connect ...`);
874
+ this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is already commissioned. Waiting for controllers to connect ...`);
733
875
  if (this.bridgeMode === 'childbridge') {
734
- const plugin = this.findPlugin(name);
735
- if (plugin)
876
+ const plugin = this.findPlugin(pluginName);
877
+ if (plugin) {
878
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
736
879
  plugin.paired = true;
880
+ }
737
881
  }
738
882
  }
739
883
  }
@@ -794,14 +938,18 @@ export class Matterbridge {
794
938
  },
795
939
  activeSessionsChangedCallback: (fabricIndex) => {
796
940
  const info = commissioningServer.getActiveSessionInformation(fabricIndex);
797
- this.log.debug(`***Active sessions changed on fabric ${fabricIndex} for ${plg}${name}${nf}`, debugStringify(info));
798
- if (info && info[0]?.isPeerActive === true && info[0]?.secure === true && info[0]?.numberOfActiveSubscriptions >= 1) {
799
- this.log.info(`***Controller connected to ${plg}${name}${nf} ready to start...`);
941
+ let connected = false;
942
+ info.forEach((session) => {
943
+ this.log.debug(`***Active session changed on fabric ${fabricIndex} ${session.fabric?.rootVendorId}/${session.fabric?.label} for ${plg}${name}${nf}`, debugStringify(session));
944
+ if (session.isPeerActive === true && session.secure === true && session.numberOfActiveSubscriptions >= 1) {
945
+ this.log.info(`***Controller ${session.fabric?.rootVendorId}/${session.fabric?.label} connected to ${plg}${name}${nf}`);
946
+ connected = true;
947
+ }
948
+ });
949
+ if (connected) {
800
950
  if (this.bridgeMode === 'childbridge') {
801
951
  const plugin = this.findPlugin(name);
802
952
  if (plugin) {
803
- if (plugin.connected === true)
804
- return; // Only once cause the devices are already added to the plugins aggregator
805
953
  plugin.paired = true;
806
954
  plugin.connected = true;
807
955
  }
@@ -809,45 +957,40 @@ export class Matterbridge {
809
957
  setTimeout(() => {
810
958
  if (this.bridgeMode === 'bridge') {
811
959
  //Logger.defaultLogLevel = Level.INFO;
812
- this.registeredPlugins.forEach(async (plugin) => {
960
+ for (const plugin of this.registeredPlugins) {
813
961
  if (!plugin.enabled)
814
- return;
815
- if (plugin.platform && !plugin.started) {
816
- this.log.info(`***Starting plugin ${plg}${plugin.name}${nf}`);
817
- await plugin.platform.onStart('Matterbridge is commissioned and controllers are connected');
818
- plugin.started = true;
819
- this.log.info(`***Started plugin ${plg}${plugin.name}${nf}`);
820
- }
821
- else {
822
- this.log.error(`***Platform not found for plugin ${plg}${plugin.name}${er}`);
823
- }
824
- });
825
- Logger.defaultLogLevel = Level.DEBUG;
962
+ continue;
963
+ this.startPlugin(plugin, 'Matterbridge is commissioned and controllers are connected', true); // No await do it asyncronously
964
+ //this.configurePlugin(plugin); // No await do it asyncronously
965
+ }
966
+ Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
826
967
  }
827
968
  if (this.bridgeMode === 'childbridge') {
828
969
  //Logger.defaultLogLevel = Level.INFO;
829
970
  const plugin = this.findPlugin(name);
830
- if (!plugin || plugin.type === 'AccessoryPlatform')
831
- return;
832
- this.registeredDevices.forEach(async (registeredDevice) => {
833
- if (registeredDevice.plugin !== name)
834
- return;
835
- this.log.debug(`***Adding device ${registeredDevice.device.name} to aggregator for plugin ${plg}${plugin.name}${db}`);
836
- if (!plugin.aggregator) {
837
- this.log.error(`***Aggregator not found for plugin ${plg}${plugin.name}${er}`);
838
- return;
971
+ if (plugin && plugin.type === 'DynamicPlatform') {
972
+ for (const registeredDevice of this.registeredDevices) {
973
+ if (registeredDevice.plugin === name) {
974
+ this.log.info(`Adding bridged device ${dev}${registeredDevice.device.name}${nf} to aggregator for plugin ${plg}${plugin.name}${db}`);
975
+ if (!plugin.aggregator) {
976
+ this.log.error(`****Aggregator not found for plugin ${plg}${plugin.name}${er}`);
977
+ continue;
978
+ }
979
+ plugin.aggregator.addBridgedDevice(registeredDevice.device);
980
+ if (plugin.addedDevices !== undefined)
981
+ plugin.addedDevices++;
982
+ this.log.info(`Added bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${registeredDevice.device.name}${nf} for plugin ${plg}${plugin.name}${nf}`);
983
+ registeredDevice.added = true;
984
+ }
839
985
  }
840
- plugin.aggregator.addBridgedDevice(registeredDevice.device);
841
- if (plugin.registeredDevices !== undefined)
842
- plugin.registeredDevices++;
843
- registeredDevice.added = true;
844
- });
845
- Logger.defaultLogLevel = Level.DEBUG;
986
+ }
987
+ for (const plugin of this.registeredPlugins) {
988
+ if (plugin.name === name && plugin.platform) {
989
+ this.configurePlugin(plugin); // No await do it asyncronously
990
+ }
991
+ }
992
+ Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
846
993
  }
847
- this.registeredPlugins.forEach(async (plugin) => {
848
- if (plugin.name === name && plugin.platform)
849
- plugin.platform.onConfigure();
850
- });
851
994
  //logEndpoint(commissioningServer.getRootEndpoint());
852
995
  }, 2000);
853
996
  }
@@ -856,7 +999,8 @@ export class Matterbridge {
856
999
  const info = commissioningServer.getCommissionedFabricInformation(fabricIndex);
857
1000
  this.log.debug(`***Commissioning changed on fabric ${fabricIndex} for ${plg}${name}${nf}`, debugStringify(info));
858
1001
  if (info.length === 0) {
859
- this.log.warn(`***Commissioning removed from fabric ${fabricIndex} for ${plg}${name}${nf}`);
1002
+ this.log.warn(`***Commissioning removed from fabric ${fabricIndex} for ${plg}${name}${nf}. Resetting the commissioning server ...`);
1003
+ commissioningServer.factoryReset(); // TODO delete from storage also "matterbridge-eve-weather.EndpointStructure"
860
1004
  }
861
1005
  },
862
1006
  });
@@ -987,6 +1131,10 @@ export class Matterbridge {
987
1131
  await fs.mkdir(this.matterbridgeDirectory);
988
1132
  }
989
1133
  this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
1134
+ // Matterbridge version
1135
+ const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
1136
+ this.matterbridgeVersion = packageJson.version;
1137
+ this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
990
1138
  // Current working directory
991
1139
  const currentDir = process.cwd();
992
1140
  this.log.debug(`Current Working Directory: ${currentDir}`);
@@ -994,7 +1142,25 @@ export class Matterbridge {
994
1142
  const cmdArgs = process.argv.slice(2).join(' ');
995
1143
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
996
1144
  }
997
- getBaseRegisteredPlugin() {
1145
+ getBaseRegisteredPlugins() {
1146
+ const baseRegisteredPlugins = this.registeredPlugins.map((plugin) => ({
1147
+ path: plugin.path,
1148
+ type: plugin.type,
1149
+ name: plugin.name,
1150
+ version: plugin.version,
1151
+ description: plugin.description,
1152
+ author: plugin.author,
1153
+ enabled: plugin.enabled,
1154
+ loaded: plugin.loaded,
1155
+ started: plugin.started,
1156
+ configured: plugin.configured,
1157
+ paired: plugin.paired,
1158
+ connected: plugin.connected,
1159
+ registeredDevices: plugin.registeredDevices,
1160
+ }));
1161
+ return baseRegisteredPlugins;
1162
+ }
1163
+ getBaseRegisteredDevices() {
998
1164
  const baseRegisteredPlugins = this.registeredPlugins.map((plugin) => ({
999
1165
  path: plugin.path,
1000
1166
  type: plugin.type,
@@ -1044,8 +1210,7 @@ export class Matterbridge {
1044
1210
  // Endpoint to provide plugins
1045
1211
  this.app.get('/api/plugins', (req, res) => {
1046
1212
  this.log.debug('The frontend sent /api/plugins');
1047
- const baseRegisteredPlugins = this.getBaseRegisteredPlugin();
1048
- res.json(baseRegisteredPlugins);
1213
+ res.json(this.getBaseRegisteredPlugins());
1049
1214
  });
1050
1215
  // Endpoint to provide devices
1051
1216
  this.app.get('/api/devices', (req, res) => {
@@ -1077,15 +1242,15 @@ export class Matterbridge {
1077
1242
  // Endpoint to provide the cluster servers of the devices
1078
1243
  this.app.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
1079
1244
  const selectedPluginName = req.params.selectedPluginName;
1080
- const selectedDeviceEndpoint = req.params.selectedDeviceEndpoint;
1245
+ const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
1081
1246
  this.log.debug(`The frontend sent /api/devices_clusters plugin:${selectedPluginName} endpoint:${selectedDeviceEndpoint}`);
1082
- if (selectedPluginName === 'none' /* || selectedDeviceEndpoint === 'none'*/) {
1247
+ if (selectedPluginName === 'none') {
1083
1248
  res.json([]);
1084
1249
  return;
1085
1250
  }
1086
1251
  const data = [];
1087
1252
  this.registeredDevices.forEach((registeredDevice) => {
1088
- if (registeredDevice.plugin === selectedPluginName) {
1253
+ if (registeredDevice.plugin === selectedPluginName && registeredDevice.device.id === selectedDeviceEndpoint) {
1089
1254
  const clusterServers = registeredDevice.device.getAllClusterServers();
1090
1255
  clusterServers.forEach((clusterServer) => {
1091
1256
  Object.entries(clusterServer.attributes).forEach(([key, value]) => {
@@ -1189,6 +1354,13 @@ ws.onmessage = (event) => {
1189
1354
  console.log(`Received message => ${event.data}`);
1190
1355
  };
1191
1356
 
1357
+ */
1358
+ /*
1359
+ // In Matterbridge
1360
+ global.matterbridgeInstance = Matterbridge.loadInstance();
1361
+
1362
+ // In plugins
1363
+ const matterbridge = global.matterbridgeInstance;
1192
1364
  */
1193
1365
  /*
1194
1366
  npx create-react-app matterbridge-frontend