Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Converted from the bluepy library to bleak library for bluetooth access #87

Merged
merged 5 commits into from
Jul 14, 2022

Conversation

B1ob
Copy link
Contributor

@B1ob B1ob commented Jul 11, 2022

airthings.py converted to bleak by mjmccans

@B1ob B1ob mentioned this pull request Jul 11, 2022
@B1ob B1ob closed this Jul 11, 2022
Updated version
custom_components/airthings_wave/airthings.py Outdated Show resolved Hide resolved
custom_components/airthings_wave/manifest.json Outdated Show resolved Hide resolved
sensor_data = sensor_decoders[str(characteristic.uuid)].decode_data(data)
_LOGGER.debug("{} Got sensordata {}".format(mac, sensor_data))


# ToDo: Is there a more elegant way to handle the below?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use an asyncio synchronization primitive instead of polling.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fully agree with the polling. I introduced an asyncio.Event. As I understand the Event it is exactly what we need here. Can you have another look?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While unlikely, the challenge with event.wait() is that there is no timeout value and it could block indefinitely if something happens, like a bluetooth disconnect, at the wrong time. I suggest using wait_for instead with a 1 second timeout, which I have tested and works for me:

# Wait for up to one second to see if a callblack comes in.
try:
    await asyncio.wait_for(self._event.wait(), 1)
except asyncio.TimeoutError:
    _LOGGER.warn("Timeout getting command data.")
if self._command_data is not None:
    sensor_data = command_decoders[str(characteristic.uuid)].decode_data(self._command_data)
    self._command_data = None
# Stop notification handler

You can see the full code here.

Pin bleak to 0.14.3
Changed device scanning (updated to latest changes from mjmccans)
Code cleanup
@B1ob B1ob reopened this Jul 12, 2022
@B1ob B1ob marked this pull request as draft July 12, 2022 08:15
self._dev = BleakClient(mac.lower())
ret = await self._dev.connect()
if ret:
_LOGGER.debug("Connected to {}".format(mac))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retry if not connected?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good finding. @mjmccans maybe you can also pull this into your repo

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch @lymanepp. I didn't catch this error because the code still worked because if it failed to connect it tossed an exception and skipped over the break. In fact, I think the same error exists in the current code and worked for the same reason, but I agree this change makes the code more correct. Great to have more eyes on the code.

@B1ob B1ob marked this pull request as ready for review July 13, 2022 06:45
if sn is not None:
if adv.addr not in self.airthing_devices:
self.airthing_devices.append(adv.addr)
if 820 in adv.metadata["manufacturer_data"]: # TODO: Not sure if this is the best way to identify Airthings devices
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance this TODO can be resolved?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lymanepp That was a note to myself, but it can probably be removed now that it has worked for others. As far as I can tell I am using the same method as before, it is just that bleak returns the values quite differently than bluepy.

@MartyTremblay
Copy link
Member

Using a 1st gen Airthings Wave which doesn't support the Hardware Revision Attribute, I'm getting the following message. I think the "Error" wording might be a bit strong and may lead users to think that the component failed.

Also, should we change it from a _LOGGER.exception to a _LOGGER.warning ?

Otherwise, component works great in 2022.6.7 and 2022.7.4 !

This error originated from a custom integration.

Logger: custom_components.airthings_wave.airthings
Source: custom_components/airthings_wave/airthings.py:266
Integration: airthings_wave (documentation, issues)
First occurred: 21:21:41 (1 occurrences)
Last logged: 21:21:41

Error getting info
Traceback (most recent call last):
  File "/config/custom_components/airthings_wave/airthings.py", line 266, in get_info
    data = await self._dev.read_gatt_char(characteristic.uuid)
  File "/usr/local/lib/python3.9/site-packages/bleak/backends/bluezdbus/client.py", line 662, in read_gatt_char
    raise BleakError(
bleak.exc.BleakError: Characteristic with UUID 00002a27-0000-1000-8000-00805f9b34fb could not be found!

@MartyTremblay MartyTremblay merged commit ef4d7ab into custom-components:master Jul 14, 2022
@mjmccans
Copy link

@MartyTremblay I agree with your comment about line 266, and I think the following line with "self._dev = None" should be deleted as well because we should not be giving up if one of the characteristics fails to read. There may actually be an unnecessary try block as well. Perhaps the code could be cleaned up as follows:

        for mac in self.airthing_devices:
            await self.connect(mac)
            if self._dev is not None and self._dev.is_connected:  
                if self._dev is not None and self._dev.is_connected:
                    device = AirthingsDeviceInfo(serial_nr=mac)
                    for characteristic in device_info_characteristics:
                        try:
                            data = await self._dev.read_gatt_char(characteristic.uuid)
                            setattr(device, characteristic.name, data.decode(characteristic.format))
                        except:
                            _LOGGER.warning("Error getting {}".format(characteristic.name))
                self.devices[mac] = device    
                await self.disconnect()
            else:
                _LOGGER.error("Not getting device info because failed to connect to device.")

@B1ob B1ob deleted the convert_to_bleak branch July 14, 2022 06:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants