Check battery level of connected bluetooth device on linux

LinuxBluetoothBluez

Linux Problem Overview


How can I check the battery level of a connected bluetooth device? The device shows the battery level on Android so I'm assuming the device supports the GATT-based Battery Service. However, by entering "menu gatt" in bluetoothctl and then listing the GATT attributes of the device with "list-attributes [dev]", nothing shows up.

A [similar question was posted to SO][2] but the OP seems to have found a solution that doesn't work for me. When I run "info [dev]" in bluetoothctl I don't see the UUID for Battery Service.

I would prefer a solution that runs on the command line and is distro-agnostic.

Please let me know if this question should be posted on SuperUser instead.

[2]: https://stackoverflow.com/questions/48058357/how-to-check-battery-level-of-a-bluetooth-paired-remote "How to check battery level of a bluetooth paired remote"

Linux Solutions


Solution 1 - Linux

This might be a bit late to the party but for me this Python project has worked fine:

https://github.com/TheWeirdDev/Bluetooth_Headset_Battery_Level

I only had to change the port in line 57 to 3 for my no-name X5 headset. If it hangs or errors with "connection refused" try a different port.

The Python program uses AT commands via RFCOMM and should work while Pulseaudio is using the A2DP sink (mine reconnects). Python 3 is needed as 2 doesn't have BT-Serial sockets. Windows will probably not work as it lacks bluez. It basically does the same thing as the Pulseaudio hack here: https://stackoverflow.com/a/56390625/920122

If you want to look at the commands as they are exchanged, try my debug fork: https://github.com/clst/Bluetooth_Headset_Battery_Level

Solution 2 - Linux

For me running this in terminal worked:

upower --dump

Solution 3 - Linux

You don't see Battery Level in the list of GATT characteristics since Bluez v5.48 because this specific GATT characteristic was moved into DBUS org.bluez.Battery1 interface.

From the command line:

  1. Connect to your target BLE device with bluetoothctl
  2. And then request DBUS by running: dbus-send --print-reply=literal --system --dest=org.bluez /org/bluez/hci0/dev_<mac_address_of_your_ble_peripheral> org.freedesktop.DBus.Properties.Get string:"org.bluez.Battery1" string:"Percentage"

In my case with my BLE peripheral with the following MAC address C3:41:A6:C8:93:42:

$ dbus-send --print-reply=literal --system --dest=org.bluez \
    /org/bluez/hci0/dev_C3_41_A6_C8_93_42 org.freedesktop.DBus.Properties.Get \
    string:"org.bluez.Battery1" string:"Percentage"
   variant       byte 94

Note: You could potentially scan and connect to your device using Bluez DBUS API.

Solution 4 - Linux

This is such a great question, ahead of development and tools that are available at the moment.

The short answer (in October 2018) > you have to write it yourself! It won't be a one liner in the terminal. I am going to write this for myself in Python, but C has a little more documentation, so if you are skilled with C go for it.

The long answer, but it's more a recommended starting point:

  1. Tony D: https://youtu.be/5fQR2PHMDWE?t=4644 managed to use bluetoothctl to read attributes and send data to a bluetooth device. Definitely check the video information, you will find great links and references: https://learn.adafruit.com/introduction-to-bluetooth-low-energy/gatt
  2. Szymon Janc: https://youtu.be/VMDyebKT5c4 developer and contributer to the LINUX Bluetooth Stack
  3. Definitely check out how this question is answered on Mobile devices. For Android it's the BAS (Battery Service): https://android.stackexchange.com/questions/106073/displaying-bluetooth-gadgets-battery-status-on-the-phone

[![On Android 8.0.1][1]][1] [1]: https://i.stack.imgur.com/7F4sf.png

Solution 5 - Linux

Here is a way to get battery level via pulseaudio logs with some hack. My bluetooth headset uses proprietary Apple HFP AT commands, HFP/A2DP protocols are handled by pulseaudio directly. It seems the only way to get those values is through pulse.

Solution 6 - Linux

By default Bluez 'hides' the Battery Service UUID. This is because there is a 'battery plugin' loaded at startup of bluetoothd.

If you don't want the battery plugin to be activated and make the Battery Service UUID visible again to bluetoothctl or any other application, then change the startup command for bluetoothd to be like this: 'bluetoothd -P battery'. That will make sure the battery plugin is not loaded. On a Raspberry Pi the bluetooth.service is located in /lib/systemd/system/bluetooth.service so you need to make the change in that file.

Solution 7 - Linux

In the bluez version you are using the Gatt attributes may be experimental.If so you need to enable the experimental characteristics by running the bluetoothd deamon by -E keyword Like "/usr/libexec/bluetooth/bluetoothd -E" this worked for me.

Solution 8 - Linux

(This answer is specific to headphones/headsets)

I'd been using the Python program from clst's answer for some time and although it worked, it required me to connect, then disconnect and run it again. If I understand the problem correctly, that happens because only one program can open a socket to talk to the bluetooth device, so it ends up fighting with PulseAudio over it.

I've recently found out about hsphfpd.

> hsphfpd is specification with some prototype implementation used for connecting Bluetooth devices with HSP and HFP profiles on Linux operating system.

Basically, since only one program can communicate with the headset at once and it wouldn't make sense to implement battery level reporting in an audio server, nor implement audio in a power management software, it moves that functionality to an external daemon. That way, PulseAudio and whatever can both use the headset at the same time. There is a version of PulseAudio patched to use hsphfpd. Even though these are both still prototypes, they seem to work very well.

hsphfpd reports battery status (and other stuff) through DBus, so to get it from the command line, you can just do

dbus-send --system --dest=org.hsphfpd --print-reply /org/hsphfpd/hci0/dev_XX_XX_XX_XX_XX_XX/hsp_hs org.freedesktop.DBus.Properties.Get string:org.hsphfpd.Endpoint string:BatteryLevel

or even call it from a program.

Both of these are available in the AUR, if you use Arch Linux.

Solution 9 - Linux

On Ubuntu 20+, it shows the battery under the devices tab in the power panel

enter image description here

Solution 10 - Linux

As said by @OlivierM above, the UUID is filtered by bluetoothd. You could undo that and export the UUID just as any other service characteristics by removing the following from the export_service() function in src/gatt-client.c

if (gatt_db_service_get_claimed(attr))
     return;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRicardoView Question on Stackoverflow
Solution 1 - LinuxclstView Answer on Stackoverflow
Solution 2 - LinuxYash NahtaView Answer on Stackoverflow
Solution 3 - LinuxOlivierMView Answer on Stackoverflow
Solution 4 - LinuxVeRoView Answer on Stackoverflow
Solution 5 - LinuxVasily OlekhovView Answer on Stackoverflow
Solution 6 - LinuxMartijn van WelieView Answer on Stackoverflow
Solution 7 - LinuxJayanth RajanView Answer on Stackoverflow
Solution 8 - LinuxLHLauriniView Answer on Stackoverflow
Solution 9 - LinuxkooskoosView Answer on Stackoverflow
Solution 10 - Linuxbr1View Answer on Stackoverflow