diff --git a/homeassistant/components/esphome/climate.py b/homeassistant/components/esphome/climate.py index 8c4e0603191..1de8db61f4b 100644 --- a/homeassistant/components/esphome/climate.py +++ b/homeassistant/components/esphome/climate.py @@ -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 diff --git a/tests/components/esphome/test_climate.py b/tests/components/esphome/test_climate.py index 216421cd8b0..2a2c232e2c4 100644 --- a/tests/components/esphome/test_climate.py +++ b/tests/components/esphome/test_climate.py @@ -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],