IanHarvey / bluepy

Python interface to Bluetooth LE on Linux
Other
1.61k stars 490 forks source link

TypeError: %X format: a number is required, not str #278

Closed ferologics closed 6 years ago

ferologics commented 6 years ago

I used bleah and got failed to scan for my device. If I turn it off the scan goes through just fine because it won't try to parse the mac UUID.

Full trace log 👉

Traceback (most recent call last):
  File "/usr/local/bin/bleah", line 4, in <module>
    __import__('pkg_resources').run_script('bleah==1.0.0', 'bleah')
  File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 719, in run_script
    self.require(requires)[0].run_script(script_name, ns)
  File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 1504, in run_script
    exec(code, namespace, namespace)
  File "/usr/local/lib/python2.7/dist-packages/bleah-1.0.0-py2.7.egg/EGG-INFO/scripts/bleah", line 148, in <module>
    main()
  File "/usr/local/lib/python2.7/dist-packages/bleah-1.0.0-py2.7.egg/EGG-INFO/scripts/bleah", line 108, in main
    devices = start_scan(args)
  File "/usr/local/lib/python2.7/dist-packages/bleah-1.0.0-py2.7.egg/bleah/scan.py", line 163, in start_scan
    return scanner.scan(args.timeout)
  File "/usr/local/lib/python2.7/dist-packages/bluepy-1.1.4-py2.7.egg/bluepy/btle.py", line 734, in scan
    self.process(timeout)
  File "/usr/local/lib/python2.7/dist-packages/bleah-1.0.0-py2.7.egg/bleah/scan.py", line 78, in process
    self.delegate.handleDiscovery(dev, (dev.updateCount <= 1), isNewData)
  File "/usr/local/lib/python2.7/dist-packages/bleah-1.0.0-py2.7.egg/bleah/scan.py", line 135, in handleDiscovery
    for ( tag, desc, val ) in dev.getScanData():
  File "/usr/local/lib/python2.7/dist-packages/bluepy-1.1.4-py2.7.egg/bluepy/btle.py", line 655, in getScanData
    for sdid in self.scanData.keys() ]
  File "/usr/local/lib/python2.7/dist-packages/bluepy-1.1.4-py2.7.egg/bluepy/btle.py", line 650, in getValueText
    return str(self.getValue(sdid))
  File "/usr/local/lib/python2.7/dist-packages/bluepy-1.1.4-py2.7.egg/bluepy/btle.py", line 641, in getValue
    return self._decodeUUID(val,2)
  File "/usr/local/lib/python2.7/dist-packages/bluepy-1.1.4-py2.7.egg/bluepy/btle.py", line 622, in _decodeUUID
    rs = ("%02X" % val[i]) + rs
TypeError: %X format: a number is required, not str
PrzemoF commented 6 years ago

I can't recreate it. What version of bluepy/bleah do you use? I see the version numbers, but bleah version number hasn't been touched for 9 months. bluepy tag for version 1.1.4 has been set in Nov 2017. By "it" you mean the device? If so, do you know what's the UUID of the device?

mdeverhart commented 6 years ago

I'm able to recreate this with bluepy 1.1.4 and bleah-1.0.0 as well. My device is returning a value of 0xFF00 for Generic Access Profile data type 0x3 (Complete List of 16-bit Service Class UUIDs):

sudo bleah -b "30:AE:A4:89:A8:86" -e

Trace Log:

Traceback (most recent call last):
  File "/bin/bleah", line 5, in <module>
    pkg_resources.run_script('bleah==1.0.0', 'bleah')
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 540, in run_script
    self.require(requires)[0].run_script(script_name, ns)
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 1455, in run_script
    execfile(script_filename, namespace, namespace)
  File "/usr/lib/python2.7/site-packages/bleah-1.0.0-py2.7.egg/EGG-INFO/scripts/bleah", line 148, in <module>
    main()
  File "/usr/lib/python2.7/site-packages/bleah-1.0.0-py2.7.egg/EGG-INFO/scripts/bleah", line 108, in main
    devices = start_scan(args)
  File "/usr/lib/python2.7/site-packages/bleah-1.0.0-py2.7.egg/bleah/scan.py", line 163, in start_scan
    return scanner.scan(args.timeout)
  File "/usr/lib/python2.7/site-packages/bluepy-1.1.4-py2.7.egg/bluepy/btle.py", line 734, in scan
    self.process(timeout)
  File "/usr/lib/python2.7/site-packages/bleah-1.0.0-py2.7.egg/bleah/scan.py", line 78, in process
    self.delegate.handleDiscovery(dev, (dev.updateCount <= 1), isNewData)
  File "/usr/lib/python2.7/site-packages/bleah-1.0.0-py2.7.egg/bleah/scan.py", line 135, in handleDiscovery
    for ( tag, desc, val ) in dev.getScanData():
  File "/usr/lib/python2.7/site-packages/bluepy-1.1.4-py2.7.egg/bluepy/btle.py", line 655, in getScanData
    for sdid in self.scanData.keys() ]
  File "/usr/lib/python2.7/site-packages/bluepy-1.1.4-py2.7.egg/bluepy/btle.py", line 650, in getValueText
    return str(self.getValue(sdid))
  File "/usr/lib/python2.7/site-packages/bluepy-1.1.4-py2.7.egg/bluepy/btle.py", line 641, in getValue
    return self._decodeUUID(val,2)
  File "/usr/lib/python2.7/site-packages/bluepy-1.1.4-py2.7.egg/bluepy/btle.py", line 622, in _decodeUUID
    rs = ("%02X" % val[i]) + rs
TypeError: %X format: a number is required, not str

Poking around with PDB, I can see that one of the Generic Access Profiles (0x3) is returning a byte-string of '\xff\x00', which _decodeUUID is choking on:

/usr/lib/python2.7/site-packages/bluepy-1.1.4-py2.7.egg/bluepy/btle.py(656)getScanData()
-> return [ (sdid, self.getDescription(sdid), self.getValueText(sdid))
(Pdb) self.scanData.keys()
[1, 10, 3, 9]
(Pdb) self.getDescription(1)
'Flags'
(Pdb) self.getValueText(1)
'06'
(Pdb) self.getDescription(10)
'Tx Power'
(Pdb) self.getValueText(10)
'eb'
(Pdb) self.getDescription(9)
'Complete Local Name'
(Pdb) self.getValueText(9)
'BLECTF'
(Pdb) self.getDescription(3)
'Complete 16b Services'
(Pdb) self.getValueText(3)
*** TypeError: %X format: a number is required, not str
(Pdb) self.getValue(3)
*** TypeError: %X format: a number is required, not str
(Pdb) self.scanData.get(3, None)
'\xff\x00'
(Pdb) val = self.scanData.get(3, None)
(Pdb) type(val)
<type 'str'>
(Pdb) val[0]
'\xff'
(Pdb) val[1]
'\x00'
(Pdb) type(val[0])
<type 'str'>
(Pdb) type(val[1])
<type 'str'>
(Pdb) ("%02X" % val[0])
*** TypeError: %X format: a number is required, not str

I ran a BLE scan and dumped the advertisement packets. My device has address 30:AE:A4:89:A8:86, which corresponds to this advertisement:

sudo hcitool lescan --duplicates &
sudo hcidump --raw
...
> 04 3E 1E 02 01 00 00 86 A8 89 A4 AE 30 12 02 01 06 02 0A EB 
  03 03 FF 00 07 09 42 4C 45 43 54 46 CA
...

Scanning with bleah causes the same failure as attempting to connect to the device with bleah. hcitool can scan for the device successfully, and gatttool can connect to it and dump characteristics/etc.

ferologics commented 6 years ago

I concur with the above, experiencing identical behavior.

extremecoders-re commented 6 years ago

As per the findings @mdeverhart, patching the _decodeUUID function fixes the error.

https://github.com/IanHarvey/bluepy/blob/927e37c38a3ea91f838cef698d2bedf6a64cf0cc/bluepy/btle.py#L616-L623

Patched

    def _decodeUUID(self, val, nbytes):
        if len(val) < nbytes:
            return None
        rs=""
        # Bytes are little-endian; convert to big-endian string
        for i in range(nbytes):
            if type(val[i]) == int:
                rs = ("%02X" % val[i]) + rs
            else:
                rs = ("%02X" % ord(val[i])) + rs
        return UUID(rs)
IanHarvey commented 6 years ago

This is a Python 2 vs Python 3 problem; when I wrote it it worked for me on Python 3, but I couldn't have tested it on 2.

I'm slightly puzzled that this is being reported against version 1.1.4, or things installed from PyPI. The code in question was added in 927e37c38 - the 1.1.4 release was way back in 2017.

IanHarvey commented 6 years ago

I've justed pushed e27f9c7 which should fix it on Python 2.7.

Let me know if this fixes it. You'll have to install from source.

mdeverhart commented 6 years ago

Yes, cloning the latest from GitHub resolves the issue while running Python 2.7. Thanks!

I believe that the confusion around the version number (1.1.4) is that the version number doesn't appear to have been changed since its release in 2017, but I didn't realize it. I can confirm that I was running the latest source from GitHub as of 7/10 when I reported my findings.

IanHarvey commented 6 years ago

I've now made a 1.2.0 release with this code in, so hopefully you can pip install Bluepy and get the fixed version.