]> git.basschouten.com Git - openhab-addons.git/commitdiff
[homekit] support stop for blinds (#13788)
authoreugen <freiter@gmail.com>
Sun, 27 Nov 2022 22:39:50 +0000 (23:39 +0100)
committerGitHub <noreply@github.com>
Sun, 27 Nov 2022 22:39:50 +0000 (23:39 +0100)
* implement new blinds logic
* fix NPE, improve emulated state

Signed-off-by: Eugen <eugen@relotrust.com>
bundles/org.openhab.io.homekit/README.md
bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitImpl.java
bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/HomekitTaggedItem.java
bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitPositionAccessoryImpl.java

index 685ce7fe00fd90b7ab1194b0f25d42796756ab92..71d48f186efbdf6dc79e1c126aeeaf71609051e2 100644 (file)
@@ -312,7 +312,19 @@ In case you need to disable this logic you can do it with configuration paramete
 Rollershutter window_covering "Window Rollershutter" {homekit = "WindowCovering"  [inverted=false]}
 Rollershutter window          "Window"               {homekit = "Window" [inverted=false]}
 Rollershutter door            "Door"                 {homekit = "Door" [inverted=false]}
+ ```
+
+HomeKit home app never sends "STOP" but only the target position. 
+If you add configuration parameter "stop=true", openHAB will emulate stop and send "STOP" command to rollershutter item if you click on the blind icon in the iOS home app while the blind is moving.
 
+```xtend
+Rollershutter window_covering "Window Rollershutter" {homekit = "WindowCovering"  [stop=true]}
+ ```
+
+Some blinds devices do support "STOP" command but would stop if they receive UP/DOWN while moving om the same direction. In order to support such devices add "stopSameDirection" parameter.
+
+```xtend
+Rollershutter window_covering "Window Rollershutter" {homekit = "WindowCovering"  [stop=true, stopSameDirection=true]}
  ```
 
 Window covering can have a number of optional characteristics like horizontal & vertical tilt, obstruction status and hold position trigger.
@@ -530,7 +542,7 @@ In order to combine multiple accessories to one HomeKit accessory you need:
 e.g. configuration for a fan with light would look as follows
 
 ```xtend
-Group           FanWithLight        "Fan with Light"                           {homekit = "Fan,Light"}
+Group           FanWithLight        "Fan with Light"                           {homekit = "Fan,Lighting"}
 Switch          FanActiveStatus     "Fan Active Status"     (FanWithLight)     {homekit = "Fan.ActiveStatus"}
 Number          FanRotationSpeed    "Fan Rotation Speed"    (FanWithLight)     {homekit = "Fan.RotationSpeed"}
 Switch          Light               "Light"                 (FanWithLight)     {homekit = "Lighting.OnState"}
index 59525fbf5cb5dcc63c65bb82b87489fe301aac6f..5fbb3f3bc3f5cee5255ac217f23bfd6c3a2580ea 100644 (file)
@@ -154,7 +154,8 @@ public class HomekitImpl implements Homekit, NetworkAddressChangeListener {
                 return;
             if (!oldSettings.name.equals(settings.name) || !oldSettings.pin.equals(settings.pin)
                     || !oldSettings.setupId.equals(settings.setupId)
-                    || !oldSettings.networkInterface.equals(settings.networkInterface)
+                    || (oldSettings.networkInterface != null
+                            && !oldSettings.networkInterface.equals(settings.networkInterface))
                     || oldSettings.port != settings.port || oldSettings.useOHmDNS != settings.useOHmDNS
                     || oldSettings.instances != settings.instances) {
                 // the HomeKit server settings changed. we do a complete re-init
index 747c76d0c39a90b234589aaf441570df8aa1ce2a..72c668d338f068e3d3e2e5a4fb5d97894c31f28b 100644 (file)
@@ -57,6 +57,8 @@ public class HomekitTaggedItem {
     public final static String PRIMARY_SERVICE = "primary";
     public final static String STEP = "step";
     public final static String UNIT = "unit";
+    public final static String EMULATE_STOP_STATE = "stop";
+    public final static String EMULATE_STOP_SAME_DIRECTION = "stopSameDirection";
 
     private static final Map<Integer, String> CREATED_ACCESSORY_IDS = new ConcurrentHashMap<>();
 
index 45eb02e5e01cd660fe4a7cc0b5e1a4bc59262b9e..300f23837a78c7177438f10ddb465bf471ee159c 100644 (file)
@@ -31,6 +31,8 @@ import org.openhab.core.library.items.NumberItem;
 import org.openhab.core.library.items.RollershutterItem;
 import org.openhab.core.library.types.DecimalType;
 import org.openhab.core.library.types.PercentType;
+import org.openhab.core.library.types.StopMoveType;
+import org.openhab.core.library.types.UpDownType;
 import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
 import org.openhab.io.homekit.internal.HomekitCharacteristicType;
 import org.openhab.io.homekit.internal.HomekitSettings;
@@ -52,12 +54,18 @@ abstract class AbstractHomekitPositionAccessoryImpl extends AbstractHomekitAcces
     protected int closedPosition;
     protected int openPosition;
     private final Map<PositionStateEnum, String> positionStateMapping;
+    protected boolean emulateState;
+    protected boolean emulateStopSameDirection;
+    protected PositionStateEnum emulatedState = PositionStateEnum.STOPPED;
 
     public AbstractHomekitPositionAccessoryImpl(HomekitTaggedItem taggedItem,
             List<HomekitTaggedItem> mandatoryCharacteristics, HomekitAccessoryUpdater updater,
             HomekitSettings settings) {
         super(taggedItem, mandatoryCharacteristics, updater, settings);
         final boolean inverted = getAccessoryConfigurationAsBoolean(HomekitTaggedItem.INVERTED, true);
+        emulateState = getAccessoryConfigurationAsBoolean(HomekitTaggedItem.EMULATE_STOP_STATE, false);
+        emulateStopSameDirection = getAccessoryConfigurationAsBoolean(HomekitTaggedItem.EMULATE_STOP_SAME_DIRECTION,
+                false);
         closedPosition = inverted ? 0 : 100;
         openPosition = inverted ? 100 : 0;
         positionStateMapping = new EnumMap<>(PositionStateEnum.class);
@@ -72,8 +80,8 @@ abstract class AbstractHomekitPositionAccessoryImpl extends AbstractHomekitAcces
     }
 
     public CompletableFuture<PositionStateEnum> getPositionState() {
-        return CompletableFuture
-                .completedFuture(getKeyFromMapping(POSITION_STATE, positionStateMapping, PositionStateEnum.STOPPED));
+        return CompletableFuture.completedFuture(emulateState ? emulatedState
+                getKeyFromMapping(POSITION_STATE, positionStateMapping, PositionStateEnum.STOPPED));
     }
 
     public CompletableFuture<Integer> getTargetPosition() {
@@ -84,9 +92,29 @@ abstract class AbstractHomekitPositionAccessoryImpl extends AbstractHomekitAcces
         getCharacteristic(TARGET_POSITION).ifPresentOrElse(taggedItem -> {
             final Item item = taggedItem.getItem();
             final int targetPosition = convertPosition(value, openPosition);
-
             if (item instanceof RollershutterItem) {
-                ((RollershutterItem) item).send(new PercentType(targetPosition));
+                // HomeKit home app never sends STOP. we emulate stop if we receive 100% or 0% while the blind is moving
+                if (emulateState && (targetPosition == 100 && emulatedState == PositionStateEnum.DECREASING)
+                        || ((targetPosition == 0 && emulatedState == PositionStateEnum.INCREASING))) {
+                    if (emulateStopSameDirection) {
+                        // some blinds devices do not support "STOP" but would stop if receive UP/DOWN while moving
+                        ((RollershutterItem) item)
+                                .send(emulatedState == PositionStateEnum.INCREASING ? UpDownType.UP : UpDownType.DOWN);
+                    } else {
+                        ((RollershutterItem) item).send(StopMoveType.STOP);
+                    }
+                    emulatedState = PositionStateEnum.STOPPED;
+                } else {
+                    ((RollershutterItem) item).send(new PercentType(targetPosition));
+                    if (emulateState) {
+                        @Nullable
+                        PercentType currentPosition = item.getStateAs(PercentType.class);
+                        emulatedState = currentPosition == null || currentPosition.intValue() == targetPosition
+                                ? PositionStateEnum.STOPPED
+                                : currentPosition.intValue() < targetPosition ? PositionStateEnum.INCREASING
+                                        : PositionStateEnum.DECREASING;
+                    }
+                }
             } else if (item instanceof DimmerItem) {
                 ((DimmerItem) item).send(new PercentType(targetPosition));
             } else if (item instanceof NumberItem) {