homebridge
Version:
HomeKit support for the impatient
76 lines • 3.6 kB
JavaScript
/**
* External Matter Accessory Publisher
*
* Shared logic for publishing external Matter accessories on dedicated bridges.
* Used by both MatterBridgeManager and ChildBridgeMatterManager to avoid code duplication.
*/
import { Logger } from '../logger.js';
import { User } from '../user.js';
import { generate } from '../util/mac.js';
import { MatterServer } from './server.js';
const log = Logger.withPrefix('Matter/External');
const COLON_RE = /:/g;
/**
* Publish an external Matter accessory on its own dedicated Matter server.
* This is required for devices like Robotic Vacuum Cleaners that Apple Home
* requires to be on their own bridge.
*
* @param accessory - The Matter accessory to publish
* @param context - Configuration context for publishing
* @returns Published accessory info, or null if publishing failed
*/
export async function publishExternalMatterAccessory(accessory, context) {
// Validate accessory has required fields
if (!accessory.UUID) {
log.error('External Matter accessory missing UUID - skipping');
return null;
}
if (!accessory.displayName) {
log.error(`External Matter accessory ${accessory.UUID} missing displayName - skipping`);
return null;
}
// Generate deterministic MAC address from UUID (same pattern as HAP external accessories)
const advertiseAddress = generate(accessory.UUID);
// For Matter, use the MAC without colons as uniqueId
const uniqueId = advertiseAddress.replace(COLON_RE, '');
// Allocate Matter port for the external Matter server
const port = await context.portService.requestMatterPort(uniqueId);
if (!port) {
log.error(`Failed to allocate Matter port for external Matter accessory ${accessory.displayName}`);
log.error('Please configure matterPorts in config.json or free up ports in the default range (5530-5541)');
return null;
}
log.info(`Allocated port ${port} for external Matter accessory: ${accessory.displayName}`);
// Create dedicated Matter server for this accessory
const matterServer = new MatterServer({
port,
uniqueId,
storagePath: User.matterPath(),
displayName: accessory.displayName || 'Matter Device',
manufacturer: accessory.manufacturer,
model: accessory.model,
firmwareRevision: accessory.firmwareRevision,
serialNumber: accessory.serialNumber || uniqueId, // use uniqueId as fallback serial number
debugModeEnabled: context.debugModeEnabled,
externalAccessory: true, // external accessory, added before server runs
networkInterfaces: context.networkInterfaces,
});
// Start the Matter server (but don't run it yet due to externalAccessory mode)
await matterServer.start();
// Get plugin identifier from accessory
const pluginIdentifier = accessory._associatedPlugin || 'unknown';
// Register the accessory to this dedicated server
await matterServer.registerPlatformAccessories(pluginIdentifier, 'ExternalMatter', [accessory]);
// Now run the server with the device already attached (required for external accessories)
await matterServer.runServer();
log.info(`✓ External Matter accessory published: ${accessory.displayName} on port ${port} (bridge ${advertiseAddress})`);
// Get commissioning info
const commissioningInfo = matterServer.getCommissioningInfo();
return {
server: matterServer,
port,
username: advertiseAddress,
commissioningInfo,
};
}
//# sourceMappingURL=ExternalMatterAccessoryPublisher.js.map