How can I listen for 'usb device inserted' events in Linux, in Python?
Asked Answered
L

4

39

I'd like to write a Python script for Amarok in Linux to automatically copy the stackoverflow podcast to my player. When I plug in the player, it would mount the drive, copy any pending podcasts, and eject the player. How can I listen for the "plugged in" event? I have looked through hald but couldn't find a good example.

Lightface answered 22/1, 2009 at 14:9 Comment(1)
Wow, can you believe we used to listen to podcasts on devices that did not have an always-on broadband connection to the internet?Lightface
P
59

Update: As said in comments, Hal is not supported in recent distributions, the standard now is udev, Here is a small example that makes use of glib loop and udev, I keep the Hal version for historical reasons.

This is basically the example in the pyudev documentation, adapted to work with older versions, and with the glib loop, notice that the filter should be customized for your specific needing:

import glib

from pyudev import Context, Monitor

try:
    from pyudev.glib import MonitorObserver

    def device_event(observer, device):
        print 'event {0} on device {1}'.format(device.action, device)
except:
    from pyudev.glib import GUDevMonitorObserver as MonitorObserver

    def device_event(observer, action, device):
        print 'event {0} on device {1}'.format(action, device)

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()

Old version with Hal and d-bus:

You can use D-Bus bindings and listen to DeviceAdded and DeviceRemoved signals. You will have to check the capabilities of the Added device in order to select the storage devices only.

Here is a small example, you can remove the comments and try it.

import dbus
import gobject

class DeviceAddedListener:
    def __init__(self):

You need to connect to Hal Manager using the System Bus.

        self.bus = dbus.SystemBus()
        self.hal_manager_obj = self.bus.get_object(
                                              "org.freedesktop.Hal", 
                                              "/org/freedesktop/Hal/Manager")
        self.hal_manager = dbus.Interface(self.hal_manager_obj,
                                          "org.freedesktop.Hal.Manager")

And you need to connect a listener to the signals you are interested on, in this case DeviceAdded.

        self.hal_manager.connect_to_signal("DeviceAdded", self._filter)

I'm using a filter based on capabilities. It will accept any volume and will call do_something with if, you can read Hal documentation to find the more suitable queries for your needs, or more information about the properties of the Hal devices.

    def _filter(self, udi):
        device_obj = self.bus.get_object ("org.freedesktop.Hal", udi)
        device = dbus.Interface(device_obj, "org.freedesktop.Hal.Device")

        if device.QueryCapability("volume"):
            return self.do_something(device)

Example function that shows some information about the volume:

     def do_something(self, volume):
        device_file = volume.GetProperty("block.device")
        label = volume.GetProperty("volume.label")
        fstype = volume.GetProperty("volume.fstype")
        mounted = volume.GetProperty("volume.is_mounted")
        mount_point = volume.GetProperty("volume.mount_point")
        try:
            size = volume.GetProperty("volume.size")
        except:
            size = 0

        print "New storage device detectec:"
        print "  device_file: %s" % device_file
        print "  label: %s" % label
        print "  fstype: %s" % fstype
        if mounted:
            print "  mount_point: %s" % mount_point
        else:
            print "  not mounted"
        print "  size: %s (%.2fGB)" % (size, float(size) / 1024**3)

if __name__ == '__main__':
    from dbus.mainloop.glib import DBusGMainLoop
    DBusGMainLoop(set_as_default=True)
    loop = gobject.MainLoop()
    DeviceAddedListener()
    loop.run()
Pacheco answered 22/1, 2009 at 22:27 Comment(3)
I'm getting an error with this code: dbus.exception.DBusException: org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.Hal was not provided by any .service files. Do you think you could help me?Beverle
@EtienneLepage-Lepitre Hal is deprecated now, generally this solution won't work any more :(Longo
Added solution using udev.Pacheco
A
10

Here is a solution in 5 lines.

import pyudev

context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
monitor.filter_by(subsystem='usb')

for device in iter(monitor.poll, None):
    if device.action == 'add':
        print('{} connected'.format(device))
        # do something very interesting here.

Save this to a file say usb_monitor.py, run python monitor.py. Plug any usb and it will print device details

→ python usb_monitor.py 
Device('/sys/devices/pci0000:00/0000:00:14.0/usb1/1-6/1-6:1.0') connected
Device('/sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0') connected

Tested on Python 3.5 with pyudev==0.21.0.

Aluminiferous answered 2/9, 2016 at 9:13 Comment(0)
C
7

I haven't tried writing such a program myself, however I've just looked at the following two links (thanks Google!), which I think will be of help:

In particular, read about the org.freedesktop.Hal.Manager interface, and its DeviceAdded and DeviceRemoved events. :-)

Hope this helps!

Chouest answered 22/1, 2009 at 14:59 Comment(0)
S
4

I think D-Bus would work as Chris mentioned, but if you're using KDE4, you might use the Solid framework in a manner similar to the KDE4 "New Device Notifier" applet.

The C++ source for that applet is here, which shows how to use Solid to detect new devices. Use PyKDE4 for Python bindings to these libraries, as shown here.

Superposition answered 22/1, 2009 at 15:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.