Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions homeassistant/components/knx/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ async def async_setup_entry(

def _create_climate_yaml(xknx: XKNX, config: ConfigType) -> XknxClimate:
"""Return a KNX Climate device to be used within XKNX."""
sync_state = config[CONF_SYNC_STATE]
climate_mode = XknxClimateMode(
xknx,
name=f"{config[CONF_NAME]} Mode",
Expand Down Expand Up @@ -151,6 +152,7 @@ def _create_climate_yaml(xknx: XKNX, config: ConfigType) -> XknxClimate:
group_address_heat_cool_state=config.get(
ClimateSchema.CONF_HEAT_COOL_STATE_ADDRESS
),
sync_state=sync_state,
operation_modes=config.get(ClimateConf.OPERATION_MODES),
controller_modes=config.get(ClimateConf.CONTROLLER_MODES),
)
Expand Down Expand Up @@ -182,6 +184,7 @@ def _create_climate_yaml(xknx: XKNX, config: ConfigType) -> XknxClimate:
group_address_command_value_state=config.get(
ClimateSchema.CONF_COMMAND_VALUE_STATE_ADDRESS
),
sync_state=sync_state,
min_temp=config.get(ClimateConf.MIN_TEMP),
max_temp=config.get(ClimateConf.MAX_TEMP),
mode=climate_mode,
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/knx/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
invert_updown=config[CoverConf.INVERT_UPDOWN],
invert_position=config[CoverConf.INVERT_POSITION],
invert_angle=config[CoverConf.INVERT_ANGLE],
sync_state=config[CONF_SYNC_STATE],
)
super().__init__(
knx_module=knx_module,
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/knx/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ def individual_color_addresses(color: str, feature: str) -> Any | None:
color_temperature_type=color_temperature_type,
min_kelvin=config[LightSchema.CONF_MIN_KELVIN],
max_kelvin=config[LightSchema.CONF_MAX_KELVIN],
sync_state=config[CONF_SYNC_STATE],
)


Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/knx/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
group_address=config[KNX_ADDRESS],
group_address_state=config.get(CONF_STATE_ADDRESS),
respond_to_read=config[CONF_RESPOND_TO_READ],
sync_state=config[CONF_SYNC_STATE],
value_type=config[CONF_TYPE],
)
super().__init__(
Expand Down
15 changes: 15 additions & 0 deletions homeassistant/components/knx/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
numeric_type_validator,
sensor_type_validator,
string_type_validator,
sync_state_no_false_validator,
sync_state_validator,
validate_number_attributes,
validate_sensor_attributes,
Expand Down Expand Up @@ -421,6 +422,9 @@ class ClimateSchema(KNXPlatformSchema):
vol.Optional(CONF_SWING_HORIZONTAL_ADDRESS): ga_list_validator,
vol.Optional(CONF_SWING_HORIZONTAL_STATE_ADDRESS): ga_list_validator,
vol.Optional(CONF_HUMIDITY_STATE_ADDRESS): ga_list_validator,
vol.Optional(
CONF_SYNC_STATE, default=True
): sync_state_no_false_validator,
Comment thread
farmio marked this conversation as resolved.
}
),
)
Expand Down Expand Up @@ -461,6 +465,9 @@ class CoverSchema(KNXPlatformSchema):
vol.Optional(CoverConf.INVERT_POSITION, default=False): cv.boolean,
vol.Optional(CoverConf.INVERT_ANGLE, default=False): cv.boolean,
vol.Optional(CONF_DEVICE_CLASS): COVER_DEVICE_CLASSES_SCHEMA,
vol.Optional(
CONF_SYNC_STATE, default=True
): sync_state_no_false_validator,
}
),
vol.Any(
Expand Down Expand Up @@ -703,6 +710,9 @@ class LightSchema(KNXPlatformSchema):
vol.Optional(CONF_MAX_KELVIN, default=DEFAULT_MAX_KELVIN): vol.All(
vol.Coerce(int), vol.Range(min=1)
),
vol.Optional(
CONF_SYNC_STATE, default=True
): sync_state_no_false_validator,
}
),
vol.Any(
Expand Down Expand Up @@ -776,6 +786,9 @@ class NumberSchema(KNXPlatformSchema):
vol.Optional(NumberConf.STEP): cv.positive_float,
vol.Optional(CONF_DEVICE_CLASS): NUMBER_DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
vol.Optional(
CONF_SYNC_STATE, default=True
): sync_state_no_false_validator,
}
),
_number_limit_sub_validator,
Expand Down Expand Up @@ -869,6 +882,7 @@ class SwitchSchema(KNXPlatformSchema):
vol.Required(KNX_ADDRESS): ga_list_validator,
vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
vol.Optional(CONF_DEVICE_CLASS): SWITCH_DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_SYNC_STATE, default=True): sync_state_no_false_validator,
}
)

Expand All @@ -885,6 +899,7 @@ class TextSchema(KNXPlatformSchema):
vol.Optional(CONF_MODE, default=TextMode.TEXT): vol.Coerce(TextMode),
vol.Required(KNX_ADDRESS): ga_list_validator,
vol.Optional(CONF_STATE_ADDRESS): ga_list_validator,
vol.Optional(CONF_SYNC_STATE, default=True): sync_state_no_false_validator,
}
)

Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/knx/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
group_address=config[KNX_ADDRESS],
group_address_state=config.get(SwitchSchema.CONF_STATE_ADDRESS),
respond_to_read=config[CONF_RESPOND_TO_READ],
sync_state=config[CONF_SYNC_STATE],
invert=config[SwitchSchema.CONF_INVERT],
)
super().__init__(
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/knx/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
group_address=config[KNX_ADDRESS],
group_address_state=config.get(CONF_STATE_ADDRESS),
respond_to_read=config[CONF_RESPOND_TO_READ],
sync_state=config[CONF_SYNC_STATE],
value_type=config[CONF_TYPE],
)
super().__init__(
Expand Down
7 changes: 7 additions & 0 deletions homeassistant/components/knx/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ def ip_v4_validator(value: Any, multicast: bool | None = None) -> str:
cv.matches_regex(r"^(init|expire|every)( \d*)?$"),
)

# entities having a writable group address can omit the state address
# instead of disabling state updates entirely
sync_state_no_false_validator = vol.All(
sync_state_validator,
vol.IsTrue("Sync state can not be disabled for this platform"),
Comment thread
farmio marked this conversation as resolved.
)


def backwards_compatible_xknx_climate_enum_member(enumClass: type[Enum]) -> vol.All:
"""Transform a string to an enum member.
Expand Down
3 changes: 2 additions & 1 deletion tests/components/knx/test_climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest

from homeassistant.components.climate import HVACMode
from homeassistant.components.knx.const import ClimateConf
from homeassistant.components.knx.const import CONF_SYNC_STATE, ClimateConf
from homeassistant.components.knx.schema import ClimateSchema
from homeassistant.const import CONF_NAME, STATE_IDLE, Platform
from homeassistant.core import HomeAssistant
Expand Down Expand Up @@ -32,6 +32,7 @@ async def test_climate_basic_temperature_set(
ClimateSchema.CONF_TEMPERATURE_ADDRESS: "1/2/3",
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS: "1/2/4",
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS: "1/2/5",
CONF_SYNC_STATE: "init",
}
}
)
Expand Down
2 changes: 2 additions & 0 deletions tests/components/knx/test_cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
CoverEntityFeature,
CoverState,
)
from homeassistant.components.knx.const import CONF_SYNC_STATE
from homeassistant.components.knx.schema import CoverSchema
from homeassistant.const import CONF_NAME, STATE_UNAVAILABLE, STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant, State
Expand All @@ -30,6 +31,7 @@ async def test_cover_basic(hass: HomeAssistant, knx: KNXTestKit) -> None:
CoverSchema.CONF_MOVE_SHORT_ADDRESS: "1/0/1",
CoverSchema.CONF_POSITION_STATE_ADDRESS: "1/0/2",
CoverSchema.CONF_POSITION_ADDRESS: "1/0/3",
CONF_SYNC_STATE: "init",
}
}
)
Expand Down
8 changes: 7 additions & 1 deletion tests/components/knx/test_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
from xknx.core import XknxConnectionState
from xknx.devices.light import Light as XknxLight

from homeassistant.components.knx.const import CONF_STATE_ADDRESS, KNX_ADDRESS, Platform
from homeassistant.components.knx.const import (
CONF_STATE_ADDRESS,
CONF_SYNC_STATE,
KNX_ADDRESS,
Platform,
)
from homeassistant.components.knx.schema import LightSchema
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
Expand Down Expand Up @@ -151,6 +156,7 @@ async def test_light_color_temp_absolute(hass: HomeAssistant, knx: KNXTestKit) -
LightSchema.CONF_COLOR_TEMP_ADDRESS: test_ct,
LightSchema.CONF_COLOR_TEMP_STATE_ADDRESS: test_ct_state,
LightSchema.CONF_COLOR_TEMP_MODE: "absolute",
CONF_SYNC_STATE: "init",
},
]
}
Expand Down
2 changes: 2 additions & 0 deletions tests/components/knx/test_number.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from homeassistant.components.knx.const import (
CONF_RESPOND_TO_READ,
CONF_STATE_ADDRESS,
CONF_SYNC_STATE,
KNX_ADDRESS,
)
from homeassistant.components.knx.schema import NumberSchema
Expand Down Expand Up @@ -139,6 +140,7 @@ async def test_number_state_restore(hass: HomeAssistant, knx: KNXTestKit) -> Non
KNX_ADDRESS: test_address,
CONF_STATE_ADDRESS: test_state_address,
CONF_TYPE: "illuminance",
CONF_SYNC_STATE: "init",
}
}
)
Expand Down
22 changes: 22 additions & 0 deletions tests/components/knx/test_switch.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
"""Test KNX switch."""

import pytest

from homeassistant.components.knx.const import (
CONF_RESPOND_TO_READ,
CONF_STATE_ADDRESS,
CONF_SYNC_STATE,
KNX_ADDRESS,
)
from homeassistant.components.knx.schema import SwitchSchema
Expand Down Expand Up @@ -64,6 +67,7 @@ async def test_switch_state(hass: HomeAssistant, knx: KNXTestKit) -> None:
CONF_NAME: "test",
KNX_ADDRESS: _ADDRESS,
CONF_STATE_ADDRESS: _STATE_ADDRESS,
CONF_SYNC_STATE: "init",
},
}
)
Expand Down Expand Up @@ -113,6 +117,24 @@ async def test_switch_state(hass: HomeAssistant, knx: KNXTestKit) -> None:
await knx.assert_telegram_count(0)


async def test_switch_sync_state_false_invalid(
hass: HomeAssistant, knx: KNXTestKit, caplog: pytest.LogCaptureFixture
) -> None:
"""Test entities having a writable address don't allow disabling state updates."""
await knx.setup_integration(
{
SwitchSchema.PLATFORM: {
CONF_NAME: "test",
KNX_ADDRESS: "1/1/1",
CONF_STATE_ADDRESS: "2/2/2",
CONF_SYNC_STATE: False,
},
}
)
assert "Sync state can not be disabled for this platform" in caplog.text
assert hass.states.get("switch.test") is None


async def test_switch_state_restore(hass: HomeAssistant, knx: KNXTestKit) -> None:
"""Test KNX switch with state_address restores last known state until bus read completes."""
_ADDRESS = "1/1/1"
Expand Down
2 changes: 2 additions & 0 deletions tests/components/knx/test_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from homeassistant.components.knx.const import (
CONF_RESPOND_TO_READ,
CONF_STATE_ADDRESS,
CONF_SYNC_STATE,
KNX_ADDRESS,
)
from homeassistant.components.knx.schema import TextSchema
Expand Down Expand Up @@ -120,6 +121,7 @@ async def test_text_state_restore(hass: HomeAssistant, knx: KNXTestKit) -> None:
CONF_NAME: "test",
KNX_ADDRESS: test_address,
CONF_STATE_ADDRESS: test_state_address,
CONF_SYNC_STATE: "init",
}
}
)
Expand Down
Loading