Danielhiversen/pySwitchbot

detailed :- Bluetooth command failed (code: 2, error: Attribute can't be read)

Closed this issue · 4 comments

Dear Team,

I can find this error in other threads too, but making a detailed discussion here. I am working with Switchbot Bots (i.e. not curtains)
I have attached the solution (along with a PR - working on this now) and an explanation of this issue below.

Evident by #32 , authors are trying to move away from bluepy, I tried using this new code into PR #44 but there are some other problems there which I'll create a new issue for.

The Problem

press(), turn_on(), turn_off() method when called gives an error:
bluepy.btle.BTLEGattError: Bluetooth command failed (code: 2, error: Attribute can't be read)

Exception has occurred: BTLEGattError (expand for detailed logs)

Exception has occurred: BTLEGattError

Error logs:

  File "/home/xyz/Desktop/projects/original/pySwitchbot/switchbot/__init__.py", line 389, in _readkey
    read_result: bytes = char.read()
  File "/home/xyz/Desktop/projects/original/pySwitchbot/switchbot/__init__.py", line 421, in _sendcommand
    notify_msg = self._readkey()
  File "/home/xyz/Desktop/projects/original/pySwitchbot/switchbot/__init__.py", line 541, in press
    result = self._sendcommand(PRESS_KEY, self._retry_count)
  File "/home/xyz/Desktop/projects/original/pySwitchbot/test.py", line 7, in <module>
    one.press()

Screenshot in VSCode Debugger

Screen Shot 2022-06-05 at 5 02 39 PM

Reproducing the issue

Very simple TBH, just called the functions on a switch

I wrote a simple script for your reference
from switchbot import GetSwitchbotDevices
from switchbot import Switchbot
switchbot_devices = GetSwitchbotDevices().discover()
print(switchbot_devices)

one = Switchbot(mac="xx:xx:xx:xx:xx:xx", password="xxxyyyzzz", retry_count=4)
one.press()

Context

  • I am working with, two Newly purchased switchbots.
  • Firmware version v5.0
  • Switches are in Press Mode and they work excellent when operating from the app.
  • Issuing any of the commands makes the switch operate with no issues, but then the script throws an exception.
  • Tried without registering them to the switchbot app. ( i.e. password not changed )
  • The devices are very close to the sending laptop.
  • Laptop os: Debian 11, Python 3.9.2
  • Tried the published version on pypi, but then came here to understand problems.
  • Have attached the details obtained from GetSwitchbotDevices().discover()
  • I encountered this issue after trying to integrate the switchbots to Homeassistant. There is an issue that it does not expose the "Type" attribute in entity, but that is for some other day.
Switchbot info from BLUEPY and BLEAK

Using bluepy

{'xyxyxyxyxyxy': {
    'mac_address': 'xy:xy:xy:xy:xy:xy',
    'isEncrypted': True,
    'model': 'H',
    'data': {
        'switchMode': False,
        'isOn': False,
        'battery': 100,
        'rssi': -51,
        },
    'modelName': 'WoHand',
    }, 'zpzpzpzpzpzp': {
    'mac_address': 'zp:zp:zp:zp:zp:zp',
    'isEncrypted': True,
    'model': 'H',
    'data': {
        'switchMode': False,
        'isOn': False,
        'battery': 100,
        'rssi': -50,
        },
    'modelName': 'WoHand',
    }}

Using Bleak

{'xyxyxyxyxyxy': {
    'mac_address': 'xy:xy:xy:xy:xy:xy',
    'Flags': '06',
    'Manufacturer': '5900d86c3e8ef67a',
    'Complete 128b Services': 'cba20d00-224d-11e6-9fb8-0002a5d5c51b',
    'data': {
        'switchMode': False,
        'isOn': False,
        'battery': 100,
        'rssi': -56,
        },
    'model': 'H',
    'modelName': 'WoHand',
    }, 'zpzpzpzpzpzp': {
    'mac_address': 'zp:zp:zp:zp:zp:zp',
    'Flags': '06',
    'Manufacturer': '5900fe7529c8360a',
    'Complete 128b Services': 'cba20d00-224d-11e6-9fb8-0002a5d5c51b',
    'data': {
        'switchMode': False,
        'isOn': False,
        'battery': 100,
        'rssi': -50,
        },
    'model': 'H',
    'modelName': 'WoHand',
    }}

The Root Cause

The root cause is calling the _readkey() being called from Line 418

Alternate Hypothesis : The Manyfacturer has changed their return codes in newer devices? Would be excellent if the author can confirm if my outputs of bluepy and bleak match their outputs. The firmware version ??

Solution

Two things have to be done:

  • The _readkey() method needs to be modified to listen to BTLEGattError error on read()
  • _readkey() does not need to be called after sending a command in the first place!!

Elaborations :

Modifying _readkey() method:

the main part which can generate an exception is kept in the else section of the try catch statement!
read_result: bytes = char.read() needs to be inside the try block, since the bluepy library will not throw an exception when calling getCharacteristics() as it only returns an array, the actual reading which might fail occurs after the read() method is called on line 386. To maintain compatibility with older versions, I think we can just use the supportsRead() method which returns true if reading characteristics are supported by the device, before calling the read method. If the device does not support reading, its safe to assume that everything went fine as it disconnected without an issue!
This is evident from the implementation in the nodejs based implementation Line 81

Another change:
Line 390 is not rechable after the function returns on line 387.

No need to call to see if the write command was successful.

I understand the author wants to get if the "write" was successful from this reply : #41 (comment) , but as per bluepy library documentation for write function The function will fail if the write operation fails, so we really don't need to worry again and check if the operation was successful.

Found this SwitchbotAPI-BLE by the Manufacturer.
Seems the read after write might be required for curtains ! not for the bots!

Hi @ninadpchaudhari,

Mind testing the new bleak version (replaces old bluepy library)? I have also modified the switchbot hassio integration to support async calls:

https://github.com/RenierM26/ha-switchbot-curtain

@RenierM26 sincerely appreciates your efforts with the Bleak library. It is certainly an excellent library and a great move from bluepy.

I did try the same version but encountered errors there too. { I mentioned the Details I got from the Bleak version in the summary of "Switchbot info from BLUEPY and BLEAK"}

For anyone who stumbles across this, I had to reboot after switching to the Bleak version and it worked well after!!
I doubt this error would exist with the latest version of the library, hence closing.
Again cheers to @RenierM26 for the port :)