29

Background: I'm using my bluetooth headset as audio output. I managed to get it working by the long list of instructions on BluetoothHeadset community documentation, and I have automated the process of activating the headset as default audio output into a script, thanks to another question.

However, since I use the bluetooth headset with both my phone and computer (and the headset doesn't support two input connections) in order for the phone not to "steal" the connection when handset is turned on, I force the headset into a discovery mode when connecting to the computer (phone gets to connect to it automatically).

So even though the headset is paired ok and would in "normal" scenario autoconnect, I have to always use the little bluetooth icon in the notification area to actually connect to my device (see screenshot).

What I want to avoid: This GUI for connecting to a known and paired bluetooth device:

Connecting to Bluetooth headset using icon

What I want instead: I'd want to make the bluetooth do exactly what the clicking the connect item in the GUI does, only by using command line. I want to use command line so I can make a single keypress shortcut for the action, and would't need to navigate the GUI every time I want to establish a connection to the device.

The question: How can I attempt to connect to a specific, known and paired bluetooth device from command line?

Further question: How do I tell if the connection was successful or not?

5 Answers5

13

Bluetooth daemon

In the default installation a daemon (bluetoothd) runs in the background (run from the file /etc/init.d/bluetooth). This daemon takes care on recognizing and connecting to known bluetooth devices and may be cofigured with configuration files in /etc/bluetooth. For autoconneting a headset the following line in audio.conf should be uncommented (remove #):

AutoConnect=true

To restart the daemon type sudo /etc/init.d/bluetooth restart.

Remark: Using the command line tool sudo hcitool cc <MAC-Adress> did not lead to a stable connection to a known device in the test environment here when the daemon was running.


DBus

In order to connect a disconnected but physically present and paired headset we can use D-Bus from a script. Here's an example in python:

#!/usr/bin/python
# Toggles headset connection

import dbus from dbus.mainloop.glib import DBusGMainLoop

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

#Get dbus interface for headset manager = bus.get_object('org.bluez', '/') iface_m = dbus.Interface(manager, 'org.bluez.Manager') adapterPath = iface_m.DefaultAdapter() adapter = bus.get_object('org.bluez', adapterPath) iface_a = dbus.Interface(adapter, 'org.bluez.Adapter') devicePath = iface_a.ListDevices()[0] # assuming first device device = bus.get_object('org.bluez', devicePath) iface_h = dbus.Interface(device, 'org.bluez.Headset')

#Check state of connection connected = iface_h.IsConnected() print 'Toggling connection. Please wait'

toggle connection

if not connected: try: iface_h.Connect() print 'Connecting: ', devicePath except: print 'Device not found' else: iface_h.Disconnect() print 'Disconnecting: ', devicePath

In case we have more than one Bluetooth device we will have to adapt the devicePath appropriately, of course. The example above will connect a Headset. Change the interface to a different protocol for any other service (e.g. AudioSink).


Pulseaudio

If you know the MAC adress of your Bluetooth device you can connect it as an output sink for pulseaudio by:

pacmd set-default-sink bluez_sink.xx_xx_xx_xx_xx_xx

Where xx_xx_xx_xx_xx_xx is the MAC address (replace ':' by '_' for pulseaudio to recognize it).

See also this answer for more details.

Takkat
  • 142,284
  • I do know the address, and I replaced the xx with it. I only get Sink bluez_sink.xx_xx_xx_xx_xx_xx does not exist. Tried both uppercase and lowercase. – Ilari Kajaste Jun 10 '11 at 08:56
  • 1
    Yes, pulseaudio-module-bluetooth is isntalle. No, nothing matching bt or blue is listed in pacmd list-sinks. (It reports only 1 sink available.) – Ilari Kajaste Jun 10 '11 at 09:29
  • 2
    So it's not recognized. This is a prerequisite to be able to connect by commandline. Try restarting BT or try restarting pulsaudio. I've not yet found out why it's sometimes not detetcted. – Takkat Jun 10 '11 at 09:38
  • Huh? So it can be in a state that it's possible to connect from GUI, but not from CLI? – Ilari Kajaste Jun 10 '11 at 09:46
  • Hm, there might be a misunderstanding here. The sink does appear in pacmd list-sinks after I've connected to the bluetooth headset via the bluetooth icon GUI. What I need is a way to establish that bluetooth connection. – Ilari Kajaste Jun 10 '11 at 09:50
  • It should connect automatically at the Bluez level (but does not always). Audio sink however is not connected automatically. – Takkat Jun 10 '11 at 09:55
  • 1
    @Takkat Oh, yes, good point. My bad! I'm using the headset in discovery more, so it won't autoconnect. I edited the question to reflect this. Sorry for leading you down to a wrong path. – Ilari Kajaste Jun 10 '11 at 10:44
  • @Ilari Kajaste: np - maybe my answers helps in other aspects ;) – Takkat Jun 10 '11 at 10:52
  • @Ilari Kajast: did some testing with hcitool with little success. Hope to give you some hints where to play with - see edit. – Takkat Jul 14 '11 at 07:36
  • sudo hcitool cc MAC didn't work for me either. I'll try to play around it a bit more, but at least on first read I didn't find any option for hcitool that would fit my case. – Ilari Kajaste Jul 14 '11 at 16:59
  • This script looked promising but I guess it is out of date now, the IsConnected attr isn't there for me resulting in: dbus.exceptions.DBusException: org.freedesktop.DBus.Error.UnknownMethod: Method "IsConnected" with signature "" on interface "org.bluez.Headset" doesn't exist – wim Apr 09 '14 at 23:50
  • @wim: thank you for pointing to this. What Bluez version are we talking about? – Takkat Apr 10 '14 at 06:48
  • @wim: Present Bluetooth implementations (Bluez vers. 4.101 is in 14.04 LTS) still support method IsConnected(). Just tested it and it still works as expected. In later Bluez >= 5.0 you may want to use method State() instead. Please also note that the script above is an example only. It will not let you choose of your BT devices. In case you have more than one device please give the appropriate device path for connecting (e.g. devicePath = iface_a.ListDevices()[1] (or higher). Hope this helps. – Takkat Apr 10 '14 at 17:49
  • dpkg --status bluez | grep '^Version:' == Version: 4.101-0ubuntu8b1 – wim Apr 10 '14 at 21:10
  • Sorry, the attr is "there" (dynamic.. it's a dbus.proxies._ProxyMethod), but I get the exception when I call it. I am picking the correct device from the devicelist. – wim Apr 10 '14 at 21:14
  • I was able to get your script working AudioSink in place of Headset. PEBKAC :) – wim Apr 10 '14 at 21:21
  • Ah yeah ;) In case you do not connect as Headset dbus can't find it... Nice you got is sorted out, will edit my answer for clarity. – Takkat Apr 11 '14 at 06:13
  • Hey @Takkat, I have slightly modified your script to select device by name. Here it is: https://gist.github.com/hyOzd/53748c81eaa73f1855ef . Note that, this version of script connects to an "AudioSink" which can be easily changed to "Headset". – HeyYO Mar 25 '15 at 14:57
11

After trying some of the above (scripts didn't work for me) I found the following solution.

First find out the MAC-Adress of the device you want to connect to

bluetoothctl

this will enter a shell and list all available devices with adress. ("quit" to exit the shell & get back to prompt)

Then connect to XX:XX:XX:XX:XX:XX bluetooth device :

echo -e 'connect XX:XX:XX:XX:XX:XX' | bluetoothctl

to disconnect

echo -e 'disconnect XX:XX:XX:XX:XX:XX' | bluetoothctl

been searching for this quite a while - nothing seemed to work, felt so relieved when I found out. Thought others might want to know about it, too. :))

Joseph
  • 303
  • 2
  • 8
  • I get ~$ bluetoothctl Agent registered [UE BOOM 2]# . Then disconnect "UE BOOM 2" Device UE BOOM 2 not available. – cipricus Sep 02 '19 at 12:13
  • @cipricus @user3140225 Could you post the output of

    bluetoothctl ?

    You need to feed the commands with the MAC-adress of the device - which is the combination of HEX Values in the format XX:XX:XX:XX:XX:XX, where X is either a letter or a number.

    – Joseph Sep 04 '19 at 06:18
  • I can see the MAC as said here. Then, trying echo -e 'connect CC:AF:78:AF:59:03' | bluetoothctl I get : Agent registered [bluetooth]# connect CC:AF:78:AF:59:03 Device CC:AF:78:AF:59:03 not available – cipricus Sep 04 '19 at 12:17
  • @cipiricus did you check whether your device is paired ? maybe try unpair, delete device and then pair again. hope this helps. – Joseph Sep 05 '19 at 19:35
  • 1
    Great, thanks Joseph, exactly what I needed... (Here's what I came up with, to reconnect my Amazon 'echo plus' )

    echo "connect $(bluetoothctl devices | grep -i "echo plus" | awk '{print $2}')" | bluetoothctl

    (command results in the string, connect 38:F7:3D:9B:6D:89 being piped to the bluetoothctl command.)

    – mlo55 Jun 16 '21 at 03:30
  • There is no need for echo. You can do bluetoothctl <command> directly in terminal.

    Just do bluetoothctl paired-devices if your device is already pared to see its mac address. Then bluetoothctl connect XX:XX:XX:XX:XX:XX to connect.

    – uranibaba Oct 12 '23 at 08:11
6

I use this script to connect my Bluetooth Audio Device. If your headset is already paired, you should be able to connect your headset in the same way using org.bluez.Headset.Connect/Disconnect in place of org.bluez.Audiosink.Connect/Disconnect.

#!/bin/bash

MAC_ADD="C8:84:47:10:11:CD"

MAC_ADD="dev_${MAC_ADD//:/_}"
BT_ADAPTER=`dbus-send --system --print-reply --dest=org.bluez / \
org.bluez.Manager.DefaultAdapter|awk '/object path/ {print $3}'`

BT_ADAPTER="${BT_ADAPTER//\"/}/$MAC_ADD"
echo "Connecting to $BT_ADAPTER..."

if [ "$1" == "on" ]; then
    dbus-send --print-reply --system --dest=org.bluez $BT_ADAPTER org.bluez.AudioSink.Connect
elif [ "$1" == "off" ]; then
    dbus-send --print-reply --system --dest=org.bluez $BT_ADAPTER org.bluez.AudioSink.Disconnect
fi

HTH!

  • this worked for me too on 16.04, thank you! others, do not forget to pass on option to the script! – MInner Jun 30 '17 at 05:58
  • 2
    Looked good, but alas, does not work:

    $ speaker on Error org.freedesktop.DBus.Error.UnknownMethod: Method "DefaultAdapter" with signature "" on interface "org.bluez.Manager" doesn't exist

    Connecting to /dev_30_21_26_69_51_DE... Error org.freedesktop.DBus.Error.UnknownObject: Method "Connect" with signature "" on interface "org.bluez.AudioSink" doesn't exist

    – Tomislav Nakic-Alfirevic Jul 11 '20 at 08:59
2

I use i3 as a window manager so I do not have the bluetooth tray icon available. For some reason the check button in unity settings is not sensitive and so I need a way to do this from time to time when my headphones don't connect.

enter image description here

It seems that bluez has changed their dbus API. The answer utilizing org.bluez.Manager no longer appears to work. Instead, it is recommended to use ObjectManager.

Here is an updated python script which will connect the first unconnected bluetooth headset that it finds (presumably the list includes all paired devices?):

#!/usr/bin/env python
# Toggles headset connection

from __future__ import print_function
from __future__ import unicode_literals

import dbus
from dbus.mainloop.glib import DBusGMainLoop

def find_headset(bus):
  manager = dbus.Interface(bus.get_object("org.bluez", "/"),
                           "org.freedesktop.DBus.ObjectManager")
  objects = manager.GetManagedObjects()

  for path, ifaces in objects.items():
    if ("org.bluez.Device1" in ifaces and
        "org.freedesktop.DBus.Properties" in ifaces):
      iprops = dbus.Interface(
          bus.get_object("org.bluez", path),
          "org.freedesktop.DBus.Properties")
      props = iprops.GetAll("org.bluez.Device1")
      # Looking for a headset. Could also match on other properties like
      # "Name". See bluez docs for whats available.
      if props.get("Class") == 0x240404:
        if props.get("Connected"):
          print("Found headset {} ({}) but it is already connected"
                .format(props.get("Name"), props.get("Address")))
          continue
        return path

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

if hpath:
  adapter = dbus.Interface(
      bus.get_object("org.bluez", hpath), "org.bluez.Device1")
  adapter.Connect()

this example, like the other example on this thread, uses the dbus python package. On ubuntu 16.04 I installed this through apt-get install python-dbus.

If you wish to match other criteria, this document appears to show a list of properties that can be queried over dbus.

I have this script saved in ~/.local/bin/bt-connect-headset which is on my PATH so I can execute it from the i3 launcher. Make it executable (chmod +x bt-connect-headset) if you plan to use it as a command.

This script has only been tested on an up-to-date ubuntu 16.04 as of 09/28/2018.

  • 1
    Used your script on Ubuntu 18.10. Thanks! – Brombomb Nov 15 '18 at 20:03
  • 1
    Super, this worked for me in Linux Mint 19 Cinnamon. However, I used a device class of 2360344 instead of 0x240404. – dom_watson Nov 29 '18 at 21:40
  • 1
    works as such in Mint Xfce 19.2. I'm sure it works in Xubuntu. - What about a script to disconnect the bluetooth ? – cipricus Aug 30 '19 at 01:06
  • 1
    @cipricus That is correct, and is already mentioned in the answer. There's a link in the answer on how to match other criteria if you need something more advanced (like matching a device of a particular name). That link also includes a list of methods available on this interface. To disconnect, my guess is use the Disconnect() method. I haven't tried it, but it's probably a safe bet ;). – cheshirekow Aug 31 '19 at 04:47
  • I was wrong. I thought it never goes beyond the first in the list of paired devices, but it does. I might have also missed the fact that you say first unconnected bluetooth headset: will it always use the headset (if powered) before other device like bluetooth speakers (no matter the position in the list)? That's what happens in my case. -- Also, in order to disconnect I cannot do it by just changing the last line to adapter.Disconnect().) – cipricus Sep 02 '19 at 11:33
  • Also, an odd thing that happens is that if the headset is first in the paired list, I cannot connect to the speakers, even if they are powered on and the headset is not. -- So, to make it all work: speakers always first paired, headset always shut off in case the other device is needed :)) – cipricus Sep 02 '19 at 11:42
  • The find_headset function didn't work for me, but if you know you're bluetooth device MAC then you can just add a line hpath="/org/bluez/hci0/dev_**_**_**_**_**_**" replacing the stars with your MAC address. – SurpriseDog Feb 27 '21 at 18:13
1

In my ~/.aliases I have

# Bluetooth Headphones
export WH1000XM4="88:C9:E8:3A:B4:32"
alias headon="bluetoothctl connect $WH1000XM4"
alias headoff="bluetoothctl disconnect $WH1000XM4"
alias headreset="bluetoothctl power off && bluetoothctl power on"

Change the MAC Address to match your device. I only chose that variable name since it matches the model number of the device. To expose your MAC addresses try bluetoothctl devices.

Thus I can connect and disconnect my headphones with headon and headoff.

I need the headreset because sometimes I turn off the headphones without disconnecting them first, and it doesn't like that (won't reconnect); but power cycling bluetooth off/on will fix that.

John Mee
  • 953
  • 1
  • 8
  • 18