Beispiel #1
0
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))
Beispiel #2
0
    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)
Beispiel #3
0
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)
Beispiel #4
0
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,
        )
Beispiel #5
0
    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