Add turn on/off support for mqtt water_heater (#97197)
parent
6c43ce69d3
commit
fb00cd8963
|
@ -64,6 +64,8 @@ from .const import (
|
|||
CONF_MODE_LIST,
|
||||
CONF_MODE_STATE_TEMPLATE,
|
||||
CONF_MODE_STATE_TOPIC,
|
||||
CONF_POWER_COMMAND_TEMPLATE,
|
||||
CONF_POWER_COMMAND_TOPIC,
|
||||
CONF_PRECISION,
|
||||
CONF_QOS,
|
||||
CONF_RETAIN,
|
||||
|
@ -113,9 +115,6 @@ CONF_HUMIDITY_MIN = "min_humidity"
|
|||
# was removed in HA Core 2023.8
|
||||
CONF_POWER_STATE_TEMPLATE = "power_state_template"
|
||||
CONF_POWER_STATE_TOPIC = "power_state_topic"
|
||||
|
||||
CONF_POWER_COMMAND_TOPIC = "power_command_topic"
|
||||
CONF_POWER_COMMAND_TEMPLATE = "power_command_template"
|
||||
CONF_PRESET_MODE_STATE_TOPIC = "preset_mode_state_topic"
|
||||
CONF_PRESET_MODE_COMMAND_TOPIC = "preset_mode_command_topic"
|
||||
CONF_PRESET_MODE_VALUE_TEMPLATE = "preset_mode_value_template"
|
||||
|
|
|
@ -40,6 +40,8 @@ CONF_MODE_COMMAND_TOPIC = "mode_command_topic"
|
|||
CONF_MODE_LIST = "modes"
|
||||
CONF_MODE_STATE_TEMPLATE = "mode_state_template"
|
||||
CONF_MODE_STATE_TOPIC = "mode_state_topic"
|
||||
CONF_POWER_COMMAND_TOPIC = "power_command_topic"
|
||||
CONF_POWER_COMMAND_TEMPLATE = "power_command_template"
|
||||
CONF_PRECISION = "precision"
|
||||
CONF_TEMP_COMMAND_TEMPLATE = "temperature_command_template"
|
||||
CONF_TEMP_COMMAND_TOPIC = "temperature_command_topic"
|
||||
|
|
|
@ -51,6 +51,8 @@ from .const import (
|
|||
CONF_MODE_LIST,
|
||||
CONF_MODE_STATE_TEMPLATE,
|
||||
CONF_MODE_STATE_TOPIC,
|
||||
CONF_POWER_COMMAND_TEMPLATE,
|
||||
CONF_POWER_COMMAND_TOPIC,
|
||||
CONF_PRECISION,
|
||||
CONF_RETAIN,
|
||||
CONF_TEMP_COMMAND_TEMPLATE,
|
||||
|
@ -91,6 +93,7 @@ VALUE_TEMPLATE_KEYS = (
|
|||
COMMAND_TEMPLATE_KEYS = {
|
||||
CONF_MODE_COMMAND_TEMPLATE,
|
||||
CONF_TEMP_COMMAND_TEMPLATE,
|
||||
CONF_POWER_COMMAND_TEMPLATE,
|
||||
}
|
||||
|
||||
|
||||
|
@ -98,6 +101,7 @@ TOPIC_KEYS = (
|
|||
CONF_CURRENT_TEMP_TOPIC,
|
||||
CONF_MODE_COMMAND_TOPIC,
|
||||
CONF_MODE_STATE_TOPIC,
|
||||
CONF_POWER_COMMAND_TOPIC,
|
||||
CONF_TEMP_COMMAND_TOPIC,
|
||||
CONF_TEMP_STATE_TOPIC,
|
||||
)
|
||||
|
@ -127,6 +131,8 @@ _PLATFORM_SCHEMA_BASE = MQTT_BASE_SCHEMA.extend(
|
|||
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
||||
vol.Optional(CONF_PAYLOAD_ON, default="ON"): cv.string,
|
||||
vol.Optional(CONF_PAYLOAD_OFF, default="OFF"): cv.string,
|
||||
vol.Optional(CONF_POWER_COMMAND_TOPIC): valid_publish_topic,
|
||||
vol.Optional(CONF_POWER_COMMAND_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_PRECISION): vol.In(
|
||||
[PRECISION_TENTHS, PRECISION_HALVES, PRECISION_WHOLE]
|
||||
),
|
||||
|
@ -266,6 +272,9 @@ class MqttWaterHeater(MqttTemperatureControlEntity, WaterHeaterEntity):
|
|||
):
|
||||
support |= WaterHeaterEntityFeature.OPERATION_MODE
|
||||
|
||||
if self._topic[CONF_POWER_COMMAND_TOPIC] is not None:
|
||||
support |= WaterHeaterEntityFeature.ON_OFF
|
||||
|
||||
self._attr_supported_features = support
|
||||
|
||||
def _prepare_subscribe_topics(self) -> None:
|
||||
|
@ -317,3 +326,19 @@ class MqttWaterHeater(MqttTemperatureControlEntity, WaterHeaterEntity):
|
|||
if self._optimistic or self._topic[CONF_MODE_STATE_TOPIC] is None:
|
||||
self._attr_current_operation = operation_mode
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the entity on."""
|
||||
if CONF_POWER_COMMAND_TOPIC in self._config:
|
||||
mqtt_payload = self._command_templates[CONF_POWER_COMMAND_TEMPLATE](
|
||||
self._config[CONF_PAYLOAD_ON]
|
||||
)
|
||||
await self._publish(CONF_POWER_COMMAND_TOPIC, mqtt_payload)
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the entity off."""
|
||||
if CONF_POWER_COMMAND_TOPIC in self._config:
|
||||
mqtt_payload = self._command_templates[CONF_POWER_COMMAND_TEMPLATE](
|
||||
self._config[CONF_PAYLOAD_OFF]
|
||||
)
|
||||
await self._publish(CONF_POWER_COMMAND_TOPIC, mqtt_payload)
|
||||
|
|
|
@ -257,6 +257,91 @@ async def test_set_operation_optimistic(
|
|||
assert state.state == "performance"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[
|
||||
help_custom_config(
|
||||
water_heater.DOMAIN,
|
||||
DEFAULT_CONFIG,
|
||||
({"power_command_topic": "power-command"},),
|
||||
)
|
||||
],
|
||||
)
|
||||
async def test_set_operation_with_power_command(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
"""Test setting of new operation mode with power command enabled."""
|
||||
mqtt_mock = await mqtt_mock_entry()
|
||||
|
||||
state = hass.states.get(ENTITY_WATER_HEATER)
|
||||
assert state.state == "off"
|
||||
await common.async_set_operation_mode(hass, "electric", ENTITY_WATER_HEATER)
|
||||
state = hass.states.get(ENTITY_WATER_HEATER)
|
||||
assert state.state == "electric"
|
||||
mqtt_mock.async_publish.assert_has_calls([call("mode-topic", "electric", 0, False)])
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
await common.async_set_operation_mode(hass, "off", ENTITY_WATER_HEATER)
|
||||
state = hass.states.get(ENTITY_WATER_HEATER)
|
||||
assert state.state == "off"
|
||||
mqtt_mock.async_publish.assert_has_calls([call("mode-topic", "off", 0, False)])
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
await common.async_turn_on(hass, ENTITY_WATER_HEATER)
|
||||
# the water heater is not updated optimistically as this is not supported
|
||||
mqtt_mock.async_publish.assert_has_calls([call("power-command", "ON", 0, False)])
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
await common.async_turn_off(hass, ENTITY_WATER_HEATER)
|
||||
mqtt_mock.async_publish.assert_has_calls([call("power-command", "OFF", 0, False)])
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[
|
||||
help_custom_config(
|
||||
water_heater.DOMAIN,
|
||||
DEFAULT_CONFIG,
|
||||
({"power_command_topic": "power-command", "optimistic": True},),
|
||||
)
|
||||
],
|
||||
)
|
||||
async def test_turn_on_and_off_optimistic_with_power_command(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
) -> None:
|
||||
"""Test setting of turn on/off with power command enabled."""
|
||||
mqtt_mock = await mqtt_mock_entry()
|
||||
|
||||
state = hass.states.get(ENTITY_WATER_HEATER)
|
||||
assert state.state == "off"
|
||||
await common.async_set_operation_mode(hass, "electric", ENTITY_WATER_HEATER)
|
||||
state = hass.states.get(ENTITY_WATER_HEATER)
|
||||
assert state.state == "electric"
|
||||
mqtt_mock.async_publish.assert_has_calls([call("mode-topic", "electric", 0, False)])
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
await common.async_set_operation_mode(hass, "off", ENTITY_WATER_HEATER)
|
||||
state = hass.states.get(ENTITY_WATER_HEATER)
|
||||
assert state.state == "off"
|
||||
|
||||
await common.async_turn_on(hass, ENTITY_WATER_HEATER)
|
||||
# the water heater is not updated optimistically as this is not supported
|
||||
state = hass.states.get(ENTITY_WATER_HEATER)
|
||||
assert state.state == "off"
|
||||
mqtt_mock.async_publish.assert_has_calls([call("power-command", "ON", 0, False)])
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
await common.async_set_operation_mode(hass, "gas", ENTITY_WATER_HEATER)
|
||||
state = hass.states.get(ENTITY_WATER_HEATER)
|
||||
assert state.state == "gas"
|
||||
await common.async_turn_off(hass, ENTITY_WATER_HEATER)
|
||||
# the water heater is not updated optimistically as this is not supported
|
||||
state = hass.states.get(ENTITY_WATER_HEATER)
|
||||
assert state.state == "gas"
|
||||
mqtt_mock.async_publish.assert_has_calls([call("power-command", "OFF", 0, False)])
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("hass_config", [DEFAULT_CONFIG])
|
||||
async def test_set_target_temperature(
|
||||
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
|
||||
|
@ -509,9 +594,11 @@ async def test_get_with_templates(
|
|||
"name": "test",
|
||||
"mode_command_topic": "mode-topic",
|
||||
"temperature_command_topic": "temperature-topic",
|
||||
"power_command_topic": "power-topic",
|
||||
# Create simple templates
|
||||
"mode_command_template": "mode: {{ value }}",
|
||||
"temperature_command_template": "temp: {{ value }}",
|
||||
"power_command_template": "pwr: {{ value }}",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -544,6 +631,14 @@ async def test_set_and_templates(
|
|||
state = hass.states.get(ENTITY_WATER_HEATER)
|
||||
assert state.attributes.get("temperature") == 107
|
||||
|
||||
# Power
|
||||
await common.async_turn_on(hass, entity_id=ENTITY_WATER_HEATER)
|
||||
mqtt_mock.async_publish.assert_called_once_with("power-topic", "pwr: ON", 0, False)
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
await common.async_turn_off(hass, entity_id=ENTITY_WATER_HEATER)
|
||||
mqtt_mock.async_publish.assert_called_once_with("power-topic", "pwr: OFF", 0, False)
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
|
@ -1047,6 +1142,20 @@ async def test_precision_whole(
|
|||
20.1,
|
||||
"temperature_command_template",
|
||||
),
|
||||
(
|
||||
water_heater.SERVICE_TURN_ON,
|
||||
"power_command_topic",
|
||||
{},
|
||||
"ON",
|
||||
"power_command_template",
|
||||
),
|
||||
(
|
||||
water_heater.SERVICE_TURN_OFF,
|
||||
"power_command_topic",
|
||||
{},
|
||||
"OFF",
|
||||
"power_command_template",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_publishing_with_custom_encoding(
|
||||
|
|
Loading…
Reference in New Issue