Skip to content

Commit

Permalink
leave BLE scanning timeout context manager with return instead of break
Browse files Browse the repository at this point in the history
  • Loading branch information
MAKOMO committed Oct 29, 2024
1 parent 80cecda commit abb3982
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 26 deletions.
6 changes: 3 additions & 3 deletions src/artisanlib/acaia.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ def fast_notifications(self) -> None:


def notify_callback(self, _sender:'BleakGATTCharacteristic', data:bytearray) -> None:
if self._async_loop_thread is not None:
if hasattr(self, '_async_loop_thread') and self._async_loop_thread is not None:
asyncio.run_coroutine_threadsafe(
self._read_queue.put(bytes(data)),
self._async_loop_thread.loop)
Expand Down Expand Up @@ -482,7 +482,7 @@ async def reader(self, stream:IteratorReader) -> None:


def on_start(self) -> None:
if self._async_loop_thread is not None:
if hasattr(self, '_async_loop_thread') and self._async_loop_thread is not None:
# start the reader
asyncio.run_coroutine_threadsafe(
self.reader(self._input_stream),
Expand All @@ -501,7 +501,7 @@ def battery_changed(self, new_value:int) -> None: # pylint: disable=no-self-use
# QObject needs to go first in this mixing and AcaiaBLE and its super class are not allowed to hold __slots__
class Acaia(QObject, AcaiaBLE): # pyright: ignore [reportGeneralTypeIssues] # Argument to class must be a base class

weight_changed_signal = pyqtSignal(int) # delivers new weight in g
weight_changed_signal = pyqtSignal(int) # delivers new weight in g
battery_changed_signal = pyqtSignal(int) # delivers new batter level in %
disconnected_signal = pyqtSignal() # issued on disconnect

Expand Down
51 changes: 30 additions & 21 deletions src/artisanlib/ble_port.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,33 @@ def description_match(self, bd:'BLEDevice', ad:'AdvertisementData',
return True, service_uuid
return False, None


# returns discovered_bd and service_uuid on success, or None
async def _scan(self,
device_descriptions:Dict[Optional[str],Optional[Set[str]]],
blacklist:Set[str],
case_sensitive:bool,
scan_timeout:float) -> 'Tuple[Optional[BLEDevice], Optional[str]]':
try:
async with asyncio.timeout(scan_timeout): # type:ignore[attr-defined]
async with BleakScanner() as scanner:
self._terminate_scan_event.clear()
async for bd, ad in scanner.advertisement_data():
if self._terminate_scan_event.is_set():
return None, None
#_log.debug("device %s, (%s): %s", bd.name, ad.local_name, ad.service_uuids)
if bd.address not in blacklist:
res:bool
res_service_uuid:Optional[str]
res, res_service_uuid = self.description_match(bd,ad,device_descriptions,case_sensitive)
if res:
_log.debug('BLE device match')
# we return the first discovered device that matches the given device descriptions
return bd, res_service_uuid
except asyncio.TimeoutError:
_log.debug('timeout on BLE scanning')
return None, None

# pylint: disable=too-many-positional-arguments
async def _scan_and_connect(self,
device_descriptions:Dict[Optional[str],Optional[Set[str]]],
Expand All @@ -84,26 +111,7 @@ async def _scan_and_connect(self,
# can cause errors
discovered_bd:Optional[BLEDevice] = None
service_uuid:Optional[str] = None
try:
async with asyncio.timeout(scan_timeout): # type:ignore[attr-defined]
async with BleakScanner() as scanner:
self._terminate_scan_event.clear()
async for bd, ad in scanner.advertisement_data():
if self._terminate_scan_event.is_set():
break
# _log.debug("device %s, (%s): %s", bd.name, ad.local_name, ad.service_uuids)
if bd.address not in blacklist:
res:bool
res_service_uuid:Optional[str]
res, res_service_uuid = self.description_match(bd,ad,device_descriptions,case_sensitive)
if res:
_log.debug('BLE device match')
# we return the first discovered device that matches the given device descriptions
discovered_bd = bd
service_uuid = res_service_uuid
break
except asyncio.TimeoutError:
_log.debug('timeout on BLE scanning')
discovered_bd, service_uuid = await self._scan(device_descriptions, blacklist, case_sensitive, scan_timeout)
if discovered_bd is None:
return None, None
client = BleakClient(
Expand Down Expand Up @@ -402,11 +410,12 @@ def stop(self) -> None:
if self._ble_client is None:
ble.terminate_scan() # we stop ongoing scanning
self._disconnect()
#del self._async_loop_thread # on this level the released object should be automatically collected by the GC
del self._async_loop_thread # on this level the released object should be automatically collected by the GC
self._async_loop_thread = None
self._ble_client = None
self._connected_service_uuid = None
self.on_stop()
_log.error('BLE client stopped')
else:
_log.error('BLE client not running')

Expand Down
4 changes: 2 additions & 2 deletions src/artisanlib/santoker.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def __init__(self,

def notify_callback(self, _sender:'BleakGATTCharacteristic', data:bytearray) -> None:
# _log.debug("notify: %s => %s", self._read_queue.qsize(), data)
if self._async_loop_thread is not None:
if hasattr(self, '_async_loop_thread') and self._async_loop_thread is not None:
asyncio.run_coroutine_threadsafe(
self._read_queue.put(bytes(data)),
self._async_loop_thread.loop)
Expand All @@ -81,7 +81,7 @@ async def reader(self, stream:IteratorReader) -> None:
await self._read_msg(stream)

def on_start(self) -> None:
if self._async_loop_thread is not None:
if hasattr(self, '_async_loop_thread') and self._async_loop_thread is not None:
# start the reader
asyncio.run_coroutine_threadsafe(
self.reader(self._input_stream),
Expand Down

0 comments on commit abb3982

Please sign in to comment.