1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-19 18:38:58 +00:00

[esphome] Implement feature_flags for climate (#153507)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Keith Burzinski
2025-10-18 16:47:11 -05:00
committed by GitHub
parent 363e5f088c
commit 7af3eb638b
2 changed files with 50 additions and 31 deletions

View File

@@ -9,6 +9,7 @@ from typing import Any, cast
from aioesphomeapi import (
ClimateAction,
ClimateFanMode,
ClimateFeature,
ClimateInfo,
ClimateMode,
ClimatePreset,
@@ -134,12 +135,16 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_translation_key = "climate"
_feature_flags = ClimateFeature(0)
@callback
def _on_static_info_update(self, static_info: EntityInfo) -> None:
"""Set attrs from static info."""
super()._on_static_info_update(static_info)
static_info = self._static_info
self._feature_flags = ClimateFeature(
static_info.supported_feature_flags_compat(self._api_version)
)
self._attr_precision = self._get_precision()
self._attr_hvac_modes = [
_CLIMATE_MODES.from_esphome(mode) for mode in static_info.supported_modes
@@ -163,11 +168,18 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
self._attr_max_temp = static_info.visual_max_temperature
self._attr_min_humidity = round(static_info.visual_min_humidity)
self._attr_max_humidity = round(static_info.visual_max_humidity)
features = ClimateEntityFeature.TARGET_TEMPERATURE
if static_info.supports_two_point_target_temperature:
features |= ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
if static_info.supports_target_humidity:
features = ClimateEntityFeature(0)
if self._feature_flags & ClimateFeature.SUPPORTS_TARGET_HUMIDITY:
features |= ClimateEntityFeature.TARGET_HUMIDITY
if self._feature_flags & ClimateFeature.REQUIRES_TWO_POINT_TARGET_TEMPERATURE:
features |= ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
else:
features |= ClimateEntityFeature.TARGET_TEMPERATURE
if (
self._feature_flags
& ClimateFeature.SUPPORTS_TWO_POINT_TARGET_TEMPERATURE
):
features |= ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
if self.preset_modes:
features |= ClimateEntityFeature.PRESET_MODE
if self.fan_modes:
@@ -203,7 +215,7 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
def hvac_action(self) -> HVACAction | None:
"""Return current action."""
# HA has no support feature field for hvac_action
if not self._static_info.supports_action:
if not self._feature_flags & ClimateFeature.SUPPORTS_ACTION:
return None
return _CLIMATE_ACTIONS.from_esphome(self._state.action)
@@ -233,7 +245,7 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
@esphome_float_state_property
def current_temperature(self) -> float | None:
"""Return the current temperature."""
if not self._static_info.supports_current_temperature:
if not self._feature_flags & ClimateFeature.SUPPORTS_CURRENT_TEMPERATURE:
return None
return self._state.current_temperature
@@ -242,7 +254,7 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
def current_humidity(self) -> int | None:
"""Return the current humidity."""
if (
not self._static_info.supports_current_humidity
(not self._feature_flags & ClimateFeature.SUPPORTS_CURRENT_HUMIDITY)
or (val := self._state.current_humidity) is None
or not isfinite(val)
):
@@ -254,7 +266,11 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
def target_temperature(self) -> float | None:
"""Return the temperature we try to reach."""
if (
not self._static_info.supports_two_point_target_temperature
not self._feature_flags
& (
ClimateFeature.REQUIRES_TWO_POINT_TARGET_TEMPERATURE
| ClimateFeature.SUPPORTS_TWO_POINT_TARGET_TEMPERATURE
)
and self.hvac_mode != HVACMode.AUTO
):
return self._state.target_temperature
@@ -295,7 +311,10 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti
cast(HVACMode, kwargs[ATTR_HVAC_MODE])
)
if ATTR_TEMPERATURE in kwargs:
if not self._static_info.supports_two_point_target_temperature:
if not self._feature_flags & (
ClimateFeature.REQUIRES_TWO_POINT_TARGET_TEMPERATURE
| ClimateFeature.SUPPORTS_TWO_POINT_TARGET_TEMPERATURE
):
data["target_temperature"] = kwargs[ATTR_TEMPERATURE]
else:
hvac_mode = kwargs.get(ATTR_HVAC_MODE) or self.hvac_mode

View File

@@ -7,6 +7,7 @@ from aioesphomeapi import (
APIClient,
ClimateAction,
ClimateFanMode,
ClimateFeature,
ClimateInfo,
ClimateMode,
ClimatePreset,
@@ -107,11 +108,10 @@ async def test_climate_entity_with_step_and_two_point(
object_id="myclimate",
key=1,
name="my climate",
supports_current_temperature=True,
supports_two_point_target_temperature=True,
feature_flags=ClimateFeature.SUPPORTS_CURRENT_TEMPERATURE
| ClimateFeature.SUPPORTS_TWO_POINT_TARGET_TEMPERATURE,
visual_target_temperature_step=2,
visual_current_temperature_step=2,
supports_action=False,
visual_min_temperature=10.0,
visual_max_temperature=30.0,
supported_modes=[ClimateMode.COOL, ClimateMode.HEAT, ClimateMode.AUTO],
@@ -346,13 +346,13 @@ async def test_climate_entity_with_humidity(
object_id="myclimate",
key=1,
name="my climate",
supports_current_temperature=True,
supports_two_point_target_temperature=True,
supports_action=True,
feature_flags=ClimateFeature.SUPPORTS_CURRENT_TEMPERATURE
| ClimateFeature.SUPPORTS_TWO_POINT_TARGET_TEMPERATURE
| ClimateFeature.SUPPORTS_CURRENT_HUMIDITY
| ClimateFeature.SUPPORTS_TARGET_HUMIDITY
| ClimateFeature.SUPPORTS_ACTION,
visual_min_temperature=10.0,
visual_max_temperature=30.0,
supports_current_humidity=True,
supports_target_humidity=True,
visual_min_humidity=10.1,
visual_max_humidity=29.7,
)
@@ -407,9 +407,9 @@ async def test_climate_entity_with_heat(
object_id="myclimate",
key=1,
name="my climate",
supports_current_temperature=True,
supports_two_point_target_temperature=True,
supports_action=True,
feature_flags=ClimateFeature.SUPPORTS_CURRENT_TEMPERATURE
| ClimateFeature.SUPPORTS_TWO_POINT_TARGET_TEMPERATURE
| ClimateFeature.SUPPORTS_ACTION,
visual_min_temperature=10.0,
visual_max_temperature=30.0,
supported_modes=[ClimateMode.COOL, ClimateMode.HEAT, ClimateMode.AUTO],
@@ -456,9 +456,9 @@ async def test_climate_entity_with_heat_cool(
object_id="myclimate",
key=1,
name="my climate",
supports_current_temperature=True,
supports_two_point_target_temperature=True,
supports_action=True,
feature_flags=ClimateFeature.SUPPORTS_CURRENT_TEMPERATURE
| ClimateFeature.SUPPORTS_TWO_POINT_TARGET_TEMPERATURE
| ClimateFeature.SUPPORTS_ACTION,
visual_min_temperature=10.0,
visual_max_temperature=30.0,
supported_modes=[ClimateMode.COOL, ClimateMode.HEAT, ClimateMode.HEAT_COOL],
@@ -516,7 +516,7 @@ async def test_climate_set_temperature_unsupported_mode(
object_id="myclimate",
key=1,
name="my climate",
supports_two_point_target_temperature=True,
feature_flags=ClimateFeature.SUPPORTS_TWO_POINT_TARGET_TEMPERATURE,
supported_modes=[ClimateMode.HEAT, ClimateMode.COOL, ClimateMode.AUTO],
visual_min_temperature=10.0,
visual_max_temperature=30.0,
@@ -563,13 +563,13 @@ async def test_climate_entity_with_inf_value(
object_id="myclimate",
key=1,
name="my climate",
supports_current_temperature=True,
supports_two_point_target_temperature=True,
supports_action=True,
feature_flags=ClimateFeature.SUPPORTS_CURRENT_TEMPERATURE
| ClimateFeature.SUPPORTS_TWO_POINT_TARGET_TEMPERATURE
| ClimateFeature.SUPPORTS_CURRENT_HUMIDITY
| ClimateFeature.SUPPORTS_TARGET_HUMIDITY
| ClimateFeature.SUPPORTS_ACTION,
visual_min_temperature=10.0,
visual_max_temperature=30.0,
supports_current_humidity=True,
supports_target_humidity=True,
visual_min_humidity=10.1,
visual_max_humidity=29.7,
)
@@ -616,10 +616,10 @@ async def test_climate_entity_attributes(
object_id="myclimate",
key=1,
name="my climate",
supports_current_temperature=True,
feature_flags=ClimateFeature.SUPPORTS_CURRENT_TEMPERATURE
| ClimateFeature.SUPPORTS_ACTION,
visual_target_temperature_step=2,
visual_current_temperature_step=2,
supports_action=True,
visual_min_temperature=10.0,
visual_max_temperature=30.0,
supported_fan_modes=[ClimateFanMode.LOW, ClimateFanMode.HIGH],