Exemplo n.º 1
0
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
Exemplo n.º 3
0
    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)