[mqtt.homeassistant] Add missing climate properties (#17659)

* Added support for humidity and preset_modes 

Signed-off-by: Václav Čejka <V.Cejka@seznam.cz>
Signed-off-by: VasekCejka <150804231+VasekCejka@users.noreply.github.com>
This commit is contained in:
VasekCejka 2024-11-02 00:05:47 +01:00 committed by GitHub
parent ed93eb572c
commit f4250b77f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 129 additions and 0 deletions

View File

@ -33,6 +33,7 @@ import org.openhab.binding.mqtt.homeassistant.internal.config.dto.AbstractChanne
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
@ -43,6 +44,7 @@ import com.google.gson.annotations.SerializedName;
*
* @author David Graeff - Initial contribution
* @author Anton Kharuzhy - Implementation
* @author Vaclav Cejka - added support for humidity and preset_modes
*/
@NonNullByDefault
public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
@ -50,13 +52,16 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
public static final String AUX_CH_ID = "aux";
public static final String AWAY_MODE_CH_ID = "away-mode";
public static final String AWAY_MODE_CH_ID_DEPRECATED = "awayMode";
public static final String CURRENT_HUMIDITY_CH_ID = "current-humidity";
public static final String CURRENT_TEMPERATURE_CH_ID = "current-temperature";
public static final String CURRENT_TEMPERATURE_CH_ID_DEPRECATED = "currentTemperature";
public static final String FAN_MODE_CH_ID = "fan-mode";
public static final String FAN_MODE_CH_ID_DEPRECATED = "fanMode";
public static final String HOLD_CH_ID = "hold";
public static final String MODE_CH_ID = "mode";
public static final String PRESET_MODE_CH_ID = "preset-mode";
public static final String SWING_CH_ID = "swing";
public static final String TARGET_HUMIDITY_CH_ID = "target-humidity";
public static final String TEMPERATURE_CH_ID = "temperature";
public static final String TEMPERATURE_HIGH_CH_ID = "temperature-high";
public static final String TEMPERATURE_HIGH_CH_ID_DEPRECATED = "temperatureHigh";
@ -120,6 +125,11 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
@SerializedName("away_mode_state_topic")
protected @Nullable String awayModeStateTopic;
@SerializedName("current_humidity_template")
protected @Nullable String currentHumidityTemplate;
@SerializedName("current_humidity_topic")
protected @Nullable String currentHumidityTopic;
@SerializedName("current_temperature_template")
protected @Nullable String currentTemperatureTemplate;
@SerializedName("current_temperature_topic")
@ -158,6 +168,18 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
protected @Nullable String modeStateTopic;
protected List<String> modes = Arrays.asList("auto", "off", "cool", "heat", "dry", "fan_only");
@SerializedName("preset_mode_command_template")
protected @Nullable String presetModeCommandTemplate;
@SerializedName("preset_mode_command_topic")
protected @Nullable String presetModeCommandTopic;
@SerializedName("preset_mode_state_topic")
protected @Nullable String presetModeStateTopic;
@SerializedName("preset_mode_value_template")
protected @Nullable String presetModeStateTemplate;
@SerializedName("preset_modes")
protected List<String> presetModes = List.of(); // defaults heavily depend on the
// type of the device
@SerializedName("swing_command_template")
protected @Nullable String swingCommandTemplate;
@SerializedName("swing_command_topic")
@ -169,6 +191,15 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
@SerializedName("swing_modes")
protected List<String> swingModes = Arrays.asList("on", "off");
@SerializedName("target_humidity_command_template")
protected @Nullable String targetHumidityCommandTemplate;
@SerializedName("target_humidity_command_topic")
protected @Nullable String targetHumidityCommandTopic;
@SerializedName("target_humidity_state_template")
protected @Nullable String targetHumidityStateTemplate;
@SerializedName("target_humidity_state_topic")
protected @Nullable String targetHumidityStateTopic;
@SerializedName("temperature_command_template")
protected @Nullable String temperatureCommandTemplate;
@SerializedName("temperature_command_topic")
@ -199,6 +230,11 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
@SerializedName("power_command_topic")
protected @Nullable String powerCommandTopic;
@SerializedName("max_humidity")
protected BigDecimal maxHumidity = new BigDecimal(99);
@SerializedName("min_humidity")
protected BigDecimal minHumidity = new BigDecimal(30);
protected Integer initial = 21;
@SerializedName("max_temp")
protected @Nullable BigDecimal maxTemp;
@ -236,6 +272,10 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
channelConfiguration.awayModeCommandTopic, channelConfiguration.awayModeStateTemplate,
channelConfiguration.awayModeStateTopic, commandFilter);
buildOptionalChannel(CURRENT_HUMIDITY_CH_ID, ComponentChannelType.NUMBER,
new NumberValue(new BigDecimal(0), new BigDecimal(100), null, Units.PERCENT), updateListener, null,
null, channelConfiguration.currentHumidityTemplate, channelConfiguration.currentHumidityTopic, null);
buildOptionalChannel(newStyleChannels ? CURRENT_TEMPERATURE_CH_ID : CURRENT_TEMPERATURE_CH_ID_DEPRECATED,
ComponentChannelType.NUMBER,
new NumberValue(null, null, precision, channelConfiguration.temperatureUnit.getUnit()), updateListener,
@ -260,11 +300,23 @@ public class Climate extends AbstractComponent<Climate.ChannelConfiguration> {
channelConfiguration.modeCommandTemplate, channelConfiguration.modeCommandTopic,
channelConfiguration.modeStateTemplate, channelConfiguration.modeStateTopic, commandFilter);
buildOptionalChannel(PRESET_MODE_CH_ID, ComponentChannelType.STRING,
new TextValue(channelConfiguration.presetModes.toArray(new String[0])), updateListener,
channelConfiguration.presetModeCommandTemplate, channelConfiguration.presetModeCommandTopic,
channelConfiguration.presetModeStateTemplate, channelConfiguration.presetModeStateTopic, commandFilter);
buildOptionalChannel(SWING_CH_ID, ComponentChannelType.STRING,
new TextValue(channelConfiguration.swingModes.toArray(new String[0])), updateListener,
channelConfiguration.swingCommandTemplate, channelConfiguration.swingCommandTopic,
channelConfiguration.swingStateTemplate, channelConfiguration.swingStateTopic, commandFilter);
buildOptionalChannel(TARGET_HUMIDITY_CH_ID, ComponentChannelType.NUMBER,
new NumberValue(channelConfiguration.minHumidity, channelConfiguration.maxHumidity, null,
Units.PERCENT),
updateListener, channelConfiguration.targetHumidityCommandTemplate,
channelConfiguration.targetHumidityCommandTopic, channelConfiguration.targetHumidityStateTemplate,
channelConfiguration.targetHumidityStateTopic, commandFilter);
buildOptionalChannel(TEMPERATURE_CH_ID, ComponentChannelType.NUMBER,
new NumberValue(channelConfiguration.minTemp, channelConfiguration.maxTemp,
channelConfiguration.tempStep, channelConfiguration.temperatureUnit.getUnit()),

View File

@ -28,6 +28,7 @@ import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
/**
* Tests for {@link Climate}
@ -320,6 +321,82 @@ public class ClimateTests extends AbstractComponentTests {
assertPublished("zigbee2mqtt/th1/power", "OFF");
}
@SuppressWarnings("null")
@Test
public void testClimateWithPresetMode() {
var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC),
"""
{
"action_template": "{% set values = {None:None,'idle':'idle','heat':'heating','cool':'cooling','fan_only':'fan'} %}{{ values[value_json.running_state] }}",
"action_topic": "zigbee2mqtt/th2",
"current_temperature_template": "{{ value_json.local_temperature }}",
"current_temperature_topic": "zigbee2mqtt/th2",
"json_attributes_topic": "zigbee2mqtt/th2",
"max_temp": "35",
"min_temp": "5",
"mode_command_topic": "zigbee2mqtt/th2/set/system_mode",
"mode_state_template": "{{ value_json.system_mode }}",
"mode_state_topic": "zigbee2mqtt/th2",
"modes": ["auto","heat","off"],
"name": "th2",
"preset_mode_command_topic": "zigbee2mqtt/th2/set/preset",
"preset_mode_state_topic": "zigbee2mqtt/th2",
"preset_mode_value_template": "{{ value_json.preset }}",
"preset_modes": ["auto","manual","off","on"],
"temp_step": 0.5,
"temperature_command_topic": "zigbee2mqtt/th2/set/current_heating_setpoint",
"temperature_state_template": "{{ value_json.current_heating_setpoint }}",
"temperature_state_topic": "zigbee2mqtt/th2",
"temperature_unit": "C"
}
""");
assertThat(component.channels.size(), is(6));
assertChannel(component, Climate.PRESET_MODE_CH_ID, "zigbee2mqtt/th2", "zigbee2mqtt/th2/set/preset", "th2",
TextValue.class);
publishMessage("zigbee2mqtt/th2", """
{"running_state": "heat",
"local_temperature": "22.2", "preset": "manual", "system_mode": "heat",
"current_heating_setpoint": "24"}
""");
assertState(component, Climate.MODE_CH_ID, new StringType("heat"));
assertState(component, Climate.TEMPERATURE_CH_ID, new QuantityType<>(24, SIUnits.CELSIUS));
assertState(component, Climate.PRESET_MODE_CH_ID, new StringType("manual"));
component.getChannel(Climate.PRESET_MODE_CH_ID).getState().publishValue(new StringType("on"));
assertPublished("zigbee2mqtt/th2/set/preset", "on");
}
@SuppressWarnings("null")
@Test
public void testClimateHumidity() {
var component = discoverComponent(configTopicToMqtt(CONFIG_TOPIC), """
{
"current_humidity_template": "{{ value_json.humidity }}",
"current_humidity_topic": "zigbee2mqtt/th2",
"max_humidity": "70",
"min_humidity": "30",
"target_humidity_command_topic": "zigbee2mqtt/th2/set/humidity_setpoint",
"target_humidity_state_template": "{{ value_json.humidity_setpoint }}",
"target_humidity_state_topic": "zigbee2mqtt/th2",
"name": "th2"
}
""");
assertThat(component.channels.size(), is(2));
assertChannel(component, Climate.CURRENT_HUMIDITY_CH_ID, "zigbee2mqtt/th2", "", "th2", NumberValue.class);
assertChannel(component, Climate.TARGET_HUMIDITY_CH_ID, "zigbee2mqtt/th2",
"zigbee2mqtt/th2/set/humidity_setpoint", "th2", NumberValue.class);
publishMessage("zigbee2mqtt/th2", """
{"humidity": "55", "humidity_setpoint": "50"}\
""");
assertState(component, Climate.CURRENT_HUMIDITY_CH_ID, new QuantityType<>(55, Units.PERCENT));
assertState(component, Climate.TARGET_HUMIDITY_CH_ID, new QuantityType<>(50, Units.PERCENT));
}
@Override
protected Set<String> getConfigTopics() {
return Set.of(CONFIG_TOPIC);