Ubuntu Linux – How to listen to USB port events
by Riley MacDonald, September 16, 2017

I wanted to automate certain tasks when connecting or disconnecting devices via my Linux based machine USB port. This post explains how I achieved this using python.

Problem:
I have a USB hub on my desk with a 4K HDMI port for an external monitor. Out of the box everything worked; but I needed use the LXRandR Display Settings GUI to configure the monitor settings every time I connected or disconnected the USB hub. I wanted this to happen automatically when I connected or disconnected the USB hub.

Approach:
Using pyudev you can listen to specific hardware events and add a callback to execute any task you wish. Great, I should be able to write a script to set the appropriate monitor settings and run it during the event callbacks. I should also be able to run the script at boot to run as a daemon.

Prerequisites:
Install pyudev using pip.

1
pip install pyudev

Implementation:
Create a new python file/project and import the pyudev libraries:

1
2
3
4
5
6
7
#!/usr/bin/env python
 
import glib
import os
 
from pyudev import Context, Monitor
from pyudev.glib import MonitorObserver

Instantiate and configure pyudev.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Create a device database connection
context = Context()
 
# Create a synchronous device event monitor
monitor = Monitor.from_netlink(context)
 
# Optional: apply a filter for the desired hardware
monitor.filter_by(subsystem='usb')
 
# Create a asynchronous observer for device events
observer = MonitorObserver(monitor)
 
# Connect the observer to a method of your choosing (in this case device_event())
observer.connect('device-event', device_event)
 
# Start the monitor
monitor.start()
 
# Run an endless loop to monitor events
glib.MainLoop().run()

Define the method connected to the observer above:

1
2
3
4
5
def device_event(observer, device):
    if device.action.decode("string-escape") == "add":
        print 'device attached. do some work.', device
    elif device.action.decode("string-escape") == "remove":
        print 'device removed. do some work.', device

At this point you should be able to run the script and watch the log while connecting / disconnecting USB devices from your machine. You should see the print outs containing the device information. Using this data copy the device.device_path unique identifier and create a variable. Using the variable you can write an if statement to ensure your desired functionality only happens for that device.

1
2
3
4
5
usb_hub_hdmi = 'usb3/3-1/3-1.1/3-1.1:1.0'
 
# Check for your device
if usb_hub_hdmi in device.device_path.decode("string-escape"):
    os.system('echo do some work')

Summary
Here’s the entire script in action. The source is available here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env python
 
import glib
import os
 
from pyudev import Context, Monitor
from pyudev.glib import MonitorObserver
 
usb_hub_hdmi = 'usb3/3-1/3-1.1/3-1.1:1.0'
 
 
def device_event(observer, device):
    update_monitor_settings(device)
 
 
def update_monitor_settings(device):
    if device.action.decode("string-escape") == "add":
        if usb_hub_hdmi in device.device_path.decode("string-escape"):
            print 'set monitor on'
            os.system('xrandr --output DP-1 --auto --output eDP-1 --mode 1920x1080 --rate 59.93 --left-of DP-1')
    elif device.action.decode("string-escape") == "remove":
        if usb_hub_hdmi in device.device_path.decode("string-escape"):
            os.system('xrandr --output DP-1 --off --output eDP-1 --mode 1920x1080 --rate 59.93 --same-as DP-1')
            print 'set monitor off'
 
 
context = Context()
monitor = Monitor.from_netlink(context)
 
monitor.filter_by(subsystem='usb')
observer = MonitorObserver(monitor)
 
observer.connect('device-event', device_event)
monitor.start()
 
glib.MainLoop().run()
Open the comment form

Leave a comment:

Comments will be reviewed before they are posted.

User Comments:

Be the first to leave a comment on this post!