Rename KNX Climate preset modes according to specification (#123964)
* Rename KNX Climate preset modes according to specification * change icon for "standby"pull/124202/head
parent
9911aa4ede
commit
b042ebe4ff
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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%]"
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue