A Practical Approach to Querying Connected USB Device Information in Python

Dec 01, 2025 · Programming · 11 views · 7.8

Keywords: Python | USB device query | lsusb command

Abstract: This article provides a comprehensive guide on querying connected USB device information in Python, focusing on a cross-platform solution using the lsusb command. It begins by addressing common issues with libraries like pyUSB, such as missing device filenames, and presents optimized code that utilizes the subprocess module to parse system command output. Through regular expression matching, the method extracts device paths, vendor IDs, product IDs, and descriptions. The discussion also covers selecting optimal parameters for unique device identification and includes supplementary approaches for Windows platforms. All code examples are rewritten with detailed explanations to ensure clarity and practical applicability for developers.

Technical Background and Challenges in USB Device Querying

Querying connected USB device information in Python is a fundamental requirement for many hardware interaction projects. Users often need to retrieve unique identifiers, device paths (e.g., /dev/ttyACM0), and detailed descriptions. However, direct use of low-level USB libraries like pyUSB can lead to compatibility issues, especially across different operating systems or library versions. For instance, with libusb0.1, the dev.filename attribute may return an empty string, preventing access to critical device path information. Such problems typically stem from mismatches between the library and system drivers, necessitating more robust alternatives.

Cross-Platform Solution Using the lsusb Command

A reliable method involves using Python's subprocess module to invoke the system's lsusb command, which is widely available on Linux and Unix-like systems. The following code demonstrates how to parse lsusb output to extract the required information:

import re
import subprocess

# Define a regex pattern to match lsusb output format
device_re = re.compile(
    b"Bus\\s+(?P<bus>\\d+)\\s+Device\\s+(?P<device>\\d+).+ID\\s(?P<id>\\w+:\\w+)\\s(?P<tag>.+)$",
    re.I
)

# Execute lsusb command and capture output
df = subprocess.check_output("lsusb")
devices = []

# Parse output line by line
for line in df.split(b'\\n'):
    if line:
        match = device_re.match(line)
        if match:
            info = match.groupdict()
            # Construct device path, e.g., /dev/bus/usb/001/009
            bus_num = info.pop('bus').decode()
            dev_num = info.pop('device').decode()
            info['device_path'] = f'/dev/bus/usb/{bus_num}/{dev_num}'
            # Convert other fields to strings
            info['id'] = info['id'].decode()
            info['tag'] = info['tag'].decode()
            devices.append(info)

# Output results
for dev in devices:
    print(f"Device Path: {dev['device_path']}")
    print(f"ID: {dev['id']}")
    print(f"Description: {dev['tag']}")
    print()

The core of this code lies in the regular expression design, which precisely matches the structure of lsusb output: bus number, device number, device ID (in vendor:product format), and description tag. By parsing this information, we can construct the full device path, which is particularly important for accessing serial devices. For example, output might include: {'device_path': '/dev/bus/usb/001/009', 'id': '05ac:0304', 'tag': 'Apple, Inc. Optical USB Mouse [Mitsumi]'}. This approach avoids direct dependency on USB libraries, enhancing compatibility across different Linux distributions.

Strategy for Selecting Unique Device Identifiers

In dynamic environments where USB devices may be reconnected to different ports, choosing a stable parameter for unique identification is crucial. Based on the above code, the device ID (i.e., the combination of vendor and product IDs) is generally the best choice, as it is determined by inherent hardware characteristics and does not change with connection ports. For instance, ID 05ac:0304 corresponds to an Apple optical mouse, and this ID remains constant regardless of which USB bus the device is connected to. In contrast, the device path (e.g., /dev/bus/usb/001/009) may vary with port changes, making it unsuitable for persistent identification. In practice, the device ID can be combined with a serial number (if available) to distinguish individual devices of the same model.

Supplementary Approach for Windows Platforms

For Windows users, the pywin32 library can be employed to query USB devices via WMI (Windows Management Instrumentation). Here is a basic example:

import win32com.client

wmi = win32com.client.GetObject("winmgmts:")
for usb_device in wmi.InstancesOf("Win32_USBHub"):
    print(f"Device ID: {usb_device.DeviceID}")
    # Further parse DeviceID for detailed information

This method leverages Windows' built-in management interfaces but may be less intuitive than the lsusb approach on Linux. Developers should note version compatibility of pywin32, and it is advisable to obtain the latest version from the official GitHub repository. In cross-platform projects, the query method can be dynamically selected based on the operating system to improve code adaptability.

Practical Recommendations and Common Issues

When implementing USB device querying, it is recommended to prioritize cross-platform tools like lsusb, as they reduce dependency on specific libraries. If pyUSB must be used, ensure the correct version of libusb (e.g., libusb-1.0) is installed and check system permissions (e.g., whether the user belongs to the dialout group to access serial devices). For issues with missing device filenames, this is often due to the library's failure to correctly map device nodes; falling back to system commands is a safer alternative. Additionally, handling byte strings in regular expressions (e.g., using b'...') ensures compatibility with Python 3 and avoids encoding errors. In complex scenarios, udev rules (Linux) or Device Manager (Windows) can be integrated to obtain richer device attributes.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.