Skip to content

Commit

Permalink
[homekit] some cleanups (openhab#8041)
Browse files Browse the repository at this point in the history
* some cleanups

Signed-off-by: Eugen Freiter <freiter@gmx.de>
  • Loading branch information
yfre authored and andrewfg committed Aug 31, 2020
1 parent 53987f6 commit 66c3980
Show file tree
Hide file tree
Showing 39 changed files with 412 additions and 479 deletions.
5 changes: 2 additions & 3 deletions bundles/org.openhab.io.homekit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -439,10 +439,9 @@ The HomeKit valve accessory supports following 2 optional characteristics:

- duration: this describes how long the valve should set "InUse" once it is activated. The duration changes will apply to the next operation. If valve is already active then duration changes have no effect.

- remaining duration: this describes the remaining duration on the valve. Notifications on this characteristic must only
be used if the remaining duration increases/decreases from the accessoryʼs usual countdown of remaining duration.
- remaining duration: this describes the remaining duration on the valve. Notifications on this characteristic must only be used if the remaining duration increases/decreases from the accessoryʼs usual countdown of remaining duration.

Upon valve activation in home app, home app starts to count down from the "duration" to "0" without contacting the server. Home app also does not trigger any acion if it remaining duration get 0.
Upon valve activation in home app, home app starts to count down from the "duration" to "0" without contacting the server. Home app also does not trigger any action if it remaining duration get 0.
It is up to valve to have an own timer and stop valve once the timer is over.
Some valves have such timer, e.g. pretty common for sprinklers.
In case the valve has no timer capability, OpenHAB can take care on this - start an internal timer and send "Off" command to the valve once the timer is over.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ public interface Homekit {
* Refreshes the saved authentication info from the underlying storage service. If you
* make changes to the saved authentication info, call this.
*
* @throws IOException
* @throws IOException exception in case new auth info could not be published via mDNS
*/
public void refreshAuthInfo() throws IOException;
void refreshAuthInfo() throws IOException;

/**
* HomeKit requests normally require authentication via the pairing mechanism. Use this
Expand All @@ -52,5 +52,5 @@ public interface Homekit {
/**
* clear all pairings with HomeKit clients
*/
public void clearHomekitPairings();
void clearHomekitPairings();
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class Debouncer {
* @param scheduler The scheduler implementation to use
* @param delay The time after which to invoke action; each time [[Debouncer.call]] is invoked, this delay is
* reset
* @param Clock The source from which we get the current time. This input should use the same source. Specified
* @param clock The source from which we get the current time. This input should use the same source. Specified
* for testing purposes
* @param action The action to invoke
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
* @author Andy Lintner - Initial contribution
*/
class HomekitAccessoryRegistry {

private @Nullable HomekitRoot bridge;
private final Map<String, HomekitAccessory> createdAccessories = new HashMap<>();
private int configurationRevision = 1;
Expand Down Expand Up @@ -98,7 +97,6 @@ public synchronized void addRootAccessory(String itemName, HomekitAccessory acce
if (bridge != null) {
bridge.addAccessory(accessory);
}
logger.trace("Added accessory {}", accessory.getId());
}

public Map<String, HomekitAccessory> getAllAccessories() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.smarthome.core.items.GenericItem;
import org.eclipse.smarthome.core.items.Item;
import org.eclipse.smarthome.core.items.StateChangeListener;
Expand All @@ -33,7 +34,7 @@
* @author Andy Lintner - Initial contribution
*/
public class HomekitAccessoryUpdater {
private Logger logger = LoggerFactory.getLogger(HomekitAccessoryUpdater.class);
private final Logger logger = LoggerFactory.getLogger(HomekitAccessoryUpdater.class);
private final ConcurrentMap<ItemKey, Subscription> subscriptionsByName = new ConcurrentHashMap<>();

public void subscribe(GenericItem item, HomekitCharacteristicChangeCallback callback) {
Expand Down Expand Up @@ -78,7 +79,8 @@ public void unsubscribe(GenericItem item, String key) {
}

@FunctionalInterface
private static interface Subscription extends StateChangeListener {
@NonNullByDefault
private interface Subscription extends StateChangeListener {

@Override
void stateChanged(Item item, State oldState, State newState);
Expand All @@ -90,8 +92,8 @@ default void stateUpdated(Item item, State state) {
}

private static class ItemKey {
public GenericItem item;
public String key;
public final GenericItem item;
public final String key;

public ItemKey(GenericItem item, String key) {
this.item = item;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,27 @@
*/
public class HomekitAuthInfoImpl implements HomekitAuthInfo {
private final Logger logger = LoggerFactory.getLogger(HomekitAuthInfoImpl.class);
public static final String STORAGE_KEY = "homekit";
private static final String STORAGE_MAC = "mac";
private static final String STORAGE_SALT = "salt";
private static final String STORAGE_PRIVATE_KEY = "privateKey";
private static final String STORAGE_USER_PREFIX = "user_";

private final Storage<String> storage;
private String mac;
private BigInteger salt;
private byte[] privateKey;
private final String pin;

public HomekitAuthInfoImpl(final Storage storage, final String pin) throws InvalidAlgorithmParameterException {
public HomekitAuthInfoImpl(Storage<String> storage, String pin) throws InvalidAlgorithmParameterException {
this.storage = storage;
this.pin = pin;
initializeStorage();
}

@Override
public void createUser(String username, byte[] publicKey) {
logger.trace("Create user {}", username);
storage.put(createUserKey(username), Base64.getEncoder().encodeToString(publicKey));
}

Expand Down Expand Up @@ -84,54 +90,53 @@ public byte[] getUserPublicKey(String username) {

@Override
public void removeUser(String username) {
logger.trace("Remove user {}", username);
storage.remove(createUserKey(username));
}

@Override
public boolean hasUser() {
Collection<String> keys = storage.getKeys();
return keys.stream().filter(k -> isUserKey(k)).count() > 0;
return keys.stream().anyMatch(this::isUserKey);
}

public void clear() {
logger.trace("Clear all users");
for (String key : new HashSet<>(storage.getKeys())) {
if (isUserKey(key)) {
storage.remove(key);
}
}
}

private String createUserKey(final String username) {
return "user_" + username;
private String createUserKey(String username) {
return STORAGE_USER_PREFIX + username;
}

private boolean isUserKey(final String key) {
return key.startsWith("user_");
private boolean isUserKey(String key) {
return key.startsWith(STORAGE_USER_PREFIX);
}

private void initializeStorage() throws InvalidAlgorithmParameterException {
mac = storage.get("mac");
@Nullable
Object saltConfig = storage.get("salt");
@Nullable
Object privateKeyConfig = storage.get("privateKey");

mac = storage.get(STORAGE_MAC);
final @Nullable Object saltConfig = storage.get(STORAGE_SALT);
final @Nullable Object privateKeyConfig = storage.get(STORAGE_PRIVATE_KEY);
if (mac == null) {
logger.warn(
"Could not find existing MAC in {}. Generating new MAC. This will require re-pairing of iOS devices.",
storage.getClass().getName());
mac = HomekitServer.generateMac();
storage.put("mac", mac);
storage.put(STORAGE_MAC, mac);
}
if (saltConfig == null) {
salt = HomekitServer.generateSalt();
storage.put("salt", salt.toString());
storage.put(STORAGE_SALT, salt.toString());
} else {
salt = new BigInteger(saltConfig.toString());
}
if (privateKeyConfig == null) {
privateKey = HomekitServer.generateKey();
storage.put("privateKey", Base64.getEncoder().encodeToString(privateKey));
storage.put(STORAGE_PRIVATE_KEY, Base64.getEncoder().encodeToString(privateKey));
} else {
privateKey = Base64.getDecoder().decode(privateKeyConfig.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
import java.time.Duration;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.smarthome.core.common.ThreadPoolManager;
import org.eclipse.smarthome.core.items.GroupItem;
import org.eclipse.smarthome.core.items.Item;
Expand All @@ -46,6 +46,7 @@
*
* @author Andy Lintner - Initial contribution
*/
@NonNullByDefault
public class HomekitChangeListener implements ItemRegistryChangeListener {
private final Logger logger = LoggerFactory.getLogger(HomekitChangeListener.class);
private final static String REVISION_CONFIG = "revision";
Expand All @@ -58,7 +59,7 @@ public class HomekitChangeListener implements ItemRegistryChangeListener {
private HomekitSettings settings;
private int lastAccessoryCount;

private Set<String> pendingUpdates = new HashSet<>();
private final Set<String> pendingUpdates = new HashSet<>();

private final ScheduledExecutorService scheduler = ThreadPoolManager
.getScheduledPool(ThreadPoolManager.THREAD_POOL_NAME_COMMON);
Expand All @@ -74,31 +75,31 @@ public class HomekitChangeListener implements ItemRegistryChangeListener {
private final Debouncer applyUpdatesDebouncer;

HomekitChangeListener(ItemRegistry itemRegistry, HomekitSettings settings, MetadataRegistry metadataRegistry,
final StorageService storageService) {
StorageService storageService) {
this.itemRegistry = itemRegistry;
this.settings = settings;
this.metadataRegistry = metadataRegistry;
storage = storageService.getStorage("homekit");
storage = storageService.getStorage(HomekitAuthInfoImpl.STORAGE_KEY);
this.applyUpdatesDebouncer = new Debouncer("update-homekit-devices", scheduler, Duration.ofMillis(1000),
Clock.systemUTC(), this::applyUpdates);

itemRegistry.addRegistryChangeListener(this);
itemRegistry.getItems().stream().forEach(this::createRootAccessories);
itemRegistry.getItems().forEach(this::createRootAccessories);
initialiseRevision();
logger.info("Created {} HomeKit items.", accessoryRegistry.getAllAccessories().size());
}

private void initialiseRevision() {
int revision;
try {
revision = Integer.valueOf(storage.get(REVISION_CONFIG));
} catch (java.lang.NumberFormatException e) {
revision = Integer.parseInt(storage.get(REVISION_CONFIG));
} catch (NumberFormatException e) {
revision = 1;
storage.put(REVISION_CONFIG, "" + revision);
}
try {
lastAccessoryCount = Integer.valueOf(storage.get(ACCESSORY_COUNT));
} catch (java.lang.NumberFormatException e) {
lastAccessoryCount = Integer.parseInt(storage.get(ACCESSORY_COUNT));
} catch (NumberFormatException e) {
lastAccessoryCount = 0;
storage.put(ACCESSORY_COUNT, "" + accessoryRegistry.getAllAccessories().size());
}
Expand All @@ -122,7 +123,7 @@ public void allItemsChanged(Collection<String> oldItemNames) {
* @param item The item that has been changed or removed.
*/
private synchronized void markDirty(Item item) {
logger.trace("Mark dirty item {}", item.getLabel());
logger.trace("Mark dirty item {}", item.getName());
pendingUpdates.add(item.getName());
/*
* If findMyAccessoryGroups fails because the accessory group has already been deleted, then we can count on a
Expand All @@ -131,7 +132,6 @@ private synchronized void markDirty(Item item) {
for (Item accessoryGroup : HomekitAccessoryFactory.getAccessoryGroups(item, itemRegistry, metadataRegistry)) {
pendingUpdates.add(accessoryGroup.getName());
}

applyUpdatesDebouncer.call();
}

Expand Down Expand Up @@ -159,10 +159,7 @@ public void makeNewConfigurationRevision() {

private synchronized void applyUpdates() {
logger.trace("apply updates");
Iterator<String> iter = pendingUpdates.iterator();

while (iter.hasNext()) {
String name = iter.next();
for (final String name : pendingUpdates) {
accessoryRegistry.remove(name);
logger.trace(" add items {}", name);
getItemOptional(name).ifPresent(this::createRootAccessories);
Expand Down Expand Up @@ -220,26 +217,23 @@ public int getConfigurationRevision() {
* creates one or more HomeKit items for given openhab item.
* one openhab item can linked to several HomeKit accessories or characteristics.
*
* @param item
* @param item openhab item
*/
private void createRootAccessories(Item item) {
logger.trace("create root accessory {}", item.getLabel());
final List<Entry<HomekitAccessoryType, HomekitCharacteristicType>> accessoryTypes = HomekitAccessoryFactory
.getAccessoryTypes(item, metadataRegistry);
final List<GroupItem> groups = HomekitAccessoryFactory.getAccessoryGroups(item, itemRegistry, metadataRegistry);
if (!accessoryTypes.isEmpty() && groups.stream().noneMatch(g -> g.getBaseItem() != null)) {
// it has homekit accessory type and is not part of bigger homekit group item without baseItem, i.e. not
// Group:Switch
logger.trace("Item {} is a HomeKit accessory of types {}", item.getName(), accessoryTypes);
final HomekitOHItemProxy itemProxy = new HomekitOHItemProxy(item);
accessoryTypes.stream().forEach(rootAccessory -> createRootAccessory(new HomekitTaggedItem(itemProxy,
accessoryTypes.forEach(rootAccessory -> createRootAccessory(new HomekitTaggedItem(itemProxy,
rootAccessory.getKey(), HomekitAccessoryFactory.getItemConfiguration(item, metadataRegistry))));
}
}

private void createRootAccessory(HomekitTaggedItem taggedItem) {
try {
logger.trace("Adding HomeKit device {}", taggedItem.getItem().getUID());
accessoryRegistry.addRootAccessory(taggedItem.getName(),
HomekitAccessoryFactory.create(taggedItem, metadataRegistry, updater, settings));
} catch (HomekitException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.List;
import java.util.concurrent.ExecutionException;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.smarthome.io.console.Console;
import org.eclipse.smarthome.io.console.extensions.AbstractConsoleCommandExtension;
import org.eclipse.smarthome.io.console.extensions.ConsoleCommandExtension;
Expand All @@ -31,6 +32,7 @@
* @author Andy Lintner - Initial contribution
*/
@Component(service = ConsoleCommandExtension.class)
@NonNullByDefault
public class HomekitCommandExtension extends AbstractConsoleCommandExtension {
private static final String SUBCMD_CLEAR_PAIRINGS = "clearPairings";
private static final String SUBCMD_LIST_ACCESSORIES = "list";
Expand All @@ -42,7 +44,8 @@ public class HomekitCommandExtension extends AbstractConsoleCommandExtension {
private static final String LEGACY_SUBCMD_PRINT_ACCESSORY = "printAccessory";

private final Logger logger = LoggerFactory.getLogger(HomekitCommandExtension.class);
private Homekit homekit;

private @NonNullByDefault({}) Homekit homekit;

public HomekitCommandExtension() {
super("homekit", "Interact with the HomeKit integration.");
Expand All @@ -59,7 +62,7 @@ public void execute(String[] args, Console console) {

case SUBCMD_ALLOW_UNAUTHENTICATED:
if (args.length > 1) {
boolean allow = Boolean.valueOf(args[1]);
boolean allow = Boolean.parseBoolean(args[1]);
allowUnauthenticatedHomekitRequests(allow, console);
} else {
console.println("true/false is required as an argument");
Expand Down Expand Up @@ -110,10 +113,6 @@ public void setHomekit(Homekit homekit) {
this.homekit = homekit;
}

public void unsetHomekit(Homekit homekit) {
this.homekit = null;
}

private void clearHomekitPairings(Console console) {
homekit.clearHomekitPairings();
console.println("Cleared HomeKit pairings");
Expand All @@ -125,7 +124,7 @@ private void allowUnauthenticatedHomekitRequests(boolean allow, Console console)
}

private void listAccessories(Console console) {
homekit.getAccessories().stream().forEach(v -> {
homekit.getAccessories().forEach(v -> {
try {
console.println(v.getId() + " " + v.getName().get());
} catch (InterruptedException | ExecutionException e) {
Expand All @@ -144,9 +143,7 @@ private void printAccessory(String id, Console console) {
v.getServices().forEach(s -> {
console.println(" Service Type: " + s.getType());
console.println(" Characteristics: ");
s.getCharacteristics().forEach(c -> {
console.println(" : " + c.getClass());
});
s.getCharacteristics().forEach(c -> console.println(" : " + c.getClass()));
});
console.println("");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ public enum HomekitCommandType {
HUE_COMMAND,
SATURATION_COMMAND,
BRIGHTNESS_COMMAND,
ON_COMMAND;
ON_COMMAND
}
Loading

0 comments on commit 66c3980

Please sign in to comment.