Bluetooth and Linux
Behind this mundane title, some notes as I was finding my bearings attempting to use Bluetooth devices when not using a conventional desktop environment.
People who use Gnome or KDE probably have an applet running that gets it all working nicely. But preferring to use Notion instead, I felt a bit lost as to how to set up Bluetooth devices, especially because there seems to be a thousand ways to achieve this, with about 990 of them not working when there's rain. So I've got a Bluetooth mouse which sometimes pairs, sometimes doesn't. I don't really know why. I wanted to understand.
1 Concepts Behind Connecting a Bluetooth Device
That really isn't rocket science, it's not. But if you don't know what connecting a Bluetooth device entails it's easy to get lost:
- Select the Bluetooth controller. That's in your PC. You normally have single one. And it's very likely to already be selected so this step is arguably moot. But I just wanted to introduce the word controller here so I could refer to it later on.
- Turn on the device. That does sound a silly thing to have to say, but I meant to mention it because if for some reason or other you get to remove a device from your controller. It may no longer be able to see it until you reset the device.
- Scan for devices.
- Pair the device.
- Connect the device.
- Optionally trust the device, so it connects again without further ago after you e.g. turn it off or turned your computer off. If only this could work reliably. If only there were Bluetooth device managers that could ensure that the Bluetooth connection be kept up. Even graphical, if must be.
2 Pairing Graphically
Here's a review of the various ways I tried to use a Bluetooth device graphically and how they didn't suit me as a user who won't use more common desktop environments:
- GNOME Bluetooth offers various components to integrate nicely in GNOME: there's an applet, a panel for the GNOME Control Center, none of which will work if you use Notion. Mind you, starting the GNOME Control Center by running the
gnome-control-center
command will already get you nowhere since all it does is open an empty window with just a few buttons, half of which don't work. - BlueDevil is to KDE what GNOME Bluetooth is to GNOME and I'm sure it's wonderful. It's just that I can't be bothered to install it because – as is usually the case with KDE applications – it comes with a wealth of dependencies, the lot resulting in installing over 100 MB's worth of software I'm otherwise never going to use.
Blueman is more independent than the other two. Installing the
blueman
package will provide you with a number of commands, most of which only work if the applet runs. So what you really want to execute isblueman-applet
. Add a new device by choosing the Setup New Device entry from the menu which opens when you click on it. The assistant will lead you through the scanning, pairing and connecting steps I mentioned earlier on.The applet menu also lists a Devices entry, and the manager that opens lets you add devices too in a somewhat less straightforward way. Hit the Search button to scan, click on the pair-of-keys button to pair and open the Device menu before selecting the correct device under the Connect To entry. Alternatively, try the Setup button to bring back the assistant. Not the most logical of user interfaces.
You may find blueman particularly treacherous if you start
blueman-manager
without running the applet. You will be able to scan and find your device, pair it but connecting will miserably fail. Unless you start the applet again.
In short, GNOME Bluetooth doesn't work inside some uncommon window managers, BlueDevil is too hungry and Blueman will only really work if you know what you're doing. And if you know what you're doing, why then not just use bluetoothctl
?
3 Pairing from a Shell
In Ubuntu, apt-get install bluez
will be all you'll ever need. It provides bluetoothctl
and bluetoothd
. And for that reason, it's likely to have already been installed because bluetoothd
is crucial to so many other applications it's hard to imagine it's not here by default. There's other BlueZ-related packages but you won't need them.
The main way to pair a device from a shell is by using bluetoothctl. However, there's a catch. It's not that bluetoothctl is a command-line tool which lets you manage your devices from any shell you like. No, bluetoothctl is the shell. And nothing but a shell. Incomprehensibly, there's no easy way to achieve from its command-line options – which are very few – what you can do from its shell. As a result, there's no way to automate away.
Nevertheless, bluetoothctl is simple and pleasantly predictable. Here's a session depicting the scanning, pairing, connecting and trusting steps:
[bluetooth]# scan on
Discovery started
[NEW] Device 12:FF:4F:61:98:EC Bluetooth Mouse
[bluetooth]# pair 12:FF:4F:61:98:EC
Attempting to pair with 12:FF:4F:61:98:EC
Pairing successful
[bluetooth]# connect 12:FF:4F:61:98:EC
Attempting to connect to 12:FF:4F:61:98:EC
Connection successful
[bluetooth]# trust 12:FF:4F:61:98:EC
Changing 12:FF:4F:61:98:EC trust succeeded
The bluetoothctl shell supports completion, and that's very welcome as you can see that arguments referring to devices have to be MAC addresses.
For the record, should you have lost connection with the device, there is no need to exit bluetoothctl, let alone systemctl restart bluetooth
, never mind restart the whole computer. Everything can be achieved inside this shell. It's also informative, as it will log on-screen events even if you manage devices from another tool. It's only fair to say that bluetoothctl is a very nice and effective way to work with Bluetooth devices. If only it could be used programmatically for ensuring that Bluetooth devices remain connected.
4 Connecting Programmatically?
I briefly schemed through Python modules for working with Bluetooth. PyBluez isn't no longer under active development. Nor is LightBlue. Other packages I saw seemed unrelated to what I needed to achieve. Gutted, I resorted to using Pexpect:
import pexpect
child = pexpect.spawn('bluetoothctl')
child.expect('[bluetooth]#')
child.sendline('pair 12:FF:4F:61:98:EC')
child.interact()
As I was writing those lines which didn't work – bluetoothctl is a little too sophisticated what with the printouts and prompts being asynchronous and all that, or maybe it's just the colours which never match [bluetoothctl]#
– it occurred to me that there might have been a simpler explanation to me losing the connections.
5 Shake and Connect
It turns out my trusted mouse – trusted in the Bluetooth sense – sort of turned itself off, perhaps after having been disconnected, perhaps even after being left idle for too long. But all it took to wake it up and reconnect was to shake it and click about a bit. And now it reliability does my biddings. All that faffing about I went through just for that. Oh, well. I learned plenty in the process.
6 References
- Bluetooth on the ArchWiki
- BlueZ
- GNOME Bluetooth
- BlueDevil
- Blueman
- Ubuntu packages still suggest that PyBluez is available at
https://karulis.github.io/pybluez
but that isn't the case anymore. I googled and found a pybluez GitHub repository which may or may not be the same project. Either way, it's no longer maintained. - LightBlue: a cross-platform Python Bluetooth API
- Pexpect