diff --git a/homeassistant/components/esphome/climate.py b/homeassistant/components/esphome/climate.py index 353481348b4fae..9527851d839e68 100644 --- a/homeassistant/components/esphome/climate.py +++ b/homeassistant/components/esphome/climate.py @@ -56,7 +56,7 @@ from homeassistant.core import callback from homeassistant.exceptions import ServiceValidationError -from .const import DOMAIN +from .const import DOMAIN, TEMPERATURE_UNIT_MAP from .entity import ( EsphomeEntity, convert_api_error_ha_error, @@ -131,7 +131,6 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEntity): """A climate implementation for ESPHome.""" - _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_translation_key = "climate" _feature_flags = ClimateFeature(0) @@ -143,6 +142,9 @@ def _on_static_info_update(self, static_info: EntityInfo) -> None: self._feature_flags = ClimateFeature( static_info.supported_feature_flags_compat(self._api_version) ) + self._attr_temperature_unit = TEMPERATURE_UNIT_MAP.get( + static_info.temperature_unit, UnitOfTemperature.CELSIUS + ) self._attr_precision = self._get_precision() self._attr_hvac_modes = [ _CLIMATE_MODES.from_esphome(mode) for mode in static_info.supported_modes diff --git a/homeassistant/components/esphome/const.py b/homeassistant/components/esphome/const.py index cde5d79446ca40..eb65222d8b9386 100644 --- a/homeassistant/components/esphome/const.py +++ b/homeassistant/components/esphome/const.py @@ -2,8 +2,10 @@ from typing import TYPE_CHECKING, Final +from aioesphomeapi import TemperatureUnit from awesomeversion import AwesomeVersion +from homeassistant.const import UnitOfTemperature from homeassistant.util.hass_dict import HassKey if TYPE_CHECKING: @@ -37,3 +39,9 @@ WAKE_WORDS_DIR_NAME = "custom_wake_words" WAKE_WORDS_API_PATH = "/api/esphome/wake_words" + +TEMPERATURE_UNIT_MAP: dict[TemperatureUnit | None, UnitOfTemperature] = { + TemperatureUnit.CELSIUS: UnitOfTemperature.CELSIUS, + TemperatureUnit.FAHRENHEIT: UnitOfTemperature.FAHRENHEIT, + TemperatureUnit.KELVIN: UnitOfTemperature.KELVIN, +} diff --git a/homeassistant/components/esphome/water_heater.py b/homeassistant/components/esphome/water_heater.py index d98da22cd6e197..c015b63136ec05 100644 --- a/homeassistant/components/esphome/water_heater.py +++ b/homeassistant/components/esphome/water_heater.py @@ -19,6 +19,7 @@ from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, UnitOfTemperature from homeassistant.core import callback +from .const import TEMPERATURE_UNIT_MAP from .entity import ( EsphomeEntity, convert_api_error_ha_error, @@ -49,7 +50,6 @@ class EsphomeWaterHeater( ): """A water heater implementation for ESPHome.""" - _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_precision = PRECISION_TENTHS @callback @@ -57,6 +57,9 @@ 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_temperature_unit = TEMPERATURE_UNIT_MAP.get( + static_info.temperature_unit, UnitOfTemperature.CELSIUS + ) self._attr_min_temp = static_info.min_temperature self._attr_max_temp = static_info.max_temperature self._attr_target_temperature_step = static_info.target_temperature_step diff --git a/tests/components/esphome/test_climate.py b/tests/components/esphome/test_climate.py index 2a2c232e2c463c..2901531815a588 100644 --- a/tests/components/esphome/test_climate.py +++ b/tests/components/esphome/test_climate.py @@ -13,6 +13,7 @@ ClimatePreset, ClimateState, ClimateSwingMode, + TemperatureUnit, ) import pytest from syrupy.assertion import SnapshotAssertion @@ -30,6 +31,7 @@ ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, ATTR_TEMPERATURE, + DATA_COMPONENT, DOMAIN as CLIMATE_DOMAIN, FAN_HIGH, SERVICE_SET_FAN_MODE, @@ -41,7 +43,7 @@ SWING_BOTH, HVACMode, ) -from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.const import ATTR_ENTITY_ID, UnitOfTemperature from homeassistant.core import HomeAssistant from homeassistant.exceptions import ServiceValidationError @@ -685,3 +687,43 @@ async def test_climate_entity_attribute_current_temperature_unsupported( state = hass.states.get("climate.test_my_climate") assert state is not None assert state.attributes[ATTR_CURRENT_TEMPERATURE] is None + + +@pytest.mark.parametrize( + ("temperature_unit", "expected_unit"), + [ + (TemperatureUnit.CELSIUS, UnitOfTemperature.CELSIUS), + (TemperatureUnit.FAHRENHEIT, UnitOfTemperature.FAHRENHEIT), + (TemperatureUnit.KELVIN, UnitOfTemperature.KELVIN), + ( + 3, + UnitOfTemperature.CELSIUS, + ), # unknown value falls back to Celsius via HA's TEMPERATURE_UNIT_MAP default + ], +) +async def test_climate_entity_temperature_unit( + hass: HomeAssistant, + mock_client: APIClient, + mock_generic_device_entry: MockGenericDeviceEntryType, + temperature_unit: TemperatureUnit | int, + expected_unit: UnitOfTemperature, +) -> None: + """Test that the temperature unit is passed through correctly.""" + entity_info = [ + ClimateInfo( + object_id="myclimate", + key=1, + name="my climate", + temperature_unit=temperature_unit, + ) + ] + states = [ClimateState(key=1, mode=ClimateMode.COOL, target_temperature=22)] + await mock_generic_device_entry( + mock_client=mock_client, + entity_info=entity_info, + states=states, + ) + assert hass.states.get("climate.test_my_climate") is not None + entity = hass.data[DATA_COMPONENT].get_entity("climate.test_my_climate") + assert entity is not None + assert entity.temperature_unit == expected_unit diff --git a/tests/components/esphome/test_water_heater.py b/tests/components/esphome/test_water_heater.py index 2263ce07074d94..6cc90597896696 100644 --- a/tests/components/esphome/test_water_heater.py +++ b/tests/components/esphome/test_water_heater.py @@ -4,6 +4,7 @@ from aioesphomeapi import ( APIClient, + TemperatureUnit, WaterHeaterFeature, WaterHeaterInfo, WaterHeaterMode, @@ -15,6 +16,7 @@ from homeassistant.components.water_heater import ( ATTR_AWAY_MODE, ATTR_OPERATION_LIST, + DATA_COMPONENT, DOMAIN as WATER_HEATER_DOMAIN, SERVICE_SET_AWAY_MODE, SERVICE_SET_OPERATION_MODE, @@ -27,6 +29,7 @@ ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, ATTR_TEMPERATURE, + UnitOfTemperature, ) from homeassistant.core import HomeAssistant @@ -462,3 +465,45 @@ async def test_water_heater_set_away_mode( mock_client.water_heater_command.assert_has_calls( [call(key=1, away=away_mode, device_id=0)] ) + + +@pytest.mark.parametrize( + ("temperature_unit", "expected_unit"), + [ + (TemperatureUnit.CELSIUS, UnitOfTemperature.CELSIUS), + (TemperatureUnit.FAHRENHEIT, UnitOfTemperature.FAHRENHEIT), + (TemperatureUnit.KELVIN, UnitOfTemperature.KELVIN), + ( + 3, + UnitOfTemperature.CELSIUS, + ), # unknown value falls back to Celsius via HA's TEMPERATURE_UNIT_MAP default + ], +) +async def test_water_heater_temperature_unit( + hass: HomeAssistant, + mock_client: APIClient, + mock_generic_device_entry: MockGenericDeviceEntryType, + temperature_unit: TemperatureUnit | int, + expected_unit: UnitOfTemperature, +) -> None: + """Test that the temperature unit is passed through correctly.""" + entity_info = [ + WaterHeaterInfo( + object_id="my_boiler", + key=1, + name="My Boiler", + min_temperature=10.0, + max_temperature=85.0, + temperature_unit=temperature_unit, + ) + ] + states = [WaterHeaterState(key=1, target_temperature=50.0)] + await mock_generic_device_entry( + mock_client=mock_client, + entity_info=entity_info, + states=states, + ) + assert hass.states.get("water_heater.test_my_boiler") is not None + entity = hass.data[DATA_COMPONENT].get_entity("water_heater.test_my_boiler") + assert entity is not None + assert entity.temperature_unit == expected_unit