mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
Add attributes to all template platforms (#178762)
This commit is contained in:
@@ -11,6 +11,7 @@ from homeassistant.components.alarm_control_panel import (
|
||||
ENTITY_ID_FORMAT,
|
||||
AlarmControlPanelEntity,
|
||||
AlarmControlPanelEntityFeature,
|
||||
AlarmControlPanelEntityStateAttribute,
|
||||
AlarmControlPanelState,
|
||||
CodeFormat,
|
||||
)
|
||||
@@ -38,7 +39,7 @@ from .helpers import (
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA,
|
||||
make_template_entity_common_modern_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -96,8 +97,8 @@ ALARM_CONTROL_PANEL_COMMON_SCHEMA = vol.Schema(
|
||||
ALARM_CONTROL_PANEL_YAML_SCHEMA = ALARM_CONTROL_PANEL_COMMON_SCHEMA.extend(
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA
|
||||
).extend(
|
||||
make_template_entity_common_modern_schema(
|
||||
ALARM_CONTROL_PANEL_DOMAIN, DEFAULT_NAME
|
||||
make_template_entity_common_schema(
|
||||
ALARM_CONTROL_PANEL_DOMAIN, DEFAULT_NAME, AlarmControlPanelEntityStateAttribute
|
||||
).schema
|
||||
)
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ from .helpers import (
|
||||
)
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
make_template_entity_common_modern_attributes_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -67,9 +67,7 @@ BINARY_SENSOR_COMMON_SCHEMA = vol.Schema(
|
||||
)
|
||||
|
||||
BINARY_SENSOR_YAML_SCHEMA = BINARY_SENSOR_COMMON_SCHEMA.extend(
|
||||
make_template_entity_common_modern_attributes_schema(
|
||||
BINARY_SENSOR_DOMAIN, DEFAULT_NAME
|
||||
).schema
|
||||
make_template_entity_common_schema(BINARY_SENSOR_DOMAIN, DEFAULT_NAME).schema
|
||||
)
|
||||
|
||||
BINARY_SENSOR_CONFIG_ENTRY_SCHEMA = BINARY_SENSOR_COMMON_SCHEMA.extend(
|
||||
|
||||
@@ -25,7 +25,7 @@ from .const import CONF_PRESS, DOMAIN
|
||||
from .helpers import async_setup_template_entry, async_setup_template_platform
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
make_template_entity_common_modern_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
|
||||
@@ -41,7 +41,11 @@ BUTTON_YAML_SCHEMA = vol.Schema(
|
||||
vol.Required(CONF_PRESS): cv.SCRIPT_SCHEMA,
|
||||
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
|
||||
}
|
||||
).extend(make_template_entity_common_modern_schema(BUTTON_DOMAIN, DEFAULT_NAME).schema)
|
||||
).extend(
|
||||
make_template_entity_common_schema(
|
||||
BUTTON_DOMAIN, DEFAULT_NAME, block_device_class=True
|
||||
).schema
|
||||
)
|
||||
|
||||
BUTTON_CONFIG_ENTRY_SCHEMA = vol.Schema(
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ from homeassistant.components.cover import (
|
||||
ENTITY_ID_FORMAT,
|
||||
CoverEntity,
|
||||
CoverEntityFeature,
|
||||
CoverEntityStateAttribute,
|
||||
CoverState,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@@ -37,7 +38,7 @@ from .helpers import (
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA,
|
||||
make_template_entity_common_modern_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -98,7 +99,12 @@ COVER_YAML_SCHEMA = vol.All(
|
||||
.extend(COVER_COMMON_SCHEMA.schema)
|
||||
.extend(TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA)
|
||||
.extend(
|
||||
make_template_entity_common_modern_schema(COVER_DOMAIN, DEFAULT_NAME).schema
|
||||
make_template_entity_common_schema(
|
||||
COVER_DOMAIN,
|
||||
DEFAULT_NAME,
|
||||
CoverEntityStateAttribute,
|
||||
block_device_class=True,
|
||||
).schema
|
||||
),
|
||||
cv.has_at_least_one_key(OPEN_ACTION, POSITION_ACTION),
|
||||
)
|
||||
|
||||
@@ -10,7 +10,10 @@ from homeassistant.components import zone
|
||||
from homeassistant.components.device_tracker import (
|
||||
DOMAIN as DEVICE_TRACKER_DOMAIN,
|
||||
ENTITY_ID_FORMAT,
|
||||
DeviceTrackerEntityCapabilityAttribute,
|
||||
DeviceTrackerEntityStateAttribute,
|
||||
TrackerEntity,
|
||||
TrackerEntityStateAttribute,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
|
||||
@@ -32,7 +35,7 @@ from .helpers import (
|
||||
)
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
make_template_entity_common_modern_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -117,8 +120,14 @@ TRACKER_COMMON_SCHEMA = vol.Schema(
|
||||
TRACKER_YAML_SCHEMA = vol.All(
|
||||
_validate_in_zones_or_lat_and_lon,
|
||||
TRACKER_COMMON_SCHEMA.extend(
|
||||
make_template_entity_common_modern_schema(
|
||||
DEVICE_TRACKER_DOMAIN, DEFAULT_NAME
|
||||
make_template_entity_common_schema(
|
||||
DEVICE_TRACKER_DOMAIN,
|
||||
DEFAULT_NAME,
|
||||
(
|
||||
DeviceTrackerEntityCapabilityAttribute,
|
||||
DeviceTrackerEntityStateAttribute,
|
||||
TrackerEntityStateAttribute,
|
||||
),
|
||||
).schema
|
||||
),
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ from .helpers import (
|
||||
)
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
make_template_entity_common_modern_attributes_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -54,9 +54,7 @@ EVENT_COMMON_SCHEMA = vol.Schema(
|
||||
)
|
||||
|
||||
EVENT_YAML_SCHEMA = EVENT_COMMON_SCHEMA.extend(
|
||||
make_template_entity_common_modern_attributes_schema(
|
||||
EVENT_DOMAIN, DEFAULT_NAME
|
||||
).schema
|
||||
make_template_entity_common_schema(EVENT_DOMAIN, DEFAULT_NAME).schema
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@ from homeassistant.components.fan import (
|
||||
DOMAIN as FAN_DOMAIN,
|
||||
ENTITY_ID_FORMAT,
|
||||
FanEntity,
|
||||
FanEntityCapabilityAttribute,
|
||||
FanEntityFeature,
|
||||
FanEntityStateAttribute,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_NAME, CONF_STATE
|
||||
@@ -38,7 +40,7 @@ from .helpers import (
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA,
|
||||
make_template_entity_common_modern_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -91,7 +93,11 @@ FAN_COMMON_SCHEMA = vol.Schema(
|
||||
)
|
||||
|
||||
FAN_YAML_SCHEMA = FAN_COMMON_SCHEMA.extend(TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA).extend(
|
||||
make_template_entity_common_modern_schema(FAN_DOMAIN, DEFAULT_NAME).schema
|
||||
make_template_entity_common_schema(
|
||||
FAN_DOMAIN,
|
||||
DEFAULT_NAME,
|
||||
(FanEntityStateAttribute, FanEntityCapabilityAttribute),
|
||||
).schema
|
||||
)
|
||||
|
||||
FAN_CONFIG_ENTRY_SCHEMA = FAN_COMMON_SCHEMA.extend(
|
||||
|
||||
@@ -9,6 +9,7 @@ from homeassistant.components.image import (
|
||||
DOMAIN as IMAGE_DOMAIN,
|
||||
ENTITY_ID_FORMAT,
|
||||
ImageEntity,
|
||||
ImageEntityStateAttribute,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_URL, CONF_VERIFY_SSL
|
||||
@@ -27,7 +28,7 @@ from .entity import AbstractTemplateEntity
|
||||
from .helpers import async_setup_template_entry, async_setup_template_platform
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
make_template_entity_common_modern_attributes_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -44,8 +45,8 @@ IMAGE_YAML_SCHEMA = vol.Schema(
|
||||
vol.Optional(CONF_VERIFY_SSL, default=True): bool,
|
||||
}
|
||||
).extend(
|
||||
make_template_entity_common_modern_attributes_schema(
|
||||
IMAGE_DOMAIN, DEFAULT_NAME
|
||||
make_template_entity_common_schema(
|
||||
IMAGE_DOMAIN, DEFAULT_NAME, ImageEntityStateAttribute
|
||||
).schema
|
||||
)
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@ from homeassistant.components.light import (
|
||||
ENTITY_ID_FORMAT,
|
||||
ColorMode,
|
||||
LightEntity,
|
||||
LightEntityCapabilityAttribute,
|
||||
LightEntityFeature,
|
||||
LightEntityStateAttribute,
|
||||
filter_supported_color_modes,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@@ -57,7 +59,7 @@ from .helpers import (
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA,
|
||||
make_template_entity_common_modern_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -134,7 +136,13 @@ LIGHT_COMMON_SCHEMA = vol.Schema(
|
||||
|
||||
LIGHT_YAML_SCHEMA = LIGHT_COMMON_SCHEMA.extend(
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA
|
||||
).extend(make_template_entity_common_modern_schema(LIGHT_DOMAIN, DEFAULT_NAME).schema)
|
||||
).extend(
|
||||
make_template_entity_common_schema(
|
||||
LIGHT_DOMAIN,
|
||||
DEFAULT_NAME,
|
||||
(LightEntityCapabilityAttribute, LightEntityStateAttribute),
|
||||
).schema
|
||||
)
|
||||
|
||||
LIGHT_CONFIG_ENTRY_SCHEMA = LIGHT_COMMON_SCHEMA.extend(
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema
|
||||
|
||||
@@ -10,6 +10,7 @@ from homeassistant.components.lock import (
|
||||
ENTITY_ID_FORMAT,
|
||||
LockEntity,
|
||||
LockEntityFeature,
|
||||
LockEntityStateAttribute,
|
||||
LockState,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@@ -36,7 +37,7 @@ from .helpers import (
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA,
|
||||
make_template_entity_common_modern_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -67,7 +68,9 @@ LOCK_COMMON_SCHEMA = vol.Schema(
|
||||
)
|
||||
|
||||
LOCK_YAML_SCHEMA = LOCK_COMMON_SCHEMA.extend(TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA).extend(
|
||||
make_template_entity_common_modern_schema(LOCK_DOMAIN, DEFAULT_NAME).schema
|
||||
make_template_entity_common_schema(
|
||||
LOCK_DOMAIN, DEFAULT_NAME, LockEntityStateAttribute
|
||||
).schema
|
||||
)
|
||||
|
||||
LOCK_CONFIG_ENTRY_SCHEMA = LOCK_COMMON_SCHEMA.extend(
|
||||
|
||||
@@ -11,6 +11,7 @@ from homeassistant.components.number import (
|
||||
DEVICE_CLASSES_SCHEMA,
|
||||
DOMAIN as NUMBER_DOMAIN,
|
||||
ENTITY_ID_FORMAT,
|
||||
NumberEntityCapabilityAttribute,
|
||||
NumberExtraStoredData,
|
||||
RestoreNumber,
|
||||
)
|
||||
@@ -40,7 +41,7 @@ from .helpers import (
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA,
|
||||
make_template_entity_common_modern_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -66,7 +67,14 @@ NUMBER_COMMON_SCHEMA = vol.Schema(
|
||||
|
||||
NUMBER_YAML_SCHEMA = NUMBER_COMMON_SCHEMA.extend(
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA
|
||||
).extend(make_template_entity_common_modern_schema(NUMBER_DOMAIN, DEFAULT_NAME).schema)
|
||||
).extend(
|
||||
make_template_entity_common_schema(
|
||||
NUMBER_DOMAIN,
|
||||
DEFAULT_NAME,
|
||||
NumberEntityCapabilityAttribute,
|
||||
block_device_class=True,
|
||||
).schema
|
||||
)
|
||||
|
||||
NUMBER_CONFIG_ENTRY_SCHEMA = NUMBER_COMMON_SCHEMA.extend(
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
"""Shared schemas for config entry and YAML config items."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from enum import StrEnum
|
||||
from itertools import chain
|
||||
from typing import TypeVar
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_DEVICE_ID,
|
||||
CONF_ENTITY_PICTURE_TEMPLATE,
|
||||
CONF_ICON,
|
||||
CONF_ICON_TEMPLATE,
|
||||
CONF_NAME,
|
||||
CONF_OPTIMISTIC,
|
||||
CONF_UNIQUE_ID,
|
||||
@@ -15,26 +18,20 @@ from homeassistant.const import (
|
||||
from homeassistant.helpers import config_validation as cv, selector
|
||||
|
||||
from .const import (
|
||||
CONF_ATTRIBUTE_TEMPLATES,
|
||||
CONF_ATTRIBUTES,
|
||||
CONF_AVAILABILITY,
|
||||
CONF_AVAILABILITY_TEMPLATE,
|
||||
CONF_DEFAULT_ENTITY_ID,
|
||||
CONF_PICTURE,
|
||||
)
|
||||
|
||||
_AttributeEnum = TypeVar("_AttributeEnum", bound=type[StrEnum])
|
||||
|
||||
TEMPLATE_ENTITY_AVAILABILITY_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_AVAILABILITY): cv.template,
|
||||
}
|
||||
)
|
||||
|
||||
TEMPLATE_ENTITY_ATTRIBUTES_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_ATTRIBUTES): vol.Schema({cv.string: cv.template}),
|
||||
}
|
||||
)
|
||||
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_NAME): cv.template,
|
||||
@@ -47,32 +44,48 @@ TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA = {
|
||||
vol.Optional(CONF_OPTIMISTIC): cv.boolean,
|
||||
}
|
||||
|
||||
TEMPLATE_ENTITY_ATTRIBUTES_SCHEMA_LEGACY = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_ATTRIBUTE_TEMPLATES, default={}): vol.Schema(
|
||||
{cv.string: cv.template}
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_AVAILABILITY_TEMPLATE): cv.template,
|
||||
}
|
||||
)
|
||||
def _blocked_attributes(
|
||||
default_name: str,
|
||||
blocked_attributes: tuple[_AttributeEnum, ...] | _AttributeEnum | None,
|
||||
block_device_class: bool,
|
||||
) -> Callable[[dict], dict]:
|
||||
|
||||
TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_ENTITY_PICTURE_TEMPLATE): cv.template,
|
||||
vol.Optional(CONF_ICON_TEMPLATE): cv.template,
|
||||
}
|
||||
).extend(TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY.schema)
|
||||
def validate(obj: dict):
|
||||
if blocked_attributes is None and not block_device_class:
|
||||
return obj
|
||||
|
||||
_blocked_attributes: set[str]
|
||||
if blocked_attributes is None:
|
||||
_blocked_attributes = set()
|
||||
elif isinstance(blocked_attributes, tuple):
|
||||
_blocked_attributes = set(chain(*blocked_attributes))
|
||||
else:
|
||||
_blocked_attributes = set(blocked_attributes)
|
||||
|
||||
if block_device_class:
|
||||
_blocked_attributes.add("device_class")
|
||||
|
||||
if blocked := (_blocked_attributes.intersection(set(obj.keys()))):
|
||||
raise vol.Invalid(
|
||||
f"Unsupported attribute(s) found for {default_name}: {', '.join(blocked)}"
|
||||
)
|
||||
|
||||
return obj
|
||||
|
||||
return validate
|
||||
|
||||
|
||||
def make_template_entity_base_schema(domain: str, default_name: str) -> vol.Schema:
|
||||
def make_template_entity_common_schema(
|
||||
domain: str,
|
||||
default_name: str,
|
||||
blocked_attributes: tuple[_AttributeEnum, ...] | _AttributeEnum | None = None,
|
||||
block_device_class: bool = False,
|
||||
) -> vol.Schema:
|
||||
"""Return a schema with default name."""
|
||||
return vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_AVAILABILITY): cv.template,
|
||||
vol.Optional(CONF_DEFAULT_ENTITY_ID): vol.All(
|
||||
cv.entity_id, cv.entity_domain(domain)
|
||||
),
|
||||
@@ -80,28 +93,14 @@ def make_template_entity_base_schema(domain: str, default_name: str) -> vol.Sche
|
||||
vol.Optional(CONF_NAME, default=default_name): cv.template,
|
||||
vol.Optional(CONF_PICTURE): cv.template,
|
||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def make_template_entity_common_modern_schema(
|
||||
domain: str,
|
||||
default_name: str,
|
||||
) -> vol.Schema:
|
||||
"""Return a schema with default name."""
|
||||
return vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_AVAILABILITY): cv.template,
|
||||
vol.Optional(CONF_VARIABLES): cv.SCRIPT_VARIABLES_SCHEMA,
|
||||
vol.Optional(CONF_ATTRIBUTES): vol.Schema(
|
||||
vol.All(
|
||||
{cv.string: cv.template},
|
||||
_blocked_attributes(
|
||||
default_name, blocked_attributes, block_device_class
|
||||
),
|
||||
)
|
||||
),
|
||||
}
|
||||
).extend(make_template_entity_base_schema(domain, default_name).schema)
|
||||
|
||||
|
||||
def make_template_entity_common_modern_attributes_schema(
|
||||
domain: str,
|
||||
default_name: str,
|
||||
) -> vol.Schema:
|
||||
"""Return a schema with default name."""
|
||||
return make_template_entity_common_modern_schema(domain, default_name).extend(
|
||||
TEMPLATE_ENTITY_ATTRIBUTES_SCHEMA.schema
|
||||
)
|
||||
|
||||
@@ -10,6 +10,7 @@ from homeassistant.components.select import (
|
||||
DOMAIN as SELECT_DOMAIN,
|
||||
ENTITY_ID_FORMAT,
|
||||
SelectEntity,
|
||||
SelectEntityCapabilityAttribute,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_NAME, CONF_OPTIONS, CONF_STATE
|
||||
@@ -33,7 +34,7 @@ from .helpers import (
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA,
|
||||
make_template_entity_common_modern_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -56,7 +57,11 @@ SELECT_COMMON_SCHEMA = vol.Schema(
|
||||
|
||||
SELECT_YAML_SCHEMA = SELECT_COMMON_SCHEMA.extend(
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA
|
||||
).extend(make_template_entity_common_modern_schema(SELECT_DOMAIN, DEFAULT_NAME).schema)
|
||||
).extend(
|
||||
make_template_entity_common_schema(
|
||||
SELECT_DOMAIN, DEFAULT_NAME, SelectEntityCapabilityAttribute
|
||||
).schema
|
||||
)
|
||||
|
||||
SELECT_CONFIG_ENTRY_SCHEMA = SELECT_COMMON_SCHEMA.extend(
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema
|
||||
|
||||
@@ -39,7 +39,7 @@ from .helpers import (
|
||||
)
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
make_template_entity_common_modern_attributes_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -77,11 +77,7 @@ SENSOR_YAML_SCHEMA = vol.All(
|
||||
}
|
||||
)
|
||||
.extend(SENSOR_COMMON_SCHEMA.schema)
|
||||
.extend(
|
||||
make_template_entity_common_modern_attributes_schema(
|
||||
SENSOR_DOMAIN, DEFAULT_NAME
|
||||
).schema
|
||||
),
|
||||
.extend(make_template_entity_common_schema(SENSOR_DOMAIN, DEFAULT_NAME).schema),
|
||||
validate_last_reset,
|
||||
)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ from .helpers import (
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA,
|
||||
make_template_entity_common_modern_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -53,7 +53,11 @@ SWITCH_COMMON_SCHEMA = vol.Schema(
|
||||
|
||||
SWITCH_YAML_SCHEMA = SWITCH_COMMON_SCHEMA.extend(
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA
|
||||
).extend(make_template_entity_common_modern_schema(SWITCH_DOMAIN, DEFAULT_NAME).schema)
|
||||
).extend(
|
||||
make_template_entity_common_schema(
|
||||
SWITCH_DOMAIN, DEFAULT_NAME, block_device_class=True
|
||||
).schema
|
||||
)
|
||||
|
||||
|
||||
SWITCH_CONFIG_ENTRY_SCHEMA = SWITCH_COMMON_SCHEMA.extend(
|
||||
|
||||
@@ -34,7 +34,7 @@ from .helpers import (
|
||||
)
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
make_template_entity_common_modern_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -76,7 +76,12 @@ UPDATE_COMMON_SCHEMA = vol.Schema(
|
||||
)
|
||||
|
||||
UPDATE_YAML_SCHEMA = UPDATE_COMMON_SCHEMA.extend(
|
||||
make_template_entity_common_modern_schema(UPDATE_DOMAIN, DEFAULT_NAME).schema
|
||||
make_template_entity_common_schema(
|
||||
UPDATE_DOMAIN,
|
||||
DEFAULT_NAME,
|
||||
UpdateEntityStateAttribute,
|
||||
block_device_class=True,
|
||||
).schema
|
||||
)
|
||||
|
||||
UPDATE_CONFIG_ENTRY_SCHEMA = UPDATE_COMMON_SCHEMA.extend(
|
||||
|
||||
@@ -43,7 +43,7 @@ from .helpers import (
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA,
|
||||
make_template_entity_common_modern_attributes_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -101,9 +101,7 @@ VACUUM_COMMON_SCHEMA = vol.Schema(
|
||||
|
||||
VACUUM_YAML_SCHEMA = vol.All(
|
||||
VACUUM_COMMON_SCHEMA.extend(TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA).extend(
|
||||
make_template_entity_common_modern_attributes_schema(
|
||||
VACUUM_DOMAIN, DEFAULT_NAME
|
||||
).schema
|
||||
make_template_entity_common_schema(VACUUM_DOMAIN, DEFAULT_NAME).schema
|
||||
),
|
||||
cv.key_dependency(CONF_SEGMENTS, CONF_UNIQUE_ID),
|
||||
cv.key_dependency(CONF_CLEAN_SEGMENTS, CONF_UNIQUE_ID),
|
||||
|
||||
@@ -28,6 +28,7 @@ from homeassistant.components.weather import (
|
||||
Forecast,
|
||||
WeatherEntity,
|
||||
WeatherEntityFeature,
|
||||
WeatherEntityStateAttribute,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
@@ -65,7 +66,7 @@ from .helpers import (
|
||||
)
|
||||
from .schemas import (
|
||||
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
|
||||
make_template_entity_common_modern_schema,
|
||||
make_template_entity_common_schema,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
@@ -227,12 +228,16 @@ WEATHER_YAML_SCHEMA = (
|
||||
)
|
||||
.extend(WEATHER_COMMON_LEGACY_SCHEMA.schema)
|
||||
.extend(
|
||||
make_template_entity_common_modern_schema(WEATHER_DOMAIN, DEFAULT_NAME).schema
|
||||
make_template_entity_common_schema(
|
||||
WEATHER_DOMAIN, DEFAULT_NAME, WeatherEntityStateAttribute
|
||||
).schema
|
||||
)
|
||||
)
|
||||
|
||||
WEATHER_MODERN_YAML_SCHEMA = WEATHER_COMMON_MODERN_SCHEMA.extend(
|
||||
make_template_entity_common_modern_schema(WEATHER_DOMAIN, DEFAULT_NAME).schema
|
||||
make_template_entity_common_schema(
|
||||
WEATHER_DOMAIN, DEFAULT_NAME, WeatherEntityStateAttribute
|
||||
).schema
|
||||
)
|
||||
|
||||
WEATHER_CONFIG_ENTRY_SCHEMA = WEATHER_COMMON_MODERN_SCHEMA.extend(
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum, StrEnum
|
||||
from itertools import chain
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -21,6 +22,8 @@ from tests.common import (
|
||||
)
|
||||
from tests.conftest import WebSocketGenerator
|
||||
|
||||
_TEST_EXTRA_ATTRIBUTES_ENTITY_ID = "sensor.test_extra_attributes"
|
||||
|
||||
|
||||
class ConfigurationStyle(Enum):
|
||||
"""Configuration Styles for template testing."""
|
||||
@@ -44,7 +47,7 @@ def make_test_trigger(*entities: str) -> dict:
|
||||
"trigger": [
|
||||
{
|
||||
"trigger": "state",
|
||||
"entity_id": list(entities),
|
||||
"entity_id": list(chain(entities, (_TEST_EXTRA_ATTRIBUTES_ENTITY_ID,))),
|
||||
},
|
||||
{"platform": "event", "event_type": "test_event"},
|
||||
],
|
||||
@@ -413,3 +416,80 @@ async def setup_restore_template_entity(
|
||||
**config,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
async def assert_extra_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
platform_setup: TemplatePlatformSetup,
|
||||
style: ConfigurationStyle,
|
||||
config: ConfigType,
|
||||
) -> None:
|
||||
"""Test extra template attributes and attribute order."""
|
||||
|
||||
# Trigger attributes are resolved in order, Modern are not.
|
||||
setup_attributes = {
|
||||
ConfigurationStyle.MODERN: {},
|
||||
ConfigurationStyle.TRIGGER: {
|
||||
"base": "{{ state_attr('sensor.test_extra_attributes', 'base') or 0 }}",
|
||||
"plus_one": "{{ base + 1 }}",
|
||||
},
|
||||
}
|
||||
|
||||
await setup_entity(
|
||||
hass,
|
||||
platform_setup,
|
||||
style,
|
||||
1,
|
||||
{
|
||||
**config,
|
||||
"attributes": {
|
||||
"static": "{{ 'static' }}",
|
||||
"dynamic": "It {{ state_attr('sensor.test_extra_attributes', 'dynamic') }}.",
|
||||
**setup_attributes[style],
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
await async_trigger(
|
||||
hass,
|
||||
_TEST_EXTRA_ATTRIBUTES_ENTITY_ID,
|
||||
"anything",
|
||||
{
|
||||
"dynamic": "",
|
||||
"base": 1,
|
||||
},
|
||||
)
|
||||
|
||||
state = hass.states.get(platform_setup.entity_id)
|
||||
assert state.attributes["static"] == "static"
|
||||
assert state.attributes["dynamic"] == "It ."
|
||||
|
||||
# Assert attribute order for trigger entities
|
||||
for attr, value in (
|
||||
("base", 1),
|
||||
("plus_one", 2),
|
||||
):
|
||||
assert (
|
||||
attr not in state.attributes and style == ConfigurationStyle.MODERN
|
||||
) or state.attributes[attr] == value
|
||||
|
||||
await async_trigger(
|
||||
hass,
|
||||
_TEST_EXTRA_ATTRIBUTES_ENTITY_ID,
|
||||
"anything",
|
||||
{
|
||||
"dynamic": "works",
|
||||
"base": 2,
|
||||
},
|
||||
)
|
||||
|
||||
state = hass.states.get(platform_setup.entity_id)
|
||||
assert state.attributes["static"] == "static"
|
||||
assert state.attributes["dynamic"] == "It works."
|
||||
for attr, value in (
|
||||
("base", 2),
|
||||
("plus_one", 3),
|
||||
):
|
||||
assert (
|
||||
attr not in state.attributes and style == ConfigurationStyle.MODERN
|
||||
) or state.attributes[attr] == value
|
||||
|
||||
@@ -8,8 +8,10 @@ from syrupy.assertion import SnapshotAssertion
|
||||
from homeassistant.components import template
|
||||
from homeassistant.components.alarm_control_panel import (
|
||||
DOMAIN as ALARM_DOMAIN,
|
||||
AlarmControlPanelEntityStateAttribute,
|
||||
AlarmControlPanelState,
|
||||
)
|
||||
from homeassistant.components.template.alarm_control_panel import DEFAULT_NAME
|
||||
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
@@ -21,6 +23,7 @@ from .conftest import (
|
||||
ConfigurationStyle,
|
||||
TemplatePlatformSetup,
|
||||
assert_action,
|
||||
assert_extra_template_attributes,
|
||||
assert_state_and_attributes,
|
||||
async_get_flow_preview_state,
|
||||
async_trigger,
|
||||
@@ -790,3 +793,41 @@ async def test_invalid_availability_template_keeps_component_available(
|
||||
assert hass.states.get(TEST_PANEL.entity_id).state != STATE_UNAVAILABLE
|
||||
error = "UndefinedError: 'x' is undefined"
|
||||
assert error in caplog_setup_text or error in caplog.text
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_extra_template_attributes(
|
||||
hass: HomeAssistant, style: ConfigurationStyle
|
||||
) -> None:
|
||||
"""Test extra attributes."""
|
||||
await assert_extra_template_attributes(
|
||||
hass, TEST_PANEL, style, {"state": "{{ 'disarmed' }}"}
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("attribute", list(AlarmControlPanelEntityStateAttribute))
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_blocked_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
attribute: AlarmControlPanelEntityStateAttribute,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test blocked extra attributes."""
|
||||
await setup_entity(
|
||||
hass,
|
||||
TEST_PANEL,
|
||||
style,
|
||||
0,
|
||||
{
|
||||
"state": "{{ 'disarmed' }}",
|
||||
"attributes": {str(attribute): "{{ 'does not matter' }}"},
|
||||
},
|
||||
)
|
||||
assert (
|
||||
f"Unsupported attribute(s) found for {DEFAULT_NAME}: {attribute}" in caplog.text
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
|
||||
from homeassistant.components.template import DOMAIN
|
||||
from homeassistant.components.template.button import DEFAULT_NAME
|
||||
from homeassistant.components.template.const import CONF_PICTURE
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_PICTURE,
|
||||
@@ -29,6 +30,7 @@ from .conftest import (
|
||||
ConfigurationStyle,
|
||||
TemplatePlatformSetup,
|
||||
assert_action,
|
||||
assert_extra_template_attributes,
|
||||
async_trigger,
|
||||
make_test_action,
|
||||
setup_and_test_nested_unique_id,
|
||||
@@ -381,3 +383,31 @@ async def test_invalid_availability_template_keeps_component_available(
|
||||
"""Test that an invalid availability keeps the device available."""
|
||||
assert hass.states.get(TEST_BUTTON.entity_id).state != STATE_UNAVAILABLE
|
||||
assert "UndefinedError: 'x' is undefined" in caplog_setup_text
|
||||
|
||||
|
||||
async def test_extra_template_attributes(hass: HomeAssistant) -> None:
|
||||
"""Test extra attributes."""
|
||||
await assert_extra_template_attributes(
|
||||
hass, TEST_BUTTON, ConfigurationStyle.MODERN, {"press": []}
|
||||
)
|
||||
|
||||
|
||||
async def test_blocked_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test blocked extra attributes."""
|
||||
await setup_entity(
|
||||
hass,
|
||||
TEST_BUTTON,
|
||||
ConfigurationStyle.MODERN,
|
||||
0,
|
||||
{
|
||||
"press": [],
|
||||
"attributes": {"device_class": "{{ 'does not matter' }}"},
|
||||
},
|
||||
)
|
||||
assert (
|
||||
f"Unsupported attribute(s) found for {DEFAULT_NAME}: device_class"
|
||||
in caplog.text
|
||||
)
|
||||
|
||||
@@ -9,8 +9,10 @@ from homeassistant.components.cover import (
|
||||
ATTR_TILT_POSITION,
|
||||
DOMAIN as COVER_DOMAIN,
|
||||
CoverEntityFeature,
|
||||
CoverEntityStateAttribute,
|
||||
CoverState,
|
||||
)
|
||||
from homeassistant.components.template.cover import DEFAULT_NAME
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
SERVICE_CLOSE_COVER,
|
||||
@@ -35,6 +37,7 @@ from .conftest import (
|
||||
ConfigurationStyle,
|
||||
TemplatePlatformSetup,
|
||||
assert_action,
|
||||
assert_extra_template_attributes,
|
||||
assert_state_and_attributes,
|
||||
async_get_flow_preview_state,
|
||||
async_trigger,
|
||||
@@ -1286,3 +1289,44 @@ async def test_restore_state(
|
||||
assert state.state == final_state
|
||||
assert state.attributes["current_position"] == 75
|
||||
assert state.attributes["current_tilt_position"] == 75
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_extra_template_attributes(
|
||||
hass: HomeAssistant, style: ConfigurationStyle
|
||||
) -> None:
|
||||
"""Test extra attributes."""
|
||||
await assert_extra_template_attributes(
|
||||
hass, TEST_COVER, style, {"state": "{{ 'open' }}", **COVER_ACTIONS}
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"attribute", [*list(CoverEntityStateAttribute), "device_class"]
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_blocked_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
attribute: CoverEntityStateAttribute,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test blocked extra attributes."""
|
||||
await setup_entity(
|
||||
hass,
|
||||
TEST_COVER,
|
||||
style,
|
||||
0,
|
||||
{
|
||||
"state": "{{ 'open' }}",
|
||||
**COVER_ACTIONS,
|
||||
"attributes": {str(attribute): "{{ 'does not matter' }}"},
|
||||
},
|
||||
)
|
||||
assert (
|
||||
f"Unsupported attribute(s) found for {DEFAULT_NAME}: {attribute}" in caplog.text
|
||||
)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
"""The tests for the Template device_tracker platform."""
|
||||
|
||||
from itertools import chain
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components import device_tracker, template, zone
|
||||
from homeassistant.components.template.device_tracker import DEFAULT_NAME
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_PICTURE,
|
||||
ATTR_ICON,
|
||||
@@ -21,6 +23,7 @@ from homeassistant.helpers.typing import ConfigType
|
||||
from .conftest import (
|
||||
ConfigurationStyle,
|
||||
TemplatePlatformSetup,
|
||||
assert_extra_template_attributes,
|
||||
assert_state_and_attributes,
|
||||
async_get_flow_preview_state,
|
||||
async_trigger,
|
||||
@@ -786,3 +789,50 @@ async def test_restore_state(
|
||||
assert state.attributes["latitude"] == 32.88
|
||||
assert state.attributes["longitude"] == 117.24
|
||||
assert state.attributes["gps_accuracy"] == 20.0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_extra_template_attributes(
|
||||
hass: HomeAssistant, style: ConfigurationStyle
|
||||
) -> None:
|
||||
"""Test extra attributes."""
|
||||
await assert_extra_template_attributes(
|
||||
hass, TEST_TRACKER, style, TEST_MINIMUM_REQUIREMENTS
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"attribute",
|
||||
list(
|
||||
chain(
|
||||
device_tracker.DeviceTrackerEntityCapabilityAttribute,
|
||||
device_tracker.DeviceTrackerEntityStateAttribute,
|
||||
device_tracker.TrackerEntityStateAttribute,
|
||||
)
|
||||
),
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_blocked_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
attribute,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test blocked extra attributes."""
|
||||
await setup_entity(
|
||||
hass,
|
||||
TEST_TRACKER,
|
||||
style,
|
||||
0,
|
||||
{
|
||||
**TEST_MINIMUM_REQUIREMENTS,
|
||||
"attributes": {str(attribute): "{{ 'does not matter' }}"},
|
||||
},
|
||||
)
|
||||
assert (
|
||||
f"Unsupported attribute(s) found for {DEFAULT_NAME}: {attribute}" in caplog.text
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""The tests for the Template fan platform."""
|
||||
|
||||
from itertools import chain
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
@@ -14,9 +15,12 @@ from homeassistant.components.fan import (
|
||||
ATTR_PRESET_MODE,
|
||||
DIRECTION_FORWARD,
|
||||
DIRECTION_REVERSE,
|
||||
FanEntityCapabilityAttribute,
|
||||
FanEntityFeature,
|
||||
FanEntityStateAttribute,
|
||||
NotValidPresetModeError,
|
||||
)
|
||||
from homeassistant.components.template.fan import DEFAULT_NAME
|
||||
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
@@ -26,6 +30,7 @@ from .conftest import (
|
||||
ConfigurationStyle,
|
||||
TemplatePlatformSetup,
|
||||
assert_action,
|
||||
assert_extra_template_attributes,
|
||||
assert_state_and_attributes,
|
||||
async_get_flow_preview_state,
|
||||
async_trigger,
|
||||
@@ -1611,3 +1616,45 @@ async def test_restore_state(
|
||||
assert state.attributes["preset_mode"] == "low"
|
||||
assert state.attributes["oscillating"] is True
|
||||
assert state.attributes["direction"] == DIRECTION_REVERSE
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_extra_template_attributes(
|
||||
hass: HomeAssistant, style: ConfigurationStyle
|
||||
) -> None:
|
||||
"""Test extra attributes."""
|
||||
await assert_extra_template_attributes(
|
||||
hass, TEST_FAN, style, {"state": "{{ 'on' }}", **OPTIMISTIC_ON_OFF_ACTIONS}
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"attribute",
|
||||
list(chain(FanEntityCapabilityAttribute, FanEntityStateAttribute)),
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_blocked_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
attribute,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test blocked extra attributes."""
|
||||
await setup_entity(
|
||||
hass,
|
||||
TEST_FAN,
|
||||
style,
|
||||
0,
|
||||
{
|
||||
"state": "{{ 'on' }}",
|
||||
**OPTIMISTIC_ON_OFF_ACTIONS,
|
||||
"attributes": {str(attribute): "{{ 'does not matter' }}"},
|
||||
},
|
||||
)
|
||||
assert (
|
||||
f"Unsupported attribute(s) found for {DEFAULT_NAME}: {attribute}" in caplog.text
|
||||
)
|
||||
|
||||
@@ -11,17 +11,27 @@ import respx
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant import setup
|
||||
from homeassistant.components import image
|
||||
from homeassistant.components.input_text import (
|
||||
ATTR_VALUE as INPUT_TEXT_ATTR_VALUE,
|
||||
DOMAIN as INPUT_TEXT_DOMAIN,
|
||||
SERVICE_SET_VALUE as INPUT_TEXT_SERVICE_SET_VALUE,
|
||||
)
|
||||
from homeassistant.components.template import DOMAIN
|
||||
from homeassistant.components.template.image import DEFAULT_NAME
|
||||
from homeassistant.const import ATTR_ENTITY_PICTURE, CONF_ENTITY_ID, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .conftest import (
|
||||
ConfigurationStyle,
|
||||
TemplatePlatformSetup,
|
||||
assert_extra_template_attributes,
|
||||
make_test_trigger,
|
||||
setup_entity,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry, assert_setup_component
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
@@ -29,6 +39,14 @@ _DEFAULT = object()
|
||||
_TEST_IMAGE = "image.template_image"
|
||||
_URL_INPUT_TEXT = "input_text.url"
|
||||
|
||||
TEST_STATE_ENTITY_ID = "sensor.test_state"
|
||||
|
||||
TEST_IMAGE = TemplatePlatformSetup(
|
||||
image.DOMAIN,
|
||||
"test_template_image",
|
||||
make_test_trigger(TEST_STATE_ENTITY_ID, _TEST_IMAGE, _URL_INPUT_TEXT),
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def imgbytes_jpg():
|
||||
@@ -582,3 +600,46 @@ async def test_device_id(
|
||||
template_entity = entity_registry.async_get("image.my_template")
|
||||
assert template_entity is not None
|
||||
assert template_entity.device_id == device_entry.id
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_extra_template_attributes(
|
||||
hass: HomeAssistant, style: ConfigurationStyle
|
||||
) -> None:
|
||||
"""Test extra attributes."""
|
||||
await assert_extra_template_attributes(
|
||||
hass,
|
||||
TEST_IMAGE,
|
||||
style,
|
||||
{
|
||||
"url": "{{ 'http://example.com' }}",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("attribute", list(image.ImageEntityStateAttribute))
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_blocked_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
attribute: image.ImageEntityStateAttribute,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test blocked extra attributes."""
|
||||
await setup_entity(
|
||||
hass,
|
||||
TEST_IMAGE,
|
||||
style,
|
||||
0,
|
||||
{
|
||||
"url": "{{ 'http://example.com' }}",
|
||||
"attributes": {str(attribute): "{{ 'does not matter' }}"},
|
||||
},
|
||||
)
|
||||
assert (
|
||||
f"Unsupported attribute(s) found for {DEFAULT_NAME}: {attribute}" in caplog.text
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""The tests for the Template light platform."""
|
||||
|
||||
from itertools import chain
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
@@ -18,8 +19,11 @@ from homeassistant.components.light import (
|
||||
ATTR_TRANSITION,
|
||||
ATTR_XY_COLOR,
|
||||
ColorMode,
|
||||
LightEntityCapabilityAttribute,
|
||||
LightEntityFeature,
|
||||
LightEntityStateAttribute,
|
||||
)
|
||||
from homeassistant.components.template.light import DEFAULT_NAME
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
SERVICE_TURN_OFF,
|
||||
@@ -38,6 +42,7 @@ from .conftest import (
|
||||
ConfigurationStyle,
|
||||
TemplatePlatformSetup,
|
||||
assert_action,
|
||||
assert_extra_template_attributes,
|
||||
assert_state_and_attributes,
|
||||
async_get_flow_preview_state,
|
||||
async_trigger,
|
||||
@@ -2231,3 +2236,45 @@ async def test_saving_state(
|
||||
"supported_color_modes": ["brightness"],
|
||||
"xy_color": None,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_extra_template_attributes(
|
||||
hass: HomeAssistant, style: ConfigurationStyle
|
||||
) -> None:
|
||||
"""Test extra attributes."""
|
||||
await assert_extra_template_attributes(
|
||||
hass, TEST_LIGHT, style, {"state": "{{ 'on' }}", **ON_OFF_ACTIONS}
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"attribute",
|
||||
list(chain(LightEntityCapabilityAttribute, LightEntityStateAttribute)),
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_blocked_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
attribute,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test blocked extra attributes."""
|
||||
await setup_entity(
|
||||
hass,
|
||||
TEST_LIGHT,
|
||||
style,
|
||||
0,
|
||||
{
|
||||
"state": "{{ 'on' }}",
|
||||
**ON_OFF_ACTIONS,
|
||||
"attributes": {str(attribute): "{{ 'does not matter' }}"},
|
||||
},
|
||||
)
|
||||
assert (
|
||||
f"Unsupported attribute(s) found for {DEFAULT_NAME}: {attribute}" in caplog.text
|
||||
)
|
||||
|
||||
@@ -7,7 +7,12 @@ from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant import setup
|
||||
from homeassistant.components import lock, template
|
||||
from homeassistant.components.lock import LockEntityFeature, LockState
|
||||
from homeassistant.components.lock import (
|
||||
LockEntityFeature,
|
||||
LockEntityStateAttribute,
|
||||
LockState,
|
||||
)
|
||||
from homeassistant.components.template.lock import DEFAULT_NAME
|
||||
from homeassistant.const import (
|
||||
ATTR_CODE,
|
||||
ATTR_ENTITY_ID,
|
||||
@@ -24,6 +29,7 @@ from homeassistant.helpers.typing import ConfigType
|
||||
from .conftest import (
|
||||
ConfigurationStyle,
|
||||
TemplatePlatformSetup,
|
||||
assert_extra_template_attributes,
|
||||
assert_state_and_attributes,
|
||||
async_get_flow_preview_state,
|
||||
async_trigger,
|
||||
@@ -1155,3 +1161,49 @@ async def test_restore_state(
|
||||
assert_state_and_attributes(
|
||||
hass, TEST_LOCK, LockState.UNLOCKED, {"code_format": "\\\\d+"}
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_extra_template_attributes(
|
||||
hass: HomeAssistant, style: ConfigurationStyle
|
||||
) -> None:
|
||||
"""Test extra attributes."""
|
||||
await assert_extra_template_attributes(
|
||||
hass,
|
||||
TEST_LOCK,
|
||||
style,
|
||||
{"state": "{{ 'locked' }}", **LOCK_ACTION, **UNLOCK_ACTION},
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"attribute",
|
||||
list(LockEntityStateAttribute),
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_blocked_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
attribute,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test blocked extra attributes."""
|
||||
await setup_entity(
|
||||
hass,
|
||||
TEST_LOCK,
|
||||
style,
|
||||
0,
|
||||
{
|
||||
"state": "{{ 'locked' }}",
|
||||
**LOCK_ACTION,
|
||||
**UNLOCK_ACTION,
|
||||
"attributes": {str(attribute): "{{ 'does not matter' }}"},
|
||||
},
|
||||
)
|
||||
assert (
|
||||
f"Unsupported attribute(s) found for {DEFAULT_NAME}: {attribute}" in caplog.text
|
||||
)
|
||||
|
||||
@@ -13,9 +13,11 @@ from homeassistant.components.number import (
|
||||
ATTR_VALUE as NUMBER_ATTR_VALUE,
|
||||
DOMAIN as NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE as NUMBER_SERVICE_SET_VALUE,
|
||||
NumberEntityCapabilityAttribute,
|
||||
)
|
||||
from homeassistant.components.template import DOMAIN
|
||||
from homeassistant.components.template.const import CONF_PICTURE
|
||||
from homeassistant.components.template.number import DEFAULT_NAME
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_ENTITY_PICTURE,
|
||||
@@ -34,6 +36,7 @@ from .conftest import (
|
||||
ConfigurationStyle,
|
||||
TemplatePlatformSetup,
|
||||
assert_action,
|
||||
assert_extra_template_attributes,
|
||||
assert_state_and_attributes,
|
||||
async_get_flow_preview_state,
|
||||
async_trigger,
|
||||
@@ -747,3 +750,47 @@ async def test_restore_state(
|
||||
"30.0",
|
||||
{"step": 3, "min": 3, "max": 60},
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_extra_template_attributes(
|
||||
hass: HomeAssistant, style: ConfigurationStyle
|
||||
) -> None:
|
||||
"""Test extra attributes."""
|
||||
await assert_extra_template_attributes(
|
||||
hass,
|
||||
TEST_NUMBER,
|
||||
style,
|
||||
TEST_REQUIRED,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"attribute",
|
||||
[*list(NumberEntityCapabilityAttribute), "device_class"],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_blocked_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
attribute,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test blocked extra attributes."""
|
||||
await setup_entity(
|
||||
hass,
|
||||
TEST_NUMBER,
|
||||
style,
|
||||
0,
|
||||
{
|
||||
**TEST_REQUIRED,
|
||||
"attributes": {str(attribute): "{{ 'does not matter' }}"},
|
||||
},
|
||||
)
|
||||
assert (
|
||||
f"Unsupported attribute(s) found for {DEFAULT_NAME}: {attribute}" in caplog.text
|
||||
)
|
||||
|
||||
@@ -12,9 +12,11 @@ from homeassistant.components.select import (
|
||||
ATTR_OPTIONS as SELECT_ATTR_OPTIONS,
|
||||
DOMAIN as SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION as SELECT_SERVICE_SELECT_OPTION,
|
||||
SelectEntityCapabilityAttribute,
|
||||
)
|
||||
from homeassistant.components.template import DOMAIN
|
||||
from homeassistant.components.template.const import CONF_PICTURE
|
||||
from homeassistant.components.template.select import DEFAULT_NAME
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_ENTITY_PICTURE,
|
||||
@@ -32,6 +34,7 @@ from .conftest import (
|
||||
ConfigurationStyle,
|
||||
TemplatePlatformSetup,
|
||||
assert_action,
|
||||
assert_extra_template_attributes,
|
||||
assert_state_and_attributes,
|
||||
async_get_flow_preview_state,
|
||||
async_trigger,
|
||||
@@ -685,3 +688,47 @@ async def test_restore_state(
|
||||
"options": ["something", "anything", "something_new"],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_extra_template_attributes(
|
||||
hass: HomeAssistant, style: ConfigurationStyle
|
||||
) -> None:
|
||||
"""Test extra attributes."""
|
||||
await assert_extra_template_attributes(
|
||||
hass,
|
||||
TEST_SELECT,
|
||||
style,
|
||||
TEST_OPTIONS,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"attribute",
|
||||
list(SelectEntityCapabilityAttribute),
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_blocked_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
attribute,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test blocked extra attributes."""
|
||||
await setup_entity(
|
||||
hass,
|
||||
TEST_SELECT,
|
||||
style,
|
||||
0,
|
||||
{
|
||||
**TEST_OPTIONS,
|
||||
"attributes": {str(attribute): "{{ 'does not matter' }}"},
|
||||
},
|
||||
)
|
||||
assert (
|
||||
f"Unsupported attribute(s) found for {DEFAULT_NAME}: {attribute}" in caplog.text
|
||||
)
|
||||
|
||||
@@ -7,6 +7,7 @@ from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components import switch, template
|
||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||
from homeassistant.components.template.switch import DEFAULT_NAME
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
SERVICE_TURN_OFF,
|
||||
@@ -25,6 +26,7 @@ from .conftest import (
|
||||
ConfigurationStyle,
|
||||
TemplatePlatformSetup,
|
||||
assert_action,
|
||||
assert_extra_template_attributes,
|
||||
async_get_flow_preview_state,
|
||||
async_trigger,
|
||||
make_test_action,
|
||||
@@ -826,3 +828,39 @@ async def test_not_optimistic(hass: HomeAssistant, expected: str) -> None:
|
||||
|
||||
state = hass.states.get(TEST_SWITCH.entity_id)
|
||||
assert state.state == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_extra_template_attributes(
|
||||
hass: HomeAssistant, style: ConfigurationStyle
|
||||
) -> None:
|
||||
"""Test extra attributes."""
|
||||
await assert_extra_template_attributes(
|
||||
hass,
|
||||
TEST_SWITCH,
|
||||
style,
|
||||
SWITCH_ACTIONS,
|
||||
)
|
||||
|
||||
|
||||
async def test_blocked_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test blocked extra attributes."""
|
||||
await setup_entity(
|
||||
hass,
|
||||
TEST_SWITCH,
|
||||
ConfigurationStyle.MODERN,
|
||||
0,
|
||||
{
|
||||
**SWITCH_ACTIONS,
|
||||
"attributes": {"device_class": "{{ 'does not matter' }}"},
|
||||
},
|
||||
)
|
||||
assert (
|
||||
f"Unsupported attribute(s) found for {DEFAULT_NAME}: device_class"
|
||||
in caplog.text
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@ import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components import template, update
|
||||
from homeassistant.components.template.update import DEFAULT_NAME
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_PICTURE,
|
||||
ATTR_ICON,
|
||||
@@ -24,6 +25,7 @@ from .conftest import (
|
||||
ConfigurationStyle,
|
||||
TemplatePlatformSetup,
|
||||
assert_action,
|
||||
assert_extra_template_attributes,
|
||||
async_get_flow_preview_state,
|
||||
make_test_action,
|
||||
make_test_trigger,
|
||||
@@ -980,3 +982,47 @@ async def test_flow_preview(
|
||||
assert state["state"] == STATE_ON
|
||||
assert state["attributes"]["installed_version"] == "1.0"
|
||||
assert state["attributes"]["latest_version"] == "2.0"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_extra_template_attributes(
|
||||
hass: HomeAssistant, style: ConfigurationStyle
|
||||
) -> None:
|
||||
"""Test extra attributes."""
|
||||
await assert_extra_template_attributes(
|
||||
hass,
|
||||
TEST_UPDATE,
|
||||
style,
|
||||
TEST_UPDATE_CONFIG,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"attribute",
|
||||
[*list(update.UpdateEntityStateAttribute), "device_class"],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_blocked_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
attribute,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test blocked extra attributes."""
|
||||
await setup_entity(
|
||||
hass,
|
||||
TEST_UPDATE,
|
||||
style,
|
||||
0,
|
||||
{
|
||||
**TEST_UPDATE_CONFIG,
|
||||
"attributes": {str(attribute): "{{ 'does not matter' }}"},
|
||||
},
|
||||
)
|
||||
assert (
|
||||
f"Unsupported attribute(s) found for {DEFAULT_NAME}: {attribute}" in caplog.text
|
||||
)
|
||||
|
||||
@@ -7,6 +7,7 @@ from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components import template
|
||||
from homeassistant.components.template.const import CONF_PICTURE, DOMAIN
|
||||
from homeassistant.components.template.weather import DEFAULT_NAME
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_WEATHER_APPARENT_TEMPERATURE,
|
||||
ATTR_WEATHER_CLOUD_COVERAGE,
|
||||
@@ -23,6 +24,7 @@ from homeassistant.components.weather import (
|
||||
DOMAIN as WEATHER_DOMAIN,
|
||||
SERVICE_GET_FORECASTS,
|
||||
Forecast,
|
||||
WeatherEntityStateAttribute,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
@@ -40,6 +42,7 @@ from homeassistant.setup import async_setup_component
|
||||
from .conftest import (
|
||||
ConfigurationStyle,
|
||||
TemplatePlatformSetup,
|
||||
assert_extra_template_attributes,
|
||||
assert_state_and_attributes,
|
||||
async_get_flow_preview_state,
|
||||
async_trigger,
|
||||
@@ -1027,3 +1030,47 @@ async def test_flow_preview(
|
||||
)
|
||||
|
||||
assert state["state"] == "sunny"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_extra_template_attributes(
|
||||
hass: HomeAssistant, style: ConfigurationStyle
|
||||
) -> None:
|
||||
"""Test extra attributes."""
|
||||
await assert_extra_template_attributes(
|
||||
hass,
|
||||
TEST_WEATHER,
|
||||
style,
|
||||
TEST_MODERN_REQUIRED,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"attribute",
|
||||
list(WeatherEntityStateAttribute),
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"style", [ConfigurationStyle.MODERN, ConfigurationStyle.TRIGGER]
|
||||
)
|
||||
async def test_blocked_template_attributes(
|
||||
hass: HomeAssistant,
|
||||
style: ConfigurationStyle,
|
||||
attribute,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test blocked extra attributes."""
|
||||
await setup_entity(
|
||||
hass,
|
||||
TEST_WEATHER,
|
||||
style,
|
||||
0,
|
||||
{
|
||||
**TEST_MODERN_REQUIRED,
|
||||
"attributes": {str(attribute): "{{ 'does not matter' }}"},
|
||||
},
|
||||
)
|
||||
assert (
|
||||
f"Unsupported attribute(s) found for {DEFAULT_NAME}: {attribute}" in caplog.text
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user