Support trigger-based template binary sensors (#49504)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
9410aefd0d
commit
1016d4ea28
6 changed files with 289 additions and 22 deletions
|
@ -1,14 +1,19 @@
|
|||
"""Support for exposing a templated binary sensor."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
DEVICE_CLASSES_SCHEMA,
|
||||
DOMAIN as BINARY_SENSOR_DOMAIN,
|
||||
ENTITY_ID_FORMAT,
|
||||
PLATFORM_SCHEMA,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
from homeassistant.components.template import TriggerUpdateCoordinator
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_FRIENDLY_NAME,
|
||||
|
@ -40,6 +45,7 @@ from .const import (
|
|||
CONF_PICTURE,
|
||||
)
|
||||
from .template_entity import TemplateEntity
|
||||
from .trigger_entity import TriggerEntity
|
||||
|
||||
CONF_DELAY_ON = "delay_on"
|
||||
CONF_DELAY_OFF = "delay_off"
|
||||
|
@ -168,8 +174,23 @@ def _async_create_template_tracking_entities(async_add_entities, hass, definitio
|
|||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the template binary sensors."""
|
||||
if discovery_info is None:
|
||||
_async_create_template_tracking_entities(
|
||||
async_add_entities,
|
||||
hass,
|
||||
rewrite_legacy_to_modern_conf(config[CONF_SENSORS]),
|
||||
)
|
||||
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, rewrite_legacy_to_modern_conf(config[CONF_SENSORS])
|
||||
async_add_entities, hass, discovery_info["entities"]
|
||||
)
|
||||
|
||||
|
||||
|
@ -283,7 +304,7 @@ class BinarySensorTemplate(TemplateEntity, BinarySensorEntity):
|
|||
self._state = state
|
||||
self.async_write_ha_state()
|
||||
|
||||
delay = (self._delay_on if state else self._delay_off).seconds
|
||||
delay = (self._delay_on if state else self._delay_off).total_seconds()
|
||||
# state with delay. Cancelled if template result changes.
|
||||
self._delay_cancel = async_call_later(self.hass, delay, _set_state)
|
||||
|
||||
|
@ -306,3 +327,83 @@ class BinarySensorTemplate(TemplateEntity, BinarySensorEntity):
|
|||
def device_class(self):
|
||||
"""Return the sensor class of the binary sensor."""
|
||||
return self._device_class
|
||||
|
||||
|
||||
class TriggerBinarySensorEntity(TriggerEntity, BinarySensorEntity):
|
||||
"""Sensor entity based on trigger data."""
|
||||
|
||||
domain = BINARY_SENSOR_DOMAIN
|
||||
extra_template_keys = (CONF_STATE,)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
coordinator: TriggerUpdateCoordinator,
|
||||
config: dict,
|
||||
) -> None:
|
||||
"""Initialize the entity."""
|
||||
super().__init__(hass, coordinator, config)
|
||||
|
||||
if isinstance(config.get(CONF_DELAY_ON), template.Template):
|
||||
self._to_render.append(CONF_DELAY_ON)
|
||||
self._parse_result.add(CONF_DELAY_ON)
|
||||
|
||||
if isinstance(config.get(CONF_DELAY_OFF), template.Template):
|
||||
self._to_render.append(CONF_DELAY_OFF)
|
||||
self._parse_result.add(CONF_DELAY_OFF)
|
||||
|
||||
self._delay_cancel = None
|
||||
self._state = False
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return state of the sensor."""
|
||||
return self._state
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
"""Handle update of the data."""
|
||||
self._process_data()
|
||||
|
||||
if self._delay_cancel:
|
||||
self._delay_cancel()
|
||||
self._delay_cancel = None
|
||||
|
||||
if not self.available:
|
||||
return
|
||||
|
||||
raw = self._rendered.get(CONF_STATE)
|
||||
state = template.result_as_boolean(raw)
|
||||
|
||||
if state == self._state:
|
||||
return
|
||||
|
||||
key = CONF_DELAY_ON if state else CONF_DELAY_OFF
|
||||
delay = self._rendered.get(key) or self._config.get(key)
|
||||
|
||||
# state without delay. None means rendering failed.
|
||||
if state is None or delay is None:
|
||||
self._state = state
|
||||
self.async_write_ha_state()
|
||||
return
|
||||
|
||||
if not isinstance(delay, timedelta):
|
||||
try:
|
||||
delay = cv.positive_time_period(delay)
|
||||
except vol.Invalid as err:
|
||||
logging.getLogger(__name__).warning(
|
||||
"Error rendering %s template: %s", key, err
|
||||
)
|
||||
return
|
||||
|
||||
@callback
|
||||
def _set_state(_):
|
||||
"""Set state of template binary sensor."""
|
||||
self._state = state
|
||||
self.async_set_context(self.coordinator.data["context"])
|
||||
self.async_write_ha_state()
|
||||
|
||||
# state with delay. Cancelled if new trigger received
|
||||
self._delay_cancel = async_call_later(
|
||||
self.hass, delay.total_seconds(), _set_state
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue