Update selectors with frontend changes (#68623)

pull/68732/head
Erik Montnemery 2022-03-27 06:41:39 +02:00 committed by GitHub
parent a2a612c640
commit 0899b67578
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 183 additions and 10 deletions

View File

@ -78,13 +78,13 @@ SINGLE_ENTITY_SELECTOR_CONFIG_SCHEMA = vol.Schema(
# Integration that provided the entity # Integration that provided the entity
vol.Optional("integration"): str, vol.Optional("integration"): str,
# Domain the entity belongs to # Domain the entity belongs to
vol.Optional("domain"): str, vol.Optional("domain"): vol.Any(str, [str]),
# Device class of the entity # Device class of the entity
vol.Optional("device_class"): str, vol.Optional("device_class"): str,
} }
) )
DEVICE_SELECTOR_CONFIG_SCHEMA = vol.Schema( SINGLE_DEVICE_SELECTOR_CONFIG_SCHEMA = vol.Schema(
{ {
# Integration linked to it with a config entry # Integration linked to it with a config entry
vol.Optional("integration"): str, vol.Optional("integration"): str,
@ -94,7 +94,6 @@ DEVICE_SELECTOR_CONFIG_SCHEMA = vol.Schema(
vol.Optional("model"): str, vol.Optional("model"): str,
# Device has to contain entities matching this selector # Device has to contain entities matching this selector
vol.Optional("entity"): SINGLE_ENTITY_SELECTOR_CONFIG_SCHEMA, vol.Optional("entity"): SINGLE_ENTITY_SELECTOR_CONFIG_SCHEMA,
vol.Optional("multiple", default=False): cv.boolean,
} }
) )
@ -140,7 +139,7 @@ class AreaSelector(Selector):
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
{ {
vol.Optional("entity"): SINGLE_ENTITY_SELECTOR_CONFIG_SCHEMA, vol.Optional("entity"): SINGLE_ENTITY_SELECTOR_CONFIG_SCHEMA,
vol.Optional("device"): DEVICE_SELECTOR_CONFIG_SCHEMA, vol.Optional("device"): SINGLE_DEVICE_SELECTOR_CONFIG_SCHEMA,
vol.Optional("multiple", default=False): cv.boolean, vol.Optional("multiple", default=False): cv.boolean,
} }
) )
@ -183,13 +182,82 @@ class BooleanSelector(Selector):
return value return value
@SELECTORS.register("color_rgb")
class ColorRGBSelector(Selector):
"""Selector of an RGB color value."""
selector_type = "color_rgb"
CONFIG_SCHEMA = vol.Schema({})
def __call__(self, data: Any) -> list[int]:
"""Validate the passed selection."""
value: list[int] = vol.All(list, vol.ExactSequence((cv.byte,) * 3))(data)
return value
@SELECTORS.register("color_temp")
class ColorTempSelector(Selector):
"""Selector of an color temperature."""
selector_type = "color_temp"
CONFIG_SCHEMA = vol.Schema(
{
vol.Optional("max_mireds"): vol.Coerce(int),
vol.Optional("min_mireds"): vol.Coerce(int),
}
)
def __call__(self, data: Any) -> int:
"""Validate the passed selection."""
value: int = vol.All(
vol.Coerce(float),
vol.Range(
min=self.config.get("min_mireds"),
max=self.config.get("max_mireds"),
),
)(data)
return value
@SELECTORS.register("date")
class DateSelector(Selector):
"""Selector of a date."""
selector_type = "date"
CONFIG_SCHEMA = vol.Schema({})
def __call__(self, data: Any) -> Any:
"""Validate the passed selection."""
cv.date(data)
return data
@SELECTORS.register("datetime")
class DateTimeSelector(Selector):
"""Selector of a datetime."""
selector_type = "datetime"
CONFIG_SCHEMA = vol.Schema({})
def __call__(self, data: Any) -> Any:
"""Validate the passed selection."""
cv.datetime(data)
return data
@SELECTORS.register("device") @SELECTORS.register("device")
class DeviceSelector(Selector): class DeviceSelector(Selector):
"""Selector of a single or list of devices.""" """Selector of a single or list of devices."""
selector_type = "device" selector_type = "device"
CONFIG_SCHEMA = DEVICE_SELECTOR_CONFIG_SCHEMA CONFIG_SCHEMA = SINGLE_DEVICE_SELECTOR_CONFIG_SCHEMA.extend(
{vol.Optional("multiple", default=False): cv.boolean}
)
def __call__(self, data: Any) -> str | list[str]: def __call__(self, data: Any) -> str | list[str]:
"""Validate the passed selection.""" """Validate the passed selection."""
@ -226,23 +294,34 @@ class EntitySelector(Selector):
selector_type = "entity" selector_type = "entity"
CONFIG_SCHEMA = SINGLE_ENTITY_SELECTOR_CONFIG_SCHEMA.extend( CONFIG_SCHEMA = SINGLE_ENTITY_SELECTOR_CONFIG_SCHEMA.extend(
{vol.Optional("multiple", default=False): cv.boolean} {
vol.Optional("exclude_entities"): [str],
vol.Optional("include_entities"): [str],
vol.Optional("multiple", default=False): cv.boolean,
}
) )
def __call__(self, data: Any) -> str | list[str]: def __call__(self, data: Any) -> str | list[str]:
"""Validate the passed selection.""" """Validate the passed selection."""
include_entities = self.config.get("include_entities")
exclude_entities = self.config.get("exclude_entities")
def validate(e_or_u: str) -> str: def validate(e_or_u: str) -> str:
e_or_u = cv.entity_id_or_uuid(e_or_u) e_or_u = cv.entity_id_or_uuid(e_or_u)
if not valid_entity_id(e_or_u): if not valid_entity_id(e_or_u):
return e_or_u return e_or_u
if allowed_domain := self.config.get("domain"): if allowed_domains := cv.ensure_list(self.config.get("domain")):
domain = split_entity_id(e_or_u)[0] domain = split_entity_id(e_or_u)[0]
if domain != allowed_domain: if domain not in allowed_domains:
raise vol.Invalid( raise vol.Invalid(
f"Entity {e_or_u} belongs to domain {domain}, " f"Entity {e_or_u} belongs to domain {domain}, "
f"expected {allowed_domain}" f"expected {allowed_domains}"
) )
if include_entities:
vol.In(include_entities)(e_or_u)
if exclude_entities:
vol.NotIn(exclude_entities)(e_or_u)
return e_or_u return e_or_u
if not self.config["multiple"]: if not self.config["multiple"]:
@ -460,7 +539,7 @@ class TargetSelector(Selector):
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
{ {
vol.Optional("entity"): SINGLE_ENTITY_SELECTOR_CONFIG_SCHEMA, vol.Optional("entity"): SINGLE_ENTITY_SELECTOR_CONFIG_SCHEMA,
vol.Optional("device"): DEVICE_SELECTOR_CONFIG_SCHEMA, vol.Optional("device"): SINGLE_DEVICE_SELECTOR_CONFIG_SCHEMA,
} }
) )

View File

@ -110,6 +110,11 @@ def test_device_selector_schema(schema, valid_selections, invalid_selections):
({}, ("sensor.abc123", FAKE_UUID), (None, "abc123")), ({}, ("sensor.abc123", FAKE_UUID), (None, "abc123")),
({"integration": "zha"}, ("sensor.abc123", FAKE_UUID), (None, "abc123")), ({"integration": "zha"}, ("sensor.abc123", FAKE_UUID), (None, "abc123")),
({"domain": "light"}, ("light.abc123", FAKE_UUID), (None, "sensor.abc123")), ({"domain": "light"}, ("light.abc123", FAKE_UUID), (None, "sensor.abc123")),
(
{"domain": ["light", "sensor"]},
("light.abc123", "sensor.abc123", FAKE_UUID),
(None, "dog.abc123"),
),
({"device_class": "motion"}, ("sensor.abc123", FAKE_UUID), (None, "abc123")), ({"device_class": "motion"}, ("sensor.abc123", FAKE_UUID), (None, "abc123")),
( (
{"integration": "zha", "domain": "light"}, {"integration": "zha", "domain": "light"},
@ -132,6 +137,26 @@ def test_device_selector_schema(schema, valid_selections, invalid_selections):
["sensor.abc123", "light.def456"], ["sensor.abc123", "light.def456"],
), ),
), ),
(
{
"include_entities": ["sensor.abc123", "sensor.def456", "sensor.ghi789"],
"exclude_entities": ["sensor.ghi789", "sensor.jkl123"],
},
("sensor.abc123", FAKE_UUID),
("sensor.ghi789", "sensor.jkl123"),
),
(
{
"multiple": True,
"include_entities": ["sensor.abc123", "sensor.def456", "sensor.ghi789"],
"exclude_entities": ["sensor.ghi789", "sensor.jkl123"],
},
(["sensor.abc123", "sensor.def456"], ["sensor.abc123", FAKE_UUID]),
(
["sensor.abc123", "sensor.jkl123"],
["sensor.abc123", "sensor.ghi789"],
),
),
), ),
) )
def test_entity_selector_schema(schema, valid_selections, invalid_selections): def test_entity_selector_schema(schema, valid_selections, invalid_selections):
@ -490,3 +515,72 @@ def test_location_selector_schema(schema, valid_selections, invalid_selections):
"""Test location selector.""" """Test location selector."""
_test_selector("location", schema, valid_selections, invalid_selections) _test_selector("location", schema, valid_selections, invalid_selections)
@pytest.mark.parametrize(
"schema,valid_selections,invalid_selections",
(
(
{},
([0, 0, 0], [255, 255, 255], [0.0, 0.0, 0.0], [255.0, 255.0, 255.0]),
(None, "abc", [0, 0, "nil"], (255, 255, 255)),
),
),
)
def test_rgb_color_selector_schema(schema, valid_selections, invalid_selections):
"""Test color_rgb selector."""
_test_selector("color_rgb", schema, valid_selections, invalid_selections)
@pytest.mark.parametrize(
"schema,valid_selections,invalid_selections",
(
(
{},
(100, 100.0),
(None, "abc", [100]),
),
(
{"min_mireds": 100, "max_mireds": 200},
(100, 200),
(99, 201),
),
),
)
def test_color_tempselector_schema(schema, valid_selections, invalid_selections):
"""Test color_temp selector."""
_test_selector("color_temp", schema, valid_selections, invalid_selections)
@pytest.mark.parametrize(
"schema,valid_selections,invalid_selections",
(
(
{},
("2022-03-24",),
(None, "abc", "00:00", "2022-03-24 00:00", "2022-03-32"),
),
),
)
def test_date_selector_schema(schema, valid_selections, invalid_selections):
"""Test date selector."""
_test_selector("date", schema, valid_selections, invalid_selections)
@pytest.mark.parametrize(
"schema,valid_selections,invalid_selections",
(
(
{},
("2022-03-24 00:00", "2022-03-24"),
(None, "abc", "00:00", "2022-03-24 24:01"),
),
),
)
def test_datetime_selector_schema(schema, valid_selections, invalid_selections):
"""Test datetime selector."""
_test_selector("datetime", schema, valid_selections, invalid_selections)