Exemple #1
0
def parse_latlon(value):
    if isinstance(value, str) and value.endswith("°"):
        # strip trailing degree character
        value = value[:-1]
    try:
        return cv.float_(value)
    except cv.Invalid:
        pass

    value = cv.string_strict(value)
    m = LAT_LON_REGEX.match(value)

    if m is None:
        raise cv.Invalid("Invalid format for latitude/longitude")
    sign = m.group(1)
    deg = m.group(2)
    minute = m.group(3)
    second = m.group(4)
    d = m.group(5)

    val = float(deg or 0) + float(minute or 0) / 60 + float(second or 0) / 3600
    if sign == "-":
        val *= -1
    if d and d in "SW":
        val *= -1
    return val
def do_substitution_pass(config):
    if CONF_SUBSTITUTIONS not in config:
        return config

    substitutions = config[CONF_SUBSTITUTIONS]
    if not isinstance(substitutions, dict):
        raise EsphomeError(
            u"Substitutions must be a key to value mapping, got {}"
            u"".format(type(substitutions)))

    key = ''
    try:
        replace_keys = []
        for key, value in substitutions.items():
            sub = validate_substitution_key(key)
            if sub != key:
                replace_keys.append((key, sub))
            substitutions[key] = cv.string_strict(value)
        for old, new in replace_keys:
            substitutions[new] = substitutions[old]
            del substitutions[old]
    except vol.Invalid as err:
        err.path.append(key)

        raise EsphomeError(
            u"Error while parsing substitutions: {}".format(err))

    config[CONF_SUBSTITUTIONS] = substitutions
    _substitute_item(substitutions, config, [])

    return config
Exemple #3
0
def do_substitution_pass(config, command_line_substitutions):
    if CONF_SUBSTITUTIONS not in config and not command_line_substitutions:
        return

    substitutions = config[CONF_SUBSTITUTIONS]
    if substitutions is None:
        substitutions = command_line_substitutions
    elif command_line_substitutions:
        substitutions = {**substitutions, **command_line_substitutions}
    with cv.prepend_path("substitutions"):
        if not isinstance(substitutions, dict):
            raise cv.Invalid(
                "Substitutions must be a key to value mapping, got {}"
                "".format(type(substitutions)))

        replace_keys = []
        for key, value in substitutions.items():
            with cv.prepend_path(key):
                sub = validate_substitution_key(key)
                if sub != key:
                    replace_keys.append((key, sub))
                substitutions[key] = cv.string_strict(value)
        for old, new in replace_keys:
            substitutions[new] = substitutions[old]
            del substitutions[old]

    config[CONF_SUBSTITUTIONS] = substitutions
    # Move substitutions to the first place to replace substitutions in them correctly
    config.move_to_end(CONF_SUBSTITUTIONS, False)
    _substitute_item(substitutions, config, [])
Exemple #4
0
def bt_uuid(value):
    in_value = cv.string_strict(value)
    value = in_value.upper()

    if len(value) == len(bt_uuid16_format):
        pattern = re.compile("^[A-F|0-9]{4,}$")
        if not pattern.match(value):
            raise cv.Invalid(
                f"Invalid hexadecimal value for 16 bit UUID format: '{in_value}'"
            )
        return value
    if len(value) == len(bt_uuid32_format):
        pattern = re.compile("^[A-F|0-9]{8,}$")
        if not pattern.match(value):
            raise cv.Invalid(
                f"Invalid hexadecimal value for 32 bit UUID format: '{in_value}'"
            )
        return value
    if len(value) == len(bt_uuid128_format):
        pattern = re.compile(
            "^[A-F|0-9]{8,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{12,}$"
        )
        if not pattern.match(value):
            raise cv.Invalid(
                f"Invalid hexadecimal value for 128 UUID format: '{in_value}'")
        return value
    raise cv.Invalid(
        f"Service UUID must be in 16 bit '{bt_uuid16_format}', 32 bit '{bt_uuid32_format}', or 128 bit '{bt_uuid128_format}' format"
    )
Exemple #5
0
def validate_arduino_version(value):
    value = cv.string_strict(value)
    value_ = value.upper()
    if CORE.is_esp8266:
        if (
            VERSION_REGEX.match(value) is not None
            and value_ not in PLATFORMIO_ESP8266_LUT
        ):
            raise cv.Invalid(
                "Unfortunately the arduino framework version '{}' is unsupported "
                "at this time. You can override this by manually using "
                "espressif8266@<platformio version>".format(value)
            )
        if value_ in PLATFORMIO_ESP8266_LUT:
            return PLATFORMIO_ESP8266_LUT[value_]
        return value
    if CORE.is_esp32:
        if (
            VERSION_REGEX.match(value) is not None
            and value_ not in PLATFORMIO_ESP32_LUT
        ):
            raise cv.Invalid(
                "Unfortunately the arduino framework version '{}' is unsupported "
                "at this time. You can override this by manually using "
                "espressif32@<platformio version>".format(value)
            )
        if value_ in PLATFORMIO_ESP32_LUT:
            return PLATFORMIO_ESP32_LUT[value_]
        return value
    raise NotImplementedError
Exemple #6
0
def validate_tariff_time(value):
    value = cv.string_strict(value)
    parts = value.split("-")
    if len(parts) != 2:
        raise cv.Invalid("Time period should be HH:MM-HH:MM format")
    time_period(parts[0])
    time_period(parts[1])
    return value
Exemple #7
0
def validate_tz(value):
    value = cv.string_strict(value)

    try:
        pytz_obj = pytz.timezone(value)
    except pytz.UnknownTimeZoneError:  # pylint: disable=broad-except
        return value

    return convert_tz(pytz_obj)
Exemple #8
0
def validate_tz(value):
    value = cv.string_strict(value)

    try:
        import pytz

        return convert_tz(pytz.timezone(value))
    except Exception:  # pylint: disable=broad-except
        return value
Exemple #9
0
def validate_password(value):
    value = cv.string_strict(value)
    if not value:
        return value
    if len(value) < 8:
        raise cv.Invalid("WPA password must be at least 8 characters long")
    if len(value) > 64:
        raise cv.Invalid("WPA password must be at most 64 characters long")
    return value
Exemple #10
0
def validate_tz(value: str) -> str:
    value = cv.string_strict(value)

    tzfile = _load_tzdata(value)
    if tzfile is None:
        # Not a IANA key, probably a TZ string
        return value

    return _extract_tz_string(tzfile)
def validate_command_id(value):
    """Validate that this value is a valid command id.
    """
    value = cv.string_strict(value).lower()
    if value in BUILTIN_CMD_IDS:
        raise cv.Invalid(f"{value} is a built-in command")
    for c in value:
        if c not in CMD_ID_CHARACTERS:
            raise cv.Invalid(f"Invalid character for command id: {c}")
    return value
Exemple #12
0
def validate_copy_output(value):
    if not isinstance(value, dict):
        raise vol.Invalid("Value must be dict")
    type = cv.string_strict(value.get(CONF_TYPE, 'float')).lower()
    value[CONF_TYPE] = type
    if type == 'binary':
        return BINARY_SCHEMA(value)
    if type == 'float':
        return FLOAT_SCHEMA(value)
    raise vol.Invalid(
        "type must either be binary or float, not {}!".format(type))
Exemple #13
0
def validate_encryption_key(value):
    value = cv.string_strict(value)
    try:
        decoded = base64.b64decode(value, validate=True)
    except ValueError as err:
        raise cv.Invalid(
            "Invalid key format, please check it's using base64") from err

    if len(decoded) != 32:
        raise cv.Invalid("Encryption key must be base64 and 32 bytes long")

    # Return original data for roundtrip conversion
    return value
Exemple #14
0
def create_aes_key(value):
    value = cv.string_strict(value)
    parts = value.split(':')
    if len(parts) != 16:
        raise cv.Invalid("AES Key must consist of 16 : (colon) separated parts")
    parts_int = []
    if any(len(part) != 2 for part in parts):
        raise cv.Invalid("AES Key must be format XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX")
    for part in parts:
        try:
            parts_int.append(int(part, 16))
        except ValueError as error:
            raise cv.Invalid("AES Key parts must be hexadecimal values from 00 to FF") from error
    return AesKey(*parts_int)
Exemple #15
0
def validate_decryption_key(value):
    value = string_strict(value)
    parts = [value[i:i + 2] for i in range(0, len(value), 2)]
    if len(parts) != 16:
        raise Invalid("Decryption key must consist of 16 hexadecimal numbers")
    parts_int = []
    if any(len(part) != 2 for part in parts):
        raise Invalid("Decryption key must be format XX")
    for part in parts:
        try:
            parts_int.append(int(part, 16))
        except ValueError:
            raise Invalid("Decryption key must be hex values from 00 to FF")

    return ''.join(f'{part:02X}' for part in parts_int)
Exemple #16
0
def _validate_key(value):
    value = cv.string_strict(value)
    parts = [value[i : i + 2] for i in range(0, len(value), 2)]
    if len(parts) != 16:
        raise cv.Invalid("Decryption key must consist of 16 hexadecimal numbers")
    parts_int = []
    if any(len(part) != 2 for part in parts):
        raise cv.Invalid("Decryption key must be format XX")
    for part in parts:
        try:
            parts_int.append(int(part, 16))
        except ValueError:
            # pylint: disable=raise-missing-from
            raise cv.Invalid("Decryption key must be hex values from 00 to FF")

    return "".join(f"{part:02X}" for part in parts_int)
Exemple #17
0
def validate_uid(value):
    value = cv.string_strict(value)
    for x in value.split('-'):
        if len(x) != 2:
            raise vol.Invalid(
                "Each part (separated by '-') of the UID must be two characters "
                "long.")
        try:
            x = int(x, 16)
        except ValueError:
            raise vol.Invalid(
                "Valid characters for parts of a UID are 0123456789ABCDEF.")
        if x < 0 or x > 255:
            raise vol.Invalid(
                "Valid values for UID parts (separated by '-') are 00 to FF")
    return value
Exemple #18
0
def validate_test_data(value):
    value = string_strict(value)
    parts = [value[i:i + 2] for i in range(0, len(value), 2)]
    if len(parts) != 123:
        raise Invalid(
            "Test Data must consist of 123 hexadecimal numbers, starting and ending with 7E"
        )
    parts_int = []
    if any(len(part) != 2 for part in parts):
        raise Invalid("Test Data must be format XX")
    for part in parts:
        try:
            parts_int.append(int(part, 16))
        except ValueError:
            raise Invalid("Test Data must be hex values from 00 to FF")

    return ''.join(f'{part:02X}' for part in parts_int)
Exemple #19
0
def validate_simple_esphome_core_version(value):
    value = cv.string_strict(value)
    if value.upper() == 'LATEST':
        if ESPHOME_CORE_VERSION == 'dev':
            return validate_simple_esphome_core_version('dev')
        return {
            CONF_REPOSITORY: LIBRARY_URI_REPO,
            CONF_TAG: 'v' + ESPHOME_CORE_VERSION,
        }
    if value.upper() == 'DEV':
        return {CONF_REPOSITORY: LIBRARY_URI_REPO, CONF_BRANCH: 'dev'}
    if VERSION_REGEX.match(value) is not None:
        return {
            CONF_REPOSITORY: LIBRARY_URI_REPO,
            CONF_TAG: 'v' + value,
        }
    raise vol.Invalid("Only simple esphome core versions!")
Exemple #20
0
def validate_file_shorthand(value):
    value = cv.string_strict(value)
    if value.startswith("gfonts://"):
        match = re.match(r"^gfonts://([^@]+)(@.+)?$", value)
        if match is None:
            raise cv.Invalid(
                "Could not parse gfonts shorthand syntax, please check it")
        family = match.group(1)
        weight = match.group(2)
        data = {
            CONF_TYPE: TYPE_GFONTS,
            CONF_FAMILY: family,
        }
        if weight is not None:
            data[CONF_WEIGHT] = weight[1:]
        return FILE_SCHEMA(data)
    return FILE_SCHEMA({
        CONF_TYPE: TYPE_LOCAL,
        CONF_PATH: value,
    })
Exemple #21
0
def do_substitution_pass(config):
    if CONF_SUBSTITUTIONS not in config:
        return

    substitutions = config[CONF_SUBSTITUTIONS]
    with cv.prepend_path('substitutions'):
        if not isinstance(substitutions, dict):
            raise cv.Invalid(
                u"Substitutions must be a key to value mapping, got {}"
                u"".format(type(substitutions)))

        replace_keys = []
        for key, value in substitutions.items():
            with cv.prepend_path(key):
                sub = validate_substitution_key(key)
                if sub != key:
                    replace_keys.append((key, sub))
                substitutions[key] = cv.string_strict(value)
        for old, new in replace_keys:
            substitutions[new] = substitutions[old]
            del substitutions[old]

    config[CONF_SUBSTITUTIONS] = substitutions
    _substitute_item(substitutions, config, [])
Exemple #22
0
def validate_import_url(value):
    value = cv.string_strict(value)
    value = cv.Length(max=255)(value)
    # ignore result, only check if it's a valid shorthand
    validate_source_shorthand(value)
    return value
def test_strict_string__valid(value):
    actual = config_validation.string_strict(value)

    assert actual == value
def test_string_string__invalid(value):
    with pytest.raises(Invalid, match="Must be string, got"):
        config_validation.string_strict(value)