They are appropriately marked.
Enums that are linked to Switches or Contacts have an `inverted` param that will reverse the sense of `ON`/`OFF` or `OPEN`/`CLOSED`.
+Enum mappings can have multiple values for a single key.
+These must be an array, not a comma separated string.
+If the characteristic can be set by HomeKit, the first value will be used when sending the command to the linked item.
+Such a mapping can be configured manually in MainUI on HomeKit metadata in the Code editor:
+```yaml
+value: "Lock"
+config:
+ SECURE:
+ - LOCK
+ - LOCKED
+ UNSECURE:
+ - UNLOCK
+ - UNLOCKED
+```
+Or in a `.items` file:
+```java
+String MyLock "My Lock" { homekit="Lock"[SECURE="LOCK","LOCKED", UNSECURE="UNLOCK","UNLOCKED"] }
+```
+
All accessories support the following characteristics that can be set via metadata or linked to a String item:
* Name (defaults to item's label)
* Manufacturer (defaults to "none")
}
@NonNullByDefault
- protected <T extends Enum<T> & CharacteristicEnum> Map<T, String> createMapping(
+ protected <T extends Enum<T> & CharacteristicEnum> Map<T, Object> createMapping(
HomekitCharacteristicType characteristicType, Class<T> klazz) {
return createMapping(characteristicType, klazz, null, false);
}
@NonNullByDefault
- protected <T extends Enum<T> & CharacteristicEnum> Map<T, String> createMapping(
+ protected <T extends Enum<T> & CharacteristicEnum> Map<T, Object> createMapping(
HomekitCharacteristicType characteristicType, Class<T> klazz, boolean inverted) {
return createMapping(characteristicType, klazz, null, inverted);
}
@NonNullByDefault
- protected <T extends Enum<T> & CharacteristicEnum> Map<T, String> createMapping(
+ protected <T extends Enum<T> & CharacteristicEnum> Map<T, Object> createMapping(
HomekitCharacteristicType characteristicType, Class<T> klazz, @Nullable List<T> customEnumList) {
return createMapping(characteristicType, klazz, customEnumList, false);
}
* @return mapping of enum values to custom string values
*/
@NonNullByDefault
- protected <T extends Enum<T> & CharacteristicEnum> Map<T, String> createMapping(
+ protected <T extends Enum<T> & CharacteristicEnum> Map<T, Object> createMapping(
HomekitCharacteristicType characteristicType, Class<T> klazz, @Nullable List<T> customEnumList,
boolean inverted) {
HomekitTaggedItem item = getCharacteristic(characteristicType).get();
* @return key for the value
*/
@NonNullByDefault
- public <T> T getKeyFromMapping(HomekitCharacteristicType characteristicType, Map<T, String> mapping,
+ public <T> T getKeyFromMapping(HomekitCharacteristicType characteristicType, Map<T, Object> mapping,
T defaultValue) {
final Optional<HomekitTaggedItem> c = getCharacteristic(characteristicType);
if (c.isPresent()) {
private final Logger logger = LoggerFactory.getLogger(AbstractHomekitPositionAccessoryImpl.class);
protected int closedPosition;
protected int openPosition;
- private final Map<PositionStateEnum, String> positionStateMapping;
+ private final Map<PositionStateEnum, Object> positionStateMapping;
protected boolean emulateState;
protected boolean emulateStopSameDirection;
protected boolean sendUpDownForExtents;
* @author Eugen Freiter - Initial contribution
*/
public class HomekitAirQualitySensorImpl extends AbstractHomekitAccessoryImpl implements AirQualityAccessory {
- private final Map<AirQualityEnum, String> qualityStateMapping;
+ private final Map<AirQualityEnum, Object> qualityStateMapping;
public HomekitAirQualitySensorImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
List<Characteristic> mandatoryRawCharacteristics, HomekitAccessoryUpdater updater, HomekitSettings settings)
*/
public class HomekitCarbonDioxideSensorImpl extends AbstractHomekitAccessoryImpl
implements CarbonDioxideSensorAccessory {
- private final Map<CarbonDioxideDetectedEnum, String> mapping;
+ private final Map<CarbonDioxideDetectedEnum, Object> mapping;
public HomekitCarbonDioxideSensorImpl(HomekitTaggedItem taggedItem,
List<HomekitTaggedItem> mandatoryCharacteristics, List<Characteristic> mandatoryRawCharacteristics,
*/
public class HomekitCarbonMonoxideSensorImpl extends AbstractHomekitAccessoryImpl
implements CarbonMonoxideSensorAccessory {
- private final Map<CarbonMonoxideDetectedEnum, String> mapping;
+ private final Map<CarbonMonoxideDetectedEnum, Object> mapping;
public HomekitCarbonMonoxideSensorImpl(HomekitTaggedItem taggedItem,
List<HomekitTaggedItem> mandatoryCharacteristics, List<Characteristic> mandatoryRawCharacteristics,
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
import javax.measure.Quantity;
import javax.measure.Unit;
* associated with, which has already been set.
* @return
*/
- public static <T extends Enum<T> & CharacteristicEnum> Map<T, String> createMapping(HomekitTaggedItem item,
+ public static <T extends Enum<T> & CharacteristicEnum> Map<T, Object> createMapping(HomekitTaggedItem item,
Class<T> klazz, @Nullable List<T> customEnumList, boolean inverted) {
- EnumMap<T, String> map = new EnumMap(klazz);
+ EnumMap<T, Object> map = new EnumMap(klazz);
var dataTypes = item.getBaseItem().getAcceptedDataTypes();
boolean switchType = dataTypes.contains(OnOffType.class);
boolean contactType = dataTypes.contains(OpenClosedType.class);
}
if (configuration != null && !configuration.isEmpty()) {
map.forEach((k, current_value) -> {
- final Object newValue = configuration.get(k.toString());
- if (newValue instanceof String || newValue instanceof Number) {
- map.put(k, newValue.toString());
+ Object newValue = configuration.get(k.toString());
+ if (newValue instanceof String || newValue instanceof Number || newValue instanceof List) {
+ if (newValue instanceof Number) {
+ newValue = newValue.toString();
+ } else if (newValue instanceof List listValue) {
+ newValue = listValue.stream().map(v -> {
+ // they probably put "NULL" in the YAML in MainUI;
+ // and they meant it as a string to match the UnDefType.NULL
+ if (v == null) {
+ return "NULL";
+ } else {
+ return v.toString();
+ }
+ }).collect(Collectors.toList());
+ }
+ map.put(k, Objects.requireNonNull(newValue));
if (customEnumList != null) {
customEnumList.add(k);
}
return map;
}
- public static <T extends Enum<T> & CharacteristicEnum> Map<T, String> createMapping(HomekitTaggedItem item,
+ public static <T extends Enum<T> & CharacteristicEnum> Map<T, Object> createMapping(HomekitTaggedItem item,
Class<T> klazz) {
return createMapping(item, klazz, null, false);
}
- public static <T extends Enum<T> & CharacteristicEnum> Map<T, String> createMapping(HomekitTaggedItem item,
+ public static <T extends Enum<T> & CharacteristicEnum> Map<T, Object> createMapping(HomekitTaggedItem item,
Class<T> klazz, @Nullable List<T> customEnumList) {
return createMapping(item, klazz, customEnumList, false);
}
- public static <T extends Enum<T> & CharacteristicEnum> Map<T, String> createMapping(HomekitTaggedItem item,
+ public static <T extends Enum<T> & CharacteristicEnum> Map<T, Object> createMapping(HomekitTaggedItem item,
Class<T> klazz, boolean inverted) {
return createMapping(item, klazz, null, inverted);
}
* @param <T> type of the result derived from
* @return key for the value
*/
- public static <T> T getKeyFromMapping(HomekitTaggedItem item, State state, Map<T, String> mapping, T defaultValue) {
+ public static <T> T getKeyFromMapping(HomekitTaggedItem item, State state, Map<T, Object> mapping, T defaultValue) {
LOGGER.trace("getKeyFromMapping: characteristic {}, state {}, mapping {}", item.getAccessoryType().getTag(),
state, mapping);
return defaultValue;
}
- return mapping.entrySet().stream().filter(entry -> value.equalsIgnoreCase(entry.getValue())).findAny()
- .map(Map.Entry::getKey).orElseGet(() -> {
- LOGGER.warn(
- "Wrong value {} for {} characteristic of the item {}. Expected one of following {}. Returning {}.",
- state.toString(), item.getAccessoryType().getTag(), item.getName(), mapping.values(),
- defaultValue);
- return defaultValue;
- });
+ return mapping.entrySet().stream().filter(entry -> {
+ Object mappingValue = entry.getValue();
+ if (mappingValue instanceof String stringValue) {
+ return value.equalsIgnoreCase(stringValue);
+ } else if (mappingValue instanceof List listValue) {
+ return listValue.stream().filter(listEntry -> value.equalsIgnoreCase(listEntry.toString())).findAny()
+ .isPresent();
+ } else {
+ LOGGER.warn("Found unexpected enum value type {}; this is a bug.", mappingValue.getClass());
+ return false;
+ }
+ }).findAny().map(Map.Entry::getKey).orElseGet(() -> {
+ LOGGER.warn(
+ "Wrong value {} for {} characteristic of the item {}. Expected one of following {}. Returning {}.",
+ state.toString(), item.getAccessoryType().getTag(), item.getName(), mapping.values(), defaultValue);
+ return defaultValue;
+ });
}
// supporting methods
}
private static <T extends CharacteristicEnum> CompletableFuture<T> getEnumFromItem(HomekitTaggedItem item,
- Map<T, String> mapping, T defaultValue) {
+ Map<T, Object> mapping, T defaultValue) {
return CompletableFuture
.completedFuture(getKeyFromMapping(item, item.getItem().getState(), mapping, defaultValue));
}
- public static <T extends Enum<T>> void setValueFromEnum(HomekitTaggedItem taggedItem, T value, Map<T, String> map) {
+ public static <T extends Enum<T>> void setValueFromEnum(HomekitTaggedItem taggedItem, T value, Map<T, Object> map) {
+ Object mapValue = map.get(value);
+ // if the mapping has multiple values for this enum, just use the first one for the command sent to the item
+ if (mapValue instanceof List listValue) {
+ if (listValue.isEmpty()) {
+ mapValue = null;
+ } else {
+ mapValue = listValue.get(0);
+ }
+ }
+ if (mapValue == null) {
+ LOGGER.warn("Unable to find mapping value for {} for item {}", value, taggedItem.getName());
+ return;
+ }
if (taggedItem.getBaseItem() instanceof NumberItem) {
- taggedItem.send(new DecimalType(Objects.requireNonNull(map.get(value))));
+ taggedItem.send(new DecimalType(mapValue.toString()));
} else if (taggedItem.getBaseItem() instanceof SwitchItem) {
- taggedItem.send(OnOffType.from(Objects.requireNonNull(map.get(value))));
+ taggedItem.send(OnOffType.from(mapValue.toString()));
} else {
- taggedItem.send(new StringType(map.get(value)));
+ taggedItem.send(new StringType(mapValue.toString()));
}
}
private static ProgrammableSwitchEventCharacteristic createProgrammableSwitchEventCharacteristic(
HomekitTaggedItem taggedItem, HomekitAccessoryUpdater updater) {
// have to build the map custom, since SINGLE_PRESS starts at 0
- Map<ProgrammableSwitchEnum, String> map = new EnumMap(ProgrammableSwitchEnum.class);
+ Map<ProgrammableSwitchEnum, Object> map = new EnumMap(ProgrammableSwitchEnum.class);
List<ProgrammableSwitchEnum> validValues = new ArrayList<>();
if (taggedItem.getBaseItem().getAcceptedDataTypes().contains(OnOffType.class)) {
private static class ProgrammableSwitchEventCharacteristicHelper {
private @Nullable ProgrammableSwitchEnum lastValue = null;
private final HomekitTaggedItem taggedItem;
- private final Map<ProgrammableSwitchEnum, String> map;
+ private final Map<ProgrammableSwitchEnum, Object> map;
private final HomekitAccessoryUpdater updater;
ProgrammableSwitchEventCharacteristicHelper(HomekitTaggedItem taggedItem, HomekitAccessoryUpdater updater,
- Map<ProgrammableSwitchEnum, String> map) {
+ Map<ProgrammableSwitchEnum, Object> map) {
this.taggedItem = taggedItem;
this.map = map;
this.updater = updater;
* @author Philipp Arndt - Initial contribution
*/
public class HomekitContactSensorImpl extends AbstractHomekitAccessoryImpl implements ContactSensorAccessory {
- private final Map<ContactStateEnum, String> mapping;
+ private final Map<ContactStateEnum, Object> mapping;
public HomekitContactSensorImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
List<Characteristic> mandatoryRawCharacteristics, HomekitAccessoryUpdater updater, HomekitSettings settings)
* @author Eugen Freiter - Initial contribution
*/
public class HomekitFilterMaintenanceImpl extends AbstractHomekitAccessoryImpl implements FilterMaintenanceAccessory {
- private final Map<FilterChangeIndicationEnum, String> mapping;
+ private final Map<FilterChangeIndicationEnum, Object> mapping;
public HomekitFilterMaintenanceImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
List<Characteristic> mandatoryRawCharacteristics, HomekitAccessoryUpdater updater, HomekitSettings settings)
public class HomekitHeaterCoolerImpl extends AbstractHomekitAccessoryImpl implements HeaterCoolerAccessory {
private final Logger logger = LoggerFactory.getLogger(HomekitHeaterCoolerImpl.class);
private final BooleanItemReader activeReader;
- private final Map<CurrentHeaterCoolerStateEnum, String> currentStateMapping;
- private final Map<TargetHeaterCoolerStateEnum, String> targetStateMapping;
+ private final Map<CurrentHeaterCoolerStateEnum, Object> currentStateMapping;
+ private final Map<TargetHeaterCoolerStateEnum, Object> targetStateMapping;
private final List<CurrentHeaterCoolerStateEnum> customCurrentStateList = new ArrayList<>();
private final List<TargetHeaterCoolerStateEnum> customTargetStateList = new ArrayList<>();
*/
@NonNullByDefault({})
public class HomekitIrrigationSystemImpl extends AbstractHomekitAccessoryImpl implements IrrigationSystemAccessory {
- private Map<InUseEnum, String> inUseMapping;
- private Map<ProgramModeEnum, String> programModeMap;
+ private Map<InUseEnum, Object> inUseMapping;
+ private Map<ProgramModeEnum, Object> programModeMap;
public HomekitIrrigationSystemImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
List<Characteristic> mandatoryRawCharacteristics, HomekitAccessoryUpdater updater, HomekitSettings settings)
* @author Tim Harper - Initial contribution
*/
public class HomekitLeakSensorImpl extends AbstractHomekitAccessoryImpl implements LeakSensorAccessory {
- private final Map<LeakDetectedStateEnum, String> mapping;
+ private final Map<LeakDetectedStateEnum, Object> mapping;
public HomekitLeakSensorImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
List<Characteristic> mandatoryRawCharacteristics, HomekitAccessoryUpdater updater, HomekitSettings settings)
*
*/
public class HomekitLockImpl extends AbstractHomekitAccessoryImpl implements LockMechanismAccessory {
- final Map<LockCurrentStateEnum, String> currentStateMapping;
- final Map<LockTargetStateEnum, String> targetStateMapping;
+ final Map<LockCurrentStateEnum, Object> currentStateMapping;
+ final Map<LockTargetStateEnum, Object> targetStateMapping;
public HomekitLockImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
List<Characteristic> mandatoryRawCharacteristics, HomekitAccessoryUpdater updater,
* @author Tim Harper - Initial contribution
*/
public class HomekitOccupancySensorImpl extends AbstractHomekitAccessoryImpl implements OccupancySensorAccessory {
- private final Map<OccupancyDetectedEnum, String> mapping;
+ private final Map<OccupancyDetectedEnum, Object> mapping;
public HomekitOccupancySensorImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
List<Characteristic> mandatoryRawCharacteristics, HomekitAccessoryUpdater updater, HomekitSettings settings)
* @author Cody Cutrer - Initial contribution
*/
public class HomekitSecuritySystemImpl extends AbstractHomekitAccessoryImpl implements SecuritySystemAccessory {
- private final Map<CurrentSecuritySystemStateEnum, String> currentStateMapping;
- private final Map<TargetSecuritySystemStateEnum, String> targetStateMapping;
+ private final Map<CurrentSecuritySystemStateEnum, Object> currentStateMapping;
+ private final Map<TargetSecuritySystemStateEnum, Object> targetStateMapping;
private final List<CurrentSecuritySystemStateEnum> customCurrentStateList = new ArrayList<>();
private final List<TargetSecuritySystemStateEnum> customTargetStateList = new ArrayList<>();
*/
public class HomekitSlatImpl extends AbstractHomekitAccessoryImpl implements SlatAccessory {
private static final String CONFIG_TYPE = "type";
- private final Map<CurrentSlatStateEnum, String> currentSlatStateMapping;
+ private final Map<CurrentSlatStateEnum, Object> currentSlatStateMapping;
private final SlatTypeEnum slatType;
public HomekitSlatImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
* @author Eugen Freiter - Initial contribution
*/
public class HomekitSmartSpeakerImpl extends AbstractHomekitAccessoryImpl implements SmartSpeakerAccessory {
- private final Map<CurrentMediaStateEnum, String> currentMediaState;
- private final Map<TargetMediaStateEnum, String> targetMediaState;
+ private final Map<CurrentMediaStateEnum, Object> currentMediaState;
+ private final Map<TargetMediaStateEnum, Object> targetMediaState;
public HomekitSmartSpeakerImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
List<Characteristic> mandatoryRawCharacteristics, HomekitAccessoryUpdater updater,
* @author Cody Cutrer - Initial contribution
*/
public class HomekitSmokeSensorImpl extends AbstractHomekitAccessoryImpl implements SmokeSensorAccessory {
- private final Map<SmokeDetectedStateEnum, String> mapping;
+ private final Map<SmokeDetectedStateEnum, Object> mapping;
public HomekitSmokeSensorImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
List<Characteristic> mandatoryRawCharacteristics, HomekitAccessoryUpdater updater, HomekitSettings settings)