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)
### UI based Configuration
+
In order to add metadata to an item:
+
- select desired item in mainUI
- click on "Add Metadata"
### Textual configuration
+
```xtend
Switch leaksensor_metadata "Leak Sensor" {homekit="LeakSensor"}
```
Dimmer window_covering_htilt "Blind horizontal tilt" (gBlind) {homekit = "WindowCovering.CurrentHorizontalTiltAngle, WindowCovering.TargetHorizontalTiltAngle"}
Dimmer window_covering_vtilt "Blind vertical tilt" (gBlind) {homekit = "WindowCovering.CurrentVerticalTiltAngle, WindowCovering.TargetVerticalTiltAngle"}
```
+
+Current and Target Position characteristics can be linked to Rollershutter but also to Number or Dimmer item types.
+e.g.
+
+```xtend
+Group gBlind "Blinds" {homekit = "WindowCovering"}
+Dimmer blind_current_position (gBlind) {homekit = "CurrentPosition"}
+Number blind_target_position (gBlind) {homekit = "TargetPosition"}
+String blind_position (gBlind) {homekit = "PositionState"}
+```
+
### Thermostat
+
A HomeKit thermostat has following mandatory characteristics:
- CurrentTemperature
Current and target temperatures have default min and max values. Any values below or above max limits will be replaced with min or max limits.
Default limits are:
+
- current temperature: min value = 0 C, max value = 100 C
- target temperature: min value = 10 C, max value = 38 C
| | | TamperedStatus | Switch, Contact | Tampered status |
| | | BatteryLowStatus | Switch, Contact | Battery status |
| Door | | | | Motorized door. One Rollershutter item covers all mandatory characteristics. see examples below. |
-| | CurrentPosition | | Rollershutter | Current position of motorized door |
-| | TargetPosition | | Rollershutter | Target position of motorized door |
-| | PositionState | | Rollershutter | Position state. Supported states: DECREASING, INCREASING, STOPPED. Mapping can be redefined at item level, e.g. [DECREASING="Down", INCREASING="Up"]. If no state provided, "STOPPED" is used. |
+| | CurrentPosition | | Rollershutter, Dimmer, Number | Current position of motorized door |
+| | TargetPosition | | Rollershutter, Dimmer, Number | Target position of motorized door |
+| | PositionState | | Rollershutter, String | Position state. Supported states: DECREASING, INCREASING, STOPPED. Mapping can be redefined at item level, e.g. [DECREASING="Down", INCREASING="Up"]. If no state provided, "STOPPED" is used. |
| | | Name | String | Name of the motorized door |
| | | HoldPosition | Switch | Motorized door should stop at its current position. A value of ON must hold the state of the accessory. A value of OFF should be ignored. |
| | | ObstructionStatus | Switch, Contact | Current status of obstruction sensor. ON-obstruction detected, OFF - no obstruction |
| Window | | | | Motorized window. One Rollershutter item covers all mandatory characteristics. see examples below. |
-| | CurrentPosition | | Rollershutter | Current position of motorized window |
-| | TargetPosition | | Rollershutter | Target position of motorized window |
-| | PositionState | | Rollershutter | Position state. Supported states: DECREASING, INCREASING, STOPPED. Mapping can be redefined at item level, e.g. [DECREASING="Down", INCREASING="Up"]. If no state provided, "STOPPED" is used. |
+| | CurrentPosition | | Rollershutter, Dimmer, Number | Current position of motorized window |
+| | TargetPosition | | Rollershutter, Dimmer, Number | Target position of motorized window |
+| | PositionState | | Rollershutter, String | Position state. Supported states: DECREASING, INCREASING, STOPPED. Mapping can be redefined at item level, e.g. [DECREASING="Down", INCREASING="Up"]. If no state provided, "STOPPED" is used. |
| | | Name | String | Name of the motorized window |
| | | HoldPosition | Switch | Motorized door should stop at its current position. A value of ON must hold the state of the accessory. A value of OFF should be ignored. |
| | | ObstructionStatus | Switch, Contact | Current status of obstruction sensor. ON-obstruction detected, OFF - no obstruction |
| WindowCovering | | | | Window covering / blinds. One Rollershutter item covers all mandatory characteristics. see examples below. |
-| | CurrentPosition | | Rollershutter | Current position of window covering |
-| | TargetPosition | | Rollershutter | Target position of window covering |
-| | PositionState | | Rollershutter | current only "STOPPED" is supported. |
+| | CurrentPosition | | Rollershutter, Dimmer, Number | Current position of window covering |
+| | TargetPosition | | Rollershutter, Dimmer, Number | Target position of window covering |
+| | PositionState | | Rollershutter, String | current only "STOPPED" is supported. |
| | | Name | String | Name of the windows covering |
| | | HoldPosition | Switch | Window covering should stop at its current position. A value of ON must hold the state of the accessory. A value of OFF should be ignored. |
| | | ObstructionStatus | Switch, Contact | Current status of obstruction sensor. ON-obstruction detected, OFF - no obstruction |
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.items.Item;
+import org.openhab.core.library.items.DimmerItem;
+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.io.homekit.internal.HomekitCharacteristicType;
import org.openhab.io.homekit.internal.HomekitSettings;
import org.openhab.io.homekit.internal.HomekitTaggedItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback;
import io.github.hapjava.characteristics.impl.windowcovering.PositionStateEnum;
*/
@NonNullByDefault
abstract class AbstractHomekitPositionAccessoryImpl extends AbstractHomekitAccessoryImpl {
+ private final Logger logger = LoggerFactory.getLogger(AbstractHomekitPositionAccessoryImpl.class);
protected int closedPosition;
protected int openPosition;
private final Map<PositionStateEnum, String> positionStateMapping;
return CompletableFuture.completedFuture(convertPositionState(TARGET_POSITION, openPosition, closedPosition));
}
- @NonNullByDefault({})
public CompletableFuture<Void> setTargetPosition(int value) {
- getItem(TARGET_POSITION, RollershutterItem.class)
- .ifPresent(item -> item.send(new PercentType(convertPosition(value, openPosition))));
+ 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));
+ } else if (item instanceof DimmerItem) {
+ ((DimmerItem) item).send(new PercentType(targetPosition));
+ } else if (item instanceof NumberItem) {
+ ((NumberItem) item).send(new DecimalType(targetPosition));
+ } else {
+ logger.warn(
+ "Unsupported item type for characteristic {} at accessory {}. Expected Rollershutter, Dimmer or Number item, got {}",
+ TARGET_POSITION, getName(), item.getClass());
+ }
+ }, () -> {
+ logger.warn("Mandatory characteristic {} not found at accessory {}. ", TARGET_POSITION, getName());
+ });
return CompletableFuture.completedFuture(null);
}
}
protected int convertPositionState(HomekitCharacteristicType type, int openPosition, int closedPosition) {
- final @Nullable DecimalType value = getStateAs(type, PercentType.class);
+ @Nullable
+ DecimalType value = null;
+ final Optional<HomekitTaggedItem> taggedItem = getCharacteristic(type);
+ if (taggedItem.isPresent()) {
+ final Item item = taggedItem.get().getItem();
+ if ((item instanceof RollershutterItem) || ((item instanceof DimmerItem))) {
+ value = item.getStateAs(PercentType.class);
+ } else {
+ value = item.getStateAs(DecimalType.class);
+ }
+ }
return value != null ? convertPosition(value.intValue(), openPosition) : closedPosition;
}
}