diff --git a/bundles/org.openhab.io.homekit/README.md b/bundles/org.openhab.io.homekit/README.md
index 6567024c203..6cc248fe47f 100644
--- a/bundles/org.openhab.io.homekit/README.md
+++ b/bundles/org.openhab.io.homekit/README.md
@@ -34,6 +34,40 @@ HomeKit integration supports following accessory types:
- Carbon Dioxide Sensor
- Carbon Monoxide Sensor
+## Quick start
+
+- install homekit binding via UI
+
+- add metadata to an existing item (see [UI based configuration](#UI-based-Configuration))
+
+- go to scan QR code from UI->Setting-HomeKit Integration
+
+ ![settings_qrcode.png](doc/settings_qrcode.png)
+
+- open home app on your iPhone or iPad
+- create new home
+
+ ![ios_add_new_home.png](doc/ios_add_new_home.png)
+
+- add accessory
+
+ ![ios_add_accessory.png](doc/ios_add_accessory.png)
+
+- scan QR code from UI->Setting-HomeKit Integration
+
+ ![ios_scan_qrcode.png](doc/ios_scan_qrcode.png)
+
+- click "Add Anyway"
+
+ ![ios_add_anyway.png](doc/ios_add_anyway.png)
+
+- follow the instruction of the home app wizard
+
+ ![ios_add_accessory_wizard.png](doc/ios_add_accessory_wizard.png)
+
+Add metadata to more item or fine-tune your configuration using further settings
+
+
## Global Configuration
Your first step will be to create the `homekit.cfg` in your `$OPENHAB_CONF/services` folder.
@@ -93,8 +127,30 @@ Complex accessories require a tag on a Group Item indicating the accessory type,
A HomeKit accessory has mandatory and optional characteristics (listed below in the table).
The mapping between openHAB items and HomeKit accessory and characteristics is done by means of [metadata](https://www.openhab.org/docs/concepts/items.html#item-metadata)
-e.g.
+### UI based Configuration
+In order to add metadata to an item:
+- select desired item in mainUI
+- click on "Add Metadata"
+
+ ![item_add_metadata_button.png](doc/item_add_metadata_button.png)
+
+- select "Apple HomeKit" namespace
+
+ ![select_homekit_namespace.png](doc/select_homekit_namespace.png)
+
+- click on "HomeKit Accessory/Characteristic"
+
+ ![add_homekit_tag.png](doc/add_homekit_tag.png)
+
+- select required HomeKit accessory type or characteristic
+
+ ![select_homekit_accessory_type.png](doc/select_homekit_accessory_type.png)
+
+- click on "Save"
+
+
+### Textual configuration
```xtend
Switch leaksensor_metadata "Leak Sensor" {homekit="LeakSensor"}
```
diff --git a/bundles/org.openhab.io.homekit/doc/add_homekit_tag.png b/bundles/org.openhab.io.homekit/doc/add_homekit_tag.png
new file mode 100644
index 00000000000..01e632c195d
Binary files /dev/null and b/bundles/org.openhab.io.homekit/doc/add_homekit_tag.png differ
diff --git a/bundles/org.openhab.io.homekit/doc/ios_add_accessory.png b/bundles/org.openhab.io.homekit/doc/ios_add_accessory.png
new file mode 100644
index 00000000000..3d157d3fd72
Binary files /dev/null and b/bundles/org.openhab.io.homekit/doc/ios_add_accessory.png differ
diff --git a/bundles/org.openhab.io.homekit/doc/ios_add_accessory_wizard.png b/bundles/org.openhab.io.homekit/doc/ios_add_accessory_wizard.png
new file mode 100644
index 00000000000..7aa391c5090
Binary files /dev/null and b/bundles/org.openhab.io.homekit/doc/ios_add_accessory_wizard.png differ
diff --git a/bundles/org.openhab.io.homekit/doc/ios_add_anyway.png b/bundles/org.openhab.io.homekit/doc/ios_add_anyway.png
new file mode 100644
index 00000000000..5c3f9d4749f
Binary files /dev/null and b/bundles/org.openhab.io.homekit/doc/ios_add_anyway.png differ
diff --git a/bundles/org.openhab.io.homekit/doc/ios_add_new_home.png b/bundles/org.openhab.io.homekit/doc/ios_add_new_home.png
new file mode 100644
index 00000000000..4a25a05ae01
Binary files /dev/null and b/bundles/org.openhab.io.homekit/doc/ios_add_new_home.png differ
diff --git a/bundles/org.openhab.io.homekit/doc/ios_scan_qrcode.png b/bundles/org.openhab.io.homekit/doc/ios_scan_qrcode.png
new file mode 100644
index 00000000000..8ee9913a850
Binary files /dev/null and b/bundles/org.openhab.io.homekit/doc/ios_scan_qrcode.png differ
diff --git a/bundles/org.openhab.io.homekit/doc/item_add_metadata_button.png b/bundles/org.openhab.io.homekit/doc/item_add_metadata_button.png
new file mode 100644
index 00000000000..d99ceb65915
Binary files /dev/null and b/bundles/org.openhab.io.homekit/doc/item_add_metadata_button.png differ
diff --git a/bundles/org.openhab.io.homekit/doc/select_homekit_accessory_type.png b/bundles/org.openhab.io.homekit/doc/select_homekit_accessory_type.png
new file mode 100644
index 00000000000..0ecf80f339b
Binary files /dev/null and b/bundles/org.openhab.io.homekit/doc/select_homekit_accessory_type.png differ
diff --git a/bundles/org.openhab.io.homekit/doc/select_homekit_namespace.png b/bundles/org.openhab.io.homekit/doc/select_homekit_namespace.png
new file mode 100644
index 00000000000..dceba171fb4
Binary files /dev/null and b/bundles/org.openhab.io.homekit/doc/select_homekit_namespace.png differ
diff --git a/bundles/org.openhab.io.homekit/doc/settings_qrcode.png b/bundles/org.openhab.io.homekit/doc/settings_qrcode.png
new file mode 100644
index 00000000000..4aecccfa324
Binary files /dev/null and b/bundles/org.openhab.io.homekit/doc/settings_qrcode.png differ
diff --git a/bundles/org.openhab.io.homekit/pom.xml b/bundles/org.openhab.io.homekit/pom.xml
index a93b9890ce2..f13eb8b741f 100644
--- a/bundles/org.openhab.io.homekit/pom.xml
+++ b/bundles/org.openhab.io.homekit/pom.xml
@@ -23,7 +23,7 @@
com.github.j-n-k
hap-java
- 2.0.0.OH2
+ 2.1.0.OH
compile
diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAuthInfoImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAuthInfoImpl.java
index 54f94e5a756..234a25c086d 100644
--- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAuthInfoImpl.java
+++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitAuthInfoImpl.java
@@ -44,11 +44,14 @@ public class HomekitAuthInfoImpl implements HomekitAuthInfo {
private String mac;
private BigInteger salt;
private byte[] privateKey;
- private final String pin;
+ private String pin;
+ private String setupId;
- public HomekitAuthInfoImpl(Storage storage, String pin) throws InvalidAlgorithmParameterException {
+ public HomekitAuthInfoImpl(Storage storage, String pin, String setupId)
+ throws InvalidAlgorithmParameterException {
this.storage = storage;
this.pin = pin;
+ this.setupId = setupId;
initializeStorage();
}
@@ -63,11 +66,28 @@ public class HomekitAuthInfoImpl implements HomekitAuthInfo {
return mac;
}
+ public void setMac(String mac) {
+ this.mac = mac;
+ }
+
@Override
public String getPin() {
return pin;
}
+ public void setPin(String pin) {
+ this.pin = pin;
+ }
+
+ @Override
+ public String getSetupId() {
+ return setupId;
+ }
+
+ public void setSetupId(String setupId) {
+ this.setupId = setupId;
+ }
+
@Override
public byte[] getPrivateKey() {
return privateKey;
diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitImpl.java
index cf2f2add315..e4dc5c0de79 100644
--- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitImpl.java
+++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitImpl.java
@@ -16,6 +16,8 @@ import java.io.IOException;
import java.net.InetAddress;
import java.security.InvalidAlgorithmParameterException;
import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
@@ -33,6 +35,7 @@ import org.openhab.core.storage.StorageService;
import org.openhab.io.homekit.Homekit;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;
+import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
@@ -44,45 +47,88 @@ import org.slf4j.LoggerFactory;
import io.github.hapjava.accessories.HomekitAccessory;
import io.github.hapjava.server.impl.HomekitRoot;
import io.github.hapjava.server.impl.HomekitServer;
+import io.github.hapjava.server.impl.crypto.HAPSetupCodeUtils;
/**
* Provides access to openHAB items via the HomeKit API
*
* @author Andy Lintner - Initial contribution
*/
-@Component(service = { Homekit.class }, configurationPid = "org.openhab.homekit", property = {
+@Component(service = { Homekit.class }, configurationPid = HomekitSettings.CONFIG_PID, property = {
Constants.SERVICE_PID + "=org.openhab.homekit", "port:Integer=9123" })
@ConfigurableService(category = "io", label = "HomeKit Integration", description_uri = "io:homekit")
@NonNullByDefault
public class HomekitImpl implements Homekit {
private final Logger logger = LoggerFactory.getLogger(HomekitImpl.class);
- private final NetworkAddressService networkAddressService;
- private final HomekitChangeListener changeListener;
+ private final NetworkAddressService networkAddressService;
+ private final ConfigurationAdmin configAdmin;
+
+ private HomekitAuthInfoImpl authInfo;
private HomekitSettings settings;
private @Nullable InetAddress networkInterface;
private @Nullable HomekitServer homekitServer;
private @Nullable HomekitRoot bridge;
- private final HomekitAuthInfoImpl authInfo;
+ private final HomekitChangeListener changeListener;
private final ScheduledExecutorService scheduler = ThreadPoolManager
.getScheduledPool(ThreadPoolManager.THREAD_POOL_NAME_COMMON);
@Activate
public HomekitImpl(@Reference StorageService storageService, @Reference ItemRegistry itemRegistry,
- @Reference NetworkAddressService networkAddressService, Map config,
- @Reference MetadataRegistry metadataRegistry) throws IOException, InvalidAlgorithmParameterException {
+ @Reference NetworkAddressService networkAddressService, @Reference MetadataRegistry metadataRegistry,
+ @Reference ConfigurationAdmin configAdmin, Map properties)
+ throws IOException, InvalidAlgorithmParameterException {
this.networkAddressService = networkAddressService;
- this.settings = processConfig(config);
+ this.configAdmin = configAdmin;
+ this.settings = processConfig(properties);
this.changeListener = new HomekitChangeListener(itemRegistry, settings, metadataRegistry, storageService);
- authInfo = new HomekitAuthInfoImpl(storageService.getStorage(HomekitAuthInfoImpl.STORAGE_KEY), settings.pin);
- startHomekitServer();
+ try {
+ authInfo = new HomekitAuthInfoImpl(storageService.getStorage(HomekitAuthInfoImpl.STORAGE_KEY), settings.pin,
+ settings.setupId);
+ startHomekitServer();
+ } catch (IOException | InvalidAlgorithmParameterException e) {
+ logger.warn("Cannot activate HomeKit binding. {}", e.getMessage());
+ throw e;
+ }
}
- private HomekitSettings processConfig(Map config) {
- HomekitSettings settings = (new Configuration(config)).as(HomekitSettings.class);
+ private HomekitSettings processConfig(Map properties) {
+ HomekitSettings settings = (new Configuration(properties)).as(HomekitSettings.class);
+ org.osgi.service.cm.Configuration config = null;
+ Dictionary props = null;
+ try {
+ config = configAdmin.getConfiguration(HomekitSettings.CONFIG_PID);
+ props = config.getProperties();
+ } catch (IOException e) {
+ logger.warn("Cannot retrieve config admin {}", e.getMessage());
+ }
+
+ if (props == null) { // if null, the configuration is new
+ props = new Hashtable<>();
+ }
if (settings.networkInterface == null) {
settings.networkInterface = networkAddressService.getPrimaryIpv4HostAddress();
+ props.put("networkInterface", settings.networkInterface);
+ }
+ if (settings.setupId == null) { // generate setupId very first time
+ settings.setupId = HAPSetupCodeUtils.generateSetupId();
+ props.put("setupId", settings.setupId);
+ }
+
+ // QR Code setup URI is always generated from PIN, setup ID and accessory category (1 = bridge)
+ String setupURI = HAPSetupCodeUtils.getSetupURI(settings.pin.replaceAll("-", ""), settings.setupId, 1);
+ if ((settings.qrCode == null) || (!settings.qrCode.equals(setupURI))) { // QR code was changed
+ settings.qrCode = setupURI;
+ props.put("qrCode", settings.qrCode);
+ }
+
+ if (config != null) {
+ try {
+ config.updateIfDifferent(props);
+ } catch (IOException e) {
+ logger.warn("Cannot update configuration {}", e.getMessage());
+ }
}
return settings;
}
@@ -97,10 +143,12 @@ public class HomekitImpl implements Homekit {
// the HomeKit server settings changed. we do a complete re-init
stopHomekitServer();
startHomekitServer();
- } else if (!oldSettings.name.equals(settings.name) || !oldSettings.pin.equals(settings.pin)) {
- // we change the root bridge only
- stopBridge();
- startBridge();
+ } else if (!oldSettings.name.equals(settings.name) || !oldSettings.pin.equals(settings.pin)
+ || !oldSettings.setupId.equals(settings.setupId)) {
+ stopHomekitServer();
+ authInfo.setPin(settings.pin);
+ authInfo.setSetupId(settings.setupId);
+ startHomekitServer();
}
} catch (IOException e) {
logger.warn("Could not initialize HomeKit connector: {}", e.getMessage());
diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitSettings.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitSettings.java
index 26af87843c0..978be779361 100644
--- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitSettings.java
+++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitSettings.java
@@ -18,6 +18,7 @@ package org.openhab.io.homekit.internal;
* @author Andy Lintner - Initial contribution
*/
public class HomekitSettings {
+ public static final String CONFIG_PID = "org.openhab.homekit";
public static final String MANUFACTURER = "openHAB Community";
public static final String SERIAL_NUMBER = "none";
public static final String MODEL = "openHAB";
@@ -26,6 +27,8 @@ public class HomekitSettings {
public String name = "openHAB";
public int port = 9123;
public String pin = "031-45-154";
+ public String setupId;
+ public String qrCode;
public int startDelay = 30;
public boolean useFahrenheitTemperature = false;
public double minimumTemperature = -100;
@@ -56,6 +59,7 @@ public class HomekitSettings {
temp = Double.doubleToLongBits(minimumTemperature);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + ((pin == null) ? 0 : pin.hashCode());
+ result = prime * result + ((setupId == null) ? 0 : setupId.hashCode());
result = prime * result + port;
result = prime * result + ((thermostatTargetModeAuto == null) ? 0 : thermostatTargetModeAuto.hashCode());
result = prime * result + ((thermostatTargetModeCool == null) ? 0 : thermostatTargetModeCool.hashCode());
@@ -89,6 +93,8 @@ public class HomekitSettings {
}
} else if (!pin.equals(other.pin)) {
return false;
+ } else if (!setupId.equals(other.setupId)) {
+ return false;
}
if (port != other.port) {
return false;
diff --git a/bundles/org.openhab.io.homekit/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.io.homekit/src/main/resources/OH-INF/config/config.xml
index 08837e2bd4c..836d07aa8e2 100644
--- a/bundles/org.openhab.io.homekit/src/main/resources/OH-INF/config/config.xml
+++ b/bundles/org.openhab.io.homekit/src/main/resources/OH-INF/config/config.xml
@@ -22,7 +22,11 @@
String values used by your thermostat to set different targetHeatingCooling modes
-
+
+
+ qrcode
+ Scan QR code with home app to add openHAB as HomeKit bridge.
+
Defines the port the HomeKit integration listens on.
@@ -33,6 +37,10 @@
Defines the pin, used for pairing, in the form ###-##-###.
031-45-154
+
+
+ Setup ID used for pairing using QR Code. Alphanumeric code of length 4.
+
Defines the IP address of the network interface to expose the HomeKit integration on.