Add initalize for abstract template entities (#147504)

This commit is contained in:
Petro31 2025-07-15 09:53:01 -04:00 committed by GitHub
parent b89b248b4c
commit c058561162
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 106 additions and 145 deletions

View File

@ -32,8 +32,6 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, selector, template
from homeassistant.helpers.device import async_device_info_to_link_from_device_id
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
AddEntitiesCallback,
@ -42,7 +40,7 @@ from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.script import Script
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import CONF_OBJECT_ID, DOMAIN
from .const import DOMAIN
from .coordinator import TriggerUpdateCoordinator
from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
@ -213,6 +211,8 @@ class AbstractTemplateAlarmControlPanel(
):
"""Representation of a templated Alarm Control Panel features."""
_entity_id_format = ENTITY_ID_FORMAT
# The super init is not called because TemplateEntity and TriggerEntity will call AbstractTemplateEntity.__init__.
# This ensures that the __init__ on AbstractTemplateEntity is not called twice.
def __init__(self, config: dict[str, Any]) -> None: # pylint: disable=super-init-not-called
@ -363,12 +363,8 @@ class StateAlarmControlPanelEntity(TemplateEntity, AbstractTemplateAlarmControlP
unique_id: str | None,
) -> None:
"""Initialize the panel."""
TemplateEntity.__init__(self, hass, config=config, unique_id=unique_id)
TemplateEntity.__init__(self, hass, config, unique_id)
AbstractTemplateAlarmControlPanel.__init__(self, config)
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, object_id, hass=hass
)
name = self._attr_name
if TYPE_CHECKING:
assert name is not None
@ -379,11 +375,6 @@ class StateAlarmControlPanelEntity(TemplateEntity, AbstractTemplateAlarmControlP
self.add_script(action_id, action_config, name, DOMAIN)
self._attr_supported_features |= supported_feature
self._attr_device_info = async_device_info_to_link_from_device_id(
hass,
config.get(CONF_DEVICE_ID),
)
async def async_added_to_hass(self) -> None:
"""Restore last state."""
await super().async_added_to_hass()
@ -434,11 +425,6 @@ class TriggerAlarmControlPanelEntity(TriggerEntity, AbstractTemplateAlarmControl
self.add_script(action_id, action_config, name, DOMAIN)
self._attr_supported_features |= supported_feature
self._attr_device_info = async_device_info_to_link_from_device_id(
hass,
config.get(CONF_DEVICE_ID),
)
async def async_added_to_hass(self) -> None:
"""Restore last state."""
await super().async_added_to_hass()

View File

@ -39,8 +39,6 @@ from homeassistant.const import (
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, selector, template
from homeassistant.helpers.device import async_device_info_to_link_from_device_id
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
AddEntitiesCallback,
@ -51,7 +49,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util
from . import TriggerUpdateCoordinator
from .const import CONF_AVAILABILITY_TEMPLATE, CONF_OBJECT_ID
from .const import CONF_AVAILABILITY_TEMPLATE
from .helpers import async_setup_template_platform
from .template_entity import TEMPLATE_ENTITY_COMMON_SCHEMA, TemplateEntity
from .trigger_entity import TriggerEntity
@ -161,6 +159,7 @@ class StateBinarySensorEntity(TemplateEntity, BinarySensorEntity, RestoreEntity)
"""A virtual binary sensor that triggers from another sensor."""
_attr_should_poll = False
_entity_id_format = ENTITY_ID_FORMAT
def __init__(
self,
@ -169,11 +168,7 @@ class StateBinarySensorEntity(TemplateEntity, BinarySensorEntity, RestoreEntity)
unique_id: str | None,
) -> None:
"""Initialize the Template binary sensor."""
super().__init__(hass, config=config, unique_id=unique_id)
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, object_id, hass=hass
)
TemplateEntity.__init__(self, hass, config, unique_id)
self._attr_device_class = config.get(CONF_DEVICE_CLASS)
self._template = config[CONF_STATE]
@ -182,10 +177,6 @@ class StateBinarySensorEntity(TemplateEntity, BinarySensorEntity, RestoreEntity)
self._delay_on_raw = config.get(CONF_DELAY_ON)
self._delay_off = None
self._delay_off_raw = config.get(CONF_DELAY_OFF)
self._attr_device_info = async_device_info_to_link_from_device_id(
hass,
config.get(CONF_DEVICE_ID),
)
async def async_added_to_hass(self) -> None:
"""Restore state."""
@ -258,6 +249,7 @@ class StateBinarySensorEntity(TemplateEntity, BinarySensorEntity, RestoreEntity)
class TriggerBinarySensorEntity(TriggerEntity, BinarySensorEntity, RestoreEntity):
"""Sensor entity based on trigger data."""
_entity_id_format = ENTITY_ID_FORMAT
domain = BINARY_SENSOR_DOMAIN
extra_template_keys = (CONF_STATE,)

View File

@ -3,12 +3,14 @@
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
import voluptuous as vol
from homeassistant.components.button import (
DEVICE_CLASSES_SCHEMA,
DOMAIN as BUTTON_DOMAIN,
ENTITY_ID_FORMAT,
ButtonEntity,
)
from homeassistant.config_entries import ConfigEntry
@ -84,6 +86,7 @@ class StateButtonEntity(TemplateEntity, ButtonEntity):
"""Representation of a template button."""
_attr_should_poll = False
_entity_id_format = ENTITY_ID_FORMAT
def __init__(
self,
@ -92,8 +95,11 @@ class StateButtonEntity(TemplateEntity, ButtonEntity):
unique_id: str | None,
) -> None:
"""Initialize the button."""
super().__init__(hass, config=config, unique_id=unique_id)
TemplateEntity.__init__(self, hass, config, unique_id)
if TYPE_CHECKING:
assert self._attr_name is not None
# Scripts can be an empty list, therefore we need to check for None
if (action := config.get(CONF_PRESS)) is not None:
self.add_script(CONF_PRESS, action, self._attr_name, DOMAIN)

View File

@ -32,12 +32,11 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import TriggerUpdateCoordinator
from .const import CONF_OBJECT_ID, DOMAIN
from .const import DOMAIN
from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import (
@ -162,6 +161,8 @@ async def async_setup_platform(
class AbstractTemplateCover(AbstractTemplateEntity, CoverEntity):
"""Representation of a template cover features."""
_entity_id_format = ENTITY_ID_FORMAT
# The super init is not called because TemplateEntity and TriggerEntity will call AbstractTemplateEntity.__init__.
# This ensures that the __init__ on AbstractTemplateEntity is not called twice.
def __init__(self, config: dict[str, Any]) -> None: # pylint: disable=super-init-not-called
@ -397,12 +398,8 @@ class StateCoverEntity(TemplateEntity, AbstractTemplateCover):
unique_id,
) -> None:
"""Initialize the Template cover."""
TemplateEntity.__init__(self, hass, config=config, unique_id=unique_id)
TemplateEntity.__init__(self, hass, config, unique_id)
AbstractTemplateCover.__init__(self, config)
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, object_id, hass=hass
)
name = self._attr_name
if TYPE_CHECKING:
assert name is not None

View File

@ -3,21 +3,39 @@
from collections.abc import Sequence
from typing import Any
from homeassistant.const import CONF_DEVICE_ID
from homeassistant.core import Context, HomeAssistant, callback
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.device import async_device_info_to_link_from_device_id
from homeassistant.helpers.entity import Entity, async_generate_entity_id
from homeassistant.helpers.script import Script, _VarsType
from homeassistant.helpers.template import TemplateStateFromEntityId
from homeassistant.helpers.typing import ConfigType
from .const import CONF_OBJECT_ID
class AbstractTemplateEntity(Entity):
"""Actions linked to a template entity."""
def __init__(self, hass: HomeAssistant) -> None:
_entity_id_format: str
def __init__(self, hass: HomeAssistant, config: ConfigType) -> None:
"""Initialize the entity."""
self.hass = hass
self._action_scripts: dict[str, Script] = {}
if self.hass:
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
self.entity_id = async_generate_entity_id(
self._entity_id_format, object_id, hass=self.hass
)
self._attr_device_info = async_device_info_to_link_from_device_id(
self.hass,
config.get(CONF_DEVICE_ID),
)
@property
def referenced_blueprint(self) -> str | None:
"""Return referenced blueprint or None."""

View File

@ -34,11 +34,10 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import CONF_OBJECT_ID, DOMAIN
from .const import DOMAIN
from .coordinator import TriggerUpdateCoordinator
from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
@ -154,6 +153,8 @@ async def async_setup_platform(
class AbstractTemplateFan(AbstractTemplateEntity, FanEntity):
"""Representation of a template fan features."""
_entity_id_format = ENTITY_ID_FORMAT
# The super init is not called because TemplateEntity and TriggerEntity will call AbstractTemplateEntity.__init__.
# This ensures that the __init__ on AbstractTemplateEntity is not called twice.
def __init__(self, config: dict[str, Any]) -> None: # pylint: disable=super-init-not-called
@ -436,12 +437,8 @@ class StateFanEntity(TemplateEntity, AbstractTemplateFan):
unique_id,
) -> None:
"""Initialize the fan."""
TemplateEntity.__init__(self, hass, config=config, unique_id=unique_id)
TemplateEntity.__init__(self, hass, config, unique_id)
AbstractTemplateFan.__init__(self, config)
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, object_id, hass=hass
)
name = self._attr_name
if TYPE_CHECKING:
assert name is not None

View File

@ -7,7 +7,11 @@ from typing import Any
import voluptuous as vol
from homeassistant.components.image import DOMAIN as IMAGE_DOMAIN, ImageEntity
from homeassistant.components.image import (
DOMAIN as IMAGE_DOMAIN,
ENTITY_ID_FORMAT,
ImageEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_DEVICE_ID, CONF_NAME, CONF_URL, CONF_VERIFY_SSL
from homeassistant.core import HomeAssistant, callback
@ -91,6 +95,7 @@ class StateImageEntity(TemplateEntity, ImageEntity):
_attr_should_poll = False
_attr_image_url: str | None = None
_entity_id_format = ENTITY_ID_FORMAT
def __init__(
self,
@ -99,7 +104,7 @@ class StateImageEntity(TemplateEntity, ImageEntity):
unique_id: str | None,
) -> None:
"""Initialize the image."""
TemplateEntity.__init__(self, hass, config=config, unique_id=unique_id)
TemplateEntity.__init__(self, hass, config, unique_id)
ImageEntity.__init__(self, hass, config[CONF_VERIFY_SSL])
self._url_template = config[CONF_URL]
self._attr_device_info = async_device_info_to_link_from_device_id(
@ -135,6 +140,7 @@ class TriggerImageEntity(TriggerEntity, ImageEntity):
"""Image entity based on trigger data."""
_attr_image_url: str | None = None
_entity_id_format = ENTITY_ID_FORMAT
domain = IMAGE_DOMAIN
extra_template_keys = (CONF_URL,)

View File

@ -43,13 +43,12 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import color as color_util
from . import TriggerUpdateCoordinator
from .const import CONF_OBJECT_ID, DOMAIN
from .const import DOMAIN
from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
from .template_entity import (
@ -215,6 +214,8 @@ async def async_setup_platform(
class AbstractTemplateLight(AbstractTemplateEntity, LightEntity):
"""Representation of a template lights features."""
_entity_id_format = ENTITY_ID_FORMAT
# The super init is not called because TemplateEntity and TriggerEntity will call AbstractTemplateEntity.__init__.
# This ensures that the __init__ on AbstractTemplateEntity is not called twice.
def __init__( # pylint: disable=super-init-not-called
@ -893,12 +894,8 @@ class StateLightEntity(TemplateEntity, AbstractTemplateLight):
unique_id: str | None,
) -> None:
"""Initialize the light."""
TemplateEntity.__init__(self, hass, config=config, unique_id=unique_id)
TemplateEntity.__init__(self, hass, config, unique_id)
AbstractTemplateLight.__init__(self, config)
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, object_id, hass=hass
)
name = self._attr_name
if TYPE_CHECKING:
assert name is not None

View File

@ -9,6 +9,7 @@ import voluptuous as vol
from homeassistant.components.lock import (
DOMAIN as LOCK_DOMAIN,
ENTITY_ID_FORMAT,
PLATFORM_SCHEMA as LOCK_PLATFORM_SCHEMA,
LockEntity,
LockEntityFeature,
@ -104,6 +105,8 @@ async def async_setup_platform(
class AbstractTemplateLock(AbstractTemplateEntity, LockEntity):
"""Representation of a template lock features."""
_entity_id_format = ENTITY_ID_FORMAT
# The super init is not called because TemplateEntity and TriggerEntity will call AbstractTemplateEntity.__init__.
# This ensures that the __init__ on AbstractTemplateEntity is not called twice.
def __init__(self, config: dict[str, Any]) -> None: # pylint: disable=super-init-not-called
@ -283,7 +286,7 @@ class StateLockEntity(TemplateEntity, AbstractTemplateLock):
unique_id: str | None,
) -> None:
"""Initialize the lock."""
TemplateEntity.__init__(self, hass, config=config, unique_id=unique_id)
TemplateEntity.__init__(self, hass, config, unique_id)
AbstractTemplateLock.__init__(self, config)
name = self._attr_name
if TYPE_CHECKING:

View File

@ -3,7 +3,7 @@
from __future__ import annotations
import logging
from typing import Any
from typing import TYPE_CHECKING, Any
import voluptuous as vol
@ -13,6 +13,7 @@ from homeassistant.components.number import (
DEFAULT_MIN_VALUE,
DEFAULT_STEP,
DOMAIN as NUMBER_DOMAIN,
ENTITY_ID_FORMAT,
NumberEntity,
)
from homeassistant.config_entries import ConfigEntry
@ -25,7 +26,6 @@ from homeassistant.const import (
)
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
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
AddEntitiesCallback,
@ -115,6 +115,7 @@ class StateNumberEntity(TemplateEntity, NumberEntity):
"""Representation of a template number."""
_attr_should_poll = False
_entity_id_format = ENTITY_ID_FORMAT
def __init__(
self,
@ -123,8 +124,10 @@ class StateNumberEntity(TemplateEntity, NumberEntity):
unique_id: str | None,
) -> None:
"""Initialize the number."""
super().__init__(hass, config=config, unique_id=unique_id)
TemplateEntity.__init__(self, hass, config, unique_id)
if TYPE_CHECKING:
assert self._attr_name is not None
self._value_template = config[CONF_STATE]
self.add_script(CONF_SET_VALUE, config[CONF_SET_VALUE], self._attr_name, DOMAIN)
@ -136,10 +139,6 @@ class StateNumberEntity(TemplateEntity, NumberEntity):
self._attr_native_step = DEFAULT_STEP
self._attr_native_min_value = DEFAULT_MIN_VALUE
self._attr_native_max_value = DEFAULT_MAX_VALUE
self._attr_device_info = async_device_info_to_link_from_device_id(
hass,
config.get(CONF_DEVICE_ID),
)
@callback
def _async_setup_templates(self) -> None:
@ -188,6 +187,7 @@ class StateNumberEntity(TemplateEntity, NumberEntity):
class TriggerNumberEntity(TriggerEntity, NumberEntity):
"""Number entity based on trigger data."""
_entity_id_format = ENTITY_ID_FORMAT
domain = NUMBER_DOMAIN
extra_template_keys = (
CONF_STATE,

View File

@ -11,13 +11,13 @@ from homeassistant.components.select import (
ATTR_OPTION,
ATTR_OPTIONS,
DOMAIN as SELECT_DOMAIN,
ENTITY_ID_FORMAT,
SelectEntity,
)
from homeassistant.config_entries import ConfigEntry
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
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
AddEntitiesCallback,
@ -93,6 +93,8 @@ async def async_setup_entry(
class AbstractTemplateSelect(AbstractTemplateEntity, SelectEntity):
"""Representation of a template select features."""
_entity_id_format = ENTITY_ID_FORMAT
# The super init is not called because TemplateEntity and TriggerEntity will call AbstractTemplateEntity.__init__.
# This ensures that the __init__ on AbstractTemplateEntity is not called twice.
def __init__(self, config: dict[str, Any]) -> None: # pylint: disable=super-init-not-called
@ -132,7 +134,7 @@ class TemplateSelect(TemplateEntity, AbstractTemplateSelect):
unique_id: str | None,
) -> None:
"""Initialize the select."""
TemplateEntity.__init__(self, hass, config=config, unique_id=unique_id)
TemplateEntity.__init__(self, hass, config, unique_id)
AbstractTemplateSelect.__init__(self, config)
name = self._attr_name
@ -142,11 +144,6 @@ class TemplateSelect(TemplateEntity, AbstractTemplateSelect):
if (select_option := config.get(CONF_SELECT_OPTION)) is not None:
self.add_script(CONF_SELECT_OPTION, select_option, name, DOMAIN)
self._attr_device_info = async_device_info_to_link_from_device_id(
hass,
config.get(CONF_DEVICE_ID),
)
@callback
def _async_setup_templates(self) -> None:
"""Set up templates."""

View File

@ -44,8 +44,6 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, selector, template
from homeassistant.helpers.device import async_device_info_to_link_from_device_id
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
AddEntitiesCallback,
@ -55,7 +53,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import dt as dt_util
from . import TriggerUpdateCoordinator
from .const import CONF_ATTRIBUTE_TEMPLATES, CONF_AVAILABILITY_TEMPLATE, CONF_OBJECT_ID
from .const import CONF_ATTRIBUTE_TEMPLATES, CONF_AVAILABILITY_TEMPLATE
from .helpers import async_setup_template_platform
from .template_entity import TEMPLATE_ENTITY_COMMON_SCHEMA, TemplateEntity
from .trigger_entity import TriggerEntity
@ -199,6 +197,7 @@ class StateSensorEntity(TemplateEntity, SensorEntity):
"""Representation of a Template Sensor."""
_attr_should_poll = False
_entity_id_format = ENTITY_ID_FORMAT
def __init__(
self,
@ -207,7 +206,7 @@ class StateSensorEntity(TemplateEntity, SensorEntity):
unique_id: str | None,
) -> None:
"""Initialize the sensor."""
super().__init__(hass, config=config, fallback_name=None, unique_id=unique_id)
super().__init__(hass, config, unique_id)
self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT)
self._attr_device_class = config.get(CONF_DEVICE_CLASS)
self._attr_state_class = config.get(CONF_STATE_CLASS)
@ -215,14 +214,6 @@ class StateSensorEntity(TemplateEntity, SensorEntity):
self._attr_last_reset_template: template.Template | None = config.get(
ATTR_LAST_RESET
)
self._attr_device_info = async_device_info_to_link_from_device_id(
hass,
config.get(CONF_DEVICE_ID),
)
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, object_id, hass=hass
)
@callback
def _async_setup_templates(self) -> None:
@ -266,6 +257,7 @@ class StateSensorEntity(TemplateEntity, SensorEntity):
class TriggerSensorEntity(TriggerEntity, RestoreSensor):
"""Sensor entity based on trigger data."""
_entity_id_format = ENTITY_ID_FORMAT
domain = SENSOR_DOMAIN
extra_template_keys = (CONF_STATE,)

View File

@ -30,8 +30,6 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, selector, template
from homeassistant.helpers.device import async_device_info_to_link_from_device_id
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
AddEntitiesCallback,
@ -40,7 +38,7 @@ from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import TriggerUpdateCoordinator
from .const import CONF_OBJECT_ID, CONF_TURN_OFF, CONF_TURN_ON, DOMAIN
from .const import CONF_TURN_OFF, CONF_TURN_ON, DOMAIN
from .helpers import async_setup_template_platform
from .template_entity import (
TEMPLATE_ENTITY_COMMON_SCHEMA_LEGACY,
@ -154,6 +152,7 @@ class StateSwitchEntity(TemplateEntity, SwitchEntity, RestoreEntity):
"""Representation of a Template switch."""
_attr_should_poll = False
_entity_id_format = ENTITY_ID_FORMAT
def __init__(
self,
@ -162,11 +161,8 @@ class StateSwitchEntity(TemplateEntity, SwitchEntity, RestoreEntity):
unique_id: str | None,
) -> None:
"""Initialize the Template switch."""
super().__init__(hass, config=config, unique_id=unique_id)
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, object_id, hass=hass
)
super().__init__(hass, config, unique_id)
name = self._attr_name
if TYPE_CHECKING:
assert name is not None
@ -180,10 +176,6 @@ class StateSwitchEntity(TemplateEntity, SwitchEntity, RestoreEntity):
self._state: bool | None = False
self._attr_assumed_state = self._template is None
self._attr_device_info = async_device_info_to_link_from_device_id(
hass,
config.get(CONF_DEVICE_ID),
)
@callback
def _update_state(self, result):
@ -246,6 +238,7 @@ class StateSwitchEntity(TemplateEntity, SwitchEntity, RestoreEntity):
class TriggerSwitchEntity(TriggerEntity, SwitchEntity, RestoreEntity):
"""Switch entity based on trigger data."""
_entity_id_format = ENTITY_ID_FORMAT
domain = SWITCH_DOMAIN
def __init__(
@ -256,6 +249,7 @@ class TriggerSwitchEntity(TriggerEntity, SwitchEntity, RestoreEntity):
) -> None:
"""Initialize the entity."""
super().__init__(hass, coordinator, config)
name = self._rendered.get(CONF_NAME, DEFAULT_NAME)
self._template = config.get(CONF_STATE)
if on_action := config.get(CONF_TURN_ON):
@ -268,11 +262,6 @@ class TriggerSwitchEntity(TriggerEntity, SwitchEntity, RestoreEntity):
self._to_render_simple.append(CONF_STATE)
self._parse_result.add(CONF_STATE)
self._attr_device_info = async_device_info_to_link_from_device_id(
hass,
config.get(CONF_DEVICE_ID),
)
async def async_added_to_hass(self) -> None:
"""Restore last state."""
await super().async_added_to_hass()

View File

@ -240,17 +240,11 @@ class TemplateEntity(AbstractTemplateEntity):
def __init__(
self,
hass: HomeAssistant,
*,
availability_template: Template | None = None,
icon_template: Template | None = None,
entity_picture_template: Template | None = None,
attribute_templates: dict[str, Template] | None = None,
config: ConfigType | None = None,
fallback_name: str | None = None,
unique_id: str | None = None,
config: ConfigType,
unique_id: str | None,
) -> None:
"""Template Entity."""
AbstractTemplateEntity.__init__(self, hass)
AbstractTemplateEntity.__init__(self, hass, config)
self._template_attrs: dict[Template, list[_TemplateAttribute]] = {}
self._template_result_info: TrackTemplateResultInfo | None = None
self._attr_extra_state_attributes = {}
@ -269,15 +263,6 @@ class TemplateEntity(AbstractTemplateEntity):
| None
) = None
self._run_variables: ScriptVariables | dict
if config is None:
self._attribute_templates = attribute_templates
self._availability_template = availability_template
self._icon_template = icon_template
self._entity_picture_template = entity_picture_template
self._friendly_name_template = None
self._run_variables = {}
self._blueprint_inputs = None
else:
self._attribute_templates = config.get(CONF_ATTRIBUTES)
self._availability_template = config.get(CONF_AVAILABILITY)
self._icon_template = config.get(CONF_ICON)
@ -302,7 +287,7 @@ class TemplateEntity(AbstractTemplateEntity):
variables = {"this": DummyState()}
# Try to render the name as it can influence the entity ID
self._attr_name = fallback_name
self._attr_name = None
if self._friendly_name_template:
with contextlib.suppress(TemplateError):
self._attr_name = self._friendly_name_template.async_render(

View File

@ -30,7 +30,7 @@ class TriggerEntity( # pylint: disable=hass-enforce-class-module
"""Initialize the entity."""
CoordinatorEntity.__init__(self, coordinator)
TriggerBaseEntity.__init__(self, hass, config)
AbstractTemplateEntity.__init__(self, hass)
AbstractTemplateEntity.__init__(self, hass, config)
self._state_render_error = False

View File

@ -34,11 +34,10 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import CONF_OBJECT_ID, DOMAIN
from .const import DOMAIN
from .coordinator import TriggerUpdateCoordinator
from .entity import AbstractTemplateEntity
from .helpers import async_setup_template_platform
@ -147,6 +146,8 @@ async def async_setup_platform(
class AbstractTemplateVacuum(AbstractTemplateEntity, StateVacuumEntity):
"""Representation of a template vacuum features."""
_entity_id_format = ENTITY_ID_FORMAT
# The super init is not called because TemplateEntity and TriggerEntity will call AbstractTemplateEntity.__init__.
# This ensures that the __init__ on AbstractTemplateEntity is not called twice.
def __init__(self, config: dict[str, Any]) -> None: # pylint: disable=super-init-not-called
@ -302,12 +303,8 @@ class TemplateStateVacuumEntity(TemplateEntity, AbstractTemplateVacuum):
unique_id,
) -> None:
"""Initialize the vacuum."""
TemplateEntity.__init__(self, hass, config=config, unique_id=unique_id)
TemplateEntity.__init__(self, hass, config, unique_id)
AbstractTemplateVacuum.__init__(self, config)
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, object_id, hass=hass
)
name = self._attr_name
if TYPE_CHECKING:
assert name is not None

View File

@ -35,7 +35,6 @@ from homeassistant.const import CONF_TEMPERATURE_UNIT, STATE_UNAVAILABLE, STATE_
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.restore_state import ExtraStoredData, RestoreEntity
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
@ -153,6 +152,7 @@ class StateWeatherEntity(TemplateEntity, WeatherEntity):
"""Representation of a weather condition."""
_attr_should_poll = False
_entity_id_format = ENTITY_ID_FORMAT
def __init__(
self,
@ -161,9 +161,8 @@ class StateWeatherEntity(TemplateEntity, WeatherEntity):
unique_id: str | None,
) -> None:
"""Initialize the Template weather."""
super().__init__(hass, config=config, unique_id=unique_id)
super().__init__(hass, config, unique_id)
name = self._attr_name
self._condition_template = config[CONF_CONDITION_TEMPLATE]
self._temperature_template = config[CONF_TEMPERATURE_TEMPLATE]
self._humidity_template = config[CONF_HUMIDITY_TEMPLATE]
@ -191,8 +190,6 @@ class StateWeatherEntity(TemplateEntity, WeatherEntity):
self._attr_native_visibility_unit = config.get(CONF_VISIBILITY_UNIT)
self._attr_native_wind_speed_unit = config.get(CONF_WIND_SPEED_UNIT)
self.entity_id = async_generate_entity_id(ENTITY_ID_FORMAT, name, hass=hass)
self._condition = None
self._temperature = None
self._humidity = None
@ -486,6 +483,7 @@ class WeatherExtraStoredData(ExtraStoredData):
class TriggerWeatherEntity(TriggerEntity, WeatherEntity, RestoreEntity):
"""Sensor entity based on trigger data."""
_entity_id_format = ENTITY_ID_FORMAT
domain = WEATHER_DOMAIN
extra_template_keys = (
CONF_CONDITION_TEMPLATE,
@ -501,6 +499,7 @@ class TriggerWeatherEntity(TriggerEntity, WeatherEntity, RestoreEntity):
) -> None:
"""Initialize."""
super().__init__(hass, coordinator, config)
self._attr_native_precipitation_unit = config.get(CONF_PRECIPITATION_UNIT)
self._attr_native_pressure_unit = config.get(CONF_PRESSURE_UNIT)
self._attr_native_temperature_unit = config.get(CONF_TEMPERATURE_UNIT)

View File

@ -9,7 +9,7 @@ from homeassistant.core import HomeAssistant
async def test_template_entity_not_implemented(hass: HomeAssistant) -> None:
"""Test abstract template entity raises not implemented error."""
entity = abstract_entity.AbstractTemplateEntity(None)
entity = abstract_entity.AbstractTemplateEntity(None, {})
with pytest.raises(NotImplementedError):
_ = entity.referenced_blueprint

View File

@ -1141,7 +1141,7 @@ async def test_duplicate_templates(hass: HomeAssistant) -> None:
"unique_id": "listening-test-event",
"trigger": {"platform": "event", "event_type": "test_event"},
"sensors": {
"hello": {
"hello_name": {
"friendly_name": "Hello Name",
"unique_id": "hello_name-id",
"device_class": "battery",
@ -1360,7 +1360,7 @@ async def test_trigger_conditional_entity_invalid_condition(
{
"trigger": {"platform": "event", "event_type": "test_event"},
"sensors": {
"hello": {
"hello_name": {
"friendly_name": "Hello Name",
"value_template": "{{ trigger.event.data.beer }}",
"entity_picture_template": "{{ '/local/dogs.png' }}",

View File

@ -9,7 +9,7 @@ from homeassistant.helpers import template
async def test_template_entity_requires_hass_set(hass: HomeAssistant) -> None:
"""Test template entity requires hass to be set before accepting templates."""
entity = template_entity.TemplateEntity(None)
entity = template_entity.TemplateEntity(None, {}, "something_unique")
with pytest.raises(ValueError, match="^hass cannot be None"):
entity.add_template_attribute("_hello", template.Template("Hello"))