diff --git a/meshtastic/ble_interface.py b/meshtastic/ble_interface.py index 96293d0..0b5c8b7 100644 --- a/meshtastic/ble_interface.py +++ b/meshtastic/ble_interface.py @@ -1,14 +1,15 @@ """Bluetooth interface """ import asyncio +import atexit import logging import struct import time -from threading import Event, Thread +from threading import Thread from typing import Optional -from print_color import print from bleak import BleakClient, BleakScanner, BLEDevice +from print_color import print from meshtastic.mesh_interface import MeshInterface from meshtastic.util import our_exit @@ -20,18 +21,13 @@ FROMNUM_UUID = "ed9da18c-a800-4f66-a670-aa7547e34453" LOGRADIO_UUID = "6c6fd238-78fa-436b-aacf-15c5be1ef2e2" - class BLEInterface(MeshInterface): """MeshInterface using BLE to connect to devices.""" class BLEError(Exception): """An exception class for BLE errors.""" - pass - class BLEState: # pylint: disable=C0115 - THREADS = False - BLE = False - MESH = False + pass def __init__( self, @@ -40,23 +36,23 @@ class BLEInterface(MeshInterface): debugOut=None, noNodes: bool = False, ): - self.state = BLEInterface.BLEState() + MeshInterface.__init__( + self, debugOut=debugOut, noProto=noProto, noNodes=noNodes + ) self.should_read = False logging.debug("Threads starting") - self._receiveThread = Thread(target=self._receiveFromRadioImpl, name="BLEReceive", daemon=True) - self._receiveThread_started = Event() - self._receiveThread_stopped = Event() + self._want_receive = True + self._receiveThread: Optional[Thread] = Thread( + target=self._receiveFromRadioImpl, name="BLEReceive", daemon=True + ) self._receiveThread.start() - self._receiveThread_started.wait(1) - self.state.THREADS = True logging.debug("Threads running") try: logging.debug(f"BLE connecting to: {address if address else 'any'}") - self.client = self.connect(address) - self.state.BLE = True + self.client: Optional[BLEClient] = self.connect(address) logging.debug("BLE connected") except BLEInterface.BLEError as e: self.close() @@ -64,29 +60,30 @@ class BLEInterface(MeshInterface): self.client.start_notify(LOGRADIO_UUID, self.log_radio_handler) - logging.debug("Mesh init starting") - MeshInterface.__init__( - self, debugOut=debugOut, noProto=noProto, noNodes=noNodes - ) + logging.debug("Mesh configure starting") self._startConfig() if not self.noProto: self._waitConnected(timeout=60.0) self.waitForConfig() - self.state.MESH = True logging.debug("Mesh init finished") logging.debug("Register FROMNUM notify callback") self.client.start_notify(FROMNUM_UUID, self.from_num_handler) + # We MUST run atexit (if we can) because otherwise (at least on linux) the BLE device is not disconnected + # and future connection attempts will fail. (BlueZ kinda sucks) + self._exit_handler = atexit.register(self.close) + def from_num_handler(self, _, b): # pylint: disable=C0116 """Handle callbacks for fromnum notify. - Note: this method does not need to be async because it is just setting a bool.""" + Note: this method does not need to be async because it is just setting a bool. + """ from_num = struct.unpack("