3

I would like to connect to a Bluetooth LE device and receive notifications from it in python. I would like to use the Bluez dbus API, but can't find an example I can understand. :-)

With gatttool, I can use the following command:

gatttool -b C4:8D:EE:C8:D2:D8 --char-write-req -a 0x001d -n 0100 –listen

How can I do the same in python, using the dbus API of Bluez?

Andreas5
  • 55
  • 1
  • 4

2 Answers2

3

To use the BlueZ DBus API's to be a gatt client is probably easiest with the pydbus library. https://pypi.org/project/pydbus/

The BlueZ DBus API documents are available at:

https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/adapter-api.txt

https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/device-api.txt

https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/gatt-api.txt

Some useful things to know to get you started:

  1. The Dbus service for bluez is called 'org.bluez'
  2. The Bluetooth adapter on a Raspberry Pi normally has '/org/bluez/hci0' as DBus object path.
  3. The DBus Object path to a device is the adapter path plus the mac address prepended by 'dev_' and the semi-colons replaced with underscores. i.e. 'DE:82:35:E7:43:BE' would be found at '/org/bluez/hci0/dev_DE_82_35_E7_43_BE'

I don't have your device so I have done an example using a BBC micro:bit to change the value of how often it updates the temperature value (period) and then get notifications of those temperature values. Hopefully it will be easy to adapt this to your situation.

This code does assume that you have already used bluetoothctl to pair your device. Type bluetoothctl devices on the command line and if you device is in the list, then this is a good sign.

import pydbus
from gi.repository import GLib

# Setup of device specific values
dev_id = 'DE:82:35:E7:43:BE'
adapter_path = '/org/bluez/hci0'
device_path = f"{adapter_path}/dev_{dev_id.replace(':', '_')}"
temp_reading_uuid = 'e95d9250-251d-470a-a062-fa1922dfa9a8'
temp_period_uuid = 'e95d1b25-251d-470a-a062-fa1922dfa9a8'

# Setup DBus informaton for adapter and remote device
bus = pydbus.SystemBus()
mngr = bus.get('org.bluez', '/')
adapter = bus.get('org.bluez', adapter_path)
device = bus.get('org.bluez', device_path)
# Connect to device (needs to have already been paired via bluetoothctl)
device.Connect()

# Some helper functions
def get_characteristic_path(device_path, uuid):
    """Find DBus path for UUID on a device"""
    mng_objs = mngr.GetManagedObjects()
    for path in mng_objs:
        chr_uuid = mng_objs[path].get('org.bluez.GattCharacteristic1', {}).get('UUID')
        if path.startswith(device_path) and chr_uuid == uuid:
           return path

def as_int(value):
    """Create integer from bytes"""
    return int.from_bytes(value, byteorder='little')

# Get a couple of characteristics on the device we are connected to
temp_reading_path = get_characteristic_path(device._path, temp_reading_uuid)
temp_period_path = get_characteristic_path(device._path, temp_period_uuid)
temp = bus.get('org.bluez', temp_reading_path)
period = bus.get('org.bluez', temp_period_path)
# Read value of characteristics
print(temp.ReadValue({}))
# [0]
print(period.ReadValue({}))
# [232, 3]
print(as_int(period.ReadValue({})))
# 1000

# Write a new value to one of the characteristics
new_value = int(1500).to_bytes(2, byteorder='little')
period.WriteValue(new_value, {})

# Enable eventloop for notifications
def temp_handler(iface, prop_changed, prop_removed):
    """Notify event handler for temperature"""
    if 'Value' in prop_changed:
        print(f"Temp value: {as_int(prop_changed['Value'])} \u00B0C")

mainloop = GLib.MainLoop()
temp.onPropertiesChanged = temp_handler
temp.StartNotify()
try:
    mainloop.run()
except KeyboardInterrupt:
    mainloop.quit()
    temp.StopNotify()
    device.Disconnect()
ukBaz
  • 6,985
  • 2
  • 8
  • 31
  • Thank you so much for your answer, but the project I needed this for has ended a long time ago. Since, at that time, we were not able to find a solution in python directly, as far as I remeber, we called bluetoothctl from python and parsed the results. Not nice, but it was sufficient for what we wanted. I wish I would have had your reply back then. Kind regards, Andreas – Andreas5 Jul 10 '20 at 09:27
  • This should be the accepted answer to this question. – A_A Jan 13 '22 at 10:32
0

See 'test/example-gatt-client' from bluez package

Yury
  • 9
  • 2
  • I saw all the examples in that folder, but unfortunately, my knowledge about the underlying techniques is not sufficient to understand, what is done there without some more explanation. The example-gatt-client seems to do a lot more than what I want I am unable to pick/identify the parts I need: connect to the BLE device and enable receiving notifications from the device. Next step would be to send some message/notification to the device and listen for the response. – Andreas5 Aug 19 '16 at 07:32