def cancel_notify(self, packet: Packet): future: Future = self._notify_futures.pop(packet.id, None) if future is None: raise PySpheroRuntimeError("Future not found") logger.debug(f"[NOTIFY_WORKER {packet}] Cancel") future.cancel()
def notify(self, packet: Packet, callback: Callable, sleep_time: float = 0.1, timeout: float = 10): packet_id = packet.id if packet_id in self._notify_futures: raise PySpheroRuntimeError("Notify thread already exists") def worker(): logger.debug(f"[NOTIFY_WORKER {packet}] Start") while self._running: response = self._get_response(packet, sleep_time=sleep_time, timeout=timeout) logger.debug(f"[NOTIFY_WORKER {packet}] Received {response}") if callback(response) is SpheroCore.STOP_NOTIFY: logger.debug( f"[NOTIFY_WORKER {packet}] Received STOP_NOTIFY") self._notify_futures.pop(packet_id, None) break future = self._executor.submit(worker) self._notify_futures[packet_id] = future return future
def stop_notify(self): if self._notify_future is None: raise PySpheroRuntimeError("Future not found") logger.debug(f"[NOTIFY_WORKER] Cancel") self._notify_future.cancel() self._notify_future = None
def _find_characteristic(self, characteristic: str): found_characteristic = None for uuid in self._device.discover_characteristics().keys(): if str(uuid) == characteristic: found_characteristic = uuid if not found_characteristic: raise PySpheroRuntimeError(f"Characteristic {characteristic} not found") return found_characteristic
def append_raw_data(self, data: List[int]): for b in data: logger.debug(f"Received {b:#04x}") self._data.append(b) # packet always ending with end byte if b == Packet.end: if len(self._data) < 6: raise PySpheroRuntimeError(f"Very small packet {[hex(x) for x in self.data]}") self._build_packet()
def _find_characteristic(self, characteristic: str): found_characteristic = None self._device.services_resolved() for service in self._device.services: for ch in service.characteristics: if str(ch.uuid) == characteristic: found_characteristic = ch if not found_characteristic: raise PySpheroRuntimeError(f"Characteristic {characteristic} not found") return found_characteristic
def from_response(cls, response_data: List[int]) -> "Packet": """ Create packet from raw data :param response_data: raw data from peripheral :return Packet: response packet """ response_data = Packet._unescape_response_data(response_data) start, flags, *data, checksum, end = response_data if start != cls.start or end != cls.end: raise PySpheroRuntimeError( f"Bad response packet: wrong start or end byte (start: {start:#04x}, end: {end:#04x})" ) target_id = None if flags & Flag.command_has_target_id.value: target_id = data.pop(0) source_id = None if flags & Flag.command_has_source_id.value: source_id = data.pop(0) device_id, command_id, sequence, *data = data packet = cls( flags=flags, target_id=target_id, source_id=source_id, device_id=device_id, command_id=command_id, sequence=sequence, data=data, ) calc_checksum = packet.checksum if calc_checksum != checksum: raise PySpheroRuntimeError( f"Bad response checksum. (Expected: {checksum:#04x}, obtained: {calc_checksum:#04x})" ) return packet
def _find_api_v2(self): ch_api_v2 = None self._device.services_resolved() for service in self._device.services: for characteristic in service.characteristics: if str(characteristic.uuid ) == SpheroCharacteristic.api_v2.value: ch_api_v2 = characteristic if not ch_api_v2: raise PySpheroRuntimeError("Sphero v2 characteristic not found") return ch_api_v2
def _unescape_response_data(response_data) -> List[int]: raw_data = [] iter_response_data = iter(response_data) for b in iter_response_data: # escaping byte allowing escaping start/end/escaping bytes if b == Packet.escape: # next byte is escaping b = next(iter_response_data, None) if b not in Packet.escaped_bytes: raise PySpheroRuntimeError(f"Bad escaping byte {b:#04x}") b |= Packet.escape_mask raw_data.append(b) return raw_data
def handleNotification(self, handle: int, data: List[int]): """ handleNotification getting raw data from peripheral and save it. This function may be called several times. Therefore, the state is stored inside the class. :param handle: :param data: raw data :return: """ for b in data: logger.debug(f"Received {b:#04x}") self.data.append(b) # packet always ending with end byte if b == Packet.end: if len(self.data) < 6: raise PySpheroRuntimeError( f"Very small packet {[hex(x) for x in self.data]}") self.build_packet()