diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/NumberValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/NumberValue.java index e7afc3e6f..de6f5c6f2 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/NumberValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/NumberValue.java @@ -16,6 +16,8 @@ import java.math.BigDecimal; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.measure.Unit; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.library.CoreItemFactory; @@ -47,19 +49,19 @@ public class NumberValue extends Value { private final @Nullable BigDecimal min; private final @Nullable BigDecimal max; private final BigDecimal step; - private final String unit; + private final Unit unit; public NumberValue(@Nullable BigDecimal min, @Nullable BigDecimal max, @Nullable BigDecimal step, - @Nullable String unit) { + @Nullable Unit unit) { super(CoreItemFactory.NUMBER, Stream.of(QuantityType.class, IncreaseDecreaseType.class, UpDownType.class) .collect(Collectors.toList())); this.min = min; this.max = max; this.step = step == null ? BigDecimal.ONE : step; - this.unit = unit == null ? "" : unit; + this.unit = unit != null ? unit : Units.ONE; } - protected boolean checkConditions(BigDecimal newValue, DecimalType oldvalue) { + protected boolean checkConditions(BigDecimal newValue) { BigDecimal min = this.min; if (min != null && newValue.compareTo(min) == -1) { logger.trace("Number not accepted as it is below the configured minimum"); @@ -90,49 +92,54 @@ public class NumberValue extends Value { @Override public void update(Command command) throws IllegalArgumentException { - DecimalType oldvalue = (state == UnDefType.UNDEF) ? new DecimalType() : (DecimalType) state; BigDecimal newValue = null; if (command instanceof DecimalType) { - if (!checkConditions(((DecimalType) command).toBigDecimal(), oldvalue)) { - return; - } - state = (DecimalType) command; + newValue = ((DecimalType) command).toBigDecimal(); } else if (command instanceof IncreaseDecreaseType || command instanceof UpDownType) { + BigDecimal oldValue = getOldValue(); if (command == IncreaseDecreaseType.INCREASE || command == UpDownType.UP) { - newValue = oldvalue.toBigDecimal().add(step); + newValue = oldValue.add(step); } else { - newValue = oldvalue.toBigDecimal().subtract(step); + newValue = oldValue.subtract(step); } - if (!checkConditions(newValue, oldvalue)) { - return; - } - state = new DecimalType(newValue); } else if (command instanceof QuantityType) { - QuantityType qType = (QuantityType) command; - - if (qType.getUnit().isCompatible(Units.ONE)) { - newValue = qType.toBigDecimal(); - } else { - qType = qType.toUnit(unit); - if (qType != null) { - newValue = qType.toBigDecimal(); - } - } - if (newValue != null) { - if (!checkConditions(newValue, oldvalue)) { - return; - } - state = new DecimalType(newValue); - } + newValue = getQuantityTypeAsDecimal((QuantityType) command); } else { newValue = new BigDecimal(command.toString()); - if (!checkConditions(newValue, oldvalue)) { - return; - } + } + if (!checkConditions(newValue)) { + return; + } + // items with units specified in the label in the UI but no unit on mqtt are stored as + // DecimalType to avoid conversions (e.g. % expects 0-1 rather than 0-100) + if (!Units.ONE.equals(unit)) { + state = new QuantityType<>(newValue, unit); + } else { state = new DecimalType(newValue); } } + private BigDecimal getOldValue() { + BigDecimal val = BigDecimal.ZERO; + if (state instanceof DecimalType) { + val = ((DecimalType) state).toBigDecimal(); + } else if (state instanceof QuantityType) { + val = ((QuantityType) state).toBigDecimal(); + } + return val; + } + + private BigDecimal getQuantityTypeAsDecimal(QuantityType qType) { + BigDecimal val = qType.toBigDecimal(); + if (!qType.getUnit().isCompatible(Units.ONE)) { + QuantityType convertedType = qType.toUnit(unit); + if (convertedType != null) { + val = convertedType.toBigDecimal(); + } + } + return val; + } + @Override public StateDescriptionFragmentBuilder createStateDescription(boolean readOnly) { StateDescriptionFragmentBuilder builder = super.createStateDescription(readOnly); @@ -144,10 +151,6 @@ public class NumberValue extends Value { if (min != null) { builder = builder.withMinimum(min); } - builder = builder.withStep(step); - if (this.unit.length() > 0) { - builder = builder.withPattern("%s " + this.unit.replace("%", "%%")); - } - return builder; + return builder.withStep(step).withPattern("%s %unit%"); } } diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ValueFactory.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ValueFactory.java index 12c20895f..70bc0ebeb 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ValueFactory.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/ValueFactory.java @@ -16,6 +16,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.mqtt.generic.ChannelConfig; import org.openhab.binding.mqtt.generic.internal.MqttBindingConstants; import org.openhab.binding.mqtt.generic.mapping.ColorMode; +import org.openhab.core.types.util.UnitUtils; /** * A factory t @@ -24,6 +25,7 @@ import org.openhab.binding.mqtt.generic.mapping.ColorMode; */ @NonNullByDefault public class ValueFactory { + /** * Creates a new channel state value. * @@ -47,7 +49,7 @@ public class ValueFactory { value = new LocationValue(); break; case MqttBindingConstants.NUMBER: - value = new NumberValue(config.min, config.max, config.step, config.unit); + value = new NumberValue(config.min, config.max, config.step, UnitUtils.parseUnit(config.unit)); break; case MqttBindingConstants.DIMMER: value = new PercentageValue(config.min, config.max, config.step, config.on, config.off); diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateTests.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateTests.java index 5753c9372..75a582261 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateTests.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/ChannelStateTests.java @@ -52,6 +52,7 @@ import org.openhab.core.io.transport.mqtt.MqttBrokerConnection; import org.openhab.core.library.types.HSBType; import org.openhab.core.library.types.RawType; import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.Units; import org.openhab.core.thing.ChannelUID; /** @@ -185,6 +186,36 @@ public class ChannelStateTests { assertThat(value.getChannelState().toString(), is("16.0")); } + @Test + public void receiveDecimalUnitTest() { + NumberValue value = new NumberValue(null, null, new BigDecimal(10), Units.WATT); + ChannelState c = spy(new ChannelState(config, channelUID, value, channelStateUpdateListener)); + c.start(connection, mock(ScheduledExecutorService.class), 100); + + c.processMessage("state", "15".getBytes()); + assertThat(value.getChannelState().toString(), is("15 W")); + + c.processMessage("state", "INCREASE".getBytes()); + assertThat(value.getChannelState().toString(), is("25 W")); + + c.processMessage("state", "DECREASE".getBytes()); + assertThat(value.getChannelState().toString(), is("15 W")); + + verify(channelStateUpdateListener, times(3)).updateChannelState(eq(channelUID), any()); + } + + @Test + public void receiveDecimalAsPercentageUnitTest() { + NumberValue value = new NumberValue(null, null, new BigDecimal(10), Units.PERCENT); + ChannelState c = spy(new ChannelState(config, channelUID, value, channelStateUpdateListener)); + c.start(connection, mock(ScheduledExecutorService.class), 100); + + c.processMessage("state", "63.7".getBytes()); + assertThat(value.getChannelState().toString(), is("63.7 %")); + + verify(channelStateUpdateListener, times(1)).updateChannelState(eq(channelUID), any()); + } + @Test public void receivePercentageTest() { PercentageValue value = new PercentageValue(new BigDecimal(-100), new BigDecimal(100), new BigDecimal(10), null, diff --git a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/values/ValueTests.java b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/values/ValueTests.java index 6d9d02f17..eb39ed243 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/values/ValueTests.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/test/java/org/openhab/binding/mqtt/generic/values/ValueTests.java @@ -26,8 +26,11 @@ import org.openhab.core.library.types.IncreaseDecreaseType; 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.types.QuantityType; import org.openhab.core.library.types.StringType; import org.openhab.core.library.types.UpDownType; +import org.openhab.core.library.unit.MetricPrefix; +import org.openhab.core.library.unit.Units; import org.openhab.core.types.Command; import org.openhab.core.types.TypeParser; @@ -160,6 +163,39 @@ public class ValueTests { assertThat(v.getChannelState(), is(OpenClosedType.OPEN)); } + @Test + public void numberUpdate() { + NumberValue v = new NumberValue(null, null, new BigDecimal(10), Units.WATT); + + // Test with command with units + v.update(new QuantityType<>(20, Units.WATT)); + assertThat(v.getMQTTpublishValue(null), is("20")); + assertThat(v.getChannelState(), is(new QuantityType<>(20, Units.WATT))); + v.update(new QuantityType<>(20, MetricPrefix.KILO(Units.WATT))); + assertThat(v.getMQTTpublishValue(null), is("20000")); + assertThat(v.getChannelState(), is(new QuantityType<>(20, MetricPrefix.KILO(Units.WATT)))); + + // Test with command without units + v.update(new QuantityType<>("20")); + assertThat(v.getMQTTpublishValue(null), is("20")); + assertThat(v.getChannelState(), is(new QuantityType<>(20, Units.WATT))); + } + + @Test + public void numberPercentageUpdate() { + NumberValue v = new NumberValue(null, null, new BigDecimal(10), Units.PERCENT); + + // Test with command with units + v.update(new QuantityType<>(20, Units.PERCENT)); + assertThat(v.getMQTTpublishValue(null), is("20")); + assertThat(v.getChannelState(), is(new QuantityType<>(20, Units.PERCENT))); + + // Test with command without units + v.update(new QuantityType<>("20")); + assertThat(v.getMQTTpublishValue(null), is("20")); + assertThat(v.getChannelState(), is(new QuantityType<>(20, Units.PERCENT))); + } + @Test public void rollershutterUpdateWithStrings() { RollershutterValue v = new RollershutterValue("fancyON", "fancyOff", "fancyStop"); diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Climate.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Climate.java index 09021686d..41531396a 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Climate.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Climate.java @@ -17,6 +17,9 @@ import java.util.Arrays; import java.util.List; import java.util.function.Predicate; +import javax.measure.Unit; +import javax.measure.quantity.Temperature; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.mqtt.generic.ChannelStateUpdateListener; @@ -27,6 +30,8 @@ import org.openhab.binding.mqtt.generic.values.Value; import org.openhab.binding.mqtt.homeassistant.internal.ComponentChannel; import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration; 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.Command; import org.openhab.core.types.State; @@ -53,10 +58,28 @@ public class Climate extends AbstractComponent { public static final String TEMPERATURE_LOW_CH_ID = "temperatureLow"; public static final String POWER_CH_ID = "power"; - private static final String CELSIUM = "C"; - private static final String FAHRENHEIT = "F"; - private static final float DEFAULT_CELSIUM_PRECISION = 0.1f; - private static final float DEFAULT_FAHRENHEIT_PRECISION = 1f; + public static enum TemperatureUnit { + @SerializedName("C") + CELSIUS(SIUnits.CELSIUS, new BigDecimal("0.1")), + @SerializedName("F") + FAHRENHEIT(ImperialUnits.FAHRENHEIT, BigDecimal.ONE); + + private final Unit unit; + private final BigDecimal defaultPrecision; + + TemperatureUnit(Unit unit, BigDecimal defaultPrecision) { + this.unit = unit; + this.defaultPrecision = defaultPrecision; + } + + public Unit getUnit() { + return unit; + } + + public BigDecimal getDefaultPrecision() { + return defaultPrecision; + } + } private static final String ACTION_OFF = "off"; private static final State ACTION_OFF_STATE = new StringType(ACTION_OFF); @@ -175,14 +198,14 @@ public class Climate extends AbstractComponent { protected Integer initial = 21; @SerializedName("max_temp") - protected @Nullable Float maxTemp; + protected @Nullable BigDecimal maxTemp; @SerializedName("min_temp") - protected @Nullable Float minTemp; + protected @Nullable BigDecimal minTemp; @SerializedName("temperature_unit") - protected String temperatureUnit = CELSIUM; // System unit by default + protected TemperatureUnit temperatureUnit = TemperatureUnit.CELSIUS; // System unit by default @SerializedName("temp_step") - protected Float tempStep = 1f; - protected @Nullable Float precision; + protected BigDecimal tempStep = BigDecimal.ONE; + protected @Nullable BigDecimal precision; @SerializedName("send_if_off") protected Boolean sendIfOff = true; } @@ -190,13 +213,8 @@ public class Climate extends AbstractComponent { public Climate(ComponentFactory.ComponentConfiguration componentConfiguration) { super(componentConfiguration, ChannelConfiguration.class); - BigDecimal minTemp = channelConfiguration.minTemp != null ? BigDecimal.valueOf(channelConfiguration.minTemp) - : null; - BigDecimal maxTemp = channelConfiguration.maxTemp != null ? BigDecimal.valueOf(channelConfiguration.maxTemp) - : null; - float precision = channelConfiguration.precision != null ? channelConfiguration.precision - : (FAHRENHEIT.equals(channelConfiguration.temperatureUnit) ? DEFAULT_FAHRENHEIT_PRECISION - : DEFAULT_CELSIUM_PRECISION); + BigDecimal precision = channelConfiguration.precision != null ? channelConfiguration.precision + : channelConfiguration.temperatureUnit.getDefaultPrecision(); final ChannelStateUpdateListener updateListener = componentConfiguration.getUpdateListener(); ComponentChannel actionChannel = buildOptionalChannel(ACTION_CH_ID, @@ -214,7 +232,8 @@ public class Climate extends AbstractComponent { channelConfiguration.awayModeStateTopic, commandFilter); buildOptionalChannel(CURRENT_TEMPERATURE_CH_ID, - new NumberValue(minTemp, maxTemp, BigDecimal.valueOf(precision), channelConfiguration.temperatureUnit), + new NumberValue(channelConfiguration.minTemp, channelConfiguration.maxTemp, precision, + channelConfiguration.temperatureUnit.getUnit()), updateListener, null, null, channelConfiguration.currentTemperatureTemplate, channelConfiguration.currentTemperatureTopic, commandFilter); @@ -237,22 +256,22 @@ public class Climate extends AbstractComponent { channelConfiguration.swingStateTemplate, channelConfiguration.swingStateTopic, commandFilter); buildOptionalChannel(TEMPERATURE_CH_ID, - new NumberValue(minTemp, maxTemp, BigDecimal.valueOf(channelConfiguration.tempStep), - channelConfiguration.temperatureUnit), + new NumberValue(channelConfiguration.minTemp, channelConfiguration.maxTemp, + channelConfiguration.tempStep, channelConfiguration.temperatureUnit.getUnit()), updateListener, channelConfiguration.temperatureCommandTemplate, channelConfiguration.temperatureCommandTopic, channelConfiguration.temperatureStateTemplate, channelConfiguration.temperatureStateTopic, commandFilter); buildOptionalChannel(TEMPERATURE_HIGH_CH_ID, - new NumberValue(minTemp, maxTemp, BigDecimal.valueOf(channelConfiguration.tempStep), - channelConfiguration.temperatureUnit), + new NumberValue(channelConfiguration.minTemp, channelConfiguration.maxTemp, + channelConfiguration.tempStep, channelConfiguration.temperatureUnit.getUnit()), updateListener, channelConfiguration.temperatureHighCommandTemplate, channelConfiguration.temperatureHighCommandTopic, channelConfiguration.temperatureHighStateTemplate, channelConfiguration.temperatureHighStateTopic, commandFilter); buildOptionalChannel(TEMPERATURE_LOW_CH_ID, - new NumberValue(minTemp, maxTemp, BigDecimal.valueOf(channelConfiguration.tempStep), - channelConfiguration.temperatureUnit), + new NumberValue(channelConfiguration.minTemp, channelConfiguration.maxTemp, + channelConfiguration.tempStep, channelConfiguration.temperatureUnit.getUnit()), updateListener, channelConfiguration.temperatureLowCommandTemplate, channelConfiguration.temperatureLowCommandTopic, channelConfiguration.temperatureLowStateTemplate, channelConfiguration.temperatureLowStateTopic, commandFilter); diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Sensor.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Sensor.java index 0451c276f..4b4bdcccb 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Sensor.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/Sensor.java @@ -23,6 +23,7 @@ import org.openhab.binding.mqtt.generic.values.TextValue; import org.openhab.binding.mqtt.generic.values.Value; import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChannelConfiguration; import org.openhab.binding.mqtt.homeassistant.internal.listener.ExpireUpdateStateListener; +import org.openhab.core.types.util.UnitUtils; import com.google.gson.annotations.SerializedName; @@ -71,7 +72,7 @@ public class Sensor extends AbstractComponent { String uom = channelConfiguration.unitOfMeasurement; if (uom != null && !uom.isBlank()) { - value = new NumberValue(null, null, null, uom); + value = new NumberValue(null, null, null, UnitUtils.parseUnit(uom)); } else { value = new TextValue(); } diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/ClimateTests.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/ClimateTests.java index ccb50ea55..9234b0810 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/ClimateTests.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/ClimateTests.java @@ -23,7 +23,10 @@ import org.openhab.binding.mqtt.generic.values.OnOffValue; import org.openhab.binding.mqtt.generic.values.TextValue; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; +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; /** * Tests for {@link Climate} @@ -82,10 +85,10 @@ public class ClimateTests extends AbstractComponentTests { + "\"current_heating_setpoint\": \"24\"}"); assertState(component, Climate.ACTION_CH_ID, new StringType("off")); assertState(component, Climate.AWAY_MODE_CH_ID, OnOffType.ON); - assertState(component, Climate.CURRENT_TEMPERATURE_CH_ID, new DecimalType(22.2)); + assertState(component, Climate.CURRENT_TEMPERATURE_CH_ID, new QuantityType<>(22.2, SIUnits.CELSIUS)); assertState(component, Climate.HOLD_CH_ID, new StringType("schedule")); assertState(component, Climate.MODE_CH_ID, new StringType("heat")); - assertState(component, Climate.TEMPERATURE_CH_ID, new DecimalType(24)); + assertState(component, Climate.TEMPERATURE_CH_ID, new QuantityType<>(24, SIUnits.CELSIUS)); component.getChannel(Climate.AWAY_MODE_CH_ID).getState().publishValue(OnOffType.OFF); assertPublished("zigbee2mqtt/th1/set/away_mode", "OFF"); @@ -146,10 +149,10 @@ public class ClimateTests extends AbstractComponentTests { + "\"current_heating_setpoint\": \"24\"}"); assertState(component, Climate.ACTION_CH_ID, new StringType("off")); assertState(component, Climate.AWAY_MODE_CH_ID, OnOffType.ON); - assertState(component, Climate.CURRENT_TEMPERATURE_CH_ID, new DecimalType(22.2)); + assertState(component, Climate.CURRENT_TEMPERATURE_CH_ID, new QuantityType<>(22.2, SIUnits.CELSIUS)); assertState(component, Climate.HOLD_CH_ID, new StringType("schedule")); assertState(component, Climate.MODE_CH_ID, new StringType("heat")); - assertState(component, Climate.TEMPERATURE_CH_ID, new DecimalType(24)); + assertState(component, Climate.TEMPERATURE_CH_ID, new QuantityType<>(24, SIUnits.CELSIUS)); // Climate is in OFF state component.getChannel(Climate.AWAY_MODE_CH_ID).getState().publishValue(OnOffType.OFF); @@ -260,14 +263,14 @@ public class ClimateTests extends AbstractComponentTests { assertState(component, Climate.ACTION_CH_ID, new StringType("fan")); assertState(component, Climate.AUX_CH_ID, OnOffType.ON); assertState(component, Climate.AWAY_MODE_CH_ID, OnOffType.OFF); - assertState(component, Climate.CURRENT_TEMPERATURE_CH_ID, new DecimalType(35.5)); + assertState(component, Climate.CURRENT_TEMPERATURE_CH_ID, new QuantityType<>(35.5, ImperialUnits.FAHRENHEIT)); assertState(component, Climate.FAN_MODE_CH_ID, new StringType("p2")); assertState(component, Climate.HOLD_CH_ID, new StringType("u2")); assertState(component, Climate.MODE_CH_ID, new StringType("B1")); assertState(component, Climate.SWING_CH_ID, new StringType("G1")); - assertState(component, Climate.TEMPERATURE_CH_ID, new DecimalType(30)); - assertState(component, Climate.TEMPERATURE_HIGH_CH_ID, new DecimalType(37)); - assertState(component, Climate.TEMPERATURE_LOW_CH_ID, new DecimalType(20)); + assertState(component, Climate.TEMPERATURE_CH_ID, new QuantityType<>(30, ImperialUnits.FAHRENHEIT)); + assertState(component, Climate.TEMPERATURE_HIGH_CH_ID, new QuantityType<>(37, ImperialUnits.FAHRENHEIT)); + assertState(component, Climate.TEMPERATURE_LOW_CH_ID, new QuantityType<>(20, ImperialUnits.FAHRENHEIT)); component.getChannel(Climate.AUX_CH_ID).getState().publishValue(OnOffType.OFF); assertPublished("zigbee2mqtt/th1/aux", "OFF"); @@ -291,6 +294,7 @@ public class ClimateTests extends AbstractComponentTests { assertPublished("zigbee2mqtt/th1/power", "OFF"); } + @Override protected Set getConfigTopics() { return Set.of(CONFIG_TOPIC); } diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/HAConfigurationTests.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/HAConfigurationTests.java index 384babbc2..9d3a405d2 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/HAConfigurationTests.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/HAConfigurationTests.java @@ -19,6 +19,7 @@ import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.math.BigDecimal; import java.util.Arrays; import java.util.List; @@ -171,18 +172,18 @@ public class HAConfigurationTests { assertThat(config.holdStateTemplate, is("{{ value_json.preset }}")); assertThat(config.holdStateTopic, is("zigbee2mqtt/th1")); assertThat(config.jsonAttributesTopic, is("zigbee2mqtt/th1")); - assertThat(config.maxTemp, is(35f)); - assertThat(config.minTemp, is(5f)); + assertThat(config.maxTemp, is(new BigDecimal(35))); + assertThat(config.minTemp, is(new BigDecimal(5))); assertThat(config.modeCommandTopic, is("zigbee2mqtt/th1/set/system_mode")); assertThat(config.modeStateTemplate, is("{{ value_json.system_mode }}")); assertThat(config.modeStateTopic, is("zigbee2mqtt/th1")); assertThat(config.modes, is(List.of("heat", "auto", "off"))); assertThat(config.getName(), is("th1")); - assertThat(config.tempStep, is(0.5f)); + assertThat(config.tempStep, is(new BigDecimal("0.5"))); assertThat(config.temperatureCommandTopic, is("zigbee2mqtt/th1/set/current_heating_setpoint")); assertThat(config.temperatureStateTemplate, is("{{ value_json.current_heating_setpoint }}")); assertThat(config.temperatureStateTopic, is("zigbee2mqtt/th1")); - assertThat(config.temperatureUnit, is("C")); + assertThat(config.temperatureUnit, is(Climate.TemperatureUnit.CELSIUS)); assertThat(config.getUniqueId(), is("0x847127fffe11dd6a_climate_zigbee2mqtt")); assertThat(config.initial, is(21)); @@ -240,11 +241,11 @@ public class HAConfigurationTests { assertThat(config.temperatureLowStateTopic, is("T")); assertThat(config.powerCommandTopic, is("U")); assertThat(config.initial, is(10)); - assertThat(config.maxTemp, is(40f)); - assertThat(config.minTemp, is(0f)); - assertThat(config.temperatureUnit, is("F")); - assertThat(config.tempStep, is(1f)); - assertThat(config.precision, is(0.5f)); + assertThat(config.maxTemp, is(new BigDecimal(40))); + assertThat(config.minTemp, is(BigDecimal.ZERO)); + assertThat(config.temperatureUnit, is(Climate.TemperatureUnit.FAHRENHEIT)); + assertThat(config.tempStep, is(BigDecimal.ONE)); + assertThat(config.precision, is(new BigDecimal("0.5"))); assertThat(config.sendIfOff, is(false)); } } diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/SensorTests.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/SensorTests.java index d630073ef..eba2a730c 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/SensorTests.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/test/java/org/openhab/binding/mqtt/homeassistant/internal/component/SensorTests.java @@ -19,7 +19,8 @@ import java.util.Set; import org.junit.jupiter.api.Test; import org.openhab.binding.mqtt.generic.values.NumberValue; -import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; import org.openhab.core.types.UnDefType; /** @@ -67,15 +68,16 @@ public class SensorTests extends AbstractComponentTests { NumberValue.class); publishMessage("zigbee2mqtt/sensor/state", "10"); - assertState(component, Sensor.SENSOR_CHANNEL_ID, DecimalType.valueOf("10")); + assertState(component, Sensor.SENSOR_CHANNEL_ID, new QuantityType<>(10, Units.WATT)); publishMessage("zigbee2mqtt/sensor/state", "20"); - assertState(component, Sensor.SENSOR_CHANNEL_ID, DecimalType.valueOf("20")); + assertState(component, Sensor.SENSOR_CHANNEL_ID, new QuantityType<>(20, Units.WATT)); assertThat(component.getChannel(Sensor.SENSOR_CHANNEL_ID).getState().getCache().createStateDescription(true) - .build().getPattern(), is("%s W")); + .build().getPattern(), is("%s %unit%")); waitForAssert(() -> assertState(component, Sensor.SENSOR_CHANNEL_ID, UnDefType.UNDEF), 10000, 200); } + @Override protected Set getConfigTopics() { return Set.of(CONFIG_TOPIC); } diff --git a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/Property.java b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/Property.java index b87e525c2..016ecfc22 100644 --- a/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/Property.java +++ b/bundles/org.openhab.binding.mqtt.homie/src/main/java/org/openhab/binding/mqtt/homie/internal/homie300/Property.java @@ -48,6 +48,7 @@ import org.openhab.core.thing.type.AutoUpdatePolicy; import org.openhab.core.thing.type.ChannelType; import org.openhab.core.thing.type.ChannelTypeBuilder; import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.types.util.UnitUtils; import org.openhab.core.util.UIDUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -216,7 +217,7 @@ public class Property implements AttributeChanged { if (attributes.unit.contains("%") && attributes.settable) { value = new PercentageValue(min, max, step, null, null); } else { - value = new NumberValue(min, max, step, attributes.unit); + value = new NumberValue(min, max, step, UnitUtils.parseUnit(attributes.unit)); } break; case datetime_: