16

I'd like to start my music player (Clementine) when my bluetooth headset connects to my computer. How do I detect the bluetooth device connecting so I can run a script to start the player?

6 Answers 6

12

I didn't like the polling approach, so I did some digging on bluez and DBus. I ended up writing the following script:

#!/usr/bin/python

import dbus
from dbus.mainloop.glib import DBusGMainLoop
import gobject

import subprocess

# ID of the device we care about
DEV_ID = '00_1D_54_AB_DC_72'

dbus_loop = DBusGMainLoop()
bus = dbus.SystemBus(mainloop=dbus_loop)

# Figure out the path to the headset
man = bus.get_object('org.bluez', '/')
iface = dbus.Interface(man, 'org.bluez.Manager')
adapterPath = iface.DefaultAdapter()

headset = bus.get_object('org.bluez', adapterPath + '/dev_' + DEV_ID)
    # ^^^ I'm not sure if that's kosher. But it works.

def cb(iface=None, mbr=None, path=None):

    if ("org.bluez.Headset" == iface and path.find(DEV_ID) > -1):
        print 'iface: %s' % iface
        print 'mbr: %s' % mbr
        print 'path: %s' % path
        print "\n"
        print "matched"

        if mbr == "Connected":
            subprocess.call(["clementine", "--play"])
            print 'conn'

        elif mbr == "Disconnected":
            subprocess.call(["clementine", "--stop"])
            print 'dconn'

headset.connect_to_signal("Connected", cb, interface_keyword='iface', member_keyword='mbr', path_keyword='path')
headset.connect_to_signal("Disconnected", cb, interface_keyword='iface', member_keyword='mbr', path_keyword='path')

loop = gobject.MainLoop()
loop.run()
2
  • Of course Bluez, and PulseAudio operate through polling DBus. As long as your headset is the default adapter, that will work fine in most cases. Make sure you also connect PulseAudio to the headset if you want to hear something. Thank you for sharing your solution :)
    – Takkat
    May 22, 2012 at 6:15
  • that's great if you know the DEV_ID prior to connection .. but what if you want to be notified of all connection events?
    – pstanton
    Nov 29, 2017 at 4:31
4

To discover a successfully established Bluetooth connection we can run

sdptool browse xx:xx:xx:xx:xx:xx

By this the SDB connection will be tested for a connection to the given MAC address. It may take considerable time until browsing times out with an error like

Failed to connect to SDP server on 00:0C:78:4F:B6:B5: Host is down

We don't know the exact purpose of your script, but most likely you wish to play audio via Clementine when a headset was connected.

Then we could just see whether there is a Bluetooth audio sink with

pacmd list-sinks | grep xx_xx_xx_xx_xx_xx

Where xx_xx_xx_xx_xx_xx is the MAC address (: needs to be replaced with _). The output will then tell you whether there is a Bluetooth audio sink available or nothing if not.

See this answer on how to switch audio to this sink.


Stream2ip

With stream2ip we can define a shell command or a script to run after a connection was established. There also is an option to automatically start a supported media player after a connection was established:

enter image description here

Stream2ip will also try to reconnect the currently running playback stream to the Bluetooth audio device in case the connection was interrupted.

2
  • Thanks for your answer. You're suggesting that I should poll with sdptool browse <device-id> until I get a 0 return code and then kick off my script, right? Is there a way to do it without polling?
    – Erigami
    May 17, 2012 at 20:23
  • Sdptool is slow. I'd go for pulseaudio. You need a loop because we don't know when your device is there.
    – Takkat
    May 17, 2012 at 20:38
2

@Erigami Your answer helped a lot but to make it work I'd to do some changes. I'm using ubuntu 14.04.

#!/usr/bin/python

import dbus
from dbus.mainloop.glib import DBusGMainLoop
import gobject

import subprocess

# ID of the device we care about
DEV_ID = 'CC:C3:EA:A5:16:90'.replace(":", "_")

dbus_loop = DBusGMainLoop()
bus = dbus.SystemBus(mainloop=dbus_loop)

# Figure out the path to the headset
man = bus.get_object('org.bluez', '/')
iface = dbus.Interface(man, 'org.bluez.Manager')
adapterPath = iface.DefaultAdapter()

print(adapterPath + '/dev_' + DEV_ID)
headset = bus.get_object('org.bluez', adapterPath + '/dev_' + DEV_ID)
# ^^^ I'm not sure if that's kosher. But it works.

def cb(*args, **kwargs):
    is_connected = args[-1]
    if isinstance(is_connected, dbus.Boolean) and is_connected:
        print("Connected")
    elif isinstance(is_connected, dbus.Boolean) and not is_connected:
        print("Disconnected")

headset.connect_to_signal("PropertyChanged", cb, interface_keyword='iface', member_keyword='mbr', path_keyword='path')

loop = gobject.MainLoop()
loop.run()

Still if this does not work then use and monitor system dbus.

dbus-monitor --system

d-feet can be used further. It is GUI tool to watch dbus objects.

1
  • 2
    If you want to improve another answer, please suggest an edit to it and don’t create a new answer. Aug 14, 2016 at 14:53
1

Here is another example for monitoring all Bluetooth devices. It does not need to specify a specific MAC address. This approach makes the xinput setting persistent even when login/out, suspend/wake and connect/dis-connect your bluetooth device.

I have a Thinkpad compact Bluetooth Keyboard, and I wish to run an xinput command whenever the keyboard is connected to adjust the speed of trackpoint. Here are the steps.

  1. Download code from Github bluetooth-ruunner. Credits given to here who first wrote this for Raspberry Pi. Modify the following section of the code to run your custom comamnds.

    subprocess.call(['xinput', 'set-prop',
                     'ThinkPad Compact Bluetooth Keyboard with TrackPoint',
                     'Device Accel Constant Deceleration', '0.6'])
    

    In my case, this is equivalent to calling from the terminal.

    $ xinput set-prop 'ThinkPad Compact Bluetooth Keyboard with TrackPoint' 'Device Accel Constant Deceleration' 0.6
    
  2. Save the modification. Try running your scripts by

    $ python bluetooth-runner.py
    

    Connect and disconnect your Bluethooth device. You should see the corresponding message printed on screen.

  3. Now, make your file executable and copy it in one of the directory in your $PATH, say ~/bin/.

    $ chmod +x bluetooth-runner.py
    $ mkdir ~/bin # if you dont have it yet
    $ cp bluetooth-runner.py ~/bin
    
  4. Now, make sure you can run the script from anywhere in the terminal (make sure it is in your search path).

  5. Fire up the Startup Applications from the ubuntu menu. Add your scripts to the startup.

    Add startup applications

  6. Now, there is only one problem left, at the time when you login, the scripts might not catch the very first bluetooth event. This is because your bluetooth device might be connected before your script is initialized in the background.

    To solve this, add your custom command directly in Startup Applications. In my case, it is the follow command:

     xinput set-prop 'ThinkPad Compact Bluetooth Keyboard with TrackPoint' 'Device Accel Constant Deceleration' 0.6
    

And now you shall be able to enjoy your Bluetooth device with Ubuntu.

1
  • python script didn't monitoring bluetooth connections. Jun 12, 2018 at 18:51
0

You write "when your headset connects to your computer". How does it do that, automatically? When you have to trigger it manually, you might as well make it a script and then run your script after the connection has been established. This is what I did to set the default output device to my bluetooth receiver (so I can change the volume with the hardware keys):

bluetooth-connect && pactl set-default-sink bluez_sink.0C_A6_94_9A_37_4D

Where bluetooth-connect looks like this: https://github.com/sblask/dotfiles/blob/c39d37ad67947b358b4a079cb41ae6f9e4a081d8/.bin/bluetooth-connect.symlink It assumes everything has been paired and is ready to connect. You find the MAC address in blueman or by running pacmd list-sinks | grep -e 'name:' -e 'index' when the bluetooth device is connected. You would want run bluetooth-connect && your-script. your-script will only be run when the connection was established successfully.

0

If none of the previous solutions worked for you for any reason, I wrote a Python script using pydbus package. dbus-python as in @Erigami, @Hitul and @Yixing answers was giving me hard times, so I switched to this one.

https://gist.github.com/altbrace/52ae1783b31257021520673fadb95b6e

This particular script changes the sound profile of the Bluetooth device to mSBC, but you can alter the subprocess call to do whatever you wish. If so, you can also remove the time.sleep() call.

Add this to background execution as described by @Yixing or however else.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .