mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Generic thermostat presets (#56080)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
fb04b19960
commit
27e3a5ba83
@ -15,8 +15,12 @@ from homeassistant.components.climate.const import (
|
|||||||
HVAC_MODE_COOL,
|
HVAC_MODE_COOL,
|
||||||
HVAC_MODE_HEAT,
|
HVAC_MODE_HEAT,
|
||||||
HVAC_MODE_OFF,
|
HVAC_MODE_OFF,
|
||||||
|
PRESET_ACTIVITY,
|
||||||
PRESET_AWAY,
|
PRESET_AWAY,
|
||||||
|
PRESET_COMFORT,
|
||||||
|
PRESET_HOME,
|
||||||
PRESET_NONE,
|
PRESET_NONE,
|
||||||
|
PRESET_SLEEP,
|
||||||
SUPPORT_PRESET_MODE,
|
SUPPORT_PRESET_MODE,
|
||||||
SUPPORT_TARGET_TEMPERATURE,
|
SUPPORT_TARGET_TEMPERATURE,
|
||||||
)
|
)
|
||||||
@ -64,10 +68,20 @@ CONF_COLD_TOLERANCE = "cold_tolerance"
|
|||||||
CONF_HOT_TOLERANCE = "hot_tolerance"
|
CONF_HOT_TOLERANCE = "hot_tolerance"
|
||||||
CONF_KEEP_ALIVE = "keep_alive"
|
CONF_KEEP_ALIVE = "keep_alive"
|
||||||
CONF_INITIAL_HVAC_MODE = "initial_hvac_mode"
|
CONF_INITIAL_HVAC_MODE = "initial_hvac_mode"
|
||||||
CONF_AWAY_TEMP = "away_temp"
|
|
||||||
CONF_PRECISION = "precision"
|
CONF_PRECISION = "precision"
|
||||||
SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE
|
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(
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_HEATER): cv.entity_id,
|
vol.Required(CONF_HEATER): cv.entity_id,
|
||||||
@ -84,13 +98,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||||||
vol.Optional(CONF_INITIAL_HVAC_MODE): vol.In(
|
vol.Optional(CONF_INITIAL_HVAC_MODE): vol.In(
|
||||||
[HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_OFF]
|
[HVAC_MODE_COOL, HVAC_MODE_HEAT, HVAC_MODE_OFF]
|
||||||
),
|
),
|
||||||
vol.Optional(CONF_AWAY_TEMP): vol.Coerce(float),
|
|
||||||
vol.Optional(CONF_PRECISION): vol.In(
|
vol.Optional(CONF_PRECISION): vol.In(
|
||||||
[PRECISION_TENTHS, PRECISION_HALVES, PRECISION_WHOLE]
|
[PRECISION_TENTHS, PRECISION_HALVES, PRECISION_WHOLE]
|
||||||
),
|
),
|
||||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
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):
|
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)
|
hot_tolerance = config.get(CONF_HOT_TOLERANCE)
|
||||||
keep_alive = config.get(CONF_KEEP_ALIVE)
|
keep_alive = config.get(CONF_KEEP_ALIVE)
|
||||||
initial_hvac_mode = config.get(CONF_INITIAL_HVAC_MODE)
|
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)
|
precision = config.get(CONF_PRECISION)
|
||||||
unit = hass.config.units.temperature_unit
|
unit = hass.config.units.temperature_unit
|
||||||
unique_id = config.get(CONF_UNIQUE_ID)
|
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,
|
hot_tolerance,
|
||||||
keep_alive,
|
keep_alive,
|
||||||
initial_hvac_mode,
|
initial_hvac_mode,
|
||||||
away_temp,
|
presets,
|
||||||
precision,
|
precision,
|
||||||
unit,
|
unit,
|
||||||
unique_id,
|
unique_id,
|
||||||
@ -156,7 +171,7 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
|
|||||||
hot_tolerance,
|
hot_tolerance,
|
||||||
keep_alive,
|
keep_alive,
|
||||||
initial_hvac_mode,
|
initial_hvac_mode,
|
||||||
away_temp,
|
presets,
|
||||||
precision,
|
precision,
|
||||||
unit,
|
unit,
|
||||||
unique_id,
|
unique_id,
|
||||||
@ -171,7 +186,7 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self._hot_tolerance = hot_tolerance
|
self._hot_tolerance = hot_tolerance
|
||||||
self._keep_alive = keep_alive
|
self._keep_alive = keep_alive
|
||||||
self._hvac_mode = initial_hvac_mode
|
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
|
self._temp_precision = precision
|
||||||
if self.ac_mode:
|
if self.ac_mode:
|
||||||
self._hvac_list = [HVAC_MODE_COOL, HVAC_MODE_OFF]
|
self._hvac_list = [HVAC_MODE_COOL, HVAC_MODE_OFF]
|
||||||
@ -187,12 +202,12 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self._unit = unit
|
self._unit = unit
|
||||||
self._unique_id = unique_id
|
self._unique_id = unique_id
|
||||||
self._support_flags = SUPPORT_FLAGS
|
self._support_flags = SUPPORT_FLAGS
|
||||||
if away_temp:
|
if len(presets):
|
||||||
self._support_flags = SUPPORT_FLAGS | SUPPORT_PRESET_MODE
|
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:
|
else:
|
||||||
self._attr_preset_modes = [PRESET_NONE]
|
self._attr_preset_modes = [PRESET_NONE]
|
||||||
self._away_temp = away_temp
|
self._presets = presets
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Run when entity about to be added."""
|
"""Run when entity about to be added."""
|
||||||
@ -528,14 +543,15 @@ class GenericThermostat(ClimateEntity, RestoreEntity):
|
|||||||
if preset_mode == self._attr_preset_mode:
|
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
|
# I don't think we need to call async_write_ha_state if we didn't change the state
|
||||||
return
|
return
|
||||||
if preset_mode == PRESET_AWAY:
|
if preset_mode == PRESET_NONE:
|
||||||
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:
|
|
||||||
self._attr_preset_mode = PRESET_NONE
|
self._attr_preset_mode = PRESET_NONE
|
||||||
self._target_temp = self._saved_target_temp
|
self._target_temp = self._saved_target_temp
|
||||||
await self._async_control_heating(force=True)
|
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()
|
self.async_write_ha_state()
|
||||||
|
@ -13,8 +13,12 @@ from homeassistant.components.climate.const import (
|
|||||||
HVAC_MODE_COOL,
|
HVAC_MODE_COOL,
|
||||||
HVAC_MODE_HEAT,
|
HVAC_MODE_HEAT,
|
||||||
HVAC_MODE_OFF,
|
HVAC_MODE_OFF,
|
||||||
|
PRESET_ACTIVITY,
|
||||||
PRESET_AWAY,
|
PRESET_AWAY,
|
||||||
|
PRESET_COMFORT,
|
||||||
|
PRESET_HOME,
|
||||||
PRESET_NONE,
|
PRESET_NONE,
|
||||||
|
PRESET_SLEEP,
|
||||||
)
|
)
|
||||||
from homeassistant.components.generic_thermostat import (
|
from homeassistant.components.generic_thermostat import (
|
||||||
DOMAIN as GENERIC_THERMOSTAT_DOMAIN,
|
DOMAIN as GENERIC_THERMOSTAT_DOMAIN,
|
||||||
@ -209,6 +213,10 @@ async def setup_comp_2(hass):
|
|||||||
"heater": ENT_SWITCH,
|
"heater": ENT_SWITCH,
|
||||||
"target_sensor": ENT_SENSOR,
|
"target_sensor": ENT_SENSOR,
|
||||||
"away_temp": 16,
|
"away_temp": 16,
|
||||||
|
"sleep_temp": 17,
|
||||||
|
"home_temp": 19,
|
||||||
|
"comfort_temp": 20,
|
||||||
|
"activity_temp": 21,
|
||||||
"initial_hvac_mode": HVAC_MODE_HEAT,
|
"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
|
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."""
|
"""Test the setting away mode."""
|
||||||
await common.async_set_temperature(hass, 23)
|
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)
|
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.
|
"""Test the setting and removing away mode.
|
||||||
|
|
||||||
Verify original temperature is restored.
|
Verify original temperature is restored.
|
||||||
"""
|
"""
|
||||||
await common.async_set_temperature(hass, 23)
|
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)
|
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)
|
await common.async_set_preset_mode(hass, PRESET_NONE)
|
||||||
state = hass.states.get(ENTITY)
|
state = hass.states.get(ENTITY)
|
||||||
assert state.attributes.get("temperature") == 23
|
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.
|
"""Test the setting away mode twice in a row.
|
||||||
|
|
||||||
Verify original temperature is restored.
|
Verify original temperature is restored.
|
||||||
"""
|
"""
|
||||||
await common.async_set_temperature(hass, 23)
|
await common.async_set_temperature(hass, 23)
|
||||||
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_AWAY)
|
await common.async_set_preset_mode(hass, preset)
|
||||||
state = hass.states.get(ENTITY)
|
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)
|
await common.async_set_preset_mode(hass, PRESET_NONE)
|
||||||
state = hass.states.get(ENTITY)
|
state = hass.states.get(ENTITY)
|
||||||
assert state.attributes.get("temperature") == 23
|
assert state.attributes.get("temperature") == 23
|
||||||
|
Loading…
x
Reference in New Issue
Block a user