diff --git a/homeassistant/components/knx/__init__.py b/homeassistant/components/knx/__init__.py index e6c1562f37e..e52a79cf25a 100644 --- a/homeassistant/components/knx/__init__.py +++ b/homeassistant/components/knx/__init__.py @@ -7,6 +7,7 @@ from typing import Final import voluptuous as vol from xknx import XKNX +from xknx.core import XknxConnectionState from xknx.core.telegram_queue import TelegramQueue from xknx.dpt import DPTArray, DPTBase, DPTBinary from xknx.exceptions import XKNXException @@ -270,6 +271,9 @@ class KNXModule: self.init_xknx() self._knx_event_callback: TelegramQueue.Callback = self.register_callback() + self.xknx.connection_manager.register_connection_state_changed_cb( + self.connection_state_changed_cb + ) def init_xknx(self) -> None: """Initialize XKNX object.""" @@ -352,6 +356,12 @@ class KNXModule: }, ) + async def connection_state_changed_cb(self, state: XknxConnectionState) -> None: + """Call invoked after a KNX connection state change was received.""" + self.connected = state == XknxConnectionState.CONNECTED + if tasks := [device.after_update() for device in self.xknx.devices]: + await asyncio.gather(*tasks) + def register_callback(self) -> TelegramQueue.Callback: """Register callback within XKNX TelegramQueue.""" address_filters = list( diff --git a/homeassistant/components/knx/manifest.json b/homeassistant/components/knx/manifest.json index c514ec6fe64..aafa6630560 100644 --- a/homeassistant/components/knx/manifest.json +++ b/homeassistant/components/knx/manifest.json @@ -2,7 +2,7 @@ "domain": "knx", "name": "KNX", "documentation": "https://www.home-assistant.io/integrations/knx", - "requirements": ["xknx==0.18.9"], + "requirements": ["xknx==0.18.10"], "codeowners": ["@Julius2342", "@farmio", "@marvin-w"], "quality_scale": "silver", "iot_class": "local_push" diff --git a/requirements_all.txt b/requirements_all.txt index 1effc0bea1d..7eeb386220e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2427,7 +2427,7 @@ xbox-webapi==2.0.11 xboxapi==2.0.1 # homeassistant.components.knx -xknx==0.18.9 +xknx==0.18.10 # homeassistant.components.bluesound # homeassistant.components.fritz diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a868f02c168..d3b47554c58 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1395,7 +1395,7 @@ wolf_smartset==0.1.11 xbox-webapi==2.0.11 # homeassistant.components.knx -xknx==0.18.9 +xknx==0.18.10 # homeassistant.components.bluesound # homeassistant.components.fritz diff --git a/tests/components/knx/conftest.py b/tests/components/knx/conftest.py index 0dc8749830e..5f60f5603aa 100644 --- a/tests/components/knx/conftest.py +++ b/tests/components/knx/conftest.py @@ -153,6 +153,7 @@ class KNXTestKit: source_address=IndividualAddress(self.INDIVIDUAL_ADDRESS), ) ) + await self.xknx.telegrams.join() await self.hass.async_block_till_done() async def receive_read( diff --git a/tests/components/knx/test_binary_sensor.py b/tests/components/knx/test_binary_sensor.py index 48b871b85e4..9f9785150c0 100644 --- a/tests/components/knx/test_binary_sensor.py +++ b/tests/components/knx/test_binary_sensor.py @@ -1,4 +1,5 @@ """Test KNX binary sensor.""" +import asyncio from datetime import timedelta from homeassistant.components.knx.const import CONF_STATE_ADDRESS, CONF_SYNC_STATE @@ -126,7 +127,7 @@ async def test_binary_sensor_counter(hass: HomeAssistant, knx: KNXTestKit): { CONF_NAME: "test", CONF_STATE_ADDRESS: "2/2/2", - BinarySensorSchema.CONF_CONTEXT_TIMEOUT: 1, + BinarySensorSchema.CONF_CONTEXT_TIMEOUT: 0.001, CONF_SYNC_STATE: False, }, ] @@ -144,8 +145,9 @@ async def test_binary_sensor_counter(hass: HomeAssistant, knx: KNXTestKit): state = hass.states.get("binary_sensor.test") assert state.state is STATE_OFF assert state.attributes.get("counter") == 0 - async_fire_time_changed(hass, dt.utcnow() + timedelta(seconds=1)) + async_fire_time_changed(hass, dt.utcnow() + timedelta(seconds=0.001)) await hass.async_block_till_done() + await asyncio.sleep(0.002) # state changed twice after context timeout - once to ON with counter 1 and once to counter 0 state = hass.states.get("binary_sensor.test") assert state.state is STATE_ON @@ -153,8 +155,12 @@ async def test_binary_sensor_counter(hass: HomeAssistant, knx: KNXTestKit): # additional async_block_till_done needed event capture await hass.async_block_till_done() assert len(events) == 2 - assert events.pop(0).data.get("new_state").attributes.get("counter") == 1 - assert events.pop(0).data.get("new_state").attributes.get("counter") == 0 + event = events.pop(0).data + assert event.get("new_state").attributes.get("counter") == 1 + assert event.get("old_state").attributes.get("counter") == 0 + event = events.pop(0).data + assert event.get("new_state").attributes.get("counter") == 0 + assert event.get("old_state").attributes.get("counter") == 1 # receive 2 telegrams in context await knx.receive_write("2/2/2", True) @@ -170,9 +176,11 @@ async def test_binary_sensor_counter(hass: HomeAssistant, knx: KNXTestKit): assert state.state is STATE_ON assert state.attributes.get("counter") == 0 await hass.async_block_till_done() - assert len(events) == 2 - assert events.pop(0).data.get("new_state").attributes.get("counter") == 2 - assert events.pop(0).data.get("new_state").attributes.get("counter") == 0 + await hass.async_block_till_done() + assert len(events) == 1 + event = events.pop(0).data + assert event.get("new_state").attributes.get("counter") == 2 + assert event.get("old_state").attributes.get("counter") == 0 async def test_binary_sensor_reset(hass: HomeAssistant, knx: KNXTestKit): @@ -200,6 +208,7 @@ async def test_binary_sensor_reset(hass: HomeAssistant, knx: KNXTestKit): assert state.state is STATE_ON async_fire_time_changed(hass, dt.utcnow() + timedelta(seconds=1)) await hass.async_block_till_done() + await hass.async_block_till_done() # state reset after after timeout state = hass.states.get("binary_sensor.test") assert state.state is STATE_OFF