diff --git a/homeassistant/components/generic_thermostat/climate.py b/homeassistant/components/generic_thermostat/climate.py index 2c27d371c5e..67d4be92c95 100644 --- a/homeassistant/components/generic_thermostat/climate.py +++ b/homeassistant/components/generic_thermostat/climate.py @@ -15,8 +15,12 @@ from homeassistant.components.climate.const import ( HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_OFF, + PRESET_ACTIVITY, PRESET_AWAY, + PRESET_COMFORT, + PRESET_HOME, PRESET_NONE, + PRESET_SLEEP, SUPPORT_PRESET_MODE, SUPPORT_TARGET_TEMPERATURE, ) @@ -64,10 +68,20 @@ CONF_COLD_TOLERANCE = "cold_tolerance" CONF_HOT_TOLERANCE = "hot_tolerance" CONF_KEEP_ALIVE = "keep_alive" CONF_INITIAL_HVAC_MODE = "initial_hvac_mode" -CONF_AWAY_TEMP = "away_temp" CONF_PRECISION = "precision" SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE +CONF_PRESETS = { + p: f"{p}_temp" + for p in ( + PRESET_AWAY, + PRESET_COMFORT, + PRESET_HOME, + PRESET_SLEEP, + PRESET_ACTIVITY, + ) +} + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Required(CONF_HEATER): cv.entity_id, @@ -84,13 +98,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( vol.Optional(CONF_INITIAL_HVAC_MODE): vol.In( [HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_OFF] ), - vol.Optional(CONF_AWAY_TEMP): vol.Coerce(float), vol.Optional(CONF_PRECISION): vol.In( [PRECISION_TENTHS, PRECISION_HALVES, PRECISION_WHOLE] ), vol.Optional(CONF_UNIQUE_ID): cv.string, } -) +).extend({vol.Optional(v): vol.Coerce(float) for (k, v) in CONF_PRESETS.items()}) async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): @@ -110,7 +123,9 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= hot_tolerance = config.get(CONF_HOT_TOLERANCE) keep_alive = config.get(CONF_KEEP_ALIVE) initial_hvac_mode = config.get(CONF_INITIAL_HVAC_MODE) - away_temp = config.get(CONF_AWAY_TEMP) + presets = { + key: config[value] for key, value in CONF_PRESETS.items() if value in config + } precision = config.get(CONF_PRECISION) unit = hass.config.units.temperature_unit unique_id = config.get(CONF_UNIQUE_ID) @@ -130,7 +145,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= hot_tolerance, keep_alive, initial_hvac_mode, - away_temp, + presets, precision, unit, unique_id, @@ -156,7 +171,7 @@ class GenericThermostat(ClimateEntity, RestoreEntity): hot_tolerance, keep_alive, initial_hvac_mode, - away_temp, + presets, precision, unit, unique_id, @@ -171,7 +186,7 @@ class GenericThermostat(ClimateEntity, RestoreEntity): self._hot_tolerance = hot_tolerance self._keep_alive = keep_alive self._hvac_mode = initial_hvac_mode - self._saved_target_temp = target_temp or away_temp + self._saved_target_temp = target_temp or next(iter(presets.values()), None) self._temp_precision = precision if self.ac_mode: self._hvac_list = [HVAC_MODE_COOL, HVAC_MODE_OFF] @@ -187,12 +202,12 @@ class GenericThermostat(ClimateEntity, RestoreEntity): self._unit = unit self._unique_id = unique_id self._support_flags = SUPPORT_FLAGS - if away_temp: + if len(presets): self._support_flags = SUPPORT_FLAGS | SUPPORT_PRESET_MODE - self._attr_preset_modes = [PRESET_NONE, PRESET_AWAY] + self._attr_preset_modes = [PRESET_NONE] + list(presets.keys()) else: self._attr_preset_modes = [PRESET_NONE] - self._away_temp = away_temp + self._presets = presets async def async_added_to_hass(self): """Run when entity about to be added.""" @@ -528,14 +543,15 @@ class GenericThermostat(ClimateEntity, RestoreEntity): if preset_mode == self._attr_preset_mode: # I don't think we need to call async_write_ha_state if we didn't change the state return - if preset_mode == PRESET_AWAY: - self._attr_preset_mode = PRESET_AWAY - self._saved_target_temp = self._target_temp - self._target_temp = self._away_temp - await self._async_control_heating(force=True) - elif preset_mode == PRESET_NONE: + if preset_mode == PRESET_NONE: self._attr_preset_mode = PRESET_NONE self._target_temp = self._saved_target_temp await self._async_control_heating(force=True) + else: + if self._attr_preset_mode == PRESET_NONE: + self._saved_target_temp = self._target_temp + self._attr_preset_mode = preset_mode + self._target_temp = self._presets[preset_mode] + await self._async_control_heating(force=True) self.async_write_ha_state() diff --git a/tests/components/generic_thermostat/test_climate.py b/tests/components/generic_thermostat/test_climate.py index a1896c94d2f..1720a54d973 100644 --- a/tests/components/generic_thermostat/test_climate.py +++ b/tests/components/generic_thermostat/test_climate.py @@ -13,8 +13,12 @@ from homeassistant.components.climate.const import ( HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_OFF, + PRESET_ACTIVITY, PRESET_AWAY, + PRESET_COMFORT, + PRESET_HOME, PRESET_NONE, + PRESET_SLEEP, ) from homeassistant.components.generic_thermostat import ( DOMAIN as GENERIC_THERMOSTAT_DOMAIN, @@ -209,6 +213,10 @@ async def setup_comp_2(hass): "heater": ENT_SWITCH, "target_sensor": ENT_SENSOR, "away_temp": 16, + "sleep_temp": 17, + "home_temp": 19, + "comfort_temp": 20, + "activity_temp": 21, "initial_hvac_mode": HVAC_MODE_HEAT, } }, @@ -288,38 +296,73 @@ async def test_set_target_temp(hass, setup_comp_2): assert state.attributes.get("temperature") == 30.0 -async def test_set_away_mode(hass, setup_comp_2): +@pytest.mark.parametrize( + "preset,temp", + [ + (PRESET_NONE, 23), + (PRESET_AWAY, 16), + (PRESET_COMFORT, 20), + (PRESET_HOME, 19), + (PRESET_SLEEP, 17), + (PRESET_ACTIVITY, 21), + ], +) +async def test_set_away_mode(hass, setup_comp_2, preset, temp): """Test the setting away mode.""" await common.async_set_temperature(hass, 23) - await common.async_set_preset_mode(hass, PRESET_AWAY) + await common.async_set_preset_mode(hass, preset) state = hass.states.get(ENTITY) - assert state.attributes.get("temperature") == 16 + assert state.attributes.get("temperature") == temp -async def test_set_away_mode_and_restore_prev_temp(hass, setup_comp_2): +@pytest.mark.parametrize( + "preset,temp", + [ + (PRESET_NONE, 23), + (PRESET_AWAY, 16), + (PRESET_COMFORT, 20), + (PRESET_HOME, 19), + (PRESET_SLEEP, 17), + (PRESET_ACTIVITY, 21), + ], +) +async def test_set_away_mode_and_restore_prev_temp(hass, setup_comp_2, preset, temp): """Test the setting and removing away mode. Verify original temperature is restored. """ await common.async_set_temperature(hass, 23) - await common.async_set_preset_mode(hass, PRESET_AWAY) + await common.async_set_preset_mode(hass, preset) state = hass.states.get(ENTITY) - assert state.attributes.get("temperature") == 16 + assert state.attributes.get("temperature") == temp await common.async_set_preset_mode(hass, PRESET_NONE) state = hass.states.get(ENTITY) assert state.attributes.get("temperature") == 23 -async def test_set_away_mode_twice_and_restore_prev_temp(hass, setup_comp_2): +@pytest.mark.parametrize( + "preset,temp", + [ + (PRESET_NONE, 23), + (PRESET_AWAY, 16), + (PRESET_COMFORT, 20), + (PRESET_HOME, 19), + (PRESET_SLEEP, 17), + (PRESET_ACTIVITY, 21), + ], +) +async def test_set_away_mode_twice_and_restore_prev_temp( + hass, setup_comp_2, preset, temp +): """Test the setting away mode twice in a row. Verify original temperature is restored. """ await common.async_set_temperature(hass, 23) - await common.async_set_preset_mode(hass, PRESET_AWAY) - await common.async_set_preset_mode(hass, PRESET_AWAY) + await common.async_set_preset_mode(hass, preset) + await common.async_set_preset_mode(hass, preset) state = hass.states.get(ENTITY) - assert state.attributes.get("temperature") == 16 + assert state.attributes.get("temperature") == temp await common.async_set_preset_mode(hass, PRESET_NONE) state = hass.states.get(ENTITY) assert state.attributes.get("temperature") == 23