hbldh / bleak

A cross platform Bluetooth Low Energy Client for Python using asyncio
MIT License
1.77k stars 294 forks source link

ServiceExplorer different on Raspberry and Windows 10 #782

Closed WouterJD closed 2 years ago

WouterJD commented 2 years ago

bleak.version = 0.14.2 Python version: 3 Operating System: Windows 10 Pro, 21H2, Build 19044.1526, Windows Feature Experience Pack 120.2212.4170.0 Raspberry: rpi0W with raspbian v10 buster, bluetoothctl: 5.50

Description

I have a loop like service_explorer.py which provides different result on Windows as Raspberry

Question

Why would same inspection-loop provide different result?

Windows 10

Inspect BLE-device with address 5C:F3:70:9F:8C:98

Service: 00001800-... (Handle: 1): Generic Access Profile
        Characteristic: 00002a00-... (Handle: 2):                               , props=['read'], value="FortiusANT Trainer"
        Characteristic: 00002a01-... (Handle: 4):                               , props=['read'], value="80 00"
Service: 00001801-... (Handle: 6): Generic Attribute Profile
        Characteristic: 00002a05-... (Handle: 7):                               , props=['indicate'], value="(N/A; not readable)"
Service: 00001826-... (Handle: 10): Fitness Machine
        Characteristic: 00002acc-... (Handle: 11): Fitness Machine Feature      , props=['read'], value="02 40 00 00 08 20 00 00"
                Supported: Cadence, PowerMeasurement, PowerTargetSetting, IndoorBikeSimulation.
        Characteristic: 00002ad2-... (Handle: 14): Indoor Bike Data             , props=['notify'], value="(N/A: Wait for notification)"
        Characteristic: 00002ada-... (Handle: 18): Fitness Machine Status       , props=['notify'], value="(N/A: Wait for notification)"
        Characteristic: 00002ad9-... (Handle: 22): Fitness Machine Control Point, props=['write', 'indicate'], value="(N/A: Wait for indication)"
        Characteristic: 00002ad8-... (Handle: 26): Supported Power Range        , props=['read'], value="00 00 e8 03 01 00"
Service: 0000180d-... (Handle: 29): Heart Rate
        Characteristic: 00002a37-... (Handle: 30): Heart Rate Measurement       , props=['notify'], value="(N/A: Wait for notification)"

Raspberry

Inspect BLE-device with address 5C:F3:70:9F:8C:98

Service: 0000180d-... (Handle: 29): Heart Rate
    Characteristic: 00002a37-... (Handle: 30): Heart Rate Measurement       , props=['notify'], value="(N/A: Wait for notification)"
Service: 00001826-... (Handle: 10): Fitness Machine
    Characteristic: 00002ad8-... (Handle: 26): Supported Power Range        , props=['read'], value="00 00 e8 03 01 00"
    Characteristic: 00002ad9-... (Handle: 22): Fitness Machine Control Point, props=['write', 'indicate'], value="(N/A: Wait for indication)"
    Characteristic: 00002ada-... (Handle: 18): Fitness Machine Status       , props=['notify'], value="(N/A: Wait for notification)"
    Characteristic: 00002ad2-... (Handle: 14): Indoor Bike Data             , props=['notify'], value="(N/A: Wait for notification)"
    Characteristic: 00002acc-... (Handle: 11): Fitness Machine Feature      , props=['read'], value="02 40 00 00 08 20 00 00"
        Supported: Cadence, PowerMeasurement, PowerTargetSetting, IndoorBikeSimulation.
Service: 00001801-... (Handle: 6): Generic Attribute Profile
    Characteristic: 00002a05-... (Handle: 7): Service Changed               , props=['indicate'], value="(N/A; not readable)"

Code

        #-----------------------------------------------------------------------
        # Inspect all services of the server
        #-----------------------------------------------------------------------
        for service in client.services:
            s = "Service: %s" % service
            s = s.replace(bc.BluetoothBaseUUIDsuffix, '-...')  # Always the same
            print(s)
            #-------------------------------------------------------------------
            # Inspect all characteristics of the service
            #-------------------------------------------------------------------
            for char in service.characteristics:
                #---------------------------------------------------------------
                # Print characteristic, properties and value
                #---------------------------------------------------------------
                if "read" in char.properties:
                    try:
                        value = bytes(await client.read_gatt_char(char.uuid))
                    except Exception as e:
                        value = e
                elif "notify" in char.properties:
                    value = '(N/A: Wait for notification)'      # Initiated by server
                elif "indicate" in char.properties and "write" in char.properties:
                    value = '(N/A: Wait for indication)'        # Requested by client
                else:
                    value = '(N/A; not readable)'

                if char.uuid == bc.cDeviceNameUUID:
                    s = '"' + value.decode('ascii') + '"'       # Device name should be printable
                else:
                    s = logfile.HexSpace(value)

                s = '\tCharacteristic: %-80s, props=%s, value=%s' % (char, char.properties, s)
                s = s.replace(bc.BluetoothBaseUUIDsuffix, '-...') # Always the same
                print(s)
dlech commented 2 years ago

BlueZ likes to hide services that it handles internally. In this case Generic Access Profile. Bleak has some workarounds to simulate the device name and battery characteristics that are hidden by BlueZ.

WouterJD commented 2 years ago

Hey thanks!

From where should I get the DeviceName and related attributes?

dlech commented 2 years ago

Just read the characteristic using the Device Name UUID.

WouterJD commented 2 years ago

Thanks, I will further implement