From a8a1527a8482634536cc015b84ecfe5023105c42 Mon Sep 17 00:00:00 2001 From: pdxlocations Date: Sat, 25 Jul 2026 21:58:52 -0700 Subject: [PATCH] Add interface connection check utility and refactor connection logic --- contact/ui/contact_ui.py | 7 ++----- contact/utilities/interfaces.py | 26 ++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/contact/ui/contact_ui.py b/contact/ui/contact_ui.py index a291908..6ee7b74 100644 --- a/contact/ui/contact_ui.py +++ b/contact/ui/contact_ui.py @@ -20,6 +20,7 @@ from contact.utilities.utils import parse_protobuf from contact.ui.colors import get_color from contact.utilities.db_handler import get_name_from_database, update_node_info_in_db, is_chat_archived, load_older_messages from contact.utilities.input_handlers import get_list_input +from contact.utilities.interfaces import interface_is_connected from contact.utilities.i18n import t from contact.utilities.emoji_utils import normalize_message_text import contact.ui.default_config as config @@ -385,11 +386,7 @@ def main_ui(stdscr: curses.window) -> None: while True: interface = interface_state.interface - if ( - hasattr(interface, "stream") - and interface.stream is None - and not ui_state.reconnect_attempted - ): + if not interface_is_connected(interface) and not ui_state.reconnect_attempted: ui_state.reconnect_attempted = True status_win = show_connection_status(stdscr, "Disconnected", "Trying to reconnect…") try: diff --git a/contact/utilities/interfaces.py b/contact/utilities/interfaces.py index 9b6d53d..a6952df 100644 --- a/contact/utilities/interfaces.py +++ b/contact/utilities/interfaces.py @@ -3,6 +3,28 @@ import time import meshtastic.serial_interface, meshtastic.tcp_interface, meshtastic.ble_interface +def interface_is_connected(interface) -> bool: + """Return the transport-independent connection state for an interface. + + TCPInterface deliberately leaves ``stream`` set to ``None`` and uses its + ``socket`` instead. Checking only ``stream`` therefore treats every TCP + connection (including meshtasticd) as disconnected. + """ + if interface is None: + return False + + connected = getattr(interface, "isConnected", None) + if hasattr(connected, "is_set"): + return connected.is_set() + if isinstance(connected, bool): + return connected + if hasattr(interface, "socket"): + return getattr(interface, "socket", None) is not None + if hasattr(interface, "stream"): + return getattr(interface, "stream", None) is not None + return getattr(interface, "localNode", None) is not None + + def initialize_interface(args): try: @@ -50,9 +72,9 @@ def reconnect_interface(args, attempts: int = 20, delay_seconds: float = 1.0): for attempt in range(attempts): try: interface = initialize_interface(args) - if interface is not None and getattr(interface, "localNode", None) is not None and getattr( + if interface_is_connected(interface) and getattr(interface, "localNode", None) is not None and getattr( interface.localNode, "localConfig", None - ) is not None and (not hasattr(interface, "stream") or interface.stream is not None): + ) is not None: return interface last_error = RuntimeError("interface did not complete connection setup") try: