[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:
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user