|
11 | 11 | import bleak |
12 | 12 | from bleak.backends.device import BLEDevice |
13 | 13 | from bleak.backends.scanner import AdvertisementData |
| 14 | +from bleak_retry_connector import BleakClient, establish_connection |
14 | 15 |
|
15 | 16 | DEFAULT_RETRY_COUNT = 3 |
16 | 17 | DEFAULT_RETRY_TIMEOUT = 1 |
@@ -285,61 +286,62 @@ def _commandkey(self, key: str) -> str: |
285 | 286 | async def _sendcommand(self, key: str, retry: int) -> bytes: |
286 | 287 | """Send command to device and read response.""" |
287 | 288 | command = bytearray.fromhex(self._commandkey(key)) |
288 | | - notify_msg = b"" |
289 | 289 | _LOGGER.debug("Sending command to switchbot %s", command) |
290 | | - |
| 290 | + max_attempts = retry + 1 |
291 | 291 | async with CONNECT_LOCK: |
292 | | - try: |
293 | | - async with bleak.BleakClient( |
294 | | - address_or_ble_device=self._device, |
295 | | - timeout=float(self._scan_timeout), |
296 | | - ) as client: |
297 | | - _LOGGER.debug("Connnected to switchbot: %s", client.is_connected) |
298 | | - |
299 | | - _LOGGER.debug("Subscribe to notifications") |
300 | | - await client.start_notify( |
301 | | - _sb_uuid(comms_type="rx"), self._notification_handler |
302 | | - ) |
303 | | - |
304 | | - _LOGGER.debug("Sending command, %s", key) |
305 | | - await client.write_gatt_char( |
306 | | - _sb_uuid(comms_type="tx"), command, False |
307 | | - ) |
308 | | - |
309 | | - await asyncio.sleep( |
310 | | - 1.0 |
311 | | - ) # Bot needs pause. Otherwise notification could be missed. |
312 | | - |
313 | | - notify_msg = self._last_notification |
314 | | - _LOGGER.info("Notification received: %s", notify_msg) |
| 292 | + for attempt in range(max_attempts): |
| 293 | + try: |
| 294 | + return await self._send_command_locked(key, command) |
| 295 | + except (bleak.BleakError, asyncio.exceptions.TimeoutError): |
| 296 | + if attempt == retry: |
| 297 | + _LOGGER.error( |
| 298 | + "Switchbot communication failed. Stopping trying", |
| 299 | + exc_info=True, |
| 300 | + ) |
| 301 | + return b"\x00" |
| 302 | + |
| 303 | + _LOGGER.debug("Switchbot communication failed with:", exc_info=True) |
| 304 | + |
| 305 | + raise RuntimeError("Unreachable") |
| 306 | + |
| 307 | + async def _send_command_locked(self, key: str, command: bytes) -> bytes: |
| 308 | + """Send command to device and read response.""" |
| 309 | + client: BleakClient | None = None |
| 310 | + try: |
| 311 | + _LOGGER.debug("Connnecting to switchbot: %s", self._device.address) |
315 | 312 |
|
316 | | - _LOGGER.debug("UnSubscribe to notifications") |
317 | | - await client.stop_notify(_sb_uuid(comms_type="rx")) |
| 313 | + client = await establish_connection( |
| 314 | + BleakClient, self._device.address, self._device, max_attempts=1 |
| 315 | + ) |
| 316 | + _LOGGER.debug("Connnected to switchbot: %s", client.is_connected) |
318 | 317 |
|
319 | | - except (bleak.BleakError, asyncio.exceptions.TimeoutError): |
| 318 | + _LOGGER.debug("Subscribe to notifications") |
| 319 | + await client.start_notify( |
| 320 | + _sb_uuid(comms_type="rx"), self._notification_handler |
| 321 | + ) |
320 | 322 |
|
321 | | - if retry < 1: |
322 | | - _LOGGER.error( |
323 | | - "Switchbot communication failed. Stopping trying", exc_info=True |
324 | | - ) |
325 | | - return b"\x00" |
| 323 | + _LOGGER.debug("Sending command, %s", key) |
| 324 | + await client.write_gatt_char(_sb_uuid(comms_type="tx"), command, False) |
326 | 325 |
|
327 | | - _LOGGER.debug("Switchbot communication failed with:", exc_info=True) |
| 326 | + await asyncio.sleep( |
| 327 | + 1.0 |
| 328 | + ) # Bot needs pause. Otherwise notification could be missed. |
328 | 329 |
|
329 | | - if notify_msg: |
330 | | - if notify_msg == b"\x07": |
331 | | - _LOGGER.error("Password required") |
332 | | - elif notify_msg == b"\t": |
333 | | - _LOGGER.error("Password incorrect") |
334 | | - return notify_msg |
| 330 | + notify_msg = self._last_notification |
| 331 | + _LOGGER.info("Notification received: %s", notify_msg) |
335 | 332 |
|
336 | | - _LOGGER.warning("Cannot connect to Switchbot. Retrying (remaining: %d)", retry) |
| 333 | + _LOGGER.debug("UnSubscribe to notifications") |
| 334 | + await client.stop_notify(_sb_uuid(comms_type="rx")) |
337 | 335 |
|
338 | | - if retry < 1: # failsafe |
339 | | - return b"\x00" |
| 336 | + finally: |
| 337 | + if client: |
| 338 | + await client.disconnect() |
340 | 339 |
|
341 | | - await asyncio.sleep(DEFAULT_RETRY_TIMEOUT) |
342 | | - return await self._sendcommand(key, retry - 1) |
| 340 | + if notify_msg == b"\x07": |
| 341 | + _LOGGER.error("Password required") |
| 342 | + elif notify_msg == b"\t": |
| 343 | + _LOGGER.error("Password incorrect") |
| 344 | + return notify_msg |
343 | 345 |
|
344 | 346 | def get_address(self) -> str: |
345 | 347 | """Return address of device.""" |
|
0 commit comments