def check_convert_value(val, target_type): """ Checks if the given value is of the given type or is convertible into the type. If the value is not convertible, a HomeKitTypeException is thrown. :param val: the original value :param target_type: the target type of the conversion :return: the converted value :raises FormatError: if the input value could not be converted to the target type """ if target_type == CharacteristicFormats.bool: try: val = strtobool(str(val)) except ValueError: raise FormatError('"{v}" is no valid "{t}"!'.format(v=val, t=target_type)) if target_type in [ CharacteristicFormats.uint64, CharacteristicFormats.uint32, CharacteristicFormats.uint16, CharacteristicFormats.uint8, CharacteristicFormats.int ]: try: val = int(val) except ValueError: raise FormatError('"{v}" is no valid "{t}"!'.format(v=val, t=target_type)) if target_type == CharacteristicFormats.float: try: val = float(val) except ValueError: raise FormatError('"{v}" is no valid "{t}"!'.format(v=val, t=target_type)) if target_type == CharacteristicFormats.data: try: base64.decodebytes(val.encode()) except binascii.Error: raise FormatError('"{v}" is no valid "{t}"!'.format(v=val, t=target_type)) if target_type == CharacteristicFormats.tlv8: try: tmp_bytes = base64.decodebytes(val.encode()) TLV.decode_bytes(tmp_bytes) except (binascii.Error, TlvParseException): raise FormatError('"{v}" is no valid "{t}"!'.format(v=val, t=target_type)) return val
def get_value_for_ble(self): value = self.get_value() if self.format == CharacteristicFormats.bool: try: val = strtobool(str(value)) except ValueError: raise FormatError('"{v}" is no valid "{t}"!'.format(v=value, t=self.format)) value = struct.pack('?', val) elif self.format == CharacteristicFormats.int: value = struct.pack('i', int(value)) elif self.format == CharacteristicFormats.float: value = struct.pack('f', float(value)) elif self.format == CharacteristicFormats.string: value = value.encode() return value
def _convert_from_python(self, aid, cid, value): """ Convert a python value into a representation usable for the Bluetooth LE transport. The type to be used will be queried from the characteristic. :param aid: the accessory id :param cid: the characteristic id :param value: the value to convert (as python value) :return: the converted value as bytes """ char_format = None characteristic = self._find_characteristic_in_pairing_data(aid, cid) if characteristic: char_format = characteristic['format'] logger.debug('value: %s format: %s', value, char_format) if char_format == CharacteristicFormats.bool: try: val = strtobool(str(value)) except ValueError: raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) return struct.pack('?', val) # no more boolean input after this line if type(value) == bool: raise FormatError('"{v}" is no valid "{t}"!'.format(v=value, t=char_format)) if char_format == CharacteristicFormats.float: try: return struct.pack('f', float(value)) except ValueError: raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) if char_format == CharacteristicFormats.string: if type(value) != str: raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) return value.encode('UTF-8') if char_format == CharacteristicFormats.data or char_format == CharacteristicFormats.tlv8: return value.hex().encode() # from here only integer values of different sizes if char_format == CharacteristicFormats.int: try: if float(value) != int(value): raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) value = struct.pack('i', int(value)) except ValueError: raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) elif char_format == CharacteristicFormats.uint8: try: if float(value) != int(value): raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) value = struct.pack('B', int(value)) except ValueError: raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) except struct.error: raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) elif char_format == CharacteristicFormats.uint16: try: if float(value) != int(value): raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) value = struct.pack('H', int(value)) except ValueError: raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) except struct.error: raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) elif char_format == CharacteristicFormats.uint32: try: if float(value) != int(value): raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) value = struct.pack('I', int(value)) except ValueError: raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) except struct.error: raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) elif char_format == CharacteristicFormats.uint64: try: if float(value) != int(value): raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) value = struct.pack('Q', int(value)) except ValueError: raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) except struct.error: raise FormatError('"{v}" is no valid "{t}"!'.format( v=value, t=char_format)) return value
def set_value(self, new_val): """ This function sets the value of this characteristic. Permissions are checked first :param new_val: :raises CharacteristicPermissionError: if the characteristic cannot be written """ if CharacteristicPermissions.paired_write not in self.perms: raise CharacteristicPermissionError(HapStatusCodes.CANT_WRITE_READ_ONLY) try: # convert input to python int if it is any kind of int if self.format in [CharacteristicFormats.uint64, CharacteristicFormats.uint32, CharacteristicFormats.uint16, CharacteristicFormats.uint8, CharacteristicFormats.int]: new_val = int(new_val) # convert input to python float if self.format == CharacteristicFormats.float: new_val = float(new_val) # convert to python bool if self.format == CharacteristicFormats.bool: new_val = strtobool(str(new_val)) except ValueError: raise FormatError(HapStatusCodes.INVALID_VALUE) if self.format in [CharacteristicFormats.uint64, CharacteristicFormats.uint32, CharacteristicFormats.uint16, CharacteristicFormats.uint8, CharacteristicFormats.int, CharacteristicFormats.float]: if self.minValue is not None and new_val < self.minValue: raise FormatError(HapStatusCodes.INVALID_VALUE) if self.maxValue is not None and self.maxValue < new_val: raise FormatError(HapStatusCodes.INVALID_VALUE) if self.minStep is not None: tmp = new_val # if minValue is set, the steps count from this on if self.minValue is not None: tmp -= self.minValue # use Decimal to calculate the module because it has not the precision problem as float... if Decimal(str(tmp)) % Decimal(str(self.minStep)) != 0: raise FormatError(HapStatusCodes.INVALID_VALUE) if self.valid_values is not None and new_val not in self.valid_values: raise FormatError(HapStatusCodes.INVALID_VALUE) if self.valid_values_range is not None and not ( self.valid_values_range[0] <= new_val <= self.valid_values_range[1]): raise FormatError(HapStatusCodes.INVALID_VALUE) if self.format == CharacteristicFormats.data: try: byte_data = base64.decodebytes(new_val.encode()) except binascii.Error: raise FormatError(HapStatusCodes.INVALID_VALUE) except Exception: raise FormatError(HapStatusCodes.OUT_OF_RESOURCES) if self.maxDataLen < len(byte_data): raise FormatError(HapStatusCodes.INVALID_VALUE) if self.format == CharacteristicFormats.string: if len(new_val) > self.maxLen: raise FormatError(HapStatusCodes.INVALID_VALUE) self.value = new_val if self._set_value_callback: self._set_value_callback(new_val)