fix(plugin): implement BasePlatformAdapter abstract methods and proper build_source event
release / test-and-release (push) Failing after 12s
release / test-and-release (push) Failing after 12s
This commit is contained in:
@@ -14,13 +14,24 @@ import time
|
|||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from gateway.platforms.base import BasePlatformAdapter, SendResult
|
from gateway.platforms.base import (
|
||||||
from gateway.platforms.event import MessageEvent, MessageType
|
BasePlatformAdapter,
|
||||||
|
MessageEvent,
|
||||||
|
MessageType,
|
||||||
|
SendResult,
|
||||||
|
)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# Allow importing and testing utility functions without gateway in environment
|
# Allow importing and testing utility functions without gateway in environment
|
||||||
class BasePlatformAdapter: # type: ignore[no-redef]
|
class BasePlatformAdapter: # type: ignore[no-redef]
|
||||||
def __init__(self, config: Any):
|
def __init__(self, config: Any, platform: Any = "meshtastic"):
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.platform = platform
|
||||||
|
|
||||||
|
def build_source(self, **kwargs):
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
async def handle_message(self, event: Any) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
class SendResult: # type: ignore[no-redef]
|
class SendResult: # type: ignore[no-redef]
|
||||||
def __init__(self, success: bool, message_id: Optional[str] = None, error: Optional[str] = None):
|
def __init__(self, success: bool, message_id: Optional[str] = None, error: Optional[str] = None):
|
||||||
@@ -107,7 +118,7 @@ class MeshtasticAdapter(BasePlatformAdapter):
|
|||||||
"""Hermes gateway adapter for Meshtastic LoRa radios over TCP."""
|
"""Hermes gateway adapter for Meshtastic LoRa radios over TCP."""
|
||||||
|
|
||||||
def __init__(self, config: Any):
|
def __init__(self, config: Any):
|
||||||
super().__init__(config)
|
super().__init__(config, platform="meshtastic")
|
||||||
self.extra = getattr(config, "extra", {}) or {}
|
self.extra = getattr(config, "extra", {}) or {}
|
||||||
self.host = self.extra.get("host", "meshtastic.local")
|
self.host = self.extra.get("host", "meshtastic.local")
|
||||||
self.port = int(self.extra.get("port", 4403))
|
self.port = int(self.extra.get("port", 4403))
|
||||||
@@ -191,24 +202,32 @@ class MeshtasticAdapter(BasePlatformAdapter):
|
|||||||
to_id = packet.get("toId")
|
to_id = packet.get("toId")
|
||||||
is_dm = to_id != "^all" and packet.get("to") == my_node_num
|
is_dm = to_id != "^all" and packet.get("to") == my_node_num
|
||||||
chat_id = from_id if is_dm else f"channel_{ch}"
|
chat_id = from_id if is_dm else f"channel_{ch}"
|
||||||
|
chat_type = "dm" if is_dm else "channel"
|
||||||
|
|
||||||
rx_snr = packet.get("rxSnr", 0.0)
|
rx_snr = packet.get("rxSnr", 0.0)
|
||||||
hop_limit = packet.get("hopLimit", 0)
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"[Meshtastic] Inbound message from %s on ch=%s (is_dm=%s, SNR=%.1fdB): %s",
|
"[Meshtastic] Inbound message from %s on ch=%s (is_dm=%s, SNR=%.1fdB): %s",
|
||||||
from_id, ch, is_dm, rx_snr, text
|
from_id, ch, is_dm, rx_snr, text
|
||||||
)
|
)
|
||||||
|
|
||||||
# Build Hermes MessageEvent
|
# Build Hermes SessionSource and MessageEvent
|
||||||
event = MessageEvent(
|
source = self.build_source(
|
||||||
platform="meshtastic",
|
|
||||||
message_type=MessageType.TEXT,
|
|
||||||
text=text,
|
|
||||||
sender_id=from_id,
|
|
||||||
sender_name=from_id,
|
|
||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
|
chat_name=f"Meshtastic {chat_id}",
|
||||||
|
chat_type=chat_type,
|
||||||
|
user_id=from_id,
|
||||||
|
user_name=from_id,
|
||||||
|
message_id=str(packet.get("id", int(time.time()))),
|
||||||
|
)
|
||||||
|
|
||||||
|
event = MessageEvent(
|
||||||
|
text=text,
|
||||||
|
message_type=MessageType.TEXT,
|
||||||
|
user_id=from_id,
|
||||||
|
user_name=from_id,
|
||||||
|
source=source,
|
||||||
|
raw_message=packet,
|
||||||
message_id=str(packet.get("id", int(time.time()))),
|
message_id=str(packet.get("id", int(time.time()))),
|
||||||
raw_event=packet,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if self._loop and self._loop.is_running():
|
if self._loop and self._loop.is_running():
|
||||||
@@ -217,18 +236,18 @@ class MeshtasticAdapter(BasePlatformAdapter):
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.exception("[Meshtastic] Error processing received packet: %s", exc)
|
logger.exception("[Meshtastic] Error processing received packet: %s", exc)
|
||||||
|
|
||||||
async def send_message(
|
async def send(
|
||||||
self,
|
self,
|
||||||
chat_id: str,
|
chat_id: str,
|
||||||
message: str,
|
content: str,
|
||||||
reply_to_id: Optional[str] = None,
|
reply_to: Optional[str] = None,
|
||||||
**kwargs: Any,
|
metadata: Optional[Dict[str, Any]] = None,
|
||||||
) -> SendResult:
|
) -> SendResult:
|
||||||
"""Send a message out over LoRa."""
|
"""Send a message out over LoRa (implements BasePlatformAdapter.send)."""
|
||||||
if not self._connected or not self._iface:
|
if not self._connected or not self._iface:
|
||||||
return SendResult(success=False, error="Meshtastic radio not connected")
|
return SendResult(success=False, error="Meshtastic radio not connected")
|
||||||
|
|
||||||
clean_text = strip_markdown(message)
|
clean_text = strip_markdown(content)
|
||||||
chunks = chunk_text(clean_text, MAX_LORA_MESSAGE_LENGTH)
|
chunks = chunk_text(clean_text, MAX_LORA_MESSAGE_LENGTH)
|
||||||
|
|
||||||
# Destination: if chat_id starts with '!', it is a direct node ID
|
# Destination: if chat_id starts with '!', it is a direct node ID
|
||||||
@@ -265,6 +284,14 @@ class MeshtasticAdapter(BasePlatformAdapter):
|
|||||||
logger.exception("[Meshtastic] Failed to send message: %s", exc)
|
logger.exception("[Meshtastic] Failed to send message: %s", exc)
|
||||||
return SendResult(success=False, error=str(exc))
|
return SendResult(success=False, error=str(exc))
|
||||||
|
|
||||||
|
async def get_chat_info(self, chat_id: str) -> Dict[str, Any]:
|
||||||
|
"""Get chat/channel info (implements BasePlatformAdapter.get_chat_info)."""
|
||||||
|
is_dm = chat_id.startswith("!")
|
||||||
|
return {
|
||||||
|
"name": f"Node {chat_id}" if is_dm else f"Channel {chat_id}",
|
||||||
|
"type": "dm" if is_dm else "channel",
|
||||||
|
}
|
||||||
|
|
||||||
async def disconnect(self) -> None:
|
async def disconnect(self) -> None:
|
||||||
"""Disconnect and clean up resources."""
|
"""Disconnect and clean up resources."""
|
||||||
self._running = False
|
self._running = False
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "hermes-meshtastic"
|
name = "hermes-meshtastic"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
description = "Meshtastic LoRa mesh gateway adapter plugin for Hermes Agent"
|
description = "Meshtastic LoRa mesh gateway adapter plugin for Hermes Agent"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
|||||||
Reference in New Issue
Block a user