Skip to content

Commit

Permalink
[mqtt.generic] Send ON/OFF for dimmer channels when so configured (op…
Browse files Browse the repository at this point in the history
…enhab#15929)

Similar to how UP/DOWN are processed for rollershutters.

Signed-off-by: Cody Cutrer <cody@cutrer.us>
  • Loading branch information
ccutrer authored and matchews committed Oct 18, 2024
1 parent fecf9dd commit 7d090b2
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public PercentageValue(@Nullable BigDecimal min, @Nullable BigDecimal max, @Null
}

@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) {
Expand Down Expand Up @@ -105,8 +105,8 @@ public PercentType parseCommand(Command command) throws IllegalArgumentException
}
} 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) {
Expand All @@ -121,9 +121,9 @@ public PercentType parseCommand(Command command) throws IllegalArgumentException
// 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.");
}
Expand All @@ -135,17 +135,36 @@ public PercentType parseCommand(Command command) throws IllegalArgumentException

@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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,12 @@
<advanced>true</advanced>
</parameter>
<parameter name="on" type="text">
<label>Custom On/Open Value</label>
<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).</description>
<default>1</default>
<label>Custom ON Command</label>
<description>A string (like "ON") that is sent to MQTT when an ON command is received, instead of an explicit 100%.</description>
</parameter>
<parameter name="off" type="text">
<label>Custom Off/Closed Value</label>
<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).</description>
<default>0</default>
<label>Custom OFF Command</label>
<description>A string (like "OFF") that is sent to MQTT when an OFF command is received, instead of an explicit 0%.</description>
</parameter>
</config-description>
</config-description:config-descriptions>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,10 @@ public void percentCalc() {
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
Expand All @@ -332,8 +334,10 @@ public void percentMQTTValue() {
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
Expand All @@ -342,7 +346,7 @@ public void decimalCalc() {
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);
}

Expand All @@ -352,26 +356,26 @@ public void increaseDecreaseCalc() {
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);
}

Expand All @@ -381,26 +385,26 @@ public void upDownCalc() {
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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,13 +255,15 @@ public void updateChannelState(ChannelUID channel, State state) {

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)));
}
}
}

Expand Down

0 comments on commit 7d090b2

Please sign in to comment.