From d17d712ff2710dc87ae729f34ecff35e6d3dec5a Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Tue, 14 Nov 2023 08:43:07 -0700 Subject: [PATCH] [mqtt] Treat incoming empty string as UNDEF for most types Signed-off-by: Cody Cutrer --- .../generic/AbstractMQTTThingHandler.java | 1 + .../binding/mqtt/generic/ChannelConfig.java | 2 ++ .../mqtt/generic/values/NumberValue.java | 3 +- .../generic/values/RollershutterValue.java | 5 +++ .../mqtt/generic/values/TextValue.java | 8 ++++- .../binding/mqtt/generic/values/Value.java | 9 +++++ .../mqtt/generic/values/ValueFactory.java | 6 ++++ .../OH-INF/config/color-channel-config.xml | 6 ++++ .../OH-INF/config/dimmer-channel-config.xml | 8 +++++ .../OH-INF/config/number-channel-config.xml | 6 ++++ .../config/rollershutter-channel-config.xml | 7 ++++ .../OH-INF/config/string-channel-config.xml | 5 +++ .../OH-INF/config/switch-channel-config.xml | 6 ++++ .../resources/OH-INF/i18n/mqtt.properties | 12 +++++++ .../mqtt/generic/values/ValueTests.java | 36 +++++++++++++++++++ .../homie/internal/homie300/Property.java | 8 +++++ 16 files changed, 126 insertions(+), 2 deletions(-) diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/AbstractMQTTThingHandler.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/AbstractMQTTThingHandler.java index 2e7ccfc065620..1a5e75972e179 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/AbstractMQTTThingHandler.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/AbstractMQTTThingHandler.java @@ -304,6 +304,7 @@ public void addAvailabilityTopic(String availability_topic, String payload_avail @Nullable TransformationServiceProvider transformationServiceProvider) { availabilityStates.computeIfAbsent(availability_topic, topic -> { Value value = new OnOffValue(payload_available, payload_not_available); + value.setUndefValue(""); ChannelGroupUID groupUID = new ChannelGroupUID(getThing().getUID(), "availability"); ChannelUID channelUID = new ChannelUID(groupUID, UIDUtils.encode(topic)); ChannelState state = new ChannelState(ChannelConfigBuilder.create().withStateTopic(topic).build(), diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/ChannelConfig.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/ChannelConfig.java index 39ff6f45710d3..861df492e7380 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/ChannelConfig.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/ChannelConfig.java @@ -43,6 +43,7 @@ public class ChannelConfig { /** If true, the state topic will not update a state, but trigger a channel instead. */ public boolean trigger = false; public String unit = ""; + public boolean emptyStringIsUndef = true; public String transformationPattern = ""; public String transformationPatternOut = ""; @@ -57,6 +58,7 @@ public class ChannelConfig { public @Nullable String stop; public @Nullable String onState; public @Nullable String offState; + public @Nullable String undefValue; public int onBrightness = 10; public String colorMode = ColorMode.HSB.toString(); 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 453d41764cb1f..7760dddabb339 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 @@ -120,7 +120,8 @@ public Command parseCommand(Command command) throws IllegalArgumentException { @Override public Type parseMessage(Command command) throws IllegalArgumentException { - if (command instanceof StringType && command.toString().equalsIgnoreCase(NAN)) { + if (command instanceof StringType + && (command.toString().equalsIgnoreCase(NAN) || command.toString().equals(undefValue))) { return UnDefType.UNDEF; } return parseCommand(command); diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/RollershutterValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/RollershutterValue.java index 0d1ac80973bd3..d02601158026b 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/RollershutterValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/RollershutterValue.java @@ -22,6 +22,8 @@ import org.openhab.core.library.types.StringType; import org.openhab.core.library.types.UpDownType; import org.openhab.core.types.Command; +import org.openhab.core.types.Type; +import org.openhab.core.types.UnDefType; /** * Implements a rollershutter value. @@ -134,6 +136,9 @@ public Command parseCommand(Command command) throws IllegalArgumentException { @Override public Type parseMessage(Command command) throws IllegalArgumentException { + if (command instanceof StringType string && string.toString().equals(undefValue)) { + return UnDefType.UNDEF; + } command = parseType(command, upStateString, downStateString); if (inverted && command instanceof PercentType percentType) { return new PercentType(100 - percentType.intValue()); diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java index dc44ae2e346a8..49aed144b30ec 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/TextValue.java @@ -28,6 +28,8 @@ import org.openhab.core.types.CommandOption; import org.openhab.core.types.StateDescriptionFragmentBuilder; import org.openhab.core.types.StateOption; +import org.openhab.core.types.Type; +import org.openhab.core.types.UnDefType; /** * Implements a text/string value. Allows to restrict the incoming value to a set of states. @@ -91,7 +93,11 @@ public StringType parseCommand(Command command) throws IllegalArgumentException } @Override - public StringType parseMessage(Command command) throws IllegalArgumentException { + public Type parseMessage(Command command) throws IllegalArgumentException { + if (command instanceof StringType string && string.toString().equals(undefValue)) { + return UnDefType.UNDEF; + } + final Set states = this.states; String valueStr = command.toString(); if (states != null && !states.contains(valueStr)) { diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/Value.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/Value.java index 76839cf514704..b0c9c15877ca7 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/Value.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/Value.java @@ -23,6 +23,7 @@ import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.RawType; +import org.openhab.core.library.types.StringType; import org.openhab.core.types.Command; import org.openhab.core.types.CommandDescriptionBuilder; import org.openhab.core.types.State; @@ -54,6 +55,7 @@ public abstract class Value { protected State state = UnDefType.UNDEF; protected final List> commandTypes; + protected @Nullable String undefValue = null; private final String itemType; protected Value(String itemType, List> commandTypes) { @@ -61,6 +63,10 @@ protected Value(String itemType, List> commandTypes) { this.commandTypes = commandTypes; } + public void setUndefValue(@Nullable String undefValue) { + this.undefValue = undefValue; + } + /** * Return a list of supported command types. The order of the list is important. *

@@ -147,6 +153,9 @@ public void update(State newState) throws IllegalArgumentException { * @exception IllegalArgumentException Thrown if for example a text is assigned to a number type. */ public Type parseMessage(Command command) throws IllegalArgumentException { + if (command instanceof StringType string && string.toString().equals(undefValue)) { + return UnDefType.UNDEF; + } return parseCommand(command); } 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 8525630e40643..c60c675a84702 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 @@ -86,6 +86,12 @@ public static Value createValueState(ChannelConfig config, String channelTypeID) default: throw new IllegalArgumentException("ChannelTypeUID not recognised: " + channelTypeID); } + + if (config.emptyStringIsUndef) { + value.setUndefValue(""); + } else { + value.setUndefValue(config.undefValue); + } return value; } } diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/color-channel-config.xml b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/color-channel-config.xml index 13c1dbf401d3e..8d9a4a03676c9 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/color-channel-config.xml +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/color-channel-config.xml @@ -89,6 +89,12 @@ false true + + + If the received MQTT value is empty, treat it as UNDEF instead of logging an error and ignoring it. + true + true + diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/dimmer-channel-config.xml b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/dimmer-channel-config.xml index b5cc03cd843ed..7b293ba4a5da9 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/dimmer-channel-config.xml +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/dimmer-channel-config.xml @@ -78,6 +78,14 @@ false true + + + If the received MQTT value is empty, treat it as UNDEF instead of logging an error and ignoring it. + true + true + + + diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/number-channel-config.xml b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/number-channel-config.xml index be146d14c5813..a28e53bf4d269 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/number-channel-config.xml +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/number-channel-config.xml @@ -78,6 +78,12 @@ false true + + + If the received MQTT value is empty, treat it as UNDEF instead of logging an error and ignoring it. + true + true + diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/rollershutter-channel-config.xml b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/rollershutter-channel-config.xml index bde8aa507b265..cafc7db57114d 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/rollershutter-channel-config.xml +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/rollershutter-channel-config.xml @@ -79,6 +79,13 @@ false true + + + If the received MQTT value is empty, treat it as UNDEF instead of logging an error and ignoring it. + true + true + + A string (like "OPEN") that is sent when commanding the rollershutter to open. If not provided, 0 (or diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/string-channel-config.xml b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/string-channel-config.xml index 238c15de671df..2a2520bef7b84 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/string-channel-config.xml +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/string-channel-config.xml @@ -78,6 +78,11 @@ false true + + + If the received MQTT value matches this, treat it as UNDEF instead of logging an error and ignoring it. + true + diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/switch-channel-config.xml b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/switch-channel-config.xml index 72d9e16124935..44595cafa5fa6 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/switch-channel-config.xml +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/config/switch-channel-config.xml @@ -78,6 +78,12 @@ false true + + + If the received MQTT value is empty, treat it as UNDEF instead of logging an error and ignoring it. + true + true + diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/i18n/mqtt.properties b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/i18n/mqtt.properties index 297eca2f473a1..4d56b04d56dc1 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/i18n/mqtt.properties +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/resources/OH-INF/i18n/mqtt.properties @@ -42,6 +42,8 @@ thing-type.config.mqtt.color_channel.colorMode.option.RGB = RGB (Red, Green, Blu thing-type.config.mqtt.color_channel.colorMode.option.XYY = CIE xyY (x, y, Brightness) thing-type.config.mqtt.color_channel.commandTopic.label = MQTT Command Topic thing-type.config.mqtt.color_channel.commandTopic.description = An MQTT topic that this thing will send a command to. If not set, this will be a read-only switch. +thing-type.config.mqtt.color_channel.emptyStringIsUndef.label = Treat Empty String as UNDEF +thing-type.config.mqtt.color_channel.emptyStringIsUndef.description = If the received MQTT value is empty, treat it as UNDEF instead of logging an error and ignoring it. thing-type.config.mqtt.color_channel.formatBeforePublish.label = Outgoing Value Format thing-type.config.mqtt.color_channel.formatBeforePublish.description = Format a value before it is published to the MQTT broker. The default is to just pass the channel/item state. If you want to apply a prefix, say "MYCOLOR,", you would use "MYCOLOR,%s". If you want to adjust the precision of a number to for example 4 digits, you would use "%.4f". thing-type.config.mqtt.color_channel.group.transformations.label = Transform Values @@ -69,6 +71,8 @@ thing-type.config.mqtt.color_channel.transformationPatternOut.label = Outgoing V thing-type.config.mqtt.color_channel.transformationPatternOut.description = Applies a transformation before publishing a MQTT topic value. Transformations are specialised in extracting a value, but some transformations like the MAP one could be useful. thing-type.config.mqtt.dimmer_channel.commandTopic.label = MQTT Command Topic thing-type.config.mqtt.dimmer_channel.commandTopic.description = An MQTT topic that this thing will send a command to. If not set, this will be a read-only switch. +thing-type.config.mqtt.dimmer_channel.emptyStringIsUndef.label = Treat Empty String as UNDEF +thing-type.config.mqtt.dimmer_channel.emptyStringIsUndef.description = If the received MQTT value is empty, treat it as UNDEF instead of logging an error and ignoring it. thing-type.config.mqtt.dimmer_channel.formatBeforePublish.label = Outgoing Value Format thing-type.config.mqtt.dimmer_channel.formatBeforePublish.description = Format a value before it is published to the MQTT broker. The default is to just pass the channel/item state. If you want to apply a prefix, say "MYCOLOR,", you would use "MYCOLOR,%s". If you want to adjust the precision of a number to for example 4 digits, you would use "%.4f". thing-type.config.mqtt.dimmer_channel.group.transformations.label = Transform Values @@ -100,6 +104,8 @@ thing-type.config.mqtt.dimmer_channel.transformationPatternOut.label = Outgoing thing-type.config.mqtt.dimmer_channel.transformationPatternOut.description = Applies a transformation before publishing a MQTT topic value. Transformations are specialised in extracting a value, but some transformations like the MAP one could be useful. thing-type.config.mqtt.number_channel.commandTopic.label = MQTT Command Topic thing-type.config.mqtt.number_channel.commandTopic.description = An MQTT topic that this thing will send a command to. If not set, this will be a read-only switch. +thing-type.config.mqtt.number_channel.emptyStringIsUndef.label = Treat Empty String as UNDEF +thing-type.config.mqtt.number_channel.emptyStringIsUndef.description = If the received MQTT value is empty, treat it as UNDEF instead of logging an error and ignoring it. thing-type.config.mqtt.number_channel.formatBeforePublish.label = Outgoing Value Format thing-type.config.mqtt.number_channel.formatBeforePublish.description = Format a value before it is published to the MQTT broker. The default is to just pass the channel/item state. If you want to apply a prefix, say "MYCOLOR,", you would use "MYCOLOR,%s". If you want to adjust the precision of a number to for example 4 digits, you would use "%.4f". thing-type.config.mqtt.number_channel.group.transformations.label = Transform Values @@ -129,6 +135,8 @@ thing-type.config.mqtt.number_channel.unit.label = Unit Of Measurement thing-type.config.mqtt.number_channel.unit.description = Unit of measurement (optional). The unit is used for representing the value in the GUI as well as for converting incoming values (like from '°F' to '°C'). Examples: "°C", "°F" thing-type.config.mqtt.rollershutter_channel.commandTopic.label = MQTT Command Topic thing-type.config.mqtt.rollershutter_channel.commandTopic.description = An MQTT topic that this thing will send a command to. If not set, this will be a read-only rollershutter. +thing-type.config.mqtt.rollershutter_channel.emptyStringIsUndef.label = Treat Empty String as UNDEF +thing-type.config.mqtt.rollershutter_channel.emptyStringIsUndef.description = If the received MQTT value is empty, treat it as UNDEF instead of logging an error and ignoring it. thing-type.config.mqtt.rollershutter_channel.formatBeforePublish.label = Outgoing Value Format thing-type.config.mqtt.rollershutter_channel.formatBeforePublish.description = Format a value before it is published to the MQTT broker. The default is to just pass the channel/item state. If you want to apply a prefix, say "MYCOLOR,", you would use "MYCOLOR,%s". If you want to adjust the precision of a number to for example 4 digits, you would use "%.4f". thing-type.config.mqtt.rollershutter_channel.group.transformations.label = Transform Values @@ -185,8 +193,12 @@ thing-type.config.mqtt.string_channel.transformationPattern.label = Incoming Val thing-type.config.mqtt.string_channel.transformationPattern.description = Applies transformations to an incoming MQTT topic value. A transformation example for a received JSON would be "JSONPATH:$.device.status.temperature" for a json {device: {status: { temperature: 23.2 }}}. You can chain transformations by separating them with the intersection character ∩. thing-type.config.mqtt.string_channel.transformationPatternOut.label = Outgoing Value Transformation thing-type.config.mqtt.string_channel.transformationPatternOut.description = Applies a transformation before publishing a MQTT topic value. Transformations are specialised in extracting a value, but some transformations like the MAP one could be useful. +thing-type.config.mqtt.string_channel.undefValue.label = UNDEF Value +thing-type.config.mqtt.string_channel.undefValue.description = If the received MQTT value matches this, treat it as UNDEF instead of logging an error and ignoring it. thing-type.config.mqtt.switch_channel.commandTopic.label = MQTT Command Topic thing-type.config.mqtt.switch_channel.commandTopic.description = An MQTT topic that this thing will send a command to. If not set, this will be a read-only switch. +thing-type.config.mqtt.switch_channel.emptyStringIsUndef.label = Treat Empty String as UNDEF +thing-type.config.mqtt.switch_channel.emptyStringIsUndef.description = If the received MQTT value is empty, treat it as UNDEF instead of logging an error and ignoring it. thing-type.config.mqtt.switch_channel.formatBeforePublish.label = Outgoing Value Format thing-type.config.mqtt.switch_channel.formatBeforePublish.description = Format a value before it is published to the MQTT broker. The default is to just pass the channel/item state. If you want to apply a prefix, say "MYCOLOR,", you would use "MYCOLOR,%s". If you want to adjust the precision of a number to for example 4 digits, you would use "%.4f". thing-type.config.mqtt.switch_channel.group.transformations.label = Transform Values 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 9ce6525d09d16..b5964dc42ad50 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 @@ -82,36 +82,44 @@ public void colorUpdate() { assertThat(hsb.getBrightness().intValue(), is(0)); hsb = (HSBType) v.parseCommand(p(v, "1")); assertThat(hsb.getBrightness().intValue(), is(1)); + + v.setUndefValue(""); + assertThat(v.parseMessage(new StringType("")), is(UnDefType.UNDEF)); } @Test public void illegalColorUpdate() { ColorValue v = new ColorValue(ColorMode.RGB, null, null, 10); assertThrows(IllegalArgumentException.class, () -> v.parseCommand(p(v, "255,255,abc"))); + assertThrows(IllegalArgumentException.class, () -> v.parseCommand(new StringType(""))); } @Test public void illegalNumberCommand() { NumberValue v = new NumberValue(null, null, null, null); assertThrows(IllegalArgumentException.class, () -> v.parseCommand(OnOffType.OFF)); + assertThrows(IllegalArgumentException.class, () -> v.parseCommand(new StringType(""))); } @Test public void illegalPercentCommand() { PercentageValue v = new PercentageValue(null, null, null, null, null); assertThrows(IllegalStateException.class, () -> v.parseCommand(new StringType("demo"))); + assertThrows(IllegalStateException.class, () -> v.parseCommand(new StringType(""))); } @Test public void illegalOnOffCommand() { OnOffValue v = new OnOffValue(null, null); assertThrows(IllegalArgumentException.class, () -> v.parseCommand(new DecimalType(101.0))); + assertThrows(IllegalArgumentException.class, () -> v.parseCommand(new StringType(""))); } @Test public void illegalPercentUpdate() { PercentageValue v = new PercentageValue(null, null, null, null, null); assertThrows(IllegalArgumentException.class, () -> v.parseCommand(new DecimalType(101.0))); + assertThrows(IllegalStateException.class, () -> v.parseCommand(new StringType(""))); } @Test @@ -137,6 +145,9 @@ public void onoffUpdate() { // Test custom formatting assertThat(v.getMQTTpublishValue(OnOffType.OFF, "=%s"), is("=fancyOff")); assertThat(v.getMQTTpublishValue(OnOffType.ON, "=%s"), is("=fancyON")); + + v.setUndefValue(""); + assertThat(v.parseMessage(new StringType("")), is(UnDefType.UNDEF)); } @Test @@ -158,6 +169,9 @@ public void openCloseUpdate() { // Test basic formatting assertThat(v.getMQTTpublishValue(OpenClosedType.CLOSED, null), is("fancyOff")); assertThat(v.getMQTTpublishValue(OpenClosedType.OPEN, null), is("fancyON")); + + v.setUndefValue(""); + assertThat(v.parseMessage(new StringType("")), is(UnDefType.UNDEF)); } @Test @@ -179,6 +193,9 @@ public void numberUpdate() { assertThat(v.parseMessage(new StringType("NaN")), is(UnDefType.UNDEF)); assertThat(v.parseMessage(new StringType("nan")), is(UnDefType.UNDEF)); + + v.setUndefValue(""); + assertThat(v.parseMessage(new StringType("")), is(UnDefType.UNDEF)); } @Test @@ -231,6 +248,9 @@ public void rollershutterUpdateWithStrings() { // Test parsing from MQTT assertThat(v.parseMessage(new StringType("fancyON")), is(UpDownType.UP)); assertThat(v.parseMessage(new StringType("fancyOff")), is(UpDownType.DOWN)); + + v.setUndefValue(""); + assertThat(v.parseMessage(new StringType("")), is(UnDefType.UNDEF)); } @Test @@ -298,6 +318,9 @@ public void percentMQTTValue() { command = v.parseCommand(new DecimalType(i)); assertThat(v.getMQTTpublishValue(command, null), is("" + i)); } + + v.setUndefValue(""); + assertThat(v.parseMessage(new StringType("")), is(UnDefType.UNDEF)); } @Test @@ -382,4 +405,17 @@ public void percentCalcInvalid() { null); assertThrows(IllegalArgumentException.class, () -> v.parseCommand(new DecimalType(9.0))); } + + @Test + public void textUpdate() { + TextValue v = new TextValue(); + + v.setUndefValue(""); + assertThat(v.parseMessage(new StringType("")), is(UnDefType.UNDEF)); + assertThat(v.parseMessage(new StringType("NULL")), is(new StringType("NULL"))); + + v.setUndefValue("NULL"); + assertThat(v.parseMessage(new StringType("NULL")), is(UnDefType.UNDEF)); + assertThat(v.parseMessage(new StringType("")), is(new StringType(""))); + } } 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 f6591405d9d0b..06a2b5b79b1e4 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 @@ -162,12 +162,15 @@ private void createChannelTypeFromAttributes() { switch (attributes.datatype) { case boolean_: value = new OnOffValue("true", "false"); + value.setUndefValue(""); break; case color_: if ("hsv".equals(attributes.format)) { value = new ColorValue(ColorMode.HSB, null, null, 100); + value.setUndefValue(""); } else if ("rgb".equals(attributes.format)) { value = new ColorValue(ColorMode.RGB, null, null, 100); + value.setUndefValue(""); } else { logger.warn("Non supported color format: '{}'. Only 'hsv' and 'rgb' are supported", attributes.format); @@ -177,6 +180,9 @@ private void createChannelTypeFromAttributes() { case enum_: String[] enumValues = attributes.format.split(","); value = new TextValue(enumValues); + if (enumValues.length != 0) { + value.setUndefValue(""); + } break; case float_: case integer_: @@ -195,9 +201,11 @@ private void createChannelTypeFromAttributes() { } else { value = new NumberValue(min, max, step, unit); } + value.setUndefValue(""); break; case datetime_: value = new DateTimeValue(); + value.setUndefValue(""); break; case string_: case unknown: