Ejemplo n.º 1
0
class BLE:
    DATA_CHARACTERISTIC_UUID = ("79b7950e-b94e-4293-bace-1db832cac77c")

    @staticmethod
    def format_notification_data(data):
        N = 2  # All data is assumed to be uint16s (2 bytes)
        grouped_data = [data[n:n + N] for n in range(0, len(data), N)]
        formatted_data = [
            int.from_bytes(temp, byteorder="little", signed=False)
            for temp in grouped_data
        ]
        return formatted_data

    def __init__(self):
        self.client = None
        self.scanned_devices = []

    def scan(self):
        asyncio.run(self.__scan())
        return self.scanned_devices

    def connect(self, address):
        connected = asyncio.run(self.__connect(address))
        return connected

    def disconnect(self):
        asyncio.run(self.__disconnect())

    def enable_notifications(self, uuid, callback):
        asyncio.run(self.__enable_notifications(uuid, callback))

    def disable_notifications(self, uuid):
        asyncio.run(self.__disable_notifications(uuid))

    async def __disable_notifications(self, uuid):
        asyncio.create_task(self.client.stop_notify(uuid))

    async def __enable_notifications(self, uuid, callback):
        asyncio.create_task(self.client.start_notify(uuid, callback))

    async def __connect(self, address):
        loop = asyncio.get_event_loop()
        self.client = BleakClient(address, loop)
        try:
            await self.client.connect()
            return True
        except:
            return False

    async def __disconnect(self):
        asyncio.create_task(self.client.disconnect())

    async def __scan(self):
        dev = await discover()
        for i in range(0, len(dev)):
            self.scanned_devices.append(dev[i])
Ejemplo n.º 2
0
class Ble():
  ble_message_max_size = 512
  command_uuid = uuid.UUID("48754770-0000-1000-8000-00805F9B34FB")
  log_uuid = uuid.UUID("48754771-0000-1000-8000-00805F9B34FB")
  keyboard_uuid = uuid.UUID("48754772-0000-1000-8000-00805F9B34FB")
  property_uuid = uuid.UUID("48754773-0000-1000-8000-00805F9B34FB")

  def __init__(self, hook_keyboard, mac_address):
    self.hook_keyboard = hook_keyboard
    self.connected = False
    self.notification_data = None
    self.mac_address = mac_address
    self.server = None
    self.client = None
    self.log_msg = ""
    self.log_level = 0
    self.terminating = False

    self.loop = asyncio.get_event_loop()
    logging.getLogger('bleak').setLevel(logging.CRITICAL)

  def _disconnected_callback(self, client):
    logging.debug("disconnected callback for: %s", client)
    self.connected = False

  def _command_notyfy_callback(self, _sender: int, data: bytearray):
    self.notification_data = data

  async def _command(self, command_id, data, wait_for_answer):
    payload = bytes([command_id])
    payload += data

    self.notification_data = None
    _answer = await self.client.write_gatt_char(self.command_uuid, payload, not wait_for_answer)
    if wait_for_answer:
      while not self.notification_data:
        await asyncio.sleep(0.01)

    return self.notification_data

  def command(self, command_id, data, wait_for_answer=True):
    if len(data) > self.ble_message_max_size:
      logging.error("try to send too long data %s for command: %d", data.hex(), command_id)
      return None
    else:
      return self.loop.run_until_complete(self._command(command_id, data, wait_for_answer))

  async def _key_pressed(self, scan_code, key_name):
    data = scan_code.to_bytes(2, byteorder='big', signed=True) + key_name.encode("utf-8")
    await self.client.write_gatt_char(self.keyboard_uuid, data, False)

  def key_pressed(self, scan_code, key_name):
    self.loop.create_task(self._key_pressed(scan_code, key_name))

  def _log_callback(self, _sender: int, data: bytearray):
    is_first = not self.log_msg
    if is_first:
      level = data[0]

    is_last = data[-1] != '\f'.encode("utf-8")[0]
    self.log_msg += data[(1 if is_first else 0) : (len(data) if is_last else -1)].decode("utf-8") # skip level and \f

    if is_last:
      logging.log(level, self.log_msg)
      self.log_msg = ""

  def _detection_callback(self, device, _advertisement_data):
    if device.name and device.name.startswith("HuGo"):
      reduced_address = device.address.replace(':', '')
      if not self.mac_address or self.mac_address.lower() == reduced_address.lower():
        self.server = device.address
      logging.debug("HuGo has been found: '%s, %s'", device.name, device.address)

  async def _scan(self):
    logging.info("Scanning...")
    scanner = BleakScanner()
    scanner.register_detection_callback(self._detection_callback)

    logging.debug("searching HuGo device")
    waiting = 0.5 # to be cough power_up window in case of power save
    countdown = int(90 / waiting) #~1.5 min
    self.server = None
    while countdown:
      await scanner.start()
      await asyncio.sleep(waiting)
      await scanner.stop()

      if self.server:
        logging.debug("server found %s", self.server )
        logging.info("Scanning DONE")
        return True

      countdown -= 1
    logging.info("Scanning FAILED")
    return False


  async def _connect(self):
    if not self.server:
      if not await self._scan():
        logging.error("HuGo not found")
        return False

    logging.info("connecting to %s", str(self.server))
    for _i in range(9): # ~90 sec
      try:
        self.client = BleakClient(self.server, loop=self.loop) #it is better to create client again when the connection fails. in some cases the connections is created partially and is not possible to establish the new one
        self.connected = await self.client.connect()
        logging.debug("device was connected with status: %s", self.connected)
        if not self.connected:
          return False

        self.client.set_disconnected_callback(self._disconnected_callback)
        await self.client.start_notify(self.command_uuid, self._command_notyfy_callback)
        #FIXME: should be removed when logging is finished
        try:
          await self.client.start_notify(self.log_uuid, self._log_callback)
        except Exception as error:
          logging.warning("logging start_notify failed %s", error)
        return True
      except Exception as e:
        logging.debug("connection error: %s", e)
        logging.debug("device was not connected via BLE")
    return False


  def connect(self):
    return self.loop.run_until_complete(self._connect())

  def disconnect(self):
    logging.info("disconnecting...")
    self.loop.run_until_complete(self.client.disconnect())
    logging.info("disconnecting DONE")
    self.connected = False

  def keyboard_monitor(self, key_event):
    if key_event.name == "esc":
      keyboard.unhook_all()
      self.terminating = True
    else:
      self.key_pressed(key_event.scan_code, key_event.name)

  async def _async_monitor(self):
    if self.hook_keyboard:
      logging.info("Keyboard monitoring. Press 'Esc' to finish.")
      keyboard.on_press(self.keyboard_monitor, suppress=True)

    while True:
      if self.terminating:
        logging.debug("terminating...")
        break

      if not self.connected:
        await self._connect()

      await asyncio.sleep(0.1)

  def monitor(self):
    try:
      self.loop.run_until_complete(self._async_monitor())
    except KeyboardInterrupt:
      self.terminating = True
Ejemplo n.º 3
0
class PolarClient:
    def __init__(self, path, name):
        # Configuration.
        parser = argparse.ArgumentParser()
        parser.add_argument("-i",
                            "--inifile",
                            default=os.path.join(path, name + '.ini'),
                            help="name of the configuration file")
        args = parser.parse_args()

        config = configparser.ConfigParser(inline_comment_prefixes=('#', ';'))
        config.read(args.inifile)

        # Redis.
        try:
            rds = redis.StrictRedis(host=config.get('redis', 'hostname'),
                                    port=config.getint('redis', 'port'),
                                    db=0,
                                    charset='utf-8',
                                    decode_responses=True)
        except redis.ConnectionError as e:
            raise RuntimeError(e)

        # Combine the patching from the configuration file and Redis.
        self.patch = EEGsynth.patch(config, rds)

        # BLE client.
        self.loop = asyncio.get_event_loop()
        self.ble_client = BleakClient(self.patch.getstring("input", "mac"),
                                      loop=self.loop)

        self.monitor = EEGsynth.monitor(name=name,
                                        debug=self.patch.getint(
                                            "general", "debug"))

    async def connect(self):
        self.monitor.info("Trying to connect to Polar belt {0}".format(
            self.patch.getstring("input", "mac")))
        await self.ble_client.connect()
        await self.ble_client.start_notify(
            self.patch.getstring("input", "hr_uuid"), self.data_handler)
        self.monitor.success("Connected to Polar belt {0}".format(
            self.patch.getstring("input", "mac")))

    def start(self):
        asyncio.ensure_future(self.connect())
        self.loop.run_forever()

    def stop(self):
        asyncio.ensure_future(
            self.ble_client.stop_notify(
                self.patch.getstring("input", "hr_uuid")))
        asyncio.ensure_future(self.ble_client.disconnect())
        self.monitor.success("Disconnected from Polar belt {0}".format(
            self.patch.getstring("input", "mac")))
        sys.exit()

    def data_handler(self, sender,
                     data):  # sender (UUID) unused but required by Bleak API
        """
        data has up to 6 bytes:
        byte 1: flags
            00 = only HR
            16 = HR and IBI(s)
        byte 2: HR
        byte 3 and 4: IBI1
        byte 5 and 6: IBI2 (if present)
        byte 7 and 8: IBI3 (if present)
        etc.

        Polar H10 Heart Rate Characteristics
        (UUID: 00002a37-0000-1000-8000-00805f9b34fb):
            + Energy expenditure is not transmitted
            + HR only transmitted as uint8, no need to check if HR is
              transmitted as uint8 or uint16 (necessary for bpm > 255)
        Acceleration and raw ECG only available via Polar SDK
        """
        bytes = list(data)
        hr = None
        ibis = []
        if bytes[0] == 00:
            hr = data[1]
        if bytes[0] == 16:
            hr = data[1]
            for i in range(2, len(bytes), 2):
                ibis.append(data[i] + 256 * data[i + 1])
        if ibis:
            for ibi in ibis:
                self.patch.setvalue(self.patch.getstring("output", "key_ibi"),
                                    ibi)
        if hr:
            self.patch.setvalue(self.patch.getstring("output", "key_hr"), hr)
        print("Received HR={0}, IBI(s)={1}".format(hr, ibis))
Ejemplo n.º 4
0
class BASE_BLE_DEVICE:
    def __init__(self,
                 scan_dev,
                 init=False,
                 name=None,
                 lenbuff=100,
                 rssi=None,
                 log=None):
        # BLE
        self.ble_client = None
        if hasattr(scan_dev, 'address'):
            self.UUID = scan_dev.address
            self.name = scan_dev.name
            self.rssi = scan_dev.rssi
            self.address = self.UUID
        else:
            self.UUID = scan_dev
            self.name = name
            self.rssi = rssi
            self.address = self.UUID
        self.connected = False
        self.services = {}
        self.services_rsum = {}
        self.services_rsum_handles = {}
        self.chars_desc_rsum = {}
        self.readables = {}
        self.writeables = {}
        self.notifiables = {}
        self.readables_handles = {}
        self.writeables_handles = {}
        self.notifiables_handles = {}
        self.loop = asyncio.get_event_loop()
        # self.raw_buff_queue = asyncio.Queue()
        self.kb_cmd = None
        self.is_notifying = False
        self.cmd_finished = True
        self.len_buffer = lenbuff
        #
        self.bytes_sent = 0
        self.buff = b''
        self.raw_buff = b''
        self.prompt = b'>>> '
        self.response = ''
        self._cmdstr = ''
        self._cmdfiltered = False
        self._kbi = '\x03'
        self._banner = '\x02'
        self._reset = '\x04'
        self._traceback = b'Traceback (most recent call last):'
        self._flush = b''
        self.output = None
        self.platform = None
        self.break_flag = None
        self.log = log
        #
        if init:
            self.connect()
            # do connect

    def set_event_loop(self, loop):
        self.loop = loop
        # self.ble_client.loop = loop

    async def connect_client(self, n_tries=3, log=True):
        n = 0
        self.ble_client = BleakClient(self.UUID)
        while n < n_tries:
            try:
                await asyncio.wait_for(self.ble_client.connect(timeout=3),
                                       timeout=60)
                self.connected = await self.ble_client.is_connected()
                if self.connected:
                    self.name = self.ble_client._device_info.name()
                    if log:
                        self.log.info("Connected to: {}".format(self.UUID))
                    break
            except Exception as e:
                if log:
                    if not self.break_flag:
                        self.log.error(e)
                        self.log.info('Trying again...')
                    else:
                        break
                time.sleep(1)
                n += 1

    async def disconnect_client(self, log=True, timeout=None):
        if timeout:

            await asyncio.wait_for(self.ble_client.disconnect(),
                                   timeout=timeout)

        else:
            await self.ble_client.disconnect()
        self.connected = await self.ble_client.is_connected()
        if not self.connected:
            if log:
                self.log.info("Disconnected successfully")

    def connect(self, n_tries=3, show_servs=False, log=True):
        self.loop.run_until_complete(
            self.connect_client(n_tries=n_tries, log=log))
        self.get_services(log=show_servs)

    def is_connected(self):
        return self.loop.run_until_complete(self.ble_client.is_connected())

    def disconnect(self, log=True, timeout=None):
        self.loop.run_until_complete(
            self.disconnect_client(log=log, timeout=timeout))

    def set_disconnected_callback(self, callback):
        self.ble_client.set_disconnected_callback(callback)

    def disconnection_callback(self, client):
        self.connected = False

    # RSSI
    def get_RSSI(self):
        if hasattr(self.ble_client, 'get_rssi'):
            self.rssi = self.loop.run_until_complete(
                self.ble_client.get_rssi())
        else:
            self.rssi = 0
        return self.rssi

    # SERVICES

    def get_services(self, log=True):
        for service in self.ble_client.services:
            if log:
                print("[Service] {0}: {1}".format(service.uuid.lower(),
                                                  service.description))
            self.services[service.description] = {
                'UUID': service.uuid.lower(),
                'CHARS': {}
            }
            self.services_rsum_handles[service.description] = []

            for char in service.characteristics:
                self.services_rsum_handles[service.description].append(
                    char.handle)
                if "read" in char.properties:
                    try:
                        self.readables[char.description] = char.uuid
                        self.readables_handles[char.handle] = char.description
                    except Exception as e:
                        print(e)

                if "notify" in char.properties or 'indicate' in char.properties:
                    try:
                        self.notifiables[char.description] = char.uuid
                        self.notifiables_handles[
                            char.handle] = char.description
                    except Exception as e:
                        print(e)

                if "write" in char.properties or 'write-without-response' in char.properties:
                    try:
                        self.writeables[char.description] = char.uuid
                        self.writeables_handles[char.handle] = char.description
                    except Exception as e:
                        print(e)
                try:
                    self.services[service.description]['CHARS'][char.uuid] = {
                        char.description: ",".join(char.properties),
                        'Descriptors': {
                            descriptor.uuid: descriptor.handle
                            for descriptor in char.descriptors
                        }
                    }
                except Exception as e:
                    print(e)

                    self.chars_desc_rsum[char.description] = {}
                    for descriptor in char.descriptors:
                        self.chars_desc_rsum[char.description][
                            descriptor.description] = descriptor.handle
                if log:

                    try:
                        print(
                            "\t[Characteristic] {0}: ({1}) | Name: {2}".format(
                                char.uuid, ",".join(char.properties),
                                char.description))
                    except Exception as e:
                        print(e)

                if log:
                    for descriptor in char.descriptors:
                        print("\t\t[Descriptor] [{0}]: {1} (Handle: {2}) ".
                              format(descriptor.uuid, descriptor.description,
                                     descriptor.handle))
        self.services_rsum = {
            key: [
                list(list(val['CHARS'].values())[i].keys())[0]
                for i in range(len(list(val['CHARS'].values())))
            ]
            for key, val in self.services.items()
        }

    # WRITE/READ SERVICES

    def fmt_data(self, data, CR=True):
        if sys.platform == 'linux':
            if CR:
                return bytearray(data + '\r', 'utf-8')
            else:
                return bytearray(data, 'utf-8')
        else:
            if CR:
                return bytes(data + '\r', 'utf-8')
            else:
                return bytes(data, 'utf-8')

    async def as_read_descriptor(self, handle):
        return bytes(await self.ble_client.read_gatt_descriptor(handle))

    def read_descriptor_raw(self, key=None, char=None):
        if key is not None:
            # print(self.chars_desc_rsum[char])
            if key in list(self.chars_desc_rsum[char]):
                data = self.loop.run_until_complete(
                    self.as_read_descriptor(self.chars_desc_rsum[char][key]))
                return data
            else:
                print('Descriptor not available for this characteristic')

    def read_descriptor(self, key=None, char=None, data_fmt="utf8"):
        try:
            if data_fmt == 'utf8':
                data = self.read_descriptor_raw(key=key,
                                                char=char).decode('utf8')
                return data
            else:
                data, = struct.unpack(data_fmt,
                                      self.read_char_raw(key=key, char=char))
                return data
        except Exception as e:
            print(e)

    async def as_read_char(self, uuid):
        return bytes(await self.ble_client.read_gatt_char(uuid))

    def read_char_raw(self, key=None, uuid=None, handle=None):
        if key is not None:
            if key in list(self.readables.keys()):
                if handle:
                    data = self.loop.run_until_complete(
                        self.as_read_char(handle))
                else:
                    data = self.loop.run_until_complete(
                        self.as_read_char(self.readables[key]))
                return data
            else:
                print('Characteristic not readable')

        else:
            if uuid is not None:
                if uuid in list(self.readables.values()):
                    if handle:
                        data = self.loop.run_until_complete(
                            self.as_read_char(handle))
                    else:
                        data = self.loop.run_until_complete(
                            self.as_read_char(uuid))
                    return data
                else:
                    print('Characteristic not readable')

    def read_char(self, key=None, uuid=None, data_fmt="<h", handle=None):
        try:
            if data_fmt == 'utf8':  # Here function that handles format and unpack properly
                data = self.read_char_raw(key=key, uuid=uuid,
                                          handle=handle).decode('utf8')
                return data
            else:
                if data_fmt == 'raw':
                    data = self.read_char_raw(key=key,
                                              uuid=uuid,
                                              handle=handle)
                    return data
                else:
                    data, = struct.unpack(
                        data_fmt,
                        self.read_char_raw(key=key, uuid=uuid, handle=handle))
                return data
        except Exception as e:
            print(e)

    async def as_write_char(self, uuid, data):
        await self.ble_client.write_gatt_char(uuid, data)

    def write_char(self, key=None, uuid=None, data=None, handle=None):
        if key is not None:
            if key in list(self.writeables.keys()):
                if handle:
                    data = self.loop.run_until_complete(
                        self.as_write_char(handle, data))
                else:
                    data = self.loop.run_until_complete(
                        self.as_write_char(self.writeables[key],
                                           data))  # make fmt_data
                return data
            else:
                print('Characteristic not writeable')

        else:
            if uuid is not None:
                if uuid in list(self.writeables.values()):
                    if handle:
                        data = self.loop.run_until_complete(
                            self.as_write_char(handle, data))
                    else:
                        data = self.loop.run_until_complete(
                            self.as_write_char(uuid, data))  # make fmt_data
                    return data
                else:
                    print('Characteristic not writeable')

    def write_char_raw(self, key=None, uuid=None, data=None):
        if key is not None:
            if key in list(self.writeables.keys()):
                data = self.loop.run_until_complete(
                    self.as_write_char(self.writeables[key],
                                       self.fmt_data(
                                           data, CR=False)))  # make fmt_data
                return data
            else:
                print('Characteristic not writeable')

        else:
            if uuid is not None:
                if uuid in list(self.writeables.values()):
                    data = self.loop.run_until_complete(
                        self.as_write_char(uuid, self.fmt_data(
                            data, CR=False)))  # make fmt_data
                    return data
                else:
                    print('Characteristic not writeable')

    def read_callback(self, sender, data):
        self.raw_buff += data

    def read_callback_follow(self, sender, data):
        try:
            if not self._cmdfiltered:
                cmd_filt = bytes(self._cmdstr + '\r\n', 'utf-8')
                data = b'' + data
                data = data.replace(cmd_filt, b'', 1)
                # data = data.replace(b'\r\n>>> ', b'')
                self._cmdfiltered = True
            else:
                try:
                    data = b'' + data
                    # data = data.replace(b'\r\n>>> ', b'')
                except Exception as e:
                    pass
            self.raw_buff += data
            if self.prompt in data:
                data = data.replace(b'\r',
                                    b'').replace(b'\r\n>>> ', b'').replace(
                                        b'>>> ',
                                        b'').decode('utf-8', 'ignore')
                if data != '':
                    print(data, end='')
            else:
                data = data.replace(b'\r',
                                    b'').replace(b'\r\n>>> ', b'').replace(
                                        b'>>> ',
                                        b'').decode('utf-8', 'ignore')
                print(data, end='')
        except KeyboardInterrupt:
            print('CALLBACK_KBI')
            pass
        #

    async def as_write_read_waitp(self, data, rtn_buff=False):
        await self.ble_client.start_notify(self.readables['TX'],
                                           self.read_callback)
        if len(data) > self.len_buffer:
            for i in range(0, len(data), self.len_buffer):
                await self.ble_client.write_gatt_char(
                    self.writeables['RX'], data[i:i + self.len_buffer])

        else:
            await self.ble_client.write_gatt_char(self.writeables['RX'], data)
        while self.prompt not in self.raw_buff:
            await asyncio.sleep(0.01, loop=self.loop)
        await self.ble_client.stop_notify(self.readables['TX'])
        if rtn_buff:
            return self.raw_buff

    async def as_write_read_follow(self, data, rtn_buff=False):
        if not self.is_notifying:
            try:
                await self.ble_client.start_notify(self.readables['TX'],
                                                   self.read_callback_follow)
                self.is_notifying = True
            except Exception as e:
                pass
        if len(data) > self.len_buffer:
            for i in range(0, len(data), self.len_buffer):
                await self.ble_client.write_gatt_char(
                    self.writeables['RX'], data[i:i + self.len_buffer])
        else:
            await self.ble_client.write_gatt_char(self.writeables['RX'], data)
        while self.prompt not in self.raw_buff:
            try:
                await asyncio.sleep(0.01, loop=self.loop)
            except KeyboardInterrupt:
                print('Catch here1')
                data = bytes(self._kbi, 'utf-8')
                await self.ble_client.write_gatt_char(self.writeables['RX'],
                                                      data)
        if self.is_notifying:
            try:
                await self.ble_client.stop_notify(self.readables['TX'])
                self.is_notifying = False
            except Exception as e:
                pass
        self._cmdfiltered = False
        if rtn_buff:
            return self.raw_buff

    def write_read(self, data='', follow=False, kb=False):
        if not follow:
            if not kb:
                try:
                    self.loop.run_until_complete(
                        self.as_write_read_waitp(data))
                except Exception as e:
                    print(e)
            else:
                asyncio.ensure_future(self.as_write_read_waitp(data),
                                      loop=self.loop)
                # wait here until there is raw_buff

        else:
            if not kb:
                try:
                    self.loop.run_until_complete(
                        self.as_write_read_follow(data))
                except Exception as e:
                    print('Catch here0')
                    print(e)
            else:

                asyncio.ensure_future(self.as_write_read_follow(data,
                                                                rtn_buff=True),
                                      loop=self.loop)

    def send_recv_cmd(self, cmd, follow=False, kb=False):
        data = self.fmt_data(cmd)  # make fmt_data
        n_bytes = len(data)
        self.write_read(data=data, follow=follow, kb=kb)
        return n_bytes

    def write(self, cmd):
        data = self.fmt_data(cmd, CR=False)  # make fmt_data
        n_bytes = len(data)
        self.write_char_raw(key='RX', data=data)
        return n_bytes

    def read_all(self):
        try:
            return self.raw_buff
        except Exception as e:
            print(e)
            return self.raw_buff

    def flush(self):
        flushed = 0
        self.buff = self.read_all()
        flushed += 1
        self.buff = b''

    def wr_cmd(self,
               cmd,
               silent=False,
               rtn=True,
               rtn_resp=False,
               long_string=False,
               follow=False,
               kb=False):
        self.output = None
        self.response = ''
        self.raw_buff = b''
        self.buff = b''
        self._cmdstr = cmd
        # self.flush()
        self.bytes_sent = self.send_recv_cmd(cmd, follow=follow,
                                             kb=kb)  # make fmt_datas
        # time.sleep(0.1)
        # self.buff = self.read_all()[self.bytes_sent:]
        self.buff = self.read_all()
        if self.buff == b'':
            # time.sleep(0.1)
            self.buff = self.read_all()
        # print(self.buff)
        # filter command
        if follow:
            silent = True
        cmd_filt = bytes(cmd + '\r\n', 'utf-8')
        self.buff = self.buff.replace(cmd_filt, b'', 1)
        if self._traceback in self.buff:
            long_string = True
        if long_string:
            self.response = self.buff.replace(b'\r', b'').replace(
                b'\r\n>>> ', b'').replace(b'>>> ',
                                          b'').decode('utf-8', 'ignore')
        else:
            self.response = self.buff.replace(b'\r\n', b'').replace(
                b'\r\n>>> ', b'').replace(b'>>> ',
                                          b'').decode('utf-8', 'ignore')
        if not silent:
            if self.response != '\n' and self.response != '':
                print(self.response)
            else:
                self.response = ''
        if rtn:
            self.get_output()
            if self.output == '\n' and self.output == '':
                self.output = None
            if self.output is None:
                if self.response != '' and self.response != '\n':
                    self.output = self.response
        if rtn_resp:
            return self.output

    async def as_wr_cmd(self,
                        cmd,
                        silent=False,
                        rtn=True,
                        rtn_resp=False,
                        long_string=False,
                        follow=False,
                        kb=False):
        self.output = None
        self.response = ''
        self.raw_buff = b''
        self.buff = b''
        self._cmdstr = cmd
        self.cmd_finished = False
        # self.flush()
        data = self.fmt_data(cmd)  # make fmt_data
        n_bytes = len(data)
        self.bytes_sent = n_bytes
        # time.sleep(0.1)
        # self.buff = self.read_all()[self.bytes_sent:]
        if follow:
            self.buff = await self.as_write_read_follow(data, rtn_buff=True)
        else:
            self.buff = await self.as_write_read_waitp(data, rtn_buff=True)
        if self.buff == b'':
            # time.sleep(0.1)
            self.buff = self.read_all()
        # print(self.buff)
        # filter command
        if follow:
            silent = True
        cmd_filt = bytes(cmd + '\r\n', 'utf-8')
        self.buff = self.buff.replace(cmd_filt, b'', 1)
        if self._traceback in self.buff:
            long_string = True
        if long_string:
            self.response = self.buff.replace(b'\r', b'').replace(
                b'\r\n>>> ', b'').replace(b'>>> ',
                                          b'').decode('utf-8', 'ignore')
        else:
            self.response = self.buff.replace(b'\r\n', b'').replace(
                b'\r\n>>> ', b'').replace(b'>>> ',
                                          b'').decode('utf-8', 'ignore')
        if not silent:
            if self.response != '\n' and self.response != '':
                print(self.response)
            else:
                self.response = ''
        if rtn:
            self.get_output()
            if self.output == '\n' and self.output == '':
                self.output = None
            if self.output is None:
                if self.response != '' and self.response != '\n':
                    self.output = self.response
        self.cmd_finished = True
        if rtn_resp:
            return self.output

    def kbi(self, silent=True, pipe=None):
        if pipe is not None:
            self.wr_cmd(self._kbi, silent=silent)
            bf_output = self.response.split('Traceback')[0]
            traceback = 'Traceback' + self.response.split('Traceback')[1]
            if bf_output != '' and bf_output != '\n':
                pipe(bf_output)
            pipe(traceback, std='stderr')
        else:
            self.wr_cmd(self._kbi, silent=silent)

    async def as_kbi(self):
        for i in range(1):
            print('This is buff: {}'.format(self.raw_buff))
            await asyncio.sleep(1, loop=self.loop)
            data = bytes(self._kbi + '\r', 'utf-8')
            await self.ble_client.write_gatt_char(self.writeables['RX'], data)

    def banner(self, pipe=None, kb=False, follow=False):
        self.wr_cmd(self._banner,
                    silent=True,
                    long_string=True,
                    kb=kb,
                    follow=follow)
        if pipe is None and not follow:
            print(self.response.replace('\n\n', '\n'))
        else:
            if pipe:
                pipe(self.response.replace('\n\n', '\n'))

    def reset(self, silent=True):
        if not silent:
            print('Rebooting device...')
        self.write_char_raw(key='RX', data=self._reset)
        if not silent:
            print('Done!')

    async def as_reset(self, silent=True):
        if not silent:
            print('Rebooting device...')
        await self.as_write_char(self.writeables['RX'],
                                 bytes(self._reset, 'utf-8'))
        if not silent:
            print('Done!')
        return None

    def get_output(self):
        try:
            self.output = ast.literal_eval(self.response)
        except Exception as e:
            if 'bytearray' in self.response:
                try:
                    self.output = bytearray(
                        ast.literal_eval(
                            self.response.strip().split('bytearray')[1]))
                except Exception as e:
                    pass
            else:
                if 'array' in self.response:
                    try:
                        arr = ast.literal_eval(
                            self.response.strip().split('array')[1])
                        self.output = array(arr[0], arr[1])
                    except Exception as e:
                        pass
            pass