Reputation: 53
Ubuntu 20.04.2 LTS bluetoothctl Version 5.53
The problem I have is a bluetooth headset being unable to directly pair without errors when switching between Ubuntu/Windows on a dual boot machine (I believe due to the process which bluetooth issues/stores public keys)
I am trying to run a simple script to remove a specific bluetooth address (device), flush its information and re-pair.
I have tried various iterations of:
#!/bin/bash
#remove old connection
echo -e 'power on\ndisconnect 88:88:E1:21:52:BE\nremove 88:88:E1:21:52:BE\nquit' | bluetoothctl
sudo systemctl restart bluetooth
sleep 1
echo -e 'power on' | bluetoothctl
echo -e 'default-agent' | bluetoothctl
echo -e 'discoverable on\ndiscoverable-timeout 100\nscan on' | bluetoothctl
sleep 10
echo -e 'pairable on' | bluetoothctl
# Re-Add our device
echo -e 'trust 88:88:E1:21:52:BE\npair 88:88:E1:21:52:BE' | bluetoothctl
sleep 4
echo -e 'connect 88:88:E1:21:52:BE' | bluetoothctl
echo -e 'discoverable off' | bluetoothctl
echo -e 'quit' | bluetoothctl
Output as follows:
Agent registered
[bluetooth]# power on
[bluetooth]# disconnect 88:88:E1:21:52:BE
Attempting to disconnect from 88:88:E1:21:52:BE
[bluetooth]# remove 88:88:E1:21:52:BE
[bluetooth]# quit
Agent registered
[bluetooth]# power on
Agent registered
[bluetooth]# default-agent
Agent registered
[bluetooth]# discoverable on
[bluetooth]# discoverable-timeout 100
[bluetooth]# scan on
Agent registered
[bluetooth]# pairable on
Agent registered
[bluetooth]# trust 88:88:E1:21:52:BE
Device 88:88:E1:21:52:BE not available
[bluetooth]# pair 88:88:E1:21:52:BE
Device 88:88:E1:21:52:BE not available
Agent registered
[bluetooth]# connect 88:88:E1:21:52:BE
Device 88:88:E1:21:52:BE not available
Agent registered
[bluetooth]# discoverable off
Agent registered
[bluetooth]# quit
However while following on the bluetoothctl command output in a seperate window
[CHG] Controller C8:58:C0:C4:41:F8 Name: [ControllerID]
[CHG] Controller C8:58:C0:C4:41:F8 Alias: BlueZ 5.53
[CHG] Controller C8:58:C0:C4:41:F8 Alias: [ControllerID]
**[CHG] Controller C8:58:C0:C4:41:F8 Discovering: no**
[CHG] Controller C8:58:C0:C4:41:F8 Discoverable: yes
The Scan on command has not had the desired effect. The bluetooth controller is not discovering and discovers no devices ergo no connection to said devices can be made.
When moving directly into the bluetoothctl command line, the scan on command has the desired effect and the controller begins scanning. Why is the scan ON command from the ubuntu terminal/bash script not having the desired effect?
Upvotes: 3
Views: 2553
Reputation: 126
When you run bluetoothctl scan on
, it seems that it exits directly the scan and thus does not discover anything.
One alternative to using python
or expect
would be to use coproc which is already included in ubuntu.
Using using coproc has the advantage of not having to install yet another package.
#!/bin/bash #you'll need to run it in bash script (does not work in zsh)
coproc mycoproc { bluetoothctl; }
sleep 2
echo "scan on" >&${mycoproc[1]}
while true; do
available_devices=$(bluetoothctl devices | grep -E "YOUR DEVICE MAC OR NAME HERE")
sleep 1
if [[ ! -z "$available_devices" ]]; then #if found device
break
fi
done
DEVICE_MAC="$(echo $available_devices | awk '{print $2}')"
bluetoothctl pair $DEVICE_MAC
sleep 4 # Wait for the pairing to establish
bluetoothctl connect $DEVICE_MAC
sleep 3 # Wait for the connection to establish
echo "scan off" >&${mycoproc[1]}
echo "exit" >&${mycoproc[1]} #this exits the coproc session
Here, the only part, where I'm unsure is about how much needs to be waited between each bluetoothctl
command
Then, specific to OP's problem, you'll be able to set the output of your computer to this device:
DEVICE_MAC_UNDERSCORE="${DEVICE_MAC//:/_}" #replace the : to _
CARD_INDEX=$(pactl list cards short | grep "$DEVICE_MAC_UNDERSCORE" | awk '{print $1}')
pactl set-card-profile $CARD_INDEX a2dp-sink #you'll find the profiles in "pactl list sinks"
SINK=$(pactl list sinks short | grep "$DEVICE_MAC_UNDERSCORE" | awk '{print $2}')
pactl set-default-sink "$SINK" #set the device as audio output
Upvotes: 0
Reputation: 1830
Even though there seems to be no clear CLI, tcl/expect can help
#!/usr/bin/expect -f
set MAC "XX:XX:XX:XX:XX:XX"
set timeout 30
spawn bluetoothctl
send "remove $MAC\n"
expect "Device $MAC " {
send "scan on\n"
}
expect " Device $MAC " {
send "pair $MAC\n"
}
expect "Paired: yes" {
send "connect $MAC\n"
}
expect "Connection successful" {
send "quit\n"
}
Save this as ./bt.sh
and execute just as any script.
You may need to apt install expect
or whatever it is spelled in your distribution.
Upvotes: 0
Reputation: 7994
I don't think it was ever intended for bluetoothctl
to ever be used in that way.
BlueZ provide a DBus APIs that are documented at: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc
As an example of what can be done with the API, here is an example in Python doing something similar to your script.
from gi.repository import GLib
from pydbus import SystemBus
from pprint import pprint
SCAN_TIME = 15
DEVICE_INTERFACE = 'org.bluez.Device1'
adapter_path = '/org/bluez/hci0'
device_address = '88:88:E1:21:52:BE'
dev_path = f'{adapter_path}/dev_{device_address.replace(":", "_").upper()}'
remove_list = set()
def stop_scan():
adapter.StopDiscovery()
mainloop.quit()
def clean_device(rm_dev):
try:
adapter.RemoveDevice(rm_dev)
except GLib.Error as err:
pass
def on_iface_added(path, interfaces):
if DEVICE_INTERFACE in interfaces:
on_device_found(path, interfaces[DEVICE_INTERFACE])
def on_device_found(device_path, device_props):
address = device_props.get('Address')
if address == device_address:
stop_scan()
def pair_device():
dev = bus.get('org.bluez', dev_path)
dev.Trusted = True
dev.Pair()
bus = SystemBus()
adapter = bus.get('org.bluez', adapter_path)
mngr = bus.get('org.bluez', '/')
mngr.onInterfacesAdded = on_iface_added
clean_device(dev_path)
mainloop = GLib.MainLoop()
GLib.timeout_add_seconds(SCAN_TIME, stop_scan)
adapter.SetDiscoveryFilter({'DuplicateData': GLib.Variant.new_boolean(True)})
adapter.StartDiscovery()
mainloop.run()
pair_device()
Upvotes: 1