Use probatio directly in core, auth, helpers and util (#182107)

This commit is contained in:
Franck Nijhof
2026-09-13 12:08:49 -04:00
committed by GitHub
parent f3f1b7ae63
commit 06ac207c22
43 changed files with 1143 additions and 1070 deletions
+11 -11
View File
@@ -4,8 +4,8 @@ import logging
import types
from typing import Any
import voluptuous as vol
from voluptuous.humanize import humanize_error
import probatio
from probatio.humanize import humanize_error
from homeassistant import data_entry_flow, requirements
from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE
@@ -18,14 +18,14 @@ from homeassistant.util.hass_dict import HassKey
MULTI_FACTOR_AUTH_MODULES: Registry[str, type[MultiFactorAuthModule]] = Registry()
MULTI_FACTOR_AUTH_MODULE_SCHEMA = vol.Schema(
MULTI_FACTOR_AUTH_MODULE_SCHEMA = probatio.Schema(
{
vol.Required(CONF_TYPE): str,
vol.Optional(CONF_NAME): str,
probatio.Required(CONF_TYPE): str,
probatio.Optional(CONF_NAME): str,
# Specify ID if you have two mfa auth module for same type.
vol.Optional(CONF_ID): str,
probatio.Optional(CONF_ID): str,
},
extra=vol.ALLOW_EXTRA,
extra=probatio.ALLOW_EXTRA,
)
DATA_REQS: HassKey[set[str]] = HassKey("mfa_auth_module_reqs_processed")
@@ -65,8 +65,8 @@ class MultiFactorAuthModule:
# Implement by extending class
@property
def input_schema(self) -> vol.Schema:
"""Return a voluptuous schema to define mfa auth module's input."""
def input_schema(self) -> probatio.Schema:
"""Return a schema to define mfa auth module's input."""
raise NotImplementedError
async def async_setup_flow(self, user_id: str) -> SetupFlow[Any]:
@@ -101,7 +101,7 @@ class SetupFlow[_MultiFactorAuthModuleT: MultiFactorAuthModule = MultiFactorAuth
def __init__(
self,
auth_module: _MultiFactorAuthModuleT,
setup_schema: vol.Schema,
setup_schema: probatio.Schema,
user_id: str,
) -> None:
"""Initialize the setup flow."""
@@ -137,7 +137,7 @@ async def auth_mfa_module_from_config(
try:
config = module.CONFIG_SCHEMA(config)
except vol.Invalid as err:
except probatio.Invalid as err:
_LOGGER.error(
"Invalid configuration for multi-factor module %s: %s",
module_name,
@@ -2,7 +2,7 @@
from typing import Any, override
import voluptuous as vol
import probatio
from homeassistant.core import HomeAssistant
@@ -15,11 +15,13 @@ from . import (
CONFIG_SCHEMA = MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend(
{
vol.Required("data"): [
vol.Schema({vol.Required("user_id"): str, vol.Required("pin"): str})
probatio.Required("data"): [
probatio.Schema(
{probatio.Required("user_id"): str, probatio.Required("pin"): str}
)
]
},
extra=vol.PREVENT_EXTRA,
extra=probatio.PREVENT_EXTRA,
)
@@ -36,14 +38,14 @@ class InsecureExampleModule(MultiFactorAuthModule):
@property
@override
def input_schema(self) -> vol.Schema:
def input_schema(self) -> probatio.Schema:
"""Validate login flow input data."""
return vol.Schema({vol.Required("pin"): str})
return probatio.Schema({probatio.Required("pin"): str})
@property
def setup_schema(self) -> vol.Schema:
def setup_schema(self) -> probatio.Schema:
"""Validate async_setup_user input data."""
return vol.Schema({vol.Required("pin"): str})
return probatio.Schema({probatio.Required("pin"): str})
@override
async def async_setup_flow(self, user_id: str) -> SetupFlow:
+15 -11
View File
@@ -8,7 +8,7 @@ import logging
from typing import Any, cast, override
import attr
import voluptuous as vol
import probatio
from homeassistant.const import CONF_EXCLUDE, CONF_INCLUDE
from homeassistant.core import HomeAssistant, callback
@@ -30,11 +30,13 @@ CONF_MESSAGE = "message"
CONFIG_SCHEMA = MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend(
{
vol.Optional(CONF_INCLUDE): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_EXCLUDE): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_MESSAGE, default="{} is your Home Assistant login code"): str,
probatio.Optional(CONF_INCLUDE): probatio.All(cv.ensure_list, [cv.string]),
probatio.Optional(CONF_EXCLUDE): probatio.All(cv.ensure_list, [cv.string]),
probatio.Optional(
CONF_MESSAGE, default="{} is your Home Assistant login code"
): str,
},
extra=vol.PREVENT_EXTRA,
extra=probatio.PREVENT_EXTRA,
)
STORAGE_VERSION = 1
@@ -108,9 +110,9 @@ class NotifyAuthModule(MultiFactorAuthModule):
@property
@override
def input_schema(self) -> vol.Schema:
def input_schema(self) -> probatio.Schema:
"""Validate login flow input data."""
return vol.Schema({vol.Required(INPUT_FIELD_CODE): str})
return probatio.Schema({probatio.Required(INPUT_FIELD_CODE): str})
async def _async_load(self) -> None:
"""Load stored data."""
@@ -277,7 +279,7 @@ class NotifySetupFlow(SetupFlow[NotifyAuthModule]):
def __init__(
self,
auth_module: NotifyAuthModule,
setup_schema: vol.Schema,
setup_schema: probatio.Schema,
user_id: str,
available_notify_services: list[str],
) -> None:
@@ -308,10 +310,12 @@ class NotifySetupFlow(SetupFlow[NotifyAuthModule]):
if not self._available_notify_services:
return self.async_abort(reason="no_available_service")
schema = vol.Schema(
schema = probatio.Schema(
{
vol.Required("notify_service"): vol.In(self._available_notify_services),
vol.Optional("target"): str,
probatio.Required("notify_service"): probatio.In(
self._available_notify_services
),
probatio.Optional("target"): str,
}
)
+6 -6
View File
@@ -4,7 +4,7 @@ import asyncio
from io import BytesIO
from typing import Any, cast, override
import voluptuous as vol
import probatio
from homeassistant.auth.models import User
from homeassistant.core import HomeAssistant
@@ -20,7 +20,7 @@ from . import (
REQUIREMENTS = ["pyotp==2.9.0", "PyQRCode==1.2.1"]
CONFIG_SCHEMA = MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend({}, extra=vol.PREVENT_EXTRA)
CONFIG_SCHEMA = MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend({}, extra=probatio.PREVENT_EXTRA)
STORAGE_VERSION = 1
STORAGE_KEY = "auth_module.totp"
@@ -88,9 +88,9 @@ class TotpAuthModule(MultiFactorAuthModule):
@property
@override
def input_schema(self) -> vol.Schema:
def input_schema(self) -> probatio.Schema:
"""Validate login flow input data."""
return vol.Schema({vol.Required(INPUT_FIELD_CODE): str})
return probatio.Schema({probatio.Required(INPUT_FIELD_CODE): str})
async def _async_load(self) -> None:
"""Load stored data."""
@@ -163,7 +163,7 @@ class TotpAuthModule(MultiFactorAuthModule):
await self._async_load()
# user_input has been validate in caller
# set INPUT_FIELD_CODE as vol.Required is not user friendly
# set INPUT_FIELD_CODE as probatio.Required is not user friendly
return await self.hass.async_add_executor_job(
self._validate_2fa, user_id, user_input.get(INPUT_FIELD_CODE, "")
)
@@ -189,7 +189,7 @@ class TotpSetupFlow(SetupFlow[TotpAuthModule]):
_image: str
def __init__(
self, auth_module: TotpAuthModule, setup_schema: vol.Schema, user: User
self, auth_module: TotpAuthModule, setup_schema: probatio.Schema, user: User
) -> None:
"""Initialize the setup flow."""
super().__init__(auth_module, setup_schema, user.id)
+2 -2
View File
@@ -3,7 +3,7 @@
from collections.abc import Callable, Iterable
from typing import TYPE_CHECKING, override
import voluptuous as vol
import probatio
from .const import CAT_ENTITIES
from .entities import ENTITY_POLICY_SCHEMA, compile_entities
@@ -15,7 +15,7 @@ from .util import test_all
if TYPE_CHECKING:
from ..models import User
POLICY_SCHEMA = vol.Schema({vol.Optional(CAT_ENTITIES): ENTITY_POLICY_SCHEMA})
POLICY_SCHEMA = probatio.Schema({probatio.Optional(CAT_ENTITIES): ENTITY_POLICY_SCHEMA})
__all__ = [
"POLICY_SCHEMA",
+14 -14
View File
@@ -3,7 +3,7 @@
from collections import OrderedDict
from collections.abc import Callable
import voluptuous as vol
import probatio
from homeassistant.helpers import device_registry as dr
@@ -12,13 +12,13 @@ from .models import PermissionLookup
from .types import CategoryType, SubCategoryDict, ValueType
from .util import SubCatLookupType, compile_policy, lookup_all
SINGLE_ENTITY_SCHEMA = vol.Any(
SINGLE_ENTITY_SCHEMA = probatio.Any(
True,
vol.Schema(
probatio.Schema(
{
vol.Optional(POLICY_READ): True,
vol.Optional(POLICY_CONTROL): True,
vol.Optional(POLICY_EDIT): True,
probatio.Optional(POLICY_READ): True,
probatio.Optional(POLICY_CONTROL): True,
probatio.Optional(POLICY_EDIT): True,
}
),
)
@@ -28,17 +28,17 @@ ENTITY_AREAS = "area_ids"
ENTITY_DEVICE_IDS = "device_ids"
ENTITY_ENTITY_IDS = "entity_ids"
ENTITY_VALUES_SCHEMA = vol.Any(True, vol.Schema({str: SINGLE_ENTITY_SCHEMA}))
ENTITY_VALUES_SCHEMA = probatio.Any(True, probatio.Schema({str: SINGLE_ENTITY_SCHEMA}))
ENTITY_POLICY_SCHEMA = vol.Any(
ENTITY_POLICY_SCHEMA = probatio.Any(
True,
vol.Schema(
probatio.Schema(
{
vol.Optional(SUBCAT_ALL): SINGLE_ENTITY_SCHEMA,
vol.Optional(ENTITY_AREAS): ENTITY_VALUES_SCHEMA,
vol.Optional(ENTITY_DEVICE_IDS): ENTITY_VALUES_SCHEMA,
vol.Optional(ENTITY_DOMAINS): ENTITY_VALUES_SCHEMA,
vol.Optional(ENTITY_ENTITY_IDS): ENTITY_VALUES_SCHEMA,
probatio.Optional(SUBCAT_ALL): SINGLE_ENTITY_SCHEMA,
probatio.Optional(ENTITY_AREAS): ENTITY_VALUES_SCHEMA,
probatio.Optional(ENTITY_DEVICE_IDS): ENTITY_VALUES_SCHEMA,
probatio.Optional(ENTITY_DOMAINS): ENTITY_VALUES_SCHEMA,
probatio.Optional(ENTITY_ENTITY_IDS): ENTITY_VALUES_SCHEMA,
}
),
)
+10 -10
View File
@@ -5,8 +5,8 @@ import logging
import types
from typing import Any
import voluptuous as vol
from voluptuous.humanize import humanize_error
import probatio
from probatio.humanize import humanize_error
from homeassistant import requirements
from homeassistant.const import CONF_ID, CONF_NAME, CONF_TYPE
@@ -34,14 +34,14 @@ DATA_REQS: HassKey[set[str]] = HassKey("auth_prov_reqs_processed")
AUTH_PROVIDERS: Registry[str, type[AuthProvider]] = Registry()
AUTH_PROVIDER_SCHEMA = vol.Schema(
AUTH_PROVIDER_SCHEMA = probatio.Schema(
{
vol.Required(CONF_TYPE): str,
vol.Optional(CONF_NAME): str,
probatio.Required(CONF_TYPE): str,
probatio.Optional(CONF_NAME): str,
# Specify ID if you have two auth providers for same type.
vol.Optional(CONF_ID): str,
probatio.Optional(CONF_ID): str,
},
extra=vol.ALLOW_EXTRA,
extra=probatio.ALLOW_EXTRA,
)
@@ -148,7 +148,7 @@ async def auth_provider_from_config(
try:
config = module.CONFIG_SCHEMA(config)
except vol.Invalid as err:
except probatio.Invalid as err:
_LOGGER.error(
"Invalid configuration for auth provider %s: %s",
provider_name,
@@ -237,8 +237,8 @@ class LoginFlow[_AuthProviderT: AuthProvider = AuthProvider](
return self.async_show_form(
step_id="select_mfa_module",
data_schema=vol.Schema(
{"multi_factor_auth_module": vol.In(self.available_mfa_modules)}
data_schema=probatio.Schema(
{"multi_factor_auth_module": probatio.In(self.available_mfa_modules)}
),
errors=errors,
)
+10 -8
View File
@@ -6,7 +6,7 @@ import logging
import os
from typing import Any, override
import voluptuous as vol
import probatio
from homeassistant.const import CONF_COMMAND
from homeassistant.exceptions import HomeAssistantError
@@ -19,13 +19,15 @@ CONF_META = "meta"
CONFIG_SCHEMA = AUTH_PROVIDER_SCHEMA.extend(
{
vol.Required(CONF_COMMAND): vol.All(
probatio.Required(CONF_COMMAND): probatio.All(
str, os.path.normpath, msg="must be an absolute path"
),
vol.Optional(CONF_ARGS, default=None): vol.Any(vol.DefaultTo(list), [str]),
vol.Optional(CONF_META, default=False): bool,
probatio.Optional(CONF_ARGS, default=None): probatio.Any(
probatio.DefaultTo(list), [str]
),
probatio.Optional(CONF_META, default=False): bool,
},
extra=vol.PREVENT_EXTRA,
extra=probatio.PREVENT_EXTRA,
)
_LOGGER = logging.getLogger(__name__)
@@ -161,10 +163,10 @@ class CommandLineLoginFlow(LoginFlow[CommandLineAuthProvider]):
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
data_schema=probatio.Schema(
{
vol.Required("username"): str,
vol.Required("password"): str,
probatio.Required("username"): str,
probatio.Required("password"): str,
}
),
errors=errors,
@@ -6,7 +6,7 @@ from collections.abc import Mapping
from typing import Any, cast, override
import bcrypt
import voluptuous as vol
import probatio
from homeassistant.const import CONF_ID
from homeassistant.core import HomeAssistant, callback
@@ -23,12 +23,12 @@ STORAGE_KEY = "auth_provider.homeassistant"
def _disallow_id(conf: dict[str, Any]) -> dict[str, Any]:
"""Disallow ID in config."""
if CONF_ID in conf:
raise vol.Invalid("ID is not allowed for the homeassistant auth provider.")
raise probatio.Invalid("ID is not allowed for the homeassistant auth provider.")
return conf
CONFIG_SCHEMA = vol.All(AUTH_PROVIDER_SCHEMA, _disallow_id)
CONFIG_SCHEMA = probatio.All(AUTH_PROVIDER_SCHEMA, _disallow_id)
@callback
@@ -376,10 +376,10 @@ class HassLoginFlow(LoginFlow[HassAuthProvider]):
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
data_schema=probatio.Schema(
{
vol.Required("username"): str,
vol.Required("password"): str,
probatio.Required("username"): str,
probatio.Required("password"): str,
}
),
errors=errors,
@@ -4,7 +4,7 @@ from collections.abc import Mapping
import hmac
from typing import override
import voluptuous as vol
import probatio
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
@@ -12,17 +12,17 @@ from homeassistant.exceptions import HomeAssistantError
from ..models import AuthFlowContext, AuthFlowResult, Credentials, UserMeta
from . import AUTH_PROVIDER_SCHEMA, AUTH_PROVIDERS, AuthProvider, LoginFlow
USER_SCHEMA = vol.Schema(
USER_SCHEMA = probatio.Schema(
{
vol.Required("username"): str,
vol.Required("password"): str,
vol.Optional("name"): str,
probatio.Required("username"): str,
probatio.Required("password"): str,
probatio.Optional("name"): str,
}
)
CONFIG_SCHEMA = AUTH_PROVIDER_SCHEMA.extend(
{vol.Required("users"): [USER_SCHEMA]}, extra=vol.PREVENT_EXTRA
{probatio.Required("users"): [USER_SCHEMA]}, extra=probatio.PREVENT_EXTRA
)
@@ -120,10 +120,10 @@ class ExampleLoginFlow(LoginFlow[ExampleAuthProvider]):
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
data_schema=probatio.Schema(
{
vol.Required("username"): str,
vol.Required("password"): str,
probatio.Required("username"): str,
probatio.Required("password"): str,
}
),
errors=errors,
@@ -15,7 +15,7 @@ from ipaddress import (
)
from typing import Any, cast, override
import voluptuous as vol
import probatio
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
@@ -42,24 +42,26 @@ CONF_ALLOW_BYPASS_LOGIN = "allow_bypass_login"
CONFIG_SCHEMA = AUTH_PROVIDER_SCHEMA.extend(
{
vol.Required(CONF_TRUSTED_NETWORKS): vol.All(cv.ensure_list, [ip_network]),
vol.Optional(CONF_TRUSTED_USERS, default={}): vol.Schema(
probatio.Required(CONF_TRUSTED_NETWORKS): probatio.All(
cv.ensure_list, [ip_network]
),
probatio.Optional(CONF_TRUSTED_USERS, default={}): probatio.Schema(
# we only validate the format of user_id or group_id
{
ip_network: vol.All(
ip_network: probatio.All(
cv.ensure_list,
[
vol.Or(
probatio.Or(
cv.uuid4_hex,
vol.Schema({vol.Required(CONF_GROUP): str}),
probatio.Schema({probatio.Required(CONF_GROUP): str}),
)
],
)
}
),
vol.Optional(CONF_ALLOW_BYPASS_LOGIN, default=False): cv.boolean,
probatio.Optional(CONF_ALLOW_BYPASS_LOGIN, default=False): cv.boolean,
},
extra=vol.PREVENT_EXTRA,
extra=probatio.PREVENT_EXTRA,
)
@@ -256,7 +258,7 @@ class TrustedNetworksLoginFlow(LoginFlow[TrustedNetworksAuthProvider]):
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{vol.Required("user"): vol.In(self._available_users)}
data_schema=probatio.Schema(
{probatio.Required("user"): probatio.In(self._available_users)}
),
)
+3 -3
View File
@@ -20,7 +20,7 @@ from typing import TYPE_CHECKING, Any, override
# _frozen_importlib._DeadlockError: deadlock detected by
# _ModuleLock('cryptography.hazmat.backends.openssl.backend')
import cryptography.hazmat.backends.openssl.backend # noqa: F401
import voluptuous as vol
import probatio
import yarl
from . import (
@@ -555,7 +555,7 @@ async def async_from_config_dict(
try:
await async_process_ha_core_config(hass, core_config)
except vol.Invalid as config_err:
except probatio.Invalid as config_err:
conf_util.async_log_schema_error(config_err, core.DOMAIN, core_config, hass)
async_notify_setup_error(hass, core.DOMAIN)
return None
@@ -692,7 +692,7 @@ def _log_file_disabled_reason() -> str | None:
try:
if cv.boolean(disable_log_file):
return LOG_FILE_DISABLED_REASON_ENVIRONMENT
except vol.Invalid:
except probatio.Invalid:
_LOGGER.warning(
"Ignoring invalid %s value: %s. Expected a boolean value: "
"1/0, true/false, yes/no, on/off, or enable/disable",
+28 -29
View File
@@ -16,9 +16,8 @@ from types import ModuleType
from typing import TYPE_CHECKING, Any, Literal, overload
from awesomeversion import AwesomeVersion
from probatio import Undefined
import voluptuous as vol
from voluptuous.humanize import MAX_VALIDATION_ERROR_ITEM_LENGTH
import probatio
from probatio.humanize import MAX_VALIDATION_ERROR_ITEM_LENGTH
from yaml.error import MarkedYAMLError
from .const import CONF_PACKAGES, CONF_PLATFORM, __version__
@@ -233,7 +232,7 @@ async def async_hass_config_yaml(hass: HomeAssistant) -> dict:
for key in config:
try:
cv.domain_key(key)
except vol.Invalid as exc:
except probatio.Invalid as exc:
suffix = ""
if annotation := find_annotation(config, exc.path):
suffix = f" at {_relpath(hass, annotation[0])}, line {annotation[1]}"
@@ -245,7 +244,7 @@ async def async_hass_config_yaml(hass: HomeAssistant) -> dict:
core_config = config.get(HOMEASSISTANT_DOMAIN, {})
try:
await merge_packages_config(hass, config, core_config.get(CONF_PACKAGES, {}))
except vol.Invalid as exc:
except probatio.Invalid as exc:
suffix = ""
if annotation := find_annotation(
config, [HOMEASSISTANT_DOMAIN, CONF_PACKAGES, *exc.path]
@@ -342,7 +341,7 @@ def process_ha_config_upgrade(hass: HomeAssistant) -> None:
@callback
def async_log_schema_error(
exc: vol.Invalid,
exc: probatio.Invalid,
domain: str,
config: dict,
hass: HomeAssistant,
@@ -355,14 +354,14 @@ def async_log_schema_error(
@callback
def async_log_config_validator_error(
exc: vol.Invalid | HomeAssistantError,
exc: probatio.Invalid | HomeAssistantError,
domain: str,
config: dict,
hass: HomeAssistant,
link: str | None = None,
) -> None:
"""Log an error from a custom config validator."""
if isinstance(exc, vol.Invalid):
if isinstance(exc, probatio.Invalid):
async_log_schema_error(exc, domain, config, hass, link)
return
@@ -448,16 +447,16 @@ def _relpath(hass: HomeAssistant, path: str) -> str:
def stringify_invalid(
hass: HomeAssistant,
exc: vol.Invalid,
exc: probatio.Invalid,
domain: str,
config: dict,
link: str | None,
max_sub_error_length: int,
) -> str:
"""Stringify voluptuous.Invalid.
"""Stringify probatio.Invalid.
This is an alternative to the custom __str__ implemented in
voluptuous.error.Invalid. The modifications are:
probatio.error.Invalid. The modifications are:
- Format the path delimited by -> instead of @data[]
- Prefix with domain, file and line of the error
- Suffix with a link to the documentation
@@ -490,7 +489,7 @@ def stringify_invalid(
f"{message_suffix}"
)
# This function is an alternative to the stringification done by
# vol.Invalid.__str__, so we need to call Exception.__str__ here
# probatio.Invalid.__str__, so we need to call Exception.__str__ here
# instead of str(exc)
output = Exception.__str__(exc)
if error_type := exc.error_type:
@@ -508,7 +507,7 @@ def stringify_invalid(
def humanize_error(
hass: HomeAssistant,
validation_error: vol.Invalid,
validation_error: probatio.Invalid,
domain: str,
config: dict,
link: str | None,
@@ -516,10 +515,10 @@ def humanize_error(
) -> str:
"""Provide a more helpful + complete validation error message.
This is a modified version of voluptuous.error.Invalid.__str__,
This is a modified version of probatio.error.Invalid.__str__,
the modifications make some minor changes to the formatting.
"""
if isinstance(validation_error, vol.MultipleInvalid):
if isinstance(validation_error, probatio.MultipleInvalid):
return "\n".join(
sorted(
humanize_error(
@@ -564,7 +563,7 @@ def format_homeassistant_error(
@callback
def format_schema_error(
hass: HomeAssistant,
exc: vol.Invalid,
exc: probatio.Invalid,
domain: str,
config: dict,
link: str | None = None,
@@ -588,12 +587,12 @@ def _log_pkg_error(
def _identify_config_schema(module: ComponentProtocol) -> str | None:
"""Extract the schema and identify list or dict based."""
if not isinstance(module.CONFIG_SCHEMA, vol.Schema):
if not isinstance(module.CONFIG_SCHEMA, probatio.Schema):
return None # type: ignore[unreachable]
schema = module.CONFIG_SCHEMA.schema
if isinstance(schema, vol.All):
if isinstance(schema, probatio.All):
for subschema in schema.validators:
if isinstance(subschema, dict):
schema = subschema
@@ -609,7 +608,7 @@ def _identify_config_schema(module: ComponentProtocol) -> str | None:
_LOGGER.exception("Unexpected error identifying config schema")
return None
if hasattr(key, "default") and not isinstance(key.default, Undefined):
if hasattr(key, "default") and not isinstance(key.default, probatio.Undefined):
default_value = module.CONFIG_SCHEMA({module.DOMAIN: key.default()})[
module.DOMAIN
]
@@ -671,7 +670,7 @@ async def merge_packages_config(
"""Merge packages into the top-level configuration.
Ignores packages that cannot be setup. Mutates config. Raises
vol.Invalid if whole package config is invalid.
probatio.Invalid if whole package config is invalid.
"""
_PACKAGES_CONFIG_SCHEMA(packages)
@@ -680,7 +679,7 @@ async def merge_packages_config(
for pack_name, pack_conf in packages.items():
try:
_validate_package_definition(pack_name, pack_conf)
except vol.Invalid as exc:
except probatio.Invalid as exc:
_log_pkg_error(
hass,
pack_name,
@@ -697,7 +696,7 @@ async def merge_packages_config(
continue
try:
domain = cv.domain_key(comp_name)
except vol.Invalid:
except probatio.Invalid:
_log_pkg_error(
hass, pack_name, comp_name, config, f"Invalid domain '{comp_name}'"
)
@@ -819,7 +818,7 @@ def _get_log_message_and_stack_print_pref(
# If no pre defined log_message is set, we generate an enriched error
# message, so we can notify about it during setup
show_stack_trace = False
if isinstance(exception, vol.Invalid):
if isinstance(exception, probatio.Invalid):
log_message = format_schema_error(
hass, exception, platform_path, platform_config, link
)
@@ -1025,7 +1024,7 @@ def extract_platform_integrations(
for key, domain_config in config.items():
try:
domain = cv.domain_key(key)
except vol.Invalid:
except probatio.Invalid:
continue
if domain not in domains:
continue
@@ -1050,7 +1049,7 @@ def extract_domain_configs(config: ConfigType, domain: str) -> Sequence[str]:
"""
domain_configs = []
for key in config:
with suppress(vol.Invalid):
with suppress(probatio.Invalid):
if cv.domain_key(key) != domain:
continue
domain_configs.append(key)
@@ -1096,7 +1095,7 @@ async def _async_load_and_validate_platform_integration(
# Validate platform specific schema
try:
return platform.PLATFORM_SCHEMA(p_integration.config) # type: ignore[no-any-return]
except vol.Invalid as exc:
except probatio.Invalid as exc:
exc_info = ConfigExceptionInfo(
exc,
ConfigErrorTranslationKey.PLATFORM_CONFIG_VALIDATION_ERR,
@@ -1179,7 +1178,7 @@ async def async_process_component_config(
return IntegrationConfigInfo(
await config_validator.async_validate_config(hass, config), []
)
except (vol.Invalid, HomeAssistantError) as exc:
except (probatio.Invalid, HomeAssistantError) as exc:
exc_info = ConfigExceptionInfo(
exc,
ConfigErrorTranslationKey.CONFIG_VALIDATION_ERR,
@@ -1206,7 +1205,7 @@ async def async_process_component_config(
return IntegrationConfigInfo(
await cv.async_validate(hass, component.CONFIG_SCHEMA, config), []
)
except vol.Invalid as exc:
except probatio.Invalid as exc:
exc_info = ConfigExceptionInfo(
exc,
ConfigErrorTranslationKey.CONFIG_VALIDATION_ERR,
@@ -1243,7 +1242,7 @@ async def async_process_component_config(
p_validated = await cv.async_validate(
hass, component_platform_schema, p_config
)
except vol.Invalid as exc:
except probatio.Invalid as exc:
exc_info = ConfigExceptionInfo(
exc,
ConfigErrorTranslationKey.PLATFORM_CONFIG_VALIDATION_ERR,
+2 -2
View File
@@ -24,8 +24,8 @@ from types import MappingProxyType
from typing import TYPE_CHECKING, Any, Self, TypedDict, cast, override
from async_interrupt import interrupt
import probatio
from propcache.api import cached_property
import voluptuous as vol
from . import data_entry_flow, loader
from .const import (
@@ -3618,7 +3618,7 @@ class ConfigFlow(ConfigEntryBaseFlow):
self,
*,
step_id: str | None = None,
data_schema: vol.Schema | None = None,
data_schema: probatio.Schema | None = None,
errors: dict[str, str] | None = None,
description_placeholders: Mapping[str, str] | None = None,
last_step: bool | None = None,
+3 -3
View File
@@ -41,8 +41,8 @@ from typing import (
override,
)
import probatio
from propcache.api import cached_property, under_cached_property
import voluptuous as vol
from . import util
from .const import (
@@ -2692,7 +2692,7 @@ class ServiceRegistry:
[ServiceCall],
Coroutine[Any, Any, ServiceResponse] | ServiceResponse | None,
],
schema: vol.Schema | None = None,
schema: probatio.Schema | None = None,
supports_response: SupportsResponse = SupportsResponse.NONE,
) -> None:
"""Register a service.
@@ -2924,7 +2924,7 @@ class ServiceRegistry:
if handler.schema:
try:
processed_data: dict[str, Any] = handler.schema(service_data)
except vol.Invalid:
except probatio.Invalid:
_LOGGER.debug(
"Invalid data for service call %s.%s: %s",
domain,
+56 -52
View File
@@ -10,7 +10,7 @@ import pathlib
from typing import TYPE_CHECKING, Any, Final, override
from urllib.parse import urlparse
import voluptuous as vol
import probatio
from webrtc_models import RTCConfiguration, RTCIceServer
import yarl
@@ -112,7 +112,7 @@ def _no_duplicate_auth_provider(
for config in configs:
key = (config[CONF_TYPE], config.get(CONF_ID))
if key in config_keys:
raise vol.Invalid(
raise probatio.Invalid(
f"Duplicate auth provider {config[CONF_TYPE]} found. "
"Please add unique IDs "
"if you want to have the same auth provider twice"
@@ -135,7 +135,7 @@ def _no_duplicate_auth_mfa_module(
for config in configs:
key = config.get(CONF_ID, config[CONF_TYPE])
if key in config_keys:
raise vol.Invalid(
raise probatio.Invalid(
f"Duplicate mfa module {config[CONF_TYPE]} found. "
"Please add unique IDs "
"if you want to have the same mfa module twice"
@@ -158,29 +158,31 @@ def _filter_bad_internal_external_urls(conf: dict) -> dict:
# Schema for all packages element
_PACKAGES_CONFIG_SCHEMA = vol.Schema({cv.string: vol.Any(dict, list)})
_PACKAGES_CONFIG_SCHEMA = probatio.Schema({cv.string: probatio.Any(dict, list)})
# Schema for individual package definition
_PACKAGE_DEFINITION_SCHEMA = vol.Schema({cv.string: vol.Any(dict, list, None)})
_CUSTOMIZE_DICT_SCHEMA = vol.Schema(
{
vol.Optional(ATTR_FRIENDLY_NAME): cv.string,
vol.Optional(ATTR_HIDDEN): cv.boolean,
vol.Optional(ATTR_ASSUMED_STATE): cv.boolean,
},
extra=vol.ALLOW_EXTRA,
_PACKAGE_DEFINITION_SCHEMA = probatio.Schema(
{cv.string: probatio.Any(dict, list, None)}
)
_CUSTOMIZE_CONFIG_SCHEMA = vol.Schema(
_CUSTOMIZE_DICT_SCHEMA = probatio.Schema(
{
vol.Optional(CONF_CUSTOMIZE, default={}): vol.Schema(
probatio.Optional(ATTR_FRIENDLY_NAME): cv.string,
probatio.Optional(ATTR_HIDDEN): cv.boolean,
probatio.Optional(ATTR_ASSUMED_STATE): cv.boolean,
},
extra=probatio.ALLOW_EXTRA,
)
_CUSTOMIZE_CONFIG_SCHEMA = probatio.Schema(
{
probatio.Optional(CONF_CUSTOMIZE, default={}): probatio.Schema(
{cv.entity_id: _CUSTOMIZE_DICT_SCHEMA}
),
vol.Optional(CONF_CUSTOMIZE_DOMAIN, default={}): vol.Schema(
probatio.Optional(CONF_CUSTOMIZE_DOMAIN, default={}): probatio.Schema(
{cv.string: _CUSTOMIZE_DICT_SCHEMA}
),
vol.Optional(CONF_CUSTOMIZE_GLOB, default={}): vol.Schema(
probatio.Optional(CONF_CUSTOMIZE_GLOB, default={}): probatio.Schema(
{cv.string: _CUSTOMIZE_DICT_SCHEMA}
),
}
@@ -243,8 +245,8 @@ def _raise_issue_if_no_country(hass: HomeAssistant, country: str | None) -> None
def _validate_currency(data: Any) -> Any:
try:
return cv.currency(data)
except vol.InInvalid:
with suppress(vol.InInvalid):
except probatio.InInvalid:
with suppress(probatio.InInvalid):
return cv.historic_currency(data)
raise
@@ -255,43 +257,43 @@ def validate_stun_or_turn_url(value: Any) -> str:
url = urlparse(url_in)
if url.scheme not in ("stun", "stuns", "turn", "turns"):
raise vol.Invalid("invalid url")
raise probatio.Invalid("invalid url")
return url_in
CORE_CONFIG_SCHEMA = vol.All(
CORE_CONFIG_SCHEMA = probatio.All(
_CUSTOMIZE_CONFIG_SCHEMA.extend(
{
CONF_NAME: vol.Coerce(str),
CONF_NAME: probatio.Coerce(str),
CONF_LATITUDE: cv.latitude,
CONF_LONGITUDE: cv.longitude,
CONF_ELEVATION: vol.Coerce(int),
CONF_ELEVATION: probatio.Coerce(int),
CONF_RADIUS: cv.positive_int,
vol.Remove(CONF_TEMPERATURE_UNIT): cv.temperature_unit,
CONF_UNIT_SYSTEM: vol.Any(
probatio.Remove(CONF_TEMPERATURE_UNIT): cv.temperature_unit,
CONF_UNIT_SYSTEM: probatio.Any(
_CONF_UNIT_SYSTEM_METRIC,
_CONF_UNIT_SYSTEM_US_CUSTOMARY,
_CONF_UNIT_SYSTEM_IMPERIAL,
),
CONF_TIME_ZONE: cv.time_zone,
vol.Optional(CONF_INTERNAL_URL): cv.url,
vol.Optional(CONF_EXTERNAL_URL): cv.url,
vol.Optional(CONF_ALLOWLIST_EXTERNAL_DIRS): vol.All(
cv.ensure_list, [vol.IsDir()]
probatio.Optional(CONF_INTERNAL_URL): cv.url,
probatio.Optional(CONF_EXTERNAL_URL): cv.url,
probatio.Optional(CONF_ALLOWLIST_EXTERNAL_DIRS): probatio.All(
cv.ensure_list, [probatio.IsDir()]
),
vol.Optional(LEGACY_CONF_WHITELIST_EXTERNAL_DIRS): vol.All(
cv.ensure_list, [vol.IsDir()]
probatio.Optional(LEGACY_CONF_WHITELIST_EXTERNAL_DIRS): probatio.All(
cv.ensure_list, [probatio.IsDir()]
),
vol.Optional(CONF_ALLOWLIST_EXTERNAL_URLS): vol.All(
probatio.Optional(CONF_ALLOWLIST_EXTERNAL_URLS): probatio.All(
cv.ensure_list, [cv.url]
),
vol.Optional(CONF_PACKAGES, default={}): _PACKAGES_CONFIG_SCHEMA,
vol.Optional(CONF_AUTH_PROVIDERS): vol.All(
probatio.Optional(CONF_PACKAGES, default={}): _PACKAGES_CONFIG_SCHEMA,
probatio.Optional(CONF_AUTH_PROVIDERS): probatio.All(
cv.ensure_list,
[
auth_providers.AUTH_PROVIDER_SCHEMA.extend(
{
CONF_TYPE: vol.NotIn(
CONF_TYPE: probatio.NotIn(
["insecure_example"],
(
"The insecure_example auth provider"
@@ -303,12 +305,12 @@ CORE_CONFIG_SCHEMA = vol.All(
],
_no_duplicate_auth_provider,
),
vol.Optional(CONF_AUTH_MFA_MODULES): vol.All(
probatio.Optional(CONF_AUTH_MFA_MODULES): probatio.All(
cv.ensure_list,
[
auth_mfa_modules.MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend(
{
CONF_TYPE: vol.NotIn(
CONF_TYPE: probatio.NotIn(
["insecure_example"],
"The insecure_example mfa module is for testing only.",
)
@@ -317,24 +319,26 @@ CORE_CONFIG_SCHEMA = vol.All(
],
_no_duplicate_auth_mfa_module,
),
vol.Optional(CONF_MEDIA_DIRS): cv.schema_with_slug_keys(vol.IsDir()),
vol.Remove(CONF_LEGACY_TEMPLATES): cv.boolean,
vol.Optional(CONF_CURRENCY): _validate_currency,
vol.Optional(CONF_COUNTRY): cv.country,
vol.Optional(CONF_LANGUAGE): cv.language,
vol.Optional(CONF_DEBUG): cv.boolean,
vol.Optional(CONF_WEBRTC): vol.Schema(
probatio.Optional(CONF_MEDIA_DIRS): cv.schema_with_slug_keys(
probatio.IsDir()
),
probatio.Remove(CONF_LEGACY_TEMPLATES): cv.boolean,
probatio.Optional(CONF_CURRENCY): _validate_currency,
probatio.Optional(CONF_COUNTRY): cv.country,
probatio.Optional(CONF_LANGUAGE): cv.language,
probatio.Optional(CONF_DEBUG): cv.boolean,
probatio.Optional(CONF_WEBRTC): probatio.Schema(
{
vol.Required(CONF_ICE_SERVERS): vol.All(
probatio.Required(CONF_ICE_SERVERS): probatio.All(
cv.ensure_list,
[
vol.Schema(
probatio.Schema(
{
vol.Required(CONF_URL): vol.All(
probatio.Required(CONF_URL): probatio.All(
cv.ensure_list, [validate_stun_or_turn_url]
),
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_CREDENTIAL): cv.string,
probatio.Optional(CONF_USERNAME): cv.string,
probatio.Optional(CONF_CREDENTIAL): cv.string,
}
)
],
@@ -352,7 +356,7 @@ async def async_process_ha_core_config(hass: HomeAssistant, config: dict) -> Non
This method is a coroutine.
"""
# CORE_CONFIG_SCHEMA is not async safe since it uses vol.IsDir
# CORE_CONFIG_SCHEMA is not async safe since it uses probatio.IsDir
# so we need to run it in an executor job.
config = await hass.async_add_executor_job(CORE_CONFIG_SCHEMA, config)
@@ -469,7 +473,7 @@ async def async_process_ha_core_config(hass: HomeAssistant, config: dict) -> Non
try:
pkg_cust = _CUSTOMIZE_CONFIG_SCHEMA(pkg_cust)
except vol.Invalid:
except probatio.Invalid:
_LOGGER.warning("Package %s contains invalid customize", name)
continue
@@ -884,7 +888,7 @@ class Config:
"language" in owner_store.data
and "language" in owner_store.data["language"]
):
with suppress(vol.InInvalid):
with suppress(probatio.InInvalid):
data["language"] = cv.language(
owner_store.data["language"]["language"]
)
+18 -18
View File
@@ -12,7 +12,7 @@ import logging
from types import MappingProxyType
from typing import Any, Generic, Required, TypedDict, TypeVar, cast
import voluptuous as vol
import probatio
from .core import HomeAssistant, callback
from .exceptions import HomeAssistantError
@@ -87,7 +87,7 @@ class UnknownStep(FlowError):
"""Unknown step specified."""
class InvalidData(vol.Invalid):
class InvalidData(probatio.Invalid):
"""Invalid data provided."""
def __init__(
@@ -130,7 +130,7 @@ class FlowResult(TypedDict, Generic[_FlowContextT, _HandlerT], total=False):
"""Typed result dict."""
context: _FlowContextT
data_schema: vol.Schema | None
data_schema: probatio.Schema | None
data: Mapping[str, Any]
description_placeholders: Mapping[str, str] | None
description: str | None
@@ -155,8 +155,8 @@ class FlowResult(TypedDict, Generic[_FlowContextT, _HandlerT], total=False):
def _map_error_to_schema_errors(
schema_errors: dict[str, Any],
error: vol.Invalid,
data_schema: vol.Schema,
error: probatio.Invalid,
data_schema: probatio.Schema,
) -> None:
"""Map an error to the correct position in the schema_errors.
@@ -171,7 +171,7 @@ def _map_error_to_schema_errors(
if len(error_path) > 1:
raise ValueError("Nested schemas are not supported")
# path_part can also be vol.Marker, but we need a string key
# path_part can also be probatio.Marker, but we need a string key
path_part_str = str(path_part)
schema_errors[path_part_str] = error.error_message
@@ -355,12 +355,12 @@ class FlowManager(abc.ABC, Generic[_FlowContextT, _FlowResultT, _HandlerT]):
if (
data_schema := cur_step.get("data_schema")
) is not None and user_input is not None:
data_schema = cast(vol.Schema, data_schema)
data_schema = cast(probatio.Schema, data_schema)
try:
user_input = data_schema(user_input)
except vol.Invalid as ex:
except probatio.Invalid as ex:
raised_errors = [ex]
if isinstance(ex, vol.MultipleInvalid):
if isinstance(ex, probatio.MultipleInvalid):
raised_errors = ex.errors
schema_errors: dict[str, Any] = {}
@@ -666,8 +666,8 @@ class FlowHandler(Generic[_FlowContextT, _FlowResultT, _HandlerT]):
return True
def add_suggested_values_to_schema(
self, data_schema: vol.Schema, suggested_values: Mapping[str, Any] | None
) -> vol.Schema:
self, data_schema: probatio.Schema, suggested_values: Mapping[str, Any] | None
) -> probatio.Schema:
"""Make a copy of the schema, populated with suggested values.
For each schema marker matching items in `suggested_values`,
@@ -694,20 +694,20 @@ class FlowHandler(Generic[_FlowContextT, _FlowResultT, _HandlerT]):
if (
suggested_values
and key in suggested_values
and isinstance(key, vol.Marker)
and isinstance(key, probatio.Marker)
):
# Copy the marker to not modify the flow schema
new_key = copy.copy(key)
new_key.description = {"suggested_value": suggested_values[key.schema]}
schema[new_key] = val
return vol.Schema(schema)
return probatio.Schema(schema)
@callback
def async_show_form(
self,
*,
step_id: str | None = None,
data_schema: vol.Schema | None = None,
data_schema: probatio.Schema | None = None,
errors: dict[str, str] | None = None,
description_placeholders: Mapping[str, str] | None = None,
last_step: bool | None = None,
@@ -891,7 +891,7 @@ class FlowHandler(Generic[_FlowContextT, _FlowResultT, _HandlerT]):
type=FlowResultType.MENU,
flow_id=self.flow_id,
handler=self.handler,
data_schema=vol.Schema({"next_step_id": vol.In(menu_options)}),
data_schema=probatio.Schema({"next_step_id": probatio.In(menu_options)}),
menu_options=menu_options,
description_placeholders=description_placeholders,
)
@@ -939,14 +939,14 @@ class SectionConfig(TypedDict, total=False):
class section:
"""Data entry flow section."""
CONFIG_SCHEMA = vol.Schema(
CONFIG_SCHEMA = probatio.Schema(
{
vol.Optional("collapsed", default=False): bool,
probatio.Optional("collapsed", default=False): bool,
},
)
def __init__(
self, schema: vol.Schema, options: SectionConfig | None = None
self, schema: probatio.Schema, options: SectionConfig | None = None
) -> None:
"""Initialize."""
self.schema = schema
+4 -4
View File
@@ -5,7 +5,7 @@ from dataclasses import dataclass
from enum import Enum
from typing import Any, Final, Self
import voluptuous as vol
import probatio
from homeassistant.const import CONF_OPTIONS
from homeassistant.core import HomeAssistant, split_entity_id
@@ -78,7 +78,7 @@ def get_relative_description_key(domain: str, key: str) -> str:
def move_top_level_schema_fields_to_options(
config: ConfigType, options_schema_dict: dict[vol.Marker, Any]
config: ConfigType, options_schema_dict: dict[probatio.Marker, Any]
) -> ConfigType:
"""Move top-level fields to options.
@@ -102,7 +102,7 @@ def move_top_level_schema_fields_to_options(
def move_options_fields_to_top_level(
config: ConfigType, base_schema: vol.Schema
config: ConfigType, base_schema: probatio.Schema
) -> ConfigType:
"""Move options fields to top-level.
@@ -128,7 +128,7 @@ def move_options_fields_to_top_level(
try:
new_config = base_schema(new_config)
except vol.Invalid:
except probatio.Invalid:
return config
new_config.update(options)
+8 -8
View File
@@ -7,7 +7,7 @@ from pathlib import Path
from typing import NamedTuple, Self
from annotatedyaml import loader as yaml_loader
import voluptuous as vol
import probatio
from homeassistant import loader
from homeassistant.config import ( # type: ignore[attr-defined]
@@ -114,13 +114,13 @@ async def async_check_ha_config_file( # noqa: C901
result.add_warning(message, domain, pack_config)
def _comp_error(
ex: vol.Invalid | HomeAssistantError,
ex: probatio.Invalid | HomeAssistantError,
domain: str,
component_config: ConfigType,
config_to_attach: ConfigType,
) -> None:
"""Handle errors from components."""
if isinstance(ex, vol.Invalid):
if isinstance(ex, probatio.Invalid):
message = format_schema_error(hass, ex, domain, component_config)
else:
message = format_homeassistant_error(hass, ex, domain, component_config)
@@ -173,7 +173,7 @@ async def async_check_ha_config_file( # noqa: C901
await merge_packages_config(
hass, config, core_config.get(CONF_PACKAGES, {}), _pack_error
)
except vol.Invalid as err:
except probatio.Invalid as err:
result.add_error(
format_schema_error(hass, err, HOMEASSISTANT_DOMAIN, core_config),
HOMEASSISTANT_DOMAIN,
@@ -224,7 +224,7 @@ async def async_check_ha_config_file( # noqa: C901
await config_validator.async_validate_config(hass, config)
)[domain]
continue
except (vol.Invalid, HomeAssistantError) as ex:
except (probatio.Invalid, HomeAssistantError) as ex:
_comp_error(ex, domain, config, config[domain])
continue
except Exception as err:
@@ -245,7 +245,7 @@ async def async_check_ha_config_file( # noqa: C901
# Don't fail if the validator removed the domain from the config
if domain in validated_config:
result[domain] = validated_config[domain]
except vol.Invalid as ex:
except probatio.Invalid as ex:
_comp_error(ex, domain, config, config[domain])
continue
@@ -265,7 +265,7 @@ async def async_check_ha_config_file( # noqa: C901
p_validated = await cv.async_validate(
hass, component_platform_schema, p_config
)
except vol.Invalid as ex:
except probatio.Invalid as ex:
_comp_error(ex, domain, p_config, p_config)
continue
@@ -305,7 +305,7 @@ async def async_check_ha_config_file( # noqa: C901
if platform_schema is not None:
try:
p_validated = platform_schema(p_validated)
except vol.Invalid as ex:
except probatio.Invalid as ex:
_comp_error(ex, f"{domain}.{p_name}", p_config, p_config)
continue
+11 -11
View File
@@ -11,8 +11,8 @@ import logging
from operator import attrgetter
from typing import Any, TypedDict, override
import voluptuous as vol
from voluptuous.humanize import humanize_error
import probatio
from probatio.humanize import humanize_error
from homeassistant.components import websocket_api
from homeassistant.const import CONF_ID
@@ -590,7 +590,7 @@ class StorageCollectionWebsocket[_StorageCollectionT: StorageCollection]:
f"{self.api_prefix}/list",
list_handler,
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
{vol.Required("type"): f"{self.api_prefix}/list"}
{probatio.Required("type"): f"{self.api_prefix}/list"}
),
)
@@ -603,7 +603,7 @@ class StorageCollectionWebsocket[_StorageCollectionT: StorageCollection]:
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
{
**self.create_schema,
vol.Required("type"): f"{self.api_prefix}/create",
probatio.Required("type"): f"{self.api_prefix}/create",
}
),
)
@@ -613,7 +613,7 @@ class StorageCollectionWebsocket[_StorageCollectionT: StorageCollection]:
f"{self.api_prefix}/subscribe",
subscribe_handler,
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
{vol.Required("type"): f"{self.api_prefix}/subscribe"}
{probatio.Required("type"): f"{self.api_prefix}/subscribe"}
),
)
@@ -626,8 +626,8 @@ class StorageCollectionWebsocket[_StorageCollectionT: StorageCollection]:
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
{
**self.update_schema,
vol.Required("type"): f"{self.api_prefix}/update",
vol.Required(self.item_id_key): str,
probatio.Required("type"): f"{self.api_prefix}/update",
probatio.Required(self.item_id_key): str,
}
),
)
@@ -640,8 +640,8 @@ class StorageCollectionWebsocket[_StorageCollectionT: StorageCollection]:
),
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
{
vol.Required("type"): f"{self.api_prefix}/delete",
vol.Required(self.item_id_key): str,
probatio.Required("type"): f"{self.api_prefix}/delete",
probatio.Required(self.item_id_key): str,
}
),
)
@@ -663,7 +663,7 @@ class StorageCollectionWebsocket[_StorageCollectionT: StorageCollection]:
data.pop("type")
item = await self.storage_collection.async_create_item(data)
connection.send_result(msg["id"], item)
except vol.Invalid as err:
except probatio.Invalid as err:
connection.send_error(
msg["id"],
websocket_api.ERR_INVALID_FORMAT,
@@ -740,7 +740,7 @@ class StorageCollectionWebsocket[_StorageCollectionT: StorageCollection]:
websocket_api.ERR_NOT_FOUND,
f"Unable to find {self.item_id_key} {item_id}",
)
except vol.Invalid as err:
except probatio.Invalid as err:
connection.send_error(
msg["id"],
websocket_api.ERR_INVALID_FORMAT,
+30 -30
View File
@@ -26,7 +26,7 @@ from typing import (
override,
)
import voluptuous as vol
import probatio
from homeassistant.const import (
CONF_ABOVE,
@@ -162,33 +162,33 @@ CONDITIONS: HassKey[dict[str, str]] = HassKey("conditions")
# Basic schemas to sanity check the condition descriptions,
# full validation is done by hassfest.conditions
_FIELD_DESCRIPTION_SCHEMA = vol.Schema(
_FIELD_DESCRIPTION_SCHEMA = probatio.Schema(
{
vol.Optional(CONF_SELECTOR): selector.validate_selector,
probatio.Optional(CONF_SELECTOR): selector.validate_selector,
},
extra=vol.ALLOW_EXTRA,
extra=probatio.ALLOW_EXTRA,
)
_CONDITION_DESCRIPTION_SCHEMA = vol.Schema(
_CONDITION_DESCRIPTION_SCHEMA = probatio.Schema(
{
vol.Optional("target"): TargetSelector.CONFIG_SCHEMA,
vol.Optional("fields"): vol.Schema({str: _FIELD_DESCRIPTION_SCHEMA}),
probatio.Optional("target"): TargetSelector.CONFIG_SCHEMA,
probatio.Optional("fields"): probatio.Schema({str: _FIELD_DESCRIPTION_SCHEMA}),
},
extra=vol.ALLOW_EXTRA,
extra=probatio.ALLOW_EXTRA,
)
def starts_with_dot(key: str) -> str:
"""Check if key starts with dot."""
if not key.startswith("."):
raise vol.Invalid("Key does not start with .")
raise probatio.Invalid("Key does not start with .")
return key
_CONDITIONS_DESCRIPTION_SCHEMA = vol.Schema(
_CONDITIONS_DESCRIPTION_SCHEMA = probatio.Schema(
{
vol.Remove(vol.All(str, starts_with_dot)): object,
cv.underscore_slug: vol.Any(None, _CONDITION_DESCRIPTION_SCHEMA),
probatio.Remove(probatio.All(str, starts_with_dot)): object,
cv.underscore_slug: probatio.Any(None, _CONDITION_DESCRIPTION_SCHEMA),
}
)
@@ -265,16 +265,16 @@ async def _register_condition_platform(
_LOGGER.exception("Error while notifying condition platform listener")
_CONDITION_BASE_SCHEMA = vol.Schema(
_CONDITION_BASE_SCHEMA = probatio.Schema(
{
**cv.CONDITION_BASE_SCHEMA,
vol.Required(CONF_CONDITION): str,
probatio.Required(CONF_CONDITION): str,
}
)
_CONDITION_SCHEMA = _CONDITION_BASE_SCHEMA.extend(
{
vol.Optional(CONF_OPTIONS): object,
vol.Optional(CONF_TARGET): cv.TARGET_FIELDS,
probatio.Optional(CONF_OPTIONS): object,
probatio.Optional(CONF_TARGET): cv.TARGET_FIELDS,
}
)
@@ -436,14 +436,14 @@ ATTR_BEHAVIOR: Final = "behavior"
BEHAVIOR_ANY: Final = "any"
BEHAVIOR_ALL: Final = "all"
ENTITY_STATE_CONDITION_SCHEMA_ANY_ALL = vol.Schema(
ENTITY_STATE_CONDITION_SCHEMA_ANY_ALL = probatio.Schema(
{
vol.Required(CONF_TARGET): cv.TARGET_FIELDS,
vol.Required(CONF_OPTIONS, default={}): {
vol.Required(ATTR_BEHAVIOR, default=BEHAVIOR_ANY): vol.In(
probatio.Required(CONF_TARGET): cv.TARGET_FIELDS,
probatio.Required(CONF_OPTIONS, default={}): {
probatio.Required(ATTR_BEHAVIOR, default=BEHAVIOR_ANY): probatio.In(
[BEHAVIOR_ANY, BEHAVIOR_ALL]
),
vol.Optional(CONF_FOR): cv.positive_time_period,
probatio.Optional(CONF_FOR): cv.positive_time_period,
},
}
)
@@ -537,7 +537,7 @@ class EntityConditionBase(Condition):
_excluded_states: Final[frozenset[str]] = frozenset(
{STATE_UNAVAILABLE, STATE_UNKNOWN}
)
_schema: vol.Schema = ENTITY_STATE_CONDITION_SCHEMA_ANY_ALL
_schema: probatio.Schema = ENTITY_STATE_CONDITION_SCHEMA_ANY_ALL
# When True, indirect target expansion (via device/area/floor) skips
# entities with an entity_category.
_primary_entities_only: ClassVar[bool] = True
@@ -942,8 +942,8 @@ def make_entity_state_condition(
NUMERICAL_CONDITION_SCHEMA = ENTITY_STATE_CONDITION_SCHEMA_ANY_ALL.extend(
{
vol.Required(CONF_OPTIONS): {
vol.Required("threshold"): NumericThresholdSelector(
probatio.Required(CONF_OPTIONS): {
probatio.Required("threshold"): NumericThresholdSelector(
NumericThresholdSelectorConfig(mode=NumericThresholdMode.IS)
),
},
@@ -1065,12 +1065,12 @@ def make_entity_numerical_condition(
def _make_numerical_condition_with_unit_schema(
unit_converter: type[BaseUnitConverter],
) -> vol.Schema:
) -> probatio.Schema:
"""Factory for numerical condition schema with unit option."""
return ENTITY_STATE_CONDITION_SCHEMA_ANY_ALL.extend(
{
vol.Required(CONF_OPTIONS): {
vol.Required("threshold"): NumericThresholdSelector(
probatio.Required(CONF_OPTIONS): {
probatio.Required("threshold"): NumericThresholdSelector(
NumericThresholdSelectorConfig(
mode=NumericThresholdMode.IS,
unit_of_measurement=list(unit_converter.VALID_UNITS),
@@ -1746,7 +1746,7 @@ def state(
for_period = cv.positive_time_period(render_complex(for_period, variables))
except TemplateError as ex:
raise ConditionErrorMessage("state", f"template error: {ex}") from ex
except vol.Invalid as ex:
except probatio.Invalid as ex:
raise ConditionErrorMessage("state", f"schema error: {ex}") from ex
duration = dt_util.utcnow() - cast(timedelta, for_period)
@@ -2014,7 +2014,7 @@ async def async_validate_condition_config(
platform_domain, condition_key
)
if not (condition_class := condition_descriptors.get(relative_condition_key)):
raise vol.Invalid(f"Invalid condition '{condition_key}' specified")
raise probatio.Invalid(f"Invalid condition '{condition_key}' specified")
return await condition_class.async_validate_complete_config(hass, config)
config = move_options_fields_to_top_level(config, _CONDITION_BASE_SCHEMA)
@@ -2257,7 +2257,7 @@ def _load_conditions_file(integration: Integration) -> dict[str, Any]:
"Unable to find conditions.yaml for the %s integration", integration.domain
)
return {}
except (HomeAssistantError, vol.Invalid) as ex:
except (HomeAssistantError, probatio.Invalid) as ex:
_LOGGER.warning(
"Unable to parse conditions.yaml for the %s integration: %s",
integration.domain,
@@ -23,7 +23,7 @@ from aiohttp import ClientError, ClientResponseError, client, hdrs, web
from habluetooth import BluetoothServiceInfoBleak
import jwt
from multidict import CIMultiDict
import voluptuous as vol
import probatio
from yarl import URL
from homeassistant import config_entries
@@ -548,11 +548,13 @@ class AbstractOAuth2FlowHandler(config_entries.ConfigFlow, metaclass=ABCMeta):
return self.async_show_form(
step_id="pick_implementation",
data_schema=vol.Schema(
data_schema=probatio.Schema(
{
vol.Required(
probatio.Required(
"implementation", default=list(implementations)[0]
): vol.In({key: impl.name for key, impl in implementations.items()})
): probatio.In(
{key: impl.name for key, impl in implementations.items()}
)
}
),
)
File diff suppressed because it is too large Load Diff
+6 -7
View File
@@ -4,8 +4,7 @@ from http import HTTPStatus
from typing import Any, Generic, TypeVar
from aiohttp import web
from probatio import to_field_list
import voluptuous as vol
import probatio
from homeassistant import data_entry_flow
from homeassistant.components.http import HomeAssistantView
@@ -51,7 +50,7 @@ class _BaseFlowManagerView(HomeAssistantView, Generic[_FlowManagerT, _FlowResult
if (schema := result["data_schema"]) is None:
data["data_schema"] = []
else:
data["data_schema"] = to_field_list(
data["data_schema"] = probatio.to_field_list(
schema, custom_serializer=cv.custom_serializer
)
return data
@@ -61,11 +60,11 @@ class FlowManagerIndexView(_BaseFlowManagerView[_FlowManagerT, _FlowResultT]):
"""View to create config flows."""
@RequestDataValidator(
vol.Schema(
probatio.Schema(
{
vol.Required("handler"): str,
probatio.Required("handler"): str,
},
extra=vol.ALLOW_EXTRA,
extra=probatio.ALLOW_EXTRA,
)
)
async def post(self, request: web.Request, data: dict[str, Any]) -> web.Response:
@@ -113,7 +112,7 @@ class FlowManagerResourceView(_BaseFlowManagerView[_FlowManagerT, _FlowResultT])
return self.json(result)
@RequestDataValidator(vol.Schema(dict), allow_empty=True)
@RequestDataValidator(probatio.Schema(dict), allow_empty=True)
async def post(
self, request: web.Request, data: dict[str, Any], flow_id: str
) -> web.Response:
+2 -2
View File
@@ -26,8 +26,8 @@ from typing import (
override,
)
import probatio
from propcache.api import cached_property
import voluptuous as vol
from homeassistant.const import (
DEVICE_DEFAULT_NAME,
@@ -209,7 +209,7 @@ def get_unit_of_measurement(hass: HomeAssistant, entity_id: str) -> str | None:
return entry.unit_of_measurement
ENTITY_CATEGORIES_SCHEMA: Final = vol.Coerce(EntityCategory)
ENTITY_CATEGORIES_SCHEMA: Final = probatio.Coerce(EntityCategory)
class EntityInfo(TypedDict):
+4 -4
View File
@@ -19,7 +19,7 @@ import time
from typing import TYPE_CHECKING, Any, Literal, NotRequired, TypedDict, override
import attr
import voluptuous as vol
import probatio
from homeassistant.const import (
EVENT_HOMEASSISTANT_START,
@@ -2768,13 +2768,13 @@ async def async_migrate_entries(
def async_validate_entity_id(registry: EntityRegistry, entity_id_or_uuid: str) -> str:
"""Validate and resolve an entity id or UUID to an entity id.
Raises vol.Invalid if the entity or UUID is invalid, or if the UUID is not
Raises probatio.Invalid if the entity or UUID is invalid, or if the UUID is not
associated with an entity registry item.
"""
if valid_entity_id(entity_id_or_uuid):
return entity_id_or_uuid
if (entry := registry.entities.get_entry(entity_id_or_uuid)) is None:
raise vol.Invalid(f"Unknown entity registry entry {entity_id_or_uuid}")
raise probatio.Invalid(f"Unknown entity registry entry {entity_id_or_uuid}")
return entry.entity_id
@@ -2801,7 +2801,7 @@ def async_validate_entity_ids(
"""Validate and resolve a list of entity ids or UUIDs to a list of entity ids.
Returns a list with UUID resolved to entity_ids.
Raises vol.Invalid if any item is invalid, or if any a UUID is not associated with
Raises probatio.Invalid if any item is invalid, or if any a UUID is not associated with
an entity registry item.
"""
+19 -17
View File
@@ -6,7 +6,7 @@ from functools import lru_cache, partial
import operator
import re
import voluptuous as vol
import probatio
from homeassistant.const import (
CONF_DOMAINS,
@@ -77,26 +77,26 @@ def convert_filter(config: dict[str, list[str]]) -> EntityFilter:
return EntityFilter(config)
BASE_FILTER_SCHEMA = vol.Schema(
BASE_FILTER_SCHEMA = probatio.Schema(
{
vol.Optional(CONF_EXCLUDE_DOMAINS, default=[]): vol.All(
probatio.Optional(CONF_EXCLUDE_DOMAINS, default=[]): probatio.All(
cv.ensure_list, [cv.string]
),
vol.Optional(CONF_EXCLUDE_ENTITY_GLOBS, default=[]): vol.All(
probatio.Optional(CONF_EXCLUDE_ENTITY_GLOBS, default=[]): probatio.All(
cv.ensure_list, [cv.string]
),
vol.Optional(CONF_EXCLUDE_ENTITIES, default=[]): cv.entity_ids,
vol.Optional(CONF_INCLUDE_DOMAINS, default=[]): vol.All(
probatio.Optional(CONF_EXCLUDE_ENTITIES, default=[]): cv.entity_ids,
probatio.Optional(CONF_INCLUDE_DOMAINS, default=[]): probatio.All(
cv.ensure_list, [cv.string]
),
vol.Optional(CONF_INCLUDE_ENTITY_GLOBS, default=[]): vol.All(
probatio.Optional(CONF_INCLUDE_ENTITY_GLOBS, default=[]): probatio.All(
cv.ensure_list, [cv.string]
),
vol.Optional(CONF_INCLUDE_ENTITIES, default=[]): cv.entity_ids,
probatio.Optional(CONF_INCLUDE_ENTITIES, default=[]): cv.entity_ids,
}
)
FILTER_SCHEMA = vol.All(BASE_FILTER_SCHEMA, convert_filter)
FILTER_SCHEMA = probatio.All(BASE_FILTER_SCHEMA, convert_filter)
def convert_include_exclude_filter(
@@ -117,28 +117,30 @@ def convert_include_exclude_filter(
)
INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER = vol.Schema(
INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER = probatio.Schema(
{
vol.Optional(CONF_DOMAINS, default=[]): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_ENTITY_GLOBS, default=[]): vol.All(
probatio.Optional(CONF_DOMAINS, default=[]): probatio.All(
cv.ensure_list, [cv.string]
),
vol.Optional(CONF_ENTITIES, default=[]): cv.entity_ids,
probatio.Optional(CONF_ENTITY_GLOBS, default=[]): probatio.All(
cv.ensure_list, [cv.string]
),
probatio.Optional(CONF_ENTITIES, default=[]): cv.entity_ids,
}
)
INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA = vol.Schema(
INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA = probatio.Schema(
{
vol.Optional(
probatio.Optional(
CONF_INCLUDE, default=INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER({})
): INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER,
vol.Optional(
probatio.Optional(
CONF_EXCLUDE, default=INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER({})
): INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER,
}
)
INCLUDE_EXCLUDE_FILTER_SCHEMA = vol.All(
INCLUDE_EXCLUDE_FILTER_SCHEMA = probatio.All(
INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA, convert_include_exclude_filter
)
+2 -2
View File
@@ -16,7 +16,7 @@ from aiohttp.web_exceptions import (
HTTPUnauthorized,
)
from aiohttp.web_urldispatcher import AbstractResource, AbstractRoute
import voluptuous as vol
import probatio
from homeassistant import exceptions
from homeassistant.const import CONTENT_TYPE_JSON
@@ -92,7 +92,7 @@ def request_handler_factory(
result = await handler(request, **request.match_info)
else:
result = handler(request, **request.match_info)
except vol.Invalid as err:
except probatio.Invalid as err:
raise HTTPBadRequest from err
except exceptions.ServiceNotFound as err:
raise HTTPInternalServerError from err
+22 -18
View File
@@ -10,8 +10,8 @@ from itertools import groupby
import logging
from typing import Any, override
import probatio
from propcache.api import cached_property
import voluptuous as vol
from homeassistant.components.homeassistant.exposed_entities import async_should_expose
from homeassistant.const import ATTR_ENTITY_ID, EntityStateAttribute
@@ -56,7 +56,7 @@ INTENT_RESPOND = "HassRespond"
INTENT_BROADCAST = "HassBroadcast"
INTENT_GET_TEMPERATURE = "HassClimateGetTemperature"
SLOT_SCHEMA = vol.Schema({}, extra=vol.ALLOW_EXTRA)
SLOT_SCHEMA = probatio.Schema({}, extra=probatio.ALLOW_EXTRA)
DATA_KEY: HassKey[dict[str, IntentHandler]] = HassKey("intent")
@@ -138,7 +138,7 @@ async def async_handle(
try:
_LOGGER.info("Triggering intent handler %s", handler)
result = await handler.async_handle(intent)
except vol.Invalid as err:
except probatio.Invalid as err:
_LOGGER.warning("Received invalid slot info for %s: %s", intent_type, err)
raise InvalidSlotInfo(f"Received invalid slot info for {intent_type}") from err
except IntentError:
@@ -843,15 +843,15 @@ class IntentHandler:
return self._slot_schema(slots) # type: ignore[no-any-return]
@cached_property
def _slot_schema(self) -> vol.Schema:
def _slot_schema(self) -> probatio.Schema:
"""Create validation schema for slots."""
assert self.slot_schema is not None
return vol.Schema(
return probatio.Schema(
{
key: SLOT_SCHEMA.extend({"value": validator})
for key, validator in self.slot_schema.items()
},
extra=vol.ALLOW_EXTRA,
extra=probatio.ALLOW_EXTRA,
)
async def async_handle(self, intent_obj: Intent) -> IntentResponse:
@@ -868,7 +868,7 @@ def non_empty_string(value: Any) -> str:
"""Coerce value to string and fail if string is empty or whitespace."""
value_str = cv.string(value)
if not value_str.strip():
raise vol.Invalid("string value is empty")
raise probatio.Invalid("string value is empty")
return value_str
@@ -883,7 +883,7 @@ class IntentSlotInfo:
description: str | None = None
"""Human readable description of the slot."""
value_schema: VolSchemaType | Callable[[Any], Any] = vol.Any
value_schema: VolSchemaType | Callable[[Any], Any] = probatio.Any
"""Validator for the slot."""
@@ -947,16 +947,20 @@ class DynamicServiceIntentHandler(IntentHandler):
def slot_schema(self) -> dict:
"""Return a slot schema."""
domain_validator = (
vol.In(list(self.required_domains)) if self.required_domains else cv.string
probatio.In(list(self.required_domains))
if self.required_domains
else cv.string
)
slot_schema = {
vol.Any("name", "area", "floor"): non_empty_string,
vol.Optional("domain"): vol.All(cv.ensure_list, [domain_validator]),
probatio.Any("name", "area", "floor"): non_empty_string,
probatio.Optional("domain"): probatio.All(
cv.ensure_list, [domain_validator]
),
}
if self.device_classes:
# The typical way to match enums is with vol.Coerce, but we build a
# The typical way to match enums is with probatio.Coerce, but we build a
# flat list to make the API simpler to describe programmatically
flattened_device_classes = vol.In(
flattened_device_classes = probatio.In(
[
device_class.value
for device_class_enum in self.device_classes
@@ -965,7 +969,7 @@ class DynamicServiceIntentHandler(IntentHandler):
)
slot_schema.update(
{
vol.Optional("device_class"): vol.All(
probatio.Optional("device_class"): probatio.All(
cv.ensure_list,
[flattened_device_classes],
)
@@ -974,15 +978,15 @@ class DynamicServiceIntentHandler(IntentHandler):
slot_schema.update(
{
vol.Optional("preferred_area_id"): cv.string,
vol.Optional("preferred_floor_id"): cv.string,
probatio.Optional("preferred_area_id"): cv.string,
probatio.Optional("preferred_floor_id"): cv.string,
}
)
if self.required_slots:
slot_schema.update(
{
vol.Required(
probatio.Required(
key, description=slot_info.description
): slot_info.value_schema
for key, slot_info in self.required_slots.items()
@@ -992,7 +996,7 @@ class DynamicServiceIntentHandler(IntentHandler):
if self.optional_slots:
slot_schema.update(
{
vol.Optional(
probatio.Optional(
key, description=slot_info.description
): slot_info.value_schema
for key, slot_info in self.optional_slots.items()
+19 -20
View File
@@ -5,9 +5,8 @@ from collections.abc import Callable
from dataclasses import dataclass, field as dc_field
from typing import Any, override
from probatio import UNSUPPORTED, to_openapi
import probatio
import slugify as unicode_slug
import voluptuous as vol
from homeassistant.const import (
ATTR_DOMAIN,
@@ -34,7 +33,7 @@ from .deprecation import deprecated_function
from .singleton import singleton
ACTION_PARAMETERS_CACHE: HassKey[
dict[str, dict[str, tuple[str | None, vol.Schema]]]
dict[str, dict[str, tuple[str | None, probatio.Schema]]]
] = HassKey("llm_action_parameters_cache")
APIS_CACHE: HassKey[dict[str, API]] = HassKey("llm_apis")
@@ -160,7 +159,7 @@ class Tool:
name: str
description: str | None = None
parameters: vol.Schema = vol.Schema({})
parameters: probatio.Schema = probatio.Schema({})
@abstractmethod
async def async_call(
@@ -247,7 +246,7 @@ class IntentTool(Tool):
extra_slots.add(field)
del slot_schema[field]
self.parameters = vol.Schema(slot_schema)
self.parameters = probatio.Schema(slot_schema)
if extra_slots:
self.extra_slots = extra_slots
@@ -416,7 +415,7 @@ def selector_serializer(schema: Any) -> Any: # noqa: C901
return {"type": "boolean"}
if not isinstance(schema, selector.Selector):
return UNSUPPORTED
return probatio.UNSUPPORTED
if isinstance(schema, selector.BackupLocationSelector):
return {"type": "string", "pattern": "^(?:\\/backup|\\w+)$"}
@@ -434,10 +433,10 @@ def selector_serializer(schema: Any) -> Any: # noqa: C901
}
if isinstance(schema, selector.ConditionSelector):
return to_openapi(cv.CONDITIONS_SCHEMA)
return probatio.to_openapi(cv.CONDITIONS_SCHEMA)
if isinstance(schema, selector.ConstantSelector):
return to_openapi(vol.Schema(schema.config["value"]))
return probatio.to_openapi(probatio.Schema(schema.config["value"]))
result: dict[str, Any]
if isinstance(schema, selector.ColorTempSelector):
@@ -464,7 +463,7 @@ def selector_serializer(schema: Any) -> Any: # noqa: C901
return {"type": "string", "format": "date-time"}
if isinstance(schema, selector.DurationSelector):
return to_openapi(cv.time_period_dict)
return probatio.to_openapi(cv.time_period_dict)
if isinstance(schema, selector.EntitySelector):
if schema.config.get("multiple"):
@@ -478,10 +477,10 @@ def selector_serializer(schema: Any) -> Any: # noqa: C901
return {"type": "string", "format": "RFC 5646"}
if isinstance(schema, selector.LocationSelector):
return to_openapi(schema.DATA_SCHEMA)
return probatio.to_openapi(schema.DATA_SCHEMA)
if isinstance(schema, selector.MediaSelector):
item_schema = to_openapi(schema.DATA_SCHEMA)
item_schema = probatio.to_openapi(schema.DATA_SCHEMA)
# Media selector allows multiple when configured
if schema.config.get("multiple"):
return {
@@ -504,7 +503,7 @@ def selector_serializer(schema: Any) -> Any: # noqa: C901
properties = {}
required = []
for field, field_schema in fields.items():
properties[field] = to_openapi(
properties[field] = probatio.to_openapi(
selector.selector(field_schema["selector"]),
custom_serializer=selector_serializer,
)
@@ -536,7 +535,7 @@ def selector_serializer(schema: Any) -> Any: # noqa: C901
return {"type": "string", "enum": options}
if isinstance(schema, selector.TargetSelector):
return to_openapi(cv.TARGET_FIELDS)
return probatio.to_openapi(cv.TARGET_FIELDS)
if isinstance(schema, selector.TemplateSelector):
return {"type": "string", "format": "jinja2"}
@@ -555,10 +554,10 @@ def selector_serializer(schema: Any) -> Any: # noqa: C901
def _get_cached_action_parameters(
hass: HomeAssistant, domain: str, action: str
) -> tuple[str | None, vol.Schema]:
) -> tuple[str | None, probatio.Schema]:
"""Get action description and schema."""
description = None
parameters = vol.Schema({})
parameters = probatio.Schema({})
parameters_cache = hass.data.get(ACTION_PARAMETERS_CACHE)
@@ -591,24 +590,24 @@ def _get_cached_action_parameters(
hass, domain, action
):
description = action_desc.get("description")
schema: dict[vol.Marker, Any] = {}
schema: dict[probatio.Marker, Any] = {}
fields = action_desc.get("fields", {})
for field, config in fields.items():
field_description = config.get("description")
if not field_description:
field_description = config.get("name")
key: vol.Marker
key: probatio.Marker
if config.get("required"):
key = vol.Required(field, description=field_description)
key = probatio.Required(field, description=field_description)
else:
key = vol.Optional(field, description=field_description)
key = probatio.Optional(field, description=field_description)
if "selector" in config:
schema[key] = selector.selector(config["selector"])
else:
schema[key] = cv.string
parameters = vol.Schema(schema)
parameters = probatio.Schema(schema)
parameters_cache.setdefault(domain, {})[action] = (description, parameters)
@@ -7,7 +7,7 @@ from dataclasses import dataclass
import types
from typing import Any, cast, override
import voluptuous as vol
import probatio
from homeassistant.config_entries import (
ConfigEntry,
@@ -37,11 +37,13 @@ class SchemaFlowFormStep(SchemaFlowStep):
"""Define a config or options flow form step."""
schema: (
vol.Schema
| Callable[[SchemaCommonFlowHandler], Coroutine[Any, Any, vol.Schema | None]]
probatio.Schema
| Callable[
[SchemaCommonFlowHandler], Coroutine[Any, Any, probatio.Schema | None]
]
| None
) = None
"""Optional voluptuous schema, or function which returns a schema or None, for
"""Optional schema, or function which returns a schema or None, for
requesting and validating user input.
- If a function is specified, the function will be passed the current
@@ -168,10 +170,12 @@ class SchemaCommonFlowHandler:
return form_step.options
return await form_step.options(self)
async def _get_schema(self, form_step: SchemaFlowFormStep) -> vol.Schema | None:
async def _get_schema(
self, form_step: SchemaFlowFormStep
) -> probatio.Schema | None:
if form_step.schema is None:
return None
if isinstance(form_step.schema, vol.Schema):
if isinstance(form_step.schema, probatio.Schema):
return form_step.schema
return await form_step.schema(self)
@@ -203,13 +207,13 @@ class SchemaCommonFlowHandler:
self,
values: dict[str, Any],
user_input: dict[str, Any],
data_schema: vol.Schema | None,
data_schema: probatio.Schema | None,
) -> None:
values.update(user_input)
if data_schema and data_schema.schema:
for key in data_schema.schema:
if (
isinstance(key, vol.Optional)
isinstance(key, probatio.Optional)
and key not in user_input
and not (
# don't remove read_only keys
+17 -15
View File
@@ -12,8 +12,8 @@ import logging
from typing import Any, Literal, TypedDict, cast, overload, override
import async_interrupt
import probatio
from propcache.api import cached_property
import voluptuous as vol
from homeassistant import exceptions
from homeassistant.components import scene
@@ -285,21 +285,23 @@ class trace_action:
def make_script_schema(
schema: Mapping[Any, Any], default_script_mode: str, extra: int = vol.PREVENT_EXTRA
) -> vol.Schema:
schema: Mapping[Any, Any],
default_script_mode: str,
extra: int = probatio.PREVENT_EXTRA,
) -> probatio.Schema:
"""Make a schema for a component that uses the script helper."""
return vol.Schema(
return probatio.Schema(
{
**schema,
vol.Optional(CONF_MODE, default=default_script_mode): vol.In(
probatio.Optional(CONF_MODE, default=default_script_mode): probatio.In(
SCRIPT_MODE_CHOICES
),
vol.Optional(CONF_MAX, default=DEFAULT_MAX): vol.All(
vol.Coerce(int), vol.Range(min=2)
),
vol.Optional(CONF_MAX_EXCEEDED, default=DEFAULT_MAX_EXCEEDED): vol.All(
vol.Upper, vol.In(_MAX_EXCEEDED_CHOICES)
probatio.Optional(CONF_MAX, default=DEFAULT_MAX): probatio.All(
probatio.Coerce(int), probatio.Range(min=2)
),
probatio.Optional(
CONF_MAX_EXCEEDED, default=DEFAULT_MAX_EXCEEDED
): probatio.All(probatio.Upper, probatio.In(_MAX_EXCEEDED_CHOICES)),
},
extra=extra,
)
@@ -620,7 +622,7 @@ class _ScriptRun:
if isinstance(
exception,
(
vol.Invalid,
probatio.Invalid,
exceptions.TemplateError,
exceptions.ServiceNotFound,
exceptions.InvalidEntityFormatError,
@@ -643,7 +645,7 @@ class _ScriptRun:
error = str(exception)
level = logging.ERROR
if isinstance(exception, vol.Invalid):
if isinstance(exception, probatio.Invalid):
error_desc = "Invalid data"
elif isinstance(exception, exceptions.TemplateError):
@@ -1063,12 +1065,12 @@ class _ScriptRun:
params[CONF_DOMAIN], params[CONF_SERVICE]
)
if supports_response == SupportsResponse.ONLY and not return_response:
raise vol.Invalid(
raise probatio.Invalid(
f"Script requires '{CONF_RESPONSE_VARIABLE}' for response data "
f"for service call {params[CONF_DOMAIN]}.{params[CONF_SERVICE]}"
)
if supports_response == SupportsResponse.NONE and return_response:
raise vol.Invalid(
raise probatio.Invalid(
f"Script does not support '{CONF_RESPONSE_VARIABLE}' for service "
f"'{params[CONF_DOMAIN]}.{params[CONF_SERVICE]}'"
" which does not support response data."
@@ -1186,7 +1188,7 @@ class _ScriptRun:
return cv.positive_time_period( # type: ignore[no-any-return]
template.render_complex(self._action[key], self._variables)
)
except (exceptions.TemplateError, vol.Invalid) as ex:
except (exceptions.TemplateError, probatio.Invalid) as ex:
self._log(
"Error rendering %s %s template: %s",
self._script.name,
File diff suppressed because it is too large Load Diff
+32 -32
View File
@@ -9,7 +9,7 @@ import logging
from types import ModuleType
from typing import TYPE_CHECKING, Any, Literal, TypedDict, cast, overload
import voluptuous as vol
import probatio
from homeassistant.auth.permissions.const import CAT_ENTITIES, POLICY_CONTROL
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
@@ -131,26 +131,26 @@ def _validate_option_or_feature(option_or_feature: str, label: str) -> Any:
try:
domain, enum, option = option_or_feature.split(".", 2)
except ValueError as exc:
raise vol.Invalid(
raise probatio.Invalid(
f"Invalid {label} '{option_or_feature}', expected <domain>.<enum>.<member>"
) from exc
base_components = _base_components()
if not (base_component := base_components.get(domain)):
raise vol.Invalid(f"Unknown base component '{domain}'")
raise probatio.Invalid(f"Unknown base component '{domain}'")
try:
attribute_enum = getattr(base_component, enum)
except AttributeError as exc:
raise vol.Invalid(f"Unknown {label} enum '{domain}.{enum}'") from exc
raise probatio.Invalid(f"Unknown {label} enum '{domain}.{enum}'") from exc
if not issubclass(attribute_enum, Enum):
raise vol.Invalid(f"Expected {label} '{domain}.{enum}' to be an enum")
raise probatio.Invalid(f"Expected {label} '{domain}.{enum}' to be an enum")
try:
return getattr(attribute_enum, option).value
except AttributeError as exc:
raise vol.Invalid(f"Unknown {label} '{enum}.{option}'") from exc
raise probatio.Invalid(f"Unknown {label} '{enum}.{option}'") from exc
def validate_attribute_option(attribute_option: str) -> Any:
@@ -165,50 +165,50 @@ def validate_supported_feature(supported_feature: str) -> Any:
# Basic schemas which translate attribute and supported feature enum names
# to their values. Full validation is done by hassfest.services
_FIELD_SCHEMA = vol.Schema(
_FIELD_SCHEMA = probatio.Schema(
{
vol.Optional(CONF_SELECTOR): selector.validate_selector,
vol.Optional("filter"): {
vol.Optional("attribute"): {
vol.Required(str): [vol.All(str, validate_attribute_option)],
probatio.Optional(CONF_SELECTOR): selector.validate_selector,
probatio.Optional("filter"): {
probatio.Optional("attribute"): {
probatio.Required(str): [probatio.All(str, validate_attribute_option)],
},
vol.Optional("supported_features"): [
vol.All(str, validate_supported_feature)
probatio.Optional("supported_features"): [
probatio.All(str, validate_supported_feature)
],
},
},
extra=vol.ALLOW_EXTRA,
extra=probatio.ALLOW_EXTRA,
)
_SECTION_SCHEMA = vol.Schema(
_SECTION_SCHEMA = probatio.Schema(
{
vol.Required("fields"): vol.Schema({str: _FIELD_SCHEMA}),
probatio.Required("fields"): probatio.Schema({str: _FIELD_SCHEMA}),
},
extra=vol.ALLOW_EXTRA,
extra=probatio.ALLOW_EXTRA,
)
_SERVICE_SCHEMA = vol.Schema(
_SERVICE_SCHEMA = probatio.Schema(
{
vol.Optional("target"): TargetSelector.CONFIG_SCHEMA,
vol.Optional("fields"): vol.Schema(
{str: vol.Any(_SECTION_SCHEMA, _FIELD_SCHEMA)}
probatio.Optional("target"): TargetSelector.CONFIG_SCHEMA,
probatio.Optional("fields"): probatio.Schema(
{str: probatio.Any(_SECTION_SCHEMA, _FIELD_SCHEMA)}
),
},
extra=vol.ALLOW_EXTRA,
extra=probatio.ALLOW_EXTRA,
)
def starts_with_dot(key: str) -> str:
"""Check if key starts with dot."""
if not key.startswith("."):
raise vol.Invalid("Key does not start with .")
raise probatio.Invalid("Key does not start with .")
return key
_SERVICES_SCHEMA = vol.Schema(
_SERVICES_SCHEMA = probatio.Schema(
{
vol.Remove(vol.All(str, starts_with_dot)): object,
cv.slug: vol.Any(None, _SERVICE_SCHEMA),
probatio.Remove(probatio.All(str, starts_with_dot)): object,
cv.slug: probatio.Any(None, _SERVICE_SCHEMA),
}
)
@@ -268,7 +268,7 @@ def async_prepare_call_from_config(
if validate_config:
try:
config = cv.SERVICE_SCHEMA(config)
except vol.Invalid as ex:
except probatio.Invalid as ex:
raise HomeAssistantError(
f"Invalid config for calling service: {ex}"
) from ex
@@ -286,7 +286,7 @@ def async_prepare_call_from_config(
raise HomeAssistantError(
f"Error rendering service name template: {ex}"
) from ex
except vol.Invalid as ex:
except probatio.Invalid as ex:
raise HomeAssistantError(
f"Template rendered invalid service: {domain_service}"
) from ex
@@ -314,7 +314,7 @@ def async_prepare_call_from_config(
raise HomeAssistantError(
f"Error rendering service target template: {ex}"
) from ex
except vol.Invalid as ex:
except probatio.Invalid as ex:
raise HomeAssistantError(
f"Template rendered invalid entity IDs: {target[CONF_ENTITY_ID]}"
) from ex
@@ -457,7 +457,7 @@ def _load_services_file(integration: Integration) -> JSON_TYPE:
"Unable to find services.yaml for the %s integration", integration.domain
)
return {}
except (HomeAssistantError, vol.Invalid) as ex:
except (HomeAssistantError, probatio.Invalid) as ex:
_LOGGER.warning(
"Unable to parse services.yaml for the %s integration: %s",
integration.domain,
@@ -615,7 +615,7 @@ def async_set_service_schema(
# Match validation applied to descriptions loaded from services.yaml.
try:
description["target"] = TargetSelector.CONFIG_SCHEMA(schema["target"])
except vol.Invalid as err:
except probatio.Invalid as err:
_LOGGER.warning(
"Invalid target in the description of service %s.%s, ignoring it: %s",
domain,
@@ -1005,7 +1005,7 @@ def async_register_admin_service(
| EntityServiceResponse
| None,
],
schema: VolSchemaType = vol.Schema({}, extra=vol.PREVENT_EXTRA),
schema: VolSchemaType = probatio.Schema({}, extra=probatio.PREVENT_EXTRA),
supports_response: SupportsResponse = SupportsResponse.NONE,
*,
description_placeholders: Mapping[str, str] | None = None,
@@ -3,7 +3,7 @@
from collections.abc import Iterable
from typing import TYPE_CHECKING
import voluptuous as vol
import probatio
from homeassistant.helpers import (
area_registry as ar,
@@ -92,7 +92,7 @@ class AreaExtension(BaseTemplateExtension):
try:
cv.entity_id(lookup_value)
except vol.Invalid:
except probatio.Invalid:
pass
else:
if entity := ent_reg.async_get(lookup_value):
@@ -4,7 +4,7 @@ from collections.abc import Iterable
from itertools import chain
from typing import TYPE_CHECKING, Any
import voluptuous as vol
import probatio
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import (
@@ -103,7 +103,7 @@ class DeviceExtension(BaseTemplateExtension):
try:
cv.entity_id(lookup_value)
except vol.Invalid:
except probatio.Invalid:
pass
else:
if entity := ent_reg.async_get(lookup_value):
@@ -3,7 +3,7 @@
from collections.abc import Iterable
from typing import TYPE_CHECKING, Any
import voluptuous as vol
import probatio
from homeassistant.helpers import (
area_registry as ar,
@@ -95,7 +95,7 @@ class LabelExtension(BaseTemplateExtension):
try:
cv.entity_id(lookup_value)
except vol.Invalid:
except probatio.Invalid:
pass
else:
if entity := ent_reg.async_get(lookup_value):
+3 -3
View File
@@ -2,7 +2,7 @@
from typing import TYPE_CHECKING, Any, NoReturn, overload
import voluptuous as vol
import probatio
from homeassistant.helpers import (
area_registry as ar,
@@ -51,7 +51,7 @@ def resolve_area_id(hass: HomeAssistant, lookup_value: Any) -> str | None:
# Check if it's an entity ID
try:
cv.entity_id(lookup_value)
except vol.Invalid:
except probatio.Invalid:
pass
else:
if entity := ent_reg.async_get(lookup_value):
@@ -83,7 +83,7 @@ def forgiving_boolean[_T](
"""Try to convert value to a boolean."""
try:
return cv.boolean(value)
except vol.Invalid:
except probatio.Invalid:
if default is _SENTINEL:
raise_no_default("bool", value)
return default
+38 -38
View File
@@ -22,7 +22,7 @@ from typing import (
override,
)
import voluptuous as vol
import probatio
from homeassistant.const import (
ATTR_ENTITY_ID,
@@ -122,33 +122,33 @@ TRIGGERS: HassKey[dict[str, str]] = HassKey("triggers")
# Basic schemas to sanity check the trigger descriptions,
# full validation is done by hassfest.triggers
_FIELD_DESCRIPTION_SCHEMA = vol.Schema(
_FIELD_DESCRIPTION_SCHEMA = probatio.Schema(
{
vol.Optional(CONF_SELECTOR): selector.validate_selector,
probatio.Optional(CONF_SELECTOR): selector.validate_selector,
},
extra=vol.ALLOW_EXTRA,
extra=probatio.ALLOW_EXTRA,
)
_TRIGGER_DESCRIPTION_SCHEMA = vol.Schema(
_TRIGGER_DESCRIPTION_SCHEMA = probatio.Schema(
{
vol.Optional("target"): TargetSelector.CONFIG_SCHEMA,
vol.Optional("fields"): vol.Schema({str: _FIELD_DESCRIPTION_SCHEMA}),
probatio.Optional("target"): TargetSelector.CONFIG_SCHEMA,
probatio.Optional("fields"): probatio.Schema({str: _FIELD_DESCRIPTION_SCHEMA}),
},
extra=vol.ALLOW_EXTRA,
extra=probatio.ALLOW_EXTRA,
)
def starts_with_dot(key: str) -> str:
"""Check if key starts with dot."""
if not key.startswith("."):
raise vol.Invalid("Key does not start with .")
raise probatio.Invalid("Key does not start with .")
return key
_TRIGGERS_DESCRIPTION_SCHEMA = vol.Schema(
_TRIGGERS_DESCRIPTION_SCHEMA = probatio.Schema(
{
vol.Remove(vol.All(str, starts_with_dot)): object,
cv.underscore_slug: vol.Any(None, _TRIGGER_DESCRIPTION_SCHEMA),
probatio.Remove(probatio.All(str, starts_with_dot)): object,
cv.underscore_slug: probatio.Any(None, _TRIGGER_DESCRIPTION_SCHEMA),
}
)
@@ -231,8 +231,8 @@ async def _register_trigger_platform(
_TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend(
{
vol.Optional(CONF_OPTIONS): object,
vol.Optional(CONF_TARGET): cv.TARGET_FIELDS,
probatio.Optional(CONF_OPTIONS): object,
probatio.Optional(CONF_TARGET): cv.TARGET_FIELDS,
}
)
@@ -355,21 +355,21 @@ def _backwards_compatible_behavior(value: Any) -> Any:
return value
ENTITY_STATE_TRIGGER_SCHEMA = vol.Schema(
ENTITY_STATE_TRIGGER_SCHEMA = probatio.Schema(
{
vol.Required(CONF_TARGET): cv.TARGET_FIELDS,
vol.Required(CONF_OPTIONS, default={}): {},
probatio.Required(CONF_TARGET): cv.TARGET_FIELDS,
probatio.Required(CONF_OPTIONS, default={}): {},
}
)
ENTITY_STATE_TRIGGER_SCHEMA_WITH_BEHAVIOR = ENTITY_STATE_TRIGGER_SCHEMA.extend(
{
vol.Required(CONF_OPTIONS, default={}): {
vol.Required(ATTR_BEHAVIOR, default=BEHAVIOR_EACH): vol.All(
probatio.Required(CONF_OPTIONS, default={}): {
probatio.Required(ATTR_BEHAVIOR, default=BEHAVIOR_EACH): probatio.All(
_backwards_compatible_behavior,
vol.In([BEHAVIOR_FIRST, BEHAVIOR_ALL, BEHAVIOR_EACH]),
probatio.In([BEHAVIOR_FIRST, BEHAVIOR_ALL, BEHAVIOR_EACH]),
),
vol.Optional(CONF_FOR): cv.positive_time_period,
probatio.Optional(CONF_FOR): cv.positive_time_period,
},
}
)
@@ -391,7 +391,7 @@ class EntityTriggerBase(Trigger):
# `_excluded_states`. Subclasses can override to relax the origin
# check.
_excluded_from_states: ClassVar[frozenset[str]] = _excluded_states
_schema: vol.Schema = ENTITY_STATE_TRIGGER_SCHEMA_WITH_BEHAVIOR
_schema: probatio.Schema = ENTITY_STATE_TRIGGER_SCHEMA_WITH_BEHAVIOR
# When True, indirect target expansion (via device/area/floor) skips
# entities with an entity_category.
_primary_entities_only: ClassVar[bool] = True
@@ -791,15 +791,15 @@ class StatelessEntityTriggerBase(EntityTriggerBase):
after startup must still fire the trigger.
"""
_schema: vol.Schema = ENTITY_STATE_TRIGGER_SCHEMA
_schema: probatio.Schema = ENTITY_STATE_TRIGGER_SCHEMA
_excluded_from_states: ClassVar[frozenset[str]] = frozenset({STATE_UNAVAILABLE})
NUMERICAL_ATTRIBUTE_CHANGED_TRIGGER_SCHEMA = ENTITY_STATE_TRIGGER_SCHEMA.extend(
{
vol.Required(CONF_OPTIONS, default={}): vol.All(
probatio.Required(CONF_OPTIONS, default={}): probatio.All(
{
vol.Required("threshold"): NumericThresholdSelector(
probatio.Required("threshold"): NumericThresholdSelector(
NumericThresholdSelectorConfig(mode=NumericThresholdMode.CHANGED)
)
},
@@ -1097,13 +1097,13 @@ class EntityNumericalStateChangedTriggerBase(EntityNumericalStateTriggerBase):
def make_numerical_state_changed_with_unit_schema(
unit_converter: type[BaseUnitConverter],
) -> vol.Schema:
) -> probatio.Schema:
"""Factory for numerical state trigger schema with unit option."""
return ENTITY_STATE_TRIGGER_SCHEMA.extend(
{
vol.Required(CONF_OPTIONS, default={}): vol.All(
probatio.Required(CONF_OPTIONS, default={}): probatio.All(
{
vol.Required("threshold"): NumericThresholdSelector(
probatio.Required("threshold"): NumericThresholdSelector(
NumericThresholdSelectorConfig(
mode=NumericThresholdMode.CHANGED,
unit_of_measurement=list(unit_converter.VALID_UNITS),
@@ -1131,8 +1131,8 @@ class EntityNumericalStateChangedTriggerWithUnitBase(
NUMERICAL_ATTRIBUTE_CROSSED_THRESHOLD_SCHEMA = (
ENTITY_STATE_TRIGGER_SCHEMA_WITH_BEHAVIOR.extend(
{
vol.Required(CONF_OPTIONS): {
vol.Required("threshold"): NumericThresholdSelector(
probatio.Required(CONF_OPTIONS): {
probatio.Required("threshold"): NumericThresholdSelector(
NumericThresholdSelectorConfig(mode=NumericThresholdMode.CROSSED)
),
},
@@ -1158,7 +1158,7 @@ class EntityNumericalStateCrossedThresholdTriggerBase(EntityNumericalStateTrigge
def _make_numerical_state_crossed_threshold_with_unit_schema(
unit_converter: type[BaseUnitConverter],
) -> vol.Schema:
) -> probatio.Schema:
"""Trigger for numerical state and state attribute changes.
This trigger only fires when the observed attribute
@@ -1166,8 +1166,8 @@ def _make_numerical_state_crossed_threshold_with_unit_schema(
"""
return ENTITY_STATE_TRIGGER_SCHEMA_WITH_BEHAVIOR.extend(
{
vol.Required(CONF_OPTIONS, default={}): {
vol.Required("threshold"): NumericThresholdSelector(
probatio.Required(CONF_OPTIONS, default={}): {
probatio.Required("threshold"): NumericThresholdSelector(
NumericThresholdSelectorConfig(
mode=NumericThresholdMode.CROSSED,
unit_of_measurement=list(unit_converter.VALID_UNITS),
@@ -1353,7 +1353,7 @@ class TriggerProtocol(Protocol):
async def async_get_triggers(self, hass: HomeAssistant) -> dict[str, type[Trigger]]:
"""Return the triggers provided by this integration."""
TRIGGER_SCHEMA: vol.Schema
TRIGGER_SCHEMA: probatio.Schema
async def async_validate_trigger_config(
self, hass: HomeAssistant, config: ConfigType
@@ -1634,11 +1634,11 @@ async def _async_get_trigger_platform(
try:
integration = await async_get_integration(hass, platform)
except IntegrationNotFound:
raise vol.Invalid(f"Invalid trigger '{trigger_key}' specified") from None
raise probatio.Invalid(f"Invalid trigger '{trigger_key}' specified") from None
try:
platform_module = await integration.async_get_platform("trigger")
except ImportError:
raise vol.Invalid(
raise probatio.Invalid(
f"Integration '{platform}' does not provide trigger support"
) from None
@@ -1662,7 +1662,7 @@ async def async_validate_trigger_config(
platform_domain, trigger_key
)
if not (trigger := trigger_descriptors.get(relative_trigger_key)):
raise vol.Invalid(f"Invalid trigger '{trigger_key}' specified")
raise probatio.Invalid(f"Invalid trigger '{trigger_key}' specified")
conf = await trigger.async_validate_complete_config(hass, conf)
elif hasattr(platform, "async_validate_trigger_config"):
conf = move_options_fields_to_top_level(conf, cv.TRIGGER_BASE_SCHEMA)
@@ -1928,7 +1928,7 @@ def _load_triggers_file(integration: Integration) -> dict[str, Any]:
"Unable to find triggers.yaml for the %s integration", integration.domain
)
return {}
except (HomeAssistantError, vol.Invalid) as ex:
except (HomeAssistantError, probatio.Invalid) as ex:
_LOGGER.warning(
"Unable to parse triggers.yaml for the %s integration: %s",
integration.domain,
@@ -5,7 +5,7 @@ import logging
from typing import Any, override
import jinja2
import voluptuous as vol
import probatio
from homeassistant.components.sensor import (
CONF_STATE_CLASS,
@@ -51,24 +51,24 @@ CONF_TO_ATTRIBUTE = {
CONF_PICTURE: EntityStateAttribute.ENTITY_PICTURE,
}
TEMPLATE_ENTITY_BASE_SCHEMA = vol.Schema(
TEMPLATE_ENTITY_BASE_SCHEMA = probatio.Schema(
{
vol.Optional(CONF_ICON): cv.template,
vol.Optional(CONF_NAME): cv.template,
vol.Optional(CONF_PICTURE): cv.template,
vol.Optional(CONF_UNIQUE_ID): cv.string,
probatio.Optional(CONF_ICON): cv.template,
probatio.Optional(CONF_NAME): cv.template,
probatio.Optional(CONF_PICTURE): cv.template,
probatio.Optional(CONF_UNIQUE_ID): cv.string,
}
)
def make_template_entity_base_schema(default_name: str) -> vol.Schema:
def make_template_entity_base_schema(default_name: str) -> probatio.Schema:
"""Return a schema with default name."""
return vol.Schema(
return probatio.Schema(
{
vol.Optional(CONF_ICON): cv.template,
vol.Optional(CONF_NAME, default=default_name): cv.template,
vol.Optional(CONF_PICTURE): cv.template,
vol.Optional(CONF_UNIQUE_ID): cv.string,
probatio.Optional(CONF_ICON): cv.template,
probatio.Optional(CONF_NAME, default=default_name): cv.template,
probatio.Optional(CONF_PICTURE): cv.template,
probatio.Optional(CONF_UNIQUE_ID): cv.string,
}
)
@@ -94,11 +94,11 @@ def log_triggered_template_error(
)
TEMPLATE_SENSOR_BASE_SCHEMA = vol.Schema(
TEMPLATE_SENSOR_BASE_SCHEMA = probatio.Schema(
{
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_STATE_CLASS): STATE_CLASSES_SCHEMA,
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
probatio.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
probatio.Optional(CONF_STATE_CLASS): STATE_CLASSES_SCHEMA,
probatio.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
}
).extend(TEMPLATE_ENTITY_BASE_SCHEMA.schema)
+3 -3
View File
@@ -4,7 +4,7 @@ from collections.abc import Mapping
from enum import Enum
from typing import Any, Never
import voluptuous as vol
import probatio
type GPSType = tuple[float, float]
type ConfigType = dict[str, Any]
@@ -13,8 +13,8 @@ type ServiceDataType = dict[str, Any]
type StateType = str | int | float | None
type TemplateVarsType = Mapping[str, Any] | None
type NoEventData = Mapping[str, Never]
type VolSchemaType = vol.Schema | vol.All | vol.Any
type VolDictType = dict[str | vol.Marker, Any]
type VolSchemaType = probatio.Schema | probatio.All | probatio.Any
type VolDictType = dict[str | probatio.Marker, Any]
# Custom type for recorder Queries
type QueryType = Any
+2 -2
View File
@@ -31,8 +31,8 @@ from awesomeversion import (
AwesomeVersionException,
AwesomeVersionStrategy,
)
import probatio
from propcache.api import cached_property
import voluptuous as vol
from . import generated
from .const import Platform
@@ -379,7 +379,7 @@ async def async_get_config_flows(
class ComponentProtocol(Protocol):
"""Define the format of an integration."""
CONFIG_SCHEMA: vol.Schema
CONFIG_SCHEMA: probatio.Schema
DOMAIN: str
async def async_setup_entry(
+4 -4
View File
@@ -4,7 +4,7 @@ from dataclasses import dataclass
from numbers import Number
from typing import TYPE_CHECKING, Final
import voluptuous as vol
import probatio
from homeassistant.const import (
ACCUMULATED_PRECIPITATION,
@@ -249,10 +249,10 @@ def _deprecated_unit_system(value: str) -> str:
return value
validate_unit_system = vol.All(
vol.Lower,
validate_unit_system = probatio.All(
probatio.Lower,
_deprecated_unit_system,
vol.Any(_CONF_UNIT_SYSTEM_METRIC, _CONF_UNIT_SYSTEM_US_CUSTOMARY),
probatio.Any(_CONF_UNIT_SYSTEM_METRIC, _CONF_UNIT_SYSTEM_US_CUSTOMARY),
)
METRIC_SYSTEM = UnitSystem(