def process_value(self, value): assert value[0] == 0 opcode = value[1] tid = value[2] payload = value[7:] if opcode == HapBleOpCodes.CHAR_WRITE: new_value = dict(TLV.decode_bytes(payload)) self.do_char_write(tid, new_value[1]) elif opcode == HapBleOpCodes.CHAR_READ: value = self.char.get_value_for_ble() value = TLV.encode_list([(TLV.kTLVHAPParamValue, value)]) response = bytearray([0x02, tid, 0x00]) tlv = len(value).to_bytes(2, byteorder='little') + value response.extend(tlv) self.queue_read_response(self.encrypt_value(bytes(response))) elif opcode == HapBleOpCodes.CHAR_SIG_READ: response = bytearray([0x02, tid, 0x00]) service_type = list(uuid.UUID(self.service.service.type).bytes) service_type.reverse() service_type = bytes(bytearray(service_type)) char_type = list(uuid.UUID(self.char.type).bytes) char_type.reverse() char_type = bytes(bytearray(char_type)) fmt = BleCharacteristicFormats.get_reverse(self.char.format, b'\x00').to_bytes(length=1, byteorder='little') unit = b'\x00\x00' gatt_fmt = fmt + unit data = [ (TLV.kTLVHAPParamHAPCharacteristicPropertiesDescriptor, b'\x00'), (TLV.kTLVHAPParamGATTPresentationFormatDescriptor, gatt_fmt), (TLV.kTLVHAPParamCharacteristicType, char_type), (TLV.kTLVHAPParamServiceInstanceId, self.service.service.iid.to_bytes(length=8, byteorder='little')), (TLV.kTLVHAPParamServiceType, service_type), ] tlv = TLV.encode_list(data) response.extend(len(tlv).to_bytes(2, byteorder='little') + tlv) self.queue_read_response(self.encrypt_value(bytes(response))) else: raise RuntimeError('Fake does not implement opcode %s' % opcode)
def parse_sig_read_response(data, expected_tid): # TODO document me # parse header and check stuff logger.debug('parse sig read response %s', bytes([int(a) for a in data]).hex()) # handle the header data cf = data[0] logger.debug('control field %d', cf) tid = data[1] logger.debug('transaction id %d (expected was %d)', tid, expected_tid) status = data[2] logger.debug('status code %d (%s)', status, HapBleStatusCodes[status]) assert cf == 0x02 assert tid == expected_tid assert status == HapBleStatusCodes.SUCCESS # get body length length = int.from_bytes(data[3:5], byteorder='little') logger.debug('expected body length %d (got %d)', length, len(data[5:])) # parse tlvs and analyse information tlv = tlv8.decode(bytes([int(a) for a in data[5:]])) description = '' characteristic_format = '' characteristic_range = None characteristic_step = None for t in tlv: if t.type_id == AdditionalParameterTypes.CharacteristicType: chr_type = [int(a) for a in t.data] chr_type.reverse() chr_type = str(uuid.UUID(''.join('%02x' % b for b in chr_type))) if t.type_id == AdditionalParameterTypes.ServiceInstanceId: svc_id = int.from_bytes(t.data, byteorder='little') if t.type_id == AdditionalParameterTypes.ServiceType: svc_type = [int(a) for a in t.data] svc_type.reverse() svc_type = str(uuid.UUID(''.join('%02x' % b for b in svc_type))) if t.type_id == AdditionalParameterTypes.HAPCharacteristicPropertiesDescriptor: chr_prop_int = int.from_bytes(t.data, byteorder='little') if t.type_id == AdditionalParameterTypes.GATTUserDescriptionDescriptor: description = t.data.decode() if t.type_id == AdditionalParameterTypes.HAPValidValuesDescriptor: print('valid values', t.data) if t.type_id == AdditionalParameterTypes.HAPValidValuesRangeDescriptor: print('valid values range', t.data) if t.type_id == AdditionalParameterTypes.GATTPresentationFormatDescriptor: unit_bytes = bytearray(t.data[2:4]) unit_bytes.reverse() characteristic_format = BleCharacteristicFormats.get( int(t.data[0]), 'unknown') unit = BleCharacteristicUnits.get( int.from_bytes(unit_bytes, byteorder='big'), 'unknown') if t.type_id == AdditionalParameterTypes.GATTValidRange: logger.debug('range: %s', t.data.hex()) lower = None upper = None if characteristic_format == 'int32' or characteristic_format == 'int': (lower, upper) = struct.unpack('ii', t.data) if characteristic_format == 'uint8': (lower, upper) = struct.unpack('BB', t.data) if characteristic_format == 'float': (lower, upper) = struct.unpack('ff', t.data) # TODO include all formats! characteristic_range = (lower, upper) if t.type_id == AdditionalParameterTypes.HAPStepValueDescriptor: characteristic_step = None if characteristic_format == 'int32': characteristic_step = struct.unpack('i', t.data)[0] if characteristic_format == 'uint8': characteristic_step = struct.unpack('B', t.data)[0] # TODO include all formats! # parse permissions # TODO refactor! perms = [] if (chr_prop_int & 0x0001) > 0: perms.append('r') if (chr_prop_int & 0x0002) > 0: perms.append('w') if (chr_prop_int & 0x0004) > 0: perms.append('aad') if (chr_prop_int & 0x0008) > 0: perms.append('tw') if (chr_prop_int & 0x0010) > 0: perms.append('pr') if (chr_prop_int & 0x0020) > 0: perms.append('pw') if (chr_prop_int & 0x0040) > 0: perms.append('hd') if (chr_prop_int & 0x0080) > 0: perms.append('evc') if (chr_prop_int & 0x0100) > 0: perms.append('evd') result = { 'description': description, 'perms': perms, 'format': characteristic_format, 'unit': unit, 'range': characteristic_range, 'step': characteristic_step, 'type': chr_type.upper(), 'sid': svc_id, 'service_type': svc_type } logger.debug('result: %s', str(result)) return result
def test_get_known_key(self): self.assertEqual('bool', BleCharacteristicFormats.get(1, 'unknown'))
def test_get_unknown_key(self): self.assertEqual('unknown', BleCharacteristicFormats.get(-0xC0FFEE, 'unknown'))