From d32dbc4cdd6090af3aead56470d6db6c26672b05 Mon Sep 17 00:00:00 2001 From: Matt Zimmerman Date: Sun, 21 Feb 2021 20:46:54 -0800 Subject: [PATCH] Add support for SmartTub heat modes (#46876) --- homeassistant/components/smarttub/climate.py | 43 ++++++++++++++++---- tests/components/smarttub/conftest.py | 1 + tests/components/smarttub/test_climate.py | 26 +++++++++++- 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/smarttub/climate.py b/homeassistant/components/smarttub/climate.py index 02d627d383e..ee6afc80fb1 100644 --- a/homeassistant/components/smarttub/climate.py +++ b/homeassistant/components/smarttub/climate.py @@ -6,6 +6,9 @@ from homeassistant.components.climate.const import ( CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE, HVAC_MODE_HEAT, + PRESET_ECO, + PRESET_NONE, + SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE, ) from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS @@ -16,6 +19,8 @@ from .entity import SmartTubEntity _LOGGER = logging.getLogger(__name__) +PRESET_DAY = "day" + async def async_setup_entry(hass, entry, async_add_entities): """Set up climate entity for the thermostat in the tub.""" @@ -32,6 +37,19 @@ async def async_setup_entry(hass, entry, async_add_entities): class SmartTubThermostat(SmartTubEntity, ClimateEntity): """The target water temperature for the spa.""" + PRESET_MODES = { + "AUTO": PRESET_NONE, + "ECO": PRESET_ECO, + "DAY": PRESET_DAY, + } + + HEAT_MODES = {v: k for k, v in PRESET_MODES.items()} + + HVAC_ACTIONS = { + "OFF": CURRENT_HVAC_IDLE, + "ON": CURRENT_HVAC_HEAT, + } + def __init__(self, coordinator, spa): """Initialize the entity.""" super().__init__(coordinator, spa, "thermostat") @@ -44,12 +62,7 @@ class SmartTubThermostat(SmartTubEntity, ClimateEntity): @property def hvac_action(self): """Return the current running hvac operation.""" - heater_status = self.get_spa_status("heater") - if heater_status == "ON": - return CURRENT_HVAC_HEAT - if heater_status == "OFF": - return CURRENT_HVAC_IDLE - return None + return self.HVAC_ACTIONS.get(self.get_spa_status("heater")) @property def hvac_modes(self): @@ -92,7 +105,17 @@ class SmartTubThermostat(SmartTubEntity, ClimateEntity): Only target temperature is supported. """ - return SUPPORT_TARGET_TEMPERATURE + return SUPPORT_PRESET_MODE | SUPPORT_TARGET_TEMPERATURE + + @property + def preset_mode(self): + """Return the current preset mode.""" + return self.PRESET_MODES[self.get_spa_status("heatMode")] + + @property + def preset_modes(self): + """Return the available preset modes.""" + return list(self.PRESET_MODES.values()) @property def current_temperature(self): @@ -109,3 +132,9 @@ class SmartTubThermostat(SmartTubEntity, ClimateEntity): temperature = kwargs[ATTR_TEMPERATURE] await self.spa.set_temperature(temperature) await self.coordinator.async_refresh() + + async def async_set_preset_mode(self, preset_mode: str): + """Activate the specified preset mode.""" + heat_mode = self.HEAT_MODES[preset_mode] + await self.spa.set_heat_mode(heat_mode) + await self.coordinator.async_refresh() diff --git a/tests/components/smarttub/conftest.py b/tests/components/smarttub/conftest.py index efba121822d..265afcfc24c 100644 --- a/tests/components/smarttub/conftest.py +++ b/tests/components/smarttub/conftest.py @@ -46,6 +46,7 @@ def mock_spa(): "setTemperature": 39, "water": {"temperature": 38}, "heater": "ON", + "heatMode": "AUTO", "state": "NORMAL", "primaryFiltration": { "cycle": 1, diff --git a/tests/components/smarttub/test_climate.py b/tests/components/smarttub/test_climate.py index 69fb642aab4..118264183e8 100644 --- a/tests/components/smarttub/test_climate.py +++ b/tests/components/smarttub/test_climate.py @@ -9,12 +9,18 @@ from homeassistant.components.climate.const import ( ATTR_HVAC_MODES, ATTR_MAX_TEMP, ATTR_MIN_TEMP, + ATTR_PRESET_MODE, + ATTR_PRESET_MODES, CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE, DOMAIN as CLIMATE_DOMAIN, HVAC_MODE_HEAT, + PRESET_ECO, + PRESET_NONE, SERVICE_SET_HVAC_MODE, + SERVICE_SET_PRESET_MODE, SERVICE_SET_TEMPERATURE, + SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE, ) from homeassistant.components.smarttub.const import DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP @@ -44,11 +50,15 @@ async def test_thermostat_update(spa, setup_entry, hass): assert set(state.attributes[ATTR_HVAC_MODES]) == {HVAC_MODE_HEAT} assert state.state == HVAC_MODE_HEAT - assert state.attributes[ATTR_SUPPORTED_FEATURES] == SUPPORT_TARGET_TEMPERATURE + assert ( + state.attributes[ATTR_SUPPORTED_FEATURES] + == SUPPORT_PRESET_MODE | SUPPORT_TARGET_TEMPERATURE + ) assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 38 assert state.attributes[ATTR_TEMPERATURE] == 39 assert state.attributes[ATTR_MAX_TEMP] == DEFAULT_MAX_TEMP assert state.attributes[ATTR_MIN_TEMP] == DEFAULT_MIN_TEMP + assert state.attributes[ATTR_PRESET_MODES] == ["none", "eco", "day"] await hass.services.async_call( CLIMATE_DOMAIN, @@ -66,6 +76,20 @@ async def test_thermostat_update(spa, setup_entry, hass): ) # does nothing + assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_NONE + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_PRESET_MODE, + {ATTR_ENTITY_ID: entity_id, ATTR_PRESET_MODE: PRESET_ECO}, + blocking=True, + ) + spa.set_heat_mode.assert_called_with("ECO") + + spa.get_status.return_value["heatMode"] = "ECO" + await trigger_update(hass) + state = hass.states.get(entity_id) + assert state.attributes.get(ATTR_PRESET_MODE) == PRESET_ECO + spa.get_status.side_effect = smarttub.APIError await trigger_update(hass) # should not fail