matterbridge 1.1.3 → 1.1.5

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 (36) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +33 -14
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +13 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.d.ts +0 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +2 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/matterbridge.d.ts +14 -6
  12. package/dist/matterbridge.d.ts.map +1 -1
  13. package/dist/matterbridge.js +351 -216
  14. package/dist/matterbridge.js.map +1 -1
  15. package/dist/matterbridgeAccessoryPlatform.d.ts +7 -13
  16. package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -1
  17. package/dist/matterbridgeAccessoryPlatform.js +9 -18
  18. package/dist/matterbridgeAccessoryPlatform.js.map +1 -1
  19. package/dist/matterbridgeDevice.d.ts +10 -0
  20. package/dist/matterbridgeDevice.d.ts.map +1 -1
  21. package/dist/matterbridgeDevice.js +11 -7
  22. package/dist/matterbridgeDevice.js.map +1 -1
  23. package/dist/matterbridgeDynamicPlatform.d.ts +7 -13
  24. package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -1
  25. package/dist/matterbridgeDynamicPlatform.js +9 -18
  26. package/dist/matterbridgeDynamicPlatform.js.map +1 -1
  27. package/frontend/build/asset-manifest.json +3 -3
  28. package/frontend/build/index.html +1 -1
  29. package/frontend/build/manifest.json +2 -2
  30. package/frontend/build/matterbridge 32x32.png +0 -0
  31. package/frontend/build/matterbridge 64x64.png +0 -0
  32. package/frontend/build/static/js/{main.b5a876cf.js → main.b6ca1c6b.js} +3 -3
  33. package/frontend/build/static/js/main.b6ca1c6b.js.map +1 -0
  34. package/package.json +4 -3
  35. package/frontend/build/static/js/main.b5a876cf.js.map +0 -1
  36. /package/frontend/build/static/js/{main.b5a876cf.js.LICENSE.txt → main.b6ca1c6b.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,17 +83,22 @@ 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
- * @throws Error if an instance of Matterbridge already exists.
85
88
  */
86
- static async loadInstance() {
89
+ static async loadInstance(cli = false) {
90
+ // eslint-disable-next-line no-console
91
+ console.error('loadInstance cli:', cli);
87
92
  if (!Matterbridge.instance) {
93
+ // eslint-disable-next-line no-console
94
+ console.error('Matterbridge instance does not exists');
88
95
  Matterbridge.instance = new Matterbridge();
89
- await Matterbridge.instance.initialize();
96
+ if (cli)
97
+ await Matterbridge.instance.initialize();
90
98
  }
91
99
  else {
92
- throw new Error('Matterbridge instance already exists');
100
+ // eslint-disable-next-line no-console
101
+ console.error('Matterbridge instance already exists');
93
102
  }
94
103
  return Matterbridge.instance;
95
104
  }
@@ -113,6 +122,7 @@ export class Matterbridge {
113
122
  - bridge: start Matterbridge in bridge mode
114
123
  - childbridge: start Matterbridge in childbridge mode
115
124
  - frontend [port]: start the frontend on the given port (default 3000)
125
+ - debug: enable debug mode (default false)
116
126
  - list: list the registered plugins
117
127
  - add [plugin path]: register the plugin
118
128
  - remove [plugin path]: remove the plugin
@@ -121,29 +131,38 @@ export class Matterbridge {
121
131
  process.exit(0);
122
132
  }
123
133
  // set Matterbridge logger
124
- this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */ });
125
- 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...');
126
138
  // log system info and create .matterbridge directory
127
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}`);
128
143
  // check node version and throw error
129
144
  requireMinNodeVersion(18);
130
145
  // register SIGINT SIGTERM signal handlers
131
146
  this.registerSignalHandlers();
132
147
  // set matter.js logger level and format
133
- Logger.defaultLogLevel = Level.DEBUG;
148
+ Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
134
149
  Logger.format = Format.ANSI;
135
150
  // Initialize NodeStorage
136
151
  this.log.debug('Creating node storage manager');
137
- this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'storage') });
152
+ this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'storage'), logging: false });
138
153
  this.log.debug('Creating node storage context for matterbridge');
139
154
  this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
140
155
  this.registeredPlugins = await this.nodeContext.get('plugins', []);
141
- /*
142
- this.registeredPlugins.forEach(async (plugin) => {
143
- this.log.debug(`Creating node storage context for plugin ${plg}${plugin.name}${db}`);
144
- plugin.nodeContext = await this.nodeStorage?.createStorage(plugin.name);
145
- });
146
- */
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
+ }
147
166
  // Parse command line
148
167
  this.parseCommandLine();
149
168
  }
@@ -156,30 +175,28 @@ export class Matterbridge {
156
175
  if (hasParameter('list')) {
157
176
  this.log.info('Registered plugins:');
158
177
  this.registeredPlugins.forEach((plugin) => {
159
- this.log.info(`- ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" version: ${plugin.version}` +
160
- ` author: "${plugin.author}" type: ${GREEN}${plugin.type}${nf} ${YELLOW}${plugin.enabled ? 'enabled' : 'disabled'}${nf}`);
161
- // loaded: ${plugin.loaded} started: ${plugin.started} paired: ${plugin.paired} connected: ${plugin.connected}
178
+ this.log.info(`- ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${YELLOW}${plugin.enabled ? 'enabled' : 'disabled'}${nf}`);
162
179
  });
163
180
  process.exit(0);
164
181
  }
165
182
  if (getParameter('add')) {
166
183
  this.log.debug(`Registering plugin ${getParameter('add')}`);
167
- await this.loadPlugin(getParameter('add'), 'add');
184
+ await this.executeCommandLine(getParameter('add'), 'add');
168
185
  process.exit(0);
169
186
  }
170
187
  if (getParameter('remove')) {
171
188
  this.log.debug(`Unregistering plugin ${getParameter('remove')}`);
172
- await this.loadPlugin(getParameter('remove'), 'remove');
189
+ await this.executeCommandLine(getParameter('remove'), 'remove');
173
190
  process.exit(0);
174
191
  }
175
192
  if (getParameter('enable')) {
176
193
  this.log.debug(`Enable plugin ${getParameter('enable')}`);
177
- await this.loadPlugin(getParameter('enable'), 'enable');
194
+ await this.executeCommandLine(getParameter('enable'), 'enable');
178
195
  process.exit(0);
179
196
  }
180
197
  if (getParameter('disable')) {
181
198
  this.log.debug(`Disable plugin ${getParameter('disable')}`);
182
- await this.loadPlugin(getParameter('disable'), 'disable');
199
+ await this.executeCommandLine(getParameter('disable'), 'disable');
183
200
  process.exit(0);
184
201
  }
185
202
  // Start the storage (we need it now for frontend and later for matterbridge)
@@ -188,128 +205,96 @@ export class Matterbridge {
188
205
  this.matterbridgeContext = this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
189
206
  // Initialize frontend
190
207
  await this.initializeFrontend(getIntParameter('frontend'));
191
- if (hasParameter('childbridge')) {
208
+ if (hasParameter('test')) {
192
209
  this.bridgeMode = 'childbridge';
193
- this.registeredPlugins.forEach(async (plugin) => {
194
- if (!plugin.enabled)
195
- return;
196
- this.log.info(`Loading registered plugin ${plg}${plugin.name}${nf} type ${GREEN}${plugin.type}${nf}`);
197
- await this.loadPlugin(plugin.path, 'load');
198
- });
199
- await this.startMatterBridge();
210
+ MatterbridgeDevice.bridgeMode = 'childbridge';
211
+ this.testStartMatterBridge(); // No await do it asyncronously
200
212
  }
201
213
  if (hasParameter('bridge')) {
202
214
  this.bridgeMode = 'bridge';
203
- this.registeredPlugins.forEach(async (plugin) => {
215
+ MatterbridgeDevice.bridgeMode = 'bridge';
216
+ for (const plugin of this.registeredPlugins) {
204
217
  if (!plugin.enabled)
205
- return;
206
- this.log.info(`Loading registered plugin ${plg}${plugin.name}${nf} type ${GREEN}${plugin.type}${nf}`);
207
- await this.loadPlugin(plugin.path, 'load');
208
- });
218
+ continue;
219
+ this.loadPlugin(plugin); // No await do it asyncronously
220
+ }
221
+ await this.startMatterBridge();
222
+ }
223
+ if (hasParameter('childbridge')) {
224
+ this.bridgeMode = 'childbridge';
225
+ MatterbridgeDevice.bridgeMode = 'childbridge';
226
+ for (const plugin of this.registeredPlugins) {
227
+ if (!plugin.enabled)
228
+ continue;
229
+ this.loadPlugin(plugin); // No await do it asyncronously
230
+ }
209
231
  await this.startMatterBridge();
210
232
  }
211
233
  }
212
234
  /**
213
235
  * Loads a plugin from the specified package.json file path.
214
236
  * @param packageJsonPath - The path to the package.json file of the plugin.
215
- * @param mode - The mode of operation. Possible values are 'load', 'add', 'remove', 'enable', 'disable'.
237
+ * @param mode - The mode of operation. Possible values are 'add', 'remove', 'enable', 'disable'.
216
238
  * @returns A Promise that resolves when the plugin is loaded successfully, or rejects with an error if loading fails.
217
239
  */
218
- async loadPlugin(packageJsonPath, mode = 'load') {
240
+ async executeCommandLine(packageJsonPath, mode) {
219
241
  if (!packageJsonPath.endsWith('package.json'))
220
242
  packageJsonPath = path.join(packageJsonPath, 'package.json');
243
+ // Resolve the package.json of the plugin
221
244
  packageJsonPath = path.resolve(packageJsonPath);
222
245
  this.log.debug(`Loading plugin from ${plg}${packageJsonPath}${db}`);
223
246
  try {
224
247
  // Load the package.json of the plugin
225
248
  const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
226
- const plugin = this.registeredPlugins.find((plugin) => plugin.name === packageJson.name);
227
- if (plugin && plugin.platform) {
228
- this.log.error(`Plugin ${plg}${plugin.name}${er} already loaded`);
229
- }
230
- // Resolve the main module path relative to package.json
231
- const pluginPath = path.resolve(path.dirname(packageJsonPath), packageJson.main);
232
- // Convert the file path to a URL
233
- const pluginUrl = pathToFileURL(pluginPath);
234
- // Dynamically import the plugin
235
- this.log.debug(`Importing plugin ${plg}${plugin?.name}${db} from ${pluginUrl.href}`);
236
- const pluginInstance = await import(pluginUrl.href);
237
- this.log.debug(`Imported plugin ${plg}${plugin?.name}${db} from ${pluginUrl.href}`);
238
- // Call the default export function of the plugin, passing this MatterBridge instance
239
- if (pluginInstance.default) {
240
- const platform = pluginInstance.default(this, new AnsiLogger({ logName: packageJson.description, logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */ }));
241
- platform.name = packageJson.name;
242
- if (mode === 'load') {
243
- this.log.info(`Plugin ${plg}${plugin?.name}${nf} type ${GREEN}${platform.type}${nf} loaded (entrypoint ${UNDERLINE}${pluginPath}${UNDERLINEOFF})`);
244
- // Update plugin info
245
- if (plugin) {
246
- plugin.path = packageJsonPath;
247
- plugin.name = packageJson.name;
248
- plugin.description = packageJson.description;
249
- plugin.version = packageJson.version;
250
- plugin.author = packageJson.author;
251
- plugin.type = platform.type;
252
- plugin.loaded = true;
253
- plugin.platform = platform;
249
+ if (mode === 'add') {
250
+ if (!this.registeredPlugins.find((plugin) => plugin.name === packageJson.name)) {
251
+ const plugin = { path: packageJsonPath, type: '', name: packageJson.name, version: packageJson.version, description: packageJson.description, author: packageJson.author, enabled: true };
252
+ if (await this.loadPlugin(plugin)) {
253
+ this.registeredPlugins.push(plugin);
254
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
255
+ this.log.info(`Plugin ${plg}${packageJsonPath}${nf} type ${plugin.type} added to matterbridge`);
254
256
  }
255
257
  else {
256
- this.log.error(`Plugin ${plg}${packageJson.name}${er} not found`);
258
+ this.log.error(`Plugin ${plg}${packageJsonPath}${wr} not added to matterbridge error`);
257
259
  }
258
260
  }
259
- else if (mode === 'add') {
260
- if (!this.registeredPlugins.find((plugin) => plugin.name === packageJson.name)) {
261
- this.registeredPlugins.push({
262
- path: packageJsonPath,
263
- type: platform.type,
264
- name: packageJson.name,
265
- version: packageJson.version,
266
- description: packageJson.description,
267
- author: packageJson.author,
268
- enabled: true,
269
- });
270
- await this.nodeContext?.set('plugins', this.registeredPlugins);
271
- this.log.info(`Plugin ${plg}${packageJsonPath}${nf} type ${platform.type} added to matterbridge`);
272
- }
273
- else {
274
- this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} already added to matterbridge`);
275
- }
261
+ else {
262
+ this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} already added to matterbridge`);
276
263
  }
277
- else if (mode === 'remove') {
278
- if (this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name)) {
279
- this.registeredPlugins.splice(this.registeredPlugins.findIndex((registeredPlugin) => registeredPlugin.name === packageJson.name), 1);
280
- await this.nodeContext?.set('plugins', this.registeredPlugins);
281
- this.log.info(`Plugin ${plg}${packageJsonPath}${nf} removed from matterbridge`);
282
- }
283
- else {
284
- this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
285
- }
264
+ }
265
+ else if (mode === 'remove') {
266
+ if (this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name)) {
267
+ this.registeredPlugins.splice(this.registeredPlugins.findIndex((registeredPlugin) => registeredPlugin.name === packageJson.name), 1);
268
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
269
+ this.log.info(`Plugin ${plg}${packageJsonPath}${nf} removed from matterbridge`);
286
270
  }
287
- else if (mode === 'enable') {
288
- const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
289
- if (plugin) {
290
- plugin.enabled = true;
291
- await this.nodeContext?.set('plugins', this.registeredPlugins);
292
- this.log.info(`Plugin ${plg}${packageJsonPath}${nf} enabled`);
293
- }
294
- else {
295
- this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
296
- }
271
+ else {
272
+ this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
297
273
  }
298
- else if (mode === 'disable') {
299
- const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
300
- if (plugin) {
301
- plugin.enabled = false;
302
- await this.nodeContext?.set('plugins', this.registeredPlugins);
303
- this.log.info(`Plugin ${plg}${packageJsonPath}${nf} disabled`);
304
- }
305
- else {
306
- this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
307
- }
274
+ }
275
+ else if (mode === 'enable') {
276
+ const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
277
+ if (plugin) {
278
+ plugin.enabled = true;
279
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
280
+ this.log.info(`Plugin ${plg}${packageJsonPath}${nf} enabled`);
281
+ }
282
+ else {
283
+ this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
284
+ }
285
+ }
286
+ else if (mode === 'disable') {
287
+ const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
288
+ if (plugin) {
289
+ plugin.enabled = false;
290
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
291
+ this.log.info(`Plugin ${plg}${packageJsonPath}${nf} disabled`);
308
292
  }
309
293
  else {
310
- this.log.error(`Plugin at ${plg}${pluginPath}${er} does not provide a default export`);
294
+ this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
311
295
  }
312
296
  }
297
+ //}
313
298
  }
314
299
  catch (err) {
315
300
  this.log.error(`Failed to load plugin from ${plg}${packageJsonPath}${er}: ${err}`);
@@ -334,13 +319,12 @@ export class Matterbridge {
334
319
  async cleanup(message) {
335
320
  if (!this.hasCleanupStarted) {
336
321
  this.hasCleanupStarted = true;
337
- this.log.debug(message);
322
+ this.log.info(message);
338
323
  // Callint the shutdown functions with a reason
339
- this.registeredPlugins.forEach((plugin) => {
340
- if (plugin.platform) {
341
- plugin.platform.onShutdown('Matterbridge is closing: ' + message);
342
- }
343
- });
324
+ for (const plugin of this.registeredPlugins) {
325
+ if (plugin.platform)
326
+ await plugin.platform.onShutdown('Matterbridge is closing: ' + message);
327
+ }
344
328
  // Set reachability to false
345
329
  /*
346
330
  this.log.debug(`*Changing reachability to false for ${this.registeredDevices.length} devices (${this.bridgeMode} mode):`);
@@ -363,10 +347,15 @@ export class Matterbridge {
363
347
  await this.stopMatter();
364
348
  // Closing storage
365
349
  await this.stopStorage();
366
- //await this.context?.set<RegisteredDevice[]>('plugins', this.registeredDevices);
367
- this.log.debug('Cleanup completed.');
350
+ const serializedRegisteredDevices = [];
351
+ this.registeredDevices.forEach((registeredDevice) => {
352
+ serializedRegisteredDevices.push(registeredDevice.device.serialize(registeredDevice.plugin));
353
+ });
354
+ //console.log('serializedRegisteredDevices:', serializedRegisteredDevices);
355
+ await this.nodeContext?.set('devices', serializedRegisteredDevices);
356
+ this.log.info('Cleanup completed.');
368
357
  process.exit(0);
369
- }, 2 * 1000);
358
+ }, 3 * 1000);
370
359
  }
371
360
  }
372
361
  /**
@@ -387,6 +376,7 @@ export class Matterbridge {
387
376
  * Adds a device to the Matterbridge.
388
377
  * @param pluginName - The name of the plugin.
389
378
  * @param device - The device to be added.
379
+ * @returns A Promise that resolves when the device is added successfully.
390
380
  */
391
381
  async addDevice(pluginName, device) {
392
382
  this.log.info(`Adding device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
@@ -398,20 +388,19 @@ export class Matterbridge {
398
388
  }
399
389
  // Add and register the device to the matterbridge in bridge mode
400
390
  if (this.bridgeMode === 'bridge') {
401
- const basic = device.getClusterServerById(BasicInformationCluster.id);
402
- if (!basic) {
403
- this.log.error(`addDevice error: cannot find the BasicInformationCluster device ${dev}${device.name}${nf} plugin ${plg}${pluginName}${nf}`);
404
- return;
405
- }
406
- device.createDefaultBridgedDeviceBasicInformationClusterServer(basic.getNodeLabelAttribute(), basic.getSerialNumberAttribute(), basic.getVendorIdAttribute(), basic.getVendorNameAttribute(), basic.getProductNameAttribute(), basic.getSoftwareVersionAttribute(), basic.getSoftwareVersionStringAttribute(), basic.getHardwareVersionAttribute(), basic.getHardwareVersionStringAttribute());
407
- //console.log(basic.getSoftwareVersionAttribute(), basic.getSoftwareVersionStringAttribute());
408
391
  this.matterAggregator.addBridgedDevice(device);
409
392
  this.registeredDevices.push({ plugin: pluginName, device, added: true });
410
- this.log.info(`Added and registered device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
393
+ if (plugin.registeredDevices !== undefined)
394
+ plugin.registeredDevices++;
395
+ if (plugin.addedDevices !== undefined)
396
+ plugin.addedDevices++;
397
+ this.log.info(`Added and registered device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
411
398
  }
412
399
  // Only register the device in childbridge mode
413
400
  if (this.bridgeMode === 'childbridge') {
414
401
  this.registeredDevices.push({ plugin: pluginName, device, added: false });
402
+ if (plugin.registeredDevices !== undefined)
403
+ plugin.registeredDevices++;
415
404
  this.log.info(`Registered device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
416
405
  }
417
406
  }
@@ -419,6 +408,7 @@ export class Matterbridge {
419
408
  * Adds a bridged device to the Matterbridge.
420
409
  * @param pluginName - The name of the plugin.
421
410
  * @param device - The bridged device to add.
411
+ * @returns {Promise<void>} - A promise that resolves when the storage process is started.
422
412
  */
423
413
  async addBridgedDevice(pluginName, device) {
424
414
  this.log.info(`Adding bridged device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
@@ -432,14 +422,18 @@ export class Matterbridge {
432
422
  if (this.bridgeMode === 'bridge') {
433
423
  this.matterAggregator.addBridgedDevice(device);
434
424
  this.registeredDevices.push({ plugin: pluginName, device, added: true });
435
- this.log.info(`Added and registered bridged device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
425
+ if (plugin.registeredDevices !== undefined)
426
+ plugin.registeredDevices++;
427
+ if (plugin.addedDevices !== undefined)
428
+ plugin.addedDevices++;
429
+ this.log.info(`Added and registered bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
436
430
  }
437
431
  // Only register the device in childbridge mode
438
432
  if (this.bridgeMode === 'childbridge') {
439
433
  this.registeredDevices.push({ plugin: pluginName, device, added: false });
434
+ if (plugin.registeredDevices !== undefined)
435
+ plugin.registeredDevices++;
440
436
  this.log.info(`Registered bridged device ${dev}${device.name}${nf} for plugin ${plg}${pluginName}${nf}`);
441
- //const basic = device.getClusterServerById(BridgedDeviceBasicInformationCluster.id);
442
- //console.log(JSON.stringify(basic, null, 2));
443
437
  }
444
438
  }
445
439
  /**
@@ -508,6 +502,144 @@ export class Matterbridge {
508
502
  await this.storageManager.close();
509
503
  this.log.debug('Storage closed');
510
504
  }
505
+ async testStartMatterBridge() {
506
+ for (const plugin of this.registeredPlugins) {
507
+ if (!plugin.enabled)
508
+ continue;
509
+ // No await do it asyncronously
510
+ this.loadPlugin(plugin)
511
+ .then(() => {
512
+ // No await do it asyncronously
513
+ this.startPlugin(plugin)
514
+ .then(() => { })
515
+ .catch((err) => {
516
+ this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
517
+ });
518
+ })
519
+ .catch((err) => {
520
+ this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
521
+ });
522
+ }
523
+ for (const plugin of this.registeredPlugins) {
524
+ if (!plugin.enabled)
525
+ continue;
526
+ // Start the interval to check if the plugin is loaded and started
527
+ let times = 0;
528
+ const interval = setInterval(() => {
529
+ times++;
530
+ this.log.info(`Waiting ${times} secs for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) and send devices ...`);
531
+ if (!plugin.loaded || !plugin.started)
532
+ return;
533
+ this.log.info(`Plugin ${plg}${plugin.name}${db} sent ${plugin.registeredDevices} devices`);
534
+ clearInterval(interval);
535
+ }, 1000);
536
+ }
537
+ }
538
+ async startPlugin(plugin, message, configure = false) {
539
+ if (!plugin.loaded || !plugin.platform) {
540
+ this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded or no platform`);
541
+ return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} not loaded or no platform`));
542
+ }
543
+ if (plugin.started) {
544
+ this.log.error(`Plugin ${plg}${plugin.name}${er} already started`);
545
+ return Promise.resolve();
546
+ }
547
+ this.log.info(`Starting plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
548
+ try {
549
+ plugin.platform
550
+ .onStart(message)
551
+ .then(() => {
552
+ plugin.started = true;
553
+ this.log.info(`Started plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
554
+ if (configure)
555
+ this.configurePlugin(plugin); // No await do it asyncronously
556
+ return Promise.resolve();
557
+ })
558
+ .catch((err) => {
559
+ this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
560
+ return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
561
+ });
562
+ }
563
+ catch (err) {
564
+ this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
565
+ return Promise.reject(new Error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`));
566
+ }
567
+ }
568
+ async configurePlugin(plugin) {
569
+ if (!plugin.loaded || !plugin.started || !plugin.platform) {
570
+ this.log.error(`Plugin ${plg}${plugin.name}${er} not loaded or not started or not platform`);
571
+ return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} not loaded or not started or not platform`));
572
+ }
573
+ if (plugin.configured) {
574
+ this.log.error(`Plugin ${plg}${plugin.name}${er} already configured`);
575
+ return Promise.resolve();
576
+ }
577
+ this.log.info(`Configuring plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
578
+ try {
579
+ plugin.platform
580
+ .onConfigure()
581
+ .then(() => {
582
+ plugin.configured = true;
583
+ this.log.info(`Configured plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
584
+ return Promise.resolve();
585
+ })
586
+ .catch((err) => {
587
+ this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`);
588
+ return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
589
+ });
590
+ }
591
+ catch (err) {
592
+ this.log.error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`);
593
+ return Promise.reject(new Error(`Failed to configure plugin ${plg}${plugin.name}${er}: ${err}`));
594
+ }
595
+ }
596
+ async loadPlugin(plugin) {
597
+ if (!plugin.enabled) {
598
+ this.log.error(`Plugin ${plg}${plugin.name}${er} not enabled`);
599
+ return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} not enabled`));
600
+ }
601
+ if (plugin.platform) {
602
+ this.log.error(`Plugin ${plg}${plugin.name}${er} already loaded`);
603
+ return Promise.resolve(plugin.platform);
604
+ }
605
+ this.log.info(`Loading plugin ${plg}${plugin.name}${db} type ${typ}${plugin.type}${db}`);
606
+ try {
607
+ // Load the package.json of the plugin
608
+ const packageJson = JSON.parse(await fs.readFile(plugin.path, 'utf8'));
609
+ // Resolve the main module path relative to package.json
610
+ const pluginEntry = path.resolve(path.dirname(plugin.path), packageJson.main);
611
+ // Dynamically import the plugin
612
+ const pluginUrl = pathToFileURL(pluginEntry);
613
+ this.log.debug(`Importing plugin ${plg}${plugin.name}${db} from ${pluginUrl.href}`);
614
+ const pluginInstance = await import(pluginUrl.href);
615
+ this.log.debug(`Imported plugin ${plg}${plugin.name}${db} from ${pluginUrl.href}`);
616
+ // Call the default export function of the plugin, passing this MatterBridge instance
617
+ if (pluginInstance.default) {
618
+ const platform = pluginInstance.default(this, new AnsiLogger({ logName: plugin.description, logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logDebug: this.debugEnabled }));
619
+ platform.name = packageJson.name;
620
+ plugin.name = packageJson.name;
621
+ plugin.description = packageJson.description;
622
+ plugin.version = packageJson.version;
623
+ plugin.author = packageJson.author;
624
+ plugin.type = platform.type;
625
+ plugin.platform = platform;
626
+ plugin.loaded = true;
627
+ plugin.registeredDevices = 0;
628
+ plugin.addedDevices = 0;
629
+ await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
630
+ this.log.info(`Loaded plugin ${plg}${plugin.name}${db} type ${typ}${platform.type}${db} (entrypoint ${UNDERLINE}${pluginEntry}${UNDERLINEOFF})`);
631
+ return Promise.resolve(platform);
632
+ }
633
+ else {
634
+ this.log.error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`);
635
+ return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`));
636
+ }
637
+ }
638
+ catch (err) {
639
+ this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
640
+ return Promise.reject(new Error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`));
641
+ }
642
+ }
511
643
  /**
512
644
  * Starts the Matterbridge based on the bridge mode.
513
645
  * If the bridge mode is 'bridge', it creates a commissioning server, matter aggregator,
@@ -522,7 +654,7 @@ export class Matterbridge {
522
654
  this.createMatterServer(this.storageManager);
523
655
  if (this.bridgeMode === 'bridge') {
524
656
  // Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
525
- // Plugins are started by callback when Matterbridge is commissioned and plugin.started is set to true
657
+ // Plugins are started and configured by callback when Matterbridge is commissioned and plugin.started is set to true
526
658
  this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
527
659
  this.matterbridgeContext = this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
528
660
  this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
@@ -535,10 +667,6 @@ export class Matterbridge {
535
667
  await this.matterServer.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
536
668
  this.log.debug('Starting matter server');
537
669
  await this.startMatterServer();
538
- this.registeredPlugins.forEach(async (plugin) => {
539
- if (plugin.platform)
540
- await plugin.platform.onMatterStarted();
541
- });
542
670
  this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, 'Matterbridge');
543
671
  }
544
672
  if (this.bridgeMode === 'childbridge') {
@@ -549,26 +677,13 @@ export class Matterbridge {
549
677
  this.registeredPlugins.forEach(async (plugin) => {
550
678
  if (!plugin.enabled)
551
679
  return;
552
- // Start the interval to check if the plugin is loaded
553
- const loadedInterval = setInterval(async () => {
554
- this.log.debug(`Waiting in load interval for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) and send devices ...`);
555
- if (!plugin.loaded)
556
- return;
557
- plugin.started = true;
558
- plugin.registeredDevices = 0;
559
- clearInterval(loadedInterval);
560
- }, 1000);
561
680
  // Start the interval to check if the plugins is started
562
- const startedInterval = setInterval(async () => {
563
- this.log.debug(`Waiting in started interval for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) and send devices ...`);
564
- if (!plugin.started)
681
+ const startInterval = setInterval(async () => {
682
+ this.log.debug(`Waiting in start interval for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) and send devices ...`);
683
+ if (!plugin.loaded)
565
684
  return;
566
685
  if (plugin.type === 'AccessoryPlatform') {
567
- this.log.debug(`Starting accessory platform for plugin ${plg}${plugin.name}${db}`);
568
- if (plugin.platform)
569
- await plugin.platform.onStart('Matterbridge Accessory platform has started commissioning');
570
- else
571
- this.log.error(`Platform not found for plugin ${plg}${plugin.name}${er}`);
686
+ await this.startPlugin(plugin, 'Matterbridge Accessory platform has started');
572
687
  this.registeredDevices.forEach(async (registeredDevice) => {
573
688
  if (registeredDevice.plugin === plugin.name) {
574
689
  plugin.storageContext = this.importCommissioningServerContext(plugin.name, registeredDevice.device); // Generate serialNumber and uniqueId
@@ -590,17 +705,13 @@ export class Matterbridge {
590
705
  plugin.commissioningServer = this.createCommisioningServer(plugin.storageContext, plugin.name);
591
706
  this.log.debug(`Creating aggregator for plugin ${plg}${plugin.name}${db}`);
592
707
  plugin.aggregator = this.createMatterAggregator(plugin.storageContext); // Generate serialNumber and uniqueId
593
- this.log.debug(`Starting dynamic platform for plugin ${plg}${plugin.name}${db}`);
594
- if (plugin.platform)
595
- await plugin.platform.onStart('Matterbridge Dynamic platform has started commissioning');
596
- else
597
- this.log.error(`Platform not found for plugin ${plg}${plugin.name}${er}`);
708
+ await this.startPlugin(plugin, 'Matterbridge Dynamic platform has started');
598
709
  this.log.debug(`Adding matter aggregator to commissioning server for plugin ${plg}${plugin.name}${db}`);
599
710
  plugin.commissioningServer.addDevice(plugin.aggregator);
600
711
  this.log.debug(`Adding commissioning server to matter server for plugin ${plg}${plugin.name}${db}`);
601
712
  await this.matterServer.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
602
713
  }
603
- clearInterval(startedInterval);
714
+ clearInterval(startInterval);
604
715
  }, 1000);
605
716
  });
606
717
  // Start the interval to check if all plugins are loaded and started and so start the matter server
@@ -630,20 +741,18 @@ export class Matterbridge {
630
741
  }
631
742
  });
632
743
  });
633
- this.log.debug('Starting matter server');
634
744
  await this.startMatterServer();
635
- this.registeredPlugins.forEach(async (plugin) => {
636
- if (plugin.platform)
637
- await plugin.platform.onMatterStarted();
745
+ for (const plugin of this.registeredPlugins) {
638
746
  this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.name);
639
- });
640
- Logger.defaultLogLevel = Level.DEBUG;
747
+ }
748
+ Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
641
749
  clearInterval(startMatterInterval);
642
750
  }, 1000);
643
751
  return;
644
752
  }
645
753
  }
646
754
  async startMatterServer() {
755
+ this.log.debug('Starting matter server');
647
756
  await this.matterServer.start();
648
757
  this.log.debug('Started matter server');
649
758
  }
@@ -789,14 +898,18 @@ export class Matterbridge {
789
898
  },
790
899
  activeSessionsChangedCallback: (fabricIndex) => {
791
900
  const info = commissioningServer.getActiveSessionInformation(fabricIndex);
792
- this.log.debug(`***Active sessions changed on fabric ${fabricIndex} for ${plg}${name}${nf}`, debugStringify(info));
793
- if (info && info[0]?.isPeerActive === true && info[0]?.secure === true && info[0]?.numberOfActiveSubscriptions >= 1) {
794
- this.log.info(`***Controller connected to ${plg}${name}${nf} ready to start...`);
901
+ let connected = false;
902
+ info.forEach((session) => {
903
+ this.log.debug(`***Active session changed on fabric ${fabricIndex} ${session.fabric?.rootVendorId}/${session.fabric?.label} for ${plg}${name}${nf}`, debugStringify(session));
904
+ if (session.isPeerActive === true && session.secure === true && session.numberOfActiveSubscriptions >= 1) {
905
+ this.log.info(`***Controller ${session.fabric?.rootVendorId}/${session.fabric?.label} connected to ${plg}${name}${nf}`);
906
+ connected = true;
907
+ }
908
+ });
909
+ if (connected) {
795
910
  if (this.bridgeMode === 'childbridge') {
796
911
  const plugin = this.findPlugin(name);
797
912
  if (plugin) {
798
- if (plugin.connected === true)
799
- return; // Only once cause the devices are already added to the plugins aggregator
800
913
  plugin.paired = true;
801
914
  plugin.connected = true;
802
915
  }
@@ -804,45 +917,40 @@ export class Matterbridge {
804
917
  setTimeout(() => {
805
918
  if (this.bridgeMode === 'bridge') {
806
919
  //Logger.defaultLogLevel = Level.INFO;
807
- this.registeredPlugins.forEach(async (plugin) => {
920
+ for (const plugin of this.registeredPlugins) {
808
921
  if (!plugin.enabled)
809
- return;
810
- if (plugin.platform && !plugin.started) {
811
- this.log.info(`***Starting plugin ${plg}${plugin.name}${nf}`);
812
- await plugin.platform.onStart('Matterbridge is commissioned and controllers are connected');
813
- plugin.started = true;
814
- this.log.info(`***Started plugin ${plg}${plugin.name}${nf}`);
815
- }
816
- else {
817
- this.log.error(`***Platform not found for plugin ${plg}${plugin.name}${er}`);
818
- }
819
- });
820
- Logger.defaultLogLevel = Level.DEBUG;
922
+ continue;
923
+ this.startPlugin(plugin, 'Matterbridge is commissioned and controllers are connected', true); // No await do it asyncronously
924
+ //this.configurePlugin(plugin); // No await do it asyncronously
925
+ }
926
+ Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
821
927
  }
822
928
  if (this.bridgeMode === 'childbridge') {
823
929
  //Logger.defaultLogLevel = Level.INFO;
824
930
  const plugin = this.findPlugin(name);
825
- if (!plugin || plugin.type === 'AccessoryPlatform')
826
- return;
827
- this.registeredDevices.forEach(async (registeredDevice) => {
828
- if (registeredDevice.plugin !== name)
829
- return;
830
- this.log.debug(`***Adding device ${registeredDevice.device.name} to aggregator for plugin ${plg}${plugin.name}${db}`);
831
- if (!plugin.aggregator) {
832
- this.log.error(`***Aggregator not found for plugin ${plg}${plugin.name}${er}`);
833
- return;
931
+ if (plugin && plugin.type === 'DynamicPlatform') {
932
+ for (const registeredDevice of this.registeredDevices) {
933
+ if (registeredDevice.plugin === name) {
934
+ this.log.info(`Adding bridged device ${dev}${registeredDevice.device.name}${nf} to aggregator for plugin ${plg}${plugin.name}${db}`);
935
+ if (!plugin.aggregator) {
936
+ this.log.error(`****Aggregator not found for plugin ${plg}${plugin.name}${er}`);
937
+ continue;
938
+ }
939
+ plugin.aggregator.addBridgedDevice(registeredDevice.device);
940
+ if (plugin.addedDevices !== undefined)
941
+ plugin.addedDevices++;
942
+ this.log.info(`Added bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${registeredDevice.device.name}${nf} for plugin ${plg}${plugin.name}${nf}`);
943
+ registeredDevice.added = true;
944
+ }
834
945
  }
835
- plugin.aggregator.addBridgedDevice(registeredDevice.device);
836
- if (plugin.registeredDevices !== undefined)
837
- plugin.registeredDevices++;
838
- registeredDevice.added = true;
839
- });
840
- Logger.defaultLogLevel = Level.DEBUG;
946
+ }
947
+ for (const plugin of this.registeredPlugins) {
948
+ if (plugin.name === name && plugin.platform) {
949
+ this.configurePlugin(plugin); // No await do it asyncronously
950
+ }
951
+ }
952
+ Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
841
953
  }
842
- this.registeredPlugins.forEach(async (plugin) => {
843
- if (plugin.name === name && plugin.platform)
844
- plugin.platform.onConfigure();
845
- });
846
954
  //logEndpoint(commissioningServer.getRootEndpoint());
847
955
  }, 2000);
848
956
  }
@@ -982,6 +1090,10 @@ export class Matterbridge {
982
1090
  await fs.mkdir(this.matterbridgeDirectory);
983
1091
  }
984
1092
  this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
1093
+ // Matterbridge version
1094
+ const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
1095
+ this.matterbridgeVersion = packageJson.version;
1096
+ this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
985
1097
  // Current working directory
986
1098
  const currentDir = process.cwd();
987
1099
  this.log.debug(`Current Working Directory: ${currentDir}`);
@@ -989,7 +1101,24 @@ export class Matterbridge {
989
1101
  const cmdArgs = process.argv.slice(2).join(' ');
990
1102
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
991
1103
  }
992
- getBaseRegisteredPlugin() {
1104
+ getBaseRegisteredPlugins() {
1105
+ const baseRegisteredPlugins = this.registeredPlugins.map((plugin) => ({
1106
+ path: plugin.path,
1107
+ type: plugin.type,
1108
+ name: plugin.name,
1109
+ version: plugin.version,
1110
+ description: plugin.description,
1111
+ author: plugin.author,
1112
+ enabled: plugin.enabled,
1113
+ loaded: plugin.loaded,
1114
+ started: plugin.started,
1115
+ paired: plugin.paired,
1116
+ connected: plugin.connected,
1117
+ registeredDevices: plugin.registeredDevices,
1118
+ }));
1119
+ return baseRegisteredPlugins;
1120
+ }
1121
+ getBaseRegisteredDevices() {
993
1122
  const baseRegisteredPlugins = this.registeredPlugins.map((plugin) => ({
994
1123
  path: plugin.path,
995
1124
  type: plugin.type,
@@ -1039,8 +1168,7 @@ export class Matterbridge {
1039
1168
  // Endpoint to provide plugins
1040
1169
  this.app.get('/api/plugins', (req, res) => {
1041
1170
  this.log.debug('The frontend sent /api/plugins');
1042
- const baseRegisteredPlugins = this.getBaseRegisteredPlugin();
1043
- res.json(baseRegisteredPlugins);
1171
+ res.json(this.getBaseRegisteredPlugins());
1044
1172
  });
1045
1173
  // Endpoint to provide devices
1046
1174
  this.app.get('/api/devices', (req, res) => {
@@ -1072,15 +1200,15 @@ export class Matterbridge {
1072
1200
  // Endpoint to provide the cluster servers of the devices
1073
1201
  this.app.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
1074
1202
  const selectedPluginName = req.params.selectedPluginName;
1075
- const selectedDeviceEndpoint = req.params.selectedDeviceEndpoint;
1203
+ const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
1076
1204
  this.log.debug(`The frontend sent /api/devices_clusters plugin:${selectedPluginName} endpoint:${selectedDeviceEndpoint}`);
1077
- if (selectedPluginName === 'none' /* || selectedDeviceEndpoint === 'none'*/) {
1205
+ if (selectedPluginName === 'none') {
1078
1206
  res.json([]);
1079
1207
  return;
1080
1208
  }
1081
1209
  const data = [];
1082
1210
  this.registeredDevices.forEach((registeredDevice) => {
1083
- if (registeredDevice.plugin === selectedPluginName) {
1211
+ if (registeredDevice.plugin === selectedPluginName && registeredDevice.device.id === selectedDeviceEndpoint) {
1084
1212
  const clusterServers = registeredDevice.device.getAllClusterServers();
1085
1213
  clusterServers.forEach((clusterServer) => {
1086
1214
  Object.entries(clusterServer.attributes).forEach(([key, value]) => {
@@ -1184,6 +1312,13 @@ ws.onmessage = (event) => {
1184
1312
  console.log(`Received message => ${event.data}`);
1185
1313
  };
1186
1314
 
1315
+ */
1316
+ /*
1317
+ // In Matterbridge
1318
+ global.matterbridgeInstance = Matterbridge.loadInstance();
1319
+
1320
+ // In plugins
1321
+ const matterbridge = global.matterbridgeInstance;
1187
1322
  */
1188
1323
  /*
1189
1324
  npx create-react-app matterbridge-frontend