Skip to content

Commit

Permalink
[mqtt.homeassistant] Add missing climate properties (#17659)
Browse files Browse the repository at this point in the history
* Added support for humidity and preset_modes 

Signed-off-by: Václav Čejka <[email protected]>
Signed-off-by: VasekCejka <[email protected]>
  • Loading branch information
VasekCejka authored Nov 1, 2024
1 parent ed93eb5 commit f4250b7
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
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;

Expand All @@ -43,20 +44,24 @@
*
* @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> {
public static final String ACTION_CH_ID = "action";
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";
Expand Down Expand Up @@ -120,6 +125,11 @@ static class ChannelConfiguration extends AbstractChannelConfiguration {
@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")
Expand Down Expand Up @@ -158,6 +168,18 @@ static class ChannelConfiguration extends AbstractChannelConfiguration {
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")
Expand All @@ -169,6 +191,15 @@ static class ChannelConfiguration extends AbstractChannelConfiguration {
@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")
Expand Down Expand Up @@ -199,6 +230,11 @@ static class ChannelConfiguration extends AbstractChannelConfiguration {
@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;
Expand Down Expand Up @@ -236,6 +272,10 @@ ComponentChannelType.SWITCH, new OnOffValue(), updateListener, null,
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,
Expand All @@ -260,11 +300,23 @@ ComponentChannelType.SWITCH, new OnOffValue(), updateListener, null,
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()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
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}
Expand Down Expand Up @@ -320,6 +321,82 @@ public void testClimate() {
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);
Expand Down

0 comments on commit f4250b7

Please sign in to comment.