def write(self, value, withResponse=False): """Blocking write of the descriptor. Args: value : a bytearray of length at most send MTU - 3 Raises: ClientError on failure """ assert isinstance(value, bytearray) op = att.OP_WRITE_REQ if withResponse else att.OP_WRITE_CMD req = bytearray([op]) req += _handle_to_bytearray(self._handleNo) req += value if not withResponse: self.client._socket._send(req) return resp = self.client._new_transaction(req) if resp is None: raise ClientError("no response") elif resp[0] == att.OP_WRITE_RESP: return resp[1:] elif resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN: raise ClientError("write failed - %s" % att.ecodeLookup(resp[4])) else: raise ClientError("unexpected - %s" % att.opcodeLookup(resp[0]))
def read(self): """Blocking read of the descriptor. Returns: A bytearray Raises: ClientError on failure """ if self._cachable and self._cached is not None: return self._cached req = bytearray([att.OP_READ_REQ]) req += _handle_to_bytearray(self._handleNo) resp = self.client._new_transaction(req) if resp is None: raise ClientError("no response") elif resp[0] == att.OP_READ_RESP: ret = resp[1:] if self._cachable: self._cached = ret return ret elif resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN: raise ClientError("read failed - %s" % att.ecodeLookup(resp[4])) else: raise ClientError("unexpected - %s" % att.opcodeLookup(resp[0]))
def write(self, value, withResponse=True): """Blocking write of the characteristic. Args: value : a bytearray of length at most send MTU - 3 withResponse : whether the write should be a request or command Raises: ClientError on failure """ assert isinstance(value, bytearray) if "w" not in self.permissions: raise ClientError("write not permitted") op = att.OP_WRITE_REQ if withResponse else att.OP_WRITE_CMD req = bytearray([op]) req += _handle_to_bytearray(self._valHandleNo) req += value if not withResponse: self.client._socket._send(req) return resp = self.client._new_transaction(req) if resp is None: raise ClientError("no response") elif resp[0] == att.OP_WRITE_RESP: return resp[1:] elif resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN: raise ClientError("write failed - %s" % att.ecodeLookup(resp[4])) else: raise ClientError("unexpected - %s" % att.opcodeLookup(resp[0]))
def __discover_services_by_uuid(self, uuid, startHandle, endHandle): primSvcUuid = UUID(gatt.PRIM_SVC_UUID) services = [] newServices = [] serviceHandles = {} currHandle = startHandle while True: req = att_pdu.new_find_by_type_value_req(currHandle, endHandle, primSvcUuid, uuid.raw[::-1]) resp = self._new_transaction(req) if not resp: raise ClientError("transaction failed") if resp[0] == att.OP_FIND_BY_TYPE_REQ: idx = 2 endGroup = currHandle # not needed while idx < len(resp) and idx + 4 <= len(resp): handleNo = att_pdu.unpack_handle(resp, idx) endGroup = att_pdu.unpack_handle(resp, idx + 2) if handleNo in self._serviceHandles: service = self._serviceHandles[handleNo] else: service = _ClientService(self, uuid, handleNo, endGroup) serviceHandles[handleNo] = service newServices.append(service) services.append(service) idx += 4 currHandle = endGroup + 1 if currHandle >= endHandle: break elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN and resp[1] == att.OP_FIND_BY_TYPE_REQ and resp[4] == att.ECODE_ATTR_NOT_FOUND): break elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN and resp[1] == att.OP_FIND_BY_TYPE_REQ): raise ClientError("error - %s" % att.ecodeLookup(resp[4])) else: raise ClientError("unexpected - %s" % att.opcodeLookup(resp[0])) self.services.extend(newServices) self.services.sort(key=lambda x: x._handleNo) self._serviceHandles.update(serviceHandles) return services
def __discover_services(self, startHandle, endHandle): primSvcUuid = UUID(gatt.PRIM_SVC_UUID) services = [] serviceHandles = {} currHandle = startHandle while True: req = att_pdu.new_read_by_group_req(currHandle, endHandle, primSvcUuid) resp = self._new_transaction(req) if not resp: raise ClientError("transaction failed") if resp[0] == att.OP_READ_BY_GROUP_RESP: attDataLen = resp[1] idx = 2 endGroup = currHandle # not needed while idx < len(resp) and idx + attDataLen <= len(resp): handleNo = att_pdu.unpack_handle(resp, idx) endGroup = att_pdu.unpack_handle(resp, idx + 2) uuid = UUID(resp[idx+4:idx+attDataLen], reverse=True) service = _ClientService(self, uuid, handleNo, endGroup) services.append(service) serviceHandles[handleNo] = service idx += attDataLen currHandle = endGroup + 1 if currHandle >= endHandle: break elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN and resp[1] == att.OP_READ_BY_GROUP_REQ and resp[4] == att.ECODE_ATTR_NOT_FOUND): break elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN and resp[1] == att.OP_READ_BY_GROUP_REQ): raise ClientError("error - %s" % att.ecodeLookup(resp[4])) else: raise ClientError("unexpected - %s" % att.opcodeLookup(resp[0])) return services, serviceHandles
def read(self): """Blocking read of the characteristic. Returns: A bytearray Raises: ClientError on failure """ if "r" not in self.permissions: raise ClientError("read not permitted") req = bytearray([att.OP_READ_REQ]) req += _handle_to_bytearray(self._valHandleNo) resp = self.client._new_transaction(req) if resp is None: raise ClientError("no response") elif resp[0] == att.OP_READ_RESP: return resp[1:] elif resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN: raise ClientError("read failed - %s" % att.ecodeLookup(resp[4])) else: raise ClientError("unexpected - %s" % att.opcodeLookup(resp[0]))
def __discover_all_characteristics(self): startHandle = self._handleNo + 1 endHandle = self._endGroup if startHandle > endHandle: return [], {} characUuid = UUID(gatt.CHARAC_UUID) characs = [] characHandles = {} currHandle = startHandle while True: req = att_pdu.new_read_by_type_req(currHandle, endHandle, characUuid) resp = self.client._new_transaction(req) if not resp: raise ClientError("transaction failed") if resp[0] == att.OP_READ_BY_TYPE_RESP: attDataLen = resp[1] idx = 2 maxHandleNo = currHandle # not needed while idx < len(resp) and idx + attDataLen <= len(resp): handleNo = att_pdu.unpack_handle(resp, idx) properties = resp[idx+2] valHandleNo = att_pdu.unpack_handle(resp, idx + 3) uuid = UUID(resp[idx+5:idx+attDataLen], reverse=True) charac = _ClientCharacteristic(self.client, self, uuid, handleNo, properties, valHandleNo) characs.append(charac) characHandles[handleNo] = characs idx += attDataLen maxHandleNo = valHandleNo currHandle = maxHandleNo + 1 if currHandle >= endHandle: break elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN and resp[1] == att.OP_READ_BY_TYPE_REQ and resp[4] == att.ECODE_ATTR_NOT_FOUND): break elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN and resp[1] == att.OP_READ_BY_TYPE_REQ and resp[4] == att.ECODE_READ_NOT_PERM): currHandle = currHandle + 1 if currHandle >= endHandle: break elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN and resp[1] == att.OP_READ_BY_TYPE_REQ): raise ClientError("error - %s" % att.ecodeLookup(resp[4])) else: raise ClientError("unexpected - %s" % att.opcodeLookup(resp[0])) for i, charac in enumerate(characs): if i + 1 < len(characs): charac._set_end_group(characs[i+1]._handleNo-1) else: charac._set_end_group(self._endGroup) return characs, characHandles
def discoverDescriptors(self): """Return a list of descriptors""" assert self._endGroup is not None startHandle = self._handleNo + 1 endHandle = self._endGroup if startHandle > endHandle: return [] descriptors = [] cccdUuid = UUID(gatt.CLIENT_CHARAC_CFG_UUID) cccd = None userDescUuid = UUID(gatt.CHARAC_USER_DESC_UUID) userDesc = None currHandle = startHandle while True: req = att_pdu.new_find_info_req(currHandle, endHandle) resp = self.client._new_transaction(req) if not resp: raise ClientError("transaction failed") if resp[0] == att.OP_FIND_INFO_RESP: attDataLen = 4 if resp[1] == att.FIND_INFO_RESP_FMT_16BIT \ else 18 idx = 2 handleNo = currHandle # not needed while idx < len(resp) and idx + attDataLen <= len(resp): handleNo = att_pdu.unpack_handle(resp, idx) uuid = UUID(resp[idx+2:idx+attDataLen], reverse=True) if handleNo == self._valHandleNo: idx += attDataLen continue if uuid == userDescUuid: descriptor = _ClientDescriptor(self.client, self, uuid, handleNo, cacheable=True) userDesc = descriptor else: descriptor = _ClientDescriptor(self.client, self, uuid, handleNo) if uuid == cccdUuid: # hide the cccd from users cccd = descriptor else: descriptors.append(descriptor) idx += attDataLen currHandle = handleNo + 1 if currHandle >= endHandle: break elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN and resp[1] == att.OP_FIND_INFO_REQ and resp[4] == att.ECODE_ATTR_NOT_FOUND): break elif (resp[0] == att.OP_ERROR and len(resp) == att_pdu.ERROR_PDU_LEN and resp[1] == att.OP_FIND_INFO_REQ): raise ClientError("error - %s" % att.ecodeLookup(resp[4])) else: raise ClientError("unexpected - %s" % att.opcodeLookup(resp[0])) self.descriptors = descriptors self._cccd = cccd self._user_desc = userDesc return descriptors