Add Phase 0 host-bridge: relay text across Meshtastic, MeshCore, Reticulum
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
"""Meshtastic adapter — wraps the official `meshtastic` Python client.
|
||||
|
||||
Uses the same pubsub pattern as meshtastic/python's own examples
|
||||
(examples/replymessage.py, examples/tcp_pubsub_send_and_receive.py):
|
||||
subscribe to the "meshtastic.receive" topic, filter for text packets,
|
||||
send via MeshInterface.sendText().
|
||||
"""
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
from pubsub import pub
|
||||
|
||||
|
||||
class MeshtasticAdapter:
|
||||
NETWORK = "meshtastic"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
on_message: Callable[[str, str, str], None],
|
||||
port: str | None = None,
|
||||
host: str | None = None,
|
||||
):
|
||||
"""port: serial device path (e.g. /dev/ttyUSB0). host: TCP hostname/IP.
|
||||
Exactly one of port/host should be set; if neither is set, the
|
||||
underlying library auto-detects a serial device.
|
||||
"""
|
||||
self._on_message = on_message
|
||||
self._port = port
|
||||
self._host = host
|
||||
self._iface = None
|
||||
|
||||
def connect(self) -> None:
|
||||
import meshtastic.serial_interface
|
||||
import meshtastic.tcp_interface
|
||||
|
||||
pub.subscribe(self._handle_receive, "meshtastic.receive")
|
||||
|
||||
if self._host:
|
||||
self._iface = meshtastic.tcp_interface.TCPInterface(hostname=self._host, timeout=10)
|
||||
else:
|
||||
self._iface = meshtastic.serial_interface.SerialInterface(devPath=self._port, timeout=10)
|
||||
|
||||
def _handle_receive(self, packet: dict, interface) -> None: # noqa: ANN001
|
||||
text = packet.get("decoded", {}).get("text")
|
||||
if not text:
|
||||
return
|
||||
sender = packet.get("fromId") or str(packet.get("from"))
|
||||
self._on_message(self.NETWORK, sender, text)
|
||||
|
||||
def send(self, text: str) -> None:
|
||||
if self._iface is None:
|
||||
raise RuntimeError("MeshtasticAdapter.send() called before connect()")
|
||||
self._iface.sendText(text)
|
||||
|
||||
def close(self) -> None:
|
||||
if self._iface is not None:
|
||||
self._iface.close()
|
||||
Reference in New Issue
Block a user