Refactor async_setup_platform for template platforms (#147379)

This commit is contained in:
Petro31 2025-07-15 02:26:34 -04:00 committed by GitHub
parent a81e83cb28
commit 816977dd75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 711 additions and 1065 deletions

View File

@ -45,12 +45,8 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import CONF_OBJECT_ID, DOMAIN
from .coordinator import TriggerUpdateCoordinator
from .entity import AbstractTemplateEntity
from .template_entity import (
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
TemplateEntity,
make_template_entity_common_modern_schema,
rewrite_common_legacy_to_modern_conf,
)
from .helpers import async_setup_template_platform
from .template_entity import TemplateEntity, make_template_entity_common_modern_schema
from .trigger_entity import TriggerEntity
_LOGGER = logging.getLogger(__name__)
@ -88,7 +84,7 @@ class TemplateCodeFormat(Enum):
text = CodeFormat.TEXT
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | {
LEGACY_FIELDS = {
CONF_VALUE_TEMPLATE: CONF_STATE,
}
@ -161,54 +157,6 @@ ALARM_CONTROL_PANEL_CONFIG_SCHEMA = vol.Schema(
)
def rewrite_legacy_to_modern_conf(
hass: HomeAssistant, config: dict[str, dict]
) -> list[dict]:
"""Rewrite legacy alarm control panel configuration definitions to modern ones."""
alarm_control_panels = []
for object_id, entity_conf in config.items():
entity_conf = {**entity_conf, CONF_OBJECT_ID: object_id}
entity_conf = rewrite_common_legacy_to_modern_conf(
hass, entity_conf, LEGACY_FIELDS
)
if CONF_NAME not in entity_conf:
entity_conf[CONF_NAME] = template.Template(object_id, hass)
alarm_control_panels.append(entity_conf)
return alarm_control_panels
@callback
def _async_create_template_tracking_entities(
async_add_entities: AddEntitiesCallback,
hass: HomeAssistant,
definitions: list[dict],
unique_id_prefix: str | None,
) -> None:
"""Create the template alarm control panels."""
alarm_control_panels = []
for entity_conf in definitions:
unique_id = entity_conf.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
alarm_control_panels.append(
AlarmControlPanelTemplate(
hass,
entity_conf,
unique_id,
)
)
async_add_entities(alarm_control_panels)
def rewrite_options_to_modern_conf(option_config: dict[str, dict]) -> dict[str, dict]:
"""Rewrite option configuration to modern configuration."""
option_config = {**option_config}
@ -231,7 +179,7 @@ async def async_setup_entry(
validated_config = ALARM_CONTROL_PANEL_CONFIG_SCHEMA(_options)
async_add_entities(
[
AlarmControlPanelTemplate(
StateAlarmControlPanelEntity(
hass,
validated_config,
config_entry.entry_id,
@ -247,27 +195,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Template cover."""
if discovery_info is None:
_async_create_template_tracking_entities(
async_add_entities,
hass,
rewrite_legacy_to_modern_conf(hass, config[CONF_ALARM_CONTROL_PANELS]),
None,
)
return
if "coordinator" in discovery_info:
async_add_entities(
TriggerAlarmControlPanelEntity(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
)
return
_async_create_template_tracking_entities(
async_add_entities,
await async_setup_template_platform(
hass,
discovery_info["entities"],
discovery_info["unique_id"],
ALARM_CONTROL_PANEL_DOMAIN,
config,
StateAlarmControlPanelEntity,
TriggerAlarmControlPanelEntity,
async_add_entities,
discovery_info,
LEGACY_FIELDS,
legacy_key=CONF_ALARM_CONTROL_PANELS,
)
@ -414,7 +351,7 @@ class AbstractTemplateAlarmControlPanel(
)
class AlarmControlPanelTemplate(TemplateEntity, AbstractTemplateAlarmControlPanel):
class StateAlarmControlPanelEntity(TemplateEntity, AbstractTemplateAlarmControlPanel):
"""Representation of a templated Alarm Control Panel."""
_attr_should_poll = False

View File

@ -24,9 +24,7 @@ from homeassistant.const import (
CONF_DEVICE_CLASS,
CONF_DEVICE_ID,
CONF_ENTITY_PICTURE_TEMPLATE,
CONF_FRIENDLY_NAME,
CONF_FRIENDLY_NAME_TEMPLATE,
CONF_ICON,
CONF_ICON_TEMPLATE,
CONF_NAME,
CONF_SENSORS,
@ -53,18 +51,9 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util
from . import TriggerUpdateCoordinator
from .const import (
CONF_ATTRIBUTES,
CONF_AVAILABILITY,
CONF_AVAILABILITY_TEMPLATE,
CONF_OBJECT_ID,
CONF_PICTURE,
)
from .template_entity import (
TEMPLATE_ENTITY_COMMON_SCHEMA,
TemplateEntity,
rewrite_common_legacy_to_modern_conf,
)
from .const import CONF_AVAILABILITY_TEMPLATE, CONF_OBJECT_ID
from .helpers import async_setup_template_platform
from .template_entity import TEMPLATE_ENTITY_COMMON_SCHEMA, TemplateEntity
from .trigger_entity import TriggerEntity
CONF_DELAY_ON = "delay_on"
@ -73,12 +62,7 @@ CONF_AUTO_OFF = "auto_off"
CONF_ATTRIBUTE_TEMPLATES = "attribute_templates"
LEGACY_FIELDS = {
CONF_ICON_TEMPLATE: CONF_ICON,
CONF_ENTITY_PICTURE_TEMPLATE: CONF_PICTURE,
CONF_AVAILABILITY_TEMPLATE: CONF_AVAILABILITY,
CONF_ATTRIBUTE_TEMPLATES: CONF_ATTRIBUTES,
CONF_FRIENDLY_NAME_TEMPLATE: CONF_NAME,
CONF_FRIENDLY_NAME: CONF_NAME,
CONF_VALUE_TEMPLATE: CONF_STATE,
}
@ -121,27 +105,6 @@ LEGACY_BINARY_SENSOR_SCHEMA = vol.All(
)
def rewrite_legacy_to_modern_conf(
hass: HomeAssistant, cfg: dict[str, dict]
) -> list[dict]:
"""Rewrite legacy binary sensor definitions to modern ones."""
sensors = []
for object_id, entity_cfg in cfg.items():
entity_cfg = {**entity_cfg, CONF_OBJECT_ID: object_id}
entity_cfg = rewrite_common_legacy_to_modern_conf(
hass, entity_cfg, LEGACY_FIELDS
)
if CONF_NAME not in entity_cfg:
entity_cfg[CONF_NAME] = template.Template(object_id, hass)
sensors.append(entity_cfg)
return sensors
PLATFORM_SCHEMA = BINARY_SENSOR_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_SENSORS): cv.schema_with_slug_keys(
@ -151,33 +114,6 @@ PLATFORM_SCHEMA = BINARY_SENSOR_PLATFORM_SCHEMA.extend(
)
@callback
def _async_create_template_tracking_entities(
async_add_entities: AddEntitiesCallback | AddConfigEntryEntitiesCallback,
hass: HomeAssistant,
definitions: list[dict],
unique_id_prefix: str | None,
) -> None:
"""Create the template binary sensors."""
sensors = []
for entity_conf in definitions:
unique_id = entity_conf.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
sensors.append(
BinarySensorTemplate(
hass,
entity_conf,
unique_id,
)
)
async_add_entities(sensors)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@ -185,27 +121,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the template binary sensors."""
if discovery_info is None:
_async_create_template_tracking_entities(
async_add_entities,
hass,
rewrite_legacy_to_modern_conf(hass, config[CONF_SENSORS]),
None,
)
return
if "coordinator" in discovery_info:
async_add_entities(
TriggerBinarySensorEntity(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
)
return
_async_create_template_tracking_entities(
async_add_entities,
await async_setup_template_platform(
hass,
discovery_info["entities"],
discovery_info["unique_id"],
BINARY_SENSOR_DOMAIN,
config,
StateBinarySensorEntity,
TriggerBinarySensorEntity,
async_add_entities,
discovery_info,
LEGACY_FIELDS,
legacy_key=CONF_SENSORS,
)
@ -219,20 +144,20 @@ async def async_setup_entry(
_options.pop("template_type")
validated_config = BINARY_SENSOR_CONFIG_SCHEMA(_options)
async_add_entities(
[BinarySensorTemplate(hass, validated_config, config_entry.entry_id)]
[StateBinarySensorEntity(hass, validated_config, config_entry.entry_id)]
)
@callback
def async_create_preview_binary_sensor(
hass: HomeAssistant, name: str, config: dict[str, Any]
) -> BinarySensorTemplate:
) -> StateBinarySensorEntity:
"""Create a preview sensor."""
validated_config = BINARY_SENSOR_CONFIG_SCHEMA(config | {CONF_NAME: name})
return BinarySensorTemplate(hass, validated_config, None)
return StateBinarySensorEntity(hass, validated_config, None)
class BinarySensorTemplate(TemplateEntity, BinarySensorEntity, RestoreEntity):
class StateBinarySensorEntity(TemplateEntity, BinarySensorEntity, RestoreEntity):
"""A virtual binary sensor that triggers from another sensor."""
_attr_should_poll = False

View File

@ -3,20 +3,17 @@
from __future__ import annotations
import logging
from typing import Any
import voluptuous as vol
from homeassistant.components.button import DEVICE_CLASSES_SCHEMA, ButtonEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_DEVICE_CLASS,
CONF_DEVICE_ID,
CONF_NAME,
CONF_UNIQUE_ID,
from homeassistant.components.button import (
DEVICE_CLASSES_SCHEMA,
DOMAIN as BUTTON_DOMAIN,
ButtonEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DEVICE_CLASS, CONF_DEVICE_ID, CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import config_validation as cv, selector
from homeassistant.helpers.device import async_device_info_to_link_from_device_id
from homeassistant.helpers.entity_platform import (
@ -26,6 +23,7 @@ from homeassistant.helpers.entity_platform import (
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import CONF_PRESS, DOMAIN
from .helpers import async_setup_template_platform
from .template_entity import TemplateEntity, make_template_entity_common_modern_schema
_LOGGER = logging.getLogger(__name__)
@ -50,19 +48,6 @@ CONFIG_BUTTON_SCHEMA = vol.Schema(
)
async def _async_create_entities(
hass: HomeAssistant, definitions: list[dict[str, Any]], unique_id_prefix: str | None
) -> list[TemplateButtonEntity]:
"""Create the Template button."""
entities = []
for definition in definitions:
unique_id = definition.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
entities.append(TemplateButtonEntity(hass, definition, unique_id))
return entities
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@ -70,15 +55,14 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the template button."""
if not discovery_info or "coordinator" in discovery_info:
raise PlatformNotReady(
"The template button platform doesn't support trigger entities"
)
async_add_entities(
await _async_create_entities(
hass, discovery_info["entities"], discovery_info["unique_id"]
)
await async_setup_template_platform(
hass,
BUTTON_DOMAIN,
config,
StateButtonEntity,
None,
async_add_entities,
discovery_info,
)
@ -92,11 +76,11 @@ async def async_setup_entry(
_options.pop("template_type")
validated_config = CONFIG_BUTTON_SCHEMA(_options)
async_add_entities(
[TemplateButtonEntity(hass, validated_config, config_entry.entry_id)]
[StateButtonEntity(hass, validated_config, config_entry.entry_id)]
)
class TemplateButtonEntity(TemplateEntity, ButtonEntity):
class StateButtonEntity(TemplateEntity, ButtonEntity):
"""Representation of a template button."""
_attr_should_poll = False

View File

@ -65,7 +65,7 @@ from . import (
weather as weather_platform,
)
from .const import DOMAIN, PLATFORMS, TemplateConfig
from .helpers import async_get_blueprints
from .helpers import async_get_blueprints, rewrite_legacy_to_modern_configs
PACKAGE_MERGE_HINT = "list"
@ -249,16 +249,16 @@ async def async_validate_config(hass: HomeAssistant, config: ConfigType) -> Conf
legacy_warn_printed = False
for old_key, new_key, transform in (
for old_key, new_key, legacy_fields in (
(
CONF_SENSORS,
DOMAIN_SENSOR,
sensor_platform.rewrite_legacy_to_modern_conf,
sensor_platform.LEGACY_FIELDS,
),
(
CONF_BINARY_SENSORS,
DOMAIN_BINARY_SENSOR,
binary_sensor_platform.rewrite_legacy_to_modern_conf,
binary_sensor_platform.LEGACY_FIELDS,
),
):
if old_key not in template_config:
@ -276,7 +276,11 @@ async def async_validate_config(hass: HomeAssistant, config: ConfigType) -> Conf
definitions = (
list(template_config[new_key]) if new_key in template_config else []
)
definitions.extend(transform(hass, template_config[old_key]))
definitions.extend(
rewrite_legacy_to_modern_configs(
hass, template_config[old_key], legacy_fields
)
)
template_config = TemplateConfig({**template_config, new_key: definitions})
config_sections.append(template_config)

View File

@ -39,12 +39,11 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import TriggerUpdateCoordinator
from .const import CONF_OBJECT_ID, DOMAIN
from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import (
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY,
TemplateEntity,
make_template_entity_common_modern_schema,
rewrite_common_legacy_to_modern_conf,
)
from .trigger_entity import TriggerEntity
@ -85,7 +84,7 @@ TILT_FEATURES = (
| CoverEntityFeature.SET_TILT_POSITION
)
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | {
LEGACY_FIELDS = {
CONF_VALUE_TEMPLATE: CONF_STATE,
CONF_POSITION_TEMPLATE: CONF_POSITION,
CONF_TILT_TEMPLATE: CONF_TILT,
@ -140,54 +139,6 @@ PLATFORM_SCHEMA = COVER_PLATFORM_SCHEMA.extend(
)
def rewrite_legacy_to_modern_conf(
hass: HomeAssistant, config: dict[str, dict]
) -> list[dict]:
"""Rewrite legacy switch configuration definitions to modern ones."""
covers = []
for object_id, entity_conf in config.items():
entity_conf = {**entity_conf, CONF_OBJECT_ID: object_id}
entity_conf = rewrite_common_legacy_to_modern_conf(
hass, entity_conf, LEGACY_FIELDS
)
if CONF_NAME not in entity_conf:
entity_conf[CONF_NAME] = template.Template(object_id, hass)
covers.append(entity_conf)
return covers
@callback
def _async_create_template_tracking_entities(
async_add_entities: AddEntitiesCallback,
hass: HomeAssistant,
definitions: list[dict],
unique_id_prefix: str | None,
) -> None:
"""Create the template switches."""
covers = []
for entity_conf in definitions:
unique_id = entity_conf.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
covers.append(
CoverTemplate(
hass,
entity_conf,
unique_id,
)
)
async_add_entities(covers)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@ -195,27 +146,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Template cover."""
if discovery_info is None:
_async_create_template_tracking_entities(
async_add_entities,
hass,
rewrite_legacy_to_modern_conf(hass, config[CONF_COVERS]),
None,
)
return
if "coordinator" in discovery_info:
async_add_entities(
TriggerCoverEntity(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
)
return
_async_create_template_tracking_entities(
async_add_entities,
await async_setup_template_platform(
hass,
discovery_info["entities"],
discovery_info["unique_id"],
COVER_DOMAIN,
config,
StateCoverEntity,
TriggerCoverEntity,
async_add_entities,
discovery_info,
LEGACY_FIELDS,
legacy_key=CONF_COVERS,
)
@ -445,7 +385,7 @@ class AbstractTemplateCover(AbstractTemplateEntity, CoverEntity):
self.async_write_ha_state()
class CoverTemplate(TemplateEntity, AbstractTemplateCover):
class StateCoverEntity(TemplateEntity, AbstractTemplateCover):
"""Representation of a Template cover."""
_attr_should_poll = False

View File

@ -41,12 +41,11 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import CONF_OBJECT_ID, DOMAIN
from .coordinator import TriggerUpdateCoordinator
from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import (
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY,
TemplateEntity,
make_template_entity_common_modern_schema,
rewrite_common_legacy_to_modern_conf,
)
from .trigger_entity import TriggerEntity
@ -73,7 +72,7 @@ CONF_OSCILLATING = "oscillating"
CONF_PERCENTAGE = "percentage"
CONF_PRESET_MODE = "preset_mode"
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | {
LEGACY_FIELDS = {
CONF_DIRECTION_TEMPLATE: CONF_DIRECTION,
CONF_OSCILLATING_TEMPLATE: CONF_OSCILLATING,
CONF_PERCENTAGE_TEMPLATE: CONF_PERCENTAGE,
@ -132,54 +131,6 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend(
)
def rewrite_legacy_to_modern_conf(
hass: HomeAssistant, config: dict[str, dict]
) -> list[dict]:
"""Rewrite legacy fan configuration definitions to modern ones."""
fans = []
for object_id, entity_conf in config.items():
entity_conf = {**entity_conf, CONF_OBJECT_ID: object_id}
entity_conf = rewrite_common_legacy_to_modern_conf(
hass, entity_conf, LEGACY_FIELDS
)
if CONF_NAME not in entity_conf:
entity_conf[CONF_NAME] = template.Template(object_id, hass)
fans.append(entity_conf)
return fans
@callback
def _async_create_template_tracking_entities(
async_add_entities: AddEntitiesCallback,
hass: HomeAssistant,
definitions: list[dict],
unique_id_prefix: str | None,
) -> None:
"""Create the template fans."""
fans = []
for entity_conf in definitions:
unique_id = entity_conf.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
fans.append(
TemplateFan(
hass,
entity_conf,
unique_id,
)
)
async_add_entities(fans)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@ -187,27 +138,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the template fans."""
if discovery_info is None:
_async_create_template_tracking_entities(
async_add_entities,
hass,
rewrite_legacy_to_modern_conf(hass, config[CONF_FANS]),
None,
)
return
if "coordinator" in discovery_info:
async_add_entities(
TriggerFanEntity(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
)
return
_async_create_template_tracking_entities(
async_add_entities,
await async_setup_template_platform(
hass,
discovery_info["entities"],
discovery_info["unique_id"],
FAN_DOMAIN,
config,
StateFanEntity,
TriggerFanEntity,
async_add_entities,
discovery_info,
LEGACY_FIELDS,
legacy_key=CONF_FANS,
)
@ -484,7 +424,7 @@ class AbstractTemplateFan(AbstractTemplateEntity, FanEntity):
)
class TemplateFan(TemplateEntity, AbstractTemplateFan):
class StateFanEntity(TemplateEntity, AbstractTemplateFan):
"""A template fan component."""
_attr_should_poll = False

View File

@ -1,19 +1,60 @@
"""Helpers for template integration."""
from collections.abc import Callable
import itertools
import logging
from typing import Any
from homeassistant.components import blueprint
from homeassistant.const import SERVICE_RELOAD
from homeassistant.const import (
CONF_ENTITY_PICTURE_TEMPLATE,
CONF_FRIENDLY_NAME,
CONF_ICON,
CONF_ICON_TEMPLATE,
CONF_NAME,
CONF_UNIQUE_ID,
SERVICE_RELOAD,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import async_get_platforms
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import template
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_platform import (
AddEntitiesCallback,
async_get_platforms,
)
from homeassistant.helpers.singleton import singleton
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import DOMAIN
from .const import (
CONF_ATTRIBUTE_TEMPLATES,
CONF_ATTRIBUTES,
CONF_AVAILABILITY,
CONF_AVAILABILITY_TEMPLATE,
CONF_OBJECT_ID,
CONF_PICTURE,
DOMAIN,
)
from .entity import AbstractTemplateEntity
from .template_entity import TemplateEntity
from .trigger_entity import TriggerEntity
DATA_BLUEPRINTS = "template_blueprints"
LOGGER = logging.getLogger(__name__)
LEGACY_FIELDS = {
CONF_ICON_TEMPLATE: CONF_ICON,
CONF_ENTITY_PICTURE_TEMPLATE: CONF_PICTURE,
CONF_AVAILABILITY_TEMPLATE: CONF_AVAILABILITY,
CONF_ATTRIBUTE_TEMPLATES: CONF_ATTRIBUTES,
CONF_FRIENDLY_NAME: CONF_NAME,
}
_LOGGER = logging.getLogger(__name__)
type CreateTemplateEntitiesCallback = Callable[
[type[TemplateEntity], AddEntitiesCallback, HomeAssistant, list[dict], str | None],
None,
]
@callback
@ -59,8 +100,131 @@ def async_get_blueprints(hass: HomeAssistant) -> blueprint.DomainBlueprints:
return blueprint.DomainBlueprints(
hass,
DOMAIN,
LOGGER,
_LOGGER,
_blueprint_in_use,
_reload_blueprint_templates,
TEMPLATE_BLUEPRINT_SCHEMA,
)
def rewrite_legacy_to_modern_config(
hass: HomeAssistant,
entity_cfg: dict[str, Any],
extra_legacy_fields: dict[str, str],
) -> dict[str, Any]:
"""Rewrite legacy config."""
entity_cfg = {**entity_cfg}
for from_key, to_key in itertools.chain(
LEGACY_FIELDS.items(), extra_legacy_fields.items()
):
if from_key not in entity_cfg or to_key in entity_cfg:
continue
val = entity_cfg.pop(from_key)
if isinstance(val, str):
val = template.Template(val, hass)
entity_cfg[to_key] = val
if CONF_NAME in entity_cfg and isinstance(entity_cfg[CONF_NAME], str):
entity_cfg[CONF_NAME] = template.Template(entity_cfg[CONF_NAME], hass)
return entity_cfg
def rewrite_legacy_to_modern_configs(
hass: HomeAssistant,
entity_cfg: dict[str, dict],
extra_legacy_fields: dict[str, str],
) -> list[dict]:
"""Rewrite legacy configuration definitions to modern ones."""
entities = []
for object_id, entity_conf in entity_cfg.items():
entity_conf = {**entity_conf, CONF_OBJECT_ID: object_id}
entity_conf = rewrite_legacy_to_modern_config(
hass, entity_conf, extra_legacy_fields
)
if CONF_NAME not in entity_conf:
entity_conf[CONF_NAME] = template.Template(object_id, hass)
entities.append(entity_conf)
return entities
@callback
def async_create_template_tracking_entities(
entity_cls: type[Entity],
async_add_entities: AddEntitiesCallback,
hass: HomeAssistant,
definitions: list[dict],
unique_id_prefix: str | None,
) -> None:
"""Create the template tracking entities."""
entities: list[Entity] = []
for definition in definitions:
unique_id = definition.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
entities.append(entity_cls(hass, definition, unique_id)) # type: ignore[call-arg]
async_add_entities(entities)
async def async_setup_template_platform(
hass: HomeAssistant,
domain: str,
config: ConfigType,
state_entity_cls: type[TemplateEntity],
trigger_entity_cls: type[TriggerEntity] | None,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None,
legacy_fields: dict[str, str] | None = None,
legacy_key: str | None = None,
) -> None:
"""Set up the Template platform."""
if discovery_info is None:
# Legacy Configuration
if legacy_fields is not None:
if legacy_key:
configs = rewrite_legacy_to_modern_configs(
hass, config[legacy_key], legacy_fields
)
else:
configs = [rewrite_legacy_to_modern_config(hass, config, legacy_fields)]
async_create_template_tracking_entities(
state_entity_cls,
async_add_entities,
hass,
configs,
None,
)
else:
_LOGGER.warning(
"Template %s entities can only be configured under template:", domain
)
return
# Trigger Configuration
if "coordinator" in discovery_info:
if trigger_entity_cls:
entities = [
trigger_entity_cls(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
]
async_add_entities(entities)
else:
raise PlatformNotReady(
f"The template {domain} platform doesn't support trigger entities"
)
return
# Modern Configuration
async_create_template_tracking_entities(
state_entity_cls,
async_add_entities,
hass,
discovery_info["entities"],
discovery_info["unique_id"],
)

View File

@ -9,13 +9,7 @@ import voluptuous as vol
from homeassistant.components.image import DOMAIN as IMAGE_DOMAIN, ImageEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_DEVICE_ID,
CONF_NAME,
CONF_UNIQUE_ID,
CONF_URL,
CONF_VERIFY_SSL,
)
from homeassistant.const import CONF_DEVICE_ID, CONF_NAME, CONF_URL, CONF_VERIFY_SSL
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, selector
@ -29,6 +23,7 @@ from homeassistant.util import dt as dt_util
from . import TriggerUpdateCoordinator
from .const import CONF_PICTURE
from .helpers import async_setup_template_platform
from .template_entity import (
TemplateEntity,
make_template_entity_common_modern_attributes_schema,
@ -59,19 +54,6 @@ IMAGE_CONFIG_SCHEMA = vol.Schema(
)
async def _async_create_entities(
hass: HomeAssistant, definitions: list[dict[str, Any]], unique_id_prefix: str | None
) -> list[StateImageEntity]:
"""Create the template image."""
entities = []
for definition in definitions:
unique_id = definition.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
entities.append(StateImageEntity(hass, definition, unique_id))
return entities
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@ -79,23 +61,14 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the template image."""
if discovery_info is None:
_LOGGER.warning(
"Template image entities can only be configured under template:"
)
return
if "coordinator" in discovery_info:
async_add_entities(
TriggerImageEntity(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
)
return
async_add_entities(
await _async_create_entities(
hass, discovery_info["entities"], discovery_info["unique_id"]
)
await async_setup_template_platform(
hass,
IMAGE_DOMAIN,
config,
StateImageEntity,
TriggerImageEntity,
async_add_entities,
discovery_info,
)

View File

@ -51,12 +51,11 @@ from homeassistant.util import color as color_util
from . import TriggerUpdateCoordinator
from .const import CONF_OBJECT_ID, DOMAIN
from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import (
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY,
TemplateEntity,
make_template_entity_common_modern_schema,
rewrite_common_legacy_to_modern_conf,
)
from .trigger_entity import TriggerEntity
@ -103,7 +102,7 @@ CONF_WHITE_VALUE_TEMPLATE = "white_value_template"
DEFAULT_MIN_MIREDS = 153
DEFAULT_MAX_MIREDS = 500
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | {
LEGACY_FIELDS = {
CONF_COLOR_ACTION: CONF_HS_ACTION,
CONF_COLOR_TEMPLATE: CONF_HS,
CONF_EFFECT_LIST_TEMPLATE: CONF_EFFECT_LIST,
@ -193,47 +192,6 @@ PLATFORM_SCHEMA = vol.All(
)
def rewrite_legacy_to_modern_conf(
hass: HomeAssistant, config: dict[str, dict]
) -> list[dict]:
"""Rewrite legacy switch configuration definitions to modern ones."""
lights = []
for object_id, entity_conf in config.items():
entity_conf = {**entity_conf, CONF_OBJECT_ID: object_id}
entity_conf = rewrite_common_legacy_to_modern_conf(
hass, entity_conf, LEGACY_FIELDS
)
if CONF_NAME not in entity_conf:
entity_conf[CONF_NAME] = template.Template(object_id, hass)
lights.append(entity_conf)
return lights
@callback
def _async_create_template_tracking_entities(
async_add_entities: AddEntitiesCallback,
hass: HomeAssistant,
definitions: list[dict],
unique_id_prefix: str | None,
) -> None:
"""Create the Template Lights."""
lights = []
for entity_conf in definitions:
unique_id = entity_conf.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
lights.append(LightTemplate(hass, entity_conf, unique_id))
async_add_entities(lights)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@ -241,27 +199,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the template lights."""
if discovery_info is None:
_async_create_template_tracking_entities(
async_add_entities,
hass,
rewrite_legacy_to_modern_conf(hass, config[CONF_LIGHTS]),
None,
)
return
if "coordinator" in discovery_info:
async_add_entities(
TriggerLightEntity(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
)
return
_async_create_template_tracking_entities(
async_add_entities,
await async_setup_template_platform(
hass,
discovery_info["entities"],
discovery_info["unique_id"],
LIGHT_DOMAIN,
config,
StateLightEntity,
TriggerLightEntity,
async_add_entities,
discovery_info,
LEGACY_FIELDS,
legacy_key=CONF_LIGHTS,
)
@ -934,7 +881,7 @@ class AbstractTemplateLight(AbstractTemplateEntity, LightEntity):
self._attr_supported_features |= LightEntityFeature.TRANSITION
class LightTemplate(TemplateEntity, AbstractTemplateLight):
class StateLightEntity(TemplateEntity, AbstractTemplateLight):
"""Representation of a templated Light, including dimmable."""
_attr_should_poll = False

View File

@ -31,12 +31,11 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import CONF_PICTURE, DOMAIN
from .coordinator import TriggerUpdateCoordinator
from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import (
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY,
TemplateEntity,
make_template_entity_common_modern_schema,
rewrite_common_legacy_to_modern_conf,
)
from .trigger_entity import TriggerEntity
@ -49,7 +48,7 @@ CONF_OPEN = "open"
DEFAULT_NAME = "Template Lock"
DEFAULT_OPTIMISTIC = False
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | {
LEGACY_FIELDS = {
CONF_CODE_FORMAT_TEMPLATE: CONF_CODE_FORMAT,
CONF_VALUE_TEMPLATE: CONF_STATE,
}
@ -83,33 +82,6 @@ PLATFORM_SCHEMA = LOCK_PLATFORM_SCHEMA.extend(
).extend(TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY.schema)
@callback
def _async_create_template_tracking_entities(
async_add_entities: AddEntitiesCallback,
hass: HomeAssistant,
definitions: list[dict],
unique_id_prefix: str | None,
) -> None:
"""Create the template fans."""
fans = []
for entity_conf in definitions:
unique_id = entity_conf.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
fans.append(
TemplateLock(
hass,
entity_conf,
unique_id,
)
)
async_add_entities(fans)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@ -117,27 +89,15 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the template fans."""
if discovery_info is None:
_async_create_template_tracking_entities(
async_add_entities,
hass,
[rewrite_common_legacy_to_modern_conf(hass, config, LEGACY_FIELDS)],
None,
)
return
if "coordinator" in discovery_info:
async_add_entities(
TriggerLockEntity(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
)
return
_async_create_template_tracking_entities(
async_add_entities,
await async_setup_template_platform(
hass,
discovery_info["entities"],
discovery_info["unique_id"],
LOCK_DOMAIN,
config,
StateLockEntity,
TriggerLockEntity,
async_add_entities,
discovery_info,
LEGACY_FIELDS,
)
@ -311,7 +271,7 @@ class AbstractTemplateLock(AbstractTemplateEntity, LockEntity):
)
class TemplateLock(TemplateEntity, AbstractTemplateLock):
class StateLockEntity(TemplateEntity, AbstractTemplateLock):
"""Representation of a template lock."""
_attr_should_poll = False

View File

@ -21,7 +21,6 @@ from homeassistant.const import (
CONF_NAME,
CONF_OPTIMISTIC,
CONF_STATE,
CONF_UNIQUE_ID,
CONF_UNIT_OF_MEASUREMENT,
)
from homeassistant.core import HomeAssistant, callback
@ -35,6 +34,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import TriggerUpdateCoordinator
from .const import CONF_MAX, CONF_MIN, CONF_STEP, DOMAIN
from .helpers import async_setup_template_platform
from .template_entity import TemplateEntity, make_template_entity_common_modern_schema
from .trigger_entity import TriggerEntity
@ -70,19 +70,6 @@ NUMBER_CONFIG_SCHEMA = vol.Schema(
)
async def _async_create_entities(
hass: HomeAssistant, definitions: list[dict[str, Any]], unique_id_prefix: str | None
) -> list[TemplateNumber]:
"""Create the Template number."""
entities = []
for definition in definitions:
unique_id = definition.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
entities.append(TemplateNumber(hass, definition, unique_id))
return entities
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@ -90,23 +77,14 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the template number."""
if discovery_info is None:
_LOGGER.warning(
"Template number entities can only be configured under template:"
)
return
if "coordinator" in discovery_info:
async_add_entities(
TriggerNumberEntity(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
)
return
async_add_entities(
await _async_create_entities(
hass, discovery_info["entities"], discovery_info["unique_id"]
)
await async_setup_template_platform(
hass,
NUMBER_DOMAIN,
config,
StateNumberEntity,
TriggerNumberEntity,
async_add_entities,
discovery_info,
)
@ -119,19 +97,21 @@ async def async_setup_entry(
_options = dict(config_entry.options)
_options.pop("template_type")
validated_config = NUMBER_CONFIG_SCHEMA(_options)
async_add_entities([TemplateNumber(hass, validated_config, config_entry.entry_id)])
async_add_entities(
[StateNumberEntity(hass, validated_config, config_entry.entry_id)]
)
@callback
def async_create_preview_number(
hass: HomeAssistant, name: str, config: dict[str, Any]
) -> TemplateNumber:
) -> StateNumberEntity:
"""Create a preview number."""
validated_config = NUMBER_CONFIG_SCHEMA(config | {CONF_NAME: name})
return TemplateNumber(hass, validated_config, None)
return StateNumberEntity(hass, validated_config, None)
class TemplateNumber(TemplateEntity, NumberEntity):
class StateNumberEntity(TemplateEntity, NumberEntity):
"""Representation of a template number."""
_attr_should_poll = False

View File

@ -14,13 +14,7 @@ from homeassistant.components.select import (
SelectEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_DEVICE_ID,
CONF_NAME,
CONF_OPTIMISTIC,
CONF_STATE,
CONF_UNIQUE_ID,
)
from homeassistant.const import CONF_DEVICE_ID, CONF_NAME, CONF_OPTIMISTIC, CONF_STATE
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv, selector
from homeassistant.helpers.device import async_device_info_to_link_from_device_id
@ -33,6 +27,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import TriggerUpdateCoordinator
from .const import DOMAIN
from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import TemplateEntity, make_template_entity_common_modern_schema
from .trigger_entity import TriggerEntity
@ -65,19 +60,6 @@ SELECT_CONFIG_SCHEMA = vol.Schema(
)
async def _async_create_entities(
hass: HomeAssistant, definitions: list[dict[str, Any]], unique_id_prefix: str | None
) -> list[TemplateSelect]:
"""Create the Template select."""
entities = []
for definition in definitions:
unique_id = definition.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
entities.append(TemplateSelect(hass, definition, unique_id))
return entities
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@ -85,23 +67,14 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the template select."""
if discovery_info is None:
_LOGGER.warning(
"Template select entities can only be configured under template:"
)
return
if "coordinator" in discovery_info:
async_add_entities(
TriggerSelectEntity(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
)
return
async_add_entities(
await _async_create_entities(
hass, discovery_info["entities"], discovery_info["unique_id"]
)
await async_setup_template_platform(
hass,
SELECT_DOMAIN,
config,
TemplateSelect,
TriggerSelectEntity,
async_add_entities,
discovery_info,
)

View File

@ -56,16 +56,12 @@ from homeassistant.util import dt as dt_util
from . import TriggerUpdateCoordinator
from .const import CONF_ATTRIBUTE_TEMPLATES, CONF_AVAILABILITY_TEMPLATE, CONF_OBJECT_ID
from .template_entity import (
TEMPLATE_ENTITY_COMMON_SCHEMA,
TemplateEntity,
rewrite_common_legacy_to_modern_conf,
)
from .helpers import async_setup_template_platform
from .template_entity import TEMPLATE_ENTITY_COMMON_SCHEMA, TemplateEntity
from .trigger_entity import TriggerEntity
LEGACY_FIELDS = {
CONF_FRIENDLY_NAME_TEMPLATE: CONF_NAME,
CONF_FRIENDLY_NAME: CONF_NAME,
CONF_VALUE_TEMPLATE: CONF_STATE,
}
@ -142,27 +138,6 @@ def extra_validation_checks(val):
return val
def rewrite_legacy_to_modern_conf(
hass: HomeAssistant, cfg: dict[str, dict]
) -> list[dict]:
"""Rewrite legacy sensor definitions to modern ones."""
sensors = []
for object_id, entity_cfg in cfg.items():
entity_cfg = {**entity_cfg, CONF_OBJECT_ID: object_id}
entity_cfg = rewrite_common_legacy_to_modern_conf(
hass, entity_cfg, LEGACY_FIELDS
)
if CONF_NAME not in entity_cfg:
entity_cfg[CONF_NAME] = template.Template(object_id, hass)
sensors.append(entity_cfg)
return sensors
PLATFORM_SCHEMA = vol.All(
SENSOR_PLATFORM_SCHEMA.extend(
{
@ -177,33 +152,6 @@ PLATFORM_SCHEMA = vol.All(
_LOGGER = logging.getLogger(__name__)
@callback
def _async_create_template_tracking_entities(
async_add_entities: AddEntitiesCallback | AddConfigEntryEntitiesCallback,
hass: HomeAssistant,
definitions: list[dict],
unique_id_prefix: str | None,
) -> None:
"""Create the template sensors."""
sensors = []
for entity_conf in definitions:
unique_id = entity_conf.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
sensors.append(
SensorTemplate(
hass,
entity_conf,
unique_id,
)
)
async_add_entities(sensors)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@ -211,27 +159,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the template sensors."""
if discovery_info is None:
_async_create_template_tracking_entities(
async_add_entities,
hass,
rewrite_legacy_to_modern_conf(hass, config[CONF_SENSORS]),
None,
)
return
if "coordinator" in discovery_info:
async_add_entities(
TriggerSensorEntity(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
)
return
_async_create_template_tracking_entities(
async_add_entities,
await async_setup_template_platform(
hass,
discovery_info["entities"],
discovery_info["unique_id"],
SENSOR_DOMAIN,
config,
StateSensorEntity,
TriggerSensorEntity,
async_add_entities,
discovery_info,
LEGACY_FIELDS,
legacy_key=CONF_SENSORS,
)
@ -244,19 +181,21 @@ async def async_setup_entry(
_options = dict(config_entry.options)
_options.pop("template_type")
validated_config = SENSOR_CONFIG_SCHEMA(_options)
async_add_entities([SensorTemplate(hass, validated_config, config_entry.entry_id)])
async_add_entities(
[StateSensorEntity(hass, validated_config, config_entry.entry_id)]
)
@callback
def async_create_preview_sensor(
hass: HomeAssistant, name: str, config: dict[str, Any]
) -> SensorTemplate:
) -> StateSensorEntity:
"""Create a preview sensor."""
validated_config = SENSOR_CONFIG_SCHEMA(config | {CONF_NAME: name})
return SensorTemplate(hass, validated_config, None)
return StateSensorEntity(hass, validated_config, None)
class SensorTemplate(TemplateEntity, SensorEntity):
class StateSensorEntity(TemplateEntity, SensorEntity):
"""Representation of a Template Sensor."""
_attr_should_poll = False

View File

@ -41,18 +41,17 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import TriggerUpdateCoordinator
from .const import CONF_OBJECT_ID, CONF_TURN_OFF, CONF_TURN_ON, DOMAIN
from .helpers import async_setup_template_platform
from .template_entity import (
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY,
TemplateEntity,
make_template_entity_common_modern_schema,
rewrite_common_legacy_to_modern_conf,
)
from .trigger_entity import TriggerEntity
_VALID_STATES = [STATE_ON, STATE_OFF, "true", "false"]
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | {
LEGACY_FIELDS = {
CONF_VALUE_TEMPLATE: CONF_STATE,
}
@ -96,27 +95,6 @@ SWITCH_CONFIG_SCHEMA = vol.Schema(
)
def rewrite_legacy_to_modern_conf(
hass: HomeAssistant, config: dict[str, dict]
) -> list[dict]:
"""Rewrite legacy switch configuration definitions to modern ones."""
switches = []
for object_id, entity_conf in config.items():
entity_conf = {**entity_conf, CONF_OBJECT_ID: object_id}
entity_conf = rewrite_common_legacy_to_modern_conf(
hass, entity_conf, LEGACY_FIELDS
)
if CONF_NAME not in entity_conf:
entity_conf[CONF_NAME] = template.Template(object_id, hass)
switches.append(entity_conf)
return switches
def rewrite_options_to_modern_conf(option_config: dict[str, dict]) -> dict[str, dict]:
"""Rewrite option configuration to modern configuration."""
option_config = {**option_config}
@ -127,33 +105,6 @@ def rewrite_options_to_modern_conf(option_config: dict[str, dict]) -> dict[str,
return option_config
@callback
def _async_create_template_tracking_entities(
async_add_entities: AddEntitiesCallback,
hass: HomeAssistant,
definitions: list[dict],
unique_id_prefix: str | None,
) -> None:
"""Create the template switches."""
switches = []
for entity_conf in definitions:
unique_id = entity_conf.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
switches.append(
SwitchTemplate(
hass,
entity_conf,
unique_id,
)
)
async_add_entities(switches)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@ -161,27 +112,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the template switches."""
if discovery_info is None:
_async_create_template_tracking_entities(
async_add_entities,
hass,
rewrite_legacy_to_modern_conf(hass, config[CONF_SWITCHES]),
None,
)
return
if "coordinator" in discovery_info:
async_add_entities(
TriggerSwitchEntity(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
)
return
_async_create_template_tracking_entities(
async_add_entities,
await async_setup_template_platform(
hass,
discovery_info["entities"],
discovery_info["unique_id"],
SWITCH_DOMAIN,
config,
StateSwitchEntity,
TriggerSwitchEntity,
async_add_entities,
discovery_info,
LEGACY_FIELDS,
legacy_key=CONF_SWITCHES,
)
@ -195,20 +135,22 @@ async def async_setup_entry(
_options.pop("template_type")
_options = rewrite_options_to_modern_conf(_options)
validated_config = SWITCH_CONFIG_SCHEMA(_options)
async_add_entities([SwitchTemplate(hass, validated_config, config_entry.entry_id)])
async_add_entities(
[StateSwitchEntity(hass, validated_config, config_entry.entry_id)]
)
@callback
def async_create_preview_switch(
hass: HomeAssistant, name: str, config: dict[str, Any]
) -> SwitchTemplate:
) -> StateSwitchEntity:
"""Create a preview switch."""
updated_config = rewrite_options_to_modern_conf(config)
validated_config = SWITCH_CONFIG_SCHEMA(updated_config | {CONF_NAME: name})
return SwitchTemplate(hass, validated_config, None)
return StateSwitchEntity(hass, validated_config, None)
class SwitchTemplate(TemplateEntity, SwitchEntity, RestoreEntity):
class StateSwitchEntity(TemplateEntity, SwitchEntity, RestoreEntity):
"""Representation of a Template switch."""
_attr_should_poll = False

View File

@ -4,7 +4,6 @@ from __future__ import annotations
from collections.abc import Callable, Mapping
import contextlib
import itertools
import logging
from typing import Any, cast
@ -14,7 +13,6 @@ import voluptuous as vol
from homeassistant.components.blueprint import CONF_USE_BLUEPRINT
from homeassistant.const import (
CONF_ENTITY_PICTURE_TEMPLATE,
CONF_FRIENDLY_NAME,
CONF_ICON,
CONF_ICON_TEMPLATE,
CONF_NAME,
@ -137,42 +135,6 @@ TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY = vol.Schema(
).extend(TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY.schema)
LEGACY_FIELDS = {
CONF_ICON_TEMPLATE: CONF_ICON,
CONF_ENTITY_PICTURE_TEMPLATE: CONF_PICTURE,
CONF_AVAILABILITY_TEMPLATE: CONF_AVAILABILITY,
CONF_ATTRIBUTE_TEMPLATES: CONF_ATTRIBUTES,
CONF_FRIENDLY_NAME: CONF_NAME,
}
def rewrite_common_legacy_to_modern_conf(
hass: HomeAssistant,
entity_cfg: dict[str, Any],
extra_legacy_fields: dict[str, str] | None = None,
) -> dict[str, Any]:
"""Rewrite legacy config."""
entity_cfg = {**entity_cfg}
if extra_legacy_fields is None:
extra_legacy_fields = {}
for from_key, to_key in itertools.chain(
LEGACY_FIELDS.items(), extra_legacy_fields.items()
):
if from_key not in entity_cfg or to_key in entity_cfg:
continue
val = entity_cfg.pop(from_key)
if isinstance(val, str):
val = Template(val, hass)
entity_cfg[to_key] = val
if CONF_NAME in entity_cfg and isinstance(entity_cfg[CONF_NAME], str):
entity_cfg[CONF_NAME] = Template(entity_cfg[CONF_NAME], hass)
return entity_cfg
class _TemplateAttribute:
"""Attribute value linked to template result."""

View File

@ -41,13 +41,12 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import CONF_OBJECT_ID, DOMAIN
from .coordinator import TriggerUpdateCoordinator
from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import (
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
TEMPLATE_ENTITY_ATTRIBUTES_SCHEMA_LEGACY,
TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY,
TemplateEntity,
make_template_entity_common_modern_attributes_schema,
rewrite_common_legacy_to_modern_conf,
)
from .trigger_entity import TriggerEntity
@ -72,7 +71,7 @@ _VALID_STATES = [
VacuumActivity.ERROR,
]
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | {
LEGACY_FIELDS = {
CONF_BATTERY_LEVEL_TEMPLATE: CONF_BATTERY_LEVEL,
CONF_FAN_SPEED_TEMPLATE: CONF_FAN_SPEED,
CONF_VALUE_TEMPLATE: CONF_STATE,
@ -125,82 +124,23 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend(
)
def rewrite_legacy_to_modern_conf(
hass: HomeAssistant, config: dict[str, dict]
) -> list[dict]:
"""Rewrite legacy switch configuration definitions to modern ones."""
vacuums = []
for object_id, entity_conf in config.items():
entity_conf = {**entity_conf, CONF_OBJECT_ID: object_id}
entity_conf = rewrite_common_legacy_to_modern_conf(
hass, entity_conf, LEGACY_FIELDS
)
if CONF_NAME not in entity_conf:
entity_conf[CONF_NAME] = template.Template(object_id, hass)
vacuums.append(entity_conf)
return vacuums
@callback
def _async_create_template_tracking_entities(
async_add_entities: AddEntitiesCallback,
hass: HomeAssistant,
definitions: list[dict],
unique_id_prefix: str | None,
) -> None:
"""Create the template switches."""
vacuums = []
for entity_conf in definitions:
unique_id = entity_conf.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
vacuums.append(
TemplateVacuum(
hass,
entity_conf,
unique_id,
)
)
async_add_entities(vacuums)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Template cover."""
if discovery_info is None:
_async_create_template_tracking_entities(
async_add_entities,
hass,
rewrite_legacy_to_modern_conf(hass, config[CONF_VACUUMS]),
None,
)
return
if "coordinator" in discovery_info:
async_add_entities(
TriggerVacuumEntity(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
)
return
_async_create_template_tracking_entities(
async_add_entities,
"""Set up the Template vacuum."""
await async_setup_template_platform(
hass,
discovery_info["entities"],
discovery_info["unique_id"],
VACUUM_DOMAIN,
config,
TemplateStateVacuumEntity,
TriggerVacuumEntity,
async_add_entities,
discovery_info,
LEGACY_FIELDS,
legacy_key=CONF_VACUUMS,
)
@ -350,7 +290,7 @@ class AbstractTemplateVacuum(AbstractTemplateEntity, StateVacuumEntity):
self._attr_fan_speed = None
class TemplateVacuum(TemplateEntity, AbstractTemplateVacuum):
class TemplateStateVacuumEntity(TemplateEntity, AbstractTemplateVacuum):
"""A template vacuum component."""
_attr_should_poll = False

View File

@ -31,12 +31,7 @@ from homeassistant.components.weather import (
WeatherEntity,
WeatherEntityFeature,
)
from homeassistant.const import (
CONF_TEMPERATURE_UNIT,
CONF_UNIQUE_ID,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.const import CONF_TEMPERATURE_UNIT, STATE_UNAVAILABLE, STATE_UNKNOWN
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, template
@ -52,11 +47,8 @@ from homeassistant.util.unit_conversion import (
)
from .coordinator import TriggerUpdateCoordinator
from .template_entity import (
TemplateEntity,
make_template_entity_common_modern_schema,
rewrite_common_legacy_to_modern_conf,
)
from .helpers import async_setup_template_platform
from .template_entity import TemplateEntity, make_template_entity_common_modern_schema
from .trigger_entity import TriggerEntity
CHECK_FORECAST_KEYS = (
@ -138,33 +130,6 @@ WEATHER_SCHEMA = vol.Schema(
PLATFORM_SCHEMA = WEATHER_PLATFORM_SCHEMA.extend(WEATHER_SCHEMA.schema)
@callback
def _async_create_template_tracking_entities(
async_add_entities: AddEntitiesCallback,
hass: HomeAssistant,
definitions: list[dict],
unique_id_prefix: str | None,
) -> None:
"""Create the weather entities."""
entities = []
for entity_conf in definitions:
unique_id = entity_conf.get(CONF_UNIQUE_ID)
if unique_id and unique_id_prefix:
unique_id = f"{unique_id_prefix}-{unique_id}"
entities.append(
WeatherTemplate(
hass,
entity_conf,
unique_id,
)
)
async_add_entities(entities)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@ -172,36 +137,19 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Template weather."""
if discovery_info is None:
config = rewrite_common_legacy_to_modern_conf(hass, config)
unique_id = config.get(CONF_UNIQUE_ID)
async_add_entities(
[
WeatherTemplate(
hass,
config,
unique_id,
)
]
)
return
if "coordinator" in discovery_info:
async_add_entities(
TriggerWeatherEntity(hass, discovery_info["coordinator"], config)
for config in discovery_info["entities"]
)
return
_async_create_template_tracking_entities(
async_add_entities,
await async_setup_template_platform(
hass,
discovery_info["entities"],
discovery_info["unique_id"],
WEATHER_DOMAIN,
config,
StateWeatherEntity,
TriggerWeatherEntity,
async_add_entities,
discovery_info,
{},
)
class WeatherTemplate(TemplateEntity, WeatherEntity):
class StateWeatherEntity(TemplateEntity, WeatherEntity):
"""Representation of a weather condition."""
_attr_should_poll = False

View File

@ -559,7 +559,7 @@ def setup_mock() -> Generator[Mock]:
"""Do setup of sensor mock."""
with patch(
"homeassistant.components.template.binary_sensor."
"BinarySensorTemplate._update_state"
"StateBinarySensorEntity._update_state"
) as _update_state:
yield _update_state

View File

@ -0,0 +1,344 @@
"""The tests for template helpers."""
import pytest
from homeassistant.components.template.alarm_control_panel import (
LEGACY_FIELDS as ALARM_CONTROL_PANEL_LEGACY_FIELDS,
)
from homeassistant.components.template.binary_sensor import (
LEGACY_FIELDS as BINARY_SENSOR_LEGACY_FIELDS,
)
from homeassistant.components.template.button import StateButtonEntity
from homeassistant.components.template.cover import LEGACY_FIELDS as COVER_LEGACY_FIELDS
from homeassistant.components.template.fan import LEGACY_FIELDS as FAN_LEGACY_FIELDS
from homeassistant.components.template.helpers import (
async_setup_template_platform,
rewrite_legacy_to_modern_config,
rewrite_legacy_to_modern_configs,
)
from homeassistant.components.template.light import LEGACY_FIELDS as LIGHT_LEGACY_FIELDS
from homeassistant.components.template.lock import LEGACY_FIELDS as LOCK_LEGACY_FIELDS
from homeassistant.components.template.sensor import (
LEGACY_FIELDS as SENSOR_LEGACY_FIELDS,
)
from homeassistant.components.template.switch import (
LEGACY_FIELDS as SWITCH_LEGACY_FIELDS,
)
from homeassistant.components.template.vacuum import (
LEGACY_FIELDS as VACUUM_LEGACY_FIELDS,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers.template import Template
@pytest.mark.parametrize(
("legacy_fields", "old_attr", "new_attr", "attr_template"),
[
(
LOCK_LEGACY_FIELDS,
"value_template",
"state",
"{{ 1 == 1 }}",
),
(
LOCK_LEGACY_FIELDS,
"code_format_template",
"code_format",
"{{ 'some format' }}",
),
],
)
async def test_legacy_to_modern_config(
hass: HomeAssistant,
legacy_fields,
old_attr: str,
new_attr: str,
attr_template: str,
) -> None:
"""Test the conversion of single legacy template to modern template."""
config = {
"friendly_name": "foo bar",
"unique_id": "foo-bar-entity",
"icon_template": "{{ 'mdi.abc' }}",
"entity_picture_template": "{{ 'mypicture.jpg' }}",
"availability_template": "{{ 1 == 1 }}",
old_attr: attr_template,
}
altered_configs = rewrite_legacy_to_modern_config(hass, config, legacy_fields)
assert {
"availability": Template("{{ 1 == 1 }}", hass),
"icon": Template("{{ 'mdi.abc' }}", hass),
"name": Template("foo bar", hass),
"picture": Template("{{ 'mypicture.jpg' }}", hass),
"unique_id": "foo-bar-entity",
new_attr: Template(attr_template, hass),
} == altered_configs
@pytest.mark.parametrize(
("legacy_fields", "old_attr", "new_attr", "attr_template"),
[
(
ALARM_CONTROL_PANEL_LEGACY_FIELDS,
"value_template",
"state",
"{{ 1 == 1 }}",
),
(
BINARY_SENSOR_LEGACY_FIELDS,
"value_template",
"state",
"{{ 1 == 1 }}",
),
(
COVER_LEGACY_FIELDS,
"value_template",
"state",
"{{ 1 == 1 }}",
),
(
COVER_LEGACY_FIELDS,
"position_template",
"position",
"{{ 100 }}",
),
(
COVER_LEGACY_FIELDS,
"tilt_template",
"tilt",
"{{ 100 }}",
),
(
FAN_LEGACY_FIELDS,
"value_template",
"state",
"{{ 1 == 1 }}",
),
(
FAN_LEGACY_FIELDS,
"direction_template",
"direction",
"{{ 1 == 1 }}",
),
(
FAN_LEGACY_FIELDS,
"oscillating_template",
"oscillating",
"{{ True }}",
),
(
FAN_LEGACY_FIELDS,
"percentage_template",
"percentage",
"{{ 100 }}",
),
(
FAN_LEGACY_FIELDS,
"preset_mode_template",
"preset_mode",
"{{ 'foo' }}",
),
(
LIGHT_LEGACY_FIELDS,
"value_template",
"state",
"{{ 1 == 1 }}",
),
(
LIGHT_LEGACY_FIELDS,
"rgb_template",
"rgb",
"{{ (255,255,255) }}",
),
(
LIGHT_LEGACY_FIELDS,
"rgbw_template",
"rgbw",
"{{ (255,255,255,255) }}",
),
(
LIGHT_LEGACY_FIELDS,
"rgbww_template",
"rgbww",
"{{ (255,255,255,255,255) }}",
),
(
LIGHT_LEGACY_FIELDS,
"effect_list_template",
"effect_list",
"{{ ['a', 'b'] }}",
),
(
LIGHT_LEGACY_FIELDS,
"effect_template",
"effect",
"{{ 'a' }}",
),
(
LIGHT_LEGACY_FIELDS,
"level_template",
"level",
"{{ 255 }}",
),
(
LIGHT_LEGACY_FIELDS,
"max_mireds_template",
"max_mireds",
"{{ 255 }}",
),
(
LIGHT_LEGACY_FIELDS,
"min_mireds_template",
"min_mireds",
"{{ 255 }}",
),
(
LIGHT_LEGACY_FIELDS,
"supports_transition_template",
"supports_transition",
"{{ True }}",
),
(
LIGHT_LEGACY_FIELDS,
"temperature_template",
"temperature",
"{{ 255 }}",
),
(
LIGHT_LEGACY_FIELDS,
"white_value_template",
"white_value",
"{{ 255 }}",
),
(
LIGHT_LEGACY_FIELDS,
"hs_template",
"hs",
"{{ (255, 255) }}",
),
(
LIGHT_LEGACY_FIELDS,
"color_template",
"hs",
"{{ (255, 255) }}",
),
(
SENSOR_LEGACY_FIELDS,
"value_template",
"state",
"{{ 1 == 1 }}",
),
(
SWITCH_LEGACY_FIELDS,
"value_template",
"state",
"{{ 1 == 1 }}",
),
(
VACUUM_LEGACY_FIELDS,
"value_template",
"state",
"{{ 1 == 1 }}",
),
(
VACUUM_LEGACY_FIELDS,
"battery_level_template",
"battery_level",
"{{ 100 }}",
),
(
VACUUM_LEGACY_FIELDS,
"fan_speed_template",
"fan_speed",
"{{ 7 }}",
),
],
)
async def test_legacy_to_modern_configs(
hass: HomeAssistant,
legacy_fields,
old_attr: str,
new_attr: str,
attr_template: str,
) -> None:
"""Test the conversion of legacy template to modern template."""
config = {
"foo": {
"friendly_name": "foo bar",
"unique_id": "foo-bar-entity",
"icon_template": "{{ 'mdi.abc' }}",
"entity_picture_template": "{{ 'mypicture.jpg' }}",
"availability_template": "{{ 1 == 1 }}",
old_attr: attr_template,
}
}
altered_configs = rewrite_legacy_to_modern_configs(hass, config, legacy_fields)
assert len(altered_configs) == 1
assert [
{
"availability": Template("{{ 1 == 1 }}", hass),
"icon": Template("{{ 'mdi.abc' }}", hass),
"name": Template("foo bar", hass),
"object_id": "foo",
"picture": Template("{{ 'mypicture.jpg' }}", hass),
"unique_id": "foo-bar-entity",
new_attr: Template(attr_template, hass),
}
] == altered_configs
@pytest.mark.parametrize(
"legacy_fields",
[
BINARY_SENSOR_LEGACY_FIELDS,
SENSOR_LEGACY_FIELDS,
],
)
async def test_friendly_name_template_legacy_to_modern_configs(
hass: HomeAssistant,
legacy_fields,
) -> None:
"""Test the conversion of friendly_name_tempalte in legacy template to modern template."""
config = {
"foo": {
"unique_id": "foo-bar-entity",
"icon_template": "{{ 'mdi.abc' }}",
"entity_picture_template": "{{ 'mypicture.jpg' }}",
"availability_template": "{{ 1 == 1 }}",
"friendly_name_template": "{{ 'foo bar' }}",
}
}
altered_configs = rewrite_legacy_to_modern_configs(hass, config, legacy_fields)
assert len(altered_configs) == 1
assert [
{
"availability": Template("{{ 1 == 1 }}", hass),
"icon": Template("{{ 'mdi.abc' }}", hass),
"object_id": "foo",
"picture": Template("{{ 'mypicture.jpg' }}", hass),
"unique_id": "foo-bar-entity",
"name": Template("{{ 'foo bar' }}", hass),
}
] == altered_configs
async def test_platform_not_ready(
hass: HomeAssistant,
) -> None:
"""Test async_setup_template_platform raises PlatformNotReady when trigger object is None."""
with pytest.raises(PlatformNotReady):
await async_setup_template_platform(
hass,
"button",
{},
StateButtonEntity,
None,
None,
{"coordinator": None, "entities": []},
)

View File

@ -17,7 +17,6 @@ from homeassistant.components.light import (
ColorMode,
LightEntityFeature,
)
from homeassistant.components.template.light import rewrite_legacy_to_modern_conf
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
@ -29,7 +28,6 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.template import Template
from homeassistant.setup import async_setup_component
from .conftest import ConfigurationStyle
@ -289,127 +287,6 @@ TEST_UNIQUE_ID_CONFIG = {
}
@pytest.mark.parametrize(
("old_attr", "new_attr", "attr_template"),
[
(
"value_template",
"state",
"{{ 1 == 1 }}",
),
(
"rgb_template",
"rgb",
"{{ (255,255,255) }}",
),
(
"rgbw_template",
"rgbw",
"{{ (255,255,255,255) }}",
),
(
"rgbww_template",
"rgbww",
"{{ (255,255,255,255,255) }}",
),
(
"effect_list_template",
"effect_list",
"{{ ['a', 'b'] }}",
),
(
"effect_template",
"effect",
"{{ 'a' }}",
),
(
"level_template",
"level",
"{{ 255 }}",
),
(
"max_mireds_template",
"max_mireds",
"{{ 255 }}",
),
(
"min_mireds_template",
"min_mireds",
"{{ 255 }}",
),
(
"supports_transition_template",
"supports_transition",
"{{ True }}",
),
(
"temperature_template",
"temperature",
"{{ 255 }}",
),
(
"white_value_template",
"white_value",
"{{ 255 }}",
),
(
"hs_template",
"hs",
"{{ (255, 255) }}",
),
(
"color_template",
"hs",
"{{ (255, 255) }}",
),
],
)
async def test_legacy_to_modern_config(
hass: HomeAssistant, old_attr: str, new_attr: str, attr_template: str
) -> None:
"""Test the conversion of legacy template to modern template."""
config = {
"foo": {
"friendly_name": "foo bar",
"unique_id": "foo-bar-light",
"icon_template": "{{ 'mdi.abc' }}",
"entity_picture_template": "{{ 'mypicture.jpg' }}",
"availability_template": "{{ 1 == 1 }}",
old_attr: attr_template,
**OPTIMISTIC_ON_OFF_LIGHT_CONFIG,
}
}
altered_configs = rewrite_legacy_to_modern_conf(hass, config)
assert len(altered_configs) == 1
assert [
{
"availability": Template("{{ 1 == 1 }}", hass),
"icon": Template("{{ 'mdi.abc' }}", hass),
"name": Template("foo bar", hass),
"object_id": "foo",
"picture": Template("{{ 'mypicture.jpg' }}", hass),
"turn_off": {
"data_template": {
"action": "turn_off",
"caller": "{{ this.entity_id }}",
},
"service": "test.automation",
},
"turn_on": {
"data_template": {
"action": "turn_on",
"caller": "{{ this.entity_id }}",
},
"service": "test.automation",
},
"unique_id": "foo-bar-light",
new_attr: Template(attr_template, hass),
}
] == altered_configs
async def async_setup_legacy_format(
hass: HomeAssistant, count: int, light_config: dict[str, Any]
) -> None:

View File

@ -7,7 +7,6 @@ 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 rewrite_legacy_to_modern_conf
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
@ -18,7 +17,6 @@ from homeassistant.const import (
)
from homeassistant.core import CoreState, HomeAssistant, ServiceCall, State
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.template import Template
from homeassistant.setup import async_setup_component
from .conftest import ConfigurationStyle, async_get_flow_preview_state
@ -306,37 +304,6 @@ async def setup_single_attribute_optimistic_switch(
)
async def test_legacy_to_modern_config(hass: HomeAssistant) -> None:
"""Test the conversion of legacy template to modern template."""
config = {
"foo": {
"friendly_name": "foo bar",
"value_template": "{{ 1 == 1 }}",
"unique_id": "foo-bar-switch",
"icon_template": "{{ 'mdi.abc' }}",
"entity_picture_template": "{{ 'mypicture.jpg' }}",
"availability_template": "{{ 1 == 1 }}",
**SWITCH_ACTIONS,
}
}
altered_configs = rewrite_legacy_to_modern_conf(hass, config)
assert len(altered_configs) == 1
assert [
{
"availability": Template("{{ 1 == 1 }}", hass),
"icon": Template("{{ 'mdi.abc' }}", hass),
"name": Template("foo bar", hass),
"object_id": "foo",
"picture": Template("{{ 'mypicture.jpg' }}", hass),
"turn_off": SWITCH_TURN_OFF,
"turn_on": SWITCH_TURN_ON,
"unique_id": "foo-bar-switch",
"state": Template("{{ 1 == 1 }}", hass),
}
] == altered_configs
@pytest.mark.parametrize(("count", "state_template"), [(1, "{{ True }}")])
@pytest.mark.parametrize(
"style",