#!/usr/bin/env python3
"""Toggle the USB relay via serial: ./relay 1|0|id [serial_port].

Without an explicit port the relay board is found automatically: every
USB serial port is probed with the ID command until one answers.
"""
import sys
import serial
import serial.tools.list_ports


def find_relay_port() -> str | None:
    """Find the relay board by probing CH340 ports with the ID command."""
    candidates = [
        p.device
        for p in serial.tools.list_ports.comports()
        if "1A86:7523" in (p.hwid or "")
    ]
    for dev in candidates:
        try:
            s = serial.Serial()
            s.port = dev
            s.baudrate = 115200
            s.timeout = 1
            s.dtr = False
            s.rts = False
            s.open()
            try:
                s.readline()  # gooi boot-banner weg
                s.reset_input_buffer()
                s.write(b"?")
                s.flush()
                if s.readline().decode(errors="replace").startswith("ID "):
                    return dev
            finally:
                s.close()
        except serial.SerialException:
            continue
    return None

def main() -> int:
    if len(sys.argv) not in (2, 3) or sys.argv[1] not in ("0", "1", "id"):
        print(f"Usage: {sys.argv[0]} 0|1|id [serial_port]", file=sys.stderr)
        return 1
    command = sys.argv[1]
    serial_cmd = "?" if command == "id" else command
    if len(sys.argv) == 3:
        port = sys.argv[2]
    else:
        port = find_relay_port()
        if port is None:
            print("ERROR: no relay module found on any USB serial port",
                  file=sys.stderr)
            return 1
        print(f"Using {port}", file=sys.stderr)
    try:
        # dtr/rts expliciet uit vóór open: voorkomt (of beperkt) de CH340
        # auto-reset (DTR->RST) bij het openen van de poort. pyserial 3.5
        # accepteert geen dtr=/rts= kwargs in de constructor, maar de
        # states worden wél geëerd door open().
        s = serial.Serial()
        s.port = port
        s.baudrate = 115200
        s.timeout = 2
        s.dtr = False
        s.rts = False
        s.open()
    except serial.SerialException as exc:
        print(f"ERROR: cannot open {port}: {exc}", file=sys.stderr)
        return 1
    try:
        # Als het board toch reset bij open: wacht op de boot-banner en
        # gooi die weg, zodat de reply niet vervuild raakt. Geen banner
        # binnen 2 s (timeout, readline geeft b"") = geen reset: door.
        s.readline()
        s.reset_input_buffer()
        s.write(serial_cmd.encode())
        s.flush()
        reply = s.readline().decode(errors="replace").strip()
    finally:
        s.close()
    if reply in ("ON", "OFF") or (command == "id" and reply.startswith("ID ")):
        print(reply)
        return 0
    print(f"ERROR: no valid reply from board (got {reply!r})", file=sys.stderr)
    return 1

if __name__ == "__main__":
    sys.exit(main())
