astutus.usb package

USB products are troublesome. The physical USB device that you purchase consists of various physical and logical USB components. The same physical components are used by various manufacturers to implement a large array of devices. In many, if not most cases, the physical components do not have stable, unique identifiers, such as serial numbers. Although, the cost to provide this capabilities is mere pennies in 2020, the large array of USB devices that have already been purchased will never have this feature. Instead, a more convoluted approach must be taken.

Each USB device is identified by a unique busnum and devnum while in operation. Unfortunately, the same device is likely to have a different busnum and devnum if it is unplugged and plugged into a different port, or even the same one at a later time. In addition, if a computer is rebooted, the devnum is likely to change, and the busnum may also change, depending on the exact start up time. The order can even be effected by the exact versions of drivers associated with particular devices.

In addition, each USB device is typically associated with a location in the directory structure of /sys/devices under the Unix family of operating system. AFAIK, with most computers this is connected to the devices in the PCI bus system. Unfortunately, the labeling of this hierachy is not stable upon reboot. The parts of the system that are initialized early are likely to be stably labeled, but due to timing of various start up activities, the lower reaches of the PCI bus tree are likely to be inconsistently labeled. So the /sys/devices hierachary can not be used to uniquely identify USB devices.

Next, the same physical device can have a different logical identity depending on what it is plugged into. Take a device that is a 3.1 USB hub. If it is plugged into a USB 1.1 port, the logical 3.0 port won’t be present. Or if it is plugged into a USB 3.0 port, but a USB 1.1 device is plugged into it, it will show up as USB 3.0 logical device, a USB 2.0 logical device, and a USB 1.1. logical device.

Despite these challenges, it is often possible to uniquely identify a particular USB device in a system based on how it connects with the computer of interest and how different physical devices are connected.

This module provides the capabilities to identify particular nodes for visualization as well as selection of nodes as needed for device control.

Submodules

astutus.usb.device_aliases module

Device aliases allow the USB device tree to identify a node with a label associated with the physical device.

This module provides the capabilities to identify particular nodes for visualization as well as selection of nodes as needed for device control.

The selectors implement here are influenced by XPath.

Conceptually, the directory hierarch for sys/devices could be mapped into an XML document. Then XPath could be directly applied to this. This approach might be used in a second implementation of this system. But for know, the initial implementation will be driven by the needs to identify particular USB relays plugged into the current computer.

In looking at the tree there are two distinct ilk of nodes:

  • PCI nodes identified by vendor and device

  • USB noded identified by idVendor and idProduct

The nodes are identified as:

  • pci({vendor}:{device})

  • usb({idVendor}:{idProduct})

class astutus.usb.device_aliases.DeviceAliases(*, filepath: str)

Bases: dict

The device aliases class provides a dictionary between selectors and aliases for a node.

The aliases should have the following attributes:

  • “color”

  • “label”

  • “order”

  • “priority”

Each of these attributes are string values.

Color should be as defined using css compatible color names or #rrggbb values.

The order and priority values should be two character values, such as “44”.

Aliases with higher priority are selected over other potential aliases of lesser priority for a particular node.

Order is used in sorting the nodes in the USB tree for display.

Here is structure of the raw aliases file:

{
"pci(0x1002:0x5a19)/pci(0x1b21:0x1042)":
    {
        "name": "Come up with good name",
        "color": "#5a1ddd",
        "description_template": "ASM1042 Alias",
        "order": "00",
        "pattern": "pci(0x1002:0x5a19)/pci(0x1b21:0x1042)",
        "priority": 50
    },
}
find(nodepath: str)[typing.Dict]

Find all aliases that partially match the nodepath.

find_highest_priority(nodepath: str) → Dict

Find the alias with the highest priority.

get(pattern: str) → Dict

Get the alias that exactly matches the pattern

static parse_raw_aliases(raw_aliases: Dict) → Dict
static read_raw_from_json(filepath: str) → Dict
to_javascript() → str

Provides the device aliases as a Javascript object with a small set of methods.

write(filepath=None) → None

Writes the aliases to filepath

if filepath is None, then write to original path

static write_raw_as_json(filepath: str, raw_aliases: Dict) → None
astutus.usb.device_aliases.find_all_pci_paths(value: str) → List[str]

Find all that terminate with a node that matches the value.

The value is something like usb(1a86:7523) or pci(0x1002:0x5a19)

astutus.usb.device_aliases.find_node_paths(value: str) → List[str]
astutus.usb.device_aliases.matches_as_node(dirpath: str, ilk: str, vendor: str, device: str) → bool
astutus.usb.device_aliases.matches_as_pci_node(dirpath: str, vendor: str, device: str) → bool
astutus.usb.device_aliases.matches_as_usb_node(dirpath: str, vendor: str, product: str) → bool

astutus.usb.device_classifier module

class astutus.usb.device_classifier.DeviceClassifier(*, expire_seconds)

Bases: object

augment_device_data(dirpath: str, device_data: Dict[str, str], extra_fields: List[str])
augument_from_lsusb(device_data: Dict[str, str]) → None
get_device_data(dirpath: str, extra_fields: List[str] = []) → Dict[str, str]
get_ilk(dirpath: str) → str
get_label(device_path: str, rules: List[Dict], formatting_data: Dict[str, str] = []) → str
get_pci_device_info(dirpath: str) → Dict[str, str]
get_template(device_path: str, rules: List[Dict]) → str
make_key(short_key)
static robust_format_map(template: str, device_data: Dict[str, str], formatting_data: Dict[str, str])
static rule_applies(rule, device_data) → bool
astutus.usb.device_classifier.get_logitech_unifying_receiver_input_type(dirpath: str, device_data: Dict[str, str])

astutus.usb.device_configurations module

class astutus.usb.device_configurations.DeviceConfiguration(config, command_runner=<function run_cmd>)

Bases: dict

find_description_template(dirpath)
find_styler(dirpath)
find_tty()
generate_description(dirpath, data)
get_color(dirpath)

Get color in #rrggbb format suitable for HTML input control.

get_name()
property idx
property name
property stylers
class astutus.usb.device_configurations.DeviceConfigurations(filepath=None, command_runner=<function run_cmd>)

Bases: object

find_configuration(data)
find_device_info(slot: str) → dict
find_pci_configuration(data)
find_usb_configuration(data)
find_usb_configuration_for_node(node)
get_item(key)
get_item_from_nodeid(nodeid)
items()
static make_generic_other_configuration(data, command_runner=<function run_cmd>)
static make_generic_usb_configuration(data, command_runner=<function run_cmd>)
static make_pci_configuration(data, command_runner=<function run_cmd>)
read_from_json(filepath=None)
to_javascript()
write_as_json(filepath)

astutus.usb.lcus_1_usb_relay module

class astutus.usb.lcus_1_usb_relay.SerialPort(*, tty_dev, baudrate, bytesize=8, parity='N', stopbits=1)

Bases: object

close()
open()
write(bytes: bytearray)
class astutus.usb.lcus_1_usb_relay.UsbRelayLcus1(tty_dev)

Bases: object

OFF_SIGNAL = bytearray(b'\xa0\x01\x01\xa2')
ON_SIGNAL = bytearray(b'\xa0\x01\x00\xa1')
turn_off()
turn_on()

astutus.usb.node module

class astutus.usb.node.DeviceNode(data: Dict, config: astutus.usb.device_configurations.DeviceConfiguration, alias: Dict, cls_order: str)

Bases: dict

Base class for particular device nodes

property colorized_node_label_for_terminal
key() → str
verbose = False
class astutus.usb.node.OtherDeviceNodeData(*, data: Dict, config: astutus.usb.device_configurations.DeviceConfiguration, alias: Dict)

Bases: astutus.usb.node.DeviceNode

cls_order = '05'
classmethod extract_data(dirpath: str) → Dict
static node_id_from_data(data: Dict) → str
class astutus.usb.node.PciDeviceNodeData(*, data: Dict, config: astutus.usb.device_configurations.DeviceConfiguration, alias: Dict)

Bases: astutus.usb.node.DeviceNode

cls_order = '10'
classmethod extract_data(dirpath: str) → Dict
static node_id_from_data(data: Dict) → str
class astutus.usb.node.UsbDeviceNodeData(*, data: Dict, config: astutus.usb.device_configurations.DeviceConfiguration, alias: Dict)

Bases: astutus.usb.node.DeviceNode

cls_order = '00'
classmethod extract_data(dirpath)
static node_id_from_data(data: Dict) → str
astutus.usb.node.node_id_for_dirpath(dirpath: str) → str
astutus.usb.node.parse_value(value: str) → Tuple[str, str, str]

Given a value break it down by the ilk of node (usb or pci), the vendor, and the device or product.

astutus.usb.node.robust_format_map(template, data)

astutus.usb.node_data module

class astutus.usb.node_data.NodeDataSearcher

Bases: object

get_node_data(dirpath)

astutus.usb.tree module

class astutus.usb.tree.UsbDeviceTree(basepath, device_aliases_filepath, device_configurations_filepath=None)

Bases: object

Provides a tree of USB devices and the PCI buses and devices that connect them.

The representation can provide aliases that are meaningful in terms of physical USB devices and ports on the computer.

The representation can be color-coded as desired by the user.

To speed up rendering, this code implements caching of intermediate calculations and lazy evaluation.

assemble_bare_tree()
assemble_tree(*, basepath, tree_dirpaths, data_by_dirpath, ilk_by_dirpath, device_aliases, device_configurations)
static augment_data_by_nodepath(tree_dirpaths, data_by_dirpath)
execute_tree_cmd(*, verbose=False, node_ids=[], show_tree=False, to_dict=False, to_tree_dirpaths=False, to_bare_tree=False)
find_data_for_paths(ilk_by_dirpath, dirpaths)
static find_tree_dirpaths(basepath, device_paths)
formulate_data_as_table(data)
static formulate_data_consisely(data)
static generate_alias_json_snippet(*, node_ids, tree_dirpaths, data_by_dirpath, device_configurations)
get_aliases()
get_data_by_dirpath()
get_device_configurations()
get_device_info_map()
get_ilk_by_dirpath()
get_tree_as_dict()
get_tree_dirpaths()
get_treelib_tree()
get_usb_device_dirpath()
static sanitize_for_html(data)
static walk_basepath_for_usb(basepath)
astutus.usb.tree.get_data_for_dirpath(ilk, dirpath, pci_device_info)
astutus.usb.tree.get_node_data(data, device_config, alias)
astutus.usb.tree.key_by_node_data_key(node)
astutus.usb.tree.main(raw_args=None)
astutus.usb.tree.parse_arguments(raw_args)

astutus.usb.usb_impl module

astutus.usb.usb_impl.extract_specified_data(dirpath: str, filenames: List[str]) → Dict
astutus.usb.usb_impl.find_busnum_and_devnum_for_sys_device(pci_path: str) → Tuple[int, int]
astutus.usb.usb_impl.find_ilk_for_dirpath(dirpath)
astutus.usb.usb_impl.find_paths_for_vendor_and_product(vendor_id: str, product_id: str) → List[str]
astutus.usb.usb_impl.find_tty_description_from_pci_path(pci_path: str) → Tuple[str, str, str, str, str, str]

returns tty, busnum, devnum, vendorid, productid, description

astutus.usb.usb_impl.find_tty_from_pci_path(pci_path)
astutus.usb.usb_impl.find_vendor_info_from_busnum_and_devnum(busnum: int, devnum: int) → Tuple[str, str, str]

returns vendorid, productid, description