"""Test knx expose.""" from unittest.mock import AsyncMock, Mock, patch import pytest from homeassistant.components.knx import ( CONF_KNX_EXPOSE, CONFIG_SCHEMA as KNX_CONFIG_SCHEMA, KNX_ADDRESS, ) from homeassistant.components.knx.const import DOMAIN as KNX_DOMAIN from homeassistant.const import CONF_ATTRIBUTE, CONF_ENTITY_ID, CONF_TYPE from homeassistant.setup import async_setup_component async def setup_knx_integration(hass, knx_mock, config=None): """Create the KNX gateway.""" if config is None: config = {} with patch("homeassistant.components.knx.XKNX", return_value=knx_mock): await async_setup_component( hass, KNX_DOMAIN, KNX_CONFIG_SCHEMA({KNX_DOMAIN: config}) ) await hass.async_block_till_done() @pytest.fixture(autouse=True) def xknx_mock(): """Create a simple XKNX mock.""" xknx_mock = Mock() xknx_mock.telegrams = AsyncMock() xknx_mock.start = AsyncMock() xknx_mock.stop = AsyncMock() return xknx_mock async def test_binary_expose(hass, xknx_mock): """Test that a binary expose sends only telegrams on state change.""" entity_id = "fake.entity" await setup_knx_integration( hass, xknx_mock, { CONF_KNX_EXPOSE: { CONF_TYPE: "binary", KNX_ADDRESS: "1/1/8", CONF_ENTITY_ID: entity_id, } }, ) assert not hass.states.async_all() # Change state to on xknx_mock.reset_mock() hass.states.async_set(entity_id, "on", {}) await hass.async_block_till_done() assert xknx_mock.telegrams.put.call_count == 1, "Expected telegram for state change" # Change attribute; keep state xknx_mock.reset_mock() hass.states.async_set(entity_id, "on", {"brightness": 180}) await hass.async_block_till_done() assert ( xknx_mock.telegrams.put.call_count == 0 ), "Expected no telegram; state not changed" # Change attribute and state xknx_mock.reset_mock() hass.states.async_set(entity_id, "off", {"brightness": 0}) await hass.async_block_till_done() assert xknx_mock.telegrams.put.call_count == 1, "Expected telegram for state change" async def test_expose_attribute(hass, xknx_mock): """Test that an expose sends only telegrams on attribute change.""" entity_id = "fake.entity" attribute = "fake_attribute" await setup_knx_integration( hass, xknx_mock, { CONF_KNX_EXPOSE: { CONF_TYPE: "percentU8", KNX_ADDRESS: "1/1/8", CONF_ENTITY_ID: entity_id, CONF_ATTRIBUTE: attribute, } }, ) assert not hass.states.async_all() # Change state to on; no attribute xknx_mock.reset_mock() hass.states.async_set(entity_id, "on", {}) await hass.async_block_till_done() assert xknx_mock.telegrams.put.call_count == 0 # Change attribute; keep state xknx_mock.reset_mock() hass.states.async_set(entity_id, "on", {attribute: 1}) await hass.async_block_till_done() assert xknx_mock.telegrams.put.call_count == 1 # Change state keep attribute xknx_mock.reset_mock() hass.states.async_set(entity_id, "off", {attribute: 1}) await hass.async_block_till_done() assert xknx_mock.telegrams.put.call_count == 0 # Change state and attribute xknx_mock.reset_mock() hass.states.async_set(entity_id, "on", {attribute: 0}) await hass.async_block_till_done() assert xknx_mock.telegrams.put.call_count == 1