mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[mqtt.homeassistant] fix multi-speed fans (#17813)
* fix step math so that the state description represents the step scaled to 0-100% Signed-off-by: Cody Cutrer <cody@cutrer.us>
This commit is contained in:
parent
d0ea14f441
commit
156e691d0b
@ -54,9 +54,10 @@ public class PercentageValue extends Value {
|
||||
private final BigDecimal stepPercent;
|
||||
private final @Nullable String onValue;
|
||||
private final @Nullable String offValue;
|
||||
private final @Nullable String formatOverride;
|
||||
|
||||
public PercentageValue(@Nullable BigDecimal min, @Nullable BigDecimal max, @Nullable BigDecimal step,
|
||||
@Nullable String onValue, @Nullable String offValue) {
|
||||
@Nullable String onValue, @Nullable String offValue, @Nullable String formatOverride) {
|
||||
super(CoreItemFactory.DIMMER, List.of(DecimalType.class, QuantityType.class, IncreaseDecreaseType.class,
|
||||
OnOffType.class, UpDownType.class, StringType.class));
|
||||
this.onValue = onValue;
|
||||
@ -69,6 +70,7 @@ public class PercentageValue extends Value {
|
||||
this.span = this.max.subtract(this.min);
|
||||
this.step = step == null ? BigDecimal.ONE : step;
|
||||
this.stepPercent = this.step.multiply(HUNDRED).divide(this.span, MathContext.DECIMAL128);
|
||||
this.formatOverride = formatOverride;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -135,7 +137,10 @@ public class PercentageValue extends Value {
|
||||
|
||||
@Override
|
||||
public String getMQTTpublishValue(Command command, @Nullable String pattern) {
|
||||
String formatPattern = pattern;
|
||||
String formatPattern = this.formatOverride;
|
||||
if (formatPattern == null) {
|
||||
formatPattern = pattern;
|
||||
}
|
||||
if (formatPattern == null) {
|
||||
formatPattern = "%s";
|
||||
}
|
||||
@ -170,7 +175,7 @@ public class PercentageValue extends Value {
|
||||
|
||||
@Override
|
||||
public StateDescriptionFragmentBuilder createStateDescription(boolean readOnly) {
|
||||
return super.createStateDescription(readOnly).withMaximum(HUNDRED).withMinimum(BigDecimal.ZERO).withStep(step)
|
||||
.withPattern("%.0f %%");
|
||||
return super.createStateDescription(readOnly).withMaximum(HUNDRED).withMinimum(BigDecimal.ZERO)
|
||||
.withStep(stepPercent).withPattern("%.0f %%");
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ public class ValueFactory {
|
||||
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);
|
||||
value = new PercentageValue(config.min, config.max, config.step, config.on, config.off, null);
|
||||
break;
|
||||
case MqttBindingConstants.COLOR_HSB:
|
||||
value = new ColorValue(ColorMode.HSB, config.on, config.off, config.onBrightness);
|
||||
|
@ -267,7 +267,7 @@ public class ChannelStateTests {
|
||||
@Test
|
||||
public void receivePercentageTest() {
|
||||
PercentageValue value = new PercentageValue(new BigDecimal(-100), new BigDecimal(100), new BigDecimal(10), null,
|
||||
null);
|
||||
null, null);
|
||||
ChannelState c = spy(new ChannelState(config, channelUIDMock, value, channelStateUpdateListenerMock));
|
||||
c.start(connectionMock, mock(ScheduledExecutorService.class), 100);
|
||||
|
||||
|
@ -17,6 +17,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
@ -99,7 +100,7 @@ public class ValueTests {
|
||||
|
||||
@Test
|
||||
public void illegalPercentCommand() {
|
||||
PercentageValue v = new PercentageValue(null, null, null, null, null);
|
||||
PercentageValue v = new PercentageValue(null, null, null, null, null, null);
|
||||
assertThrows(IllegalStateException.class, () -> v.parseCommand(new StringType("demo")));
|
||||
}
|
||||
|
||||
@ -111,7 +112,7 @@ public class ValueTests {
|
||||
|
||||
@Test
|
||||
public void illegalPercentUpdate() {
|
||||
PercentageValue v = new PercentageValue(null, null, null, null, null);
|
||||
PercentageValue v = new PercentageValue(null, null, null, null, null, null);
|
||||
assertThrows(IllegalArgumentException.class, () -> v.parseCommand(new DecimalType(101.0)));
|
||||
}
|
||||
|
||||
@ -304,7 +305,9 @@ public class ValueTests {
|
||||
@Test
|
||||
public void percentCalc() {
|
||||
PercentageValue v = new PercentageValue(new BigDecimal(10.0), new BigDecimal(110.0), new BigDecimal(1.0), null,
|
||||
null);
|
||||
null, null);
|
||||
assertThat(v.createStateDescription(false).build().getStep(), is(new BigDecimal(1)));
|
||||
|
||||
assertThat(v.parseCommand(new DecimalType("110.0")), is(PercentType.HUNDRED));
|
||||
assertThat(v.getMQTTpublishValue(PercentType.HUNDRED, null), is("110"));
|
||||
assertThat(v.parseCommand(new DecimalType(10.0)), is(PercentType.ZERO));
|
||||
@ -316,9 +319,20 @@ public class ValueTests {
|
||||
assertThat(v.getMQTTpublishValue(OnOffType.OFF, null), is("10"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void percentFormatOverride() {
|
||||
PercentageValue v = new PercentageValue(BigDecimal.ZERO, new BigDecimal(3.0), null, null, null, "%.0f");
|
||||
assertThat(v.createStateDescription(false).build().getStep(),
|
||||
is(new BigDecimal(100).divide(new BigDecimal(3), MathContext.DECIMAL128)));
|
||||
assertThat(v.getMQTTpublishValue(PercentType.HUNDRED, null), is("3"));
|
||||
assertThat(v.getMQTTpublishValue(PercentType.valueOf("67"), null), is("2"));
|
||||
assertThat(v.getMQTTpublishValue(PercentType.valueOf("33"), null), is("1"));
|
||||
assertThat(v.getMQTTpublishValue(PercentType.ZERO, null), is("0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void percentMQTTValue() {
|
||||
PercentageValue v = new PercentageValue(null, null, null, null, null);
|
||||
PercentageValue v = new PercentageValue(null, null, null, null, null, null);
|
||||
assertThat(v.parseCommand(new DecimalType("10.10000")), is(new PercentType("10.1")));
|
||||
assertThat(v.getMQTTpublishValue(new PercentType("10.1"), null), is("10.1"));
|
||||
Command command;
|
||||
@ -333,7 +347,7 @@ public class ValueTests {
|
||||
@Test
|
||||
public void percentCustomOnOff() {
|
||||
PercentageValue v = new PercentageValue(new BigDecimal("0.0"), new BigDecimal("100.0"), new BigDecimal("1.0"),
|
||||
"on", "off");
|
||||
"on", "off", null);
|
||||
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));
|
||||
@ -343,7 +357,7 @@ public class ValueTests {
|
||||
@Test
|
||||
public void decimalCalc() {
|
||||
PercentageValue v = new PercentageValue(new BigDecimal("0.1"), new BigDecimal("1.0"), new BigDecimal("0.1"),
|
||||
null, null);
|
||||
null, null, null);
|
||||
assertThat(v.parseCommand(new DecimalType(1.0)), is(PercentType.HUNDRED));
|
||||
assertThat(v.parseCommand(new DecimalType(0.1)), is(PercentType.ZERO));
|
||||
PercentType command = (PercentType) v.parseCommand(new DecimalType(0.2));
|
||||
@ -353,7 +367,7 @@ public class ValueTests {
|
||||
@Test
|
||||
public void increaseDecreaseCalc() {
|
||||
PercentageValue v = new PercentageValue(new BigDecimal("1.0"), new BigDecimal("11.0"), new BigDecimal("0.5"),
|
||||
null, null);
|
||||
null, null, null);
|
||||
|
||||
// Normal operation.
|
||||
PercentType command = (PercentType) v.parseCommand(new DecimalType("6.0"));
|
||||
@ -382,7 +396,7 @@ public class ValueTests {
|
||||
@Test
|
||||
public void upDownCalc() {
|
||||
PercentageValue v = new PercentageValue(new BigDecimal("1.0"), new BigDecimal("11.0"), new BigDecimal("0.5"),
|
||||
null, null);
|
||||
null, null, null);
|
||||
|
||||
// Normal operation.
|
||||
PercentType command = (PercentType) v.parseCommand(new DecimalType("6.0"));
|
||||
@ -411,7 +425,7 @@ public class ValueTests {
|
||||
@Test
|
||||
public void percentCalcInvalid() {
|
||||
PercentageValue v = new PercentageValue(new BigDecimal(10.0), new BigDecimal(110.0), new BigDecimal(1.0), null,
|
||||
null);
|
||||
null, null);
|
||||
assertThrows(IllegalArgumentException.class, () -> v.parseCommand(new DecimalType(9.0)));
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,9 @@ public class Fan extends AbstractComponent<Fan.ChannelConfiguration> implements
|
||||
public static final String OSCILLATION_CHANNEL_ID = "oscillation";
|
||||
public static final String DIRECTION_CHANNEL_ID = "direction";
|
||||
|
||||
private static final BigDecimal BIG_DECIMAL_HUNDRED = new BigDecimal(100);
|
||||
private static final String FORMAT_INTEGER = "%.0f";
|
||||
|
||||
/**
|
||||
* Configuration class for MQTT component
|
||||
*/
|
||||
@ -60,6 +63,8 @@ public class Fan extends AbstractComponent<Fan.ChannelConfiguration> implements
|
||||
|
||||
protected @Nullable Boolean optimistic;
|
||||
|
||||
@SerializedName("state_value_template")
|
||||
protected @Nullable String stateValueTemplate;
|
||||
@SerializedName("state_topic")
|
||||
protected @Nullable String stateTopic;
|
||||
@SerializedName("command_template")
|
||||
@ -136,7 +141,7 @@ public class Fan extends AbstractComponent<Fan.ChannelConfiguration> implements
|
||||
: this;
|
||||
onOffChannel = buildChannel(newStyleChannels ? SWITCH_CHANNEL_ID : SWITCH_CHANNEL_ID_DEPRECATED,
|
||||
ComponentChannelType.SWITCH, onOffValue, "On/Off State", onOffListener)
|
||||
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.getValueTemplate())
|
||||
.stateTopic(channelConfiguration.stateTopic, channelConfiguration.stateValueTemplate)
|
||||
.commandTopic(channelConfiguration.commandTopic, channelConfiguration.isRetain(),
|
||||
channelConfiguration.getQos(), channelConfiguration.commandTemplate)
|
||||
.inferOptimistic(channelConfiguration.optimistic)
|
||||
@ -144,10 +149,9 @@ public class Fan extends AbstractComponent<Fan.ChannelConfiguration> implements
|
||||
|
||||
rawSpeedState = UnDefType.NULL;
|
||||
|
||||
int speeds = Math.min(channelConfiguration.speedRangeMax, 100) - Math.max(channelConfiguration.speedRangeMin, 1)
|
||||
+ 1;
|
||||
speedValue = new PercentageValue(BigDecimal.ZERO, BigDecimal.valueOf(100), BigDecimal.valueOf(100.0d / speeds),
|
||||
channelConfiguration.payloadOn, channelConfiguration.payloadOff);
|
||||
speedValue = new PercentageValue(BigDecimal.valueOf(channelConfiguration.speedRangeMin - 1),
|
||||
BigDecimal.valueOf(channelConfiguration.speedRangeMax), null, channelConfiguration.payloadOn,
|
||||
channelConfiguration.payloadOff, FORMAT_INTEGER);
|
||||
|
||||
if (channelConfiguration.percentageCommandTopic != null) {
|
||||
hiddenChannels.add(onOffChannel);
|
||||
|
@ -73,6 +73,8 @@ public abstract class Light extends AbstractComponent<Light.ChannelConfiguration
|
||||
protected static final String ON_COMMAND_TYPE_BRIGHTNESS = "brightness";
|
||||
protected static final String ON_COMMAND_TYPE_LAST = "last";
|
||||
|
||||
protected static final String FORMAT_INTEGER = "%.0f";
|
||||
|
||||
/**
|
||||
* Configuration class for MQTT component
|
||||
*/
|
||||
@ -276,7 +278,7 @@ public abstract class Light extends AbstractComponent<Light.ChannelConfiguration
|
||||
|
||||
onOffValue = new OnOffValue(channelConfiguration.payloadOn, channelConfiguration.payloadOff);
|
||||
brightnessValue = new PercentageValue(null, new BigDecimal(channelConfiguration.brightnessScale), null, null,
|
||||
null);
|
||||
null, FORMAT_INTEGER);
|
||||
@Nullable
|
||||
List<String> effectList = channelConfiguration.effectList;
|
||||
if (effectList != null) {
|
||||
|
@ -84,7 +84,7 @@ public class TemplateSchemaLight extends AbstractRawSchemaLight {
|
||||
}
|
||||
|
||||
onOffValue = new OnOffValue("on", "off");
|
||||
brightnessValue = new PercentageValue(null, new BigDecimal(255), null, null, null);
|
||||
brightnessValue = new PercentageValue(null, new BigDecimal(255), null, null, null, FORMAT_INTEGER);
|
||||
|
||||
if (channelConfiguration.redTemplate != null && channelConfiguration.greenTemplate != null
|
||||
&& channelConfiguration.blueTemplate != null) {
|
||||
|
@ -173,7 +173,7 @@ public class Vacuum extends AbstractComponent<Vacuum.ChannelConfiguration> {
|
||||
if (supportedFeatures.contains(FEATURE_BATTERY)) {
|
||||
buildOptionalChannel(newStyleChannels ? BATTERY_LEVEL_CH_ID : BATTERY_LEVEL_CH_ID_DEPRECATED,
|
||||
ComponentChannelType.DIMMER,
|
||||
new PercentageValue(BigDecimal.ZERO, BigDecimal.valueOf(100), BigDecimal.ONE, null, null),
|
||||
new PercentageValue(BigDecimal.ZERO, BigDecimal.valueOf(100), BigDecimal.ONE, null, null, null),
|
||||
updateListener, null, null, "{{ value_json.battery_level }}", channelConfiguration.stateTopic);
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,8 @@ public class Valve extends AbstractComponent<Valve.ChannelConfiguration> impleme
|
||||
private static final String POSITION_KEY = "position";
|
||||
private static final String STATE_KEY = "state";
|
||||
|
||||
private static final String FORMAT_INTEGER = "%.0f";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(Valve.class);
|
||||
|
||||
/**
|
||||
@ -121,7 +123,7 @@ public class Valve extends AbstractComponent<Valve.ChannelConfiguration> impleme
|
||||
onOffValue = new OnOffValue(channelConfiguration.stateOpen, channelConfiguration.stateClosed,
|
||||
channelConfiguration.payloadOpen, channelConfiguration.payloadClose);
|
||||
positionValue = new PercentageValue(BigDecimal.valueOf(channelConfiguration.positionClosed),
|
||||
BigDecimal.valueOf(channelConfiguration.positionOpen), null, null, null);
|
||||
BigDecimal.valueOf(channelConfiguration.positionOpen), null, null, null, FORMAT_INTEGER);
|
||||
|
||||
if (channelConfiguration.reportsPosition) {
|
||||
buildChannel(VALVE_CHANNEL_ID, ComponentChannelType.DIMMER, positionValue, getName(), this)
|
||||
|
@ -16,6 +16,7 @@ import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
@ -90,6 +91,70 @@ public class FanTests extends AbstractComponentTests {
|
||||
assertPublished("zigbee2mqtt/fan/set/state", "ON_");
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
@Test
|
||||
public void testPercentageWithTemplates() throws InterruptedException {
|
||||
var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
|
||||
"""
|
||||
{
|
||||
"availability": [
|
||||
{
|
||||
"topic": "zigbee2mqtt/bridge/state"
|
||||
}
|
||||
],
|
||||
"device": {
|
||||
"identifiers": [
|
||||
"zigbee2mqtt_0x0000000000000000"
|
||||
],
|
||||
"manufacturer": "Fans inc",
|
||||
"model": "Fan",
|
||||
"name": "FanBlower",
|
||||
"sw_version": "Zigbee2MQTT 1.18.2"
|
||||
},
|
||||
"name": "fan",
|
||||
"state_topic": "zigbee2mqtt/fan",
|
||||
"state_value_template": "{{ value_json.fan_state }}",
|
||||
"command_topic": "zigbee2mqtt/fan/set/fan_state",
|
||||
"percentage_command_template": "{{ {0:'off', 1:'low', 2:'medium', 3:'high'}[value] | default('') }}",
|
||||
"percentage_command_topic": "zigbee2mqtt/fan/set/fan_mode",
|
||||
"percentage_state_topic": "zigbee2mqtt/fan",
|
||||
"percentage_value_template": "{{ {'off':0, 'low':1, 'medium':2, 'high':3}[value_json.fan_mode] | default('None') }}",
|
||||
"speed_range_max": 3,
|
||||
"speed_range_min": 1
|
||||
}
|
||||
""");
|
||||
|
||||
assertThat(component.channels.size(), is(1));
|
||||
assertThat(component.getName(), is("fan"));
|
||||
|
||||
assertChannel(component, Fan.SPEED_CHANNEL_ID, "zigbee2mqtt/fan", "zigbee2mqtt/fan/set/fan_mode", "Speed",
|
||||
PercentageValue.class, null);
|
||||
|
||||
publishMessage("zigbee2mqtt/fan", "{ \"fan_state\": \"OFF\", \"fan_mode\": \"high\"}");
|
||||
assertState(component, Fan.SPEED_CHANNEL_ID, PercentType.ZERO);
|
||||
publishMessage("zigbee2mqtt/fan", "{ \"fan_state\": \"ON\", \"fan_mode\": \"high\"}");
|
||||
assertState(component, Fan.SPEED_CHANNEL_ID, PercentType.HUNDRED);
|
||||
publishMessage("zigbee2mqtt/fan", "{ \"fan_state\": \"ON\", \"fan_mode\": \"medium\"}");
|
||||
assertState(component, Fan.SPEED_CHANNEL_ID,
|
||||
new PercentType(new BigDecimal(200).divide(new BigDecimal(3), MathContext.DECIMAL128)));
|
||||
publishMessage("zigbee2mqtt/fan", "{ \"fan_state\": \"ON\", \"fan_mode\": \"low\"}");
|
||||
assertState(component, Fan.SPEED_CHANNEL_ID,
|
||||
new PercentType(new BigDecimal(100).divide(new BigDecimal(3), MathContext.DECIMAL128)));
|
||||
|
||||
component.getChannel(Fan.SPEED_CHANNEL_ID).getState().publishValue(OnOffType.OFF);
|
||||
assertPublished("zigbee2mqtt/fan/set/fan_state", "OFF");
|
||||
component.getChannel(Fan.SPEED_CHANNEL_ID).getState().publishValue(OnOffType.ON);
|
||||
assertPublished("zigbee2mqtt/fan/set/fan_state", "ON");
|
||||
component.getChannel(Fan.SPEED_CHANNEL_ID).getState().publishValue(PercentType.HUNDRED);
|
||||
assertPublished("zigbee2mqtt/fan/set/fan_mode", "high");
|
||||
component.getChannel(Fan.SPEED_CHANNEL_ID).getState().publishValue(PercentType.ZERO);
|
||||
assertPublished("zigbee2mqtt/fan/set/fan_mode", "off");
|
||||
component.getChannel(Fan.SPEED_CHANNEL_ID).getState().publishValue(new PercentType(33));
|
||||
assertPublished("zigbee2mqtt/fan/set/fan_mode", "low");
|
||||
component.getChannel(Fan.SPEED_CHANNEL_ID).getState().publishValue(new PercentType(66));
|
||||
assertPublished("zigbee2mqtt/fan/set/fan_mode", "medium");
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
@Test
|
||||
public void testInferredOptimistic() throws InterruptedException {
|
||||
@ -287,7 +352,7 @@ public class FanTests extends AbstractComponentTests {
|
||||
assertChannel(component, Fan.SPEED_CHANNEL_ID, "bedroom_fan/speed/percentage_state",
|
||||
"bedroom_fan/speed/percentage", "Speed", PercentageValue.class);
|
||||
var channel = Objects.requireNonNull(component.getChannel(Fan.SPEED_CHANNEL_ID));
|
||||
assertThat(channel.getStateDescription().getStep(), is(BigDecimal.valueOf(10.0d)));
|
||||
assertThat(channel.getStateDescription().getStep(), is(BigDecimal.valueOf(10)));
|
||||
assertChannel(component, Fan.OSCILLATION_CHANNEL_ID, "bedroom_fan/oscillation/state",
|
||||
"bedroom_fan/oscillation/set", "Oscillation", OnOffValue.class);
|
||||
assertChannel(component, Fan.DIRECTION_CHANNEL_ID, "bedroom_fan/direction/state", "bedroom_fan/direction/set",
|
||||
@ -300,16 +365,16 @@ public class FanTests extends AbstractComponentTests {
|
||||
publishMessage("bedroom_fan/on/state", "false");
|
||||
assertState(component, Fan.SPEED_CHANNEL_ID, PercentType.ZERO);
|
||||
publishMessage("bedroom_fan/on/state", "true");
|
||||
publishMessage("bedroom_fan/speed/percentage_state", "50");
|
||||
publishMessage("bedroom_fan/speed/percentage_state", "5");
|
||||
assertState(component, Fan.SPEED_CHANNEL_ID, new PercentType(50));
|
||||
publishMessage("bedroom_fan/on/state", "false");
|
||||
// Off, even though we got an updated speed
|
||||
assertState(component, Fan.SPEED_CHANNEL_ID, PercentType.ZERO);
|
||||
publishMessage("bedroom_fan/speed/percentage_state", "25");
|
||||
publishMessage("bedroom_fan/speed/percentage_state", "2");
|
||||
assertState(component, Fan.SPEED_CHANNEL_ID, PercentType.ZERO);
|
||||
publishMessage("bedroom_fan/on/state", "true");
|
||||
// Now that it's on, the channel reflects the proper speed
|
||||
assertState(component, Fan.SPEED_CHANNEL_ID, new PercentType(25));
|
||||
assertState(component, Fan.SPEED_CHANNEL_ID, new PercentType(20));
|
||||
|
||||
publishMessage("bedroom_fan/oscillation/state", "true");
|
||||
assertState(component, Fan.OSCILLATION_CHANNEL_ID, OnOffType.ON);
|
||||
@ -333,7 +398,7 @@ public class FanTests extends AbstractComponentTests {
|
||||
// Setting to a specific speed turns it on first
|
||||
component.getChannel(Fan.SPEED_CHANNEL_ID).getState().publishValue(PercentType.HUNDRED);
|
||||
assertPublished("bedroom_fan/on/set", "true");
|
||||
assertPublished("bedroom_fan/speed/percentage", "100");
|
||||
assertPublished("bedroom_fan/speed/percentage", "10");
|
||||
|
||||
component.getChannel(Fan.OSCILLATION_CHANNEL_ID).getState().publishValue(OnOffType.ON);
|
||||
assertPublished("bedroom_fan/oscillation/set", "true");
|
||||
|
@ -191,7 +191,7 @@ public class Property implements AttributeChanged {
|
||||
step = new BigDecimal(1);
|
||||
}
|
||||
if (attributes.unit.contains("%") && attributes.settable) {
|
||||
value = new PercentageValue(min, max, step, null, null);
|
||||
value = new PercentageValue(min, max, step, null, null, null);
|
||||
} else {
|
||||
value = new NumberValue(min, max, step, unit);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user