From 25ffd25fda8ab71fd11a184b50b505f36108bd75 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Sun, 25 Jan 2026 18:34:45 +0300 Subject: [PATCH 1/2] fix(ble): handle BLEError with user-friendly messages Replace raw tracebacks with helpful error messages that explain: - What went wrong - Possible causes - How to fix it Covers all BLEError cases: - Device not found (BLE disabled, sleep mode, out of range) - Multiple devices found (need to specify which one) - Write errors (pairing PIN, Linux bluetooth group) - Read errors (device disconnected) Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- meshtastic/__main__.py | 61 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index 16854002..3795aa3f 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -1365,13 +1365,60 @@ def common(): print(f"Found: name='{x.name}' address='{x.address}'") meshtastic.util.our_exit("BLE scan finished", 0) elif args.ble: - client = BLEInterface( - args.ble if args.ble != "any" else None, - debugOut=logfile, - noProto=args.noproto, - noNodes=args.no_nodes, - timeout=args.timeout, - ) + try: + client = BLEInterface( + args.ble if args.ble != "any" else None, + debugOut=logfile, + noProto=args.noproto, + noNodes=args.no_nodes, + timeout=args.timeout, + ) + except BLEInterface.BLEError as e: + msg = str(e) + if "No Meshtastic BLE peripheral" in msg: + meshtastic.util.our_exit( + "BLE device not found.\n\n" + "Possible causes:\n" + " - Bluetooth is disabled on the Meshtastic device\n" + " - Device is in deep sleep mode\n" + " - Device is out of range\n\n" + "Try:\n" + " - Press the reset button on your device\n" + " - Run 'meshtastic --ble-scan' to see available devices", + 1, + ) + elif "More than one" in msg: + meshtastic.util.our_exit( + "Multiple Meshtastic BLE devices found.\n\n" + "Please specify which device to connect to:\n" + " - Run 'meshtastic --ble-scan' to list devices\n" + " - Use 'meshtastic --ble ' to connect", + 1, + ) + elif "Error writing BLE" in msg: + meshtastic.util.our_exit( + "Failed to write to BLE device.\n\n" + "Possible causes:\n" + " - Device requires pairing PIN (check device screen)\n" + " - On Linux: user not in 'bluetooth' group\n" + " - Connection was interrupted\n\n" + "Try:\n" + " - Restart Bluetooth on your computer\n" + " - Reset the Meshtastic device", + 1, + ) + elif "Error reading BLE" in msg: + meshtastic.util.our_exit( + "Failed to read from BLE device.\n\n" + "The device may have disconnected unexpectedly.\n\n" + "Try:\n" + " - Move closer to the device\n" + " - Reset the Meshtastic device\n" + " - Restart Bluetooth on your computer", + 1, + ) + else: + meshtastic.util.our_exit(f"BLE error: {e}", 1) elif args.host: try: if ":" in args.host: From 1fc2407c5e79dc348e0bdc8b2957d488b52b58d2 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Sun, 25 Jan 2026 18:53:46 +0300 Subject: [PATCH 2/2] fix(cli): add timeout error handling for serial connections Handle MeshInterface.MeshInterfaceError when device is rebooting or connection times out, with user-friendly error message. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- meshtastic/__main__.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index 3795aa3f..b04084a5 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -1474,6 +1474,23 @@ def common(): message += " Please close any applications or webpages that may be using the device and try again.\n" message += f"\nOriginal error: {ex}" meshtastic.util.our_exit(message) + except MeshInterface.MeshInterfaceError as ex: + msg = str(ex) + if "Timed out" in msg: + meshtastic.util.our_exit( + "Connection timed out.\n\n" + "Possible causes:\n" + " - Device is rebooting\n" + " - Device firmware is updating\n" + " - Serial connection was interrupted\n\n" + "Try:\n" + " - Wait a few seconds and try again\n" + " - Check if device is fully booted (LED patterns)\n" + " - Reconnect the USB cable", + 1, + ) + else: + meshtastic.util.our_exit(f"Connection error: {ex}", 1) if client.devPath is None: try: client = meshtastic.tcp_interface.TCPInterface(