docs: def read_line(self, timeout=0.0, purge_buffer=False):
          """
          Reads a line from the device.
  
          :param timeout: read timeout
          :type timeout: float
          :param purge_buffer: Indicates whether to purge the buffer prior to
                               reading.
          :type purge_buffer: bool
  
          :returns: line that was read
          :raises: :py:class:`~alarmdecoder.util.CommError`, :py:class:`~alarmdecoder.util.TimeoutError`
          """
  
          def timeout_event():
              """Handles read timeout event"""
              timeout_event.reading = False
          timeout_event.reading = True
  
          if purge_buffer:
              self._buffer = b''
  
          got_line, ret = False, None
  
          timer = threading.Timer(timeout, timeout_event)
          if timeout > 0:
              timer.start()
  
          try:
              while timeout_event.reading:
                  buf = self._device.read_data(1)
  
                  if buf != b'':
                      ub = bytes_hack(buf)
  
                      self._buffer += ub
  
                      if ub == b"\n":
                          self._buffer = self._buffer.rstrip(b"\r\n")
  
                          if len(self._buffer) > 0:
                              got_line = True
                              break
                  else:
                      time.sleep(0.01)
  
          except (usb.core.USBError, FtdiError) as err:
              raise CommError('Error reading from device: {0}'.format(str(err)), err)
  
          else:
              if got_line:
                  ret, self._buffer = self._buffer, b''
  
                  self.on_read(data=ret)
  
              else:
                  raise TimeoutError('Timeout while waiting for line terminator.')
  
          finally:
              timer.cancel()
  
          return ret