From 09ba1bb41dd6700bdb06dd1bc018295987b1e362 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Tue, 23 Jul 2024 13:56:38 -0600 Subject: [PATCH] [mqtt.generic] Send ON/OFF for dimmer channels when so configured (#15929) Similar to how UP/DOWN are processed for rollershutters. Signed-off-by: Cody Cutrer Signed-off-by: Ciprian Pascu --- .../mqtt/generic/values/PercentageValue.java | 39 ++++++++++++----- .../OH-INF/config/dimmer-channel-config.xml | 12 ++---- .../resources/OH-INF/i18n/mqtt.properties | 12 +++--- .../mqtt/generic/values/ValueTests.java | 42 ++++++++++--------- .../internal/component/JSONSchemaLight.java | 14 ++++--- 5 files changed, 70 insertions(+), 49 deletions(-) diff --git a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/PercentageValue.java b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/PercentageValue.java index 77643506ce2..e2d37a7b506 100644 --- a/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/PercentageValue.java +++ b/bundles/org.openhab.binding.mqtt.generic/src/main/java/org/openhab/binding/mqtt/generic/values/PercentageValue.java @@ -72,7 +72,7 @@ public class PercentageValue extends Value { } @Override - public PercentType parseCommand(Command command) throws IllegalArgumentException { + public Command parseCommand(Command command) throws IllegalArgumentException { PercentType oldvalue = (state instanceof UnDefType) ? new PercentType() : (PercentType) state; // Nothing do to -> We have received a percentage if (command instanceof PercentType percent) { @@ -105,8 +105,8 @@ public class PercentageValue extends Value { } } else // // On/Off equals 100 or 0 percent - if (command instanceof OnOffType increaseDecreaseCommand) { - return increaseDecreaseCommand == OnOffType.ON ? PercentType.HUNDRED : PercentType.ZERO; + if (command instanceof OnOffType) { + return command; } else// // Increase or decrease by "step" if (command instanceof UpDownType upDownCommand) { @@ -121,9 +121,9 @@ public class PercentageValue extends Value { // Check against custom on/off values if (command instanceof StringType) { if (onValue != null && command.toString().equals(onValue)) { - return new PercentType(max); + return OnOffType.ON; } else if (offValue != null && command.toString().equals(offValue)) { - return new PercentType(min); + return OnOffType.OFF; } else { throw new IllegalStateException("Unable to parse " + command.toString() + " as a percent."); } @@ -135,17 +135,36 @@ public class PercentageValue extends Value { @Override public String getMQTTpublishValue(Command command, @Nullable String pattern) { + String formatPattern = pattern; + if (formatPattern == null) { + formatPattern = "%s"; + } + + if (command instanceof OnOffType onOffCommand) { + if (onOffCommand == OnOffType.ON) { + if (onValue != null) { + command = new StringType(onValue); + } else { + command = PercentType.HUNDRED; + } + } else { + if (offValue != null) { + command = new StringType(offValue); + } else { + command = PercentType.ZERO; + } + } + } + if (command instanceof StringType) { + return command.format(formatPattern); + } + // Formula: From percentage to custom min/max: value*span/100+min // Calculation need to happen with big decimals to either return a straight integer or a decimal depending on // the value. BigDecimal value = ((PercentType) command).toBigDecimal().multiply(span).divide(HUNDRED, MathContext.DECIMAL128) .add(min).stripTrailingZeros(); - String formatPattern = pattern; - if (formatPattern == null) { - formatPattern = "%s"; - } - return new DecimalType(value).format(formatPattern); } 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 b5cc03cd843..38fcaa72d30 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 @@ -97,16 +97,12 @@ true - - A number (like 1, 10) or a string (like "enabled") that is additionally recognised as on/open state. You - can use this parameter for a second keyword, next to ON (OPEN respectively on a Contact). - 1 + + A string (like "ON") that is sent to MQTT when an ON command is received, instead of an explicit 100%. - - A number (like 0, -10) or a string (like "disabled") that is additionally recognised as off/closed - state. You can use this parameter for a second keyword, next to OFF (CLOSED respectively on a Contact). - 0 + + A string (like "OFF") that is sent to MQTT when an OFF command is received, instead of an explicit 0%. 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 655291465fb..c7e6188782a 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 @@ -77,10 +77,10 @@ thing-type.config.mqtt.dimmer_channel.max.label = Absolute Maximum thing-type.config.mqtt.dimmer_channel.max.description = This configuration represents the maximum of the allowed range. For a percentage channel that equals one-hundred percent. thing-type.config.mqtt.dimmer_channel.min.label = Absolute Minimum thing-type.config.mqtt.dimmer_channel.min.description = This configuration represents the minimum of the allowed range. For a percentage channel that equals zero percent. -thing-type.config.mqtt.dimmer_channel.off.label = Custom Off/Closed Value -thing-type.config.mqtt.dimmer_channel.off.description = A number (like 0, -10) or a string (like "disabled") that is additionally recognised as off/closed state. You can use this parameter for a second keyword, next to OFF (CLOSED respectively on a Contact). -thing-type.config.mqtt.dimmer_channel.on.label = Custom On/Open Value -thing-type.config.mqtt.dimmer_channel.on.description = A number (like 1, 10) or a string (like "enabled") that is additionally recognised as on/open state. You can use this parameter for a second keyword, next to ON (OPEN respectively on a Contact). +thing-type.config.mqtt.dimmer_channel.off.label = Custom OFF Command +thing-type.config.mqtt.dimmer_channel.off.description = A string (like "OFF") that is sent to MQTT when an OFF command is received, instead of an explicit 0%. +thing-type.config.mqtt.dimmer_channel.on.label = Custom ON Command +thing-type.config.mqtt.dimmer_channel.on.description = A string (like "ON") that is sent to MQTT when an ON command is received, instead of an explicit 100%. thing-type.config.mqtt.dimmer_channel.postCommand.label = Is Command thing-type.config.mqtt.dimmer_channel.postCommand.description = If the received MQTT value should not only update the state of linked items, but command them, enable this option. thing-type.config.mqtt.dimmer_channel.qos.label = QoS @@ -170,6 +170,8 @@ thing-type.config.mqtt.string_channel.formatBeforePublish.label = Outgoing Value thing-type.config.mqtt.string_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.string_channel.group.transformations.label = Transform Values thing-type.config.mqtt.string_channel.group.transformations.description = These configuration parameters allow you to alter a value before it is published to MQTT or before a received value is assigned to an item. +thing-type.config.mqtt.string_channel.nullValue.label = NULL Value +thing-type.config.mqtt.string_channel.nullValue.description = If the received MQTT value matches this, treat it as NULL. thing-type.config.mqtt.string_channel.postCommand.label = Is Command thing-type.config.mqtt.string_channel.postCommand.description = If the received MQTT value should not only update the state of linked items, but command them, enable this option. thing-type.config.mqtt.string_channel.qos.label = QoS @@ -185,8 +187,6 @@ 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.nullValue.label = NULL Value -thing-type.config.mqtt.string_channel.nullValue.description = If the received MQTT value matches this, treat it as NULL. 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.formatBeforePublish.label = Outgoing Value Format 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 21a9046b0e0..dd95a8e784b 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 @@ -310,8 +310,10 @@ public class ValueTests { assertThat(v.parseCommand(new DecimalType(10.0)), is(PercentType.ZERO)); assertThat(v.getMQTTpublishValue(PercentType.ZERO, null), is("10")); - assertThat(v.parseCommand(OnOffType.ON), is(PercentType.HUNDRED)); - assertThat(v.parseCommand(OnOffType.OFF), is(PercentType.ZERO)); + assertThat(v.parseCommand(OnOffType.ON), is(OnOffType.ON)); + assertThat(v.getMQTTpublishValue(OnOffType.ON, null), is("110")); + assertThat(v.parseCommand(OnOffType.OFF), is(OnOffType.OFF)); + assertThat(v.getMQTTpublishValue(OnOffType.OFF, null), is("10")); } @Test @@ -332,8 +334,10 @@ public class ValueTests { public void percentCustomOnOff() { PercentageValue v = new PercentageValue(new BigDecimal("0.0"), new BigDecimal("100.0"), new BigDecimal("1.0"), "on", "off"); - assertThat(v.parseCommand(new StringType("on")), is(PercentType.HUNDRED)); - assertThat(v.parseCommand(new StringType("off")), is(PercentType.ZERO)); + assertThat(v.parseCommand(new StringType("on")), is(OnOffType.ON)); + assertThat(v.getMQTTpublishValue(OnOffType.ON, "%s"), is("on")); + assertThat(v.parseCommand(new StringType("off")), is(OnOffType.OFF)); + assertThat(v.getMQTTpublishValue(OnOffType.OFF, "%s"), is("off")); } @Test @@ -342,7 +346,7 @@ public class ValueTests { null, null); assertThat(v.parseCommand(new DecimalType(1.0)), is(PercentType.HUNDRED)); assertThat(v.parseCommand(new DecimalType(0.1)), is(PercentType.ZERO)); - PercentType command = v.parseCommand(new DecimalType(0.2)); + PercentType command = (PercentType) v.parseCommand(new DecimalType(0.2)); assertEquals(command.floatValue(), 11.11f, 0.01f); } @@ -352,26 +356,26 @@ public class ValueTests { null, null); // Normal operation. - PercentType command = v.parseCommand(new DecimalType("6.0")); + PercentType command = (PercentType) v.parseCommand(new DecimalType("6.0")); assertEquals(command.floatValue(), 50.0f, 0.01f); v.update(command); - command = v.parseCommand(IncreaseDecreaseType.INCREASE); + command = (PercentType) v.parseCommand(IncreaseDecreaseType.INCREASE); assertEquals(command.floatValue(), 55.0f, 0.01f); - command = v.parseCommand(IncreaseDecreaseType.DECREASE); + command = (PercentType) v.parseCommand(IncreaseDecreaseType.DECREASE); assertEquals(command.floatValue(), 45.0f, 0.01f); // Lower limit. - command = v.parseCommand(new DecimalType("1.1")); + command = (PercentType) v.parseCommand(new DecimalType("1.1")); assertEquals(command.floatValue(), 1.0f, 0.01f); v.update(command); - command = v.parseCommand(IncreaseDecreaseType.DECREASE); + command = (PercentType) v.parseCommand(IncreaseDecreaseType.DECREASE); assertEquals(command.floatValue(), 0.0f, 0.01f); // Upper limit. - command = v.parseCommand(new DecimalType("10.8")); + command = (PercentType) v.parseCommand(new DecimalType("10.8")); assertEquals(command.floatValue(), 98.0f, 0.01f); v.update(command); - command = v.parseCommand(IncreaseDecreaseType.INCREASE); + command = (PercentType) v.parseCommand(IncreaseDecreaseType.INCREASE); assertEquals(command.floatValue(), 100.0f, 0.01f); } @@ -381,26 +385,26 @@ public class ValueTests { null, null); // Normal operation. - PercentType command = v.parseCommand(new DecimalType("6.0")); + PercentType command = (PercentType) v.parseCommand(new DecimalType("6.0")); assertEquals(command.floatValue(), 50.0f, 0.01f); v.update(command); - command = v.parseCommand(UpDownType.UP); + command = (PercentType) v.parseCommand(UpDownType.UP); assertEquals(command.floatValue(), 55.0f, 0.01f); - command = v.parseCommand(UpDownType.DOWN); + command = (PercentType) v.parseCommand(UpDownType.DOWN); assertEquals(command.floatValue(), 45.0f, 0.01f); // Lower limit. - command = v.parseCommand(new DecimalType("1.1")); + command = (PercentType) v.parseCommand(new DecimalType("1.1")); assertEquals(command.floatValue(), 1.0f, 0.01f); v.update(command); - command = v.parseCommand(UpDownType.DOWN); + command = (PercentType) v.parseCommand(UpDownType.DOWN); assertEquals(command.floatValue(), 0.0f, 0.01f); // Upper limit. - command = v.parseCommand(new DecimalType("10.8")); + command = (PercentType) v.parseCommand(new DecimalType("10.8")); assertEquals(command.floatValue(), 98.0f, 0.01f); v.update(command); - command = v.parseCommand(UpDownType.UP); + command = (PercentType) v.parseCommand(UpDownType.UP); assertEquals(command.floatValue(), 100.0f, 0.01f); } diff --git a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/JSONSchemaLight.java b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/JSONSchemaLight.java index 1a279d553c2..b678d3830ea 100644 --- a/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/JSONSchemaLight.java +++ b/bundles/org.openhab.binding.mqtt.homeassistant/src/main/java/org/openhab/binding/mqtt/homeassistant/internal/component/JSONSchemaLight.java @@ -255,13 +255,15 @@ public class JSONSchemaLight extends AbstractRawSchemaLight { boolean off = false; if (jsonState.state != null) { - onOffValue.update(onOffValue.parseCommand(new StringType(jsonState.state))); + onOffValue.update((State) onOffValue.parseMessage(new StringType(jsonState.state))); off = onOffValue.getChannelState().equals(OnOffType.OFF); - if (brightnessValue.getChannelState() instanceof UnDefType) { - brightnessValue.update(off ? PercentType.ZERO : PercentType.HUNDRED); - } - if (colorValue.getChannelState() instanceof UnDefType) { - colorValue.update(off ? HSBType.BLACK : HSBType.WHITE); + if (onOffValue.getChannelState() instanceof OnOffType onOffState) { + if (brightnessValue.getChannelState() instanceof UnDefType) { + brightnessValue.update(Objects.requireNonNull(onOffState.as(PercentType.class))); + } + if (colorValue.getChannelState() instanceof UnDefType) { + colorValue.update(Objects.requireNonNull(onOffState.as(PercentType.class))); + } } }