diff --git a/homeassistant/components/template/binary_sensor.py b/homeassistant/components/template/binary_sensor.py index ee28c3228c8..f5198c4c4db 100644 --- a/homeassistant/components/template/binary_sensor.py +++ b/homeassistant/components/template/binary_sensor.py @@ -48,6 +48,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util import dt as dt_util from . import TriggerUpdateCoordinator +from .entity import AbstractTemplateEntity from .helpers import ( async_setup_template_entry, async_setup_template_platform, @@ -168,11 +169,27 @@ def async_create_preview_binary_sensor( ) -class StateBinarySensorEntity(TemplateEntity, BinarySensorEntity, RestoreEntity): +class AbstractTemplateBinarySensor( + AbstractTemplateEntity, BinarySensorEntity, RestoreEntity +): + """Representation of a template binary sensor 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 + """Initialize the features.""" + + self._attr_device_class = config.get(CONF_DEVICE_CLASS) + self._template: template.Template = config[CONF_STATE] + self._delay_cancel: CALLBACK_TYPE | None = None + + +class StateBinarySensorEntity(TemplateEntity, AbstractTemplateBinarySensor): """A virtual binary sensor that triggers from another sensor.""" _attr_should_poll = False - _entity_id_format = ENTITY_ID_FORMAT def __init__( self, @@ -182,19 +199,19 @@ class StateBinarySensorEntity(TemplateEntity, BinarySensorEntity, RestoreEntity) ) -> None: """Initialize the Template binary sensor.""" TemplateEntity.__init__(self, hass, config, unique_id) - - self._attr_device_class = config.get(CONF_DEVICE_CLASS) - self._template: template.Template = config[CONF_STATE] - self._delay_cancel = None + AbstractTemplateBinarySensor.__init__(self, config) self._delay_on = None - self._delay_on_raw = config.get(CONF_DELAY_ON) + self._delay_on_template = config.get(CONF_DELAY_ON) self._delay_off = None - self._delay_off_raw = config.get(CONF_DELAY_OFF) + self._delay_off_template = config.get(CONF_DELAY_OFF) async def async_added_to_hass(self) -> None: """Restore state.""" if ( - (self._delay_on_raw is not None or self._delay_off_raw is not None) + ( + self._delay_on_template is not None + or self._delay_off_template is not None + ) and (last_state := await self.async_get_last_state()) is not None and last_state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE) ): @@ -206,20 +223,20 @@ class StateBinarySensorEntity(TemplateEntity, BinarySensorEntity, RestoreEntity) """Set up templates.""" self.add_template_attribute("_state", self._template, None, self._update_state) - if self._delay_on_raw is not None: + if self._delay_on_template is not None: try: - self._delay_on = cv.positive_time_period(self._delay_on_raw) + self._delay_on = cv.positive_time_period(self._delay_on_template) except vol.Invalid: self.add_template_attribute( - "_delay_on", self._delay_on_raw, cv.positive_time_period + "_delay_on", self._delay_on_template, cv.positive_time_period ) - if self._delay_off_raw is not None: + if self._delay_off_template is not None: try: - self._delay_off = cv.positive_time_period(self._delay_off_raw) + self._delay_off = cv.positive_time_period(self._delay_off_template) except vol.Invalid: self.add_template_attribute( - "_delay_off", self._delay_off_raw, cv.positive_time_period + "_delay_off", self._delay_off_template, cv.positive_time_period ) super()._async_setup_templates() @@ -259,12 +276,10 @@ class StateBinarySensorEntity(TemplateEntity, BinarySensorEntity, RestoreEntity) self._delay_cancel = async_call_later(self.hass, delay, _set_state) -class TriggerBinarySensorEntity(TriggerEntity, BinarySensorEntity, RestoreEntity): +class TriggerBinarySensorEntity(TriggerEntity, AbstractTemplateBinarySensor): """Sensor entity based on trigger data.""" - _entity_id_format = ENTITY_ID_FORMAT domain = BINARY_SENSOR_DOMAIN - extra_template_keys = (CONF_STATE,) def __init__( self, @@ -273,7 +288,8 @@ class TriggerBinarySensorEntity(TriggerEntity, BinarySensorEntity, RestoreEntity config: dict, ) -> None: """Initialize the entity.""" - super().__init__(hass, coordinator, config) + TriggerEntity.__init__(self, hass, coordinator, config) + AbstractTemplateBinarySensor.__init__(self, config) for key in (CONF_STATE, CONF_DELAY_ON, CONF_DELAY_OFF, CONF_AUTO_OFF): if isinstance(config.get(key), template.Template): @@ -282,7 +298,6 @@ class TriggerBinarySensorEntity(TriggerEntity, BinarySensorEntity, RestoreEntity self._last_delay_from: bool | None = None self._last_delay_to: bool | None = None - self._delay_cancel: CALLBACK_TYPE | None = None self._auto_off_cancel: CALLBACK_TYPE | None = None self._auto_off_time: datetime | None = None