[homekit] increase flexibility of ColorTemperature (#13538)

* [homekit] increase flexibility of ColorTemperature

allow Number or Dimmer items, and mired or Kelvin units.

Signed-off-by: Cody Cutrer <cody@cutrer.us>
This commit is contained in:
Cody Cutrer
2022-10-22 12:24:51 -06:00
committed by GitHub
parent 16c0bae0dd
commit 66c7211b26
3 changed files with 155 additions and 8 deletions

View File

@@ -30,6 +30,7 @@ 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.PercentType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.State;
import org.openhab.core.types.StateDescription;
@@ -55,6 +56,7 @@ public class HomekitTaggedItem {
public final static String MIN_VALUE = "minValue";
public final static String PRIMARY_SERVICE = "primary";
public final static String STEP = "step";
public final static String UNIT = "unit";
private static final Map<Integer, String> CREATED_ACCESSORY_IDS = new ConcurrentHashMap<>();
@@ -181,6 +183,23 @@ public class HomekitTaggedItem {
getName());
}
/**
* Send QuantityType command to a NumberItem (or a Group:Number)
*
* @param command
*/
public void send(QuantityType command) {
if (getItem() instanceof GroupItem && getBaseItem() instanceof NumberItem) {
((GroupItem) getItem()).send(command);
return;
} else if (getItem() instanceof NumberItem) {
((NumberItem) getItem()).send(command);
return;
}
logger.warn("Received QuantityType command for item {} that doesn't support it. This is probably a bug.",
getName());
}
/**
* Send OnOffType command to a SwitchItem (or a Group:Switch)
*
@@ -322,6 +341,9 @@ public class HomekitTaggedItem {
if ((value instanceof Long) && (defaultValue instanceof BigDecimal)) {
return (T) BigDecimal.valueOf((Long) value);
}
if (defaultValue instanceof String) {
return (T) value.toString();
}
}
}
@@ -384,6 +406,27 @@ public class HomekitTaggedItem {
return getConfiguration(key, BigDecimal.valueOf(defaultValue)).doubleValue();
}
/**
* return configuration as quantity of the given unit
*
* @param key configuration key
* @param defaultValue default value
* @return value
*/
public QuantityType getConfigurationAsQuantity(String key, QuantityType defaultValue) {
String stringValue = getConfiguration(key, new String());
if (stringValue.isEmpty()) {
return defaultValue;
}
var parsedValue = new QuantityType(stringValue);
var convertedValue = parsedValue.toInvertibleUnit(defaultValue.getUnit());
// not convertible? just assume it's in the expected unit
if (convertedValue == null) {
return new QuantityType(parsedValue.toBigDecimal(), defaultValue.getUnit());
}
return convertedValue;
}
/**
* parse and apply item configuration.
*/

View File

@@ -18,6 +18,7 @@ import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@@ -43,6 +44,7 @@ import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.openhab.io.homekit.Homekit;
@@ -614,13 +616,83 @@ public class HomekitCharacteristicFactory {
private static ColorTemperatureCharacteristic createColorTemperatureCharacteristic(HomekitTaggedItem taggedItem,
HomekitAccessoryUpdater updater) {
int minValue = taggedItem.getConfigurationAsInt(HomekitTaggedItem.MIN_VALUE,
ColorTemperatureCharacteristic.DEFAULT_MIN_VALUE);
return new ColorTemperatureCharacteristic(minValue,
taggedItem.getConfigurationAsInt(HomekitTaggedItem.MAX_VALUE,
ColorTemperatureCharacteristic.DEFAULT_MAX_VALUE),
getIntSupplier(taggedItem, minValue), setIntConsumer(taggedItem),
getSubscriber(taggedItem, COLOR_TEMPERATURE, updater),
// Check if units are expressed in Kelvin, not mireds, and adjust
// the min/max appropriately
Unit unit = null;
var numberItem = taggedItem.getBaseItem();
if (numberItem instanceof NumberItem) {
unit = ((NumberItem) numberItem).getUnit();
}
if (unit == null) {
unit = Units.MIRED;
}
final Unit finalUnit = unit;
final boolean inverted = taggedItem.isInverted();
if (!unit.equals(Units.KELVIN) && !unit.equals(Units.MIRED)) {
logger.warn("Item {} must be in either K or mired. Given {}.", taggedItem.getName(), unit);
return new ColorTemperatureCharacteristic(null, null, null, null);
}
var minValueQt = taggedItem.getConfigurationAsQuantity(HomekitTaggedItem.MIN_VALUE,
Objects.requireNonNull(new QuantityType(ColorTemperatureCharacteristic.DEFAULT_MIN_VALUE, Units.MIRED)
.toInvertibleUnit(unit)));
var maxValueQt = taggedItem.getConfigurationAsQuantity(HomekitTaggedItem.MAX_VALUE,
Objects.requireNonNull(new QuantityType(ColorTemperatureCharacteristic.DEFAULT_MAX_VALUE, Units.MIRED)
.toInvertibleUnit(unit)));
int minValue = minValueQt.toInvertibleUnit(Units.MIRED).intValue();
int maxValue = maxValueQt.toInvertibleUnit(Units.MIRED).intValue();
// It's common to swap these if you're providing in Kelvin instead of mired
if (minValue > maxValue) {
int temp = minValue;
minValue = maxValue;
maxValue = temp;
}
final int finalMinValue = minValue;
final int range = maxValue - minValue;
return new ColorTemperatureCharacteristic(minValue, maxValue, () -> {
int value = finalMinValue;
final State state = taggedItem.getItem().getState();
if (state instanceof QuantityType<?>) {
// Number:Temperature
QuantityType<?> qt = (QuantityType<?>) state;
qt = qt.toInvertibleUnit(Units.MIRED);
if (qt == null) {
logger.warn("Item {}'s state '{}' is not convertible to mireds.", taggedItem.getName(), state);
} else {
value = qt.intValue();
}
} else if (state instanceof PercentType) {
double percent = ((PercentType) state).doubleValue();
// invert so that 0% == coolest
if (inverted) {
percent = 100.0 - percent;
}
// Dimmer
// scale to the originally configured range
value = (int) (percent * range / 100) + finalMinValue;
} else if (state instanceof DecimalType) {
value = ((DecimalType) state).intValue();
}
return CompletableFuture.completedFuture(value);
}, (value) -> {
if (taggedItem.getBaseItem() instanceof DimmerItem) {
// scale to a percent
double percent = (((double) value) - finalMinValue) * 100 / range;
if (inverted) {
percent = 100.0 - percent;
}
taggedItem.send(new PercentType(BigDecimal.valueOf(percent)));
} else if (taggedItem.getBaseItem() instanceof NumberItem) {
taggedItem.send(new QuantityType(value, Units.MIRED));
}
}, getSubscriber(taggedItem, COLOR_TEMPERATURE, updater),
getUnsubscriber(taggedItem, COLOR_TEMPERATURE, updater));
}