[homekit] Improve multiple instance management (#14016)

* [homekit] improve instance management

 * allow addressing individual instances for most console commands
 * don't restart all instances if simply adding/removing instances on
   config change
 * clear stored info when removing instances

* [homekit] reset instance identity when clearing pairings
* [homekit] log the actual interface we looked up

Signed-off-by: Cody Cutrer <cody@cutrer.us>
This commit is contained in:
Cody Cutrer 2023-01-03 15:10:42 -07:00 committed by GitHub
parent f082df923f
commit 47f5489d70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 272 additions and 90 deletions

View File

@ -13,7 +13,7 @@
package org.openhab.io.homekit; package org.openhab.io.homekit;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.Collection;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
@ -45,17 +45,43 @@ public interface Homekit {
void allowUnauthenticatedRequests(boolean allow); void allowUnauthenticatedRequests(boolean allow);
/** /**
* returns list of HomeKit accessories registered at bridge. * returns list of HomeKit accessories registered on all bridge instances.
*/ */
List<HomekitAccessory> getAccessories(); Collection<HomekitAccessory> getAccessories();
/** /**
* clear all pairings with HomeKit clients * returns list of HomeKit accessories registered on a specific instance.
*/
Collection<HomekitAccessory> getAccessories(int instance);
/**
* clear all pairings with HomeKit clients on all bridge instances.
*/ */
void clearHomekitPairings(); void clearHomekitPairings();
/**
* clear all pairings with HomeKit clients for a specific instance.
*
* @param instance the instance number (1-based)
*/
void clearHomekitPairings(int instance);
/** /**
* Prune dummy accessories (accessories that no longer have associated items) * Prune dummy accessories (accessories that no longer have associated items)
* on all bridge instances.
*/ */
void pruneDummyAccessories(); void pruneDummyAccessories();
/**
* Prune dummy accessories (accessories that no longer have associated items)
* for a specific instance
*
* @param instance the instance number (1-based)
*/
void pruneDummyAccessories(int instance);
/**
* returns how many bridge instances there are
*/
int getInstanceCount();
} }

View File

@ -147,15 +147,25 @@ public class HomekitAuthInfoImpl implements HomekitAuthInfo {
} }
public void clear() { public void clear() {
logger.trace("clear all users");
if (!this.blockUserDeletion) { if (!this.blockUserDeletion) {
for (String key : new HashSet<>(storage.getKeys())) { for (String key : new HashSet<>(storage.getKeys())) {
if (isUserKey(key)) { if (isUserKey(key)) {
storage.remove(key); storage.remove(key);
} }
} }
mac = HomekitServer.generateMac();
storage.put(STORAGE_MAC, mac);
storage.remove(STORAGE_SALT);
storage.remove(STORAGE_PRIVATE_KEY);
try {
initializeStorage();
logger.info("All users cleared from HomeKit bridge; re-pairing required.");
} catch (InvalidAlgorithmParameterException e) {
logger.warn(
"Failed generating new encryption settings for HomeKit bridge; re-pairing required, but will likely fail.");
}
} else { } else {
logger.debug("deletion of users information was blocked by binding settings"); logger.warn("Deletion of HomeKit users was blocked by addon settings.");
} }
} }

View File

@ -13,6 +13,7 @@
package org.openhab.io.homekit.internal; package org.openhab.io.homekit.internal;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -30,6 +31,7 @@ import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import io.github.hapjava.accessories.HomekitAccessory;
import io.github.hapjava.services.Service; import io.github.hapjava.services.Service;
/** /**
@ -51,6 +53,9 @@ public class HomekitCommandExtension extends AbstractConsoleCommandExtension {
SUBCMD_ALLOW_UNAUTHENTICATED, SUBCMD_PRUNE_DUMMY_ACCESSORIES, SUBCMD_LIST_DUMMY_ACCESSORIES), SUBCMD_ALLOW_UNAUTHENTICATED, SUBCMD_PRUNE_DUMMY_ACCESSORIES, SUBCMD_LIST_DUMMY_ACCESSORIES),
false); false);
private static final String PARAM_INSTANCE = "--instance";
private static final String PARAM_INSTANCE_HELP = " [--instance <instance id>]";
private class CommandCompleter implements ConsoleCommandCompleter { private class CommandCompleter implements ConsoleCommandCompleter {
public boolean complete(String[] args, int cursorArgumentIndex, int cursorPosition, List<String> candidates) { public boolean complete(String[] args, int cursorArgumentIndex, int cursorPosition, List<String> candidates) {
if (cursorArgumentIndex == 0) { if (cursorArgumentIndex == 0) {
@ -70,37 +75,67 @@ public class HomekitCommandExtension extends AbstractConsoleCommandExtension {
} }
@Override @Override
public void execute(String[] args, Console console) { public void execute(String[] argsArray, Console console) {
if (args.length > 0) { if (argsArray.length > 0) {
String subCommand = args[0]; List<String> args = Arrays.asList(argsArray);
Integer instance = null;
// capture the common instance argument and take it out of args
for (int i = 0; i < args.size() - 1; ++i) {
if (PARAM_INSTANCE.equals(args.get(i))) {
instance = Integer.parseInt(args.get(i + 1));
int instanceCount = homekit.getInstanceCount();
if (instance < 1 || instance > instanceCount) {
console.println("Instance " + args.get(i + 1) + " out of range 1.." + instanceCount);
return;
}
List<String> newArgs = args.subList(0, i);
if (i < args.size() - 2) {
newArgs.addAll(args.subList(i + 2, args.size() - 1));
}
args = newArgs;
break;
}
}
String subCommand = args.get(0);
switch (subCommand) { switch (subCommand) {
case SUBCMD_CLEAR_PAIRINGS: case SUBCMD_CLEAR_PAIRINGS:
clearHomekitPairings(console); if (args.size() != 1) {
console.println("Unknown arguments; not clearing pairings");
} else {
clearHomekitPairings(console, instance);
}
break; break;
case SUBCMD_ALLOW_UNAUTHENTICATED: case SUBCMD_ALLOW_UNAUTHENTICATED:
if (args.length > 1) { if (args.size() > 1) {
boolean allow = Boolean.parseBoolean(args[1]); boolean allow = Boolean.parseBoolean(args.get(1));
allowUnauthenticatedHomekitRequests(allow, console); allowUnauthenticatedHomekitRequests(allow, console);
} else { } else {
console.println("true/false is required as an argument"); console.println("true/false is required as an argument");
} }
break; break;
case SUBCMD_LIST_ACCESSORIES: case SUBCMD_LIST_ACCESSORIES:
listAccessories(console); listAccessories(console, instance);
break; break;
case SUBCMD_PRINT_ACCESSORY: case SUBCMD_PRINT_ACCESSORY:
if (args.length > 1) { if (args.size() > 1) {
printAccessory(args[1], console); printAccessory(args.get(1), console, instance);
} else { } else {
console.println("accessory id or name is required as an argument"); console.println("accessory id or name is required as an argument");
} }
break; break;
case SUBCMD_PRUNE_DUMMY_ACCESSORIES: case SUBCMD_PRUNE_DUMMY_ACCESSORIES:
pruneDummyAccessories(console); if (args.size() != 1) {
console.println("Unknown arguments; not pruning dummy accessories");
} else {
pruneDummyAccessories(console, instance);
}
break; break;
case SUBCMD_LIST_DUMMY_ACCESSORIES: case SUBCMD_LIST_DUMMY_ACCESSORIES:
listDummyAccessories(console); listDummyAccessories(console, instance);
break; break;
default: default:
console.println("Unknown command '" + subCommand + "'"); console.println("Unknown command '" + subCommand + "'");
@ -114,16 +149,19 @@ public class HomekitCommandExtension extends AbstractConsoleCommandExtension {
@Override @Override
public List<String> getUsages() { public List<String> getUsages() {
return Arrays.asList(buildCommandUsage(SUBCMD_LIST_ACCESSORIES, "list all HomeKit accessories"), return Arrays.asList(
buildCommandUsage(SUBCMD_PRINT_ACCESSORY + " <accessory id | accessory name>", buildCommandUsage(SUBCMD_LIST_ACCESSORIES + PARAM_INSTANCE_HELP,
"print additional details of the accessories which partially match provided ID or name."), "list all HomeKit accessories, optionally for a specific instance."),
buildCommandUsage(SUBCMD_CLEAR_PAIRINGS, "removes all pairings with HomeKit clients."), buildCommandUsage(SUBCMD_PRINT_ACCESSORY + PARAM_INSTANCE_HELP + " <accessory id | accessory name>",
"print additional details of the accessories which partially match provided ID or name, optionally searching a specific instance."),
buildCommandUsage(SUBCMD_CLEAR_PAIRINGS + PARAM_INSTANCE_HELP,
"removes all pairings with HomeKit clients, optionally for a specific instance."),
buildCommandUsage(SUBCMD_ALLOW_UNAUTHENTICATED + " <boolean>", buildCommandUsage(SUBCMD_ALLOW_UNAUTHENTICATED + " <boolean>",
"enables or disables unauthenticated access to facilitate debugging"), "enables or disables unauthenticated access to facilitate debugging"),
buildCommandUsage(SUBCMD_PRUNE_DUMMY_ACCESSORIES, buildCommandUsage(SUBCMD_PRUNE_DUMMY_ACCESSORIES + PARAM_INSTANCE_HELP,
"removes dummy accessories whose items no longer exist."), "removes dummy accessories whose items no longer exist, optionally for a specific instance."),
buildCommandUsage(SUBCMD_LIST_DUMMY_ACCESSORIES, buildCommandUsage(SUBCMD_LIST_DUMMY_ACCESSORIES + PARAM_INSTANCE_HELP,
"list dummy accessories whose items no longer exist.")); "list dummy accessories whose items no longer exist, optionally for a specific instance."));
} }
@Reference @Reference
@ -136,9 +174,14 @@ public class HomekitCommandExtension extends AbstractConsoleCommandExtension {
return new CommandCompleter(); return new CommandCompleter();
} }
private void clearHomekitPairings(Console console) { private void clearHomekitPairings(Console console, @Nullable Integer instance) {
homekit.clearHomekitPairings(); if (instance != null) {
console.println("Cleared HomeKit pairings"); homekit.clearHomekitPairings(instance);
console.println("Cleared HomeKit pairings for instance " + instance);
} else {
homekit.clearHomekitPairings();
console.println("Cleared HomeKit pairings");
}
} }
private void allowUnauthenticatedHomekitRequests(boolean allow, Console console) { private void allowUnauthenticatedHomekitRequests(boolean allow, Console console) {
@ -146,13 +189,18 @@ public class HomekitCommandExtension extends AbstractConsoleCommandExtension {
console.println((allow ? "Enabled " : "Disabled ") + "unauthenticated HomeKit access"); console.println((allow ? "Enabled " : "Disabled ") + "unauthenticated HomeKit access");
} }
private void pruneDummyAccessories(Console console) { private void pruneDummyAccessories(Console console, @Nullable Integer instance) {
homekit.pruneDummyAccessories(); if (instance != null) {
console.println("Dummy accessories pruned."); homekit.pruneDummyAccessories(instance);
console.println("Dummy accessories pruned for instance " + instance);
} else {
homekit.pruneDummyAccessories();
console.println("Dummy accessories pruned");
}
} }
private void listAccessories(Console console) { private void listAccessories(Console console, @Nullable Integer instance) {
homekit.getAccessories().forEach(v -> { getInstanceAccessories(instance).forEach(v -> {
try { try {
console.println(v.getId() + " " + v.getName().get()); console.println(v.getId() + " " + v.getName().get());
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
@ -161,8 +209,8 @@ public class HomekitCommandExtension extends AbstractConsoleCommandExtension {
}); });
} }
private void listDummyAccessories(Console console) { private void listDummyAccessories(Console console, @Nullable Integer instance) {
homekit.getAccessories().forEach(v -> { getInstanceAccessories(instance).forEach(v -> {
try { try {
if (v instanceof DummyHomekitAccessory) { if (v instanceof DummyHomekitAccessory) {
console.println(v.getSerialNumber().get()); console.println(v.getSerialNumber().get());
@ -191,8 +239,8 @@ public class HomekitCommandExtension extends AbstractConsoleCommandExtension {
service.getLinkedServices().forEach((s) -> printService(console, s, indent + 2)); service.getLinkedServices().forEach((s) -> printService(console, s, indent + 2));
} }
private void printAccessory(String id, Console console) { private void printAccessory(String id, Console console, @Nullable Integer instance) {
homekit.getAccessories().forEach(v -> { getInstanceAccessories(instance).forEach(v -> {
try { try {
if (("" + v.getId()).contains(id) || ((v.getName().get() != null) if (("" + v.getId()).contains(id) || ((v.getName().get() != null)
&& (v.getName().get().toUpperCase().contains(id.toUpperCase())))) { && (v.getName().get().toUpperCase().contains(id.toUpperCase())))) {
@ -206,4 +254,17 @@ public class HomekitCommandExtension extends AbstractConsoleCommandExtension {
} }
}); });
} }
/**
* Get in-scope accessories
*
* @param instance if null, means all accessories from all instances
*/
private Collection<HomekitAccessory> getInstanceAccessories(@Nullable Integer instance) {
if (instance != null) {
return homekit.getAccessories(instance);
} else {
return homekit.getAccessories();
}
}
} }

View File

@ -17,10 +17,12 @@ import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary; import java.util.Dictionary;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import javax.jmdns.JmDNS; import javax.jmdns.JmDNS;
@ -96,8 +98,7 @@ public class HomekitImpl implements Homekit, NetworkAddressChangeListener, Ready
public HomekitImpl(@Reference StorageService storageService, @Reference ItemRegistry itemRegistry, public HomekitImpl(@Reference StorageService storageService, @Reference ItemRegistry itemRegistry,
@Reference NetworkAddressService networkAddressService, @Reference MetadataRegistry metadataRegistry, @Reference NetworkAddressService networkAddressService, @Reference MetadataRegistry metadataRegistry,
@Reference ConfigurationAdmin configAdmin, @Reference MDNSClient mdnsClient, @Reference ConfigurationAdmin configAdmin, @Reference MDNSClient mdnsClient,
@Reference ReadyService readyService, Map<String, Object> properties) @Reference ReadyService readyService, Map<String, Object> properties) {
throws IOException, InvalidAlgorithmParameterException {
this.storageService = storageService; this.storageService = storageService;
this.networkAddressService = networkAddressService; this.networkAddressService = networkAddressService;
this.configAdmin = configAdmin; this.configAdmin = configAdmin;
@ -160,14 +161,29 @@ public class HomekitImpl implements Homekit, NetworkAddressChangeListener, Ready
|| !oldSettings.setupId.equals(settings.setupId) || !oldSettings.setupId.equals(settings.setupId)
|| (oldSettings.networkInterface != null || (oldSettings.networkInterface != null
&& !oldSettings.networkInterface.equals(settings.networkInterface)) && !oldSettings.networkInterface.equals(settings.networkInterface))
|| oldSettings.port != settings.port || oldSettings.useOHmDNS != settings.useOHmDNS || oldSettings.port != settings.port || oldSettings.useOHmDNS != settings.useOHmDNS) {
|| oldSettings.instances != settings.instances) {
// the HomeKit server settings changed. we do a complete re-init // the HomeKit server settings changed. we do a complete re-init
networkInterface = null;
// Clear out pairing info for instances that have been removed
for (int i = oldSettings.instances - 1; i >= settings.instances; --i) {
clearStorage(i);
}
stopHomekitServer(); stopHomekitServer();
if (currentStartLevel >= StartLevelService.STARTLEVEL_STATES) { if (currentStartLevel >= StartLevelService.STARTLEVEL_STATES) {
startHomekitServer(); startHomekitServer();
} }
} else { } else {
// Stop removed instances
for (int i = oldSettings.instances - 1; i >= settings.instances; --i) {
clearStorage(i);
stopHomekitServer(i);
}
// Start up new instances
for (int i = oldSettings.instances; i < settings.instances; ++i) {
startHomekitServer(i);
}
// Notify remaining instances of the change
for (HomekitChangeListener changeListener : changeListeners) { for (HomekitChangeListener changeListener : changeListeners) {
changeListener.updateSettings(settings); changeListener.updateSettings(settings);
} }
@ -212,60 +228,74 @@ public class HomekitImpl implements Homekit, NetworkAddressChangeListener, Ready
return bridge; return bridge;
} }
private void startHomekitServer() throws IOException, InvalidAlgorithmParameterException { private void startHomekitServer(int instance) throws IOException, InvalidAlgorithmParameterException {
logger.trace("start HomeKit bridge"); logger.trace("starting HomeKit bridge instance {}", instance + 1);
if (homekitServers.isEmpty()) {
try { InetAddress localNetworkInterface = ensureNetworkInterface();
networkInterface = InetAddress
.getByName(((settings.networkInterface != null) && (!settings.networkInterface.isEmpty())) String storageKey = HomekitAuthInfoImpl.STORAGE_KEY;
? settings.networkInterface if (instance != 0) {
: networkAddressService.getPrimaryIpv4HostAddress()); storageKey += instance;
} catch (UnknownHostException e) { }
logger.warn("cannot resolve the Pv4 address / hostname {}.", Storage<Object> storage = storageService.getStorage(storageKey);
networkAddressService.getPrimaryIpv4HostAddress()); HomekitAuthInfoImpl authInfo = new HomekitAuthInfoImpl(storage, settings.pin, settings.setupId,
settings.blockUserDeletion);
@Nullable
HomekitServer homekitServer = null;
if (settings.useOHmDNS) {
for (JmDNS mdns : mdnsClient.getClientInstances()) {
if (mdns.getInetAddress().equals(localNetworkInterface)) {
logger.trace("suitable mDNS client for IP {} found and will be used for HomeKit",
localNetworkInterface);
homekitServer = new HomekitServer(mdns, settings.port + instance);
}
} }
}
if (homekitServer == null) {
if (settings.useOHmDNS) {
logger.trace("no suitable mDNS server for IP {} found", localNetworkInterface);
}
logger.trace("create HomeKit server with dedicated mDNS server");
homekitServer = new HomekitServer(localNetworkInterface, settings.port + instance);
}
homekitServers.add(homekitServer);
HomekitChangeListener changeListener = new HomekitChangeListener(itemRegistry, settings, metadataRegistry,
storage, instance + 1);
changeListeners.add(changeListener);
startBridge(homekitServer, authInfo, changeListener, instance + 1);
authInfos.add(authInfo);
}
private void startHomekitServer() throws IOException, InvalidAlgorithmParameterException {
if (homekitServers.isEmpty()) {
for (int i = 0; i < settings.instances; ++i) { for (int i = 0; i < settings.instances; ++i) {
String storage_key = HomekitAuthInfoImpl.STORAGE_KEY; startHomekitServer(i);
if (i != 0) {
storage_key += i;
}
Storage<Object> storage = storageService.getStorage(storage_key);
HomekitAuthInfoImpl authInfo = new HomekitAuthInfoImpl(storage, settings.pin, settings.setupId,
settings.blockUserDeletion);
@Nullable
HomekitServer homekitServer = null;
if (settings.useOHmDNS) {
for (JmDNS mdns : mdnsClient.getClientInstances()) {
if (mdns.getInetAddress().equals(networkInterface)) {
logger.trace("suitable mDNS client for IP {} found and will be used for HomeKit",
networkInterface);
homekitServer = new HomekitServer(mdns, settings.port + i);
}
}
}
if (homekitServer == null) {
if (settings.useOHmDNS) {
logger.trace("no suitable mDNS server for IP {} found", networkInterface);
}
logger.trace("create HomeKit server with dedicated mDNS server");
homekitServer = new HomekitServer(networkInterface, settings.port + i);
}
homekitServers.add(homekitServer);
HomekitChangeListener changeListener = new HomekitChangeListener(itemRegistry, settings,
metadataRegistry, storage, i + 1);
changeListeners.add(changeListener);
bridges.add(startBridge(homekitServer, authInfo, changeListener, i + 1));
authInfos.add(authInfo);
} }
} else { } else {
logger.warn("trying to start HomeKit server but it is already initialized"); logger.warn("trying to start HomeKit server but it is already initialized");
} }
} }
private InetAddress ensureNetworkInterface() throws IOException {
InetAddress localNetworkInterface = networkInterface;
if (localNetworkInterface != null) {
return localNetworkInterface;
}
String interfaceName = ((settings.networkInterface != null) && (!settings.networkInterface.isEmpty()))
? settings.networkInterface
: networkAddressService.getPrimaryIpv4HostAddress();
try {
return (networkInterface = Objects.requireNonNull(InetAddress.getByName(interfaceName)));
} catch (UnknownHostException e) {
logger.warn("cannot resolve the IPv4 address / hostname {}.", interfaceName);
throw e;
}
}
private void stopHomekitServer() { private void stopHomekitServer() {
logger.trace("stop HomeKit bridge"); logger.trace("stopping HomeKit bridge");
changeListeners.parallelStream().forEach(HomekitChangeListener::stop); changeListeners.parallelStream().forEach(HomekitChangeListener::stop);
bridges.parallelStream().forEach(HomekitRoot::stop); bridges.parallelStream().forEach(HomekitRoot::stop);
homekitServers.parallelStream().forEach(HomekitServer::stop); homekitServers.parallelStream().forEach(HomekitServer::stop);
@ -275,6 +305,26 @@ public class HomekitImpl implements Homekit, NetworkAddressChangeListener, Ready
authInfos.clear(); authInfos.clear();
} }
private void stopHomekitServer(int instance) {
logger.trace("stopping HomeKit bridge instance {}", instance + 1);
changeListeners.get(instance).stop();
bridges.get(instance).stop();
homekitServers.get(instance).stop();
changeListeners.remove(instance);
bridges.remove(instance);
homekitServers.remove(instance);
authInfos.remove(instance);
}
private void clearStorage(int index) {
String storageKey = HomekitAuthInfoImpl.STORAGE_KEY;
if (index != 0) {
storageKey += index;
}
Storage<Object> storage = storageService.getStorage(storageKey);
storage.getKeys().forEach(k -> storage.remove(k));
}
@Deactivate @Deactivate
protected void deactivate() { protected void deactivate() {
networkAddressService.removeNetworkAddressChangeListener(this); networkAddressService.removeNetworkAddressChangeListener(this);
@ -296,7 +346,7 @@ public class HomekitImpl implements Homekit, NetworkAddressChangeListener, Ready
} }
@Override @Override
public List<HomekitAccessory> getAccessories() { public Collection<HomekitAccessory> getAccessories() {
List<HomekitAccessory> accessories = new ArrayList<>(); List<HomekitAccessory> accessories = new ArrayList<>();
for (HomekitChangeListener changeListener : changeListeners) { for (HomekitChangeListener changeListener : changeListeners) {
accessories.addAll(changeListener.getAccessories().values()); accessories.addAll(changeListener.getAccessories().values());
@ -304,13 +354,33 @@ public class HomekitImpl implements Homekit, NetworkAddressChangeListener, Ready
return accessories; return accessories;
} }
@Override
public Collection<HomekitAccessory> getAccessories(int instance) {
if (instance < 1 || instance > changeListeners.size()) {
logger.warn("Instance {} is out of range 1..{}.", instance, changeListeners.size());
return List.of();
}
return changeListeners.get(instance - 1).getAccessories().values();
}
@Override @Override
public void clearHomekitPairings() { public void clearHomekitPairings() {
for (int i = 1; i <= authInfos.size(); ++i) {
clearHomekitPairings(i);
}
}
@Override
public void clearHomekitPairings(int instance) {
if (instance < 1 || instance > authInfos.size()) {
logger.warn("Instance {} is out of range 1..{}.", instance, authInfos.size());
return;
}
try { try {
for (HomekitAuthInfoImpl authInfo : authInfos) { authInfos.get(instance - 1).clear();
authInfo.clear(); bridges.get(instance - 1).refreshAuthInfo();
}
refreshAuthInfo();
} catch (Exception e) { } catch (Exception e) {
logger.warn("could not clear HomeKit pairings", e); logger.warn("could not clear HomeKit pairings", e);
} }
@ -323,6 +393,21 @@ public class HomekitImpl implements Homekit, NetworkAddressChangeListener, Ready
} }
} }
@Override
public void pruneDummyAccessories(int instance) {
if (instance < 1 || instance > authInfos.size()) {
logger.warn("Instance {} is out of range 1..{}.", instance, authInfos.size());
return;
}
changeListeners.get(instance - 1).pruneDummyAccessories();
}
@Override
public int getInstanceCount() {
return homekitServers.size();
}
@Override @Override
public synchronized void onChanged(final List<CidrAddress> added, final List<CidrAddress> removed) { public synchronized void onChanged(final List<CidrAddress> added, final List<CidrAddress> removed) {
logger.trace("HomeKit bridge reacting on network interface changes."); logger.trace("HomeKit bridge reacting on network interface changes.");