ukBaz / python-bluezero

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

using class methods as callbacks fails for writes #396

Closed pclass-sensonix closed 11 months ago

pclass-sensonix commented 1 year ago

Hello and thank you for this awesome library. It has been really useful for me.

I want to implement my characteristics as classes in Python and I am running into an issue where I cannot register my class method as a callback to write a ble gatt characteristic.

When I try to use the class method for the gatt writes, the client returns an error saying 'characteristic not writable', or 'failed to write'. From the python-bluezero gatt server's perspective, I see no errors and no print statements indicating that a write request was even made.

The reads work as a class method, but not writing. I think it may have to do with the function signature, but anything I have tried has not worked yet. What do I have to do to get the callback to work as a class method? Thank you!

This works:

def write_control_point(value, options):
    """
    Called when a central writes to our write characteristic.
    :param value: data sent by the central
    :param options:
    """

    print("WRITING NOW blah")

    # Take the value and write it to the specific key in the config
    writeConfigValue(bytes(value), 'MESH_BSSID')

# Add Node Mesh Wifi SSID characteristic
nodeMeshWifiSSIDChar = NodeMeshWifiSSIDCharacteristic()
 custom_svc.add_characteristic(srv_id=1, chr_id=12, uuid=NODE_MESH_WIFI_SSID_CHAR,
                                       value=[], notifying=False,
                                       flags=['read', 'write'],
                                       read_callback=staticmethod(nodeMeshWifiSSIDChar.ReadValue),
                                       write_callback=write_control_point,
                                       notify_callback=None
                                       )

But this does not:

# Add Node Mesh Wifi SSID characteristic
nodeMeshWifiSSIDChar = NodeMeshWifiSSIDCharacteristic()
 custom_svc.add_characteristic(srv_id=1, chr_id=12, uuid=NODE_MESH_WIFI_SSID_CHAR,
                                       value=[], notifying=False,
                                       flags=['read', 'write'],
                                       read_callback=staticmethod(nodeMeshWifiSSIDChar.ReadValue),
                                       write_callback=staticmethod(nodeMeshWifiSSIDChar.WriteValue),
                                       notify_callback=None
                                       )

class NodeMeshWifiSSIDCharacteristic(BleGattMeshNodeCharacteristic):
    """
    Mesh Node Mesh Wifi SSID Characteristic implementation for BLE GATT Server
    """

    description = b"Get/Set the Mesh WiFi SSID"

    def __init__(self):

        # Initialize base class
        super().__init__()

        self.key = 'MESH_BSSID'
        self.value = [readConfigValue(self.key)]

    def ReadValue(self):
        """
        Read function for mesh wifi ssid Characteristic

        :param options:
        :return: bytes object
        """

        self.logger.info("Request to read wifi ssid ...")

        return self.ReadConfigFileValueForKey()

    def WriteValue(self, value, options):
        """
        Write function for mesh wifi ssid Characteristic

        :param value: data sent by the central
        :param options:
        """

        self.logger.info("Request to write wifi ssid ...")
        self.WriteConfigFileValueForKey(value)
ukBaz commented 1 year ago

Thank you for the kind words about the library.

From the information you've shared, nothing is coming to mind as to where the issue is. So let me share some information about where to look to get more debug information. Hopefully that will be helpful and yield some results for you.

When the BLE peripheral is created, the following method is registered with D-Bus and is called when a write happens to that characteristic. The method is acting as an interface between D-Bus and your callback. The method takes the values from D-Bus and converts them into Python objects. It then calls the write_callback it was given.

https://github.com/ukBaz/python-bluezero/blob/6db02adc16daeafd913b2cdf3d8e46ad10ccda2b/bluezero/localGATT.py#L366-L375

Getting the Python debugger to stop in this function (or adding a print statement) might be useful to see what is happening in your situation.

sudo busctl monitor org.bluez will show you the messages on org.bluez D-Bus. This should show the calls to WriteValue so you can see what is coming from D-Bus.

Finally, it is not obvious to me why you have wrapped your call back in staticmethod. e.g.

write_callback=staticmethod(nodeMeshWifiSSIDChar.WriteValue),

In your method you are using self so I would have thought you needed the object instance passed in. I don't have the context you are using this in so I can't tell if this is a code smell or a bug.

ukBaz commented 11 months ago

Closing because of inactivity.