import asyncio
from bleak import BleakClient, BleakScanner
import sys
RX_CHAR_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
TX_CHAR_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"
async def run_terminal(client):
    def handle_rx(sender, data):
        print(f"<< {data.decode(errors='ignore')}", end="")
    await asyncio.sleep(0.5)
    await client.start_notify(TX_CHAR_UUID, handle_rx)
    await asyncio.sleep(0.5)
    print("Ready for input. Ctrl+C to quit.")
    try:
        while True:
            msg = await asyncio.to_thread(input, "")
            msg_to_send = msg if len(msg) > 3 else msg + " " * (4 - len(msg))
            await client.write_gatt_char(RX_CHAR_UUID, msg_to_send.encode())

    except KeyboardInterrupt:
        print("\n[!] Keyboard interrupt received, exiting program.")
        return
    except Exception as e:
        raise
async def connect_and_run(device):
    print(f"Connecting to {device.name} ({device.address})...")
    try:
        async with BleakClient(device.address) as client:
            print(f"Connected to {device.name}")
            await run_terminal(client)
    except Exception as e:
        print(f"[!] Disconnected or error: {e}")
        return False
    return True
async def main():
    try:
        while True:
            print("Scanning for BLE devices...")
            devices = await BleakScanner.discover()
            if not devices:
                print("[!] No BLE devices found. Retrying...")
                await asyncio.sleep(2)
                continue
            for i, d in enumerate(devices):
                print(f"{i}: {d.name or 'Unknown'}")
            try:
                idx = int(input("Select device by number: "))
                if idx < 0 or idx >= len(devices):
                    print("[!] Invalid selection.")
                    continue
            except ValueError:
                print("[!] Please enter a valid number.")
                continue
            device = devices[idx]
            connected = await connect_and_run(device)
            if not connected:
                rescan = input("Rescan? [y/N]: ").strip().lower()
                if rescan != 'y':
                    break
            else:
                break
    except KeyboardInterrupt:
        print("\n[!] Keyboard interrupt received, exiting program.")
        sys.exit(0)
if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        pass
