How to script bluetoothctl commands

I’m trying to script a series of commands with bluetoothctl, but it’s a bit tricky in some parts.

So what I have is a laptop with an additional bluetooth dongle since the builtin one isn’t working great, the builtin is the controller that’s always set as the default in bluetoothctl though.

What I want to do in a bash function/script:

  • bluetoothctl select (external dongle)
  • bluetoothctl scan on
  • (wait until a specific headset device is detected)
  • bluetoothctl pair (headset)
  • bluetoothctl connect (headset)
  • bluetoothctl trust (headset)

The main problem with the above is that the scan on command has no “until found” flag, I would like to scan until my headset is found. How would I solve that? I can’t grep the output because bluetoothctl doesn’t finish by itself, i.e. the scan command keeps running in the foreground.

Also the first command where I select the controller doesn’t seem to persist, if I run bluetoothctl select <dongle> and then bluetoothctl list, then the internal controller will still be shown as the default.

I had a similar issue. My solution was to blacklist the internal device in /etc/udev/rules.d/ by creating a file called 90-bluetooth.rules. This file consits only of two lines.

# Intel Bluetooth should be disabled
 SUBSYSTEM=="usb", ATTRS{idVendor}=="8087", ATTRS{idProduct}=="0a2a", ATTR{authorized}="0"

You need to replace the vendor and device ID with the actual ones on your system.

This completely disables the built-in device, so only the dongle is used.

1 Like

WHAT @Trekkie00 SAY :wink:
And all other can be done by configuration it does not need scripts to create… but on pulseaudio it will may never get working, i switched to pipewire and if i enable my BT Headset it will get connected automatic.

The problem for me is that I’m switching the usb bt dongle between two computers using a usb peripherals switch, so the dongle is disconnected from the running host daily. This doesn’t work that great, so I found that it’s easiest to just reset everything, even though I am using pipewire.

Wouldn’t buying a second dongle make more sense then? They are not too expensive.

Not if I can solve it with a simple script and spend no money at all :wink:

Mainly I’m wondering how I can somehow grep the bluetoothctl scan on command, so that immediately when my headphones are detected, I can stop and connect. This seems really tricky since bluetoothctl wants to run in the foreground and there are no flags to set for the scan command to look for a specific device.

bluetoothctl is mainly an application, what you start and inside you are calling commands…

may help

While it’s true that bluetoothctl is an application with it’s own console I figured you should be able to run bluetoothctl scan on and act on the output, but it does seem tricky to interact with it. I suppose that’s more of a general design issue with bluetoothctl, I wonder if there are any open issues for it to allow a more programmatic approach to using it.

Some examples show that you should be able to just do bluetoothctl -- pair <mac>, but I never found that to work, I always need to scan first and detect the device before the pair command works.

The solution for this is to use the expect utility, it allows one to interact with application consoles, and does exactly what I’m looking for it to do:

#!/usr/bin/expect -f

set device "00:1B:66:88:1D:52"
set controller "F0:2F:74:63:3A:12"
set timeout 60

spawn bluetoothctl
expect "Agent registered"
send -- "list\r"
expect "$controller"
send -- "select $controller\r"
send -- "remove $device\r"
expect "Device has been removed"
send -- "scan on\r"
expect "$device"
send -- "pair $device\r"
expect "Pairing successful"
send -- "connect $device\r"
expect "Connection successful"
send -- "trust $device\r"
expect "trust succeeded"
send -- "exit\r"
expect eof

EDIT: Updated script example with simplifications

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.