Configurable KNX Telegram history size (#93248)

* Configurable KNX Telegram history size

* Add maximum value to description
This commit is contained in:
Matthias Alphart 2023-05-22 18:09:59 +02:00 committed by GitHub
parent 3bf9eaffdf
commit b10e73e2d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 25 deletions

View file

@ -60,6 +60,7 @@ from .const import (
CONF_KNX_SECURE_USER_ID,
CONF_KNX_SECURE_USER_PASSWORD,
CONF_KNX_STATE_UPDATER,
CONF_KNX_TELEGRAM_LOG_SIZE,
CONF_KNX_TUNNELING,
CONF_KNX_TUNNELING_TCP,
CONF_KNX_TUNNELING_TCP_SECURE,
@ -68,6 +69,7 @@ from .const import (
DOMAIN,
KNX_ADDRESS,
SUPPORTED_PLATFORMS,
TELEGRAM_LOG_DEFAULT,
)
from .device import KNXInterfaceDevice
from .expose import KNXExposeSensor, KNXExposeTime, create_knx_exposure
@ -384,7 +386,12 @@ class KNXModule:
self.xknx.connection_manager.register_connection_state_changed_cb(
self.connection_state_changed_cb
)
self.telegrams = Telegrams(hass, self.xknx, self.project)
self.telegrams = Telegrams(
hass=hass,
xknx=self.xknx,
project=self.project,
log_size=entry.data.get(CONF_KNX_TELEGRAM_LOG_SIZE, TELEGRAM_LOG_DEFAULT),
)
self.interface_device = KNXInterfaceDevice(
hass=hass, entry=entry, xknx=self.xknx
)

View file

@ -49,12 +49,15 @@ from .const import (
CONF_KNX_SECURE_USER_ID,
CONF_KNX_SECURE_USER_PASSWORD,
CONF_KNX_STATE_UPDATER,
CONF_KNX_TELEGRAM_LOG_SIZE,
CONF_KNX_TUNNEL_ENDPOINT_IA,
CONF_KNX_TUNNELING,
CONF_KNX_TUNNELING_TCP,
CONF_KNX_TUNNELING_TCP_SECURE,
DEFAULT_ROUTING_IA,
DOMAIN,
TELEGRAM_LOG_DEFAULT,
TELEGRAM_LOG_MAX,
KNXConfigEntryData,
)
from .schema import ia_validator, ip_v4_validator
@ -70,6 +73,7 @@ DEFAULT_ENTRY_DATA = KNXConfigEntryData(
rate_limit=CONF_KNX_DEFAULT_RATE_LIMIT,
route_back=False,
state_updater=CONF_KNX_DEFAULT_STATE_UPDATER,
telegram_log_size=TELEGRAM_LOG_DEFAULT,
)
CONF_KEYRING_FILE: Final = "knxkeys_file"
@ -203,7 +207,11 @@ class KNXCommonFlow(ABC, FlowHandler):
)
async def async_step_tunnel(self, user_input: dict | None = None) -> FlowResult:
"""Select a tunnel from a list. Will be skipped if the gateway scan was unsuccessful or if only one gateway was found."""
"""Select a tunnel from a list.
Will be skipped if the gateway scan was unsuccessful
or if only one gateway was found.
"""
if user_input is not None:
if user_input[CONF_KNX_GATEWAY] == OPTION_MANUAL_TUNNEL:
if self._found_tunnels:
@ -804,6 +812,7 @@ class KNXOptionsFlow(KNXCommonFlow, OptionsFlow):
self.new_entry_data = KNXConfigEntryData(
state_updater=user_input[CONF_KNX_STATE_UPDATER],
rate_limit=user_input[CONF_KNX_RATE_LIMIT],
telegram_log_size=user_input[CONF_KNX_TELEGRAM_LOG_SIZE],
)
return self.finish_flow()
@ -811,15 +820,13 @@ class KNXOptionsFlow(KNXCommonFlow, OptionsFlow):
vol.Required(
CONF_KNX_STATE_UPDATER,
default=self.initial_data.get(
CONF_KNX_STATE_UPDATER,
CONF_KNX_DEFAULT_STATE_UPDATER,
CONF_KNX_STATE_UPDATER, CONF_KNX_DEFAULT_STATE_UPDATER
),
): selector.BooleanSelector(),
vol.Required(
CONF_KNX_RATE_LIMIT,
default=self.initial_data.get(
CONF_KNX_RATE_LIMIT,
CONF_KNX_DEFAULT_RATE_LIMIT,
CONF_KNX_RATE_LIMIT, CONF_KNX_DEFAULT_RATE_LIMIT
),
): vol.All(
selector.NumberSelector(
@ -831,9 +838,27 @@ class KNXOptionsFlow(KNXCommonFlow, OptionsFlow):
),
vol.Coerce(int),
),
vol.Required(
CONF_KNX_TELEGRAM_LOG_SIZE,
default=self.initial_data.get(
CONF_KNX_TELEGRAM_LOG_SIZE, TELEGRAM_LOG_DEFAULT
),
): vol.All(
selector.NumberSelector(
selector.NumberSelectorConfig(
min=0,
max=TELEGRAM_LOG_MAX,
mode=selector.NumberSelectorMode.BOX,
),
),
vol.Coerce(int),
),
}
return self.async_show_form(
step_id="communication_settings",
data_schema=vol.Schema(data_schema),
last_step=True,
description_placeholders={
"telegram_log_size_max": f"{TELEGRAM_LOG_MAX}",
},
)

View file

@ -52,6 +52,10 @@ CONF_KNX_DEFAULT_RATE_LIMIT: Final = 0
DEFAULT_ROUTING_IA: Final = "0.0.240"
CONF_KNX_TELEGRAM_LOG_SIZE: Final = "telegram_log_size"
TELEGRAM_LOG_DEFAULT: Final = 50
TELEGRAM_LOG_MAX: Final = 5000 # ~2 MB or ~5 hours of reasonable bus load
##
# Secure constants
##
@ -88,23 +92,26 @@ class KNXConfigEntryData(TypedDict, total=False):
connection_type: str
individual_address: str
local_ip: str | None
local_ip: str | None # not required
multicast_group: str
multicast_port: int
route_back: bool
route_back: bool # not required
host: str # only required for tunnelling
port: int # only required for tunnelling
tunnel_endpoint_ia: str | None
# KNX secure
user_id: int | None # not required
user_password: str | None # not required
device_authentication: str | None # not required
knxkeys_filename: str # not required
knxkeys_password: str # not required
backbone_key: str | None # not required
sync_latency_tolerance: int | None # not required
# OptionsFlow only
state_updater: bool
rate_limit: int
host: str
port: int
tunnel_endpoint_ia: str | None
user_id: int | None
user_password: str | None
device_authentication: str | None
knxkeys_filename: str
knxkeys_password: str
backbone_key: str | None
sync_latency_tolerance: int | None
# Integration only (not forwarded to xknx)
telegram_log_size: int # not required
class KNXBusMonitorMessage(TypedDict):

View file

@ -133,11 +133,13 @@
"title": "Communication settings",
"data": {
"state_updater": "State updater",
"rate_limit": "Rate limit"
"rate_limit": "Rate limit",
"telegram_log_size": "Telegram history limit"
},
"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.",
"rate_limit": "Maximum outgoing telegrams per second.\n`0` to disable limit. Recommended: 0 or 20 to 40"
"rate_limit": "Maximum outgoing telegrams per second.\n`0` to disable limit. Recommended: 0 or 20 to 40",
"telegram_log_size": "Telegrams to keep in memory for KNX panel group monitor. Maximum: {telegram_log_size_max}"
}
},
"connection_type": {

View file

@ -35,7 +35,13 @@ class TelegramDict(TypedDict):
class Telegrams:
"""Class to handle KNX telegrams."""
def __init__(self, hass: HomeAssistant, xknx: XKNX, project: KNXProject) -> None:
def __init__(
self,
hass: HomeAssistant,
xknx: XKNX,
project: KNXProject,
log_size: int,
) -> None:
"""Initialize Telegrams class."""
self.hass = hass
self.project = project
@ -46,7 +52,7 @@ class Telegrams:
match_for_outgoing=True,
)
)
self.recent_telegrams: deque[TelegramDict] = deque(maxlen=50)
self.recent_telegrams: deque[TelegramDict] = deque(maxlen=log_size)
async def _xknx_telegram_cb(self, telegram: Telegram) -> None:
"""Handle incoming and outgoing telegrams from xknx."""

View file

@ -37,6 +37,7 @@ from homeassistant.components.knx.const import (
CONF_KNX_SECURE_USER_ID,
CONF_KNX_SECURE_USER_PASSWORD,
CONF_KNX_STATE_UPDATER,
CONF_KNX_TELEGRAM_LOG_SIZE,
CONF_KNX_TUNNEL_ENDPOINT_IA,
CONF_KNX_TUNNELING,
CONF_KNX_TUNNELING_TCP,
@ -820,7 +821,6 @@ async def test_tunneling_setup_for_multiple_found_gateways(
CONF_PORT: 3675,
CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240",
CONF_KNX_ROUTE_BACK: False,
CONF_KNX_LOCAL_IP: None,
CONF_KNX_TUNNEL_ENDPOINT_IA: None,
CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None,
CONF_KNX_SECURE_USER_ID: None,
@ -900,9 +900,17 @@ async def test_form_with_automatic_connection_handling(
assert result2["type"] == FlowResultType.CREATE_ENTRY
assert result2["title"] == CONF_KNX_AUTOMATIC.capitalize()
assert result2["data"] == {
**DEFAULT_ENTRY_DATA,
# don't use **DEFAULT_ENTRY_DATA here to check for correct usage of defaults
CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC,
CONF_KNX_INDIVIDUAL_ADDRESS: "0.0.240",
CONF_KNX_LOCAL_IP: None,
CONF_KNX_MCAST_PORT: DEFAULT_MCAST_PORT,
CONF_KNX_MCAST_GRP: DEFAULT_MCAST_GRP,
CONF_KNX_RATE_LIMIT: 0,
CONF_KNX_ROUTE_BACK: False,
CONF_KNX_TUNNEL_ENDPOINT_IA: None,
CONF_KNX_STATE_UPDATER: True,
CONF_KNX_TELEGRAM_LOG_SIZE: 50,
}
knx_setup.assert_called_once()
@ -1202,6 +1210,7 @@ async def test_options_flow_connection_type(
CONF_KNX_SECURE_DEVICE_AUTHENTICATION: None,
CONF_KNX_SECURE_USER_ID: None,
CONF_KNX_SECURE_USER_PASSWORD: None,
CONF_KNX_TELEGRAM_LOG_SIZE: 50,
}
@ -1331,6 +1340,7 @@ async def test_options_communication_settings(
user_input={
CONF_KNX_STATE_UPDATER: False,
CONF_KNX_RATE_LIMIT: 40,
CONF_KNX_TELEGRAM_LOG_SIZE: 3000,
},
)
await hass.async_block_till_done()
@ -1341,6 +1351,7 @@ async def test_options_communication_settings(
CONF_KNX_CONNECTION_TYPE: CONF_KNX_AUTOMATIC,
CONF_KNX_STATE_UPDATER: False,
CONF_KNX_RATE_LIMIT: 40,
CONF_KNX_TELEGRAM_LOG_SIZE: 3000,
}
knx_setup.assert_called_once()