1
0
mirror of https://github.com/home-assistant/core.git synced 2026-04-17 23:53:49 +01:00

Add climate.is_hvac_mode condition (#166570)

This commit is contained in:
Erik Montnemery
2026-03-26 16:24:27 +01:00
committed by Franck Nijhof
parent 1b972d4adc
commit ee9d9781ee
5 changed files with 109 additions and 1 deletions

View File

@@ -1,10 +1,18 @@
"""Provides conditions for climates.""" """Provides conditions for climates."""
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.const import ATTR_TEMPERATURE, CONF_OPTIONS, UnitOfTemperature
from homeassistant.core import HomeAssistant, State from homeassistant.core import HomeAssistant, State
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.automation import DomainSpec from homeassistant.helpers.automation import DomainSpec
from homeassistant.helpers.condition import ( from homeassistant.helpers.condition import (
ENTITY_STATE_CONDITION_SCHEMA_ANY_ALL,
Condition, Condition,
ConditionConfig,
EntityConditionBase,
EntityNumericalConditionWithUnitBase, EntityNumericalConditionWithUnitBase,
make_entity_numerical_condition, make_entity_numerical_condition,
make_entity_state_condition, make_entity_state_condition,
@@ -13,6 +21,36 @@ from homeassistant.util.unit_conversion import TemperatureConverter
from .const import ATTR_HUMIDITY, ATTR_HVAC_ACTION, DOMAIN, HVACAction, HVACMode from .const import ATTR_HUMIDITY, ATTR_HVAC_ACTION, DOMAIN, HVACAction, HVACMode
CONF_HVAC_MODE = "hvac_mode"
_HVAC_MODE_CONDITION_SCHEMA = ENTITY_STATE_CONDITION_SCHEMA_ANY_ALL.extend(
{
vol.Required(CONF_OPTIONS): {
vol.Required(CONF_HVAC_MODE): vol.All(
cv.ensure_list, vol.Length(min=1), [vol.Coerce(HVACMode)]
),
},
}
)
class ClimateHVACModeCondition(EntityConditionBase):
"""Condition for climate HVAC mode."""
_domain_specs = {DOMAIN: DomainSpec()}
_schema = _HVAC_MODE_CONDITION_SCHEMA
def __init__(self, hass: HomeAssistant, config: ConditionConfig) -> None:
"""Initialize the HVAC mode condition."""
super().__init__(hass, config)
if TYPE_CHECKING:
assert config.options is not None
self._hvac_modes: set[str] = set(config.options[CONF_HVAC_MODE])
def is_valid_state(self, entity_state: State) -> bool:
"""Check if the state matches any of the expected HVAC modes."""
return entity_state.state in self._hvac_modes
class ClimateTargetTemperatureCondition(EntityNumericalConditionWithUnitBase): class ClimateTargetTemperatureCondition(EntityNumericalConditionWithUnitBase):
"""Mixin for climate target temperature conditions with unit conversion.""" """Mixin for climate target temperature conditions with unit conversion."""
@@ -28,6 +66,7 @@ class ClimateTargetTemperatureCondition(EntityNumericalConditionWithUnitBase):
CONDITIONS: dict[str, type[Condition]] = { CONDITIONS: dict[str, type[Condition]] = {
"is_hvac_mode": ClimateHVACModeCondition,
"is_off": make_entity_state_condition(DOMAIN, HVACMode.OFF), "is_off": make_entity_state_condition(DOMAIN, HVACMode.OFF),
"is_on": make_entity_state_condition( "is_on": make_entity_state_condition(
DOMAIN, DOMAIN,

View File

@@ -45,6 +45,21 @@ is_cooling: *condition_common
is_drying: *condition_common is_drying: *condition_common
is_heating: *condition_common is_heating: *condition_common
is_hvac_mode:
target: *condition_climate_target
fields:
behavior: *condition_behavior
hvac_mode:
context:
filter_target: target
required: true
selector:
state:
hide_states:
- unavailable
- unknown
multiple: true
target_humidity: target_humidity:
target: *condition_climate_target target: *condition_climate_target
fields: fields:

View File

@@ -9,6 +9,9 @@
"is_heating": { "is_heating": {
"condition": "mdi:fire" "condition": "mdi:fire"
}, },
"is_hvac_mode": {
"condition": "mdi:thermostat"
},
"is_off": { "is_off": {
"condition": "mdi:power-off" "condition": "mdi:power-off"
}, },

View File

@@ -41,6 +41,20 @@
}, },
"name": "Climate-control device is heating" "name": "Climate-control device is heating"
}, },
"is_hvac_mode": {
"description": "Tests if one or more climate-control devices are set to a specific HVAC mode.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::condition_behavior_description%]",
"name": "[%key:component::climate::common::condition_behavior_name%]"
},
"hvac_mode": {
"description": "The HVAC modes to test for.",
"name": "Modes"
}
},
"name": "Climate-control device HVAC mode"
},
"is_off": { "is_off": {
"description": "Tests if one or more climate-control devices are off.", "description": "Tests if one or more climate-control devices are off.",
"fields": { "fields": {

View File

@@ -47,6 +47,7 @@ async def target_climates(hass: HomeAssistant) -> dict[str, list[str]]:
"climate.is_cooling", "climate.is_cooling",
"climate.is_drying", "climate.is_drying",
"climate.is_heating", "climate.is_heating",
"climate.is_hvac_mode",
"climate.target_humidity", "climate.target_humidity",
"climate.target_temperature", "climate.target_temperature",
], ],
@@ -83,6 +84,24 @@ async def test_climate_conditions_gated_by_labs_flag(
], ],
other_states=[HVACMode.OFF], other_states=[HVACMode.OFF],
), ),
*(
param
for mode in HVACMode
for param in parametrize_condition_states_any(
condition="climate.is_hvac_mode",
condition_options={"hvac_mode": [mode]},
target_states=[mode],
other_states=[m for m in HVACMode if m != mode],
)
),
*parametrize_condition_states_any(
condition="climate.is_hvac_mode",
condition_options={"hvac_mode": [HVACMode.HEAT, HVACMode.COOL]},
target_states=[HVACMode.HEAT, HVACMode.COOL],
other_states=[
m for m in HVACMode if m not in (HVACMode.HEAT, HVACMode.COOL)
],
),
], ],
) )
async def test_climate_state_condition_behavior_any( async def test_climate_state_condition_behavior_any(
@@ -133,6 +152,24 @@ async def test_climate_state_condition_behavior_any(
], ],
other_states=[HVACMode.OFF], other_states=[HVACMode.OFF],
), ),
*(
param
for mode in HVACMode
for param in parametrize_condition_states_all(
condition="climate.is_hvac_mode",
condition_options={"hvac_mode": [mode]},
target_states=[mode],
other_states=[m for m in HVACMode if m != mode],
)
),
*parametrize_condition_states_all(
condition="climate.is_hvac_mode",
condition_options={"hvac_mode": [HVACMode.HEAT, HVACMode.COOL]},
target_states=[HVACMode.HEAT, HVACMode.COOL],
other_states=[
m for m in HVACMode if m not in (HVACMode.HEAT, HVACMode.COOL)
],
),
], ],
) )
async def test_climate_state_condition_behavior_all( async def test_climate_state_condition_behavior_all(