diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index f1caf38bf8b..0fc9dbbaae1 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -588,6 +588,23 @@ def ensure_list_csv(value: Any) -> List: return ensure_list(value) +def multi_select(options: dict) -> Callable[[List], List]: + """Multi select validator returning list of selected values.""" + + def validator(selected: List) -> list: + """Return list of selected values.""" + if not isinstance(selected, list): + raise vol.Invalid("Not a list") + + for value in selected: + if value not in options: + raise vol.Invalid(f"{value} is not a valid option") + + return selected + + return validator + + def deprecated( key: str, replacement_key: Optional[str] = None, @@ -713,6 +730,9 @@ def custom_serializer(schema: Any) -> Any: if schema is positive_time_period_dict: return {"type": "positive_time_period_dict"} + if schema is multi_select: + return {"type": "multi_select"} + return voluptuous_serialize.UNSUPPORTED diff --git a/tests/helpers/test_config_validation.py b/tests/helpers/test_config_validation.py index 57554d37bb1..afb215822c4 100644 --- a/tests/helpers/test_config_validation.py +++ b/tests/helpers/test_config_validation.py @@ -472,6 +472,22 @@ def test_datetime(): schema("2016-11-23T18:59:08") +def test_multi_select(): + """Test multi select validation. + + Expected behavior: + - Will not accept any input but a list + - Will not accept selections outside of configured scope + """ + schema = vol.Schema(cv.multi_select({"paulus": "Paulus", "robban": "Robban"})) + + with pytest.raises(vol.Invalid): + schema("robban") + schema(["paulus", "martinhj"]) + + schema(["robban", "paulus"]) + + @pytest.fixture def schema(): """Create a schema used for testing deprecation."""