diff --git a/homeassistant/components/template/alarm_control_panel.py b/homeassistant/components/template/alarm_control_panel.py index e802ea40ebb..f60253a17bb 100644 --- a/homeassistant/components/template/alarm_control_panel.py +++ b/homeassistant/components/template/alarm_control_panel.py @@ -189,20 +189,20 @@ class AlarmControlPanelTemplate(TemplateEntity, AlarmControlPanelEntity): return self._code_arm_required @callback - def _update_state(self, state): - if isinstance(state, TemplateError): + def _update_state(self, result): + if isinstance(result, TemplateError): self._state = None return # Validate state - if state in _VALID_STATES: - self._state = state - _LOGGER.debug("Valid state - %s", state) + if result in _VALID_STATES: + self._state = result + _LOGGER.debug("Valid state - %s", result) return _LOGGER.error( "Received invalid alarm panel state: %s. Expected: %s", - state, + result, ", ".join(_VALID_STATES), ) self._state = None diff --git a/homeassistant/components/template/binary_sensor.py b/homeassistant/components/template/binary_sensor.py index 8dba37ddace..c2b12f8a1bc 100644 --- a/homeassistant/components/template/binary_sensor.py +++ b/homeassistant/components/template/binary_sensor.py @@ -27,7 +27,7 @@ from homeassistant.helpers.event import async_call_later from homeassistant.helpers.template import result_as_boolean from .const import CONF_AVAILABILITY_TEMPLATE -from .template_entity import TemplateEntityWithAttributesAvailabilityAndImages +from .template_entity import TemplateEntity _LOGGER = logging.getLogger(__name__) @@ -93,9 +93,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= async_add_entities(sensors) -class BinarySensorTemplate( - TemplateEntityWithAttributesAvailabilityAndImages, BinarySensorEntity -): +class BinarySensorTemplate(TemplateEntity, BinarySensorEntity): """A virtual binary sensor that triggers from another sensor.""" def __init__( @@ -115,10 +113,10 @@ class BinarySensorTemplate( ): """Initialize the Template binary sensor.""" super().__init__( - attribute_templates, - availability_template, - icon_template, - entity_picture_template, + attribute_templates=attribute_templates, + availability_template=availability_template, + icon_template=icon_template, + entity_picture_template=entity_picture_template, ) self.entity_id = async_generate_entity_id(ENTITY_ID_FORMAT, device, hass=hass) self._name = friendly_name diff --git a/homeassistant/components/template/cover.py b/homeassistant/components/template/cover.py index 0f8dc9f39fc..ec0853894a0 100644 --- a/homeassistant/components/template/cover.py +++ b/homeassistant/components/template/cover.py @@ -38,7 +38,7 @@ from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.script import Script from .const import CONF_AVAILABILITY_TEMPLATE -from .template_entity import TemplateEntityWithAvailabilityAndImages +from .template_entity import TemplateEntity _LOGGER = logging.getLogger(__name__) _VALID_STATES = [STATE_OPEN, STATE_CLOSED, "true", "false"] @@ -148,7 +148,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= async_add_entities(covers) -class CoverTemplate(TemplateEntityWithAvailabilityAndImages, CoverEntity): +class CoverTemplate(TemplateEntity, CoverEntity): """Representation of a Template cover.""" def __init__( @@ -174,7 +174,9 @@ class CoverTemplate(TemplateEntityWithAvailabilityAndImages, CoverEntity): ): """Initialize the Template cover.""" super().__init__( - availability_template, icon_template, entity_picture_template, + availability_template=availability_template, + icon_template=icon_template, + entity_picture_template=entity_picture_template, ) self.entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, device_id, hass=hass diff --git a/homeassistant/components/template/fan.py b/homeassistant/components/template/fan.py index 78d4829b632..a6f8741aad9 100644 --- a/homeassistant/components/template/fan.py +++ b/homeassistant/components/template/fan.py @@ -35,7 +35,7 @@ from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.script import Script from .const import CONF_AVAILABILITY_TEMPLATE -from .template_entity import TemplateEntityWithAvailability +from .template_entity import TemplateEntity _LOGGER = logging.getLogger(__name__) @@ -125,7 +125,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= async_add_entities(fans) -class TemplateFan(TemplateEntityWithAvailability, FanEntity): +class TemplateFan(TemplateEntity, FanEntity): """A template fan component.""" def __init__( @@ -147,7 +147,7 @@ class TemplateFan(TemplateEntityWithAvailability, FanEntity): unique_id, ): """Initialize the fan.""" - super().__init__(availability_template) + super().__init__(availability_template=availability_template) self.hass = hass self.entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, device_id, hass=hass diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index b407514c3d3..7945c56b3f5 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -34,7 +34,7 @@ from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.script import Script from .const import CONF_AVAILABILITY_TEMPLATE -from .template_entity import TemplateEntityWithAvailabilityAndImages +from .template_entity import TemplateEntity _LOGGER = logging.getLogger(__name__) _VALID_STATES = [STATE_ON, STATE_OFF, "true", "false"] @@ -131,7 +131,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= async_add_entities(lights) -class LightTemplate(TemplateEntityWithAvailabilityAndImages, LightEntity): +class LightTemplate(TemplateEntity, LightEntity): """Representation of a templated Light, including dimmable.""" def __init__( @@ -157,7 +157,9 @@ class LightTemplate(TemplateEntityWithAvailabilityAndImages, LightEntity): ): """Initialize the light.""" super().__init__( - availability_template, icon_template, entity_picture_template, + availability_template=availability_template, + icon_template=icon_template, + entity_picture_template=entity_picture_template, ) self.entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, device_id, hass=hass diff --git a/homeassistant/components/template/lock.py b/homeassistant/components/template/lock.py index 288cc0ece7a..6097dcdb9e8 100644 --- a/homeassistant/components/template/lock.py +++ b/homeassistant/components/template/lock.py @@ -18,7 +18,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.script import Script from .const import CONF_AVAILABILITY_TEMPLATE -from .template_entity import TemplateEntityWithAvailability +from .template_entity import TemplateEntity _LOGGER = logging.getLogger(__name__) @@ -63,7 +63,7 @@ async def async_setup_platform(hass, config, async_add_devices, discovery_info=N ) -class TemplateLock(TemplateEntityWithAvailability, LockEntity): +class TemplateLock(TemplateEntity, LockEntity): """Representation of a template lock.""" def __init__( @@ -78,7 +78,7 @@ class TemplateLock(TemplateEntityWithAvailability, LockEntity): unique_id, ): """Initialize the lock.""" - super().__init__(availability_template) + super().__init__(availability_template=availability_template) self._state = None self._name = name self._state_template = value_template diff --git a/homeassistant/components/template/sensor.py b/homeassistant/components/template/sensor.py index 2b0aec1fd53..895a8e2785b 100644 --- a/homeassistant/components/template/sensor.py +++ b/homeassistant/components/template/sensor.py @@ -27,7 +27,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity, async_generate_entity_id from .const import CONF_AVAILABILITY_TEMPLATE -from .template_entity import TemplateEntityWithAttributesAvailabilityAndImages +from .template_entity import TemplateEntity CONF_ATTRIBUTE_TEMPLATES = "attribute_templates" @@ -94,7 +94,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= return True -class SensorTemplate(TemplateEntityWithAttributesAvailabilityAndImages, Entity): +class SensorTemplate(TemplateEntity, Entity): """Representation of a Template Sensor.""" def __init__( @@ -114,10 +114,10 @@ class SensorTemplate(TemplateEntityWithAttributesAvailabilityAndImages, Entity): ): """Initialize the sensor.""" super().__init__( - attribute_templates, - availability_template, - icon_template, - entity_picture_template, + attribute_templates=attribute_templates, + availability_template=availability_template, + icon_template=icon_template, + entity_picture_template=entity_picture_template, ) self.entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, device_id, hass=hass diff --git a/homeassistant/components/template/switch.py b/homeassistant/components/template/switch.py index a041ecfe146..c31c89861eb 100644 --- a/homeassistant/components/template/switch.py +++ b/homeassistant/components/template/switch.py @@ -27,7 +27,7 @@ from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.script import Script from .const import CONF_AVAILABILITY_TEMPLATE -from .template_entity import TemplateEntityWithAvailabilityAndImages +from .template_entity import TemplateEntity _LOGGER = logging.getLogger(__name__) _VALID_STATES = [STATE_ON, STATE_OFF, "true", "false"] @@ -86,9 +86,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= async_add_entities(switches) -class SwitchTemplate( - TemplateEntityWithAvailabilityAndImages, SwitchEntity, RestoreEntity -): +class SwitchTemplate(TemplateEntity, SwitchEntity, RestoreEntity): """Representation of a Template switch.""" def __init__( @@ -105,7 +103,11 @@ class SwitchTemplate( unique_id, ): """Initialize the Template switch.""" - super().__init__(availability_template, icon_template, entity_picture_template) + super().__init__( + availability_template=availability_template, + icon_template=icon_template, + entity_picture_template=entity_picture_template, + ) self.entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, device_id, hass=hass ) diff --git a/homeassistant/components/template/template_entity.py b/homeassistant/components/template/template_entity.py index 24ac1c64db1..406ee0f9953 100644 --- a/homeassistant/components/template/template_entity.py +++ b/homeassistant/components/template/template_entity.py @@ -8,7 +8,6 @@ import voluptuous as vol from homeassistant.core import EVENT_HOMEASSISTANT_START, callback from homeassistant.exceptions import TemplateError import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.config_validation import match_all from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import Event, async_track_template_result from homeassistant.helpers.template import Template, result_as_boolean @@ -24,7 +23,7 @@ class _TemplateAttribute: entity: Entity, attribute: str, template: Template, - validator: Callable[[Any], Any] = match_all, + validator: Callable[[Any], Any] = None, on_update: Optional[Callable[[Any], None]] = None, none_on_template_error: Optional[bool] = False, ): @@ -130,20 +129,82 @@ class _TemplateAttribute: class TemplateEntity(Entity): """Entity that uses templates to calculate attributes.""" - def __init__(self): + def __init__( + self, + *, + availability_template=None, + icon_template=None, + entity_picture_template=None, + attribute_templates=None, + ): """Template Entity.""" self._template_attrs = [] + self._attribute_templates = attribute_templates + self._attributes = {} + self._availability_template = availability_template + self._available = True + self._icon_template = icon_template + self._entity_picture_template = entity_picture_template + self._icon = None + self._entity_picture = None @property def should_poll(self): """No polling needed.""" return False + @callback + def _update_available(self, result): + if isinstance(result, TemplateError): + self._available = True + return + + self._available = result_as_boolean(result) + + @callback + def _update_state(self, result): + if self._availability_template: + return + + self._available = not isinstance(result, TemplateError) + + @property + def available(self) -> bool: + """Return if the device is available.""" + return self._available + + @property + def icon(self): + """Return the icon to use in the frontend, if any.""" + return self._icon + + @property + def entity_picture(self): + """Return the entity_picture to use in the frontend, if any.""" + return self._entity_picture + + @property + def device_state_attributes(self): + """Return the state attributes.""" + return self._attributes + + @callback + def _add_attribute_template(self, attribute_key, attribute_template): + """Create a template tracker for the attribute.""" + + def _update_attribute(result): + attr_result = None if isinstance(result, TemplateError) else result + self._attributes[attribute_key] = attr_result + + self.add_template_attribute( + attribute_key, attribute_template, None, _update_attribute + ) + def add_template_attribute( self, attribute: str, template: Template, - validator: Callable[[Any], Any] = match_all, + validator: Callable[[Any], Any] = None, on_update: Optional[Callable[[Any], None]] = None, none_on_template_error: bool = False, ) -> None: @@ -183,79 +244,13 @@ class TemplateEntity(Entity): async def async_added_to_hass(self) -> None: """Run when entity about to be added to hass.""" - self.hass.bus.async_listen_once( - EVENT_HOMEASSISTANT_START, self._async_template_startup - ) - - async def async_update(self) -> None: - """Call for forced update.""" - for attribute in self._template_attrs: - if attribute.async_update: - attribute.async_update() - - -class TemplateEntityWithAvailability(TemplateEntity): - """Entity that uses templates to calculate attributes with an availability template.""" - - def __init__(self, availability_template): - """Template Entity.""" - self._availability_template = availability_template - self._available = True - super().__init__() - - @callback - def _update_available(self, result): - if isinstance(result, TemplateError): - self._available = True - return - - self._available = result_as_boolean(result) - - @callback - def _update_state(self, result): - if self._availability_template: - return - - self._available = not isinstance(result, TemplateError) - - @property - def available(self) -> bool: - """Return if the device is available.""" - return self._available - - async def async_added_to_hass(self): - """Register callbacks.""" if self._availability_template is not None: self.add_template_attribute( "_available", self._availability_template, None, self._update_available ) - - await super().async_added_to_hass() - - -class TemplateEntityWithAvailabilityAndImages(TemplateEntityWithAvailability): - """Entity that uses templates to calculate attributes with an availability, icon, and images template.""" - - def __init__(self, availability_template, icon_template, entity_picture_template): - """Template Entity.""" - self._icon_template = icon_template - self._entity_picture_template = entity_picture_template - self._icon = None - self._entity_picture = None - super().__init__(availability_template) - - @property - def icon(self): - """Return the icon to use in the frontend, if any.""" - return self._icon - - @property - def entity_picture(self): - """Return the entity_picture to use in the frontend, if any.""" - return self._entity_picture - - async def async_added_to_hass(self): - """Register callbacks.""" + if self._attribute_templates is not None: + for key, value in self._attribute_templates.items(): + self._add_attribute_template(key, value) if self._icon_template is not None: self.add_template_attribute( "_icon", self._icon_template, vol.Or(cv.whitespace, cv.icon) @@ -265,47 +260,12 @@ class TemplateEntityWithAvailabilityAndImages(TemplateEntityWithAvailability): "_entity_picture", self._entity_picture_template ) - await super().async_added_to_hass() - - -class TemplateEntityWithAttributesAvailabilityAndImages( - TemplateEntityWithAvailabilityAndImages -): - """Entity that uses templates to calculate attributes with an attributes, availability, icon, and images template.""" - - def __init__( - self, - attribute_templates, - availability_template, - icon_template, - entity_picture_template, - ): - """Template Entity.""" - super().__init__(availability_template, icon_template, entity_picture_template) - self._attribute_templates = attribute_templates - self._attributes = {} - - @callback - def _add_attribute_template(self, attribute_key, attribute_template): - """Create a template tracker for the attribute.""" - - def _update_attribute(result): - attr_result = None if isinstance(result, TemplateError) else result - self._attributes[attribute_key] = attr_result - - self.add_template_attribute( - attribute_key, attribute_template, None, _update_attribute + self.hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_START, self._async_template_startup ) - @property - def device_state_attributes(self): - """Return the state attributes.""" - return self._attributes - - async def async_added_to_hass(self): - """Register callbacks.""" - - for key, value in self._attribute_templates.items(): - self._add_attribute_template(key, value) - - await super().async_added_to_hass() + async def async_update(self) -> None: + """Call for forced update.""" + for attribute in self._template_attrs: + if attribute.async_update: + attribute.async_update() diff --git a/homeassistant/components/template/vacuum.py b/homeassistant/components/template/vacuum.py index dc2a2713978..ce6202bdc67 100644 --- a/homeassistant/components/template/vacuum.py +++ b/homeassistant/components/template/vacuum.py @@ -44,7 +44,7 @@ from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.script import Script from .const import CONF_AVAILABILITY_TEMPLATE -from .template_entity import TemplateEntityWithAttributesAvailabilityAndImages +from .template_entity import TemplateEntity _LOGGER = logging.getLogger(__name__) @@ -141,9 +141,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= async_add_entities(vacuums) -class TemplateVacuum( - TemplateEntityWithAttributesAvailabilityAndImages, StateVacuumEntity -): +class TemplateVacuum(TemplateEntity, StateVacuumEntity): """A template vacuum component.""" def __init__( @@ -168,7 +166,8 @@ class TemplateVacuum( ): """Initialize the vacuum.""" super().__init__( - attribute_templates, availability_template, None, None, + attribute_templates=attribute_templates, + availability_template=availability_template, ) self.entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, device_id, hass=hass