Simple way to query connected USB devices info in Python?
Asked Answered
S

7

47

How can we query connected USB devices info in Python? I want to get UID Device Name (ex: SonyEricsson W660), path to device (ex: /dev/ttyACM0)

And also what would be the best Parameter out of above info to be used as identifying the device whenever it's connected again? (UID?)

I am working on Ubuntu 11.04.

ATM I have this code (using pyUSB)

busses = usb.busses()
for bus in busses:
  devices = bus.devices
  for dev in devices:
    print repr(dev)
    print "Device:", dev.filename
    print "  idVendor: %d (0x%04x)" % (dev.idVendor, dev.idVendor)
    print "  idProduct: %d (0x%04x)" % (dev.idProduct, dev.idProduct)
    print "Manufacturer:", dev.iManufacturer
    print "Serial:", dev.iSerialNumber
    print "Product:", dev.iProduct

The problem is I don't get desired output, will paste one example:

<usb.legacy.Device object at 0x1653990>
Device: 
  idVendor: 4046 (0x0fce)
  idProduct: 53411 (0xd0a3)
Manufacturer: 1
Serial: 3
Product: 2

First I don't get filename, it's most important to me. I am assuming it is the /dev/ttyACM0 etc part. Second, I guess there was some UID of every USB device, or I should use both Vendor or Product id?


Apparently I have some setup issues, I think I am using wrong USB Library. (using libusb0.1) ATM. That's why I get Device (dev.filename) string empty. If someone can please just tell that on what operating system he is using what USB Library and what version of PyUSB I think it will solve my problems.

Sands answered 13/11, 2011 at 7:43 Comment(2)
That might help https://mcmap.net/q/372156/-usb-devices-udev-and-d-bus .Superlative
Thanks lionbest, it might help in the long run, currently I am only concerned with querying them :)Sands
H
81

I can think of a quick code like this.

Since all USB ports can be accessed via /dev/bus/usb/< bus >/< device >

For the ID generated, even if you unplug the device and reattach it [ could be some other port ]. It will be the same.

import re
import subprocess
device_re = re.compile("Bus\s+(?P<bus>\d+)\s+Device\s+(?P<device>\d+).+ID\s(?P<id>\w+:\w+)\s(?P<tag>.+)$", re.I)
df = subprocess.check_output("lsusb")
devices = []
for i in df.split('\n'):
    if i:
        info = device_re.match(i)
        if info:
            dinfo = info.groupdict()
            dinfo['device'] = '/dev/bus/usb/%s/%s' % (dinfo.pop('bus'), dinfo.pop('device'))
            devices.append(dinfo)
print devices

Sample output here will be:

[
{'device': '/dev/bus/usb/001/009', 'tag': 'Apple, Inc. Optical USB Mouse [Mitsumi]', 'id': '05ac:0304'},
{'device': '/dev/bus/usb/001/001', 'tag': 'Linux Foundation 2.0 root hub', 'id': '1d6b:0002'},
{'device': '/dev/bus/usb/001/002', 'tag': 'Intel Corp. Integrated Rate Matching Hub', 'id': '8087:0020'},
{'device': '/dev/bus/usb/001/004', 'tag': 'Microdia ', 'id': '0c45:641d'}
]

Code Updated for Python 3

import re
import subprocess
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)
df = subprocess.check_output("lsusb")
devices = []
for i in df.split(b'\n'):
    if i:
        info = device_re.match(i)
        if info:
            dinfo = info.groupdict()
            dinfo['device'] = '/dev/bus/usb/%s/%s' % (dinfo.pop('bus'), dinfo.pop('device'))
            devices.append(dinfo)
            
print(devices)
Hath answered 25/11, 2011 at 6:50 Comment(12)
Awesome code, thanks. Works without depending on any external lib and gives me what I want. Exactly. Thanks.Sands
Mate can you please clarify one thing? I get following info: ` /dev/bus/usb/004/002 22b8:4902 Motorola PCS Triplet GSM Phone (AT)` Which is of the device I want to connect, but When I do with gammu I get this: gammu.ERR_DEVICEOPENERROR: {'Text': u'Error opening device. Unknown, busy or no permissions.', 'Code': 2, 'Where': 'Init'} Any idea (I have tried running script with sudo)?Sands
Hi, I have very little experience with Gammu, but can you pastebin your configuration file ? Let me have a look.Hath
Gammu is not so relevant here, I just want to know that when i plug my GSM Modem dmesg shows it gets registered as an ACM device and is attached to /dev/ttyACM0 port, now from that port the connection to Modem is flawless. however what lsubs returns is "/dev/bus...../002" and I can't connect to that device. Are you sure that using that device with longer path should work like using it from /dev/ttyACM0 path?Sands
This works great. Is there a way to get the actual device user-given name (e.g. 'Ben's USB') and not just the hardwired name (e.g. Apple, Inc. Optical USB Mouse [Mitsumi])?Gordan
great code @meson10, BTW it seems not considering "\n" and it prints all in the same line. How to fix this?Bilyeu
@FrancescoMantovani Hi, I had done this some 5 years ago and things might have changed since then. I can re-visit it, if you would want.Hath
@Hath yes, that would be great. I've tried to do that by myself but it didn't worked :( . I'm curious to see where I was wrongBilyeu
@FrancescoMantovani Can you share your code as a Gist or something? I will take it up from there.Hath
in windows ,I have tried with your code but lsusb is not supported. I am getting the issue and can u help me with solution of lsusb?Beutner
@Beutner does this answer help? https://mcmap.net/q/360104/-simple-way-to-query-connected-usb-devices-info-in-pythonPlaty
Quick improvement to match the sample output: print(*devices, sep='\n').Probationer
M
44

If you are working on windows, you can use pywin32 (old link: see update below).

I found an example here:

import win32com.client

wmi = win32com.client.GetObject ("winmgmts:")
for usb in wmi.InstancesOf ("Win32_USBHub"):
    print usb.DeviceID

Update Apr 2020:

'pywin32' release versions from 218 and up can be found here at github. Current version 227.

Moira answered 13/11, 2011 at 9:14 Comment(1)
Where do you get the documentation for the executable names (win32com.client.GetObject, wmi.InstancesOf ), field names (DeviceID), and string constants ("winmgts:" and "Win32_USBHub") in your program?Pentagram
R
5

For a system with legacy usb coming back and libusb-1.0, this approach will work to retrieve the various actual strings. I show the vendor and product as examples. It can cause some I/O, because it actually reads the info from the device (at least the first time, anyway.) Some devices don't provide this information, so the presumption that they do will throw an exception in that case; that's ok, so we pass.

import usb.core
import usb.backend.libusb1

busses = usb.busses()
for bus in busses:
    devices = bus.devices
    for dev in devices:
        if dev != None:
            try:
                xdev = usb.core.find(idVendor=dev.idVendor, idProduct=dev.idProduct)
                if xdev._manufacturer is None:
                    xdev._manufacturer = usb.util.get_string(xdev, xdev.iManufacturer)
                if xdev._product is None:
                    xdev._product = usb.util.get_string(xdev, xdev.iProduct)
                stx = '%6d %6d: '+str(xdev._manufacturer).strip()+' = '+str(xdev._product).strip()
                print stx % (dev.idVendor,dev.idProduct)
            except:
                pass
Resupine answered 3/11, 2014 at 23:35 Comment(0)
B
4

For linux, I wrote a script called find_port.py which you can find here: https://github.com/dhylands/usb-ser-mon/blob/master/usb_ser_mon/find_port.py

It uses pyudev to enumerate all tty devices, and can match on various attributes.

Use the --list option to show all of the know USB serial ports and their attributes. You can filter by VID, PID, serial number, or vendor name. Use --help to see the filtering options.

find_port.py prints the /dev/ttyXXX name rather than the /dev/usb/... name.

Bethune answered 27/5, 2015 at 14:52 Comment(0)
B
0

If you just need the name of the device here is a little hack which i wrote in bash. To run it in python you need the following snippet. Just replace $1 and $2 with Bus number and Device number eg 001 or 002.

import os
os.system("lsusb | grep \"Bus $1 Device $2\" | sed 's/\// /' | awk '{for(i=7;i<=NF;++i)print $i}'")

Alternately you can save it as a bash script and run it from there too. Just save it as a bash script like foo.sh make it executable.

#!/bin/bash
myvar=$(lsusb | grep "Bus $1 Device $2" | sed 's/\// /' | awk '{for(i=7;i<=NF;++i)print $i}')
echo $myvar

Then call it in python script as

import os
os.system('foo.sh')
Broadcasting answered 14/11, 2011 at 9:11 Comment(3)
I'll try it in a while and get backSands
Qasim simple lsubs gives me such info: Bus 002 Device 002: ID 413c:1003 Dell Computer Corp. Keyboard Hub Bus 002 Device 003: ID 413c:2010 Dell Computer Corp. Keyboard Which is missing one of most important things I need Actual file name onto which it's mapped (ex: /dev/ttyACM0)Sands
you can write a udev rule and make a symlink /dev/ttyACM<x> -> /dev/mydevice, if you are to package this - no problem, just put the rule into the package (make sure the filename will not clash or echo the rule into an exiting file in post-install script). Otherwise you can query it using usbfs (usually /proc/bus/usb/) or a udev command (not sure what it was called).Gruber
B
0

When I run your code, I get the following output for example.

<usb.Device object at 0xef38c0>
Device: 001
  idVendor: 7531 (0x1d6b)
  idProduct: 1 (0x0001)
Manufacturer: 3
Serial: 1
Product: 2

Noteworthy are that a) I have usb.Device objects whereas you have usb.legacy.Device objects, and b) I have device filenames.

Each usb.Bus has a dirname field and each usb.Device has the filename. As you can see, the filename is something like 001, and so is the dirname. You can combine these to get the bus file. For dirname=001 and filname=001, it should be something like /dev/bus/usb/001/001.

You should first, though figure out what this "usb.legacy" situation is. I'm running the latest version and I don't even have a legacy sub-module.

Finally, you should use the idVendor and idProduct fields to uniquely identify the device when it's plugged in.

Bradberry answered 18/11, 2011 at 13:17 Comment(5)
Thanks for your thorough analysis of my problem. Yes I was confused, I get Device parameter empty as you can see. Can you please tell me what USB library you are using? like libusb0.1 or OpenUSB etc? Also what operating system you were on? I downloaded the latest pyUSB from Github btw.Sands
Hello mate, can you please give me above info? It will greatly help me. Thanks.Sands
Sorry, I have limited internet access these days. I used pyusb 0.4.3 on an up-to-date Arch Linux system.Bradberry
I wasn't clear before; I'm using the latest stable version. There's also the 1.x series which is under development.Bradberry
Thanks mate :) but since I don't need to write or read on USB devices just query them and The correct answer requires me not to depend on pyUSB therefore I am using that ATM.Sands
D
0

For linux systems, here is a slight modification to the original posters code that works with python 3 and resolves the conversion between bytes and strings.

import re
import subprocess

device_re = re.compile("Bus\s+(?P<bus>\d+)\s+Device\s+(?P<device>\d+).+ID\s(?P<id>\w+:\w+)\s(?P<tag>.+)$", re.I)
df = str(subprocess.check_output("lsusb"), 'utf-8')
print(df)
devices = []
for i in df.split('\n'):
    if i:
        info = device_re.match(i)
        if info:
            dinfo = info.groupdict()
            dinfo['device'] = '/dev/bus/usb/%s/%s' % (dinfo.pop('bus'), dinfo.pop('device'))
            devices.append(dinfo)
            
print(devices)

Example output from this code:

[
{'id': '1d6b:0003', 'tag': 'Linux Foundation 3.0 root hub', 'device': '/dev/bus/usb/002/001'}, 
{'id': '0403:6011', 'tag': 'Future Technology Devices International, Ltd FT4232H Quad HS USB-UART/FIFO IC', 'device': '/dev/bus/usb/001/006'}, 
{'id': '1cbe:0003', 'tag': 'Luminary Micro Inc. Micro API', 'device': '/dev/bus/usb/001/018'}
]
Discussant answered 16/2, 2023 at 21:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.