mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[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:
parent
16c0bae0dd
commit
66c7211b26
@ -243,6 +243,38 @@ Examples:
|
||||
Dimmer dimmer_light_3 "Dimmer Light 3" {homekit="Lighting, Lighting.Brightness" [dimmerMode="filterOnExceptBrightness100"]}
|
||||
```
|
||||
|
||||
### Color Temperature
|
||||
|
||||
Color temperature can be represented various ways in OpenHAB. Given the base bulb configured like this:
|
||||
|
||||
```xtend
|
||||
Group gLight "CCT Light" { homekit="Lighting" }
|
||||
Switch light_switch (gLight) { homekit="Lighting.OnState" }
|
||||
```
|
||||
|
||||
The color temperature might be configured in any of these ways:
|
||||
|
||||
```xtend
|
||||
// Number item presumed in mireds
|
||||
Number light_temp (gLight) { homekit="Lighting.ColorTemperature" }
|
||||
|
||||
// Number item explicitly in mireds
|
||||
Number:Temperature light_temp "Temp [%.0f mired]" { homekit="Lighting.ColorTemperature" }
|
||||
|
||||
// Number item explicitly in Kelvin
|
||||
Number:Temperature light_temp "Temp [%.0f K]" { homekit="Lighting.ColorTemperature" }
|
||||
|
||||
// Dimmer item, with allowed range given in mireds
|
||||
Dimmer light_temp { homekit="Lighting.ColorTemperature"[ minValue=50, maxValue=400 ]}
|
||||
|
||||
// Dimmer item, with allowed range given in Kelvin
|
||||
Dimmer light_temp { homekit="Lighting.ColorTemperature"[ minValue="2700 K", maxValue="5000 K" ]}
|
||||
|
||||
// Dimmer item, where 0% represents "warm" instead of "cool" (i.e. if it's backed by a channel
|
||||
// that's ultimately interpreting the value in Kelvin instead of mireds)
|
||||
Dimmer light_temp { homekit="Lighting.ColorTemperature"[ minValue="2700 K", maxValue="5000 K", inverted=true ]}
|
||||
```
|
||||
|
||||
### Windows Covering (Blinds) / Window / Door
|
||||
|
||||
HomeKit Windows Covering, Window and Door accessory types have following mandatory characteristics:
|
||||
@ -674,7 +706,7 @@ Support for this is planned for the future release of openHAB HomeKit binding.
|
||||
| | | Hue | Dimmer, Color | Hue |
|
||||
| | | Saturation | Dimmer, Color | Saturation in % (1-100) |
|
||||
| | | Brightness | Dimmer, Color | Brightness in % (1-100). See "Usage of dimmer modes" for configuration details. |
|
||||
| | | ColorTemperature | Number | Color temperature represented in reciprocal megaKelvin. The default value range is from 50 to 400. Color temperature should not be used in combination with hue, saturation and brightness. It supports following configuration parameters: minValue, maxValue |
|
||||
| | | ColorTemperature | Number, Dimmer | Color temperature. If the item is a Number with no units, it is represented in mireds. The default value range is from 50 to 400 (2500 K to 20,000 K). If the item is a Dimmer, it will be transformed linearly to mireds. Color temperature should not be used in combination with hue, saturation and brightness. It supports following configuration parameters: minValue, maxValue, inverted |
|
||||
| Fan | | | | Fan |
|
||||
| | ActiveStatus | | Switch, Dimmer | Accessory current working status. A value of "ON"/"OPEN" indicates that the accessory is active and is functioning without any errors. |
|
||||
| | | CurrentFanState | Number | Current fan state. values: 0=INACTIVE, 1=IDLE, 2=BLOWING AIR |
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user