Fix KNX default state updater option (#135611)

This commit is contained in:
Matthias Alphart 2025-01-19 12:53:09 +01:00 committed by GitHub
parent acbb15a496
commit af0f416497
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 94 additions and 7 deletions

View File

@ -10,6 +10,7 @@ from typing import Final
import voluptuous as vol import voluptuous as vol
from xknx import XKNX from xknx import XKNX
from xknx.core import XknxConnectionState from xknx.core import XknxConnectionState
from xknx.core.state_updater import StateTrackerType, TrackerOptions
from xknx.core.telegram_queue import TelegramQueue from xknx.core.telegram_queue import TelegramQueue
from xknx.dpt import DPTBase from xknx.dpt import DPTBase
from xknx.exceptions import ConversionError, CouldNotParseTelegram, XKNXException from xknx.exceptions import ConversionError, CouldNotParseTelegram, XKNXException
@ -273,11 +274,18 @@ class KNXModule:
self.project = KNXProject(hass=hass, entry=entry) self.project = KNXProject(hass=hass, entry=entry)
self.config_store = KNXConfigStore(hass=hass, config_entry=entry) self.config_store = KNXConfigStore(hass=hass, config_entry=entry)
default_state_updater = (
TrackerOptions(tracker_type=StateTrackerType.EXPIRE, update_interval_min=60)
if self.entry.data[CONF_KNX_STATE_UPDATER]
else TrackerOptions(
tracker_type=StateTrackerType.INIT, update_interval_min=60
)
)
self.xknx = XKNX( self.xknx = XKNX(
address_format=self.project.get_address_format(), address_format=self.project.get_address_format(),
connection_config=self.connection_config(), connection_config=self.connection_config(),
rate_limit=self.entry.data[CONF_KNX_RATE_LIMIT], rate_limit=self.entry.data[CONF_KNX_RATE_LIMIT],
state_updater=self.entry.data[CONF_KNX_STATE_UPDATER], state_updater=default_state_updater,
) )
self.xknx.connection_manager.register_connection_state_changed_cb( self.xknx.connection_manager.register_connection_state_changed_cb(
self.connection_state_changed_cb self.connection_state_changed_cb

View File

@ -114,7 +114,7 @@ class KNXConfigEntryData(TypedDict, total=False):
backbone_key: str | None # not required backbone_key: str | None # not required
sync_latency_tolerance: int | None # not required sync_latency_tolerance: int | None # not required
# OptionsFlow only # OptionsFlow only
state_updater: bool state_updater: bool # default state updater: True -> expire 60; False -> init
rate_limit: int rate_limit: int
# Integration only (not forwarded to xknx) # Integration only (not forwarded to xknx)
telegram_log_size: int # not required telegram_log_size: int # not required

View File

@ -161,7 +161,7 @@
"telegram_log_size": "Telegram history limit" "telegram_log_size": "Telegram history limit"
}, },
"data_description": { "data_description": {
"state_updater": "Set default for reading states from the KNX Bus. When disabled, Home Assistant will not actively retrieve entity states from the KNX Bus. Can be overridden by `sync_state` entity options.", "state_updater": "Sets the default behavior for reading state addresses from the KNX Bus.\nWhen enabled, Home Assistant will monitor each group address and read it from the bus if no value has been received for one hour.\nWhen disabled, state addresses will only be read once after a bus connection is established.\nThis behavior can be overridden for individual entities using the `sync_state` option.",
"rate_limit": "Maximum outgoing telegrams per second.\n`0` to disable limit. Recommended: `0` or between `20` and `40`", "rate_limit": "Maximum outgoing telegrams per second.\n`0` to disable limit. Recommended: `0` or between `20` and `40`",
"telegram_log_size": "Telegrams to keep in memory for KNX panel group monitor. Maximum: {telegram_log_size_max}" "telegram_log_size": "Telegrams to keep in memory for KNX panel group monitor. Maximum: {telegram_log_size_max}"
} }

View File

@ -335,7 +335,7 @@ async def create_ui_entity(
hass_ws_client: WebSocketGenerator, hass_ws_client: WebSocketGenerator,
hass_storage: dict[str, Any], hass_storage: dict[str, Any],
) -> KnxEntityGenerator: ) -> KnxEntityGenerator:
"""Return a helper to create a KNX entities via WS. """Return a helper to create KNX entities via WS.
The KNX integration must be set up before using the helper. The KNX integration must be set up before using the helper.
""" """

View File

@ -1,7 +1,9 @@
"""Test KNX init.""" """Test KNX init."""
from datetime import timedelta
from unittest.mock import patch from unittest.mock import patch
from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from xknx.io import ( from xknx.io import (
DEFAULT_MCAST_GRP, DEFAULT_MCAST_GRP,
@ -11,7 +13,10 @@ from xknx.io import (
SecureConfig, SecureConfig,
) )
from homeassistant.components.knx.config_flow import DEFAULT_ROUTING_IA from homeassistant.components.knx.config_flow import (
DEFAULT_ENTRY_DATA,
DEFAULT_ROUTING_IA,
)
from homeassistant.components.knx.const import ( from homeassistant.components.knx.const import (
CONF_KNX_AUTOMATIC, CONF_KNX_AUTOMATIC,
CONF_KNX_CONNECTION_TYPE, CONF_KNX_CONNECTION_TYPE,
@ -40,12 +45,13 @@ from homeassistant.components.knx.const import (
KNXConfigEntryData, KNXConfigEntryData,
) )
from homeassistant.config_entries import ConfigEntryState from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.const import CONF_HOST, CONF_PORT, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from . import KnxEntityGenerator
from .conftest import KNXTestKit from .conftest import KNXTestKit
from tests.common import MockConfigEntry from tests.common import MockConfigEntry, async_fire_time_changed
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -262,6 +268,79 @@ async def test_init_connection_handling(
) )
async def _init_switch_and_wait_for_first_state_updater_run(
hass: HomeAssistant,
knx: KNXTestKit,
create_ui_entity: KnxEntityGenerator,
freezer: FrozenDateTimeFactory,
config_entry_data: KNXConfigEntryData,
) -> None:
"""Return a config entry with default data."""
config_entry = MockConfigEntry(
title="KNX", domain=KNX_DOMAIN, data=config_entry_data
)
knx.mock_config_entry = config_entry
await knx.setup_integration({})
await create_ui_entity(
platform=Platform.SWITCH,
knx_data={
"ga_switch": {"write": "1/1/1", "state": "2/2/2"},
"respond_to_read": True,
"sync_state": True, # True uses xknx default state updater
"invert": False,
},
)
# created entity sends read-request to KNX bus on connection
await knx.assert_read("2/2/2")
await knx.receive_response("2/2/2", True)
freezer.tick(timedelta(minutes=59))
async_fire_time_changed(hass)
await hass.async_block_till_done()
await hass.async_block_till_done()
await knx.assert_no_telegram()
freezer.tick(timedelta(minutes=1)) # 60 minutes passed
async_fire_time_changed(hass)
await hass.async_block_till_done()
await hass.async_block_till_done()
async def test_default_state_updater_enabled(
hass: HomeAssistant,
knx: KNXTestKit,
create_ui_entity: KnxEntityGenerator,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test default state updater is applied to xknx device instances."""
config_entry = DEFAULT_ENTRY_DATA | KNXConfigEntryData(
connection_type=CONF_KNX_AUTOMATIC, # missing in default data
state_updater=True,
)
await _init_switch_and_wait_for_first_state_updater_run(
hass, knx, create_ui_entity, freezer, config_entry
)
await knx.assert_read("2/2/2")
await knx.receive_response("2/2/2", True)
async def test_default_state_updater_disabled(
hass: HomeAssistant,
knx: KNXTestKit,
create_ui_entity: KnxEntityGenerator,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test default state updater is applied to xknx device instances."""
config_entry = DEFAULT_ENTRY_DATA | KNXConfigEntryData(
connection_type=CONF_KNX_AUTOMATIC, # missing in default data
state_updater=False,
)
await _init_switch_and_wait_for_first_state_updater_run(
hass, knx, create_ui_entity, freezer, config_entry
)
await knx.assert_no_telegram()
async def test_async_remove_entry( async def test_async_remove_entry(
hass: HomeAssistant, hass: HomeAssistant,
knx: KNXTestKit, knx: KNXTestKit,