class KanoBLEClient(object): def __init__(self, wand_address, loop, spells=None): self.wand_address = wand_address self.int_decoder = BinToInt(48) self.float_decoder = BinToFloat(16, 31) self.client = None self.decoder = wand_decoder() self.wand_sensors = WandSensors(self.sensor_update_handler) self.loop = loop self.dateframe_handler = None async def connect(self, debug=False): if debug: import sys l = logging.getLogger("asyncio") l.setLevel(logging.DEBUG) h = logging.StreamHandler(sys.stdout) h.setLevel(logging.DEBUG) l.addHandler(h) logger.addHandler(h) self.client = BleakClient(self.wand_address, loop=self.loop) await self.client.connect() x = await self.client.is_connected() logger.info("Connected: {0}".format(x)) async def start_recieving_data(self, sensors=None): await start_notify(self.client, self.sensor_handling, sensors) async def stop_recieving_data(self, sensors): await stop_notify(self.client, sensors) def change_spell(self, spell): self.wand_sensors.data_folder = "./training_data/" + spell + "/" print(f"changed wand folder to {self.wand_sensors.data_folder}") def sensor_update_handler(self, sensors): print(sensors) # print("SENSORS RECIEVED") return def sensor_handling(self, sender, data): """Simple notification handler which prints the data received.""" sender = get_key(sender, CHARACTERISTIC_UUIDS) if sender == BUTTON: self.wand_sensors.set_button(self.decoder.decode_button(data)) self.loop.run_until_complete( self.client.write_gatt_char(RESET_CHAR, struct.pack("h", 1))) elif sender == NINE_AXIS: self.wand_sensors.set_gyro(*self.decoder.decode_nine_axis(data)) elif sender == ACCELEROMETER: self.wand_sensors.set_accel(*self.decoder.decode_nine_axis(data)) elif sender == BATTERY: self.decoder.decode_battery(data) elif sender == TEMPERATURE: self.wand_sensors.set_temp(self.decoder.decode_temp(data)) elif sender == MAGNETOMETER: self.wand_sensors.set_magneto(self.decoder.decode_magnet(data))
def send_data(self, client: BleakClient, packet: Packet, requires_response: bool = True): data = bytes(packet) logger.debug(f"State: {self.state}, sending: {hexlify(data)}") if requires_response: self._event.clear() return client.write_gatt_char(GATT_WRITE, data)
class BleakLink(BaseLink): def __init__(self, device="hci0", loop=None, *args, **kwargs): self.device = device self.timeout = 5 self.loop = loop or asyncio.get_event_loop() self._rx_fifo = Fifo() self._client = None self._th = None def __enter__(self): self.start() return self def start(self): if self._th: return self._th = Thread(target=run_worker, args=(self.loop, )) self._th.daemon = True self._th.start() def __exit__(self, exc_type, exc_value, traceback): if self._client: self.close() def close(self): asyncio.run_coroutine_threadsafe(self._client.disconnect(), self.loop).result(10) def scan(self, timeout=1): devices = asyncio.run_coroutine_threadsafe( discover(timeout=timeout, device=self.device), self.loop).result(timeout * 3) # We need to keep scanning going for connect() to properly work asyncio.run_coroutine_threadsafe( discover(timeout=timeout, device=self.device), self.loop) return [ (dev.name, dev.address) for dev in devices if dev.metadata.get('manufacturer_data', {}).get(_manuf_id, []) in [_manuf_data_xiaomi, _manuf_data_xiaomi_pro, _manuf_data_ninebot] ] def open(self, port): fut = asyncio.run_coroutine_threadsafe(self._connect(port), self.loop) fut.result(10) async def _connect(self, port): if isinstance(port, tuple): port = port[1] self._client = BleakClient(port, device=self.device) await self._client.connect() print("connected") await self._client.start_notify(_tx_char_uuid, self._data_received) print("services:", list(await self._client.get_services())) def _data_received(self, sender, data): self._rx_fifo.write(data) def write(self, data): fut = asyncio.run_coroutine_threadsafe( self._client.write_gatt_char(_rx_char_uuid, bytearray(data), True), self.loop, ) return fut.result(3) def read(self, size): try: data = self._rx_fifo.read(size, timeout=self.timeout) except queue.Empty: raise LinkTimeoutException return data def fetch_keys(self): return asyncio.run_coroutine_threadsafe( self._client.read_gatt_char(_keys_char_uuid), self.loop).result(5)
class IdasenDesk: """ Idasen desk. Args: mac: Bluetooth MAC address of the desk. exit_on_fail: If set to True, failing to connect will call ``sys.exit(1)``, otherwise the exception will be raised. Note: There is no locking to prevent you from running multiple movement coroutines simultaneously. Example: Basic Usage:: from idasen import IdasenDesk async with IdasenDesk(mac="AA:AA:AA:AA:AA:AA") as desk: # call methods here... """ #: Minimum desk height in meters. MIN_HEIGHT: float = 0.62 #: Maximum desk height in meters. MAX_HEIGHT: float = 1.27 #: Number of times to retry upon failure to connect. RETRY_COUNT: int = 3 def __init__(self, mac: str, exit_on_fail: bool = False): self._logger = _DeskLoggingAdapter(logger=logging.getLogger(__name__), extra={"mac": mac}) self._mac = mac self._exit_on_fail = exit_on_fail self._client = BleakClient(self._mac) async def __aenter__(self): await self._connect() return self async def __aexit__(self, *args, **kwargs) -> Optional[bool]: return await self._client.__aexit__(*args, **kwargs) async def _connect(self): i = 0 while True: try: await self._client.__aenter__() return except Exception: if i >= self.RETRY_COUNT: self._logger.critical("Connection failed") if self._exit_on_fail: sys.exit(1) raise i += 1 self._logger.warning( f"Failed to connect, retrying ({i}/{self.RETRY_COUNT})...") time.sleep(0.3 * i) async def is_connected(self) -> bool: """ Check connection status of the desk. Returns: Boolean representing connection status. >>> async def example() -> bool: ... async with IdasenDesk(mac="AA:AA:AA:AA:AA:AA") as desk: ... return await desk.is_connected() >>> asyncio.run(example()) True """ return await self._client.is_connected() @property def mac(self) -> str: """ Desk MAC address. """ return self._mac async def move_up(self): """ Move the desk upwards. This command moves the desk upwards for a fixed duration (approximately one second) as set by your desk controller. >>> async def example(): ... async with IdasenDesk(mac="AA:AA:AA:AA:AA:AA") as desk: ... await desk.move_up() >>> asyncio.run(example()) """ await self._client.write_gatt_char(_UUID_COMMAND, _COMMAND_UP, response=False) async def move_down(self): """ Move the desk downwards. This command moves the desk downwards for a fixed duration (approximately one second) as set by your desk controller. >>> async def example(): ... async with IdasenDesk(mac="AA:AA:AA:AA:AA:AA") as desk: ... await desk.move_down() >>> asyncio.run(example()) """ await self._client.write_gatt_char(_UUID_COMMAND, _COMMAND_DOWN, response=False) async def move_to_target(self, target: float): """ Move the desk to the target position. Args: target: Target position in meters. Raises: ValueError: Target exceeds maximum or minimum limits. >>> async def example(): ... async with IdasenDesk(mac="AA:AA:AA:AA:AA:AA") as desk: ... await desk.move_to_target(1.1) >>> asyncio.run(example()) """ if target > self.MAX_HEIGHT: raise ValueError( f"target position of {target:.3f} meters exceeds maximum of " f"{self.MAX_HEIGHT:.3f}") elif target < self.MIN_HEIGHT: raise ValueError( f"target position of {target:.3f} meters exceeds minimum of " f"{self.MIN_HEIGHT:.3f}") while True: height = await self.get_height() difference = target - height self._logger.debug(f"{target=} {height=} {difference=}") if abs(difference) < 0.005: # tolerance of 0.005 meters self._logger.info(f"reached target of {target:.3f}") await self.stop() return elif difference > 0: await self.move_up() elif difference < 0: await self.move_down() async def stop(self): """ Stop desk movement. """ await asyncio.gather( self._client.write_gatt_char(_UUID_COMMAND, _COMMAND_STOP, response=False), self._client.write_gatt_char(_UUID_REFERENCE_INPUT, _COMMAND_REFERENCE_INPUT_STOP, response=False), ) async def get_height(self) -> float: """ Get the desk height in meters. Returns: Desk height in meters. >>> async def example() -> float: ... async with IdasenDesk(mac="AA:AA:AA:AA:AA:AA") as desk: ... await desk.move_to_target(1.0) ... return await desk.get_height() >>> asyncio.run(example()) 1.0 """ return _bytes_to_meters(await self._client.read_gatt_char(_UUID_HEIGHT)) @classmethod async def discover(cls) -> Optional[str]: """ Try to find the desk's MAC address by discovering currently connected devices. Returns: MAC address if found, ``None`` if not found. """ try: devices = await discover() except Exception: return None return next( (device.address for device in devices if device.name.startswith("Desk")), None, )
global exit_signal_received if isinstance(bulb_led, Bulb): bulb_led.stop_music() if isinstance(device, ble_device_type): device.disconnect() print("exit") exit_signal_received = True # start queue = Queue(maxsize=1) #BLE connection client = BleakClient(address_ledstrip) client.connect() client.write_gatt_char(IO_DATA_CHAR_UUID, bytearray([0xCC, 0x23, 0x33])) #Yeelight Connection bulb_led = Bulb("192.168.1.72", effect="smooth") bulb_led.start_music(2000) time.sleep(1) t = threading.Thread(name="color_change_thread", target=color_setter_thread, args=( bulb_led, client, queue, )) t.start() ble_device_type = BleakClient