2020-11-13 21:53:55 +00:00
|
|
|
"""Selectors for Home Assistant."""
|
|
|
|
from typing import Any, Callable, Dict, cast
|
|
|
|
|
|
|
|
import voluptuous as vol
|
|
|
|
|
2020-11-25 19:03:56 +00:00
|
|
|
from homeassistant.const import CONF_MODE, CONF_UNIT_OF_MEASUREMENT
|
2020-11-13 21:53:55 +00:00
|
|
|
from homeassistant.util import decorator
|
|
|
|
|
|
|
|
SELECTORS = decorator.Registry()
|
|
|
|
|
|
|
|
|
|
|
|
def validate_selector(config: Any) -> Dict:
|
|
|
|
"""Validate a selector."""
|
|
|
|
if not isinstance(config, dict):
|
|
|
|
raise vol.Invalid("Expected a dictionary")
|
|
|
|
|
|
|
|
if len(config) != 1:
|
|
|
|
raise vol.Invalid(f"Only one type can be specified. Found {', '.join(config)}")
|
|
|
|
|
|
|
|
selector_type = list(config)[0]
|
|
|
|
|
2020-11-19 15:48:43 +00:00
|
|
|
selector_class = SELECTORS.get(selector_type)
|
2020-11-13 21:53:55 +00:00
|
|
|
|
2020-11-19 15:48:43 +00:00
|
|
|
if selector_class is None:
|
2020-11-13 21:53:55 +00:00
|
|
|
raise vol.Invalid(f"Unknown selector type {selector_type} found")
|
|
|
|
|
2020-11-26 14:44:25 +00:00
|
|
|
# Selectors can be empty
|
2020-11-25 14:10:04 +00:00
|
|
|
if config[selector_type] is None:
|
|
|
|
return {selector_type: {}}
|
|
|
|
|
2020-11-19 15:48:43 +00:00
|
|
|
return {
|
|
|
|
selector_type: cast(Dict, selector_class.CONFIG_SCHEMA(config[selector_type]))
|
|
|
|
}
|
2020-11-13 21:53:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Selector:
|
|
|
|
"""Base class for selectors."""
|
|
|
|
|
|
|
|
CONFIG_SCHEMA: Callable
|
|
|
|
|
|
|
|
|
|
|
|
@SELECTORS.register("entity")
|
|
|
|
class EntitySelector(Selector):
|
|
|
|
"""Selector of a single entity."""
|
|
|
|
|
|
|
|
CONFIG_SCHEMA = vol.Schema(
|
|
|
|
{
|
2020-11-20 14:24:42 +00:00
|
|
|
# Integration that provided the entity
|
2020-11-13 21:53:55 +00:00
|
|
|
vol.Optional("integration"): str,
|
2020-11-20 14:24:42 +00:00
|
|
|
# Domain the entity belongs to
|
2020-11-13 21:53:55 +00:00
|
|
|
vol.Optional("domain"): str,
|
2020-11-25 14:10:04 +00:00
|
|
|
# Device class of the entity
|
|
|
|
vol.Optional("device_class"): str,
|
2020-11-13 21:53:55 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@SELECTORS.register("device")
|
|
|
|
class DeviceSelector(Selector):
|
|
|
|
"""Selector of a single device."""
|
|
|
|
|
|
|
|
CONFIG_SCHEMA = vol.Schema(
|
|
|
|
{
|
2020-11-20 14:24:42 +00:00
|
|
|
# Integration linked to it with a config entry
|
2020-11-13 21:53:55 +00:00
|
|
|
vol.Optional("integration"): str,
|
2020-11-20 14:24:42 +00:00
|
|
|
# Manufacturer of device
|
2020-11-13 21:53:55 +00:00
|
|
|
vol.Optional("manufacturer"): str,
|
2020-11-20 14:24:42 +00:00
|
|
|
# Model of device
|
2020-11-13 21:53:55 +00:00
|
|
|
vol.Optional("model"): str,
|
2020-11-26 14:44:25 +00:00
|
|
|
# Device has to contain entities matching this selector
|
|
|
|
vol.Optional(
|
|
|
|
"entity"
|
|
|
|
): EntitySelector.CONFIG_SCHEMA, # pylint: disable=no-member
|
2020-11-13 21:53:55 +00:00
|
|
|
}
|
|
|
|
)
|
2020-11-25 19:03:56 +00:00
|
|
|
|
|
|
|
|
2020-11-26 14:44:25 +00:00
|
|
|
@SELECTORS.register("area")
|
|
|
|
class AreaSelector(Selector):
|
|
|
|
"""Selector of a single area."""
|
|
|
|
|
|
|
|
CONFIG_SCHEMA = vol.Schema({})
|
|
|
|
|
|
|
|
|
2020-11-25 19:03:56 +00:00
|
|
|
@SELECTORS.register("number")
|
|
|
|
class NumberSelector(Selector):
|
|
|
|
"""Selector of a numeric value."""
|
|
|
|
|
|
|
|
CONFIG_SCHEMA = vol.Schema(
|
|
|
|
{
|
|
|
|
vol.Required("min"): vol.Coerce(float),
|
|
|
|
vol.Required("max"): vol.Coerce(float),
|
|
|
|
vol.Optional("step", default=1): vol.All(
|
|
|
|
vol.Coerce(float), vol.Range(min=1e-3)
|
|
|
|
),
|
|
|
|
vol.Optional(CONF_UNIT_OF_MEASUREMENT): str,
|
|
|
|
vol.Optional(CONF_MODE, default="slider"): vol.In(["box", "slider"]),
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@SELECTORS.register("boolean")
|
|
|
|
class BooleanSelector(Selector):
|
|
|
|
"""Selector of a boolean value."""
|
|
|
|
|
|
|
|
CONFIG_SCHEMA = vol.Schema({})
|
|
|
|
|
|
|
|
|
2020-11-26 14:44:25 +00:00
|
|
|
@SELECTORS.register("time")
|
|
|
|
class TimeSelector(Selector):
|
|
|
|
"""Selector of a time value."""
|
2020-11-25 19:03:56 +00:00
|
|
|
|
2020-11-26 14:44:25 +00:00
|
|
|
CONFIG_SCHEMA = vol.Schema({})
|
2020-11-28 22:33:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
@SELECTORS.register("target")
|
|
|
|
class TargetSelector(Selector):
|
|
|
|
"""Selector of a target value (area ID, device ID, entity ID etc).
|
|
|
|
|
|
|
|
Value should follow cv.ENTITY_SERVICE_FIELDS format.
|
|
|
|
"""
|
|
|
|
|
2020-11-30 22:35:24 +00:00
|
|
|
CONFIG_SCHEMA = vol.Schema({"entity": {"domain": str, "device_class": str}})
|