homebridge 2.0.0-alpha.43 → 2.0.0-alpha.45

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 (85) hide show
  1. package/dist/api.d.ts +37 -7
  2. package/dist/api.d.ts.map +1 -1
  3. package/dist/api.js +30 -4
  4. package/dist/api.js.map +1 -1
  5. package/dist/bridgeService.d.ts +5 -16
  6. package/dist/bridgeService.d.ts.map +1 -1
  7. package/dist/bridgeService.js +8 -24
  8. package/dist/bridgeService.js.map +1 -1
  9. package/dist/childBridgeFork.d.ts +3 -15
  10. package/dist/childBridgeFork.d.ts.map +1 -1
  11. package/dist/childBridgeFork.js +46 -181
  12. package/dist/childBridgeFork.js.map +1 -1
  13. package/dist/childBridgeService.d.ts +20 -43
  14. package/dist/childBridgeService.d.ts.map +1 -1
  15. package/dist/childBridgeService.js +23 -66
  16. package/dist/childBridgeService.js.map +1 -1
  17. package/dist/cli.d.ts.map +1 -1
  18. package/dist/cli.js +1 -3
  19. package/dist/cli.js.map +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +1 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/ipcService.d.ts +23 -21
  25. package/dist/ipcService.d.ts.map +1 -1
  26. package/dist/ipcService.js +0 -15
  27. package/dist/ipcService.js.map +1 -1
  28. package/dist/matter/index.d.ts +1 -1
  29. package/dist/matter/index.d.ts.map +1 -1
  30. package/dist/matter/index.js.map +1 -1
  31. package/dist/matter/matterAccessoryCache.d.ts +73 -0
  32. package/dist/matter/matterAccessoryCache.d.ts.map +1 -0
  33. package/dist/matter/matterAccessoryCache.js +168 -0
  34. package/dist/matter/matterAccessoryCache.js.map +1 -0
  35. package/dist/matter/matterBehaviors.d.ts +123 -0
  36. package/dist/matter/matterBehaviors.d.ts.map +1 -0
  37. package/dist/matter/matterBehaviors.js +582 -0
  38. package/dist/matter/matterBehaviors.js.map +1 -0
  39. package/dist/matter/matterConfigValidator.d.ts +0 -1
  40. package/dist/matter/matterConfigValidator.d.ts.map +1 -1
  41. package/dist/matter/matterConfigValidator.js +15 -45
  42. package/dist/matter/matterConfigValidator.js.map +1 -1
  43. package/dist/matter/matterErrorHandler.d.ts +1 -1
  44. package/dist/matter/matterErrorHandler.d.ts.map +1 -1
  45. package/dist/matter/matterErrorHandler.js +35 -22
  46. package/dist/matter/matterErrorHandler.js.map +1 -1
  47. package/dist/matter/matterNetworkMonitor.d.ts +3 -0
  48. package/dist/matter/matterNetworkMonitor.d.ts.map +1 -1
  49. package/dist/matter/matterNetworkMonitor.js +49 -26
  50. package/dist/matter/matterNetworkMonitor.js.map +1 -1
  51. package/dist/matter/matterServer.d.ts +79 -9
  52. package/dist/matter/matterServer.d.ts.map +1 -1
  53. package/dist/matter/matterServer.js +491 -111
  54. package/dist/matter/matterServer.js.map +1 -1
  55. package/dist/matter/matterSharedTypes.d.ts +36 -16
  56. package/dist/matter/matterSharedTypes.d.ts.map +1 -1
  57. package/dist/matter/matterSharedTypes.js +0 -3
  58. package/dist/matter/matterSharedTypes.js.map +1 -1
  59. package/dist/matter/matterStorage.d.ts +11 -1
  60. package/dist/matter/matterStorage.d.ts.map +1 -1
  61. package/dist/matter/matterStorage.js +12 -2
  62. package/dist/matter/matterStorage.js.map +1 -1
  63. package/dist/matter/matterTypes.d.ts +69 -20
  64. package/dist/matter/matterTypes.d.ts.map +1 -1
  65. package/dist/matter/matterTypes.js.map +1 -1
  66. package/dist/matter/matterValidation.d.ts +57 -0
  67. package/dist/matter/matterValidation.d.ts.map +1 -0
  68. package/dist/matter/matterValidation.js +97 -0
  69. package/dist/matter/matterValidation.js.map +1 -0
  70. package/dist/plugin.d.ts.map +1 -1
  71. package/dist/plugin.js +2 -4
  72. package/dist/plugin.js.map +1 -1
  73. package/dist/server.d.ts +0 -12
  74. package/dist/server.d.ts.map +1 -1
  75. package/dist/server.js +97 -280
  76. package/dist/server.js.map +1 -1
  77. package/package.json +3 -3
  78. package/dist/bridgeTypes.d.ts +0 -54
  79. package/dist/bridgeTypes.d.ts.map +0 -1
  80. package/dist/bridgeTypes.js +0 -8
  81. package/dist/bridgeTypes.js.map +0 -1
  82. package/dist/matter/matterDiagnostics.d.ts +0 -121
  83. package/dist/matter/matterDiagnostics.d.ts.map +0 -1
  84. package/dist/matter/matterDiagnostics.js +0 -323
  85. package/dist/matter/matterDiagnostics.js.map +0 -1
package/dist/server.js CHANGED
@@ -38,14 +38,10 @@ export class Server {
38
38
  externalPortService;
39
39
  config;
40
40
  // used to keep track of child bridges
41
- // Key is either HAP username (MAC address) or Matter-only identifier (e.g., "matter-plugin-platform")
41
+ // Key is HAP username (MAC address)
42
42
  childBridges = new Map();
43
43
  // Matter server instance for main bridge (if enabled)
44
44
  matterServer;
45
- // Cache for Matter bridge identifier -> ChildBridgeService lookups
46
- matterBridgeCache = new Map();
47
- // Track platform configurations for routing accessories
48
- platformConfigs = new Map();
49
45
  // current server status
50
46
  serverStatus = "pending" /* ServerStatus.PENDING */;
51
47
  constructor(options = {}) {
@@ -72,7 +68,7 @@ export class Server {
72
68
  };
73
69
  // shallow copy the homebridge options to the bridge options object
74
70
  Object.assign(bridgeConfig, this.options);
75
- this.bridgeService = new BridgeService(this.api, this.pluginManager, this.externalPortService, bridgeConfig, this.config.bridge, this.config);
71
+ this.bridgeService = new BridgeService(this.api, this.pluginManager, this.externalPortService, bridgeConfig, this.config.bridge);
76
72
  // Handle platform accessory registration
77
73
  this.api.on("registerPlatformAccessories" /* InternalAPIEvent.REGISTER_PLATFORM_ACCESSORIES */, this.handleRegisterPlatformAccessories.bind(this));
78
74
  this.api.on("unregisterPlatformAccessories" /* InternalAPIEvent.UNREGISTER_PLATFORM_ACCESSORIES */, this.handleUnregisterPlatformAccessories.bind(this));
@@ -101,15 +97,38 @@ export class Server {
101
97
  */
102
98
  setServerStatus(status) {
103
99
  this.serverStatus = status;
104
- this.ipcService.sendMessage("serverStatusUpdate" /* IpcOutgoingEvent.SERVER_STATUS_UPDATE */, {
105
- type: 'hap', // Main bridge is HAP
100
+ const statusUpdate = {
106
101
  status: this.serverStatus,
107
102
  paired: this.bridgeService?.bridge?._accessoryInfo?.paired() ?? null,
108
103
  setupUri: this.bridgeService?.bridge?.setupURI() ?? null,
109
- name: this.bridgeService?.bridge?.displayName || this.config.bridge.name,
104
+ name: this.config.bridge.name,
110
105
  username: this.config.bridge.username,
111
106
  pin: this.config.bridge.pin,
112
- });
107
+ matter: {
108
+ enabled: false,
109
+ },
110
+ };
111
+ // Include Matter commissioning info if Matter is enabled
112
+ if (this.matterServer) {
113
+ const commissioningInfo = this.matterServer.getCommissioningInfo();
114
+ statusUpdate.matter = {
115
+ enabled: true,
116
+ port: this.config.bridge.matter?.port,
117
+ setupUri: commissioningInfo.qrCode,
118
+ pin: commissioningInfo.manualPairingCode,
119
+ serialNumber: commissioningInfo.serialNumber,
120
+ commissioned: commissioningInfo.commissioned || false,
121
+ deviceCount: this.matterServer.getAccessories().length,
122
+ };
123
+ }
124
+ else if (this.config.bridge.matter) {
125
+ // Matter is configured but not yet started (or failed to start)
126
+ statusUpdate.matter = {
127
+ enabled: false,
128
+ port: this.config.bridge.matter?.port,
129
+ };
130
+ }
131
+ this.ipcService.sendMessage("serverStatusUpdate" /* IpcOutgoingEvent.SERVER_STATUS_UPDATE */, statusUpdate);
113
132
  }
114
133
  async start() {
115
134
  if (this.config.bridge.disableIpc !== true) {
@@ -128,7 +147,7 @@ export class Server {
128
147
  if (this.config.accessories.length > 0) {
129
148
  this.loadAccessories();
130
149
  }
131
- // start child HAP bridges
150
+ // start child bridges
132
151
  for (const childBridge of this.childBridges.values()) {
133
152
  childBridge.start();
134
153
  }
@@ -147,10 +166,12 @@ export class Server {
147
166
  if (!this.config.bridge.matter) {
148
167
  return;
149
168
  }
169
+ // Declare matterPort outside try block so it's accessible in catch
170
+ let matterPort;
150
171
  try {
151
172
  log.info('Initializing Matter server for main bridge...');
152
173
  // Allocate port from pool if not explicitly configured
153
- let matterPort = this.config.bridge.matter.port;
174
+ matterPort = this.config.bridge.matter.port;
154
175
  if (!matterPort) {
155
176
  matterPort = await this.externalPortService.requestPort(`${this.config.bridge.username}:MATTER`);
156
177
  if (!matterPort) {
@@ -161,22 +182,25 @@ export class Server {
161
182
  log.info(`Allocated port ${matterPort} from pool for main Matter bridge`);
162
183
  }
163
184
  }
164
- // Create Matter server instance
185
+ // Create Matter server instance with config inheritance from main bridge
165
186
  this.matterServer = new MatterServer({
166
187
  storagePath: User.matterPath(),
167
188
  port: matterPort,
168
- uniqueId: 'main-bridge',
169
- name: this.config.bridge.matter.name || (this.config.bridge.name ? `${this.config.bridge.name} (Matter)` : 'Homebridge Matter Bridge'),
189
+ uniqueId: this.config.bridge.username,
190
+ manufacturer: this.config.bridge.manufacturer,
191
+ model: this.config.bridge.model,
192
+ firmwareRevision: this.config.bridge.firmwareRevision,
193
+ serialNumber: this.config.bridge.serialNumber,
194
+ debugModeEnabled: this.options.debugModeEnabled,
170
195
  });
171
196
  // Start the Matter server
172
197
  await this.matterServer.start();
173
198
  log.info('Matter server initialized for main bridge');
174
199
  // Inform the API that Matter is enabled
175
200
  this.api._setMatterEnabled(true);
176
- // Send Matter status update to notify UI that Matter server is ready
177
- if (this.config.bridge.disableIpc !== true) {
178
- this.sendMainBridgeMatterStatusUpdate();
179
- }
201
+ // Set the Matter server reference for API methods like getAccessoryState
202
+ this.api._setMatterServer(this.matterServer);
203
+ // Matter server is running - status updates will be handled through unified child bridge events
180
204
  }
181
205
  catch (error) {
182
206
  log.error('Failed to initialize Matter server for main bridge:', error);
@@ -192,40 +216,32 @@ export class Server {
192
216
  log.error('║ • Incomplete writes during shutdown ║');
193
217
  log.error('║ ║');
194
218
  log.error('║ To fix this, delete the corrupted storage directory: ║');
195
- log.error('║ rm -rf ~/.homebridge/matter/main-bridge ║');
219
+ log.error(`║ rm -rf ~/.homebridge/matter/${this.config.bridge.username} ║`);
196
220
  log.error('║ ║');
197
221
  log.error('║ Note: You will need to re-pair your Matter devices after deletion. ║');
198
222
  log.error('╚════════════════════════════════════════════════════════════════════════════╝');
199
223
  log.error('');
200
224
  }
225
+ else if (error.code === 'EADDRINUSE' || (error.message && error.message.includes('address already in use'))) {
226
+ log.error('');
227
+ log.error('╔════════════════════════════════════════════════════════════════════════════╗');
228
+ log.error('║ MATTER PORT ALREADY IN USE ║');
229
+ log.error('╠════════════════════════════════════════════════════════════════════════════╣');
230
+ log.error(`║ Port ${matterPort} is already in use by another application. ║`);
231
+ log.error('║ ║');
232
+ log.error('║ To fix this: ║');
233
+ log.error('║ 1. Stop the application using this port, or ║');
234
+ log.error('║ 2. Configure a different port in your config.json: ║');
235
+ log.error('║ "bridge": { ║');
236
+ log.error('║ "matter": { ║');
237
+ log.error('║ "port": <different-port> ║');
238
+ log.error('║ } ║');
239
+ log.error('║ } ║');
240
+ log.error('╚════════════════════════════════════════════════════════════════════════════╝');
241
+ log.error('');
242
+ }
201
243
  }
202
244
  }
203
- /**
204
- * Send Matter status update for main bridge
205
- */
206
- sendMainBridgeMatterStatusUpdate() {
207
- if (!this.matterServer || !this.matterServer.isServerRunning()) {
208
- return;
209
- }
210
- const commissioningInfo = this.matterServer.getCommissioningInfo();
211
- // Transform property names to match UI expectations
212
- const statusUpdate = {
213
- type: 'matter',
214
- status: 'ok',
215
- port: this.config.bridge.matter?.port || 5540,
216
- setupUri: commissioningInfo.qrCode, // Map qrCode -> setupUri for UI
217
- pin: commissioningInfo.manualPairingCode, // Map manualPairingCode -> pin for UI
218
- serialNumber: commissioningInfo.serialNumber,
219
- passcode: commissioningInfo.passcode,
220
- discriminator: commissioningInfo.discriminator,
221
- name: this.config.bridge.matter?.name || (this.config.bridge.name ? `${this.config.bridge.name} (Matter)` : 'Homebridge Matter Bridge'),
222
- plugin: 'main-bridge',
223
- identifier: 'main-bridge',
224
- deviceCount: this.matterServer.getAccessories().length,
225
- commissioned: commissioningInfo.commissioned,
226
- };
227
- this.ipcService.sendMessage("matterBridgeStatusUpdate" /* IpcOutgoingEvent.MATTER_BRIDGE_STATUS_UPDATE */, statusUpdate);
228
- }
229
245
  async teardown() {
230
246
  this.bridgeService.teardown();
231
247
  // Stop main Matter server if running
@@ -242,7 +258,6 @@ export class Server {
242
258
  }
243
259
  publishBridge() {
244
260
  this.bridgeService.publishBridge();
245
- // Main bridge always has a pin (validated in loadConfig)
246
261
  this.printSetupInfo(this.config.bridge.pin);
247
262
  }
248
263
  handlePublishExternalAccessories(accessories) {
@@ -322,7 +337,6 @@ export class Server {
322
337
  bridge.username = bridge.username || defaultBridge.username;
323
338
  bridge.pin = bridge.pin || defaultBridge.pin;
324
339
  config.bridge = bridge;
325
- // Main bridge username is always required and must be valid
326
340
  const username = config.bridge.username;
327
341
  if (!validMacAddress(username)) {
328
342
  throw new Error(`Not a valid username: ${username}. Must be 6 pairs of colon-separated hexadecimal chars (A-F 0-9), like a MAC address.`);
@@ -342,10 +356,6 @@ export class Server {
342
356
  if (config.bridge.matter || config.platforms.some((p) => p._bridge?.matter) || config.accessories.some((a) => a._bridge?.matter)) {
343
357
  // Validate main bridge Matter config
344
358
  if (config.bridge.matter) {
345
- // Apply default name before validation if not set
346
- if (!config.bridge.matter.name) {
347
- config.bridge.matter.name = config.bridge.name ? `${config.bridge.name} (Matter)` : 'Homebridge Matter Bridge';
348
- }
349
359
  const validation = MatterConfigValidator.validate(config.bridge.matter);
350
360
  if (!validation.isValid) {
351
361
  log.error('Main bridge Matter configuration is invalid. Matter will not be enabled for the main bridge.');
@@ -434,37 +444,29 @@ export class Server {
434
444
  }
435
445
  const logger = Logger.withPrefix(displayName);
436
446
  logger('Initializing %s accessory...', accessoryIdentifier);
437
- // Handle child bridge (HAP, Matter-only, or HAP+Matter)
438
447
  if (accessoryConfig._bridge) {
439
- // Ensure the username is always uppercase if it exists (HAP bridges only)
440
- if (accessoryConfig._bridge.username) {
441
- accessoryConfig._bridge.username = accessoryConfig._bridge.username.toUpperCase();
442
- }
443
- // Generate unique key for this child bridge
444
- const bridgeKey = this.getChildBridgeKey("accessory" /* PluginType.ACCESSORY */, accessoryIdentifier, plugin.getPluginIdentifier(), accessoryConfig._bridge);
448
+ // ensure the username is always uppercase
449
+ accessoryConfig._bridge.username = accessoryConfig._bridge.username.toUpperCase();
445
450
  try {
446
- this.validateChildBridgeConfig("accessory" /* PluginType.ACCESSORY */, accessoryIdentifier, plugin.getPluginIdentifier(), accessoryConfig._bridge, bridgeKey);
451
+ this.validateChildBridgeConfig("accessory" /* PluginType.ACCESSORY */, accessoryIdentifier, accessoryConfig._bridge);
447
452
  }
448
453
  catch (error) {
449
454
  log.error(error.message);
450
455
  return;
451
456
  }
452
457
  let childBridge;
453
- if (this.childBridges.has(bridgeKey)) {
454
- childBridge = this.childBridges.get(bridgeKey);
455
- logger(`Adding to existing child bridge ${bridgeKey}`);
458
+ if (this.childBridges.has(accessoryConfig._bridge.username)) {
459
+ childBridge = this.childBridges.get(accessoryConfig._bridge.username);
460
+ logger(`Adding to existing child bridge ${accessoryConfig._bridge.username}`);
456
461
  }
457
462
  else {
458
- const bridgeType = accessoryConfig._bridge.matterOnly ? 'Matter-only' : 'HAP';
459
- logger(`Initializing ${bridgeType} child bridge ${bridgeKey}`);
463
+ logger(`Initializing child bridge ${accessoryConfig._bridge.username}`);
460
464
  childBridge = new ChildBridgeService("accessory" /* PluginType.ACCESSORY */, accessoryIdentifier, plugin, accessoryConfig._bridge, this.config, this.options, this.api, this.ipcService, this.externalPortService);
461
- this.childBridges.set(bridgeKey, childBridge);
465
+ this.childBridges.set(accessoryConfig._bridge.username, childBridge);
462
466
  }
463
467
  // add config to child bridge service
464
468
  childBridge.addConfig(accessoryConfig);
465
- // Matter for child bridges is handled inside the forked child process (childBridgeFork.ts)
466
- // to maintain isolation - each child process manages its own Matter server
467
- return; // Done - child bridge
469
+ return;
468
470
  }
469
471
  const accessoryInstance = new constructor(logger, accessoryConfig, this.api);
470
472
  // pass accessoryIdentifier for UUID generation, and optional parameter uuid_base which can be used instead of displayName for UUID generation
@@ -521,29 +523,21 @@ export class Server {
521
523
  }
522
524
  const logger = Logger.withPrefix(displayName);
523
525
  logger('Initializing %s platform...', platformIdentifier);
524
- // Handle child bridge (HAP, Matter-only, or HAP+Matter)
525
526
  if (platformConfig._bridge) {
526
- // Ensure the username is always uppercase if it exists (HAP bridges only)
527
- if (platformConfig._bridge.username) {
528
- platformConfig._bridge.username = platformConfig._bridge.username.toUpperCase();
529
- }
530
- // Generate unique key for this child bridge
531
- const bridgeKey = this.getChildBridgeKey("platform" /* PluginType.PLATFORM */, platformIdentifier, plugin.getPluginIdentifier(), platformConfig._bridge);
527
+ // ensure the username is always uppercase
528
+ platformConfig._bridge.username = platformConfig._bridge.username.toUpperCase();
532
529
  try {
533
- this.validateChildBridgeConfig("platform" /* PluginType.PLATFORM */, platformIdentifier, plugin.getPluginIdentifier(), platformConfig._bridge, bridgeKey);
530
+ this.validateChildBridgeConfig("platform" /* PluginType.PLATFORM */, platformIdentifier, platformConfig._bridge);
534
531
  }
535
532
  catch (error) {
536
533
  log.error(error.message);
537
534
  return;
538
535
  }
539
- const bridgeType = platformConfig._bridge.matterOnly ? 'Matter-only' : 'HAP';
540
- logger(`Initializing ${bridgeType} child bridge ${bridgeKey}`);
536
+ logger(`Initializing child bridge ${platformConfig._bridge.username}`);
541
537
  const childBridge = new ChildBridgeService("platform" /* PluginType.PLATFORM */, platformIdentifier, plugin, platformConfig._bridge, this.config, this.options, this.api, this.ipcService, this.externalPortService);
542
- this.childBridges.set(bridgeKey, childBridge);
538
+ this.childBridges.set(platformConfig._bridge.username, childBridge);
543
539
  // add config to child bridge service
544
540
  childBridge.addConfig(platformConfig);
545
- // Matter for child bridges is handled inside the forked child process (childBridgeFork.ts)
546
- // to maintain isolation - each child process manages its own Matter server
547
541
  return;
548
542
  }
549
543
  const platform = new constructor(logger, platformConfig, this.api);
@@ -560,48 +554,21 @@ export class Server {
560
554
  });
561
555
  return promises;
562
556
  }
563
- /**
564
- * Generate a unique identifier for a child bridge
565
- * - For HAP bridges: returns the uppercase MAC address
566
- * - For Matter-only bridges: returns "matter-{plugin}-{identifier}"
567
- */
568
- getChildBridgeKey(type, identifier, pluginName, bridgeConfig) {
569
- if (bridgeConfig.matterOnly) {
570
- // Matter-only bridge: generate identifier from plugin and platform/accessory name
571
- return `matter-${pluginName}-${identifier}`;
572
- }
573
- // HAP bridge: use MAC address (already validated and uppercased)
574
- return bridgeConfig.username.toUpperCase();
575
- }
576
557
  /**
577
558
  * Validate an external bridge config
578
559
  */
579
- validateChildBridgeConfig(type, identifier, pluginName, bridgeConfig, bridgeKey) {
580
- // Matter-only bridges don't require HAP fields
581
- if (bridgeConfig.matterOnly) {
582
- if (!bridgeConfig.matter) {
583
- throw new Error(`Error loading the ${type} "${identifier}" requested in your config.json - `
584
- + 'Matter-only bridge must have a "matter" configuration block in _bridge.matter.');
585
- }
586
- // Check for duplicate Matter-only bridges
587
- if (this.childBridges.has(bridgeKey)) {
588
- throw new Error(`Error loading the ${type} "${identifier}" requested in your config.json - `
589
- + 'A Matter-only bridge with this plugin and platform/accessory combination already exists. Each Matter-only bridge must be unique.');
590
- }
591
- // Matter-only bridges don't need username/pin validation
592
- return;
593
- }
594
- // HAP bridges (or HAP+Matter bridges) require username and pin
560
+ validateChildBridgeConfig(type, identifier, bridgeConfig) {
561
+ // All child bridges require username
595
562
  if (!bridgeConfig.username) {
596
563
  throw new Error(`Error loading the ${type} "${identifier}" requested in your config.json - `
597
- + 'Missing required field "_bridge.username". Set "matterOnly": true if you want a Matter-only bridge.');
564
+ + 'Missing required field "_bridge.username".');
598
565
  }
599
566
  if (!validMacAddress(bridgeConfig.username)) {
600
567
  throw new Error(`Error loading the ${type} "${identifier}" requested in your config.json - `
601
568
  + `not a valid username in _bridge.username: "${bridgeConfig.username}". Must be 6 pairs of colon-separated hexadecimal chars (A-F 0-9), like a MAC address.`);
602
569
  }
603
- if (this.childBridges.has(bridgeKey)) {
604
- const childBridge = this.childBridges.get(bridgeKey);
570
+ if (this.childBridges.has(bridgeConfig.username)) {
571
+ const childBridge = this.childBridges.get(bridgeConfig.username);
605
572
  if (type === "platform" /* PluginType.PLATFORM */) {
606
573
  // only a single platform can exist on one child bridge
607
574
  throw new Error(`Error loading the ${type} "${identifier}" requested in your config.json - `
@@ -613,8 +580,6 @@ export class Server {
613
580
  + `Duplicate username found in _bridge.username: "${bridgeConfig.username}". You can only group accessories of the same type in a child bridge.`);
614
581
  }
615
582
  }
616
- // Check that child bridge username doesn't conflict with main bridge
617
- // Main bridge always has a username (validated in loadConfig)
618
583
  if (bridgeConfig.username === this.config.bridge.username.toUpperCase()) {
619
584
  throw new Error(`Error loading the ${type} "${identifier}" requested in your config.json - `
620
585
  + `Username found in _bridge.username: "${bridgeConfig.username}" is the same as the main bridge. Each child bridge platform/accessory must have it's own unique username.`);
@@ -631,9 +596,7 @@ export class Server {
631
596
  // noinspection SuspiciousTypeOfGuard
632
597
  if (typeof username === 'string') {
633
598
  const childBridge = this.childBridges.get(username.toUpperCase());
634
- if (childBridge) {
635
- childBridge.restartChildBridge();
636
- }
599
+ childBridge?.restartChildBridge();
637
600
  }
638
601
  });
639
602
  // handle stop child bridge event
@@ -641,9 +604,7 @@ export class Server {
641
604
  // noinspection SuspiciousTypeOfGuard
642
605
  if (typeof username === 'string') {
643
606
  const childBridge = this.childBridges.get(username.toUpperCase());
644
- if (childBridge) {
645
- childBridge.stopChildBridge();
646
- }
607
+ childBridge?.stopChildBridge();
647
608
  }
648
609
  });
649
610
  // handle start child bridge event
@@ -651,176 +612,32 @@ export class Server {
651
612
  // noinspection SuspiciousTypeOfGuard
652
613
  if (typeof username === 'string') {
653
614
  const childBridge = this.childBridges.get(username.toUpperCase());
654
- if (childBridge) {
655
- childBridge.startChildBridge();
656
- }
615
+ childBridge?.startChildBridge();
657
616
  }
658
617
  });
659
618
  this.ipcService.on("childBridgeMetadataRequest" /* IpcIncomingEvent.CHILD_BRIDGE_METADATA_REQUEST */, () => {
660
- const childBridgeMetadata = Array.from(this.childBridges.values()).map(x => x.getMetadata());
661
- this.ipcService.sendMessage("childBridgeMetadataResponse" /* IpcOutgoingEvent.CHILD_BRIDGE_METADATA_RESPONSE */, childBridgeMetadata);
662
- });
663
- // Matter bridge IPC handlers
664
- // Main bridge Matter server runs in this process alongside HAP
665
- // Child bridge Matter servers run in forked child processes (see childBridgeFork.ts)
666
- this.ipcService.on("restartMatterBridge" /* IpcIncomingEvent.RESTART_MATTER_BRIDGE */, (matterBridgeId) => {
667
- if (matterBridgeId === 'main-bridge') {
668
- // Main Matter bridge runs in the main process, so restart the entire Homebridge instance
669
- log.info('Restarting Homebridge (Matter bridge restart requested)...');
670
- process.kill(process.pid, 'SIGTERM');
671
- }
672
- else {
673
- // Handle child bridge using cache for efficient lookup
674
- const childBridge = this.matterBridgeCache.get(matterBridgeId);
675
- if (childBridge) {
676
- childBridge.restartChildBridge();
677
- }
678
- else {
679
- log.warn(`Child bridge ${matterBridgeId} not found for Matter restart`);
680
- }
681
- }
682
- });
683
- this.ipcService.on("stopMatterBridge" /* IpcIncomingEvent.STOP_MATTER_BRIDGE */, (matterBridgeId) => {
684
- if (matterBridgeId === 'main-bridge') {
685
- log.warn('Stop requested for main Matter bridge - this is not supported');
686
- log.info('The main Matter bridge runs alongside the HAP bridge and cannot be stopped independently');
687
- }
688
- else {
689
- // Handle child bridge using cache for efficient lookup
690
- const childBridge = this.matterBridgeCache.get(matterBridgeId);
691
- if (childBridge) {
692
- childBridge.stopChildBridge();
693
- }
694
- else {
695
- log.warn(`Child bridge ${matterBridgeId} not found for Matter stop`);
696
- }
697
- }
698
- });
699
- this.ipcService.on("startMatterBridge" /* IpcIncomingEvent.START_MATTER_BRIDGE */, (matterBridgeId) => {
700
- if (matterBridgeId === 'main-bridge') {
701
- log.warn('Start requested for main Matter bridge - this is not supported');
702
- log.info('The main Matter bridge starts automatically with Homebridge');
703
- }
704
- else {
705
- // Handle child bridge using cache for efficient lookup
706
- const childBridge = this.matterBridgeCache.get(matterBridgeId);
707
- if (childBridge) {
708
- childBridge.startChildBridge();
709
- }
710
- else {
711
- log.warn(`Child bridge ${matterBridgeId} not found for Matter start`);
712
- }
713
- }
714
- });
715
- this.ipcService.on("matterBridgeMetadataRequest" /* IpcIncomingEvent.MATTER_BRIDGE_METADATA_REQUEST */, () => {
716
- const matterBridges = [];
717
- // Add main bridge Matter metadata if enabled
718
- if (this.matterServer && this.matterServer.isServerRunning()) {
719
- const commissioningInfo = this.matterServer.getCommissioningInfo();
720
- matterBridges.push({
721
- type: 'matter',
722
- status: 'ok',
723
- port: this.config.bridge.matter?.port || 5540,
724
- setupUri: commissioningInfo.qrCode,
725
- pin: commissioningInfo.manualPairingCode,
726
- serialNumber: commissioningInfo.serialNumber,
727
- name: this.config.bridge.name,
728
- plugin: 'main-bridge',
729
- identifier: 'main-bridge',
730
- deviceCount: this.matterServer.getAccessories().length,
731
- commissioned: commissioningInfo.commissioned,
732
- matterEnabled: true,
733
- });
734
- }
735
- // Add child bridge Matter metadata
736
- for (const childBridge of this.childBridges.values()) {
737
- const matterInfo = childBridge.getCommissioningInfo();
738
- const matterConfig = childBridge.getMatterConfig();
739
- if (matterInfo && matterConfig) {
740
- const metadata = childBridge.getMetadata();
741
- // Use plugin-identifier format to match storage
742
- const childIdentifier = `${metadata.plugin}-${childBridge.identifier}`;
743
- // Populate cache for efficient lookups
744
- this.matterBridgeCache.set(childIdentifier, childBridge);
745
- matterBridges.push({
746
- type: 'matter',
747
- status: metadata.status === 'ok' ? 'ok' : 'pending',
748
- port: matterConfig.port,
749
- setupUri: matterInfo.qrCode,
750
- pin: matterInfo.manualPairingCode,
751
- serialNumber: matterInfo.serialNumber,
752
- name: matterConfig.name || metadata.name,
753
- plugin: metadata.plugin,
754
- identifier: childIdentifier,
755
- commissioned: matterInfo.commissioned,
756
- username: metadata.username,
757
- matterEnabled: true,
758
- });
759
- }
760
- }
761
- this.ipcService.sendMessage("matterBridgeMetadataResponse" /* IpcOutgoingEvent.MATTER_BRIDGE_METADATA_RESPONSE */, matterBridges);
762
- });
763
- this.ipcService.on("matterAccessoriesRequest" /* IpcIncomingEvent.MATTER_ACCESSORIES_REQUEST */, () => {
764
- // Return Matter accessories from all bridges
765
- const allMatterAccessories = {
766
- children: {},
767
- };
768
- // Add main bridge accessories
769
- if (this.matterServer) {
770
- allMatterAccessories.children['main-bridge'] = this.matterServer.getAccessories();
771
- }
772
- // Child bridge accessories are managed by child processes
773
- this.ipcService.sendMessage("matterAccessoriesResponse" /* IpcOutgoingEvent.MATTER_ACCESSORIES_RESPONSE */, allMatterAccessories);
774
- });
775
- this.ipcService.on("matterCommissioningInfoRequest" /* IpcIncomingEvent.MATTER_COMMISSIONING_INFO_REQUEST */, (matterBridgeId) => {
776
- let rawInfo = { commissioned: false };
777
- // Handle main bridge
778
- if (matterBridgeId === 'main-bridge') {
779
- if (this.matterServer && this.matterServer.isServerRunning()) {
780
- rawInfo = this.matterServer.getCommissioningInfo();
781
- }
782
- else {
783
- log.debug(`[Matter] Server not available when commissioning info requested for: ${matterBridgeId}`);
784
- }
785
- }
786
- else {
787
- // Handle child bridge using plugin-identifier format (e.g., homebridge-virtual-accessories-VirtualAccessoriesForHomebridge)
788
- // Use cache for efficient O(1) lookup
789
- const childBridge = this.matterBridgeCache.get(matterBridgeId);
790
- if (childBridge) {
791
- rawInfo = childBridge.getCommissioningInfo() || { commissioned: false };
792
- }
793
- else {
794
- log.debug(`[Matter] Child bridge ${matterBridgeId} not found when commissioning info requested`);
795
- }
796
- }
797
- // Transform property names to match MatterCommissioningInfo interface
798
- const commissioningInfo = {
799
- commissioned: rawInfo.commissioned || false,
800
- pin: rawInfo.manualPairingCode, // Map manualPairingCode -> pin
801
- setupUri: rawInfo.qrCode, // Map qrCode -> setupUri
802
- serialNumber: rawInfo.serialNumber,
803
- };
804
- this.ipcService.sendMessage("matterCommissioningInfoResponse" /* IpcOutgoingEvent.MATTER_COMMISSIONING_INFO_RESPONSE */, commissioningInfo);
619
+ this.ipcService.sendMessage("childBridgeMetadataResponse" /* IpcOutgoingEvent.CHILD_BRIDGE_METADATA_RESPONSE */, Array.from(this.childBridges.values()).map(x => x.getMetadata()));
805
620
  });
806
621
  }
807
622
  printSetupInfo(pin) {
808
- log.info('Setup Payload:');
809
- log.info(this.bridgeService.bridge.setupURI());
623
+ /* eslint-disable no-console */
624
+ console.log('Setup Payload:');
625
+ console.log(this.bridgeService.bridge.setupURI());
810
626
  if (!this.options.hideQRCode) {
811
- log.info('Scan this code with your HomeKit app on your iOS device to pair with Homebridge:');
627
+ console.log('Scan this code with your HomeKit app on your iOS device to pair with Homebridge:');
812
628
  qrcode.setErrorLevel('M'); // HAP specifies level M or higher for ECC
813
629
  qrcode.generate(this.bridgeService.bridge.setupURI());
814
- log.info('Or enter this code with your HomeKit app on your iOS device to pair with Homebridge:');
630
+ console.log('Or enter this code with your HomeKit app on your iOS device to pair with Homebridge:');
815
631
  }
816
632
  else {
817
- log.info('Enter this code with your HomeKit app on your iOS device to pair with Homebridge:');
633
+ console.log('Enter this code with your HomeKit app on your iOS device to pair with Homebridge:');
818
634
  }
819
- log.info(chalk.black.bgWhite(' '));
820
- log.info(chalk.black.bgWhite(' ┌────────────┐ '));
821
- log.info(chalk.black.bgWhite(` │ ${pin} │ `));
822
- log.info(chalk.black.bgWhite(' └────────────┘ '));
823
- log.info(chalk.black.bgWhite(' '));
635
+ console.log(chalk.black.bgWhite(' '));
636
+ console.log(chalk.black.bgWhite(' ┌────────────┐ '));
637
+ console.log(chalk.black.bgWhite(` │ ${pin} │ `));
638
+ console.log(chalk.black.bgWhite(' └────────────┘ '));
639
+ console.log(chalk.black.bgWhite(' '));
640
+ /* eslint-enable no-console */
824
641
  }
825
642
  }
826
643
  //# sourceMappingURL=server.js.map