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 .const import CONF_OBJECT_ID, DOMAIN
from .coordinator import TriggerUpdateCoordinator from .coordinator import TriggerUpdateCoordinator
from .entity import AbstractTemplateEntity from .entity import AbstractTemplateEntity
from .template_entity import ( from .helpers import async_setup_template_platform
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS, from .template_entity import TemplateEntity, make_template_entity_common_modern_schema
TemplateEntity,
make_template_entity_common_modern_schema,
rewrite_common_legacy_to_modern_conf,
)
from .trigger_entity import TriggerEntity from .trigger_entity import TriggerEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -88,7 +84,7 @@ class TemplateCodeFormat(Enum):
text = CodeFormat.TEXT text = CodeFormat.TEXT
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | { LEGACY_FIELDS = {
CONF_VALUE_TEMPLATE: CONF_STATE, 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]: def rewrite_options_to_modern_conf(option_config: dict[str, dict]) -> dict[str, dict]:
"""Rewrite option configuration to modern configuration.""" """Rewrite option configuration to modern configuration."""
option_config = {**option_config} option_config = {**option_config}
@ -231,7 +179,7 @@ async def async_setup_entry(
validated_config = ALARM_CONTROL_PANEL_CONFIG_SCHEMA(_options) validated_config = ALARM_CONTROL_PANEL_CONFIG_SCHEMA(_options)
async_add_entities( async_add_entities(
[ [
AlarmControlPanelTemplate( StateAlarmControlPanelEntity(
hass, hass,
validated_config, validated_config,
config_entry.entry_id, config_entry.entry_id,
@ -247,27 +195,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the Template cover.""" """Set up the Template cover."""
if discovery_info is None: await async_setup_template_platform(
_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,
hass, hass,
discovery_info["entities"], ALARM_CONTROL_PANEL_DOMAIN,
discovery_info["unique_id"], 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.""" """Representation of a templated Alarm Control Panel."""
_attr_should_poll = False _attr_should_poll = False

View File

@ -24,9 +24,7 @@ from homeassistant.const import (
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
CONF_DEVICE_ID, CONF_DEVICE_ID,
CONF_ENTITY_PICTURE_TEMPLATE, CONF_ENTITY_PICTURE_TEMPLATE,
CONF_FRIENDLY_NAME,
CONF_FRIENDLY_NAME_TEMPLATE, CONF_FRIENDLY_NAME_TEMPLATE,
CONF_ICON,
CONF_ICON_TEMPLATE, CONF_ICON_TEMPLATE,
CONF_NAME, CONF_NAME,
CONF_SENSORS, CONF_SENSORS,
@ -53,18 +51,9 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from . import TriggerUpdateCoordinator from . import TriggerUpdateCoordinator
from .const import ( from .const import CONF_AVAILABILITY_TEMPLATE, CONF_OBJECT_ID
CONF_ATTRIBUTES, from .helpers import async_setup_template_platform
CONF_AVAILABILITY, from .template_entity import TEMPLATE_ENTITY_COMMON_SCHEMA, TemplateEntity
CONF_AVAILABILITY_TEMPLATE,
CONF_OBJECT_ID,
CONF_PICTURE,
)
from .template_entity import (
TEMPLATE_ENTITY_COMMON_SCHEMA,
TemplateEntity,
rewrite_common_legacy_to_modern_conf,
)
from .trigger_entity import TriggerEntity from .trigger_entity import TriggerEntity
CONF_DELAY_ON = "delay_on" CONF_DELAY_ON = "delay_on"
@ -73,12 +62,7 @@ CONF_AUTO_OFF = "auto_off"
CONF_ATTRIBUTE_TEMPLATES = "attribute_templates" CONF_ATTRIBUTE_TEMPLATES = "attribute_templates"
LEGACY_FIELDS = { 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_TEMPLATE: CONF_NAME,
CONF_FRIENDLY_NAME: CONF_NAME,
CONF_VALUE_TEMPLATE: CONF_STATE, 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( PLATFORM_SCHEMA = BINARY_SENSOR_PLATFORM_SCHEMA.extend(
{ {
vol.Required(CONF_SENSORS): cv.schema_with_slug_keys( 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( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
@ -185,27 +121,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the template binary sensors.""" """Set up the template binary sensors."""
if discovery_info is None: await async_setup_template_platform(
_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,
hass, hass,
discovery_info["entities"], BINARY_SENSOR_DOMAIN,
discovery_info["unique_id"], 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") _options.pop("template_type")
validated_config = BINARY_SENSOR_CONFIG_SCHEMA(_options) validated_config = BINARY_SENSOR_CONFIG_SCHEMA(_options)
async_add_entities( async_add_entities(
[BinarySensorTemplate(hass, validated_config, config_entry.entry_id)] [StateBinarySensorEntity(hass, validated_config, config_entry.entry_id)]
) )
@callback @callback
def async_create_preview_binary_sensor( def async_create_preview_binary_sensor(
hass: HomeAssistant, name: str, config: dict[str, Any] hass: HomeAssistant, name: str, config: dict[str, Any]
) -> BinarySensorTemplate: ) -> StateBinarySensorEntity:
"""Create a preview sensor.""" """Create a preview sensor."""
validated_config = BINARY_SENSOR_CONFIG_SCHEMA(config | {CONF_NAME: name}) 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.""" """A virtual binary sensor that triggers from another sensor."""
_attr_should_poll = False _attr_should_poll = False

View File

@ -3,20 +3,17 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import Any
import voluptuous as vol import voluptuous as vol
from homeassistant.components.button import DEVICE_CLASSES_SCHEMA, ButtonEntity from homeassistant.components.button import (
from homeassistant.config_entries import ConfigEntry DEVICE_CLASSES_SCHEMA,
from homeassistant.const import ( DOMAIN as BUTTON_DOMAIN,
CONF_DEVICE_CLASS, ButtonEntity,
CONF_DEVICE_ID,
CONF_NAME,
CONF_UNIQUE_ID,
) )
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.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import config_validation as cv, selector 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.device import async_device_info_to_link_from_device_id
from homeassistant.helpers.entity_platform import ( from homeassistant.helpers.entity_platform import (
@ -26,6 +23,7 @@ from homeassistant.helpers.entity_platform import (
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import CONF_PRESS, DOMAIN from .const import CONF_PRESS, DOMAIN
from .helpers import async_setup_template_platform
from .template_entity import TemplateEntity, make_template_entity_common_modern_schema from .template_entity import TemplateEntity, make_template_entity_common_modern_schema
_LOGGER = logging.getLogger(__name__) _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( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
@ -70,15 +55,14 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the template button.""" """Set up the template button."""
if not discovery_info or "coordinator" in discovery_info: await async_setup_template_platform(
raise PlatformNotReady( hass,
"The template button platform doesn't support trigger entities" BUTTON_DOMAIN,
) config,
StateButtonEntity,
async_add_entities( None,
await _async_create_entities( async_add_entities,
hass, discovery_info["entities"], discovery_info["unique_id"] discovery_info,
)
) )
@ -92,11 +76,11 @@ async def async_setup_entry(
_options.pop("template_type") _options.pop("template_type")
validated_config = CONFIG_BUTTON_SCHEMA(_options) validated_config = CONFIG_BUTTON_SCHEMA(_options)
async_add_entities( 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.""" """Representation of a template button."""
_attr_should_poll = False _attr_should_poll = False

View File

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

View File

@ -39,12 +39,11 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import TriggerUpdateCoordinator from . import TriggerUpdateCoordinator
from .const import CONF_OBJECT_ID, DOMAIN from .const import CONF_OBJECT_ID, DOMAIN
from .entity import AbstractTemplateEntity from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import ( from .template_entity import (
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY, TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY,
TemplateEntity, TemplateEntity,
make_template_entity_common_modern_schema, make_template_entity_common_modern_schema,
rewrite_common_legacy_to_modern_conf,
) )
from .trigger_entity import TriggerEntity from .trigger_entity import TriggerEntity
@ -85,7 +84,7 @@ TILT_FEATURES = (
| CoverEntityFeature.SET_TILT_POSITION | CoverEntityFeature.SET_TILT_POSITION
) )
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | { LEGACY_FIELDS = {
CONF_VALUE_TEMPLATE: CONF_STATE, CONF_VALUE_TEMPLATE: CONF_STATE,
CONF_POSITION_TEMPLATE: CONF_POSITION, CONF_POSITION_TEMPLATE: CONF_POSITION,
CONF_TILT_TEMPLATE: CONF_TILT, 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( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
@ -195,27 +146,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the Template cover.""" """Set up the Template cover."""
if discovery_info is None: await async_setup_template_platform(
_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,
hass, hass,
discovery_info["entities"], COVER_DOMAIN,
discovery_info["unique_id"], 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() self.async_write_ha_state()
class CoverTemplate(TemplateEntity, AbstractTemplateCover): class StateCoverEntity(TemplateEntity, AbstractTemplateCover):
"""Representation of a Template cover.""" """Representation of a Template cover."""
_attr_should_poll = False _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 .const import CONF_OBJECT_ID, DOMAIN
from .coordinator import TriggerUpdateCoordinator from .coordinator import TriggerUpdateCoordinator
from .entity import AbstractTemplateEntity from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import ( from .template_entity import (
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY, TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY,
TemplateEntity, TemplateEntity,
make_template_entity_common_modern_schema, make_template_entity_common_modern_schema,
rewrite_common_legacy_to_modern_conf,
) )
from .trigger_entity import TriggerEntity from .trigger_entity import TriggerEntity
@ -73,7 +72,7 @@ CONF_OSCILLATING = "oscillating"
CONF_PERCENTAGE = "percentage" CONF_PERCENTAGE = "percentage"
CONF_PRESET_MODE = "preset_mode" CONF_PRESET_MODE = "preset_mode"
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | { LEGACY_FIELDS = {
CONF_DIRECTION_TEMPLATE: CONF_DIRECTION, CONF_DIRECTION_TEMPLATE: CONF_DIRECTION,
CONF_OSCILLATING_TEMPLATE: CONF_OSCILLATING, CONF_OSCILLATING_TEMPLATE: CONF_OSCILLATING,
CONF_PERCENTAGE_TEMPLATE: CONF_PERCENTAGE, 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( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
@ -187,27 +138,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the template fans.""" """Set up the template fans."""
if discovery_info is None: await async_setup_template_platform(
_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,
hass, hass,
discovery_info["entities"], FAN_DOMAIN,
discovery_info["unique_id"], 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.""" """A template fan component."""
_attr_should_poll = False _attr_should_poll = False

View File

@ -1,19 +1,60 @@
"""Helpers for template integration.""" """Helpers for template integration."""
from collections.abc import Callable
import itertools
import logging import logging
from typing import Any
from homeassistant.components import blueprint 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.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.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 .entity import AbstractTemplateEntity
from .template_entity import TemplateEntity
from .trigger_entity import TriggerEntity
DATA_BLUEPRINTS = "template_blueprints" 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 @callback
@ -59,8 +100,131 @@ def async_get_blueprints(hass: HomeAssistant) -> blueprint.DomainBlueprints:
return blueprint.DomainBlueprints( return blueprint.DomainBlueprints(
hass, hass,
DOMAIN, DOMAIN,
LOGGER, _LOGGER,
_blueprint_in_use, _blueprint_in_use,
_reload_blueprint_templates, _reload_blueprint_templates,
TEMPLATE_BLUEPRINT_SCHEMA, 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.components.image import DOMAIN as IMAGE_DOMAIN, ImageEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import CONF_DEVICE_ID, CONF_NAME, CONF_URL, CONF_VERIFY_SSL
CONF_DEVICE_ID,
CONF_NAME,
CONF_UNIQUE_ID,
CONF_URL,
CONF_VERIFY_SSL,
)
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, selector 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 . import TriggerUpdateCoordinator
from .const import CONF_PICTURE from .const import CONF_PICTURE
from .helpers import async_setup_template_platform
from .template_entity import ( from .template_entity import (
TemplateEntity, TemplateEntity,
make_template_entity_common_modern_attributes_schema, 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( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
@ -79,23 +61,14 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the template image.""" """Set up the template image."""
if discovery_info is None: await async_setup_template_platform(
_LOGGER.warning( hass,
"Template image entities can only be configured under template:" IMAGE_DOMAIN,
) config,
return StateImageEntity,
TriggerImageEntity,
if "coordinator" in discovery_info: async_add_entities,
async_add_entities( discovery_info,
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"]
)
) )

View File

@ -51,12 +51,11 @@ from homeassistant.util import color as color_util
from . import TriggerUpdateCoordinator from . import TriggerUpdateCoordinator
from .const import CONF_OBJECT_ID, DOMAIN from .const import CONF_OBJECT_ID, DOMAIN
from .entity import AbstractTemplateEntity from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import ( from .template_entity import (
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY, TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY,
TemplateEntity, TemplateEntity,
make_template_entity_common_modern_schema, make_template_entity_common_modern_schema,
rewrite_common_legacy_to_modern_conf,
) )
from .trigger_entity import TriggerEntity from .trigger_entity import TriggerEntity
@ -103,7 +102,7 @@ CONF_WHITE_VALUE_TEMPLATE = "white_value_template"
DEFAULT_MIN_MIREDS = 153 DEFAULT_MIN_MIREDS = 153
DEFAULT_MAX_MIREDS = 500 DEFAULT_MAX_MIREDS = 500
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | { LEGACY_FIELDS = {
CONF_COLOR_ACTION: CONF_HS_ACTION, CONF_COLOR_ACTION: CONF_HS_ACTION,
CONF_COLOR_TEMPLATE: CONF_HS, CONF_COLOR_TEMPLATE: CONF_HS,
CONF_EFFECT_LIST_TEMPLATE: CONF_EFFECT_LIST, 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( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
@ -241,27 +199,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the template lights.""" """Set up the template lights."""
if discovery_info is None: await async_setup_template_platform(
_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,
hass, hass,
discovery_info["entities"], LIGHT_DOMAIN,
discovery_info["unique_id"], 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 self._attr_supported_features |= LightEntityFeature.TRANSITION
class LightTemplate(TemplateEntity, AbstractTemplateLight): class StateLightEntity(TemplateEntity, AbstractTemplateLight):
"""Representation of a templated Light, including dimmable.""" """Representation of a templated Light, including dimmable."""
_attr_should_poll = False _attr_should_poll = False

View File

@ -31,12 +31,11 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import CONF_PICTURE, DOMAIN from .const import CONF_PICTURE, DOMAIN
from .coordinator import TriggerUpdateCoordinator from .coordinator import TriggerUpdateCoordinator
from .entity import AbstractTemplateEntity from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import ( from .template_entity import (
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY, TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY,
TemplateEntity, TemplateEntity,
make_template_entity_common_modern_schema, make_template_entity_common_modern_schema,
rewrite_common_legacy_to_modern_conf,
) )
from .trigger_entity import TriggerEntity from .trigger_entity import TriggerEntity
@ -49,7 +48,7 @@ CONF_OPEN = "open"
DEFAULT_NAME = "Template Lock" DEFAULT_NAME = "Template Lock"
DEFAULT_OPTIMISTIC = False DEFAULT_OPTIMISTIC = False
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | { LEGACY_FIELDS = {
CONF_CODE_FORMAT_TEMPLATE: CONF_CODE_FORMAT, CONF_CODE_FORMAT_TEMPLATE: CONF_CODE_FORMAT,
CONF_VALUE_TEMPLATE: CONF_STATE, CONF_VALUE_TEMPLATE: CONF_STATE,
} }
@ -83,33 +82,6 @@ PLATFORM_SCHEMA = LOCK_PLATFORM_SCHEMA.extend(
).extend(TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY.schema) ).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( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
@ -117,27 +89,15 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the template fans.""" """Set up the template fans."""
if discovery_info is None: await async_setup_template_platform(
_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,
hass, hass,
discovery_info["entities"], LOCK_DOMAIN,
discovery_info["unique_id"], 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.""" """Representation of a template lock."""
_attr_should_poll = False _attr_should_poll = False

View File

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

View File

@ -14,13 +14,7 @@ from homeassistant.components.select import (
SelectEntity, SelectEntity,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import CONF_DEVICE_ID, CONF_NAME, CONF_OPTIMISTIC, CONF_STATE
CONF_DEVICE_ID,
CONF_NAME,
CONF_OPTIMISTIC,
CONF_STATE,
CONF_UNIQUE_ID,
)
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv, selector 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.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 . import TriggerUpdateCoordinator
from .const import DOMAIN from .const import DOMAIN
from .entity import AbstractTemplateEntity from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import TemplateEntity, make_template_entity_common_modern_schema from .template_entity import TemplateEntity, make_template_entity_common_modern_schema
from .trigger_entity import TriggerEntity 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( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
@ -85,23 +67,14 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the template select.""" """Set up the template select."""
if discovery_info is None: await async_setup_template_platform(
_LOGGER.warning( hass,
"Template select entities can only be configured under template:" SELECT_DOMAIN,
) config,
return TemplateSelect,
TriggerSelectEntity,
if "coordinator" in discovery_info: async_add_entities,
async_add_entities( discovery_info,
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"]
)
) )

View File

@ -56,16 +56,12 @@ from homeassistant.util import dt as dt_util
from . import TriggerUpdateCoordinator from . import TriggerUpdateCoordinator
from .const import CONF_ATTRIBUTE_TEMPLATES, CONF_AVAILABILITY_TEMPLATE, CONF_OBJECT_ID from .const import CONF_ATTRIBUTE_TEMPLATES, CONF_AVAILABILITY_TEMPLATE, CONF_OBJECT_ID
from .template_entity import ( from .helpers import async_setup_template_platform
TEMPLATE_ENTITY_COMMON_SCHEMA, from .template_entity import TEMPLATE_ENTITY_COMMON_SCHEMA, TemplateEntity
TemplateEntity,
rewrite_common_legacy_to_modern_conf,
)
from .trigger_entity import TriggerEntity from .trigger_entity import TriggerEntity
LEGACY_FIELDS = { LEGACY_FIELDS = {
CONF_FRIENDLY_NAME_TEMPLATE: CONF_NAME, CONF_FRIENDLY_NAME_TEMPLATE: CONF_NAME,
CONF_FRIENDLY_NAME: CONF_NAME,
CONF_VALUE_TEMPLATE: CONF_STATE, CONF_VALUE_TEMPLATE: CONF_STATE,
} }
@ -142,27 +138,6 @@ def extra_validation_checks(val):
return 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( PLATFORM_SCHEMA = vol.All(
SENSOR_PLATFORM_SCHEMA.extend( SENSOR_PLATFORM_SCHEMA.extend(
{ {
@ -177,33 +152,6 @@ PLATFORM_SCHEMA = vol.All(
_LOGGER = logging.getLogger(__name__) _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( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
@ -211,27 +159,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the template sensors.""" """Set up the template sensors."""
if discovery_info is None: await async_setup_template_platform(
_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,
hass, hass,
discovery_info["entities"], SENSOR_DOMAIN,
discovery_info["unique_id"], 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 = dict(config_entry.options)
_options.pop("template_type") _options.pop("template_type")
validated_config = SENSOR_CONFIG_SCHEMA(_options) 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 @callback
def async_create_preview_sensor( def async_create_preview_sensor(
hass: HomeAssistant, name: str, config: dict[str, Any] hass: HomeAssistant, name: str, config: dict[str, Any]
) -> SensorTemplate: ) -> StateSensorEntity:
"""Create a preview sensor.""" """Create a preview sensor."""
validated_config = SENSOR_CONFIG_SCHEMA(config | {CONF_NAME: name}) 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.""" """Representation of a Template Sensor."""
_attr_should_poll = False _attr_should_poll = False

View File

@ -41,18 +41,17 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import TriggerUpdateCoordinator from . import TriggerUpdateCoordinator
from .const import CONF_OBJECT_ID, CONF_TURN_OFF, CONF_TURN_ON, DOMAIN from .const import CONF_OBJECT_ID, CONF_TURN_OFF, CONF_TURN_ON, DOMAIN
from .helpers import async_setup_template_platform
from .template_entity import ( from .template_entity import (
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY, TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY,
TemplateEntity, TemplateEntity,
make_template_entity_common_modern_schema, make_template_entity_common_modern_schema,
rewrite_common_legacy_to_modern_conf,
) )
from .trigger_entity import TriggerEntity from .trigger_entity import TriggerEntity
_VALID_STATES = [STATE_ON, STATE_OFF, "true", "false"] _VALID_STATES = [STATE_ON, STATE_OFF, "true", "false"]
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | { LEGACY_FIELDS = {
CONF_VALUE_TEMPLATE: CONF_STATE, 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]: def rewrite_options_to_modern_conf(option_config: dict[str, dict]) -> dict[str, dict]:
"""Rewrite option configuration to modern configuration.""" """Rewrite option configuration to modern configuration."""
option_config = {**option_config} option_config = {**option_config}
@ -127,33 +105,6 @@ def rewrite_options_to_modern_conf(option_config: dict[str, dict]) -> dict[str,
return option_config 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( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
@ -161,27 +112,16 @@ async def async_setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the template switches.""" """Set up the template switches."""
if discovery_info is None: await async_setup_template_platform(
_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,
hass, hass,
discovery_info["entities"], SWITCH_DOMAIN,
discovery_info["unique_id"], 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.pop("template_type")
_options = rewrite_options_to_modern_conf(_options) _options = rewrite_options_to_modern_conf(_options)
validated_config = SWITCH_CONFIG_SCHEMA(_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 @callback
def async_create_preview_switch( def async_create_preview_switch(
hass: HomeAssistant, name: str, config: dict[str, Any] hass: HomeAssistant, name: str, config: dict[str, Any]
) -> SwitchTemplate: ) -> StateSwitchEntity:
"""Create a preview switch.""" """Create a preview switch."""
updated_config = rewrite_options_to_modern_conf(config) updated_config = rewrite_options_to_modern_conf(config)
validated_config = SWITCH_CONFIG_SCHEMA(updated_config | {CONF_NAME: name}) 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.""" """Representation of a Template switch."""
_attr_should_poll = False _attr_should_poll = False

View File

@ -4,7 +4,6 @@ from __future__ import annotations
from collections.abc import Callable, Mapping from collections.abc import Callable, Mapping
import contextlib import contextlib
import itertools
import logging import logging
from typing import Any, cast from typing import Any, cast
@ -14,7 +13,6 @@ import voluptuous as vol
from homeassistant.components.blueprint import CONF_USE_BLUEPRINT from homeassistant.components.blueprint import CONF_USE_BLUEPRINT
from homeassistant.const import ( from homeassistant.const import (
CONF_ENTITY_PICTURE_TEMPLATE, CONF_ENTITY_PICTURE_TEMPLATE,
CONF_FRIENDLY_NAME,
CONF_ICON, CONF_ICON,
CONF_ICON_TEMPLATE, CONF_ICON_TEMPLATE,
CONF_NAME, CONF_NAME,
@ -137,42 +135,6 @@ TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY = vol.Schema(
).extend(TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY.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: class _TemplateAttribute:
"""Attribute value linked to template result.""" """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 .const import CONF_OBJECT_ID, DOMAIN
from .coordinator import TriggerUpdateCoordinator from .coordinator import TriggerUpdateCoordinator
from .entity import AbstractTemplateEntity from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import ( from .template_entity import (
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
TEMPLATE_ENTITY_ATTRIBUTES_SCHEMA_LEGACY, TEMPLATE_ENTITY_ATTRIBUTES_SCHEMA_LEGACY,
TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY, TEMPLATE_ENTITY_AVAILABILITY_SCHEMA_LEGACY,
TemplateEntity, TemplateEntity,
make_template_entity_common_modern_attributes_schema, make_template_entity_common_modern_attributes_schema,
rewrite_common_legacy_to_modern_conf,
) )
from .trigger_entity import TriggerEntity from .trigger_entity import TriggerEntity
@ -72,7 +71,7 @@ _VALID_STATES = [
VacuumActivity.ERROR, VacuumActivity.ERROR,
] ]
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | { LEGACY_FIELDS = {
CONF_BATTERY_LEVEL_TEMPLATE: CONF_BATTERY_LEVEL, CONF_BATTERY_LEVEL_TEMPLATE: CONF_BATTERY_LEVEL,
CONF_FAN_SPEED_TEMPLATE: CONF_FAN_SPEED, CONF_FAN_SPEED_TEMPLATE: CONF_FAN_SPEED,
CONF_VALUE_TEMPLATE: CONF_STATE, 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( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the Template cover.""" """Set up the Template vacuum."""
if discovery_info is None: await async_setup_template_platform(
_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,
hass, hass,
discovery_info["entities"], VACUUM_DOMAIN,
discovery_info["unique_id"], 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 self._attr_fan_speed = None
class TemplateVacuum(TemplateEntity, AbstractTemplateVacuum): class TemplateStateVacuumEntity(TemplateEntity, AbstractTemplateVacuum):
"""A template vacuum component.""" """A template vacuum component."""
_attr_should_poll = False _attr_should_poll = False

View File

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

View File

@ -559,7 +559,7 @@ def setup_mock() -> Generator[Mock]:
"""Do setup of sensor mock.""" """Do setup of sensor mock."""
with patch( with patch(
"homeassistant.components.template.binary_sensor." "homeassistant.components.template.binary_sensor."
"BinarySensorTemplate._update_state" "StateBinarySensorEntity._update_state"
) as _update_state: ) as _update_state:
yield _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, ColorMode,
LightEntityFeature, LightEntityFeature,
) )
from homeassistant.components.template.light import rewrite_legacy_to_modern_conf
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
SERVICE_TURN_OFF, SERVICE_TURN_OFF,
@ -29,7 +28,6 @@ from homeassistant.const import (
) )
from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.template import Template
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from .conftest import ConfigurationStyle 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( async def async_setup_legacy_format(
hass: HomeAssistant, count: int, light_config: dict[str, Any] hass: HomeAssistant, count: int, light_config: dict[str, Any]
) -> None: ) -> None:

View File

@ -7,7 +7,6 @@ from syrupy.assertion import SnapshotAssertion
from homeassistant.components import switch, template from homeassistant.components import switch, template
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.components.template.switch import rewrite_legacy_to_modern_conf
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
SERVICE_TURN_OFF, SERVICE_TURN_OFF,
@ -18,7 +17,6 @@ from homeassistant.const import (
) )
from homeassistant.core import CoreState, HomeAssistant, ServiceCall, State from homeassistant.core import CoreState, HomeAssistant, ServiceCall, State
from homeassistant.helpers import device_registry as dr, entity_registry as er 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 homeassistant.setup import async_setup_component
from .conftest import ConfigurationStyle, async_get_flow_preview_state 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(("count", "state_template"), [(1, "{{ True }}")])
@pytest.mark.parametrize( @pytest.mark.parametrize(
"style", "style",