Skip to content

Commit

Permalink
[homekit] extend support of inverted flag to other accessories (openh…
Browse files Browse the repository at this point in the history
…ab#10212)

Signed-off-by: Eugen Freiter <[email protected]>
  • Loading branch information
yfre authored and thinkingstone committed Nov 7, 2021
1 parent b6f2ff7 commit 9b27679
Show file tree
Hide file tree
Showing 19 changed files with 72 additions and 54 deletions.
18 changes: 14 additions & 4 deletions bundles/org.openhab.io.homekit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,9 @@ Following table summarizes the optional characteristics supported by sensors.
| TamperedStatus | Switch, Contact | Accessory tampered status. "ON"/"OPEN" indicates that the accessory has been tampered. Value should return to "OFF"/"CLOSED" when the accessory has been reset to a non-tampered state. |
| BatteryLowStatus | Switch, Contact | Accessory battery status. "ON"/"OPEN" indicates that the battery level of the accessory is low. Value should return to "OFF"/"CLOSED" when the battery charges to a level thats above the low threshold. |

Switch and Contact items support inversion of the state mapping, e.g. by default the openHAB switch state "ON" is mapped to HomeKit contact sensor state "Open", and "OFF" to "Closed".
The configuration "inverted='true'" inverts this mapping, so that "ON" will be mapped to "Closed" and "OFF" to "Open".

Examples of sensor definitions.
Sensors without optional characteristics:

Expand All @@ -429,6 +432,8 @@ Switch leaksensor_single "Leak Sensor" {homekit="LeakSenso
Number light_sensor "Light Sensor" {homekit="LightSensor"}
Number temperature_sensor "Temperature Sensor [%.1f C]" {homekit="TemperatureSensor"}
Contact contact_sensor "Contact Sensor" {homekit="ContactSensor"}
Contact contact_sensor "Contact Sensor" {homekit="ContactSensor" [inverted="true"]}
Switch occupancy_sensor "Occupancy Sensor" {homekit="OccupancyDetectedState"}
Switch motion_sensor "Motion Sensor" {homekit="MotionSensor"}
Number humidity_sensor "Humidity Sensor" {homekit="HumiditySensor"}
Expand All @@ -439,19 +444,24 @@ Sensors with optional characteristics:
```xtend
Group gLeakSensor "Leak Sensor" {homekit="LeakSensor"}
Switch leaksensor "Leak Sensor State" (gLeakSensor) {homekit="LeakDetectedState"}
Switch leaksensor_bat "Leak Sensor Battery" (gLeakSensor) {homekit="BatteryLowStatus"}
Switch leaksensor_active "Leak Sensor Active" (gLeakSensor) {homekit="ActiveStatus"}
Switch leaksensor_bat "Leak Sensor Battery" (gLeakSensor) {homekit="BatteryLowStatus" }
Switch leaksensor_active "Leak Sensor Active" (gLeakSensor) {homekit="ActiveStatus" [inverted="true"]}
Switch leaksensor_fault "Leak Sensor Fault" (gLeakSensor) {homekit="FaultStatus"}
Switch leaksensor_tampered "Leak Sensor Tampered" (gLeakSensor) {homekit="TamperedStatus"}
Group gMotionSensor "Motion Sensor" {homekit="MotionSensor"}
Switch motionsensor "Motion Sensor State" (gMotionSensor) {homekit="MotionDetectedState"}
Switch motionsensor_bat "Motion Sensor Battery" (gMotionSensor) {homekit="BatteryLowStatus"}
Switch motionsensor_bat "Motion Sensor Battery" (gMotionSensor) {homekit="BatteryLowStatus" [inverted="true"]}
Switch motionsensor_active "Motion Sensor Active" (gMotionSensor) {homekit="ActiveStatus"}
Switch motionsensor_fault "Motion Sensor Fault" (gMotionSensor) {homekit="FaultStatus"}
Switch motionsensor_tampered "Motion Sensor Tampered" (gMotionSensor) {homekit="TamperedStatus"}
```

or using UI

![sensor_ui_config.png](doc/sensor_ui_config.png)


## Supported accessory type

| Accessory Tag | Mandatory Characteristics | Optional Characteristics | Supported OH items | Description |
Expand Down Expand Up @@ -490,7 +500,7 @@ Switch motionsensor_tampered "Motion Sensor Tampered"
| | | FaultStatus | Switch, Contact | Fault status |
| | | TamperedStatus | Switch, Contact | Tampered status |
| | | BatteryLowStatus | Switch, Contact | Battery status |
| ContactSensor | | | | Contact Sensor,An accessory with on/off state that can be viewed in HomeKit but not changed such as a contact sensor for a door or window |
| ContactSensor | | | | Contact Sensor, An accessory with on/off state that can be viewed in HomeKit but not changed such as a contact sensor for a door or window |
| | ContactSensorState | | Switch, Contact | Contact sensor state (ON=open, OFF=closed) |
| | | Name | String | Name of the sensor |
| | | ActiveStatus | Switch, Contact | Working status |
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,16 @@ public <T> T getConfiguration(String key, T defaultValue) {
return defaultValue;
}

/**
* returns true if inverted flag is set, i.e. item has the configuration "inverted=true"
*
* @return true if inverted flag is set to true
*/
public boolean isInverted() {
final String invertedConfig = getConfiguration(HomekitTaggedItem.INVERTED, "false");
return invertedConfig.equalsIgnoreCase("yes") || invertedConfig.equalsIgnoreCase("true");
}

/**
* return configuration as double if exists otherwise return defaultValue
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,15 @@ protected double convertFromCelsius(double degrees) {
ImperialUnits.FAHRENHEIT);
}

/**
* create boolean reader with ON state mapped to trueOnOffValue or trueOpenClosedValue depending of item type
*
* @param characteristicType characteristic id
* @param trueOnOffValue ON value for switch
* @param trueOpenClosedValue ON value for contact
* @return boolean readed
* @throws IncompleteAccessoryException
*/
@NonNullByDefault
protected BooleanItemReader createBooleanReader(HomekitCharacteristicType characteristicType,
OnOffType trueOnOffValue, OpenClosedType trueOpenClosedValue) throws IncompleteAccessoryException {
Expand All @@ -308,4 +317,20 @@ protected BooleanItemReader createBooleanReader(HomekitCharacteristicType charac
.orElseThrow(() -> new IncompleteAccessoryException(characteristicType)),
trueOnOffValue, trueOpenClosedValue);
}

/**
* create boolean reader with default ON/OFF mapping considering inverted flag
*
* @param characteristicType characteristic id
* @return boolean reader
* @throws IncompleteAccessoryException
*/
@NonNullByDefault
protected BooleanItemReader createBooleanReader(HomekitCharacteristicType characteristicType)
throws IncompleteAccessoryException {
final HomekitTaggedItem taggedItem = getCharacteristic(characteristicType)
.orElseThrow(() -> new IncompleteAccessoryException(characteristicType));
return new BooleanItemReader(taggedItem.getItem(), taggedItem.isInverted() ? OnOffType.OFF : OnOffType.ON,
taggedItem.isInverted() ? OpenClosedType.CLOSED : OpenClosedType.OPEN);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
import java.util.List;
import java.util.concurrent.CompletableFuture;

import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
import org.openhab.io.homekit.internal.HomekitSettings;
import org.openhab.io.homekit.internal.HomekitTaggedItem;
Expand All @@ -40,8 +38,7 @@ public HomekitCarbonDioxideSensorImpl(HomekitTaggedItem taggedItem,
List<HomekitTaggedItem> mandatoryCharacteristics, HomekitAccessoryUpdater updater, HomekitSettings settings)
throws IncompleteAccessoryException {
super(taggedItem, mandatoryCharacteristics, updater, settings);
carbonDioxideDetectedReader = createBooleanReader(CARBON_DIOXIDE_DETECTED_STATE, OnOffType.ON,
OpenClosedType.OPEN);
carbonDioxideDetectedReader = createBooleanReader(CARBON_DIOXIDE_DETECTED_STATE);
getServices().add(new CarbonDioxideSensorService(this));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
import java.util.List;
import java.util.concurrent.CompletableFuture;

import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
import org.openhab.io.homekit.internal.HomekitSettings;
import org.openhab.io.homekit.internal.HomekitTaggedItem;
Expand All @@ -40,8 +38,7 @@ public HomekitCarbonMonoxideSensorImpl(HomekitTaggedItem taggedItem,
List<HomekitTaggedItem> mandatoryCharacteristics, HomekitAccessoryUpdater updater, HomekitSettings settings)
throws IncompleteAccessoryException {
super(taggedItem, mandatoryCharacteristics, updater, settings);
carbonMonoxideDetectedReader = createBooleanReader(CARBON_MONOXIDE_DETECTED_STATE, OnOffType.ON,
OpenClosedType.OPEN);
carbonMonoxideDetectedReader = createBooleanReader(CARBON_MONOXIDE_DETECTED_STATE);
getServices().add(new CarbonMonoxideSensorService(this));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,11 @@ private static <T extends CharacteristicEnum> CompletableFuture<T> getEnumFromIt
T offEnum, T onEnum, T defaultEnum) {
final State state = item.getItem().getState();
if (state instanceof OnOffType) {
return CompletableFuture.completedFuture(state.equals(OnOffType.OFF) ? offEnum : onEnum);
return CompletableFuture
.completedFuture(state.equals(item.isInverted() ? OnOffType.ON : OnOffType.OFF) ? offEnum : onEnum);
} else if (state instanceof OpenClosedType) {
return CompletableFuture.completedFuture(state.equals(OpenClosedType.CLOSED) ? offEnum : onEnum);
return CompletableFuture.completedFuture(
state.equals(item.isInverted() ? OpenClosedType.OPEN : OpenClosedType.CLOSED) ? offEnum : onEnum);
} else if (state instanceof DecimalType) {
return CompletableFuture.completedFuture(((DecimalType) state).intValue() == 0 ? offEnum : onEnum);
} else if (state instanceof UnDefType) {
Expand All @@ -182,9 +184,9 @@ private static void setValueFromEnum(HomekitTaggedItem taggedItem, Characteristi
CharacteristicEnum offEnum, CharacteristicEnum onEnum) {
if (taggedItem.getItem() instanceof SwitchItem) {
if (value.equals(offEnum)) {
((SwitchItem) taggedItem.getItem()).send(OnOffType.OFF);
((SwitchItem) taggedItem.getItem()).send(taggedItem.isInverted() ? OnOffType.ON : OnOffType.OFF);
} else if (value.equals(onEnum)) {
((SwitchItem) taggedItem.getItem()).send(OnOffType.ON);
((SwitchItem) taggedItem.getItem()).send(taggedItem.isInverted() ? OnOffType.OFF : OnOffType.ON);
} else {
logger.warn("Enum value {} is not supported. Only following values are supported: {},{}", value,
offEnum, onEnum);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
import java.util.List;
import java.util.concurrent.CompletableFuture;

import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
import org.openhab.io.homekit.internal.HomekitSettings;
import org.openhab.io.homekit.internal.HomekitTaggedItem;
Expand All @@ -38,7 +36,7 @@ public class HomekitContactSensorImpl extends AbstractHomekitAccessoryImpl imple
public HomekitContactSensorImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
super(taggedItem, mandatoryCharacteristics, updater, settings);
contactSensedReader = createBooleanReader(CONTACT_SENSOR_STATE, OnOffType.ON, OpenClosedType.OPEN);
contactSensedReader = createBooleanReader(CONTACT_SENSOR_STATE);
getServices().add(new ContactSensorService(this));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
import java.util.List;
import java.util.concurrent.CompletableFuture;

import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
import org.openhab.io.homekit.internal.HomekitSettings;
import org.openhab.io.homekit.internal.HomekitTaggedItem;
Expand All @@ -38,7 +36,7 @@ class HomekitFanImpl extends AbstractHomekitAccessoryImpl implements FanAccessor
public HomekitFanImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
super(taggedItem, mandatoryCharacteristics, updater, settings);
activeReader = createBooleanReader(ACTIVE_STATUS, OnOffType.ON, OpenClosedType.OPEN);
activeReader = createBooleanReader(ACTIVE_STATUS);
this.getServices().add(new FanService(this));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import org.openhab.core.library.items.StringItem;
import org.openhab.core.library.items.SwitchItem;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.StringType;
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
import org.openhab.io.homekit.internal.HomekitSettings;
Expand All @@ -50,7 +49,7 @@ public class HomekitGarageDoorOpenerImpl extends AbstractHomekitAccessoryImpl im
public HomekitGarageDoorOpenerImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
super(taggedItem, mandatoryCharacteristics, updater, settings);
obstructionReader = createBooleanReader(OBSTRUCTION_STATUS, OnOffType.ON, OpenClosedType.OPEN);
obstructionReader = createBooleanReader(OBSTRUCTION_STATUS);
getServices().add(new GarageDoorOpenerService(this));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
import java.util.List;
import java.util.concurrent.CompletableFuture;

import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
import org.openhab.io.homekit.internal.HomekitSettings;
import org.openhab.io.homekit.internal.HomekitTaggedItem;
Expand All @@ -38,7 +36,7 @@ public class HomekitLeakSensorImpl extends AbstractHomekitAccessoryImpl implemen
public HomekitLeakSensorImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
super(taggedItem, mandatoryCharacteristics, updater, settings);
leakDetectedReader = createBooleanReader(LEAK_DETECTED_STATE, OnOffType.ON, OpenClosedType.OPEN);
leakDetectedReader = createBooleanReader(LEAK_DETECTED_STATE);
getServices().add(new LeakSensorService(this));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,8 @@ public class HomekitLockImpl extends AbstractHomekitAccessoryImpl implements Loc
public HomekitLockImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
HomekitAccessoryUpdater updater, HomekitSettings settings) {
super(taggedItem, mandatoryCharacteristics, updater, settings);
final String invertedConfig = getAccessoryConfiguration(HomekitTaggedItem.INVERTED, "false");
final boolean inverted = invertedConfig.equalsIgnoreCase("yes") || invertedConfig.equalsIgnoreCase("true");
securedState = inverted ? OnOffType.OFF : OnOffType.ON;
unsecuredState = inverted ? OnOffType.ON : OnOffType.OFF;
securedState = taggedItem.isInverted() ? OnOffType.OFF : OnOffType.ON;
unsecuredState = taggedItem.isInverted() ? OnOffType.ON : OnOffType.OFF;
getServices().add(new LockMechanismService(this));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
import java.util.List;
import java.util.concurrent.CompletableFuture;

import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
import org.openhab.io.homekit.internal.HomekitCharacteristicType;
import org.openhab.io.homekit.internal.HomekitSettings;
Expand All @@ -36,8 +34,7 @@ public class HomekitMotionSensorImpl extends AbstractHomekitAccessoryImpl implem
public HomekitMotionSensorImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
super(taggedItem, mandatoryCharacteristics, updater, settings);
motionSensedReader = createBooleanReader(HomekitCharacteristicType.MOTION_DETECTED_STATE, OnOffType.ON,
OpenClosedType.OPEN);
motionSensedReader = createBooleanReader(HomekitCharacteristicType.MOTION_DETECTED_STATE);
getServices().add(new MotionSensorService(this));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
import java.util.List;
import java.util.concurrent.CompletableFuture;

import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
import org.openhab.io.homekit.internal.HomekitSettings;
import org.openhab.io.homekit.internal.HomekitTaggedItem;
Expand All @@ -38,7 +36,7 @@ public class HomekitOccupancySensorImpl extends AbstractHomekitAccessoryImpl imp
public HomekitOccupancySensorImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
super(taggedItem, mandatoryCharacteristics, updater, settings);
occupancySensedReader = createBooleanReader(OCCUPANCY_DETECTED_STATE, OnOffType.ON, OpenClosedType.OPEN);
occupancySensedReader = createBooleanReader(OCCUPANCY_DETECTED_STATE);
getServices().add(new OccupancySensorService(this));
}

Expand Down
Loading

0 comments on commit 9b27679

Please sign in to comment.