| 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:
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"}
```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
+
+
+
+
## Supported accessory type
| Accessory Tag | Mandatory Characteristics | Optional Characteristics | Supported OH items | Description |
| | | 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 |
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
*
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 {
.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);
+ }
}
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;
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));
}
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;
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));
}
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) {
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);
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;
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));
}
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;
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));
}
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;
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));
}
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;
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));
}
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));
}
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;
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));
}
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;
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));
}
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;
public HomekitOutletImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
super(taggedItem, mandatoryCharacteristics, updater, settings);
- inUseReader = createBooleanReader(HomekitCharacteristicType.INUSE_STATUS, OnOffType.ON, OpenClosedType.OPEN);
- onReader = createBooleanReader(HomekitCharacteristicType.ON_STATE, OnOffType.ON, OpenClosedType.OPEN);
+ inUseReader = createBooleanReader(HomekitCharacteristicType.INUSE_STATUS);
+ onReader = createBooleanReader(HomekitCharacteristicType.ON_STATE);
getServices().add(new OutletService(this));
}
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;
public HomekitSmokeSensorImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
super(taggedItem, mandatoryCharacteristics, updater, settings);
- smokeDetectedReader = createBooleanReader(SMOKE_DETECTED_STATE, OnOffType.ON, OpenClosedType.OPEN);
+ smokeDetectedReader = createBooleanReader(SMOKE_DETECTED_STATE);
this.getServices().add(new SmokeSensorService(this));
}
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;
public HomekitSpeakerImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
super(taggedItem, mandatoryCharacteristics, updater, settings);
- muteReader = createBooleanReader(HomekitCharacteristicType.MUTE, OnOffType.ON, OpenClosedType.OPEN);
+ muteReader = createBooleanReader(HomekitCharacteristicType.MUTE);
getServices().add(new SpeakerService(this));
}
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;
public HomekitSwitchImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
super(taggedItem, mandatoryCharacteristics, updater, settings);
- onReader = createBooleanReader(ON_STATE, OnOffType.ON, OpenClosedType.OPEN);
+ onReader = createBooleanReader(ON_STATE);
getServices().add(new SwitchService(this));
}
import org.openhab.core.library.items.SwitchItem;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
-import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.types.RefreshType;
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
import org.openhab.io.homekit.internal.HomekitCharacteristicType;
public HomekitValveImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
HomekitAccessoryUpdater updater, HomekitSettings settings) throws IncompleteAccessoryException {
super(taggedItem, mandatoryCharacteristics, updater, settings);
- inUseReader = createBooleanReader(INUSE_STATUS, OnOffType.ON, OpenClosedType.OPEN);
- activeReader = createBooleanReader(ACTIVE_STATUS, OnOffType.ON, OpenClosedType.OPEN);
+ inUseReader = createBooleanReader(INUSE_STATUS);
+ activeReader = createBooleanReader(ACTIVE_STATUS);
ValveService service = new ValveService(this);
getServices().add(service);
final String timerConfig = getAccessoryConfiguration(CONFIG_TIMER, "");