diff --git a/homeassistant/components/esphome/climate.py b/homeassistant/components/esphome/climate.py index 2c1d005f9be..34043da012e 100644 --- a/homeassistant/components/esphome/climate.py +++ b/homeassistant/components/esphome/climate.py @@ -11,6 +11,7 @@ from aioesphomeapi import ( ClimatePreset, ClimateState, ClimateSwingMode, + EntityInfo, ) from homeassistant.components.climate import ( @@ -51,7 +52,7 @@ from homeassistant.const import ( PRECISION_WHOLE, UnitOfTemperature, ) -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from .entity import ( @@ -140,71 +141,32 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti _attr_temperature_unit = UnitOfTemperature.CELSIUS - @property - def precision(self) -> float: - """Return the precision of the climate device.""" - precicions = [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS] - if self._static_info.visual_current_temperature_step != 0: - step = self._static_info.visual_current_temperature_step - else: - step = self._static_info.visual_target_temperature_step - for prec in precicions: - if step >= prec: - return prec - # Fall back to highest precision, tenths - return PRECISION_TENTHS - - @property - def hvac_modes(self) -> list[HVACMode]: - """Return the list of available operation modes.""" - return [ - _CLIMATE_MODES.from_esphome(mode) - for mode in self._static_info.supported_modes + @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._attr_precision = self._get_precision() + self._attr_hvac_modes = [ + _CLIMATE_MODES.from_esphome(mode) for mode in static_info.supported_modes ] - - @property - def fan_modes(self) -> list[str]: - """Return the list of available fan modes.""" - return [ - _FAN_MODES.from_esphome(mode) - for mode in self._static_info.supported_fan_modes - ] + self._static_info.supported_custom_fan_modes - - @property - def preset_modes(self) -> list[str]: - """Return preset modes.""" - return [ + self._attr_fan_modes = [ + _FAN_MODES.from_esphome(mode) for mode in static_info.supported_fan_modes + ] + static_info.supported_custom_fan_modes + self._attr_preset_modes = [ _PRESETS.from_esphome(preset) - for preset in self._static_info.supported_presets_compat(self._api_version) - ] + self._static_info.supported_custom_presets - - @property - def swing_modes(self) -> list[str]: - """Return the list of available swing modes.""" - return [ + for preset in static_info.supported_presets_compat(self._api_version) + ] + static_info.supported_custom_presets + self._attr_swing_modes = [ _SWING_MODES.from_esphome(mode) - for mode in self._static_info.supported_swing_modes + for mode in static_info.supported_swing_modes ] - - @property - def target_temperature_step(self) -> float: - """Return the supported step of target temperature.""" # Round to one digit because of floating point math - return round(self._static_info.visual_target_temperature_step, 1) - - @property - def min_temp(self) -> float: - """Return the minimum temperature.""" - return self._static_info.visual_min_temperature - - @property - def max_temp(self) -> float: - """Return the maximum temperature.""" - return self._static_info.visual_max_temperature - - @property - def supported_features(self) -> ClimateEntityFeature: - """Return the list of supported features.""" + self._attr_target_temperature_step = round( + static_info.visual_target_temperature_step, 1 + ) + self._attr_min_temp = static_info.visual_min_temperature + self._attr_max_temp = static_info.visual_max_temperature features = ClimateEntityFeature(0) if self._static_info.supports_two_point_target_temperature: features |= ClimateEntityFeature.TARGET_TEMPERATURE_RANGE @@ -216,7 +178,21 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti features |= ClimateEntityFeature.FAN_MODE if self.swing_modes: features |= ClimateEntityFeature.SWING_MODE - return features + self._attr_supported_features = features + + def _get_precision(self) -> float: + """Return the precision of the climate device.""" + precicions = [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS] + static_info = self._static_info + if static_info.visual_current_temperature_step != 0: + step = static_info.visual_current_temperature_step + else: + step = static_info.visual_target_temperature_step + for prec in precicions: + if step >= prec: + return prec + # Fall back to highest precision, tenths + return PRECISION_TENTHS @property @esphome_state_property @@ -237,16 +213,16 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti @esphome_state_property def fan_mode(self) -> str | None: """Return current fan setting.""" - return self._state.custom_fan_mode or _FAN_MODES.from_esphome( - self._state.fan_mode - ) + state = self._state + return state.custom_fan_mode or _FAN_MODES.from_esphome(state.fan_mode) @property @esphome_state_property def preset_mode(self) -> str | None: """Return current preset mode.""" - return self._state.custom_preset or _PRESETS.from_esphome( - self._state.preset_compat(self._api_version) + state = self._state + return state.custom_preset or _PRESETS.from_esphome( + state.preset_compat(self._api_version) ) @property @@ -281,7 +257,7 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti async def async_set_temperature(self, **kwargs: Any) -> None: """Set new target temperature (and operation mode if set).""" - data: dict[str, Any] = {"key": self._static_info.key} + data: dict[str, Any] = {"key": self._key} if ATTR_HVAC_MODE in kwargs: data["mode"] = _CLIMATE_MODES.from_hass( cast(HVACMode, kwargs[ATTR_HVAC_MODE]) @@ -297,12 +273,12 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set new target operation mode.""" await self._client.climate_command( - key=self._static_info.key, mode=_CLIMATE_MODES.from_hass(hvac_mode) + key=self._key, mode=_CLIMATE_MODES.from_hass(hvac_mode) ) async def async_set_preset_mode(self, preset_mode: str) -> None: """Set preset mode.""" - kwargs: dict[str, Any] = {"key": self._static_info.key} + kwargs: dict[str, Any] = {"key": self._key} if preset_mode in self._static_info.supported_custom_presets: kwargs["custom_preset"] = preset_mode else: @@ -311,7 +287,7 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti async def async_set_fan_mode(self, fan_mode: str) -> None: """Set new fan mode.""" - kwargs: dict[str, Any] = {"key": self._static_info.key} + kwargs: dict[str, Any] = {"key": self._key} if fan_mode in self._static_info.supported_custom_fan_modes: kwargs["custom_fan_mode"] = fan_mode else: @@ -321,5 +297,5 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti async def async_set_swing_mode(self, swing_mode: str) -> None: """Set new swing mode.""" await self._client.climate_command( - key=self._static_info.key, swing_mode=_SWING_MODES.from_hass(swing_mode) + key=self._key, swing_mode=_SWING_MODES.from_hass(swing_mode) )