1

I'm trying to figure out the Bluez DBus API, and I'm having trouble understanding some of the concepts.

The most thorough Bluez examples I've seen have all used Python's dbus-python library. For example, this python script demonstrates how to create a BLE advertisement. A lot of the content is in the Advertisement class, which extends dbus.service.Object. The Advertisement class implements a GetAll method (from the org.freedesktop.DBus.Properties interface) and a Release method (from the org.bluez.LEAdvertisement1 interface). In order to register that advertisement, it calls RegisterAdvertisement from the org.bluez.LEAdvertisingManager1 interface.

I'm getting tripped up because I'm more used to Qt DBus, so I was thinking about what it would look like if the same thing was done using PyQt instead of dbus-python. I think the biggest hurdles are:

  1. I can't figure out how the Advertisement class in the example would fit into the interface/adaptor model of Qt Dbus
  2. In the example, the Advertisement class implements methods from two interfaces. I found this discussion, which implies that it can be done, but I don't think I fully understand the mechanics of it

So, in summary, if I wanted to re-implement this example with Qt DBus, what would the Advertisement class look like? Would it extend QDBusAbstractAdaptor or QDBusAbstractInterface? Would it contain one (or both) of those things? Or is there something else entirely that I'm missing?

Edit:

Based on some info from @ukBaz, I dug a little deeper and tried a few more things, but I've come up empty so far. For reference, I have two scripts in this github repo. The stock-example.py script is pulled directly from the Bluez source tree. The qt-example.py script is my attempt at "translating" it to use QtDBus.

What I have found is that the stock script and the Qt script both work enough that I can see them advertising in nRF Connect on my phone. However, the call to RegisterAdvertisement in the Qt app always times out:

class BleAdManager(QDBusAbstractInterface):
    def __init__(self, dbus_obj_path, parent=None):
        self._ble_adapter_dbus_path = dbus_obj_path
        self._dbus_system_bus = QDBusConnection.systemBus()

        super().__init__("org.bluez",
                         self._ble_adapter_dbus_path,
                         "org.bluez.LEAdvertisingManager1",
                         self._dbus_system_bus,
                         parent)

    def RegisterAdvertisement(self, ad_path):
        print("Registering {0}...".format(ad_path))
        msg = self.call('RegisterAdvertisement', QDBusObjectPath(ad_path), {})
        print("Call returned.")
        reply = QDBusReply(msg)

        if reply.isValid():
            print("Valid")
            return reply.value()
        else:
            print(reply.error().message())
            return None

When I run this, it hangs on the QDBusAbstractInterface.call() for about 25 seconds, then spits out

Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.

Since the stock script works, it can't be a security policy problem. Should the remote application send me a reply to RegisterAdvertisement? If not, how do I handle that gracefully with QtDBus (i.e., without it hanging and giving me an error)? If so, why wouldn't I get a response, and how should I attempt to debug?

maldata
  • 385
  • 1
  • 14
  • Hey, I know this was two years ago, but I was running into a similar problem with the long wait then timeout. You need to make sure the event loop is running when you make the call to `RegisterAdvertisement` (or start it right after) because `RegisterAdvertisement` will query your object for it's properties, but the call will hang because you are waiting for the response from `RegisterAdvertisement`. I solved this by using an async method call. – BeyondPerception May 11 '23 at 20:54

1 Answers1

0

The documentation for the BlueZ Advertising D-Bus API is available at:

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

The org.bluez.LEAdvertisement1 is a D-Bus service that is published to the SystemBus and is responsible for providing the data for the advertisements. This published service can be at any D-Bus object path.

The org.bluez.LEAdvertisingManager1 is a D-Bus client that, through RegisterAdvertisement, informs BlueZ what the org.bluez.LEAdvertisement1 D-Bus object path is.

I have not used D-Bus in PyQt so I went looking for an example and found the following which seems to use DBus-Python:

https://wiki.qt.io/Qt_for_Python_DBusIntegration

As a side note https://www.freedesktop.org/wiki/Software/DBusBindings/ does say the following:

New applications should use pydbus, txdbus or GDBus/QtDBus bindings

There is an example of using pydbus to create the advertisement at:

https://raspberrypi.stackexchange.com/a/114130/121848

ukBaz
  • 6,985
  • 2
  • 8
  • 31
  • Thanks, @ukBaz! I think some of my problems *are* Qt-specific. I'm struggling to understand how a "D-Bus service" and "D-Bus client" map to Qt's adapters and interfaces. Based on this, it seems like I need an adapter class to expose my advertisement object on the system bus, and then I need an interface class to talk to the advertising manager. However, the adapter seems to also need to implement the `org.freedesktop.DBus.Properties` and `org.bluez.LEAdvertisement1` interfaces. Is that true, and if so, can a class inherit from or contain an adapter *and* two interfaces? – maldata Aug 04 '21 at 13:49