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.
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"}
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;
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);
}
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() {
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) {