From 51248cf2c16b3a394ec6fc286f0120381dc784e3 Mon Sep 17 00:00:00 2001 From: Evgeny Goroshko Date: Tue, 2 Dec 2025 16:48:01 +0200 Subject: [PATCH] Add more parameters to trigger event data Like SNR, RSSI, Hops, Sender name and id --- .../meshtastic/aiomeshtastic/packet.py | 19 ++++++++ custom_components/meshtastic/api.py | 3 ++ custom_components/meshtastic/const.py | 27 +++++++++--- custom_components/meshtastic/logbook.py | 44 +++++++++++++++++++ 4 files changed, 87 insertions(+), 6 deletions(-) diff --git a/custom_components/meshtastic/aiomeshtastic/packet.py b/custom_components/meshtastic/aiomeshtastic/packet.py index be37311..8e39775 100644 --- a/custom_components/meshtastic/aiomeshtastic/packet.py +++ b/custom_components/meshtastic/aiomeshtastic/packet.py @@ -31,6 +31,25 @@ def rx_time(self) -> int | None: def rx_snr(self) -> float | None: return self.mesh_packet.rx_snr if self.mesh_packet is not None else None + @property + def rx_rssi(self) -> int | None: + return self.mesh_packet.rx_rssi if self.mesh_packet is not None else None + + @property + def hop_start(self) -> int | None: + return self.mesh_packet.hop_start if self.mesh_packet is not None else None + + @property + def hop_limit(self) -> int | None: + return self.mesh_packet.hop_limit if self.mesh_packet is not None else None + + @property + def hop_count(self) -> int | None: + """Calculate how many hops this packet traveled (hop_start - hop_limit).""" + if self.mesh_packet is not None and self.hop_start is not None and self.hop_limit is not None: + return self.hop_start - self.hop_limit + return None + @property def to_id(self) -> int | None: return self.mesh_packet.to if self.mesh_packet is not None else None diff --git a/custom_components/meshtastic/api.py b/custom_components/meshtastic/api.py index 29458a9..9507c20 100644 --- a/custom_components/meshtastic/api.py +++ b/custom_components/meshtastic/api.py @@ -317,6 +317,9 @@ async def _on_text_message(self, node: MeshNode, packet: Packet) -> None: "to": {"node": to_node, "channel": to_channel}, "gateway": self.get_own_node()["num"], "message": packet.app_payload, + "rx_snr": packet.rx_snr, + "rx_rssi": packet.rx_rssi, + "hop_count": packet.hop_count, }, ) diff --git a/custom_components/meshtastic/const.py b/custom_components/meshtastic/const.py index 017e6d1..f218fe2 100644 --- a/custom_components/meshtastic/const.py +++ b/custom_components/meshtastic/const.py @@ -92,12 +92,27 @@ class MeshtasticDomainEventType(enum.StrEnum): EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_MESSAGE: Final = "message" - - -class MeshtasticDomainEventData(TypedDict): - CONF_DEVICE_ID: str - CONF_ENTITY_ID: str | None - EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_MESSAGE: str +EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_CHANNEL_NAME: Final = "channel_name" +EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_SENDER_SHORT_NAME: Final = "sender_short_name" +EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_SENDER_LONG_NAME: Final = "sender_long_name" +EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_SENDER_ID: Final = "sender_id" +EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_RX_SNR: Final = "rx_snr" +EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_RX_RSSI: Final = "rx_rssi" +EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_HOP_COUNT: Final = "hop_count" + + +class MeshtasticDomainEventData(TypedDict, total=False): + CONF_DEVICE_ID: str # Required + CONF_TYPE: str # Required + CONF_ENTITY_ID: str | None # Optional + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_MESSAGE: str # Required + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_CHANNEL_NAME: str # Optional + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_SENDER_SHORT_NAME: str # Optional + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_SENDER_LONG_NAME: str # Optional + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_SENDER_ID: str # Optional + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_RX_SNR: float # Optional + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_RX_RSSI: int # Optional + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_HOP_COUNT: int # Optional # Primary user facing event diff --git a/custom_components/meshtastic/logbook.py b/custom_components/meshtastic/logbook.py index 1ecc479..b26d710 100644 --- a/custom_components/meshtastic/logbook.py +++ b/custom_components/meshtastic/logbook.py @@ -29,7 +29,14 @@ from .const import ( DOMAIN, EVENT_MESHTASTIC_DOMAIN_EVENT, + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_CHANNEL_NAME, + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_HOP_COUNT, EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_MESSAGE, + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_RX_RSSI, + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_RX_SNR, + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_SENDER_ID, + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_SENDER_LONG_NAME, + EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_SENDER_SHORT_NAME, EVENT_MESHTASTIC_DOMAIN_MESSAGE_LOG, EVENT_MESHTASTIC_MESSAGE_LOG_EVENT_DATA_ATTR_FROM_NAME, EVENT_MESHTASTIC_MESSAGE_LOG_EVENT_DATA_ATTR_MESSAGE, @@ -135,6 +142,9 @@ async def _on_text_message( config_entry_id, gateway_node_id, to, to_device ) message = data["message"] + rx_snr = data.get("rx_snr") + rx_rssi = data.get("rx_rssi") + hop_count = data.get("hop_count") if produce_domain_event: if from_device: @@ -143,21 +153,55 @@ async def _on_text_message( CONF_TYPE: MeshtasticDomainEventType.MESSAGE_SENT, EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_MESSAGE: message, } + # Add signal quality metrics if available + if rx_snr is not None: + domain_event_data[EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_RX_SNR] = rx_snr + if rx_rssi is not None: + domain_event_data[EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_RX_RSSI] = rx_rssi + if hop_count is not None: + domain_event_data[EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_HOP_COUNT] = hop_count if to_channel_entity_id: domain_event_data[CONF_ENTITY_ID] = to_channel_entity_id + # Add channel name if available + if channel_entity := entity_registry.entities.get(to_channel_entity_id): + domain_event_data[EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_CHANNEL_NAME] = ( + channel_entity.name or channel_entity.original_name + ) if to_dm_entity_id: domain_event_data[CONF_ENTITY_ID] = to_dm_entity_id hass.bus.async_fire(event_type=EVENT_MESHTASTIC_DOMAIN_EVENT, event_data=domain_event_data) if to_device: + # Get sender node info + sender_node_info = entry.runtime_data.client.get_node_info(int(from_node_id)) + domain_event_data: MeshtasticDomainEventData = { CONF_DEVICE_ID: to_device.id, CONF_TYPE: MeshtasticDomainEventType.MESSAGE_RECEIVED, EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_MESSAGE: message, } + # Add sender information if available + if sender_node_info: + domain_event_data[EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_SENDER_SHORT_NAME] = sender_node_info.short_name + domain_event_data[EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_SENDER_LONG_NAME] = sender_node_info.long_name + domain_event_data[EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_SENDER_ID] = sender_node_info.user_id + + # Add signal quality metrics if available + if rx_snr is not None: + domain_event_data[EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_RX_SNR] = rx_snr + if rx_rssi is not None: + domain_event_data[EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_RX_RSSI] = rx_rssi + if hop_count is not None: + domain_event_data[EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_HOP_COUNT] = hop_count + if to_channel_entity_id: domain_event_data[CONF_ENTITY_ID] = to_channel_entity_id + # Add channel name if available + if channel_entity := entity_registry.entities.get(to_channel_entity_id): + domain_event_data[EVENT_MESHTASTIC_DOMAIN_EVENT_DATA_ATTR_CHANNEL_NAME] = ( + channel_entity.name or channel_entity.original_name + ) if to_dm_entity_id: domain_event_data[CONF_ENTITY_ID] = to_dm_entity_id