[homekit] support stop for blinds (#13788)
* implement new blinds logic * fix NPE, improve emulated state Signed-off-by: Eugen <eugen@relotrust.com>
This commit is contained in:
parent
1d9bf63d5e
commit
5c5b3c29c9
|
@ -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_covering "Window Rollershutter" {homekit = "WindowCovering" [inverted=false]}
|
||||||
Rollershutter window "Window" {homekit = "Window" [inverted=false]}
|
Rollershutter window "Window" {homekit = "Window" [inverted=false]}
|
||||||
Rollershutter door "Door" {homekit = "Door" [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.
|
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
|
e.g. configuration for a fan with light would look as follows
|
||||||
|
|
||||||
```xtend
|
```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"}
|
Switch FanActiveStatus "Fan Active Status" (FanWithLight) {homekit = "Fan.ActiveStatus"}
|
||||||
Number FanRotationSpeed "Fan Rotation Speed" (FanWithLight) {homekit = "Fan.RotationSpeed"}
|
Number FanRotationSpeed "Fan Rotation Speed" (FanWithLight) {homekit = "Fan.RotationSpeed"}
|
||||||
Switch Light "Light" (FanWithLight) {homekit = "Lighting.OnState"}
|
Switch Light "Light" (FanWithLight) {homekit = "Lighting.OnState"}
|
||||||
|
|
|
@ -154,7 +154,8 @@ public class HomekitImpl implements Homekit, NetworkAddressChangeListener {
|
||||||
return;
|
return;
|
||||||
if (!oldSettings.name.equals(settings.name) || !oldSettings.pin.equals(settings.pin)
|
if (!oldSettings.name.equals(settings.name) || !oldSettings.pin.equals(settings.pin)
|
||||||
|| !oldSettings.setupId.equals(settings.setupId)
|
|| !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.port != settings.port || oldSettings.useOHmDNS != settings.useOHmDNS
|
||||||
|| oldSettings.instances != settings.instances) {
|
|| 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
|
||||||
|
|
|
@ -57,6 +57,8 @@ public class HomekitTaggedItem {
|
||||||
public final static String PRIMARY_SERVICE = "primary";
|
public final static String PRIMARY_SERVICE = "primary";
|
||||||
public final static String STEP = "step";
|
public final static String STEP = "step";
|
||||||
public final static String UNIT = "unit";
|
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<>();
|
private static final Map<Integer, String> CREATED_ACCESSORY_IDS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@ import org.openhab.core.library.items.NumberItem;
|
||||||
import org.openhab.core.library.items.RollershutterItem;
|
import org.openhab.core.library.items.RollershutterItem;
|
||||||
import org.openhab.core.library.types.DecimalType;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.PercentType;
|
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.HomekitAccessoryUpdater;
|
||||||
import org.openhab.io.homekit.internal.HomekitCharacteristicType;
|
import org.openhab.io.homekit.internal.HomekitCharacteristicType;
|
||||||
import org.openhab.io.homekit.internal.HomekitSettings;
|
import org.openhab.io.homekit.internal.HomekitSettings;
|
||||||
|
@ -52,12 +54,18 @@ abstract class AbstractHomekitPositionAccessoryImpl extends AbstractHomekitAcces
|
||||||
protected int closedPosition;
|
protected int closedPosition;
|
||||||
protected int openPosition;
|
protected int openPosition;
|
||||||
private final Map<PositionStateEnum, String> positionStateMapping;
|
private final Map<PositionStateEnum, String> positionStateMapping;
|
||||||
|
protected boolean emulateState;
|
||||||
|
protected boolean emulateStopSameDirection;
|
||||||
|
protected PositionStateEnum emulatedState = PositionStateEnum.STOPPED;
|
||||||
|
|
||||||
public AbstractHomekitPositionAccessoryImpl(HomekitTaggedItem taggedItem,
|
public AbstractHomekitPositionAccessoryImpl(HomekitTaggedItem taggedItem,
|
||||||
List<HomekitTaggedItem> mandatoryCharacteristics, HomekitAccessoryUpdater updater,
|
List<HomekitTaggedItem> mandatoryCharacteristics, HomekitAccessoryUpdater updater,
|
||||||
HomekitSettings settings) {
|
HomekitSettings settings) {
|
||||||
super(taggedItem, mandatoryCharacteristics, updater, settings);
|
super(taggedItem, mandatoryCharacteristics, updater, settings);
|
||||||
final boolean inverted = getAccessoryConfigurationAsBoolean(HomekitTaggedItem.INVERTED, true);
|
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;
|
closedPosition = inverted ? 0 : 100;
|
||||||
openPosition = inverted ? 100 : 0;
|
openPosition = inverted ? 100 : 0;
|
||||||
positionStateMapping = new EnumMap<>(PositionStateEnum.class);
|
positionStateMapping = new EnumMap<>(PositionStateEnum.class);
|
||||||
|
@ -72,8 +80,8 @@ abstract class AbstractHomekitPositionAccessoryImpl extends AbstractHomekitAcces
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<PositionStateEnum> getPositionState() {
|
public CompletableFuture<PositionStateEnum> getPositionState() {
|
||||||
return CompletableFuture
|
return CompletableFuture.completedFuture(emulateState ? emulatedState
|
||||||
.completedFuture(getKeyFromMapping(POSITION_STATE, positionStateMapping, PositionStateEnum.STOPPED));
|
: getKeyFromMapping(POSITION_STATE, positionStateMapping, PositionStateEnum.STOPPED));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Integer> getTargetPosition() {
|
public CompletableFuture<Integer> getTargetPosition() {
|
||||||
|
@ -84,9 +92,29 @@ abstract class AbstractHomekitPositionAccessoryImpl extends AbstractHomekitAcces
|
||||||
getCharacteristic(TARGET_POSITION).ifPresentOrElse(taggedItem -> {
|
getCharacteristic(TARGET_POSITION).ifPresentOrElse(taggedItem -> {
|
||||||
final Item item = taggedItem.getItem();
|
final Item item = taggedItem.getItem();
|
||||||
final int targetPosition = convertPosition(value, openPosition);
|
final int targetPosition = convertPosition(value, openPosition);
|
||||||
|
|
||||||
if (item instanceof RollershutterItem) {
|
if (item instanceof RollershutterItem) {
|
||||||
|
// 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));
|
((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) {
|
} else if (item instanceof DimmerItem) {
|
||||||
((DimmerItem) item).send(new PercentType(targetPosition));
|
((DimmerItem) item).send(new PercentType(targetPosition));
|
||||||
} else if (item instanceof NumberItem) {
|
} else if (item instanceof NumberItem) {
|
||||||
|
|
Loading…
Reference in New Issue