diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitAccessoryImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitAccessoryImpl.java index 774533086..34dac4e48 100644 --- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitAccessoryImpl.java +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/AbstractHomekitAccessoryImpl.java @@ -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 > double convertAndRound(double value, Unit from, Unit 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 diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCharacteristicFactory.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCharacteristicFactory.java index 8a3fb18ef..8094f13ae 100644 --- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCharacteristicFactory.java +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitCharacteristicFactory.java @@ -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 CompletableFuture 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 > double convertAndRound(double value, Unit from, Unit 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> getAngleSupplier(HomekitTaggedItem taggedItem, int defaultValue) { return () -> CompletableFuture.completedFuture(getAngleFromItem(taggedItem, defaultValue)); @@ -305,6 +333,26 @@ public class HomekitCharacteristicFactory { }; } + private static Supplier> 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 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 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)); } diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitHeaterCoolerImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitHeaterCoolerImpl.java index bb3ad0f77..43aa8e2ca 100644 --- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitHeaterCoolerImpl.java +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitHeaterCoolerImpl.java @@ -108,9 +108,12 @@ public class HomekitHeaterCoolerImpl extends AbstractHomekitAccessoryImpl implem public CompletableFuture 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 getTemperatureDisplayUnit() { return CompletableFuture - .completedFuture(getSettings().useFahrenheitTemperature ? TemperatureDisplayUnitEnum.FAHRENHEIT + .completedFuture(HomekitCharacteristicFactory.useFahrenheit() ? TemperatureDisplayUnitEnum.FAHRENHEIT : TemperatureDisplayUnitEnum.CELSIUS); } diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitTemperatureSensorImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitTemperatureSensorImpl.java index c20574247..207140975 100644 --- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitTemperatureSensorImpl.java +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitTemperatureSensorImpl.java @@ -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 diff --git a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitThermostatImpl.java b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitThermostatImpl.java index 5c14a4ae9..105f0b1bb 100644 --- a/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitThermostatImpl.java +++ b/bundles/org.openhab.io.homekit/src/main/java/org/openhab/io/homekit/internal/accessories/HomekitThermostatImpl.java @@ -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 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 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 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