fix Fahrenheit conversion (#12024)

Signed-off-by: Eugen Freiter <freiter@gmx.de>
This commit is contained in:
eugen 2022-01-14 22:56:50 +01:00 committed by GitHub
parent 67ccf858f8
commit 843ca55c68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 107 additions and 53 deletions

View File

@ -12,8 +12,6 @@
*/
package org.openhab.io.homekit.internal.accessories;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -22,9 +20,6 @@ import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import javax.measure.Quantity;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.items.GenericItem;
@ -32,8 +27,6 @@ import org.openhab.core.items.Item;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
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.types.State;
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
import org.openhab.io.homekit.internal.HomekitCharacteristicType;
@ -282,32 +275,13 @@ abstract class AbstractHomekitAccessoryImpl implements HomekitAccessory {
characteristics.add(characteristic);
}
@NonNullByDefault
private <T extends Quantity<T>> double convertAndRound(double value, Unit<T> from, Unit<T> to) {
double rawValue = from.equals(to) ? value : from.getConverterTo(to).convert(value);
return new BigDecimal(rawValue).setScale(1, RoundingMode.HALF_UP).doubleValue();
}
@NonNullByDefault
protected double convertToCelsius(double degrees) {
return convertAndRound(degrees,
getSettings().useFahrenheitTemperature ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS, SIUnits.CELSIUS);
}
@NonNullByDefault
protected double convertFromCelsius(double degrees) {
return convertAndRound(degrees,
getSettings().useFahrenheitTemperature ? SIUnits.CELSIUS : ImperialUnits.FAHRENHEIT,
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
* @return boolean read
* @throws IncompleteAccessoryException
*/
@NonNullByDefault

View File

@ -15,6 +15,7 @@ package org.openhab.io.homekit.internal.accessories;
import static org.openhab.io.homekit.internal.HomekitCharacteristicType.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@ -22,6 +23,9 @@ import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;
import javax.measure.Quantity;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.items.GenericItem;
@ -35,13 +39,18 @@ import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.openhab.io.homekit.Homekit;
import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
import org.openhab.io.homekit.internal.HomekitCharacteristicType;
import org.openhab.io.homekit.internal.HomekitCommandType;
import org.openhab.io.homekit.internal.HomekitException;
import org.openhab.io.homekit.internal.HomekitImpl;
import org.openhab.io.homekit.internal.HomekitTaggedItem;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -170,6 +179,12 @@ public class HomekitCharacteristicFactory {
// METHODS TO CREATE SINGLE CHARACTERISTIC FROM OH ITEM
// supporting methods
public static boolean useFahrenheit() {
return FrameworkUtil.getBundle(HomekitImpl.class).getBundleContext()
.getServiceReference(Homekit.class.getName()).getProperty("useFahrenheitTemperature") == Boolean.TRUE;
}
private static <T extends CharacteristicEnum> CompletableFuture<T> getEnumFromItem(HomekitTaggedItem item,
T offEnum, T onEnum, T defaultEnum) {
final State state = item.getItem().getState();
@ -239,6 +254,19 @@ public class HomekitCharacteristicFactory {
return value;
}
private static <T extends Quantity<T>> double convertAndRound(double value, Unit<T> from, Unit<T> to) {
double rawValue = from.equals(to) ? value : from.getConverterTo(to).convert(value);
return new BigDecimal(rawValue).setScale(1, RoundingMode.HALF_UP).doubleValue();
}
public static double convertToCelsius(double degrees) {
return convertAndRound(degrees, useFahrenheit() ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS, SIUnits.CELSIUS);
}
public static double convertFromCelsius(double degrees) {
return convertAndRound(degrees, SIUnits.CELSIUS, useFahrenheit() ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS);
}
private static Supplier<CompletableFuture<Integer>> getAngleSupplier(HomekitTaggedItem taggedItem,
int defaultValue) {
return () -> CompletableFuture.completedFuture(getAngleFromItem(taggedItem, defaultValue));
@ -305,6 +333,26 @@ public class HomekitCharacteristicFactory {
};
}
private static Supplier<CompletableFuture<Double>> getTemperatureSupplier(HomekitTaggedItem taggedItem,
double defaultValue) {
return () -> {
final @Nullable DecimalType value = taggedItem.getItem().getStateAs(DecimalType.class);
return CompletableFuture
.completedFuture(value != null ? convertToCelsius(value.doubleValue()) : defaultValue);
};
}
private static ExceptionalConsumer<Double> setTemperatureConsumer(HomekitTaggedItem taggedItem) {
return (value) -> {
if (taggedItem.getItem() instanceof NumberItem) {
((NumberItem) taggedItem.getItem()).send(new DecimalType(convertFromCelsius(value)));
} else {
logger.warn("Item type {} is not supported for {}. Only Number type is supported.",
taggedItem.getItem().getType(), taggedItem.getName());
}
};
}
protected static Consumer<HomekitCharacteristicChangeCallback> getSubscriber(HomekitTaggedItem taggedItem,
HomekitCharacteristicType key, HomekitAccessoryUpdater updater) {
return (callback) -> updater.subscribe((GenericItem) taggedItem.getItem(), key.getTag(), callback);
@ -631,10 +679,10 @@ public class HomekitCharacteristicFactory {
CoolingThresholdTemperatureCharacteristic.DEFAULT_MAX_VALUE),
taggedItem.getConfigurationAsDouble(HomekitTaggedItem.STEP,
CoolingThresholdTemperatureCharacteristic.DEFAULT_STEP),
getDoubleSupplier(taggedItem,
getTemperatureSupplier(taggedItem,
taggedItem.getConfigurationAsDouble(HomekitTaggedItem.MIN_VALUE,
CoolingThresholdTemperatureCharacteristic.DEFAULT_MIN_VALUE)),
setDoubleConsumer(taggedItem), getSubscriber(taggedItem, COOLING_THRESHOLD_TEMPERATURE, updater),
setTemperatureConsumer(taggedItem), getSubscriber(taggedItem, COOLING_THRESHOLD_TEMPERATURE, updater),
getUnsubscriber(taggedItem, COOLING_THRESHOLD_TEMPERATURE, updater));
}
@ -647,10 +695,10 @@ public class HomekitCharacteristicFactory {
HeatingThresholdTemperatureCharacteristic.DEFAULT_MAX_VALUE),
taggedItem.getConfigurationAsDouble(HomekitTaggedItem.STEP,
HeatingThresholdTemperatureCharacteristic.DEFAULT_STEP),
getDoubleSupplier(taggedItem,
getTemperatureSupplier(taggedItem,
taggedItem.getConfigurationAsDouble(HomekitTaggedItem.MIN_VALUE,
HeatingThresholdTemperatureCharacteristic.DEFAULT_MIN_VALUE)),
setDoubleConsumer(taggedItem), getSubscriber(taggedItem, HEATING_THRESHOLD_TEMPERATURE, updater),
setTemperatureConsumer(taggedItem), getSubscriber(taggedItem, HEATING_THRESHOLD_TEMPERATURE, updater),
getUnsubscriber(taggedItem, HEATING_THRESHOLD_TEMPERATURE, updater));
}

View File

@ -108,9 +108,12 @@ public class HomekitHeaterCoolerImpl extends AbstractHomekitAccessoryImpl implem
public CompletableFuture<Double> getCurrentTemperature() {
final @Nullable DecimalType state = getStateAs(HomekitCharacteristicType.CURRENT_TEMPERATURE,
DecimalType.class);
return CompletableFuture.completedFuture(state != null ? convertToCelsius(state.doubleValue())
return CompletableFuture.completedFuture(state != null
? HomekitCharacteristicFactory.convertToCelsius(state.doubleValue())
: getAccessoryConfiguration(HomekitCharacteristicType.CURRENT_TEMPERATURE, HomekitTaggedItem.MIN_VALUE,
BigDecimal.valueOf(CurrentTemperatureCharacteristic.DEFAULT_MIN_VALUE)).doubleValue());
BigDecimal.valueOf(HomekitCharacteristicFactory
.convertFromCelsius(CurrentTemperatureCharacteristic.DEFAULT_MIN_VALUE)))
.doubleValue());
}
@Override
@ -151,7 +154,7 @@ public class HomekitHeaterCoolerImpl extends AbstractHomekitAccessoryImpl implem
public CompletableFuture<TemperatureDisplayUnitEnum> getTemperatureDisplayUnit() {
return CompletableFuture
.completedFuture(getSettings().useFahrenheitTemperature ? TemperatureDisplayUnitEnum.FAHRENHEIT
.completedFuture(HomekitCharacteristicFactory.useFahrenheit() ? TemperatureDisplayUnitEnum.FAHRENHEIT
: TemperatureDisplayUnitEnum.CELSIUS);
}

View File

@ -25,6 +25,7 @@ import org.openhab.io.homekit.internal.HomekitTaggedItem;
import io.github.hapjava.accessories.TemperatureSensorAccessory;
import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback;
import io.github.hapjava.characteristics.impl.thermostat.CurrentTemperatureCharacteristic;
import io.github.hapjava.characteristics.impl.thermostat.TargetTemperatureCharacteristic;
import io.github.hapjava.services.impl.TemperatureSensorService;
@ -46,7 +47,8 @@ class HomekitTemperatureSensorImpl extends AbstractHomekitAccessoryImpl implemen
final @Nullable DecimalType state = getStateAs(HomekitCharacteristicType.CURRENT_TEMPERATURE,
DecimalType.class);
return CompletableFuture
.completedFuture(state != null ? convertToCelsius(state.doubleValue()) : getMinCurrentTemperature());
.completedFuture(state != null ? HomekitCharacteristicFactory.convertToCelsius(state.doubleValue())
: getMinCurrentTemperature());
}
@Override
@ -56,16 +58,23 @@ class HomekitTemperatureSensorImpl extends AbstractHomekitAccessoryImpl implemen
@Override
public double getMinCurrentTemperature() {
return convertToCelsius(
// Apple defines default values in Celsius. We need to convert them to Fahrenheit if openHAB is using Fahrenheit
// convertToCelsius and convertFromCelsius are only converting if useFahrenheit is set to true, so no additional
// check here needed
return HomekitCharacteristicFactory.convertToCelsius(
getAccessoryConfiguration(HomekitCharacteristicType.CURRENT_TEMPERATURE, HomekitTaggedItem.MIN_VALUE,
BigDecimal.valueOf(TargetTemperatureCharacteristic.DEFAULT_MIN_VALUE)).doubleValue());
BigDecimal.valueOf(HomekitCharacteristicFactory
.convertFromCelsius(CurrentTemperatureCharacteristic.DEFAULT_MIN_VALUE)))
.doubleValue());
}
@Override
public double getMaxCurrentTemperature() {
return convertToCelsius(
return HomekitCharacteristicFactory.convertToCelsius(
getAccessoryConfiguration(HomekitCharacteristicType.CURRENT_TEMPERATURE, HomekitTaggedItem.MAX_VALUE,
BigDecimal.valueOf(TargetTemperatureCharacteristic.DEFAULT_MAX_VALUE)).doubleValue());
BigDecimal.valueOf(HomekitCharacteristicFactory
.convertFromCelsius(CurrentTemperatureCharacteristic.DEFAULT_MAX_VALUE)))
.doubleValue());
}
@Override

View File

@ -37,6 +37,7 @@ import org.slf4j.LoggerFactory;
import io.github.hapjava.accessories.ThermostatAccessory;
import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback;
import io.github.hapjava.characteristics.impl.thermostat.CurrentHeatingCoolingStateEnum;
import io.github.hapjava.characteristics.impl.thermostat.CurrentTemperatureCharacteristic;
import io.github.hapjava.characteristics.impl.thermostat.TargetHeatingCoolingStateEnum;
import io.github.hapjava.characteristics.impl.thermostat.TargetTemperatureCharacteristic;
import io.github.hapjava.characteristics.impl.thermostat.TemperatureDisplayUnitEnum;
@ -106,21 +107,31 @@ class HomekitThermostatImpl extends AbstractHomekitAccessoryImpl implements Ther
@Override
public CompletableFuture<Double> getCurrentTemperature() {
DecimalType state = getStateAs(HomekitCharacteristicType.CURRENT_TEMPERATURE, DecimalType.class);
return CompletableFuture.completedFuture(state != null ? convertToCelsius(state.doubleValue()) : 0.0);
return CompletableFuture
.completedFuture(state != null ? HomekitCharacteristicFactory.convertToCelsius(state.doubleValue())
: getMinCurrentTemperature());
}
@Override
public double getMinCurrentTemperature() {
return convertToCelsius(
// Apple defines default values in Celsius. We need to convert them to Fahrenheit if openHAB is using Fahrenheit
// convertToCelsius and convertFromCelsius are only converting if useFahrenheit is set to true, so no additional
// check here needed
return HomekitCharacteristicFactory.convertToCelsius(
getAccessoryConfiguration(HomekitCharacteristicType.CURRENT_TEMPERATURE, HomekitTaggedItem.MIN_VALUE,
BigDecimal.valueOf(TargetTemperatureCharacteristic.DEFAULT_MIN_VALUE)).doubleValue());
BigDecimal.valueOf(HomekitCharacteristicFactory
.convertFromCelsius(CurrentTemperatureCharacteristic.DEFAULT_MIN_VALUE)))
.doubleValue());
}
@Override
public double getMaxCurrentTemperature() {
return convertToCelsius(
return HomekitCharacteristicFactory.convertToCelsius(
getAccessoryConfiguration(HomekitCharacteristicType.CURRENT_TEMPERATURE, HomekitTaggedItem.MAX_VALUE,
BigDecimal.valueOf(TargetTemperatureCharacteristic.DEFAULT_MAX_VALUE)).doubleValue());
BigDecimal.valueOf(HomekitCharacteristicFactory
.convertFromCelsius(CurrentTemperatureCharacteristic.DEFAULT_MAX_VALUE)))
.doubleValue());
}
@Override
@ -138,7 +149,7 @@ class HomekitThermostatImpl extends AbstractHomekitAccessoryImpl implements Ther
@Override
public CompletableFuture<TemperatureDisplayUnitEnum> getTemperatureDisplayUnit() {
return CompletableFuture
.completedFuture(getSettings().useFahrenheitTemperature ? TemperatureDisplayUnitEnum.FAHRENHEIT
.completedFuture(HomekitCharacteristicFactory.useFahrenheit() ? TemperatureDisplayUnitEnum.FAHRENHEIT
: TemperatureDisplayUnitEnum.CELSIUS);
}
@ -150,7 +161,8 @@ class HomekitThermostatImpl extends AbstractHomekitAccessoryImpl implements Ther
@Override
public CompletableFuture<Double> getTargetTemperature() {
DecimalType state = getStateAs(HomekitCharacteristicType.TARGET_TEMPERATURE, DecimalType.class);
return CompletableFuture.completedFuture(state != null ? convertToCelsius(state.doubleValue()) : 0.0);
return CompletableFuture.completedFuture(
state != null ? HomekitCharacteristicFactory.convertToCelsius(state.doubleValue()) : 0.0);
}
@Override
@ -165,7 +177,7 @@ class HomekitThermostatImpl extends AbstractHomekitAccessoryImpl implements Ther
HomekitCharacteristicType.TARGET_TEMPERATURE);
if (characteristic.isPresent()) {
((NumberItem) characteristic.get().getItem())
.send(new DecimalType(BigDecimal.valueOf(convertFromCelsius(value))));
.send(new DecimalType(BigDecimal.valueOf(HomekitCharacteristicFactory.convertFromCelsius(value))));
} else {
logger.warn("Missing mandatory characteristic {}", HomekitCharacteristicType.TARGET_TEMPERATURE);
}
@ -173,16 +185,24 @@ class HomekitThermostatImpl extends AbstractHomekitAccessoryImpl implements Ther
@Override
public double getMinTargetTemperature() {
return convertToCelsius(
getAccessoryConfiguration(HomekitCharacteristicType.TARGET_TEMPERATURE, HomekitTaggedItem.MIN_VALUE,
BigDecimal.valueOf(TargetTemperatureCharacteristic.DEFAULT_MIN_VALUE)).doubleValue());
return HomekitCharacteristicFactory
.convertToCelsius(
getAccessoryConfiguration(HomekitCharacteristicType.TARGET_TEMPERATURE,
HomekitTaggedItem.MIN_VALUE,
BigDecimal.valueOf(HomekitCharacteristicFactory
.convertFromCelsius(TargetTemperatureCharacteristic.DEFAULT_MIN_VALUE)))
.doubleValue());
}
@Override
public double getMaxTargetTemperature() {
return convertToCelsius(
getAccessoryConfiguration(HomekitCharacteristicType.TARGET_TEMPERATURE, HomekitTaggedItem.MAX_VALUE,
BigDecimal.valueOf(TargetTemperatureCharacteristic.DEFAULT_MAX_VALUE)).doubleValue());
return HomekitCharacteristicFactory
.convertToCelsius(
getAccessoryConfiguration(HomekitCharacteristicType.TARGET_TEMPERATURE,
HomekitTaggedItem.MAX_VALUE,
BigDecimal.valueOf(HomekitCharacteristicFactory
.convertFromCelsius(TargetTemperatureCharacteristic.DEFAULT_MAX_VALUE)))
.doubleValue());
}
@Override