diff --git a/homeassistant/components/knx/climate.py b/homeassistant/components/knx/climate.py index abce143c760..4932df55087 100644 --- a/homeassistant/components/knx/climate.py +++ b/homeassistant/components/knx/climate.py @@ -10,11 +10,10 @@ from xknx.devices import ( ClimateMode as XknxClimateMode, Device as XknxDevice, ) -from xknx.dpt.dpt_20 import HVACControllerMode +from xknx.dpt.dpt_20 import HVACControllerMode, HVACOperationMode from homeassistant import config_entries from homeassistant.components.climate import ( - PRESET_AWAY, ClimateEntity, ClimateEntityFeature, HVACAction, @@ -32,19 +31,12 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType from . import KNXModule -from .const import ( - CONTROLLER_MODES, - CURRENT_HVAC_ACTIONS, - DATA_KNX_CONFIG, - DOMAIN, - PRESET_MODES, -) +from .const import CONTROLLER_MODES, CURRENT_HVAC_ACTIONS, DATA_KNX_CONFIG, DOMAIN from .knx_entity import KnxYamlEntity from .schema import ClimateSchema ATTR_COMMAND_VALUE = "command_value" CONTROLLER_MODES_INV = {value: key for key, value in CONTROLLER_MODES.items()} -PRESET_MODES_INV = {value: key for key, value in PRESET_MODES.items()} async def async_setup_entry( @@ -142,6 +134,7 @@ class KNXClimate(KnxYamlEntity, ClimateEntity): _device: XknxClimate _attr_temperature_unit = UnitOfTemperature.CELSIUS + _attr_translation_key = "knx_climate" _enable_turn_on_off_backwards_compatibility = False def __init__(self, knx_module: KNXModule, config: ConfigType) -> None: @@ -165,8 +158,14 @@ class KNXClimate(KnxYamlEntity, ClimateEntity): ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON ) - if self.preset_modes: + if ( + self._device.mode is not None + and self._device.mode.operation_modes # empty list when not writable + ): self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE + self._attr_preset_modes = [ + mode.name.lower() for mode in self._device.mode.operation_modes + ] self._attr_target_temperature_step = self._device.temperature_step self._attr_unique_id = ( f"{self._device.temperature.group_address_state}_" @@ -309,32 +308,18 @@ class KNXClimate(KnxYamlEntity, ClimateEntity): Requires ClimateEntityFeature.PRESET_MODE. """ if self._device.mode is not None and self._device.mode.supports_operation_mode: - return PRESET_MODES.get(self._device.mode.operation_mode, PRESET_AWAY) + return self._device.mode.operation_mode.name.lower() return None - @property - def preset_modes(self) -> list[str] | None: - """Return a list of available preset modes. - - Requires ClimateEntityFeature.PRESET_MODE. - """ - if self._device.mode is None: - return None - - presets = [ - PRESET_MODES.get(operation_mode) - for operation_mode in self._device.mode.operation_modes - ] - return list(filter(None, presets)) - async def async_set_preset_mode(self, preset_mode: str) -> None: """Set new preset mode.""" if ( self._device.mode is not None - and self._device.mode.supports_operation_mode - and (knx_operation_mode := PRESET_MODES_INV.get(preset_mode)) is not None + and self._device.mode.operation_modes # empty list when not writable ): - await self._device.mode.set_operation_mode(knx_operation_mode) + await self._device.mode.set_operation_mode( + HVACOperationMode[preset_mode.upper()] + ) self.async_write_ha_state() @property diff --git a/homeassistant/components/knx/const.py b/homeassistant/components/knx/const.py index 6400f0fe466..9ceb18385cb 100644 --- a/homeassistant/components/knx/const.py +++ b/homeassistant/components/knx/const.py @@ -6,18 +6,10 @@ from collections.abc import Awaitable, Callable from enum import Enum from typing import Final, TypedDict -from xknx.dpt.dpt_20 import HVACControllerMode, HVACOperationMode +from xknx.dpt.dpt_20 import HVACControllerMode from xknx.telegram import Telegram -from homeassistant.components.climate import ( - PRESET_AWAY, - PRESET_COMFORT, - PRESET_ECO, - PRESET_NONE, - PRESET_SLEEP, - HVACAction, - HVACMode, -) +from homeassistant.components.climate import HVACAction, HVACMode from homeassistant.const import Platform DOMAIN: Final = "knx" @@ -174,12 +166,3 @@ CURRENT_HVAC_ACTIONS: Final = { HVACMode.FAN_ONLY: HVACAction.FAN, HVACMode.DRY: HVACAction.DRYING, } - -PRESET_MODES: Final = { - # Map DPT 20.102 HVAC operating modes to HA presets - HVACOperationMode.AUTO: PRESET_NONE, - HVACOperationMode.BUILDING_PROTECTION: PRESET_ECO, - HVACOperationMode.ECONOMY: PRESET_SLEEP, - HVACOperationMode.STANDBY: PRESET_AWAY, - HVACOperationMode.COMFORT: PRESET_COMFORT, -} diff --git a/homeassistant/components/knx/icons.json b/homeassistant/components/knx/icons.json index 736923375ee..2aee34219f6 100644 --- a/homeassistant/components/knx/icons.json +++ b/homeassistant/components/knx/icons.json @@ -1,5 +1,19 @@ { "entity": { + "climate": { + "knx_climate": { + "state_attributes": { + "preset_mode": { + "state": { + "comfort": "mdi:sofa", + "standby": "mdi:home-export-outline", + "economy": "mdi:leaf", + "building_protection": "mdi:sun-snowflake-variant" + } + } + } + } + }, "sensor": { "individual_address": { "default": "mdi:router-network" diff --git a/homeassistant/components/knx/strings.json b/homeassistant/components/knx/strings.json index d6e1e2f49f0..8d8692f6b7a 100644 --- a/homeassistant/components/knx/strings.json +++ b/homeassistant/components/knx/strings.json @@ -267,6 +267,22 @@ } }, "entity": { + "climate": { + "knx_climate": { + "state_attributes": { + "preset_mode": { + "name": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::name%]", + "state": { + "auto": "Auto", + "comfort": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::comfort%]", + "standby": "Standby", + "economy": "[%key:component::climate::entity_component::_::state_attributes::preset_mode::state::eco%]", + "building_protection": "Building protection" + } + } + } + } + }, "sensor": { "individual_address": { "name": "[%key:component::knx::config::step::routing::data::individual_address%]" diff --git a/tests/components/knx/test_climate.py b/tests/components/knx/test_climate.py index 9f198b48bd4..ec0498dc447 100644 --- a/tests/components/knx/test_climate.py +++ b/tests/components/knx/test_climate.py @@ -2,7 +2,7 @@ import pytest -from homeassistant.components.climate import PRESET_ECO, PRESET_SLEEP, HVACMode +from homeassistant.components.climate import HVACMode from homeassistant.components.knx.schema import ClimateSchema from homeassistant.const import CONF_NAME, STATE_IDLE from homeassistant.core import HomeAssistant @@ -331,7 +331,6 @@ async def test_climate_preset_mode( } } ) - events = async_capture_events(hass, "state_changed") # StateUpdater initialize state # StateUpdater semaphore allows 2 concurrent requests @@ -340,30 +339,28 @@ async def test_climate_preset_mode( await knx.receive_response("1/2/3", RAW_FLOAT_21_0) await knx.receive_response("1/2/5", RAW_FLOAT_22_0) await knx.assert_read("1/2/7") - await knx.receive_response("1/2/7", (0x01,)) - events.clear() + await knx.receive_response("1/2/7", (0x01,)) # comfort + knx.assert_state("climate.test", HVACMode.HEAT, preset_mode="comfort") # set preset mode await hass.services.async_call( "climate", "set_preset_mode", - {"entity_id": "climate.test", "preset_mode": PRESET_ECO}, + {"entity_id": "climate.test", "preset_mode": "building_protection"}, blocking=True, ) await knx.assert_write("1/2/6", (0x04,)) - assert len(events) == 1 - events.pop() + knx.assert_state("climate.test", HVACMode.HEAT, preset_mode="building_protection") # set preset mode await hass.services.async_call( "climate", "set_preset_mode", - {"entity_id": "climate.test", "preset_mode": PRESET_SLEEP}, + {"entity_id": "climate.test", "preset_mode": "economy"}, blocking=True, ) await knx.assert_write("1/2/6", (0x03,)) - assert len(events) == 1 - events.pop() + knx.assert_state("climate.test", HVACMode.HEAT, preset_mode="economy") assert len(knx.xknx.devices) == 2 assert len(knx.xknx.devices[0].device_updated_cbs) == 2