[mqtt.generic] Add UOM to inbound values for MQTT Channels (#10727)
* Add UOM for MQTT Channels Signed-off-by: James Melville <jamesmelville@gmail.com> * Fix dependencies Signed-off-by: James Melville <jamesmelville@gmail.com> * Simplify units parsing, remove channelUID from NumberValue constructor Signed-off-by: James Melville <jamesmelville@gmail.com> * Simplify pattern Signed-off-by: James Melville <jamesmelville@gmail.com> * Fix tests Signed-off-by: James Melville <jamesmelville@gmail.com> * Correct Units reference Signed-off-by: James Melville <jamesmelville@gmail.com> * Correct homeassistant binding changes Signed-off-by: James Melville <jamesmelville@gmail.com> * Wrap precision in temperature unit definition Signed-off-by: James Melville <jamesmelville@gmail.com> * Use BigDecimal for precision Signed-off-by: James Melville <jamesmelville@gmail.com> * Use BigDecimal throughout Signed-off-by: James Melville <jamesmelville@gmail.com> * Fix SAT Signed-off-by: James Melville <jamesmelville@gmail.com> * Inverty equals check Signed-off-by: James Melville <jamesmelville@gmail.com>
This commit is contained in:
@@ -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%");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user