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
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, [])
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" )
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
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
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)
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
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
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
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))
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
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)
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)
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)
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
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)
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!")
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, })
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, [])
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)