Search code examples
pythonbluetooth-lowenergydbusbluez

Blocking DBus call from python API


I am programming a BLE device and therefore need to get some information from the org.freedesktop.DBus.Properties interface, but can't get it to work from dbus python API. From the console this is no problem. For example, from dbus-send I can invoke following method call successfully (with correct mac address of course):

$ dbus-send --system --dest=org.bluez --print-reply "/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX" org.freedesktop.DBus.Properties.Get string:'org.bluez.Device1' string:'Paired'

>> method return time=1645780543.222377 sender=:1.7 -> destination=:1.329 serial=1113 reply_serial=2
   variant       boolean true

Now, what I'm trying to do is actually something like this:

import dbus
bus = dbus.SystemBus()

connected = bus.call_blocking(
    'org.bluez',                             #bus_name
    '/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX', #object_path
    'org.freedesktop.DBus.Properties',       #dbus_interface
    'Get',                                   #method
    signature='(ss)',                        #signature
    args=['org.bluez.Device1', 'Connected'], #args
)
print(connected)

which gives me the error: ERROR:dbus.connection:Unable to set arguments ['org.bluez.Device1', 'Paired'] according to signature '(ss)': <class 'TypeError'>: Fewer items found in struct's D-Bus signature than in Python arguments

I tried also with no signature with no success. And I also found a similar question here, but for C-API. So I tried to adapt it to the python dbus API, but still can't get it to work. Moreover, the official documentation isn't very helpful as well, as there is no clear statement on how the argument mechanism works here or a reference to such an explanation. This is pretty annoying, since I can invoke a blocking call for instance on the GetManagedObjects method from org.freedesktop.DBus.ObjectManager interface that way, but that one takes no arguments of course...

Any help appreciated.


Solution

  • There are more Pythonic libraries for D-Bus such as pydbus

    device_path = "/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX"
    
    bus = pydbus.SystemBus()
    
    device = bus.get('org.bluez', device_path)
    print(device.Connected)
    

    If you did want to do it with the deprecated python-dbus library then I have always done it this way:

    BLUEZ_SERVICE_NAME = 'org.bluez'
    DEVICE_INTERFACE = 'org.bluez.Device1'
    remote_device_path = device_path
    remote_device_obj = self.bus.get_object(BLUEZ_SERVICE_NAME,
                                            remote_device_path)
    remote_device_props = dbus.Interface(remote_device_obj,
                                         dbus.PROPERTIES_IFACE)
    print(remote_device_props.Get(DEVICE_INTERFACE, 'Connected'))
    

    If you want to do it with PyGObject library then an example of that is:

    from gi.repository import Gio, GLib
    
    bus_type = Gio.BusType.SYSTEM
    bus_name = 'org.bluez'
    object_path = '/org/bluez/hci0'
    prop_iface = 'org.freedesktop.DBus.Properties'
    adapter_iface = 'org.bluez.Adapter1'
    
    
    adapter_props_proxy = Gio.DBusProxy.new_for_bus_sync(
                bus_type=bus_type,
                flags=Gio.DBusProxyFlags.NONE,
                info=None,
                name=bus_name,
                object_path=object_path,
                interface_name=prop_iface,
                cancellable=None)
    
    all_props = adapter_props_proxy.GetAll('(s)', adapter_iface)
    print(all_props)
    powered = adapter_props_proxy.Get('(ss)', adapter_iface, 'Powered')
    print(powered)