mirror of
https://github.com/home-assistant/core.git
synced 2026-02-15 07:36:16 +00:00
Support KNX climate entity configuration from UI (#154162)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -12,6 +12,7 @@ from xknx.devices import (
|
||||
)
|
||||
from xknx.devices.fan import FanSpeedMode
|
||||
from xknx.dpt.dpt_20 import HVACControllerMode, HVACOperationMode
|
||||
from xknx.remote_value.remote_value_setpoint_shift import SetpointShiftMode
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.climate import (
|
||||
@@ -34,13 +35,53 @@ from homeassistant.const import (
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.entity_platform import (
|
||||
AddConfigEntryEntitiesCallback,
|
||||
async_get_current_platform,
|
||||
)
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import CONTROLLER_MODES, CURRENT_HVAC_ACTIONS, KNX_MODULE_KEY
|
||||
from .entity import KnxYamlEntity
|
||||
from .const import (
|
||||
CONF_SYNC_STATE,
|
||||
CONTROLLER_MODES,
|
||||
CURRENT_HVAC_ACTIONS,
|
||||
DOMAIN,
|
||||
KNX_MODULE_KEY,
|
||||
ClimateConf,
|
||||
)
|
||||
from .entity import (
|
||||
KnxUiEntity,
|
||||
KnxUiEntityPlatformController,
|
||||
KnxYamlEntity,
|
||||
_KnxEntityBase,
|
||||
)
|
||||
from .knx_module import KNXModule
|
||||
from .schema import ClimateSchema
|
||||
from .storage.const import (
|
||||
CONF_ENTITY,
|
||||
CONF_GA_ACTIVE,
|
||||
CONF_GA_CONTROLLER_MODE,
|
||||
CONF_GA_CONTROLLER_STATUS,
|
||||
CONF_GA_FAN_SPEED,
|
||||
CONF_GA_FAN_SWING,
|
||||
CONF_GA_FAN_SWING_HORIZONTAL,
|
||||
CONF_GA_HEAT_COOL,
|
||||
CONF_GA_HUMIDITY_CURRENT,
|
||||
CONF_GA_ON_OFF,
|
||||
CONF_GA_OP_MODE_COMFORT,
|
||||
CONF_GA_OP_MODE_ECO,
|
||||
CONF_GA_OP_MODE_PROTECTION,
|
||||
CONF_GA_OP_MODE_STANDBY,
|
||||
CONF_GA_OPERATION_MODE,
|
||||
CONF_GA_SETPOINT_SHIFT,
|
||||
CONF_GA_TEMPERATURE_CURRENT,
|
||||
CONF_GA_TEMPERATURE_TARGET,
|
||||
CONF_GA_VALVE,
|
||||
CONF_IGNORE_AUTO_MODE,
|
||||
CONF_TARGET_TEMPERATURE,
|
||||
)
|
||||
from .storage.entity_store_schema import ConfClimateFanSpeedMode, ConfSetpointShiftMode
|
||||
from .storage.util import ConfigExtractor
|
||||
|
||||
ATTR_COMMAND_VALUE = "command_value"
|
||||
CONTROLLER_MODES_INV = {value: key for key, value in CONTROLLER_MODES.items()}
|
||||
@@ -53,12 +94,30 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up climate(s) for KNX platform."""
|
||||
knx_module = hass.data[KNX_MODULE_KEY]
|
||||
config: list[ConfigType] = knx_module.config_yaml[Platform.CLIMATE]
|
||||
|
||||
async_add_entities(
|
||||
KNXClimate(knx_module, entity_config) for entity_config in config
|
||||
platform = async_get_current_platform()
|
||||
knx_module.config_store.add_platform(
|
||||
platform=Platform.CLIMATE,
|
||||
controller=KnxUiEntityPlatformController(
|
||||
knx_module=knx_module,
|
||||
entity_platform=platform,
|
||||
entity_class=KnxUiClimate,
|
||||
),
|
||||
)
|
||||
|
||||
entities: list[KnxYamlEntity | KnxUiEntity] = []
|
||||
if yaml_platform_config := knx_module.config_yaml.get(Platform.CLIMATE):
|
||||
entities.extend(
|
||||
KnxYamlClimate(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.CLIMATE):
|
||||
entities.extend(
|
||||
KnxUiClimate(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
)
|
||||
if entities:
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
def _create_climate(xknx: XKNX, config: ConfigType) -> XknxClimate:
|
||||
"""Return a KNX Climate device to be used within XKNX."""
|
||||
@@ -99,8 +158,8 @@ def _create_climate(xknx: XKNX, config: ConfigType) -> XknxClimate:
|
||||
group_address_heat_cool_state=config.get(
|
||||
ClimateSchema.CONF_HEAT_COOL_STATE_ADDRESS
|
||||
),
|
||||
operation_modes=config.get(ClimateSchema.CONF_OPERATION_MODES),
|
||||
controller_modes=config.get(ClimateSchema.CONF_CONTROLLER_MODES),
|
||||
operation_modes=config.get(ClimateConf.OPERATION_MODES),
|
||||
controller_modes=config.get(ClimateConf.CONTROLLER_MODES),
|
||||
)
|
||||
|
||||
return XknxClimate(
|
||||
@@ -120,24 +179,24 @@ def _create_climate(xknx: XKNX, config: ConfigType) -> XknxClimate:
|
||||
ClimateSchema.CONF_SETPOINT_SHIFT_STATE_ADDRESS
|
||||
),
|
||||
setpoint_shift_mode=config.get(ClimateSchema.CONF_SETPOINT_SHIFT_MODE),
|
||||
setpoint_shift_max=config[ClimateSchema.CONF_SETPOINT_SHIFT_MAX],
|
||||
setpoint_shift_min=config[ClimateSchema.CONF_SETPOINT_SHIFT_MIN],
|
||||
temperature_step=config[ClimateSchema.CONF_TEMPERATURE_STEP],
|
||||
setpoint_shift_max=config[ClimateConf.SETPOINT_SHIFT_MAX],
|
||||
setpoint_shift_min=config[ClimateConf.SETPOINT_SHIFT_MIN],
|
||||
temperature_step=config[ClimateConf.TEMPERATURE_STEP],
|
||||
group_address_on_off=config.get(ClimateSchema.CONF_ON_OFF_ADDRESS),
|
||||
group_address_on_off_state=config.get(ClimateSchema.CONF_ON_OFF_STATE_ADDRESS),
|
||||
on_off_invert=config[ClimateSchema.CONF_ON_OFF_INVERT],
|
||||
on_off_invert=config[ClimateConf.ON_OFF_INVERT],
|
||||
group_address_active_state=config.get(ClimateSchema.CONF_ACTIVE_STATE_ADDRESS),
|
||||
group_address_command_value_state=config.get(
|
||||
ClimateSchema.CONF_COMMAND_VALUE_STATE_ADDRESS
|
||||
),
|
||||
min_temp=config.get(ClimateSchema.CONF_MIN_TEMP),
|
||||
max_temp=config.get(ClimateSchema.CONF_MAX_TEMP),
|
||||
min_temp=config.get(ClimateConf.MIN_TEMP),
|
||||
max_temp=config.get(ClimateConf.MAX_TEMP),
|
||||
mode=climate_mode,
|
||||
group_address_fan_speed=config.get(ClimateSchema.CONF_FAN_SPEED_ADDRESS),
|
||||
group_address_fan_speed_state=config.get(
|
||||
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS
|
||||
),
|
||||
fan_speed_mode=config[ClimateSchema.CONF_FAN_SPEED_MODE],
|
||||
fan_speed_mode=config[ClimateConf.FAN_SPEED_MODE],
|
||||
group_address_swing=config.get(ClimateSchema.CONF_SWING_ADDRESS),
|
||||
group_address_swing_state=config.get(ClimateSchema.CONF_SWING_STATE_ADDRESS),
|
||||
group_address_horizontal_swing=config.get(
|
||||
@@ -152,91 +211,195 @@ def _create_climate(xknx: XKNX, config: ConfigType) -> XknxClimate:
|
||||
)
|
||||
|
||||
|
||||
class KNXClimate(KnxYamlEntity, ClimateEntity):
|
||||
def _create_climate_ui(xknx: XKNX, conf: ConfigExtractor, name: str) -> XknxClimate:
|
||||
"""Return a KNX Climate device to be used within XKNX from UI config."""
|
||||
sync_state = conf.get(CONF_SYNC_STATE)
|
||||
op_modes: list[str | HVACOperationMode] = list(HVACOperationMode)
|
||||
if conf.get(CONF_IGNORE_AUTO_MODE):
|
||||
op_modes.remove(HVACOperationMode.AUTO)
|
||||
|
||||
climate_mode = XknxClimateMode(
|
||||
xknx,
|
||||
name=f"{name} Mode",
|
||||
group_address_operation_mode=conf.get_write(CONF_GA_OPERATION_MODE),
|
||||
group_address_operation_mode_state=conf.get_state_and_passive(
|
||||
CONF_GA_OPERATION_MODE
|
||||
),
|
||||
group_address_operation_mode_comfort=conf.get_write_and_passive(
|
||||
CONF_GA_OP_MODE_COMFORT
|
||||
),
|
||||
group_address_operation_mode_economy=conf.get_write_and_passive(
|
||||
CONF_GA_OP_MODE_ECO
|
||||
),
|
||||
group_address_operation_mode_protection=conf.get_write_and_passive(
|
||||
CONF_GA_OP_MODE_PROTECTION
|
||||
),
|
||||
group_address_operation_mode_standby=conf.get_write_and_passive(
|
||||
CONF_GA_OP_MODE_STANDBY
|
||||
),
|
||||
group_address_controller_status=conf.get_write(CONF_GA_CONTROLLER_STATUS),
|
||||
group_address_controller_status_state=conf.get_state_and_passive(
|
||||
CONF_GA_CONTROLLER_STATUS
|
||||
),
|
||||
group_address_controller_mode=conf.get_write(CONF_GA_CONTROLLER_MODE),
|
||||
group_address_controller_mode_state=conf.get_state_and_passive(
|
||||
CONF_GA_CONTROLLER_MODE
|
||||
),
|
||||
group_address_heat_cool=conf.get_write(CONF_GA_HEAT_COOL),
|
||||
group_address_heat_cool_state=conf.get_state_and_passive(CONF_GA_HEAT_COOL),
|
||||
sync_state=sync_state,
|
||||
operation_modes=op_modes,
|
||||
)
|
||||
|
||||
sps_mode = None
|
||||
if _sps_dpt := conf.get_dpt(CONF_TARGET_TEMPERATURE, CONF_GA_SETPOINT_SHIFT):
|
||||
sps_mode = (
|
||||
SetpointShiftMode.DPT6010
|
||||
if _sps_dpt == ConfSetpointShiftMode.COUNT
|
||||
else SetpointShiftMode.DPT9002
|
||||
)
|
||||
_fan_speed_dpt = conf.get_dpt(CONF_GA_FAN_SPEED)
|
||||
fan_speed_mode = (
|
||||
FanSpeedMode.STEP
|
||||
if _fan_speed_dpt == ConfClimateFanSpeedMode.STEPS
|
||||
else FanSpeedMode.PERCENT
|
||||
)
|
||||
|
||||
return XknxClimate(
|
||||
xknx,
|
||||
name=name,
|
||||
group_address_temperature=conf.get_state_and_passive(
|
||||
CONF_GA_TEMPERATURE_CURRENT
|
||||
),
|
||||
group_address_target_temperature=conf.get_write(
|
||||
CONF_TARGET_TEMPERATURE, CONF_GA_TEMPERATURE_TARGET
|
||||
),
|
||||
group_address_target_temperature_state=conf.get_state_and_passive(
|
||||
CONF_TARGET_TEMPERATURE, CONF_GA_TEMPERATURE_TARGET
|
||||
),
|
||||
group_address_setpoint_shift=conf.get_write(
|
||||
CONF_TARGET_TEMPERATURE, CONF_GA_SETPOINT_SHIFT
|
||||
),
|
||||
group_address_setpoint_shift_state=conf.get_state_and_passive(
|
||||
CONF_TARGET_TEMPERATURE, CONF_GA_SETPOINT_SHIFT
|
||||
),
|
||||
setpoint_shift_mode=sps_mode,
|
||||
setpoint_shift_max=conf.get(
|
||||
CONF_TARGET_TEMPERATURE, ClimateConf.SETPOINT_SHIFT_MAX, default=6
|
||||
),
|
||||
setpoint_shift_min=conf.get(
|
||||
CONF_TARGET_TEMPERATURE, ClimateConf.SETPOINT_SHIFT_MIN, default=-6
|
||||
),
|
||||
temperature_step=conf.get(
|
||||
CONF_TARGET_TEMPERATURE, ClimateConf.TEMPERATURE_STEP, default=0.1
|
||||
),
|
||||
group_address_on_off=conf.get_write(CONF_GA_ON_OFF),
|
||||
group_address_on_off_state=conf.get_state_and_passive(CONF_GA_ON_OFF),
|
||||
on_off_invert=conf.get(ClimateConf.ON_OFF_INVERT, default=False),
|
||||
group_address_active_state=conf.get_state_and_passive(CONF_GA_ACTIVE),
|
||||
group_address_command_value_state=conf.get_state_and_passive(CONF_GA_VALVE),
|
||||
sync_state=sync_state,
|
||||
min_temp=conf.get(ClimateConf.MIN_TEMP),
|
||||
max_temp=conf.get(ClimateConf.MAX_TEMP),
|
||||
mode=climate_mode,
|
||||
group_address_fan_speed=conf.get_write(CONF_GA_FAN_SPEED),
|
||||
group_address_fan_speed_state=conf.get_state_and_passive(CONF_GA_FAN_SPEED),
|
||||
fan_speed_mode=fan_speed_mode,
|
||||
group_address_humidity_state=conf.get_state_and_passive(
|
||||
CONF_GA_HUMIDITY_CURRENT
|
||||
),
|
||||
group_address_swing=conf.get_write(CONF_GA_FAN_SWING),
|
||||
group_address_swing_state=conf.get_state_and_passive(CONF_GA_FAN_SWING),
|
||||
group_address_horizontal_swing=conf.get_write(CONF_GA_FAN_SWING_HORIZONTAL),
|
||||
group_address_horizontal_swing_state=conf.get_state_and_passive(
|
||||
CONF_GA_FAN_SWING_HORIZONTAL
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class _KnxClimate(ClimateEntity, _KnxEntityBase):
|
||||
"""Representation of a KNX climate device."""
|
||||
|
||||
_device: XknxClimate
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_translation_key = "knx_climate"
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize of a KNX climate device."""
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=_create_climate(knx_module.xknx, config),
|
||||
)
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
default_hvac_mode: HVACMode
|
||||
_last_hvac_mode: HVACMode
|
||||
fan_zero_mode: str
|
||||
_fan_modes_percentages: list[int]
|
||||
|
||||
def _init_from_device_config(
|
||||
self,
|
||||
device: XknxClimate,
|
||||
default_hvac_mode: HVACMode,
|
||||
fan_max_step: int,
|
||||
fan_zero_mode: str,
|
||||
) -> None:
|
||||
"""Set attributes that depend on device config."""
|
||||
self.default_hvac_mode = default_hvac_mode
|
||||
# non-OFF HVAC mode to be used when turning on the device without on_off address
|
||||
self._last_hvac_mode = self.default_hvac_mode
|
||||
|
||||
self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
if self._device.supports_on_off:
|
||||
if device.supports_on_off:
|
||||
self._attr_supported_features |= (
|
||||
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
if (
|
||||
self._device.mode is not None
|
||||
and len(self._device.mode.controller_modes) >= 2
|
||||
and HVACControllerMode.OFF in self._device.mode.controller_modes
|
||||
device.mode is not None
|
||||
and len(device.mode.controller_modes) >= 2
|
||||
and HVACControllerMode.OFF in device.mode.controller_modes
|
||||
):
|
||||
self._attr_supported_features |= (
|
||||
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
|
||||
)
|
||||
|
||||
if (
|
||||
self._device.mode is not None
|
||||
and self._device.mode.operation_modes # empty list when not writable
|
||||
device.mode is not None
|
||||
and 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
|
||||
mode.name.lower() for mode in device.mode.operation_modes
|
||||
]
|
||||
|
||||
fan_max_step = config[ClimateSchema.CONF_FAN_MAX_STEP]
|
||||
self.fan_zero_mode = fan_zero_mode
|
||||
self._fan_modes_percentages = [
|
||||
int(100 * i / fan_max_step) for i in range(fan_max_step + 1)
|
||||
]
|
||||
self.fan_zero_mode: str = config[ClimateSchema.CONF_FAN_ZERO_MODE]
|
||||
|
||||
if self._device.fan_speed is not None and self._device.fan_speed.initialized:
|
||||
if device.fan_speed is not None and device.fan_speed.initialized:
|
||||
self._attr_supported_features |= ClimateEntityFeature.FAN_MODE
|
||||
|
||||
if fan_max_step == 3:
|
||||
self._attr_fan_modes = [
|
||||
self.fan_zero_mode,
|
||||
fan_zero_mode,
|
||||
FAN_LOW,
|
||||
FAN_MEDIUM,
|
||||
FAN_HIGH,
|
||||
]
|
||||
elif fan_max_step == 2:
|
||||
self._attr_fan_modes = [self.fan_zero_mode, FAN_LOW, FAN_HIGH]
|
||||
self._attr_fan_modes = [fan_zero_mode, FAN_LOW, FAN_HIGH]
|
||||
elif fan_max_step == 1:
|
||||
self._attr_fan_modes = [self.fan_zero_mode, FAN_ON]
|
||||
elif self._device.fan_speed_mode == FanSpeedMode.STEP:
|
||||
self._attr_fan_modes = [self.fan_zero_mode] + [
|
||||
self._attr_fan_modes = [fan_zero_mode, FAN_ON]
|
||||
elif device.fan_speed_mode == FanSpeedMode.STEP:
|
||||
self._attr_fan_modes = [fan_zero_mode] + [
|
||||
str(i) for i in range(1, fan_max_step + 1)
|
||||
]
|
||||
else:
|
||||
self._attr_fan_modes = [self.fan_zero_mode] + [
|
||||
self._attr_fan_modes = [fan_zero_mode] + [
|
||||
f"{percentage}%" for percentage in self._fan_modes_percentages[1:]
|
||||
]
|
||||
if self._device.swing.initialized:
|
||||
|
||||
if device.swing.initialized:
|
||||
self._attr_supported_features |= ClimateEntityFeature.SWING_MODE
|
||||
self._attr_swing_modes = [SWING_ON, SWING_OFF]
|
||||
|
||||
if self._device.horizontal_swing.initialized:
|
||||
if device.horizontal_swing.initialized:
|
||||
self._attr_supported_features |= ClimateEntityFeature.SWING_HORIZONTAL_MODE
|
||||
self._attr_swing_horizontal_modes = [SWING_ON, SWING_OFF]
|
||||
|
||||
self._attr_target_temperature_step = self._device.temperature_step
|
||||
self._attr_unique_id = (
|
||||
f"{self._device.temperature.group_address_state}_"
|
||||
f"{self._device.target_temperature.group_address_state}_"
|
||||
f"{self._device.target_temperature.group_address}_"
|
||||
f"{self._device._setpoint_shift.group_address}" # noqa: SLF001
|
||||
)
|
||||
self.default_hvac_mode: HVACMode = config[
|
||||
ClimateSchema.CONF_DEFAULT_CONTROLLER_MODE
|
||||
]
|
||||
# non-OFF HVAC mode to be used when turning on the device without on_off address
|
||||
self._last_hvac_mode: HVACMode = self.default_hvac_mode
|
||||
self._attr_target_temperature_step = device.temperature_step
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> float | None:
|
||||
@@ -475,3 +638,63 @@ class KNXClimate(KnxYamlEntity, ClimateEntity):
|
||||
if hvac_mode is not HVACMode.OFF:
|
||||
self._last_hvac_mode = hvac_mode
|
||||
super().after_update_callback(device)
|
||||
|
||||
|
||||
class KnxYamlClimate(_KnxClimate, KnxYamlEntity):
|
||||
"""Representation of a KNX climate device configured from YAML."""
|
||||
|
||||
_device: XknxClimate
|
||||
|
||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||
"""Initialize of a KNX climate device."""
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
device=_create_climate(knx_module.xknx, config),
|
||||
)
|
||||
default_hvac_mode: HVACMode = config[ClimateConf.DEFAULT_CONTROLLER_MODE]
|
||||
fan_max_step = config[ClimateConf.FAN_MAX_STEP]
|
||||
fan_zero_mode: str = config[ClimateConf.FAN_ZERO_MODE]
|
||||
self._init_from_device_config(
|
||||
device=self._device,
|
||||
default_hvac_mode=default_hvac_mode,
|
||||
fan_max_step=fan_max_step,
|
||||
fan_zero_mode=fan_zero_mode,
|
||||
)
|
||||
|
||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||
self._attr_unique_id = (
|
||||
f"{self._device.temperature.group_address_state}_"
|
||||
f"{self._device.target_temperature.group_address_state}_"
|
||||
f"{self._device.target_temperature.group_address}_"
|
||||
f"{self._device._setpoint_shift.group_address}" # noqa: SLF001
|
||||
)
|
||||
|
||||
|
||||
class KnxUiClimate(_KnxClimate, KnxUiEntity):
|
||||
"""Representation of a KNX climate device configured from the UI."""
|
||||
|
||||
_device: XknxClimate
|
||||
|
||||
def __init__(
|
||||
self, knx_module: KNXModule, unique_id: str, config: ConfigType
|
||||
) -> None:
|
||||
"""Initialize of a KNX climate device."""
|
||||
super().__init__(
|
||||
knx_module=knx_module,
|
||||
unique_id=unique_id,
|
||||
entity_config=config[CONF_ENTITY],
|
||||
)
|
||||
knx_conf = ConfigExtractor(config[DOMAIN])
|
||||
self._device = _create_climate_ui(
|
||||
knx_module.xknx, knx_conf, config[CONF_ENTITY][CONF_NAME]
|
||||
)
|
||||
|
||||
default_hvac_mode = HVACMode(knx_conf.get(ClimateConf.DEFAULT_CONTROLLER_MODE))
|
||||
fan_max_step = knx_conf.get(ClimateConf.FAN_MAX_STEP)
|
||||
fan_zero_mode = knx_conf.get(ClimateConf.FAN_ZERO_MODE)
|
||||
self._init_from_device_config(
|
||||
device=self._device,
|
||||
default_hvac_mode=default_hvac_mode,
|
||||
fan_max_step=fan_max_step,
|
||||
fan_zero_mode=fan_zero_mode,
|
||||
)
|
||||
|
||||
@@ -160,6 +160,7 @@ SUPPORTED_PLATFORMS_YAML: Final = {
|
||||
|
||||
SUPPORTED_PLATFORMS_UI: Final = {
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.CLIMATE,
|
||||
Platform.COVER,
|
||||
Platform.LIGHT,
|
||||
Platform.SWITCH,
|
||||
@@ -193,3 +194,23 @@ class CoverConf:
|
||||
INVERT_UPDOWN: Final = "invert_updown"
|
||||
INVERT_POSITION: Final = "invert_position"
|
||||
INVERT_ANGLE: Final = "invert_angle"
|
||||
|
||||
|
||||
class ClimateConf:
|
||||
"""Common config keys for climate."""
|
||||
|
||||
MIN_TEMP: Final = "min_temp"
|
||||
MAX_TEMP: Final = "max_temp"
|
||||
TEMPERATURE_STEP: Final = "temperature_step"
|
||||
SETPOINT_SHIFT_MAX: Final = "setpoint_shift_max"
|
||||
SETPOINT_SHIFT_MIN: Final = "setpoint_shift_min"
|
||||
|
||||
ON_OFF_INVERT: Final = "on_off_invert"
|
||||
|
||||
OPERATION_MODES: Final = "operation_modes"
|
||||
CONTROLLER_MODES: Final = "controller_modes"
|
||||
DEFAULT_CONTROLLER_MODE: Final = "default_controller_mode"
|
||||
|
||||
FAN_MAX_STEP: Final = "fan_max_step"
|
||||
FAN_SPEED_MODE: Final = "fan_speed_mode"
|
||||
FAN_ZERO_MODE: Final = "fan_zero_mode"
|
||||
|
||||
@@ -56,6 +56,7 @@ from .const import (
|
||||
CONF_STATE_ADDRESS,
|
||||
CONF_SYNC_STATE,
|
||||
KNX_ADDRESS,
|
||||
ClimateConf,
|
||||
ColorTempModes,
|
||||
CoverConf,
|
||||
FanZeroMode,
|
||||
@@ -306,10 +307,7 @@ class ClimateSchema(KNXPlatformSchema):
|
||||
CONF_SETPOINT_SHIFT_ADDRESS = "setpoint_shift_address"
|
||||
CONF_SETPOINT_SHIFT_STATE_ADDRESS = "setpoint_shift_state_address"
|
||||
CONF_SETPOINT_SHIFT_MODE = "setpoint_shift_mode"
|
||||
CONF_SETPOINT_SHIFT_MAX = "setpoint_shift_max"
|
||||
CONF_SETPOINT_SHIFT_MIN = "setpoint_shift_min"
|
||||
CONF_TEMPERATURE_ADDRESS = "temperature_address"
|
||||
CONF_TEMPERATURE_STEP = "temperature_step"
|
||||
CONF_TARGET_TEMPERATURE_ADDRESS = "target_temperature_address"
|
||||
CONF_TARGET_TEMPERATURE_STATE_ADDRESS = "target_temperature_state_address"
|
||||
CONF_OPERATION_MODE_ADDRESS = "operation_mode_address"
|
||||
@@ -327,19 +325,10 @@ class ClimateSchema(KNXPlatformSchema):
|
||||
CONF_OPERATION_MODE_NIGHT_ADDRESS = "operation_mode_night_address"
|
||||
CONF_OPERATION_MODE_COMFORT_ADDRESS = "operation_mode_comfort_address"
|
||||
CONF_OPERATION_MODE_STANDBY_ADDRESS = "operation_mode_standby_address"
|
||||
CONF_OPERATION_MODES = "operation_modes"
|
||||
CONF_CONTROLLER_MODES = "controller_modes"
|
||||
CONF_DEFAULT_CONTROLLER_MODE = "default_controller_mode"
|
||||
CONF_ON_OFF_ADDRESS = "on_off_address"
|
||||
CONF_ON_OFF_STATE_ADDRESS = "on_off_state_address"
|
||||
CONF_ON_OFF_INVERT = "on_off_invert"
|
||||
CONF_MIN_TEMP = "min_temp"
|
||||
CONF_MAX_TEMP = "max_temp"
|
||||
CONF_FAN_SPEED_ADDRESS = "fan_speed_address"
|
||||
CONF_FAN_SPEED_STATE_ADDRESS = "fan_speed_state_address"
|
||||
CONF_FAN_MAX_STEP = "fan_max_step"
|
||||
CONF_FAN_SPEED_MODE = "fan_speed_mode"
|
||||
CONF_FAN_ZERO_MODE = "fan_zero_mode"
|
||||
CONF_HUMIDITY_STATE_ADDRESS = "humidity_state_address"
|
||||
CONF_SWING_ADDRESS = "swing_address"
|
||||
CONF_SWING_STATE_ADDRESS = "swing_state_address"
|
||||
@@ -359,13 +348,13 @@ class ClimateSchema(KNXPlatformSchema):
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(
|
||||
CONF_SETPOINT_SHIFT_MAX, default=DEFAULT_SETPOINT_SHIFT_MAX
|
||||
ClimateConf.SETPOINT_SHIFT_MAX, default=DEFAULT_SETPOINT_SHIFT_MAX
|
||||
): vol.All(int, vol.Range(min=0, max=32)),
|
||||
vol.Optional(
|
||||
CONF_SETPOINT_SHIFT_MIN, default=DEFAULT_SETPOINT_SHIFT_MIN
|
||||
ClimateConf.SETPOINT_SHIFT_MIN, default=DEFAULT_SETPOINT_SHIFT_MIN
|
||||
): vol.All(int, vol.Range(min=-32, max=0)),
|
||||
vol.Optional(
|
||||
CONF_TEMPERATURE_STEP, default=DEFAULT_TEMPERATURE_STEP
|
||||
ClimateConf.TEMPERATURE_STEP, default=DEFAULT_TEMPERATURE_STEP
|
||||
): vol.All(float, vol.Range(min=0, max=2)),
|
||||
vol.Required(CONF_TEMPERATURE_ADDRESS): ga_list_validator,
|
||||
vol.Required(CONF_TARGET_TEMPERATURE_STATE_ADDRESS): ga_list_validator,
|
||||
@@ -408,29 +397,29 @@ class ClimateSchema(KNXPlatformSchema):
|
||||
vol.Optional(CONF_ON_OFF_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_ON_OFF_STATE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(
|
||||
CONF_ON_OFF_INVERT, default=DEFAULT_ON_OFF_INVERT
|
||||
ClimateConf.ON_OFF_INVERT, default=DEFAULT_ON_OFF_INVERT
|
||||
): cv.boolean,
|
||||
vol.Optional(CONF_OPERATION_MODES): vol.All(
|
||||
vol.Optional(ClimateConf.OPERATION_MODES): vol.All(
|
||||
cv.ensure_list,
|
||||
[backwards_compatible_xknx_climate_enum_member(HVACOperationMode)],
|
||||
),
|
||||
vol.Optional(CONF_CONTROLLER_MODES): vol.All(
|
||||
vol.Optional(ClimateConf.CONTROLLER_MODES): vol.All(
|
||||
cv.ensure_list,
|
||||
[backwards_compatible_xknx_climate_enum_member(HVACControllerMode)],
|
||||
),
|
||||
vol.Optional(
|
||||
CONF_DEFAULT_CONTROLLER_MODE, default=HVACMode.HEAT
|
||||
ClimateConf.DEFAULT_CONTROLLER_MODE, default=HVACMode.HEAT
|
||||
): vol.Coerce(HVACMode),
|
||||
vol.Optional(CONF_MIN_TEMP): vol.Coerce(float),
|
||||
vol.Optional(CONF_MAX_TEMP): vol.Coerce(float),
|
||||
vol.Optional(ClimateConf.MIN_TEMP): vol.Coerce(float),
|
||||
vol.Optional(ClimateConf.MAX_TEMP): vol.Coerce(float),
|
||||
vol.Optional(CONF_ENTITY_CATEGORY): ENTITY_CATEGORIES_SCHEMA,
|
||||
vol.Optional(CONF_FAN_SPEED_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_FAN_SPEED_STATE_ADDRESS): ga_list_validator,
|
||||
vol.Optional(CONF_FAN_MAX_STEP, default=3): cv.byte,
|
||||
vol.Optional(ClimateConf.FAN_MAX_STEP, default=3): cv.byte,
|
||||
vol.Optional(
|
||||
CONF_FAN_SPEED_MODE, default=DEFAULT_FAN_SPEED_MODE
|
||||
ClimateConf.FAN_SPEED_MODE, default=DEFAULT_FAN_SPEED_MODE
|
||||
): vol.All(vol.Upper, cv.enum(FanSpeedMode)),
|
||||
vol.Optional(CONF_FAN_ZERO_MODE, default=FAN_OFF): vol.Coerce(
|
||||
vol.Optional(ClimateConf.FAN_ZERO_MODE, default=FAN_OFF): vol.Coerce(
|
||||
FanZeroMode
|
||||
),
|
||||
vol.Optional(CONF_SWING_ADDRESS): ga_list_validator,
|
||||
|
||||
@@ -14,6 +14,28 @@ CONF_DPT: Final = "dpt"
|
||||
CONF_GA_SENSOR: Final = "ga_sensor"
|
||||
CONF_GA_SWITCH: Final = "ga_switch"
|
||||
|
||||
# Climate
|
||||
CONF_GA_TEMPERATURE_CURRENT: Final = "ga_temperature_current"
|
||||
CONF_GA_HUMIDITY_CURRENT: Final = "ga_humidity_current"
|
||||
CONF_TARGET_TEMPERATURE: Final = "target_temperature"
|
||||
CONF_GA_TEMPERATURE_TARGET: Final = "ga_temperature_target"
|
||||
CONF_GA_SETPOINT_SHIFT: Final = "ga_setpoint_shift"
|
||||
CONF_GA_ACTIVE: Final = "ga_active"
|
||||
CONF_GA_VALVE: Final = "ga_valve"
|
||||
CONF_GA_OPERATION_MODE: Final = "ga_operation_mode"
|
||||
CONF_IGNORE_AUTO_MODE: Final = "ignore_auto_mode"
|
||||
CONF_GA_OP_MODE_COMFORT: Final = "ga_operation_mode_comfort"
|
||||
CONF_GA_OP_MODE_ECO: Final = "ga_operation_mode_economy"
|
||||
CONF_GA_OP_MODE_STANDBY: Final = "ga_operation_mode_standby"
|
||||
CONF_GA_OP_MODE_PROTECTION: Final = "ga_operation_mode_protection"
|
||||
CONF_GA_HEAT_COOL: Final = "ga_heat_cool"
|
||||
CONF_GA_ON_OFF: Final = "ga_on_off"
|
||||
CONF_GA_CONTROLLER_MODE: Final = "ga_controller_mode"
|
||||
CONF_GA_CONTROLLER_STATUS: Final = "ga_controller_status"
|
||||
CONF_GA_FAN_SPEED: Final = "ga_fan_speed"
|
||||
CONF_GA_FAN_SWING: Final = "ga_fan_swing"
|
||||
CONF_GA_FAN_SWING_HORIZONTAL: Final = "ga_fan_swing_horizontal"
|
||||
|
||||
# Cover
|
||||
CONF_GA_UP_DOWN: Final = "ga_up_down"
|
||||
CONF_GA_STOP: Final = "ga_stop"
|
||||
|
||||
@@ -4,6 +4,7 @@ from enum import StrEnum, unique
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.climate import HVACMode
|
||||
from homeassistant.const import (
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ENTITY_ID,
|
||||
@@ -24,8 +25,10 @@ from ..const import (
|
||||
CONF_SYNC_STATE,
|
||||
DOMAIN,
|
||||
SUPPORTED_PLATFORMS_UI,
|
||||
ClimateConf,
|
||||
ColorTempModes,
|
||||
CoverConf,
|
||||
FanZeroMode,
|
||||
)
|
||||
from .const import (
|
||||
CONF_COLOR,
|
||||
@@ -34,27 +37,47 @@ from .const import (
|
||||
CONF_DATA,
|
||||
CONF_DEVICE_INFO,
|
||||
CONF_ENTITY,
|
||||
CONF_GA_ACTIVE,
|
||||
CONF_GA_ANGLE,
|
||||
CONF_GA_BLUE_BRIGHTNESS,
|
||||
CONF_GA_BLUE_SWITCH,
|
||||
CONF_GA_BRIGHTNESS,
|
||||
CONF_GA_COLOR,
|
||||
CONF_GA_COLOR_TEMP,
|
||||
CONF_GA_CONTROLLER_MODE,
|
||||
CONF_GA_CONTROLLER_STATUS,
|
||||
CONF_GA_FAN_SPEED,
|
||||
CONF_GA_FAN_SWING,
|
||||
CONF_GA_FAN_SWING_HORIZONTAL,
|
||||
CONF_GA_GREEN_BRIGHTNESS,
|
||||
CONF_GA_GREEN_SWITCH,
|
||||
CONF_GA_HEAT_COOL,
|
||||
CONF_GA_HUE,
|
||||
CONF_GA_HUMIDITY_CURRENT,
|
||||
CONF_GA_ON_OFF,
|
||||
CONF_GA_OP_MODE_COMFORT,
|
||||
CONF_GA_OP_MODE_ECO,
|
||||
CONF_GA_OP_MODE_PROTECTION,
|
||||
CONF_GA_OP_MODE_STANDBY,
|
||||
CONF_GA_OPERATION_MODE,
|
||||
CONF_GA_POSITION_SET,
|
||||
CONF_GA_POSITION_STATE,
|
||||
CONF_GA_RED_BRIGHTNESS,
|
||||
CONF_GA_RED_SWITCH,
|
||||
CONF_GA_SATURATION,
|
||||
CONF_GA_SENSOR,
|
||||
CONF_GA_SETPOINT_SHIFT,
|
||||
CONF_GA_STEP,
|
||||
CONF_GA_STOP,
|
||||
CONF_GA_SWITCH,
|
||||
CONF_GA_TEMPERATURE_CURRENT,
|
||||
CONF_GA_TEMPERATURE_TARGET,
|
||||
CONF_GA_UP_DOWN,
|
||||
CONF_GA_VALVE,
|
||||
CONF_GA_WHITE_BRIGHTNESS,
|
||||
CONF_GA_WHITE_SWITCH,
|
||||
CONF_IGNORE_AUTO_MODE,
|
||||
CONF_TARGET_TEMPERATURE,
|
||||
)
|
||||
from .knx_selector import (
|
||||
AllSerializeFirst,
|
||||
@@ -313,8 +336,151 @@ SWITCH_KNX_SCHEMA = vol.Schema(
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@unique
|
||||
class ConfSetpointShiftMode(StrEnum):
|
||||
"""Enum for setpoint shift mode."""
|
||||
|
||||
COUNT = "6.010"
|
||||
FLOAT = "9.002"
|
||||
|
||||
|
||||
@unique
|
||||
class ConfClimateFanSpeedMode(StrEnum):
|
||||
"""Enum for climate fan speed mode."""
|
||||
|
||||
PERCENTAGE = "5.001"
|
||||
STEPS = "5.010"
|
||||
|
||||
|
||||
CLIMATE_KNX_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_GA_TEMPERATURE_CURRENT): GASelector(
|
||||
write=False, state_required=True, valid_dpt="9.001"
|
||||
),
|
||||
vol.Optional(CONF_GA_HUMIDITY_CURRENT): GASelector(
|
||||
write=False, valid_dpt="9.002"
|
||||
),
|
||||
vol.Required(CONF_TARGET_TEMPERATURE): GroupSelect(
|
||||
GroupSelectOption(
|
||||
translation_key="group_direct_temp",
|
||||
schema={
|
||||
vol.Required(CONF_GA_TEMPERATURE_TARGET): GASelector(
|
||||
write_required=True, valid_dpt="9.001"
|
||||
),
|
||||
vol.Required(
|
||||
ClimateConf.MIN_TEMP, default=7
|
||||
): selector.NumberSelector(
|
||||
selector.NumberSelectorConfig(
|
||||
min=-20, max=80, step=1, unit_of_measurement="°C"
|
||||
)
|
||||
),
|
||||
vol.Required(
|
||||
ClimateConf.MAX_TEMP, default=28
|
||||
): selector.NumberSelector(
|
||||
selector.NumberSelectorConfig(
|
||||
min=0, max=100, step=1, unit_of_measurement="°C"
|
||||
)
|
||||
),
|
||||
vol.Required(
|
||||
ClimateConf.TEMPERATURE_STEP, default=0.1
|
||||
): selector.NumberSelector(
|
||||
selector.NumberSelectorConfig(
|
||||
min=0.1, max=2, step=0.1, unit_of_measurement="K"
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
GroupSelectOption(
|
||||
translation_key="group_setpoint_shift",
|
||||
schema={
|
||||
vol.Required(CONF_GA_TEMPERATURE_TARGET): GASelector(
|
||||
write=False, state_required=True, valid_dpt="9.001"
|
||||
),
|
||||
vol.Required(CONF_GA_SETPOINT_SHIFT): GASelector(
|
||||
write_required=True,
|
||||
state_required=True,
|
||||
dpt=ConfSetpointShiftMode,
|
||||
),
|
||||
vol.Required(
|
||||
ClimateConf.SETPOINT_SHIFT_MIN, default=-6
|
||||
): selector.NumberSelector(
|
||||
selector.NumberSelectorConfig(
|
||||
min=-32, max=0, step=1, unit_of_measurement="K"
|
||||
)
|
||||
),
|
||||
vol.Required(
|
||||
ClimateConf.SETPOINT_SHIFT_MAX, default=6
|
||||
): selector.NumberSelector(
|
||||
selector.NumberSelectorConfig(
|
||||
min=0, max=32, step=1, unit_of_measurement="K"
|
||||
)
|
||||
),
|
||||
vol.Required(
|
||||
ClimateConf.TEMPERATURE_STEP, default=0.1
|
||||
): selector.NumberSelector(
|
||||
selector.NumberSelectorConfig(
|
||||
min=0.1, max=2, step=0.1, unit_of_measurement="K"
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
collapsible=False,
|
||||
),
|
||||
"section_activity": KNXSectionFlat(collapsible=True),
|
||||
vol.Optional(CONF_GA_ACTIVE): GASelector(write=False, valid_dpt="1"),
|
||||
vol.Optional(CONF_GA_VALVE): GASelector(write=False, valid_dpt="5.001"),
|
||||
"section_operation_mode": KNXSectionFlat(collapsible=True),
|
||||
vol.Optional(CONF_GA_OPERATION_MODE): GASelector(valid_dpt="20.102"),
|
||||
vol.Optional(CONF_IGNORE_AUTO_MODE): selector.BooleanSelector(),
|
||||
"section_operation_mode_individual": KNXSectionFlat(collapsible=True),
|
||||
vol.Optional(CONF_GA_OP_MODE_COMFORT): GASelector(state=False, valid_dpt="1"),
|
||||
vol.Optional(CONF_GA_OP_MODE_ECO): GASelector(state=False, valid_dpt="1"),
|
||||
vol.Optional(CONF_GA_OP_MODE_STANDBY): GASelector(state=False, valid_dpt="1"),
|
||||
vol.Optional(CONF_GA_OP_MODE_PROTECTION): GASelector(
|
||||
state=False, valid_dpt="1"
|
||||
),
|
||||
"section_heat_cool": KNXSectionFlat(collapsible=True),
|
||||
vol.Optional(CONF_GA_HEAT_COOL): GASelector(valid_dpt="1.100"),
|
||||
"section_on_off": KNXSectionFlat(collapsible=True),
|
||||
vol.Optional(CONF_GA_ON_OFF): GASelector(valid_dpt="1"),
|
||||
vol.Optional(ClimateConf.ON_OFF_INVERT): selector.BooleanSelector(),
|
||||
"section_controller_mode": KNXSectionFlat(collapsible=True),
|
||||
vol.Optional(CONF_GA_CONTROLLER_MODE): GASelector(valid_dpt="20.105"),
|
||||
vol.Optional(CONF_GA_CONTROLLER_STATUS): GASelector(write=False),
|
||||
vol.Required(
|
||||
ClimateConf.DEFAULT_CONTROLLER_MODE, default=HVACMode.HEAT
|
||||
): selector.SelectSelector(
|
||||
selector.SelectSelectorConfig(
|
||||
options=list(HVACMode),
|
||||
translation_key="component.climate.selector.hvac_mode",
|
||||
)
|
||||
),
|
||||
"section_fan": KNXSectionFlat(collapsible=True),
|
||||
vol.Optional(CONF_GA_FAN_SPEED): GASelector(dpt=ConfClimateFanSpeedMode),
|
||||
vol.Required(ClimateConf.FAN_MAX_STEP, default=3): AllSerializeFirst(
|
||||
selector.NumberSelector(
|
||||
selector.NumberSelectorConfig(min=1, max=100, step=1)
|
||||
),
|
||||
vol.Coerce(int),
|
||||
),
|
||||
vol.Required(
|
||||
ClimateConf.FAN_ZERO_MODE, default=FanZeroMode.OFF
|
||||
): selector.SelectSelector(
|
||||
selector.SelectSelectorConfig(
|
||||
options=list(FanZeroMode),
|
||||
translation_key="component.knx.config_panel.entities.create.climate.knx.fan_zero_mode",
|
||||
)
|
||||
),
|
||||
vol.Optional(CONF_GA_FAN_SWING): GASelector(valid_dpt="1"),
|
||||
vol.Optional(CONF_GA_FAN_SWING_HORIZONTAL): GASelector(valid_dpt="1"),
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): SyncStateSelector(),
|
||||
},
|
||||
)
|
||||
|
||||
KNX_SCHEMA_FOR_PLATFORM = {
|
||||
Platform.BINARY_SENSOR: BINARY_SENSOR_KNX_SCHEMA,
|
||||
Platform.CLIMATE: CLIMATE_KNX_SCHEMA,
|
||||
Platform.COVER: COVER_KNX_SCHEMA,
|
||||
Platform.LIGHT: LIGHT_KNX_SCHEMA,
|
||||
Platform.SWITCH: SWITCH_KNX_SCHEMA,
|
||||
|
||||
@@ -412,6 +412,164 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"climate": {
|
||||
"description": "The KNX climate platform is used as an interface to heating actuators, HVAC gateways, etc.",
|
||||
"knx": {
|
||||
"ga_temperature_current": {
|
||||
"label": "Current temperature"
|
||||
},
|
||||
"ga_humidity_current": {
|
||||
"label": "Current humidity"
|
||||
},
|
||||
"target_temperature": {
|
||||
"title": "Target temperature",
|
||||
"description": "Set the target temperature.",
|
||||
"options": {
|
||||
"group_direct_temp": {
|
||||
"label": "Absolute setpoint",
|
||||
"description": "Set the target temperature by an absolute value."
|
||||
},
|
||||
"group_setpoint_shift": {
|
||||
"label": "Setpoint shift",
|
||||
"description": "Shift the target temperature from a base setpoint."
|
||||
}
|
||||
},
|
||||
"ga_temperature_target": {
|
||||
"label": "Target temperature",
|
||||
"description": "Current absolute target temperature."
|
||||
},
|
||||
"min_temp": {
|
||||
"label": "Minimum temperature",
|
||||
"description": "Minimum temperature that can be set."
|
||||
},
|
||||
"max_temp": {
|
||||
"label": "Maximum temperature",
|
||||
"description": "Maximum temperature that can be set."
|
||||
},
|
||||
"temperature_step": {
|
||||
"label": "Temperature step",
|
||||
"description": "Smallest step size to change the temperature. For setpoint shift configureations this sets the scale factor of the shift value."
|
||||
},
|
||||
"ga_setpoint_shift": {
|
||||
"label": "Setpoint shift",
|
||||
"description": "Target temperature deviation from a base setpoint."
|
||||
},
|
||||
"setpoint_shift_min": {
|
||||
"label": "Minimum setpoint shift",
|
||||
"description": "Lowest allowed deviation from the base setpoint."
|
||||
},
|
||||
"setpoint_shift_max": {
|
||||
"label": "Maximum setpoint shift",
|
||||
"description": "Highest allowed deviation from the base setpoint."
|
||||
}
|
||||
},
|
||||
"section_activity": {
|
||||
"title": "Activity",
|
||||
"description": "Determine if the device is active or idle."
|
||||
},
|
||||
"ga_active": {
|
||||
"label": "Active",
|
||||
"description": "Binary value indicating if the device is active or idle. If configured, this takes precedence over valve position."
|
||||
},
|
||||
"ga_valve": {
|
||||
"label": "Valve position",
|
||||
"description": "Current control value / valve position in percent. `0` sets the climate entity to idle."
|
||||
},
|
||||
"section_operation_mode": {
|
||||
"title": "Operation mode",
|
||||
"description": "Set the preset mode of the device."
|
||||
},
|
||||
"ga_operation_mode": {
|
||||
"label": "Operation mode",
|
||||
"description": "Current operation mode."
|
||||
},
|
||||
"ignore_auto_mode": {
|
||||
"label": "Ignore auto mode",
|
||||
"description": "Enable when your controller doesn't support `auto` mode. It will be ignored by the integration then."
|
||||
},
|
||||
"section_operation_mode_individual": {
|
||||
"title": "Individual operation modes",
|
||||
"description": "Set the preset mode of the device using individual group addresses."
|
||||
},
|
||||
"ga_operation_mode_comfort": {
|
||||
"label": "Comfort mode"
|
||||
},
|
||||
"ga_operation_mode_economy": {
|
||||
"label": "Economy mode"
|
||||
},
|
||||
"ga_operation_mode_standby": {
|
||||
"label": "Standby mode"
|
||||
},
|
||||
"ga_operation_mode_protection": {
|
||||
"label": "Building protection mode"
|
||||
},
|
||||
"section_heat_cool": {
|
||||
"title": "Heating/Cooling",
|
||||
"description": "Set whether the device is in heating or cooling mode."
|
||||
},
|
||||
"ga_heat_cool": {
|
||||
"label": "Heating/Cooling"
|
||||
},
|
||||
"section_on_off": {
|
||||
"title": "On/Off",
|
||||
"description": "Turn the device on or off."
|
||||
},
|
||||
"ga_on_off": {
|
||||
"label": "On/Off"
|
||||
},
|
||||
"on_off_invert": {
|
||||
"label": "[%key:component::knx::config_panel::entities::create::binary_sensor::knx::invert::label%]",
|
||||
"description": "[%key:component::knx::config_panel::entities::create::binary_sensor::knx::invert::description%]"
|
||||
},
|
||||
"section_controller_mode": {
|
||||
"title": "Controller mode",
|
||||
"description": "Set the mode of the climate device."
|
||||
},
|
||||
"ga_controller_mode": {
|
||||
"label": "Controller mode"
|
||||
},
|
||||
"ga_controller_status": {
|
||||
"label": "Controller status",
|
||||
"description": "HVAC controller mode and preset status. Eberle Status octet (KNX AN 097/07 rev 3) non-standardized DPT."
|
||||
},
|
||||
"default_controller_mode": {
|
||||
"label": "Default mode",
|
||||
"description": "Climate mode to be set on initialization."
|
||||
},
|
||||
"section_fan": {
|
||||
"title": "Fan",
|
||||
"description": "Configuration for fan control (AC units)."
|
||||
},
|
||||
"ga_fan_speed": {
|
||||
"label": "Fan speed",
|
||||
"description": "Set the current fan speed.",
|
||||
"options": {
|
||||
"5_001": "Percent",
|
||||
"5_010": "Steps"
|
||||
}
|
||||
},
|
||||
"fan_max_step": {
|
||||
"label": "Fan steps",
|
||||
"description": "The maximum amount of steps for the fan."
|
||||
},
|
||||
"fan_zero_mode": {
|
||||
"label": "Zero fan speed mode",
|
||||
"description": "Set the mode that represents fan speed `0`.",
|
||||
"options": {
|
||||
"off": "[%key:common::state::off%]",
|
||||
"auto": "[%key:common::state::auto%]"
|
||||
}
|
||||
},
|
||||
"ga_fan_swing": {
|
||||
"label": "Fan swing",
|
||||
"description": "Toggle (vertical) fan swing mode. Use this if only one direction is supported."
|
||||
},
|
||||
"ga_fan_swing_horizontal": {
|
||||
"label": "Fan horizontal swing",
|
||||
"description": "Toggle horizontal fan swing mode."
|
||||
}
|
||||
}
|
||||
},
|
||||
"cover": {
|
||||
"description": "The KNX cover platform is used as an interface to shutter actuators.",
|
||||
"knx": {
|
||||
|
||||
136
tests/components/knx/fixtures/config_store_climate.json
Normal file
136
tests/components/knx/fixtures/config_store_climate.json
Normal file
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"version": 2,
|
||||
"minor_version": 2,
|
||||
"key": "knx/config_store.json",
|
||||
"data": {
|
||||
"entities": {
|
||||
"climate": {
|
||||
"knx_es_01K76NGZRMJA74CBRQF9KXNPE8": {
|
||||
"entity": {
|
||||
"name": "direct_indi-op_heat-cool",
|
||||
"device_info": null,
|
||||
"entity_category": null
|
||||
},
|
||||
"knx": {
|
||||
"ga_temperature_current": {
|
||||
"state": "0/0/1",
|
||||
"passive": []
|
||||
},
|
||||
"default_controller_mode": "cool",
|
||||
"fan_max_step": 4,
|
||||
"fan_zero_mode": "auto",
|
||||
"sync_state": true,
|
||||
"target_temperature": {
|
||||
"ga_temperature_target": {
|
||||
"write": "0/1/1",
|
||||
"state": "0/1/2",
|
||||
"passive": []
|
||||
},
|
||||
"min_temp": 10.0,
|
||||
"max_temp": 24.0,
|
||||
"temperature_step": 0.1
|
||||
},
|
||||
"ga_operation_mode_comfort": {
|
||||
"write": "0/2/1",
|
||||
"passive": []
|
||||
},
|
||||
"ga_operation_mode_protection": {
|
||||
"write": "0/2/4",
|
||||
"passive": []
|
||||
},
|
||||
"ga_operation_mode_economy": {
|
||||
"write": "0/2/2",
|
||||
"passive": []
|
||||
},
|
||||
"ga_operation_mode_standby": {
|
||||
"write": "0/2/3",
|
||||
"passive": []
|
||||
},
|
||||
"ga_heat_cool": {
|
||||
"write": "0/3/1",
|
||||
"state": "0/3/2",
|
||||
"passive": []
|
||||
},
|
||||
"ga_on_off": {
|
||||
"write": "0/4/1",
|
||||
"state": "0/4/2",
|
||||
"passive": []
|
||||
},
|
||||
"on_off_invert": true
|
||||
}
|
||||
},
|
||||
"knx_es_01K76NGZRMJA74CBRQF9KXNPE9": {
|
||||
"entity": {
|
||||
"name": "sps_op-mode_contr-mode",
|
||||
"device_info": null,
|
||||
"entity_category": null
|
||||
},
|
||||
"knx": {
|
||||
"ga_temperature_current": {
|
||||
"state": "1/0/1",
|
||||
"passive": []
|
||||
},
|
||||
"ga_humidity_current": {
|
||||
"state": "1/0/2",
|
||||
"passive": []
|
||||
},
|
||||
"target_temperature": {
|
||||
"ga_temperature_target": {
|
||||
"state": "1/1/0",
|
||||
"passive": []
|
||||
},
|
||||
"ga_setpoint_shift": {
|
||||
"write": "1/1/1",
|
||||
"dpt": "9.002",
|
||||
"state": "1/1/2",
|
||||
"passive": []
|
||||
},
|
||||
"setpoint_shift_min": -8.0,
|
||||
"setpoint_shift_max": 8.0,
|
||||
"temperature_step": 0.5
|
||||
},
|
||||
"ga_active": {
|
||||
"state": "1/1/3",
|
||||
"passive": []
|
||||
},
|
||||
"ga_valve": {
|
||||
"state": "1/1/4",
|
||||
"passive": []
|
||||
},
|
||||
"ignore_auto_mode": true,
|
||||
"ga_operation_mode": {
|
||||
"write": "1/2/1",
|
||||
"state": "1/2/2",
|
||||
"passive": []
|
||||
},
|
||||
"ga_controller_mode": {
|
||||
"write": "1/3/1",
|
||||
"state": "1/3/2",
|
||||
"passive": []
|
||||
},
|
||||
"default_controller_mode": "heat",
|
||||
"ga_fan_speed": {
|
||||
"write": "1/4/1",
|
||||
"dpt": "5.001",
|
||||
"state": "1/4/2",
|
||||
"passive": []
|
||||
},
|
||||
"fan_max_step": 4,
|
||||
"fan_zero_mode": "auto",
|
||||
"ga_fan_swing": {
|
||||
"write": "1/4/3",
|
||||
"state": "1/4/4",
|
||||
"passive": []
|
||||
},
|
||||
"ga_fan_swing_horizontal": {
|
||||
"write": "1/4/5",
|
||||
"state": "1/4/6",
|
||||
"passive": []
|
||||
},
|
||||
"sync_state": "init"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,643 @@
|
||||
'type': 'result',
|
||||
})
|
||||
# ---
|
||||
# name: test_knx_get_schema[climate]
|
||||
dict({
|
||||
'id': 1,
|
||||
'result': list([
|
||||
dict({
|
||||
'name': 'ga_temperature_current',
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': True,
|
||||
}),
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 9,
|
||||
'sub': 1,
|
||||
}),
|
||||
]),
|
||||
'write': False,
|
||||
}),
|
||||
'required': True,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_humidity_current',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': False,
|
||||
}),
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 9,
|
||||
'sub': 2,
|
||||
}),
|
||||
]),
|
||||
'write': False,
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'collapsible': False,
|
||||
'name': 'target_temperature',
|
||||
'required': True,
|
||||
'schema': list([
|
||||
dict({
|
||||
'schema': list([
|
||||
dict({
|
||||
'name': 'ga_temperature_target',
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': False,
|
||||
}),
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 9,
|
||||
'sub': 1,
|
||||
}),
|
||||
]),
|
||||
'write': dict({
|
||||
'required': True,
|
||||
}),
|
||||
}),
|
||||
'required': True,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'default': 7,
|
||||
'name': 'min_temp',
|
||||
'required': True,
|
||||
'selector': dict({
|
||||
'number': dict({
|
||||
'max': 80.0,
|
||||
'min': -20.0,
|
||||
'mode': 'slider',
|
||||
'step': 1.0,
|
||||
'unit_of_measurement': '°C',
|
||||
}),
|
||||
}),
|
||||
'type': 'ha_selector',
|
||||
}),
|
||||
dict({
|
||||
'default': 28,
|
||||
'name': 'max_temp',
|
||||
'required': True,
|
||||
'selector': dict({
|
||||
'number': dict({
|
||||
'max': 100.0,
|
||||
'min': 0.0,
|
||||
'mode': 'slider',
|
||||
'step': 1.0,
|
||||
'unit_of_measurement': '°C',
|
||||
}),
|
||||
}),
|
||||
'type': 'ha_selector',
|
||||
}),
|
||||
dict({
|
||||
'default': 0.1,
|
||||
'name': 'temperature_step',
|
||||
'required': True,
|
||||
'selector': dict({
|
||||
'number': dict({
|
||||
'max': 2.0,
|
||||
'min': 0.1,
|
||||
'mode': 'slider',
|
||||
'step': 0.1,
|
||||
'unit_of_measurement': 'K',
|
||||
}),
|
||||
}),
|
||||
'type': 'ha_selector',
|
||||
}),
|
||||
]),
|
||||
'translation_key': 'group_direct_temp',
|
||||
'type': 'knx_group_select_option',
|
||||
}),
|
||||
dict({
|
||||
'schema': list([
|
||||
dict({
|
||||
'name': 'ga_temperature_target',
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': True,
|
||||
}),
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 9,
|
||||
'sub': 1,
|
||||
}),
|
||||
]),
|
||||
'write': False,
|
||||
}),
|
||||
'required': True,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_setpoint_shift',
|
||||
'options': dict({
|
||||
'dptSelect': list([
|
||||
dict({
|
||||
'dpt': dict({
|
||||
'main': 6,
|
||||
'sub': 10,
|
||||
}),
|
||||
'translation_key': '6_010',
|
||||
'value': '6.010',
|
||||
}),
|
||||
dict({
|
||||
'dpt': dict({
|
||||
'main': 9,
|
||||
'sub': 2,
|
||||
}),
|
||||
'translation_key': '9_002',
|
||||
'value': '9.002',
|
||||
}),
|
||||
]),
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': True,
|
||||
}),
|
||||
'write': dict({
|
||||
'required': True,
|
||||
}),
|
||||
}),
|
||||
'required': True,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'default': -6,
|
||||
'name': 'setpoint_shift_min',
|
||||
'required': True,
|
||||
'selector': dict({
|
||||
'number': dict({
|
||||
'max': 0.0,
|
||||
'min': -32.0,
|
||||
'mode': 'slider',
|
||||
'step': 1.0,
|
||||
'unit_of_measurement': 'K',
|
||||
}),
|
||||
}),
|
||||
'type': 'ha_selector',
|
||||
}),
|
||||
dict({
|
||||
'default': 6,
|
||||
'name': 'setpoint_shift_max',
|
||||
'required': True,
|
||||
'selector': dict({
|
||||
'number': dict({
|
||||
'max': 32.0,
|
||||
'min': 0.0,
|
||||
'mode': 'slider',
|
||||
'step': 1.0,
|
||||
'unit_of_measurement': 'K',
|
||||
}),
|
||||
}),
|
||||
'type': 'ha_selector',
|
||||
}),
|
||||
dict({
|
||||
'default': 0.1,
|
||||
'name': 'temperature_step',
|
||||
'required': True,
|
||||
'selector': dict({
|
||||
'number': dict({
|
||||
'max': 2.0,
|
||||
'min': 0.1,
|
||||
'mode': 'slider',
|
||||
'step': 0.1,
|
||||
'unit_of_measurement': 'K',
|
||||
}),
|
||||
}),
|
||||
'type': 'ha_selector',
|
||||
}),
|
||||
]),
|
||||
'translation_key': 'group_setpoint_shift',
|
||||
'type': 'knx_group_select_option',
|
||||
}),
|
||||
]),
|
||||
'type': 'knx_group_select',
|
||||
}),
|
||||
dict({
|
||||
'collapsible': True,
|
||||
'name': 'section_activity',
|
||||
'required': False,
|
||||
'type': 'knx_section_flat',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_active',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': False,
|
||||
}),
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 1,
|
||||
'sub': None,
|
||||
}),
|
||||
]),
|
||||
'write': False,
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_valve',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': False,
|
||||
}),
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 5,
|
||||
'sub': 1,
|
||||
}),
|
||||
]),
|
||||
'write': False,
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'collapsible': True,
|
||||
'name': 'section_operation_mode',
|
||||
'required': False,
|
||||
'type': 'knx_section_flat',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_operation_mode',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': False,
|
||||
}),
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 20,
|
||||
'sub': 102,
|
||||
}),
|
||||
]),
|
||||
'write': dict({
|
||||
'required': False,
|
||||
}),
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ignore_auto_mode',
|
||||
'optional': True,
|
||||
'required': False,
|
||||
'selector': dict({
|
||||
'boolean': dict({
|
||||
}),
|
||||
}),
|
||||
'type': 'ha_selector',
|
||||
}),
|
||||
dict({
|
||||
'collapsible': True,
|
||||
'name': 'section_operation_mode_individual',
|
||||
'required': False,
|
||||
'type': 'knx_section_flat',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_operation_mode_comfort',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': False,
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 1,
|
||||
'sub': None,
|
||||
}),
|
||||
]),
|
||||
'write': dict({
|
||||
'required': False,
|
||||
}),
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_operation_mode_economy',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': False,
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 1,
|
||||
'sub': None,
|
||||
}),
|
||||
]),
|
||||
'write': dict({
|
||||
'required': False,
|
||||
}),
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_operation_mode_standby',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': False,
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 1,
|
||||
'sub': None,
|
||||
}),
|
||||
]),
|
||||
'write': dict({
|
||||
'required': False,
|
||||
}),
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_operation_mode_protection',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': False,
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 1,
|
||||
'sub': None,
|
||||
}),
|
||||
]),
|
||||
'write': dict({
|
||||
'required': False,
|
||||
}),
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'collapsible': True,
|
||||
'name': 'section_heat_cool',
|
||||
'required': False,
|
||||
'type': 'knx_section_flat',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_heat_cool',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': False,
|
||||
}),
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 1,
|
||||
'sub': 100,
|
||||
}),
|
||||
]),
|
||||
'write': dict({
|
||||
'required': False,
|
||||
}),
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'collapsible': True,
|
||||
'name': 'section_on_off',
|
||||
'required': False,
|
||||
'type': 'knx_section_flat',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_on_off',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': False,
|
||||
}),
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 1,
|
||||
'sub': None,
|
||||
}),
|
||||
]),
|
||||
'write': dict({
|
||||
'required': False,
|
||||
}),
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'name': 'on_off_invert',
|
||||
'optional': True,
|
||||
'required': False,
|
||||
'selector': dict({
|
||||
'boolean': dict({
|
||||
}),
|
||||
}),
|
||||
'type': 'ha_selector',
|
||||
}),
|
||||
dict({
|
||||
'collapsible': True,
|
||||
'name': 'section_controller_mode',
|
||||
'required': False,
|
||||
'type': 'knx_section_flat',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_controller_mode',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': False,
|
||||
}),
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 20,
|
||||
'sub': 105,
|
||||
}),
|
||||
]),
|
||||
'write': dict({
|
||||
'required': False,
|
||||
}),
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_controller_status',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': False,
|
||||
}),
|
||||
'write': False,
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'default': 'heat',
|
||||
'name': 'default_controller_mode',
|
||||
'required': True,
|
||||
'selector': dict({
|
||||
'select': dict({
|
||||
'custom_value': False,
|
||||
'multiple': False,
|
||||
'options': list([
|
||||
'off',
|
||||
'heat',
|
||||
'cool',
|
||||
'heat_cool',
|
||||
'auto',
|
||||
'dry',
|
||||
'fan_only',
|
||||
]),
|
||||
'sort': False,
|
||||
'translation_key': 'component.climate.selector.hvac_mode',
|
||||
}),
|
||||
}),
|
||||
'type': 'ha_selector',
|
||||
}),
|
||||
dict({
|
||||
'collapsible': True,
|
||||
'name': 'section_fan',
|
||||
'required': False,
|
||||
'type': 'knx_section_flat',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_fan_speed',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'dptSelect': list([
|
||||
dict({
|
||||
'dpt': dict({
|
||||
'main': 5,
|
||||
'sub': 1,
|
||||
}),
|
||||
'translation_key': '5_001',
|
||||
'value': '5.001',
|
||||
}),
|
||||
dict({
|
||||
'dpt': dict({
|
||||
'main': 5,
|
||||
'sub': 10,
|
||||
}),
|
||||
'translation_key': '5_010',
|
||||
'value': '5.010',
|
||||
}),
|
||||
]),
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': False,
|
||||
}),
|
||||
'write': dict({
|
||||
'required': False,
|
||||
}),
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'default': 3,
|
||||
'name': 'fan_max_step',
|
||||
'required': True,
|
||||
'selector': dict({
|
||||
'number': dict({
|
||||
'max': 100.0,
|
||||
'min': 1.0,
|
||||
'mode': 'slider',
|
||||
'step': 1.0,
|
||||
}),
|
||||
}),
|
||||
'type': 'ha_selector',
|
||||
}),
|
||||
dict({
|
||||
'default': 'off',
|
||||
'name': 'fan_zero_mode',
|
||||
'required': True,
|
||||
'selector': dict({
|
||||
'select': dict({
|
||||
'custom_value': False,
|
||||
'multiple': False,
|
||||
'options': list([
|
||||
'off',
|
||||
'auto',
|
||||
]),
|
||||
'sort': False,
|
||||
'translation_key': 'component.knx.config_panel.entities.create.climate.knx.fan_zero_mode',
|
||||
}),
|
||||
}),
|
||||
'type': 'ha_selector',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_fan_swing',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': False,
|
||||
}),
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 1,
|
||||
'sub': None,
|
||||
}),
|
||||
]),
|
||||
'write': dict({
|
||||
'required': False,
|
||||
}),
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'name': 'ga_fan_swing_horizontal',
|
||||
'optional': True,
|
||||
'options': dict({
|
||||
'passive': True,
|
||||
'state': dict({
|
||||
'required': False,
|
||||
}),
|
||||
'validDPTs': list([
|
||||
dict({
|
||||
'main': 1,
|
||||
'sub': None,
|
||||
}),
|
||||
]),
|
||||
'write': dict({
|
||||
'required': False,
|
||||
}),
|
||||
}),
|
||||
'required': False,
|
||||
'type': 'knx_group_address',
|
||||
}),
|
||||
dict({
|
||||
'allow_false': False,
|
||||
'default': True,
|
||||
'name': 'sync_state',
|
||||
'optional': True,
|
||||
'required': False,
|
||||
'type': 'knx_sync_state',
|
||||
}),
|
||||
]),
|
||||
'success': True,
|
||||
'type': 'result',
|
||||
})
|
||||
# ---
|
||||
# name: test_knx_get_schema[cover]
|
||||
dict({
|
||||
'id': 1,
|
||||
|
||||
@@ -3,16 +3,19 @@
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.climate import HVACMode
|
||||
from homeassistant.components.knx.const import ClimateConf
|
||||
from homeassistant.components.knx.schema import ClimateSchema
|
||||
from homeassistant.const import CONF_NAME, STATE_IDLE
|
||||
from homeassistant.const import CONF_NAME, STATE_IDLE, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from . import KnxEntityGenerator
|
||||
from .conftest import KNXTestKit
|
||||
|
||||
from tests.common import async_capture_events
|
||||
|
||||
RAW_FLOAT_MINUS_1_0 = (0x87, 0x9C)
|
||||
RAW_FLOAT_20_0 = (0x07, 0xD0)
|
||||
RAW_FLOAT_21_0 = (0x0C, 0x1A)
|
||||
RAW_FLOAT_22_0 = (0x0C, 0x4C)
|
||||
@@ -158,7 +161,7 @@ async def test_climate_hvac_mode(
|
||||
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
|
||||
ClimateSchema.CONF_CONTROLLER_MODE_ADDRESS: controller_mode_ga,
|
||||
ClimateSchema.CONF_CONTROLLER_MODE_STATE_ADDRESS: "1/2/7",
|
||||
ClimateSchema.CONF_OPERATION_MODES: ["Auto"],
|
||||
ClimateConf.OPERATION_MODES: ["Auto"],
|
||||
}
|
||||
| (
|
||||
{
|
||||
@@ -452,8 +455,8 @@ async def test_fan_speed_3_steps(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
||||
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
|
||||
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
|
||||
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
|
||||
ClimateSchema.CONF_FAN_SPEED_MODE: "step",
|
||||
ClimateSchema.CONF_FAN_MAX_STEP: 3,
|
||||
ClimateConf.FAN_SPEED_MODE: "step",
|
||||
ClimateConf.FAN_MAX_STEP: 3,
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -508,8 +511,8 @@ async def test_fan_speed_2_steps(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
||||
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
|
||||
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
|
||||
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
|
||||
ClimateSchema.CONF_FAN_SPEED_MODE: "step",
|
||||
ClimateSchema.CONF_FAN_MAX_STEP: 2,
|
||||
ClimateConf.FAN_SPEED_MODE: "step",
|
||||
ClimateConf.FAN_MAX_STEP: 2,
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -561,8 +564,8 @@ async def test_fan_speed_1_step(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
||||
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
|
||||
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
|
||||
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
|
||||
ClimateSchema.CONF_FAN_SPEED_MODE: "step",
|
||||
ClimateSchema.CONF_FAN_MAX_STEP: 1,
|
||||
ClimateConf.FAN_SPEED_MODE: "step",
|
||||
ClimateConf.FAN_MAX_STEP: 1,
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -604,8 +607,8 @@ async def test_fan_speed_5_steps(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
||||
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
|
||||
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
|
||||
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
|
||||
ClimateSchema.CONF_FAN_SPEED_MODE: "step",
|
||||
ClimateSchema.CONF_FAN_MAX_STEP: 5,
|
||||
ClimateConf.FAN_SPEED_MODE: "step",
|
||||
ClimateConf.FAN_MAX_STEP: 5,
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -660,7 +663,7 @@ async def test_fan_speed_percentage(hass: HomeAssistant, knx: KNXTestKit) -> Non
|
||||
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
|
||||
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
|
||||
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
|
||||
ClimateSchema.CONF_FAN_SPEED_MODE: "percent",
|
||||
ClimateConf.FAN_SPEED_MODE: "percent",
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -725,8 +728,8 @@ async def test_fan_speed_percentage_4_steps(
|
||||
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
|
||||
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
|
||||
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
|
||||
ClimateSchema.CONF_FAN_SPEED_MODE: "percent",
|
||||
ClimateSchema.CONF_FAN_MAX_STEP: 4,
|
||||
ClimateConf.FAN_SPEED_MODE: "percent",
|
||||
ClimateConf.FAN_MAX_STEP: 4,
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -785,9 +788,9 @@ async def test_fan_speed_zero_mode_auto(hass: HomeAssistant, knx: KNXTestKit) ->
|
||||
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
|
||||
ClimateSchema.CONF_FAN_SPEED_ADDRESS: "1/2/6",
|
||||
ClimateSchema.CONF_FAN_SPEED_STATE_ADDRESS: "1/2/7",
|
||||
ClimateSchema.CONF_FAN_MAX_STEP: 3,
|
||||
ClimateSchema.CONF_FAN_SPEED_MODE: "step",
|
||||
ClimateSchema.CONF_FAN_ZERO_MODE: "auto",
|
||||
ClimateConf.FAN_MAX_STEP: 3,
|
||||
ClimateConf.FAN_SPEED_MODE: "step",
|
||||
ClimateConf.FAN_ZERO_MODE: "auto",
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -938,3 +941,83 @@ async def test_horizontal_swing(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
||||
)
|
||||
await knx.assert_write("1/2/6", False)
|
||||
knx.assert_state("climate.test", HVACMode.HEAT, swing_horizontal_mode="off")
|
||||
|
||||
|
||||
async def test_climate_ui_create(
|
||||
hass: HomeAssistant,
|
||||
knx: KNXTestKit,
|
||||
create_ui_entity: KnxEntityGenerator,
|
||||
) -> None:
|
||||
"""Test creating a climate entity."""
|
||||
await knx.setup_integration()
|
||||
await create_ui_entity(
|
||||
platform=Platform.CLIMATE,
|
||||
entity_data={"name": "test"},
|
||||
knx_data={
|
||||
"ga_temperature_current": {"state": "0/0/1"},
|
||||
"target_temperature": {
|
||||
"ga_temperature_target": {"write": "1/1/1", "state": "1/1/2"},
|
||||
},
|
||||
"sync_state": True,
|
||||
},
|
||||
)
|
||||
# created entity sends read-request to KNX bus
|
||||
await knx.assert_read("0/0/1", response=RAW_FLOAT_20_0)
|
||||
await knx.assert_read("1/1/2", response=RAW_FLOAT_20_0)
|
||||
knx.assert_state("climate.test", HVACMode.HEAT)
|
||||
|
||||
|
||||
async def test_climate_ui_load(knx: KNXTestKit) -> None:
|
||||
"""Test loading climate entities from storage."""
|
||||
await knx.setup_integration(config_store_fixture="config_store_climate.json")
|
||||
# direct_indi-op_heat-cool
|
||||
await knx.assert_read(
|
||||
"0/0/1", response=RAW_FLOAT_20_0, ignore_order=True
|
||||
) # current
|
||||
await knx.assert_read("0/1/2", response=RAW_FLOAT_20_0, ignore_order=True) # target
|
||||
await knx.assert_read(
|
||||
"0/4/2", response=False, ignore_order=True
|
||||
) # on_off - inverted
|
||||
await knx.assert_read("0/3/2", response=True, ignore_order=True) # heat-cool
|
||||
await knx.assert_read("0/2/1", response=True, ignore_order=True) # comfort
|
||||
await knx.assert_read("0/2/2", response=False, ignore_order=True) # eco
|
||||
await knx.assert_read("0/2/3", response=False, ignore_order=True) # standby
|
||||
await knx.assert_read("0/2/4", response=False, ignore_order=True) # protection
|
||||
|
||||
# sps_op-mode_contr-mode
|
||||
await knx.assert_read(
|
||||
"1/0/1", response=RAW_FLOAT_20_0, ignore_order=True
|
||||
) # current
|
||||
await knx.assert_read("1/1/0", response=RAW_FLOAT_21_0, ignore_order=True) # target
|
||||
await knx.assert_read(
|
||||
"1/1/2", response=RAW_FLOAT_MINUS_1_0, ignore_order=True
|
||||
) # shift
|
||||
await knx.assert_read("1/1/3", response=True, ignore_order=True) # active
|
||||
await knx.assert_read("1/1/4", response=(0x22,), ignore_order=True) # valve
|
||||
await knx.assert_read("1/4/2", response=(0x22,), ignore_order=True) # fan speed
|
||||
await knx.assert_read("1/4/4", response=False, ignore_order=True) # swing vertical
|
||||
await knx.assert_read(
|
||||
"1/4/6", response=False, ignore_order=True
|
||||
) # swing horizontal
|
||||
await knx.assert_read(
|
||||
"1/0/2", response=RAW_FLOAT_20_0, ignore_order=True
|
||||
) # humidity
|
||||
await knx.assert_read(
|
||||
"1/2/2", # operation mode
|
||||
response=(0x01,), # comfort
|
||||
ignore_order=True,
|
||||
)
|
||||
await knx.assert_read(
|
||||
"1/3/2", # controller mode
|
||||
response=(0x03,), # cool
|
||||
ignore_order=True,
|
||||
)
|
||||
|
||||
knx.assert_state(
|
||||
"climate.direct_indi_op_heat_cool",
|
||||
HVACMode.HEAT,
|
||||
)
|
||||
knx.assert_state(
|
||||
"climate.sps_op_mode_contr_mode",
|
||||
HVACMode.COOL,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user