[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:
parent
f082df923f
commit
47f5489d70
@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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.");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user