Generic thermostat presets (#56080)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Brian Egge 2021-12-23 19:29:29 -05:00 committed by GitHub
parent fb04b19960
commit 27e3a5ba83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 85 additions and 26 deletions

View file

@ -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()

View file

@ -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