Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions custom_components/meshtastic/aiomeshtastic/packet.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions custom_components/meshtastic/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
)

Expand Down
27 changes: 21 additions & 6 deletions custom_components/meshtastic/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 44 additions & 0 deletions custom_components/meshtastic/logbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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:
Expand All @@ -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

Expand Down