ukBaz / python-bluezero

A simple Python interface to Bluez
MIT License
387 stars 112 forks source link

Unable to set `DiscoverableTimeout` #403

Open furgerf opened 5 months ago

furgerf commented 5 months ago

I'm able to read out the current DiscoverableTimeout on an instance of Adapter but when I try to write it, I get the following exception:

>>> from bluezero.adapter import Adapter
>>> adapter = next(Adapter.available())
>>> print(adapter.discoverabletimeout)
180
>>> adapter.discoverabletimeout = 180
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/snap/xxx/x86/lib/python3.11/site-packages/bluezero/adapter.py", line 196, in discoverabletimeout
    self.adapter_props.Set(constants.ADAPTER_INTERFACE,
  File "/snap/xxx/x86/lib/python3.11/site-packages/dbus/proxies.py", line 141, in __call__
    return self._connection.call_blocking(self._named_service,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/snap/xxx/x86/lib/python3.11/site-packages/dbus/connection.py", line 634, in call_blocking
    reply_message = self.send_message_with_reply_and_block(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.InvalidSignature: Invalid signature for 'DiscoverableTimeout'

This is the default value, I can change it using bluetoothctl and the adapter instance correctly returns the modified value:

[bluetooth]# discoverable-timeout 123
Changing discoverable-timeout 123 succeeded
[CHG] Controller E4:5F:01:3B:66:D1 DiscoverableTimeout: 0x0000007b
>>> print(adapter.discoverabletimeout)
123

There's nothing in bluetoothctl or journalctl but I see the following with busctl monitor org.bluez:

‣ Type=method_call  Endian=l  Flags=0  Version=1  Priority=0 Cookie=20
  Sender=:1.232  Destination=:1.8  Path=/org/bluez/hci0  Interface=org.freedesktop.DBus.Properties  Member=Set
  UniqueName=:1.232
  MESSAGE "ssv" {
          STRING "org.bluez.Adapter1";
          STRING "DiscoverableTimeout";
          VARIANT "i" {
                  INT32 180;
          };
  };

‣ Type=error  Endian=l  Flags=1  Version=1  Priority=0 Cookie=535  ReplyCookie=20
  Sender=:1.8  Destination=:1.232
  ErrorName=org.freedesktop.DBus.Error.InvalidSignature  ErrorMessage="Invalid signature for 'DiscoverableTimeout'"
  UniqueName=:1.8
  MESSAGE "s" {
          STRING "Invalid signature for 'DiscoverableTimeout'";
  };

Using bluezero 0.8.0 with bluez 5.53, running in a snap on UbuntuCore.

ukBaz commented 5 months ago

Thank you for the very good debug information as this allowed me to quickly narrow down the issue.

One of the recognised issues with dbus-python is that it "guesses" at types. It is guessing at INT32 wrapped as a VARIANT. My assumption is that somewhere in the stack there is a better job being done on checking if that is the signature for the property is correct. And of course it isn't. It should be a UINT32 https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/adapter-api.txt?h=5.53#n283

The quick fix is to set the dbus type when setting the property e.g.

adapter.discoverabletimeout = dbus.UInt32(130, variant_level=1)

Longer term, it is to go through the code and update the property setters. For this particular property it would need to go from:

    @discoverabletimeout.setter
    def discoverabletimeout(self, new_timeout):
        self.adapter_props.Set(constants.ADAPTER_INTERFACE,
                               'DiscoverableTimeout', new_timeout)

to

    @discoverabletimeout.setter
    def discoverabletimeout(self, new_timeout):
        dbus_value = dbus.UInt32(new_timeout, variant_level=1)
        self.adapter_props.Set(constants.ADAPTER_INTERFACE,
                               'DiscoverableTimeout', dbus_value)
furgerf commented 5 months ago

Great that works, thanks!