#!/usr/bin/env python

"""Program which detects USB serial ports.
This program will search for a USB serial port using a search criteria.
In its simplest form, you can use the -l (--list) option to list all of
the detected serial ports.
You can also add the following filters:
--vid 2341      Will only match devices with a Vendor ID of 2341.
--pid 0001      Will only match devices with a Product ID of 0001
--vendor Micro  Will only match devices whose vendor name starts with Micro
--seral  00123  Will only match devices whose serial number stats with 00123
If you use -l or --list then detailed information about all of the matches
will be printed. If you don't use -l (or --list) then only the name of
the device will be printed (i.e. /dev/ttyACM0). This is useful in scripts
where you want to pass the name of the serial port into a utiity to open.
"""

from __future__ import print_function

import pyudev
import sys
import argparse


def is_usb_serial(device, vid=None, pid=None, vendor=None, serial=None, *args,
                  **kwargs):
    """Checks device to see if its a USB Serial device.
    The caller already filters on the subsystem being 'tty'.
    If serial_num or vendor is provided, then it will further check to
    see if the serial number and vendor of the device also matches.
    """
    if 'ID_VENDOR' not in device.properties:
        return False
    if vid is not None:
        if device.properties['ID_VENDOR_ID'] != vid:
            return False
    if pid is not None:
        if device.properties['ID_MODEL_ID'] != pid:
            return False
    if vendor is not None:
        if 'ID_VENDOR' not in device.properties:
            return False
        if not device.properties['ID_VENDOR'].startswith(vendor):
            return False
    if serial is not None:
        if 'ID_SERIAL_SHORT' not in device.properties:
            return False
        if not device.properties['ID_SERIAL_SHORT'].startswith(serial):
            return False
    return True


def extra_info(device):
    extra_items = []
    if 'ID_VENDOR' in device.properties:
        extra_items.append("vendor '%s'" % device.properties['ID_VENDOR'])
    if 'ID_SERIAL_SHORT' in device.properties:
        extra_items.append("serial '%s'" % device.properties['ID_SERIAL_SHORT'])
    if extra_items:
        return ' with ' + ' '.join(extra_items)
    return ''


def list_devices(vid=None, pid=None, vendor=None, serial=None, *args,
                 **kwargs):
    devs = []
    context = pyudev.Context()
    for device in context.list_devices(subsystem='tty'):
        if is_usb_serial(device, vid=vid, pid=pid, vendor=vendor,
                         serial=serial):
            devs.append([device.properties['ID_VENDOR_ID'], device.properties['ID_MODEL_ID'],
                         extra_info(device), device.device_node])
    return devs


def main():
    """The main program."""
    parser = argparse.ArgumentParser(
        prog="find-port.py",
        usage="%(prog)s [options] [command]",
        description="Find the /dev/tty port for a USB Serial devices",
    )
    parser.add_argument(
        "-l", "--list",
        dest="list",
        action="store_true",
        help="List USB Serial devices currently connected"
    )
    parser.add_argument(
        "-s", "--serial",
        dest="serial",
        help="Only show devices with the indicated serial number",
        default=None,
    )
    parser.add_argument(
        "-n", "--vendor",
        dest="vendor",
        help="Only show devices with the indicated vendor name",
        default=None
    )
    parser.add_argument(
        "--pid",
        dest="pid",
        action="store",
        help="Only show device with indicated PID",
        default=None
    )
    parser.add_argument(
        "-v", "--verbose",
        dest="verbose",
        action="store_true",
        help="Turn on verbose messages",
        default=False
    )
    parser.add_argument(
        "--vid",
        dest="vid",
        action="store",
        help="Only show device with indicated VID",
        default=None
    )
    args = parser.parse_args(sys.argv[1:])

    if args.verbose:
        print('pyudev version = %s' % pyudev.__version__)

    if args.list:
        '''Print all USB Serial devices'''
        devices = list_devices(**vars(args))
        for device in devices:
            print('USB Serial Device {}:{}{} found @{}'.format(*device))
        if len(devices) == 0:
            print('No USB Serial devices detected.')
        return

    context = pyudev.Context()
    for device in context.list_devices(subsystem='tty'):
        if is_usb_serial(device, **vars(args)):
            print(device.device_node)
            return
    sys.exit(1)


if __name__ == "__main__":
    main()