1 """KNX Telegram handler."""
3 from __future__
import annotations
5 from collections
import deque
6 from typing
import Final, TypedDict
9 from xknx.dpt
import DPTArray, DPTBase, DPTBinary
10 from xknx.dpt.dpt
import DPTComplexData, DPTEnumData
11 from xknx.exceptions
import XKNXException
12 from xknx.telegram
import Telegram, TelegramDirection
13 from xknx.telegram.apci
import GroupValueResponse, GroupValueWrite
21 from .const
import DOMAIN
22 from .project
import KNXProject
24 STORAGE_VERSION: Final = 1
25 STORAGE_KEY: Final = f
"{DOMAIN}/telegrams_history.json"
28 SIGNAL_KNX_TELEGRAM: SignalType[Telegram, TelegramDict] =
SignalType(
"knx_telegram")
32 """Decoded payload value and metadata."""
38 value: bool | str | int | float | dict[str, str | int | float | bool] |
None
42 """Represent a Telegram as a dict."""
48 payload: int | tuple[int, ...] |
None
56 """Class to handle KNX telegrams."""
65 """Initialize Telegrams class."""
69 hass, STORAGE_VERSION, STORAGE_KEY
72 xknx.telegram_queue.register_telegram_received_cb(
74 match_for_outgoing=
True,
77 self.recent_telegrams: deque[TelegramDict] = deque(maxlen=log_size)
81 """Load history from store."""
84 if self.recent_telegrams.maxlen == 0:
87 for telegram
in telegrams:
89 if isinstance(telegram[
"payload"], list):
90 telegram[
"payload"] =
tuple(telegram[
"payload"])
91 self.recent_telegrams.extend(telegrams)
93 t[
"destination"]: t
for t
in telegrams
if t[
"payload"]
is not None
97 """Save history to store."""
98 if self.recent_telegrams:
102 """Handle incoming and outgoing telegrams from xknx."""
104 self.recent_telegrams.append(telegram_dict)
105 if telegram_dict[
"payload"]
is not None:
107 self.
last_ga_telegramslast_ga_telegrams[telegram_dict[
"destination"]] = telegram_dict
111 """Convert a Telegram to a dict."""
113 payload_data: int | tuple[int, ...] |
None =
None
119 ga_info := self.
projectproject.group_addresses.get(
120 f
"{telegram.destination_address}"
123 dst_name = ga_info.name
126 device := self.
projectproject.devices.get(f
"{telegram.source_address}")
128 src_name = f
"{device['manufacturer_name']} {device['name']}"
129 elif telegram.direction
is TelegramDirection.OUTGOING:
130 src_name =
"Home Assistant"
132 if isinstance(telegram.payload, (GroupValueWrite, GroupValueResponse)):
133 payload_data = telegram.payload.value.value
135 if telegram.decoded_data
is not None:
136 transcoder = telegram.decoded_data.transcoder
140 destination=f
"{telegram.destination_address}",
141 destination_name=dst_name,
142 direction=telegram.direction.value,
143 dpt_main=transcoder.dpt_main_number
if transcoder
is not None else None,
144 dpt_sub=transcoder.dpt_sub_number
if transcoder
is not None else None,
145 dpt_name=transcoder.value_type
if transcoder
is not None else None,
146 payload=payload_data,
147 source=f
"{telegram.source_address}",
148 source_name=src_name,
149 telegramtype=telegram.payload.__class__.__name__,
150 timestamp=dt_util.now().isoformat(),
151 unit=transcoder.unit
if transcoder
is not None else None,
157 value: bool | float | str | DPTComplexData | DPTEnumData,
158 ) -> bool | str | int | float | dict[str, str | int | float | bool]:
159 """Return a serializable representation of decoded data."""
160 if isinstance(value, DPTComplexData):
161 return value.as_dict()
162 if isinstance(value, DPTEnumData):
163 return value.name.lower()
168 payload: DPTArray | DPTBinary, transcoder: type[DPTBase]
169 ) -> DecodedTelegramPayload:
170 """Decode the payload of a KNX telegram with custom transcoder."""
172 value = transcoder.from_knx(payload)
173 except XKNXException:
174 value =
"Error decoding value"
179 dpt_main=transcoder.dpt_main_number,
180 dpt_sub=transcoder.dpt_sub_number,
181 dpt_name=transcoder.value_type,
182 unit=transcoder.unit,
None __init__(self, HomeAssistant hass, XKNX xknx, KNXProject project, int log_size)
TelegramDict telegram_to_dict(self, Telegram telegram)
None _xknx_telegram_cb(self, Telegram telegram)
bool|str|int|float|dict[str, str|int|float|bool] _serializable_decoded_data(bool|float|str|DPTComplexData|DPTEnumData value)
DecodedTelegramPayload decode_telegram_payload(DPTArray|DPTBinary payload, type[DPTBase] transcoder)
None async_load(HomeAssistant hass)
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)
None async_remove(HomeAssistant hass, str intent_type)
None async_save(self, _T data)